From a111cd0051581055481d6ed9df21804b9db1727a Mon Sep 17 00:00:00 2001 From: Inderpal Singh Date: Tue, 23 Oct 2012 17:16:08 +0530 Subject: Add DNW Support for Origen Quad board Signed-off-by: Inderpal Singh --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/usbd-otg-hs.c | 1936 ++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/usbd-otg-hs.h | 306 ++++++ drivers/usb/host/Makefile | 1 + drivers/usb/host/usb_ohci.c | 750 +++++++++++++++ drivers/usb/host/usb_ohci.h | 397 ++++++++ 6 files changed, 3391 insertions(+) create mode 100644 drivers/usb/gadget/usbd-otg-hs.c create mode 100644 drivers/usb/gadget/usbd-otg-hs.h create mode 100644 drivers/usb/host/usb_ohci.c create mode 100644 drivers/usb/host/usb_ohci.h (limited to 'drivers') diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1d21ffbb9..9e4db373e 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -51,6 +51,7 @@ COBJS-$(CONFIG_SPL_DSU_SUPPORT) += dsuboot.o endif endif +COBJS-y += usbd-otg-hs.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS)) diff --git a/drivers/usb/gadget/usbd-otg-hs.c b/drivers/usb/gadget/usbd-otg-hs.c new file mode 100644 index 000000000..ec9d34902 --- /dev/null +++ b/drivers/usb/gadget/usbd-otg-hs.c @@ -0,0 +1,1936 @@ +/* + * cpu/s5pc1xx/usbd-otg-hs.c + * + * (C) Copyright 2007 + * Byungjae Lee, Samsung Erectronics, bjlee@samsung.com. + * - only support for S5PC100 + * + * 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 + +#if defined(CONFIG_S5PC110) || defined(CONFIG_S5PC210) || defined(CONFIG_S5PC220) || defined(CONFIG_S5P6450) || defined(CONFIG_ARCH_EXYNOS) +#include +#include +#include +#include +#include "../../../common/cpu.h" +#include "usbd-otg-hs.h" + +#if defined(CONFIG_S5P6450) + DECLARE_GLOBAL_DATA_PTR; +#endif + +#undef USB_OTG_DEBUG_SETUP +#ifdef USB_OTG_DEBUG_SETUP +#define DBG_SETUP0(fmt, args...) printf("[%s:%d] " fmt, __FUNCTION__, __LINE__, ##args) +#define DBG_SETUP1(fmt, args...) printf("\t" fmt, ##args) +#define DBG_SETUP2(fmt, args...) printf(fmt, ##args) +#else +#define DBG_SETUP0(fmt, args...) do { } while (0) +#define DBG_SETUP1(fmt, args...) do { } while (0) +#define DBG_SETUP2(fmt, args...) do { } while (0) +#endif + +#undef USB_OTG_DEBUG_BULK +#ifdef USB_OTG_DEBUG_BULK +#define DBG_BULK0(fmt, args...) printf("[%s:%d] " fmt, __FUNCTION__, __LINE__, ##args) +#define DBG_BULK1(fmt, args...) printf("\t" fmt, ##args) +#else +#define DBG_BULK0(fmt, args...) do { } while (0) +#define DBG_BULK1(fmt, args...) do { } while (0) +#endif + +#define USB_CHECKSUM_EN + +#define TRUE 1 +#define FALSE 0 +#define SUSPEND_RESUME_ON FALSE + + +u32 s3c_usbd_dn_addr = 0; +u32 s3c_usbd_dn_cnt = 0; +u32 remode_wakeup; +u16 config_value; + +int DNW; +int is_fastboot = 0; +int s3c_receive_done = 0; +int s3c_got_header = 0; + +USB_OPMODE op_mode = USB_CPU; +USB_SPEED speed = USB_HIGH; + +otg_dev_t otg; +get_status_t get_status; +get_intf_t get_intf; + +enum EP_INDEX +{ + EP0, EP1, EP2, EP3, EP4 +}; + +/*------------------------------------------------*/ +/* EP0 state */ +enum EP0_STATE +{ + EP0_STATE_INIT = 0, + EP0_STATE_GD_DEV_0 = 11, + EP0_STATE_GD_DEV_1 = 12, + EP0_STATE_GD_DEV_2 = 13, + EP0_STATE_GD_CFG_0 = 21, + EP0_STATE_GD_CFG_1 = 22, + EP0_STATE_GD_CFG_2 = 23, + EP0_STATE_GD_CFG_3 = 24, + EP0_STATE_GD_CFG_4 = 25, + EP0_STATE_GD_STR_I0 = 30, + EP0_STATE_GD_STR_I1 = 31, + EP0_STATE_GD_STR_I2 = 32, + EP0_STATE_GD_STR_I3 = 133, + EP0_STATE_GD_DEV_QUALIFIER = 33, + EP0_STATE_INTERFACE_GET = 34, + EP0_STATE_GET_STATUS0 = 35, + EP0_STATE_GET_STATUS1 = 36, + EP0_STATE_GET_STATUS2 = 37, + EP0_STATE_GET_STATUS3 = 38, + EP0_STATE_GET_STATUS4 = 39, + EP0_STATE_GD_OTHER_SPEED = 40, + EP0_STATE_GD_CFG_ONLY_0 = 41, + EP0_STATE_GD_CFG_ONLY_1 = 42, + EP0_STATE_GD_IF_ONLY_0 = 44, + EP0_STATE_GD_IF_ONLY_1 = 45, + EP0_STATE_GD_EP0_ONLY_0 = 46, + EP0_STATE_GD_EP1_ONLY_0 = 47, + EP0_STATE_GD_EP2_ONLY_0 = 48, + EP0_STATE_GD_EP3_ONLY_0 = 49, + EP0_STATE_GD_OTHER_SPEED_HIGH_1 = 51, + EP0_STATE_GD_OTHER_SPEED_HIGH_2 = 52, + EP0_STATE_GD_OTHER_SPEED_HIGH_3 = 53 +}; + +/*definitions related to CSR setting */ + +/* S5P_OTG_GOTGCTL*/ +#define B_SESSION_VALID (0x1<<19) +#define A_SESSION_VALID (0x1<<18) + +/* S5P_OTG_GAHBCFG*/ +#define PTXFE_HALF (0<<8) +#define PTXFE_ZERO (1<<8) +#define NPTXFE_HALF (0<<7) +#define NPTXFE_ZERO (1<<7) +#define MODE_SLAVE (0<<5) +#define MODE_DMA (1<<5) +#define BURST_SINGLE (0<<1) +#define BURST_INCR (1<<1) +#define BURST_INCR4 (3<<1) +#define BURST_INCR8 (5<<1) +#define BURST_INCR16 (7<<1) +#define GBL_INT_UNMASK (1<<0) +#define GBL_INT_MASK (0<<0) + +/* S5P_OTG_GRSTCTL*/ +#define AHB_MASTER_IDLE (1u<<31) +#define CORE_SOFT_RESET (0x1<<0) + +/* S5P_OTG_GINTSTS/S5P_OTG_GINTMSK core interrupt register */ +#define INT_RESUME (1u<<31) +#define INT_DISCONN (0x1<<29) +#define INT_CONN_ID_STS_CNG (0x1<<28) +#define INT_OUT_EP (0x1<<19) +#define INT_IN_EP (0x1<<18) +#define INT_ENUMDONE (0x1<<13) +#define INT_RESET (0x1<<12) +#define INT_SUSPEND (0x1<<11) +#define INT_TX_FIFO_EMPTY (0x1<<5) +#define INT_RX_FIFO_NOT_EMPTY (0x1<<4) +#define INT_SOF (0x1<<3) +#define INT_DEV_MODE (0x0<<0) +#define INT_HOST_MODE (0x1<<1) + +/* S5P_OTG_GRXSTSP STATUS*/ +#define GLOBAL_OUT_NAK (0x1<<17) +#define OUT_PKT_RECEIVED (0x2<<17) +#define OUT_TRNASFER_COMPLETED (0x3<<17) +#define SETUP_TRANSACTION_COMPLETED (0x4<<17) +#define SETUP_PKT_RECEIVED (0x6<<17) + +/* S5P_OTG_DCTL device control register */ +#define NORMAL_OPERATION (0x1<<0) +#define SOFT_DISCONNECT (0x1<<1) +#define TEST_J_MODE (TEST_J<<4) +#define TEST_K_MODE (TEST_K<<4) +#define TEST_SE0_NAK_MODE (TEST_SE0_NAK<<4) +#define TEST_PACKET_MODE (TEST_PACKET<<4) +#define TEST_FORCE_ENABLE_MODE (TEST_FORCE_ENABLE<<4) +#define TEST_CONTROL_FIELD (0x7<<4) + +/* S5P_OTG_DAINT device all endpoint interrupt register */ +#define INT_IN_EP0 (0x1<<0) +#define INT_IN_EP1 (0x1<<1) +#define INT_IN_EP3 (0x1<<3) +#define INT_OUT_EP0 (0x1<<16) +#define INT_OUT_EP2 (0x1<<18) +#define INT_OUT_EP4 (0x1<<20) + +/* S5P_OTG_DIEPCTL0/S5P_OTG_DOEPCTL0 */ +#define DEPCTL_EPENA (0x1<<31) +#define DEPCTL_EPDIS (0x1<<30) +#define DEPCTL_SNAK (0x1<<27) +#define DEPCTL_CNAK (0x1<<26) +#define DEPCTL_CTRL_TYPE (EP_TYPE_CONTROL<<18) +#define DEPCTL_ISO_TYPE (EP_TYPE_ISOCHRONOUS<<18) +#define DEPCTL_BULK_TYPE (EP_TYPE_BULK<<18) +#define DEPCTL_INTR_TYPE (EP_TYPE_INTERRUPT<<18) +#define DEPCTL_USBACTEP (0x1<<15) + +/*ep0 enable, clear nak, next ep0, max 64byte */ +#define EPEN_CNAK_EP0_64 (DEPCTL_EPENA|DEPCTL_CNAK|(CONTROL_EP<<11)|(0<<0)) + +/*ep0 enable, clear nak, next ep0, 8byte */ +#define EPEN_CNAK_EP0_8 (DEPCTL_EPENA|DEPCTL_CNAK|(CONTROL_EP<<11)|(3<<0)) + +/* DIEPCTLn/DOEPCTLn */ +#define BACK2BACK_SETUP_RECEIVED (0x1<<6) +#define INTKN_TXFEMP (0x1<<4) +#define NON_ISO_IN_EP_TIMEOUT (0x1<<3) +#define CTRL_OUT_EP_SETUP_PHASE_DONE (0x1<<3) +#define AHB_ERROR (0x1<<2) +#define TRANSFER_DONE (0x1<<0) + + +/* codes representing languages */ +const u8 string_desc0[] = +{ + 4, STRING_DESCRIPTOR, LANGID_US_L, LANGID_US_H, +}; + +const u8 dnw_string_desc1[] = /* Manufacturer */ +{ + (0x14+2), STRING_DESCRIPTOR, + 'S', 0x0, 'y', 0x0, 's', 0x0, 't', 0x0, 'e', 0x0, + 'm', 0x0, ' ', 0x0, 'M', 0x0, 'C', 0x0, 'U', 0x0, +}; + +const u8 dnw_string_desc2[] = /* Product */ +{ + (0x2a+2), STRING_DESCRIPTOR, + 'S', 0x0, 'E', 0x0, 'C', 0x0, ' ', 0x0, 'S', 0x0, + '3', 0x0, 'C', 0x0, '6', 0x0, '4', 0x0, '0', 0x0, + '0', 0x0, 'X', 0x0, ' ', 0x0, 'T', 0x0, 'e', 0x0, + 's', 0x0, 't', 0x0, ' ', 0x0, 'B', 0x0, '/', 0x0, + 'D', 0x0 +}; + +/* setting the device qualifier descriptor and a string descriptor */ +const u8 qualifier_desc[] = +{ + 0x0a, /* 0 desc size */ + 0x06, /* 1 desc type (DEVICE_QUALIFIER)*/ + 0x00, /* 2 USB release */ + 0x02, /* 3 => 2.00*/ + 0xFF, /* 4 class */ + 0x00, /* 5 subclass */ + 0x00, /* 6 protocol */ + 64, /* 7 max pack size */ + 0x01, /* 8 number of other-speed configuration */ + 0x00, /* 9 reserved */ +}; + +const u8 config_full[] = +{ + 0x09, /* 0 desc size */ + 0x07, /* 1 desc type (other speed)*/ + 0x20, /* 2 Total length of data returned */ + 0x00, /* 3 */ + 0x01, /* 4 Number of interfaces supported by this speed configuration */ + 0x01, /* 5 value to use to select configuration */ + 0x00, /* 6 index of string desc */ + /* 7 same as configuration desc */ + CONF_ATTR_DEFAULT|CONF_ATTR_SELFPOWERED, + 0x19, /* 8 same as configuration desc */ + +}; + +const u8 config_full_total[] = +{ + 0x09, 0x07 ,0x20 ,0x00 ,0x01 ,0x01 ,0x00 ,0xC0 ,0x19, + 0x09 ,0x04 ,0x00 ,0x00 ,0x02 ,0xff ,0x00 ,0x00 ,0x00, + 0x07 ,0x05 ,0x83 ,0x02 ,0x40 ,0x00 ,0x00, + 0x07 ,0x05 ,0x04 ,0x02 ,0x40 ,0x00 ,0x00 +}; + +const u8 config_high[] = +{ + 0x09, /* 0 desc size */ + 0x07, /* 1 desc type (other speed)*/ + 0x20, /* 2 Total length of data returned */ + 0x00, /* 3 */ + 0x01, /* 4 Number of interfaces supported by this speed configuration */ + 0x01, /* 5 value to use to select configuration */ + 0x00, /* 6 index of string desc */ + /* 7 same as configuration desc */ + CONF_ATTR_DEFAULT|CONF_ATTR_SELFPOWERED, + 0x19, /* 8 same as configuration desc */ + +}; + +const u8 config_high_total[] = +{ + 0x09, 0x07 ,0x20 ,0x00 ,0x01 ,0x01 ,0x00 ,0xC0 ,0x19, + 0x09 ,0x04 ,0x00 ,0x00 ,0x02 ,0xff ,0x00 ,0x00 ,0x00, + 0x07 ,0x05 ,0x81 ,0x02 ,0x00 ,0x02 ,0x00, + 0x07 ,0x05 ,0x02 ,0x02 ,0x00 ,0x02 ,0x00 +}; + +/* Descriptor size */ +enum DESCRIPTOR_SIZE +{ + DEVICE_DESC_SIZE = sizeof(device_desc_t), + CONFIG_DESC_SIZE = sizeof(config_desc_t), + INTERFACE_DESC_SIZE = sizeof(intf_desc_t), + ENDPOINT_DESC_SIZE = sizeof(ep_desc_t), + DEVICE_QUALIFIER_SIZE = sizeof(qualifier_desc), + OTHER_SPEED_CFG_SIZE = 9 + +}; + +/*32 +++*/ +#define CONFIG_DESC_TOTAL_SIZE \ + (CONFIG_DESC_SIZE+INTERFACE_DESC_SIZE+ENDPOINT_DESC_SIZE*2) +#define TEST_PKT_SIZE 53 + +u8 test_pkt [TEST_PKT_SIZE] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*JKJKJKJK x 9*/ + 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, /*JJKKJJKK x 8*/ + 0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE, /*JJJJKKKK x 8*/ + 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, /*JJJJJJJKKKKKKK x8 - '1'*/ + 0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD, /*'1' + JJJJJJJK x 8*/ + 0xFC,0x7E,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0x7E /*{JKKKKKKK x 10},JK*/ +}; + +int s3c_usbctl_init(void); +void s3c_usb_set_inep_xfersize(EP_TYPE type, u32 pktcnt, u32 xfersize); +void s3c_usb_write_in_fifo(u8 *buf, int num); +void s3c_usb_read_out_fifo(u8 *buf, int num); + + +void s3c_usb_init_phy(void) +{ +#if defined(CONFIG_ARCH_EXYNOS5) + writel(0x0, USB_CFG_REG); + writel(0x7454, EXYNOS5_OTG_SYS); + udelay(10); + writel(0x0454, EXYNOS5_OTG_SYS); + udelay(10); +#else /* EXYNOS4 or under */ +#if defined(CONFIG_S5PC110) + writel(0xa0, S5P_OTG_PHYPWR); + writel(0x3, S5P_OTG_PHYCLK); +#elif defined(CONFIG_S5PC220) + writel(0x0, USB_CFG_REG); + writel(0x7f80, S5P_OTG_PHYPWR); + writel(0x5, S5P_OTG_PHYCLK); +#elif defined(CONFIG_S5PC210) + writel(0x1f80, S5P_OTG_PHYPWR); + writel(0x3, S5P_OTG_PHYCLK); +#elif defined(CONFIG_S5P6450) + writel(0xa0, S5P_OTG_PHYPWR); + writel(0x21, S5P_OTG_PHYCLK); +#endif + writel(0x1, S5P_OTG_RSTCON); + udelay(10); + writel(0x0, S5P_OTG_RSTCON); + udelay(10); +#endif +} + +/* OTG PHY Power Off */ +void s3c_usb_phy_off(void) { +#if defined(CONFIG_ARCH_EXYNOS5) + writel(0x145F, EXYNOS5_OTG_SYS); +#else + writel(readl(S5P_OTG_PHYPWR)|(0x18), S5P_OTG_PHYPWR); +#endif +#if !defined(CONFIG_S5P6450) + writel(readl(USB_PHY_CONTROL)&~(1<<0), USB_PHY_CONTROL); +#else + writel(readl(OTHERS)|(1<<16), OTHERS); +#endif +} + +void s3c_usb_core_soft_reset(void) +{ + u32 tmp; + + writel(CORE_SOFT_RESET, S5P_OTG_GRSTCTL); + + do + { + tmp = readl(S5P_OTG_GRSTCTL); + }while(!(tmp & AHB_MASTER_IDLE)); + +} + +void s3c_usb_wait_cable_insert(void) +{ + u32 tmp; + int ucFirst=1; + + do { + udelay(50); + + tmp = readl(S5P_OTG_GOTGCTL); + + if (tmp & (B_SESSION_VALID|A_SESSION_VALID)) { + printf("OTG cable Connected!\n"); + break; + } else if(ucFirst == 1) { + printf("Insert a OTG cable into the connector!\n"); + ucFirst = 0; + } + } while(1); +} + +void s3c_usb_init_core(void) +{ + writel(PTXFE_HALF|NPTXFE_HALF|MODE_SLAVE|BURST_SINGLE|GBL_INT_UNMASK, + S5P_OTG_GAHBCFG); + + writel( 0<<15 /* PHY Low Power Clock sel */ + |1<<14 /* Non-Periodic TxFIFO Rewind Enable */ + |0x5<<10 /* Turnaround time */ + |0<<9 /* 0:HNP disable, 1:HNP enable */ + |0<<8 /* 0:SRP disable, 1:SRP enable */ + |0<<7 /* ULPI DDR sel */ + |0<<6 /* 0: high speed utmi+, 1: full speed serial */ + |0<<4 /* 0: utmi+, 1:ulpi */ + |1<<3 /* phy i/f 0:8bit, 1:16bit */ + |0x7<<0, /* HS/FS Timeout**/ + S5P_OTG_GUSBCFG ); +} + +void s3c_usb_check_current_mode(u8 *pucMode) +{ + u32 tmp; + + tmp = readl(S5P_OTG_GINTSTS); + *pucMode = tmp & 0x1; +} + +void s3c_usb_set_soft_disconnect(void) +{ + u32 tmp; + + tmp = readl(S5P_OTG_DCTL); + tmp |= SOFT_DISCONNECT; + writel(tmp, S5P_OTG_DCTL); +} + +void s3c_usb_clear_soft_disconnect(void) +{ + u32 tmp; + + tmp = readl(S5P_OTG_DCTL); + tmp &= ~SOFT_DISCONNECT; + writel(tmp, S5P_OTG_DCTL); +} + +void s3c_usb_init_device(void) +{ + writel(1<<18|otg.speed<<0, S5P_OTG_DCFG); /* [][1: full speed(30Mhz) 0:high speed]*/ + + writel(INT_RESUME|INT_OUT_EP|INT_IN_EP|INT_ENUMDONE| + INT_RESET|INT_SUSPEND|INT_RX_FIFO_NOT_EMPTY, + S5P_OTG_GINTMSK); /*gint unmask */ +} + +int s3c_usbctl_init(void) +{ + u8 ucMode; + + DBG_SETUP0("USB Control Init\n"); +#if defined(CONFIG_S5PC110)||defined(CONFIG_S5PV310) || defined(CONFIG_S5PC210) || defined(CONFIG_S5PC220) || defined(CONFIG_ARCH_EXYNOS) + writel(readl(USB_PHY_CONTROL)|(1<<0), USB_PHY_CONTROL); /*USB PHY0 Enable */ // c110 +#elif defined(CONFIG_S5PC100) + writel(readl(OTHERS)|~(1<<16), OTHERS); +#elif defined(CONFIG_S5P6450) + writel(readl(OTHERS)&~(1<<16), OTHERS); +#else +#error "* CFG_ERROR : you have to select proper CPU for Android Fastboot" +#endif + + otg.speed = speed; + otg.set_config = 0; + otg.ep0_state = EP0_STATE_INIT; + otg.ep0_substate = 0; + s3c_usb_init_phy(); + s3c_usb_core_soft_reset(); + s3c_usb_wait_cable_insert(); + s3c_usb_init_core(); + s3c_usb_check_current_mode(&ucMode); + is_fastboot = 0; + + if (ucMode == INT_DEV_MODE) { + s3c_usb_set_soft_disconnect(); + udelay(10); + s3c_usb_clear_soft_disconnect(); + s3c_usb_init_device(); + return 0; + } else { + printf("Error : Current Mode is Host\n"); + return 0; + } +} + +int s3c_usbc_activate (void) +{ + /* dont used in usb high speed, but used in common file cmd_usbd.c */ + return 0; +} + +int s3c_usb_stop (void) +{ + /* dont used in usb high speed, but used in common file cmd_usbd.c */ + s3c_usb_core_soft_reset(); + s3c_usb_phy_off(); + return 0; +} + +void s3c_usb_print_pkt(u8 *pt, u8 count) +{ + int i; + printf("[s3c_usb_print_pkt:"); + + for(i=0;i MEM:%x DNW:%x\n",checkSum,dnCS); + printf("Checksum failed.\n\n"); + } + +} + +void s3c_usb_set_inep_xfersize(EP_TYPE type, u32 pktcnt, u32 xfersize) +{ + if(type == EP_TYPE_CONTROL) + { + writel((pktcnt<<19)|(xfersize<<0), S5P_OTG_DIEPTSIZ0); + } + else if(type == EP_TYPE_BULK) + { + writel((1<<29)|(pktcnt<<19)|(xfersize<<0), S5P_OTG_DIEPTSIZ_IN); + } +} + +void s3c_usb_set_outep_xfersize(EP_TYPE type, u32 pktcnt, u32 xfersize) +{ + if(type == EP_TYPE_CONTROL) + { + writel((1<<29)|(pktcnt<<19)|(xfersize<<0), S5P_OTG_DOEPTSIZ0); + } + else if(type == EP_TYPE_BULK) + { + writel((pktcnt<<19)|(xfersize<<0), S5P_OTG_DOEPTSIZ_OUT); + } +} + +void s3c_usb_write_ep0_fifo(u8 *buf, int num) +{ + int i; + u32 Wr_Data=0; + + DBG_SETUP1("[s3c_usb_write_ep0_fifo:"); + + for(i=0;i>8); + buf[i+2] = (u8)(data>>16); + buf[i+3] = (u8)(data>>24); + } +} + +void s3c_usb_get_desc(void) +{ + switch (otg.dev_req.wValue_H) { + case DEVICE_DESCRIPTOR: + otg.req_length = (u32)((otg.dev_req.wLength_H << 8) | + otg.dev_req.wLength_L); + DBG_SETUP1("DEVICE_DESCRIPTOR = 0x%x \n",otg.req_length); + otg.ep0_state = EP0_STATE_GD_DEV_0; + break; + + case CONFIGURATION_DESCRIPTOR: + otg.req_length = (u32)((otg.dev_req.wLength_H << 8) | + otg.dev_req.wLength_L); + DBG_SETUP1("CONFIGURATION_DESCRIPTOR = 0x%x \n",otg.req_length); + + /* GET_DESCRIPTOR:CONFIGURATION+INTERFACE+ENDPOINT0+ENDPOINT1 */ + if (otg.req_length > CONFIG_DESC_SIZE){ + otg.ep0_state = EP0_STATE_GD_CFG_0; + } else + otg.ep0_state = EP0_STATE_GD_CFG_ONLY_0; + break; + + case STRING_DESCRIPTOR : + DBG_SETUP1("STRING_DESCRIPTOR \n"); + + switch(otg.dev_req.wValue_L) { + case 0: + otg.ep0_state = EP0_STATE_GD_STR_I0; + break; + case 1: + otg.ep0_state = EP0_STATE_GD_STR_I1; + break; + case 2: + otg.ep0_state = EP0_STATE_GD_STR_I2; + break; + case 3: + otg.ep0_state = EP0_STATE_GD_STR_I3; + break; + default: + break; + } + break; + + case ENDPOINT_DESCRIPTOR: + DBG_SETUP1("ENDPOINT_DESCRIPTOR \n"); + switch(otg.dev_req.wValue_L&0xf) { + case 0: + otg.ep0_state=EP0_STATE_GD_EP0_ONLY_0; + break; + case 1: + otg.ep0_state=EP0_STATE_GD_EP1_ONLY_0; + break; + default: + break; + } + break; + + case DEVICE_QUALIFIER: + otg.req_length = (u32)((otg.dev_req.wLength_H << 8) | + otg.dev_req.wLength_L); + DBG_SETUP1("DEVICE_QUALIFIER = 0x%x \n",otg.req_length); + otg.ep0_state = EP0_STATE_GD_DEV_QUALIFIER; + break; + + case OTHER_SPEED_CONFIGURATION : + DBG_SETUP1("OTHER_SPEED_CONFIGURATION \n"); + otg.req_length = (u32)((otg.dev_req.wLength_H << 8) | + otg.dev_req.wLength_L); + otg.ep0_state = EP0_STATE_GD_OTHER_SPEED; + break; + + } +} + +void s3c_usb_clear_feature(void) +{ + switch (otg.dev_req.bmRequestType) { + case DEVICE_RECIPIENT: + DBG_SETUP1("DEVICE_RECIPIENT \n"); + if (otg.dev_req.wValue_L == 1) + remode_wakeup = FALSE; + break; + + case ENDPOINT_RECIPIENT: + DBG_SETUP1("ENDPOINT_RECIPIENT \n"); + if (otg.dev_req.wValue_L == 0) { + if ((otg.dev_req.wIndex_L & 0x7f) == CONTROL_EP) + get_status.ep_ctrl= 0; + + /* IN Endpoint */ + if ((otg.dev_req.wIndex_L & 0x7f) == BULK_IN_EP) + get_status.ep_in= 0; + + /* OUT Endpoint */ + if ((otg.dev_req.wIndex_L & 0x7f) == BULK_OUT_EP) + get_status.ep_out= 0; + } + break; + + default: + DBG_SETUP1("\n"); + break; + } + otg.ep0_state = EP0_STATE_INIT; + +} + +void s3c_usb_set_feature(void) +{ + u32 tmp; + + switch (otg.dev_req.bmRequestType) { + case DEVICE_RECIPIENT: + DBG_SETUP1("DEVICE_RECIPIENT \n"); + if (otg.dev_req.wValue_L == 1) + remode_wakeup = TRUE; + break; + + case ENDPOINT_RECIPIENT: + DBG_SETUP1("ENDPOINT_RECIPIENT \n"); + if (otg.dev_req.wValue_L == 0) { + if ((otg.dev_req.wIndex_L & 0x7f) == CONTROL_EP) + get_status.ep_ctrl= 1; + + if ((otg.dev_req.wIndex_L & 0x7f) == BULK_IN_EP) + get_status.ep_in= 1; + + if ((otg.dev_req.wIndex_L & 0x7f) == BULK_OUT_EP) + get_status.ep_out= 1; + } + break; + + default: + DBG_SETUP1("\n"); + break; + } + + switch (otg.dev_req.wValue_L) { + case EP_STALL: + /* TBD: additional processing if required */ + break; + + case TEST_MODE: + if ((0 != otg.dev_req.wIndex_L ) ||(0 != otg.dev_req.bmRequestType)) + break; + + /* Set TEST MODE*/ + tmp = readl(S5P_OTG_DCTL); + tmp = (tmp & ~(TEST_CONTROL_FIELD)) | (TEST_FORCE_ENABLE_MODE); + writel(tmp, S5P_OTG_DCTL); + + switch(otg.dev_req.wIndex_H) { + case TEST_J: + /*Set Test J*/ + tmp = readl(S5P_OTG_DCTL); + tmp = (tmp & ~(TEST_CONTROL_FIELD)) | (TEST_J_MODE); + writel(tmp, S5P_OTG_DCTL); + break; + + case TEST_K: + /*Set Test K*/ + tmp = readl(S5P_OTG_DCTL); + tmp = (tmp & ~(TEST_CONTROL_FIELD)) | (TEST_K_MODE); + writel(tmp, S5P_OTG_DCTL); + break; + + case TEST_SE0_NAK: + /*Set Test SE0NAK*/ + tmp = readl(S5P_OTG_DCTL); + tmp = (tmp & ~(TEST_CONTROL_FIELD)) | (TEST_SE0_NAK_MODE); + writel(tmp, S5P_OTG_DCTL); + break; + + case TEST_PACKET: + DBG_SETUP1 ("Test_packet\n"); + writel(EPEN_CNAK_EP0_64, S5P_OTG_DIEPCTL0); + s3c_usb_set_inep_xfersize(EP_TYPE_CONTROL, 1, TEST_PKT_SIZE); + s3c_usb_write_ep0_fifo(test_pkt, TEST_PKT_SIZE); + tmp = readl(S5P_OTG_DCTL); + tmp = (tmp & ~(TEST_CONTROL_FIELD)) | (TEST_PACKET_MODE); + writel(tmp, S5P_OTG_DCTL); + DBG_SETUP1 ("S5P_OTG_DCTL=0x%08x\n", tmp); + break; + } + break; + + default: + break; + } + otg.ep0_state = EP0_STATE_INIT; +} + +void s3c_usb_get_status(void) +{ + switch(otg.dev_req.bmRequestType) { + case (0x80): /*device */ + DBG_SETUP1("DEVICE\n"); + get_status.Device=((u8)remode_wakeup<<1)|0x1; /* SelfPowered */ + otg.ep0_state = EP0_STATE_GET_STATUS0; + break; + + case (0x81): /*interface */ + DBG_SETUP1("INTERFACE\n"); + get_status.Interface=0; + otg.ep0_state = EP0_STATE_GET_STATUS1; + break; + + case (0x82): /*endpoint */ + DBG_SETUP1("ENDPOINT\n"); + if ((otg.dev_req.wIndex_L & 0x7f) == CONTROL_EP) + otg.ep0_state = EP0_STATE_GET_STATUS2; + + if ((otg.dev_req.wIndex_L & 0x7f) == BULK_IN_EP) + otg.ep0_state = EP0_STATE_GET_STATUS3; + + if ((otg.dev_req.wIndex_L & 0x7f) == BULK_OUT_EP) + otg.ep0_state = EP0_STATE_GET_STATUS4; + break; + + default: + DBG_SETUP1("\n"); + break; + } +} + +void s3c_usb_ep0_int_hndlr(void) +{ + u16 i; + u32 buf[2]={0x0000, }; + u16 addr; + + DBG_SETUP0("Event EP0\n"); + + if (otg.ep0_state == EP0_STATE_INIT) { + + for(i=0;i<2;i++) + buf[i] = readl(S5P_OTG_EP0_FIFO); + + otg.dev_req.bmRequestType = buf[0]; + otg.dev_req.bRequest = buf[0]>>8; + otg.dev_req.wValue_L = buf[0]>>16; + otg.dev_req.wValue_H = buf[0]>>24; + otg.dev_req.wIndex_L = buf[1]; + otg.dev_req.wIndex_H = buf[1]>>8; + otg.dev_req.wLength_L = buf[1]>>16; + otg.dev_req.wLength_H = buf[1]>>24; + +#ifdef USB_OTG_DEBUG_SETUP + s3c_usb_print_pkt((u8 *)&otg.dev_req, 8); +#endif + + switch (otg.dev_req.bRequest) { + case STANDARD_SET_ADDRESS: + /* Set Address Update bit */ + addr = (otg.dev_req.wValue_L); + writel(1<<18|addr<<4|otg.speed<<0, S5P_OTG_DCFG); + DBG_SETUP1("S5P_OTG_DCFG : %x, STANDARD_SET_ADDRESS : %d\n", + readl(S5P_OTG_DCFG), addr); + otg.ep0_state = EP0_STATE_INIT; + break; + + case STANDARD_SET_DESCRIPTOR: + DBG_SETUP1("STANDARD_SET_DESCRIPTOR \n"); + break; + + case STANDARD_SET_CONFIGURATION: + DBG_SETUP1("STANDARD_SET_CONFIGURATION \n"); + /* Configuration value in configuration descriptor */ + config_value = otg.dev_req.wValue_L; + otg.set_config = 1; + otg.ep0_state = EP0_STATE_INIT; + break; + + case STANDARD_GET_CONFIGURATION: + DBG_SETUP1("STANDARD_GET_CONFIGURATION \n"); + s3c_usb_set_inep_xfersize(EP_TYPE_CONTROL, 1, 1); + + /*ep0 enable, clear nak, next ep0, 8byte */ + writel(EPEN_CNAK_EP0_8, S5P_OTG_DIEPCTL0); + writel(config_value, S5P_OTG_EP0_FIFO); + otg.ep0_state = EP0_STATE_INIT; + break; + + case STANDARD_GET_DESCRIPTOR: + DBG_SETUP1("STANDARD_GET_DESCRIPTOR :"); + s3c_usb_get_desc(); + break; + + case STANDARD_CLEAR_FEATURE: + DBG_SETUP1("STANDARD_CLEAR_FEATURE :"); + s3c_usb_clear_feature(); + break; + + case STANDARD_SET_FEATURE: + DBG_SETUP1("STANDARD_SET_FEATURE :"); + s3c_usb_set_feature(); + break; + + case STANDARD_GET_STATUS: + DBG_SETUP1("STANDARD_GET_STATUS :"); + s3c_usb_get_status(); + break; + + case STANDARD_GET_INTERFACE: + DBG_SETUP1("STANDARD_GET_INTERFACE \n"); + otg.ep0_state = EP0_STATE_INTERFACE_GET; + break; + + case STANDARD_SET_INTERFACE: + DBG_SETUP1("STANDARD_SET_INTERFACE \n"); + get_intf.AlternateSetting= otg.dev_req.wValue_L; + otg.ep0_state = EP0_STATE_INIT; + break; + + case STANDARD_SYNCH_FRAME: + DBG_SETUP1("STANDARD_SYNCH_FRAME \n"); + otg.ep0_state = EP0_STATE_INIT; + break; + + default: + break; + } + } + + s3c_usb_set_inep_xfersize(EP_TYPE_CONTROL, 1, otg.ctrl_max_pktsize); + + /*clear nak, next ep0, 64byte */ + writel(((1<<26)|(CONTROL_EP<<11)|(0<<0)), S5P_OTG_DIEPCTL0); + +} + +void s3c_usb_set_otherspeed_conf_desc(u32 length) +{ + /* Standard device descriptor */ + if (length ==9) + { + s3c_usb_set_inep_xfersize(EP_TYPE_CONTROL, 1, 9); + writel(EPEN_CNAK_EP0_64, S5P_OTG_DIEPCTL0); + s3c_usb_write_ep0_fifo(((u8 *)&config_full)+0, 9); + } + else if(length ==32) + { + s3c_usb_set_inep_xfersize(EP_TYPE_CONTROL, 1, 32); + writel(EPEN_CNAK_EP0_64, S5P_OTG_DIEPCTL0); + s3c_usb_write_ep0_fifo(((u8 *)&config_full_total)+0, 32); + + } + otg.ep0_state = EP0_STATE_INIT; +} + +void s3c_usb_transfer_ep0(void) +{ + const u8 *string_desc1, *string_desc2, *string_desc3; + u32 string_desc1_size, string_desc2_size, string_desc3_size; + + if (is_fastboot) { +/* string_desc1 = fboot_string_desc1; + string_desc2 = fboot_string_desc2; + string_desc3 = fboot_string_desc3; + + string_desc1_size = sizeof(fboot_string_desc1); + string_desc2_size = sizeof(fboot_string_desc2); + string_desc3_size = sizeof(fboot_string_desc3); +*/ } else { + string_desc1 = dnw_string_desc1; + string_desc2 = dnw_string_desc2; + string_desc3 = dnw_string_desc2; + + string_desc1_size = sizeof(dnw_string_desc1); + string_desc2_size = sizeof(dnw_string_desc2); + string_desc3_size = sizeof(dnw_string_desc2); + } + + DBG_SETUP0("otg.ep0_state = %d\n", otg.ep0_state); + + switch (otg.ep0_state) { + case EP0_STATE_INIT: + s3c_usb_set_inep_xfersize(EP_TYPE_CONTROL, 1, 0); + + /*ep0 enable, clear nak, next ep0, 8byte */ + writel(EPEN_CNAK_EP0_8, S5P_OTG_DIEPCTL0); + DBG_SETUP1("EP0_STATE_INIT\n"); + break; + + /* GET_DESCRIPTOR:DEVICE */ + case EP0_STATE_GD_DEV_0: + DBG_SETUP1("EP0_STATE_GD_DEV_0 :"); + + /*ep0 enable, clear nak, next ep0, max 64byte */ + writel(EPEN_CNAK_EP0_64, S5P_OTG_DIEPCTL0); + if (otg.req_length < DEVICE_DESC_SIZE) { + s3c_usb_set_inep_xfersize(EP_TYPE_CONTROL, 1, otg.req_length); + s3c_usb_write_ep0_fifo(((u8 *)&(otg.desc.dev))+0, otg.req_length); + } else { + s3c_usb_set_inep_xfersize(EP_TYPE_CONTROL, 1, DEVICE_DESC_SIZE); + s3c_usb_write_ep0_fifo(((u8 *)&(otg.desc.dev))+0, DEVICE_DESC_SIZE); + } + otg.ep0_state = EP0_STATE_INIT; + break; + + /* GET_DESCRIPTOR:CONFIGURATION+INTERFACE+ENDPOINT0+ENDPOINT1 */ + case EP0_STATE_GD_CFG_0: + DBG_SETUP1("EP0_STATE_GD_CFG_0 :"); + writel(EPEN_CNAK_EP0_64, S5P_OTG_DIEPCTL0); + if(otg.req_length otg.bulkin_max_pktsize) { + s3c_usb_set_inep_xfersize(EP_TYPE_BULK, 1, otg.bulkin_max_pktsize); + + /*ep3 enable, clear nak, bulk, usb active, next ep3, max pkt 64*/ + writel(1u<<31|1<<26|2<<18|1<<15|otg.bulkin_max_pktsize<<0, + S5P_OTG_DIEPCTL_IN); + + s3c_usb_write_in_fifo(bulkin_buf, otg.bulkin_max_pktsize); + + otg.up_ptr += otg.bulkin_max_pktsize; + + while(readl(S5P_OTG_DIEPCTL_IN) & (1<<31)); + } else if(remain_cnt > 0) { + s3c_usb_set_inep_xfersize(EP_TYPE_BULK, 1, remain_cnt); + + /*ep3 enable, clear nak, bulk, usb active, next ep3, max pkt 64*/ + writel(1u<<31|1<<26|2<<18|1<<15|otg.bulkin_max_pktsize<<0, + S5P_OTG_DIEPCTL_IN); + + s3c_usb_write_in_fifo(bulkin_buf, remain_cnt); + + otg.up_ptr += remain_cnt; + + while(readl(S5P_OTG_DIEPCTL_IN) & (1<<31)); + } else { /*remain_cnt = 0*/ + writel((DEPCTL_SNAK|DEPCTL_BULK_TYPE), S5P_OTG_DIEPCTL_IN); + } +} + +void s3c_usb_upload_start(void) +{ + u8 tmp_buf[12]; + u32 check; + + s3c_usb_read_out_fifo((u8 *)tmp_buf, 10); + check = *((u8 *)(tmp_buf+8)) + (*((u8 *)(tmp_buf+9))<<8); + + if (check==0x1) { + otg.up_addr = + *((u8 *)(tmp_buf+0))+ + (*((u8 *)(tmp_buf+1))<<8)+ + (*((u8 *)(tmp_buf+2))<<16)+ + (*((u8 *)(tmp_buf+3))<<24); + + otg.up_size = + *((u8 *)(tmp_buf+4))+ + (*((u8 *)(tmp_buf+5))<<8)+ + (*((u8 *)(tmp_buf+6))<<16)+ + (*((u8 *)(tmp_buf+7))<<24); + + otg.up_ptr=(u8 *)otg.up_addr; + DBG_BULK1("UploadAddress : 0x%x, UploadSize: %d\n", + otg.up_addr, otg.up_size); + + if (otg.op_mode == USB_CPU) { + if (otg.up_size > otg.bulkin_max_pktsize) { + s3c_usb_set_inep_xfersize(EP_TYPE_BULK, 1, + otg.bulkin_max_pktsize); + } else { + s3c_usb_set_inep_xfersize(EP_TYPE_BULK, 1, + otg.up_size); + } + + /*ep1 enable, clear nak, bulk, usb active, max pkt 64*/ + writel(1u<<31|1<<26|2<<18|1<<15|otg.bulkin_max_pktsize<<0, + S5P_OTG_DIEPCTL_IN); + } else if ((otg.op_mode == USB_DMA) && (otg.up_size > 0)) { + u32 pktcnt, remainder; + + DBG_BULK1("Dma Start for IN PKT \n"); + + writel(MODE_DMA|BURST_INCR4|GBL_INT_UNMASK, + S5P_OTG_GAHBCFG); + writel(INT_RESUME|INT_OUT_EP|INT_IN_EP| INT_ENUMDONE| + INT_RESET|INT_SUSPEND, S5P_OTG_GINTMSK); + + writel((u32)otg.up_ptr, S5P_OTG_DIEPDMA_IN); + + pktcnt = (u32)(otg.up_size/otg.bulkin_max_pktsize); + remainder = (u32)(otg.up_size%otg.bulkin_max_pktsize); + if(remainder != 0) { + pktcnt += 1; + } + + if (pktcnt > 1023) { + s3c_usb_set_inep_xfersize(EP_TYPE_BULK, 1023, + (otg.bulkin_max_pktsize*1023)); + } else { + s3c_usb_set_inep_xfersize(EP_TYPE_BULK, pktcnt, + otg.up_size); + } + + /*ep1 enable, clear nak, bulk, usb active, next ep1, max pkt */ + writel(1u<<31|1<<26|2<<18|1<<15|BULK_IN_EP<<11| + otg.bulkin_max_pktsize<<0, + S5P_OTG_DIEPCTL_IN); + } + } + otg.dn_filesize=0; +} + +void s3c_usb_download_start(u32 fifo_cnt_byte) +{ + u8 tmp_buf[8]; + + s3c_usb_read_out_fifo((u8 *)tmp_buf, 8); + DBG_BULK1("downloadFileSize==0, 1'st BYTE_READ_CNT_REG : %x\n", + fifo_cnt_byte); + + otg.dn_addr=s3c_usbd_dn_addr; + otg.dn_filesize= + *((u8 *)(tmp_buf+4))+ + (*((u8 *)(tmp_buf+5))<<8)+ + (*((u8 *)(tmp_buf+6))<<16)+ + (*((u8 *)(tmp_buf+7))<<24); + + otg.dn_ptr=(u8 *)otg.dn_addr; + DBG_BULK1("downloadAddress : 0x%x, downloadFileSize: %x\n", + otg.dn_addr, otg.dn_filesize); + + /* The first 8-bytes are deleted.*/ + s3c_usb_read_out_fifo((u8 *)otg.dn_ptr, fifo_cnt_byte-8); + otg.dn_ptr += fifo_cnt_byte-8; + + if (otg.op_mode == USB_CPU) { + s3c_usb_set_outep_xfersize(EP_TYPE_BULK, 1, + otg.bulkout_max_pktsize); + + /*ep3 enable, clear nak, bulk, usb active, next ep3, max pkt 64*/ + writel(1u<<31|1<<26|2<<18|1<<15|otg.bulkout_max_pktsize<<0, + S5P_OTG_DOEPCTL_OUT); + } else if (otg.dn_filesize>otg.bulkout_max_pktsize) { + u32 pkt_cnt, remain_cnt; + + DBG_BULK1("downloadFileSize!=0, Dma Start for 2nd OUT PKT \n"); + writel(INT_RESUME|INT_OUT_EP|INT_IN_EP|INT_ENUMDONE| + INT_RESET|INT_SUSPEND, S5P_OTG_GINTMSK); /*gint unmask */ + writel(MODE_DMA|BURST_INCR4|GBL_INT_UNMASK, + S5P_OTG_GAHBCFG); + writel((u32)otg.dn_ptr, S5P_OTG_DOEPDMA_OUT); + pkt_cnt = (u32)(otg.dn_filesize-otg.bulkout_max_pktsize)/otg.bulkout_max_pktsize; + remain_cnt = (u32)((otg.dn_filesize-otg.bulkout_max_pktsize)%otg.bulkout_max_pktsize); + if(remain_cnt != 0) { + pkt_cnt += 1; + } + + if (pkt_cnt > 1023) { + s3c_usb_set_outep_xfersize(EP_TYPE_BULK, 1023, + (otg.bulkout_max_pktsize*1023)); + } else { + s3c_usb_set_outep_xfersize(EP_TYPE_BULK, pkt_cnt, + (otg.dn_filesize-otg.bulkout_max_pktsize)); + } + + /*ep3 enable, clear nak, bulk, usb active, next ep3, max pkt 64*/ + writel(1u<<31|1<<26|2<<18|1<<15|otg.bulkout_max_pktsize<<0, + S5P_OTG_DOEPCTL_OUT); + } +} + +void s3c_usb_download_continue(u32 fifo_cnt_byte) +{ + if (otg.op_mode == USB_CPU) { + s3c_usb_read_out_fifo((u8 *)otg.dn_ptr, fifo_cnt_byte); + otg.dn_ptr += fifo_cnt_byte; + DBG_BULK1("downloadFileSize!=0, 2nd BYTE_READ_CNT_REG = 0x%x, m_pDownPt = 0x%x\n", + fifo_cnt_byte, otg.dn_ptr); + + s3c_usb_set_outep_xfersize(EP_TYPE_BULK, 1, otg.bulkout_max_pktsize); + + /*ep3 enable, clear nak, bulk, usb active, next ep3, max pkt 64*/ + writel(1u<<31|1<<26|2<<18|1<<15|otg.bulkout_max_pktsize<<0, + S5P_OTG_DOEPCTL_OUT); + + /* USB format : addr(4)+size(4)+data(n)+cs(2) */ + if (((u32)otg.dn_ptr - otg.dn_addr) >= (otg.dn_filesize - 8)) { + printf("Download Done!! Download Address: 0x%x, Download Filesize:0x%x\n", + otg.dn_addr, (otg.dn_filesize-10)); + + s3c_usbd_dn_cnt = otg.dn_filesize-10; + s3c_usbd_dn_addr = otg.dn_addr; + +#ifdef USB_CHECKSUM_EN + s3c_usb_verify_checksum(); +#endif + s3c_receive_done = 1; + } + + }\ +} + +void s3c_usb_int_bulkout(u32 fifo_cnt_byte) +{ + DBG_BULK0("Bulk Out Function : otg.dn_filesize=0x%x\n", otg.dn_filesize); + if (otg.dn_filesize==0) { + if (fifo_cnt_byte == 10) { + s3c_usb_upload_start(); + } else { + s3c_usb_download_start(fifo_cnt_byte); + } + } else { + s3c_usb_download_continue(fifo_cnt_byte); + } +} + +void s3c_usb_dma_in_done(void) +{ + s32 remain_cnt; + + DBG_BULK0("DMA IN : Transfer Done\n"); + + otg.up_ptr = (u8 *)readl(S5P_OTG_DIEPDMA_IN); + remain_cnt = otg.up_size- ((u32)otg.up_ptr - otg.up_addr); + + if (remain_cnt>0) { + u32 pktcnt, remainder; + pktcnt = (u32)(remain_cnt/otg.bulkin_max_pktsize); + remainder = (u32)(remain_cnt%otg.bulkin_max_pktsize); + if(remainder != 0) { + pktcnt += 1; + } + DBG_SETUP1("remain_cnt : %d \n", remain_cnt); + if (pktcnt> 1023) { + s3c_usb_set_inep_xfersize(EP_TYPE_BULK, 1023, + (otg.bulkin_max_pktsize*1023)); + } else { + s3c_usb_set_inep_xfersize(EP_TYPE_BULK, pktcnt, + remain_cnt); + } + + /*ep1 enable, clear nak, bulk, usb active, next ep1, max pkt */ + writel(1u<<31|1<<26|2<<18|1<<15|BULK_IN_EP<<11|otg.bulkin_max_pktsize<<0, + S5P_OTG_DIEPCTL_IN); + } else + DBG_SETUP1("DMA IN : Transfer Complete\n"); +} + +void s3c_usb_dma_out_done(void) +{ + s32 remain_cnt; + + DBG_BULK1("DMA OUT : Transfer Done\n"); + otg.dn_ptr = (u8 *)readl(S5P_OTG_DOEPDMA_OUT); + + remain_cnt = otg.dn_filesize - ((u32)otg.dn_ptr - otg.dn_addr + 8); + + if (remain_cnt>0) { + u32 pktcnt, remainder; + pktcnt = (u32)(remain_cnt/otg.bulkout_max_pktsize); + remainder = (u32)(remain_cnt%otg.bulkout_max_pktsize); + if(remainder != 0) { + pktcnt += 1; + } + DBG_BULK1("remain_cnt : %d \n", remain_cnt); + if (pktcnt> 1023) { + s3c_usb_set_outep_xfersize(EP_TYPE_BULK, 1023, + (otg.bulkout_max_pktsize*1023)); + } else { + s3c_usb_set_outep_xfersize(EP_TYPE_BULK, pktcnt, + remain_cnt); + } + + /*ep3 enable, clear nak, bulk, usb active, next ep3, max pkt 64*/ + writel(1u<<31|1<<26|2<<18|1<<15|otg.bulkout_max_pktsize<<0, + S5P_OTG_DOEPCTL_OUT); + } else { + DBG_BULK1("DMA OUT : Transfer Complete\n"); + udelay(500); /*for FPGA ???*/ + } +} + +void s3c_usb_set_all_outep_nak(void) +{ + u8 i; + u32 tmp; + + for(i=0;i<16;i++) + { + tmp = readl(S5P_OTG_DOEPCTL0+0x20*i); + tmp |= DEPCTL_SNAK; + writel(tmp, S5P_OTG_DOEPCTL0+0x20*i); + } +} + +void s3c_usb_clear_all_outep_nak(void) +{ + u8 i; + u32 tmp; + + for(i=0;i<16;i++) + { + tmp = readl(S5P_OTG_DOEPCTL0+0x20*i); + tmp |= (DEPCTL_EPENA|DEPCTL_CNAK); + writel(tmp, S5P_OTG_DOEPCTL0+0x20*i); + } +} + +void s3c_usb_set_max_pktsize(USB_SPEED speed) +{ + if (speed == USB_HIGH) + { + otg.speed = USB_HIGH; + otg.ctrl_max_pktsize = HS_CTRL_PKT_SIZE; + otg.bulkin_max_pktsize = HS_BULK_PKT_SIZE; + otg.bulkout_max_pktsize = HS_BULK_PKT_SIZE; + } + else + { + otg.speed = USB_FULL; + otg.ctrl_max_pktsize = FS_CTRL_PKT_SIZE; + otg.bulkin_max_pktsize = FS_BULK_PKT_SIZE; + otg.bulkout_max_pktsize = FS_BULK_PKT_SIZE; + } +} + +void s3c_usb_set_endpoint(void) +{ + /* Unmask S5P_OTG_DAINT source */ + writel(0xff, S5P_OTG_DIEPINT0); + writel(0xff, S5P_OTG_DOEPINT0); + writel(0xff, S5P_OTG_DIEPINT_IN); + writel(0xff, S5P_OTG_DOEPINT_OUT); + + /* Init For Ep0*/ + /*MPS:64bytes */ + writel(((1<<26)|(CONTROL_EP<<11)|(0<<0)), S5P_OTG_DIEPCTL0); + /*ep0 enable, clear nak */ + writel((1u<<31)|(1<<26)|(0<<0), S5P_OTG_DOEPCTL0); +} + +void s3c_usb_set_descriptors(void) +{ + /* Standard device descriptor */ + otg.desc.dev.bLength=DEVICE_DESC_SIZE; /*0x12*/ + otg.desc.dev.bDescriptorType=DEVICE_DESCRIPTOR; + otg.desc.dev.bDeviceClass=0xFF; /* 0x0*/ + otg.desc.dev.bDeviceSubClass=0x0; + otg.desc.dev.bDeviceProtocol=0x0; + otg.desc.dev.bMaxPacketSize0=otg.ctrl_max_pktsize; + otg.desc.dev.idVendorL=0xE8; /*0x45;*/ + otg.desc.dev.idVendorH=0x04; /*0x53;*/ + otg.desc.dev.idProductL=0x34; /*0x00*/ + otg.desc.dev.idProductH=0x12; /*0x64*/ + otg.desc.dev.bcdDeviceL=0x00; + otg.desc.dev.bcdDeviceH=0x01; + otg.desc.dev.iManufacturer=0x1; /* index of string descriptor */ + otg.desc.dev.iProduct=0x2; /* index of string descriptor */ + otg.desc.dev.iSerialNumber=0x0; + otg.desc.dev.bNumConfigurations=0x1; + if (otg.speed == USB_FULL) { + otg.desc.dev.bcdUSBL=0x10; + otg.desc.dev.bcdUSBH=0x01; /* Ver 1.10*/ + } + else { + otg.desc.dev.bcdUSBL=0x00; + otg.desc.dev.bcdUSBH=0x02; /* Ver 2.0*/ + } + + /* Standard configuration descriptor */ + otg.desc.config.bLength=CONFIG_DESC_SIZE; /* 0x9 bytes */ + otg.desc.config.bDescriptorType=CONFIGURATION_DESCRIPTOR; + otg.desc.config.wTotalLengthL=CONFIG_DESC_TOTAL_SIZE; + otg.desc.config.wTotalLengthH=0; + otg.desc.config.bNumInterfaces=1; +/* dbg descConf.bConfigurationValue=2; // why 2? There's no reason.*/ + otg.desc.config.bConfigurationValue=1; + otg.desc.config.iConfiguration=0; + otg.desc.config.bmAttributes=CONF_ATTR_DEFAULT|CONF_ATTR_SELFPOWERED; /* bus powered only.*/ + otg.desc.config.maxPower=25; /* draws 50mA current from the USB bus.*/ + + /* Standard interface descriptor */ + otg.desc.intf.bLength=INTERFACE_DESC_SIZE; /* 9*/ + otg.desc.intf.bDescriptorType=INTERFACE_DESCRIPTOR; + otg.desc.intf.bInterfaceNumber=0x0; + otg.desc.intf.bAlternateSetting=0x0; /* ?*/ + otg.desc.intf.bNumEndpoints = 2; /* # of endpoints except EP0*/ + otg.desc.intf.bInterfaceClass=0xff; /* 0x0 ?*/ + otg.desc.intf.bInterfaceSubClass=0x0; + otg.desc.intf.bInterfaceProtocol=0x0; + otg.desc.intf.iInterface=0x0; + + /* Standard endpoint0 descriptor */ + otg.desc.ep1.bLength=ENDPOINT_DESC_SIZE; + otg.desc.ep1.bDescriptorType=ENDPOINT_DESCRIPTOR; + otg.desc.ep1.bEndpointAddress=BULK_IN_EP|EP_ADDR_IN; + otg.desc.ep1.bmAttributes=EP_ATTR_BULK; + otg.desc.ep1.wMaxPacketSizeL=(u8)otg.bulkin_max_pktsize; /* 64*/ + otg.desc.ep1.wMaxPacketSizeH=(u8)(otg.bulkin_max_pktsize>>8); + otg.desc.ep1.bInterval=0x0; /* not used */ + + /* Standard endpoint1 descriptor */ + otg.desc.ep2.bLength=ENDPOINT_DESC_SIZE; + otg.desc.ep2.bDescriptorType=ENDPOINT_DESCRIPTOR; + otg.desc.ep2.bEndpointAddress=BULK_OUT_EP|EP_ADDR_OUT; + otg.desc.ep2.bmAttributes=EP_ATTR_BULK; + otg.desc.ep2.wMaxPacketSizeL=(u8)otg.bulkout_max_pktsize; /* 64*/ + otg.desc.ep2.wMaxPacketSizeH=(u8)(otg.bulkout_max_pktsize>>8); + otg.desc.ep2.bInterval=0x0; /* not used */ +} + +void s3c_usb_check_speed(USB_SPEED *speed) +{ + u32 status; + + status = readl(S5P_OTG_DSTS); /* System status read */ + + *speed = (USB_SPEED)((status&0x6) >>1); +} + +void s3c_usb_clear_dnfile_info(void) +{ + otg.dn_addr = 0; + otg.dn_filesize = 0; + otg.dn_ptr = 0; +} + +void s3c_usb_clear_upfile_info(void) +{ + otg.up_addr= 0; + otg.up_size= 0; + otg.up_ptr = 0; +} + + +int s3c_usb_check_setconf(void) +{ + if (otg.set_config == 0) + return FALSE; + else + return TRUE; +} + +void s3c_usb_set_opmode(USB_OPMODE mode) +{ + otg.op_mode = mode; + + writel(INT_RESUME|INT_OUT_EP|INT_IN_EP|INT_ENUMDONE| + INT_RESET|INT_SUSPEND|INT_RX_FIFO_NOT_EMPTY, + S5P_OTG_GINTMSK); /*gint unmask */ + + writel(MODE_SLAVE|BURST_SINGLE|GBL_INT_UNMASK, S5P_OTG_GAHBCFG); + + s3c_usb_set_outep_xfersize(EP_TYPE_BULK, 1, otg.bulkout_max_pktsize); + s3c_usb_set_inep_xfersize(EP_TYPE_BULK, 1, 0); + + /*bulk out ep enable, clear nak, bulk, usb active, next ep3, max pkt */ + writel(1u<<31|1<<26|2<<18|1<<15|otg.bulkout_max_pktsize<<0, + S5P_OTG_DOEPCTL_OUT); + + /*bulk in ep enable, clear nak, bulk, usb active, next ep1, max pkt */ + writel(0u<<31|1<<26|2<<18|1<<15|otg.bulkin_max_pktsize<<0, + S5P_OTG_DIEPCTL_IN); +} + +void s3c_usb_reset(void) +{ + s3c_usb_set_all_outep_nak(); + + otg.ep0_state = EP0_STATE_INIT; + writel(((1<>1) == USB_HIGH) { + DBG_SETUP1("High Speed Detection\n"); + s3c_usb_set_max_pktsize(USB_HIGH); + } + else if(((status&0x6) >>1) == USB_FULL) { + DBG_SETUP1("Full Speed Detec tion\n"); + s3c_usb_set_max_pktsize(USB_FULL); + } + else { + printf("**** Error:Neither High_Speed nor Full_Speed\n"); + return FALSE; + } + + s3c_usb_set_endpoint(); +/* if (is_fastboot) + fboot_usb_set_descriptors(); + else +*/ s3c_usb_set_descriptors(); + s3c_usb_clear_dnfile_info(); + s3c_usb_set_opmode(op_mode); + + return TRUE; +} + +void s3c_usb_pkt_receive(void) +{ + u32 rx_status; + u32 fifo_cnt_byte; + + rx_status = readl(S5P_OTG_GRXSTSP); + DBG_SETUP0("S5P_OTG_GRXSTSP = 0x%x\n", rx_status); + + if ((rx_status & (0xf<<17)) == SETUP_PKT_RECEIVED) { + DBG_SETUP1("SETUP_PKT_RECEIVED\n"); + s3c_usb_ep0_int_hndlr(); + + } else if ((rx_status & (0xf<<17)) == OUT_PKT_RECEIVED) { + fifo_cnt_byte = (rx_status & 0x7ff0)>>4; + DBG_SETUP1("OUT_PKT_RECEIVED\n"); + + if((rx_status & BULK_OUT_EP)&&(fifo_cnt_byte)) { +/* if (is_fastboot) + fboot_usb_int_bulkout(fifo_cnt_byte); + else +*/ s3c_usb_int_bulkout(fifo_cnt_byte); + if( otg.op_mode == USB_CPU ) + writel(INT_RESUME|INT_OUT_EP|INT_IN_EP| + INT_ENUMDONE|INT_RESET|INT_SUSPEND| + INT_RX_FIFO_NOT_EMPTY, + S5P_OTG_GINTMSK); + return; + } + + } else if ((rx_status & (0xf<<17)) == GLOBAL_OUT_NAK) { + DBG_SETUP1("GLOBAL_OUT_NAK\n"); + + } else if ((rx_status & (0xf<<17)) == OUT_TRNASFER_COMPLETED) { + DBG_SETUP1("OUT_TRNASFER_COMPLETED\n"); + + } else if ((rx_status & (0xf<<17)) == SETUP_TRANSACTION_COMPLETED) { + DBG_SETUP1("SETUP_TRANSACTION_COMPLETED\n"); + + } else { + DBG_SETUP1("Reserved\n"); + } +} + +void s3c_usb_transfer(void) +{ + u32 ep_int; + u32 check_dma; + u32 ep_int_status; + + ep_int = readl(S5P_OTG_DAINT); + DBG_SETUP0("S5P_OTG_DAINT = 0x%x", ep_int); + + if (ep_int & (1< +#include +#include + +#define S3C_USBD_DETECT_IRQ() (readl(S5P_OTG_GINTSTS) & \ + (GINTSTS_WkUpInt|GINTSTS_OEPInt|GINTSTS_IEPInt| \ + GINTSTS_EnumDone|GINTSTS_USBRst|GINTSTS_USBSusp|GINTSTS_RXFLvl)) +#define S3C_USBD_CLEAR_IRQ() do { \ + writel(BIT_ALLMSK, (S5P_OTG_GINTSTS)); \ + } while (0) + +#define CONTROL_EP 0 +#define BULK_IN_EP 1 +#define BULK_OUT_EP 2 + +#define FS_CTRL_PKT_SIZE 64 +#define FS_BULK_PKT_SIZE 64 + +#define HS_CTRL_PKT_SIZE 64 +#define HS_BULK_PKT_SIZE 512 + +#define RX_FIFO_SIZE 512 +#define NPTX_FIFO_START_ADDR RX_FIFO_SIZE +#define NPTX_FIFO_SIZE 512 +#define PTX_FIFO_SIZE 512 + +// string descriptor +#define LANGID_US_L (0x09) +#define LANGID_US_H (0x04) + +// Feature Selectors +#define EP_STALL 0 +#define DEVICE_REMOTE_WAKEUP 1 +#define TEST_MODE 2 + +/* Test Mode Selector*/ +#define TEST_J 1 +#define TEST_K 2 +#define TEST_SE0_NAK 3 +#define TEST_PACKET 4 +#define TEST_FORCE_ENABLE 5 + +#define S5P_OTG_DIEPCTL_IN (S5P_OTG_DIEPCTL0 + 0x20*BULK_IN_EP) +#define S5P_OTG_DIEPINT_IN (S5P_OTG_DIEPINT0 + 0x20*BULK_IN_EP) +#define S5P_OTG_DIEPTSIZ_IN (S5P_OTG_DIEPTSIZ0 + 0x20*BULK_IN_EP) +#define S5P_OTG_DIEPDMA_IN (S5P_OTG_DIEPDMA0 + 0x20*BULK_IN_EP) +#define S5P_OTG_DOEPCTL_OUT (S5P_OTG_DOEPCTL0 + 0x20*BULK_OUT_EP) +#define S5P_OTG_DOEPINT_OUT (S5P_OTG_DOEPINT0 + 0x20*BULK_OUT_EP) +#define S5P_OTG_DOEPTSIZ_OUT (S5P_OTG_DOEPTSIZ0 + 0x20*BULK_OUT_EP) +#define S5P_OTG_DOEPDMA_OUT (S5P_OTG_DOEPDMA0 + 0x20*BULK_OUT_EP) +#define S5P_OTG_IN_FIFO (S5P_OTG_EP0_FIFO + 0x1000*BULK_IN_EP) +#define S5P_OTG_OUT_FIFO (S5P_OTG_EP0_FIFO + 0x1000*BULK_OUT_EP) + + +typedef struct +{ + u8 bLength; + u8 bDescriptorType; + u8 bcdUSBL; + u8 bcdUSBH; + u8 bDeviceClass; + u8 bDeviceSubClass; + u8 bDeviceProtocol; + u8 bMaxPacketSize0; + u8 idVendorL; + u8 idVendorH; + u8 idProductL; + u8 idProductH; + u8 bcdDeviceL; + u8 bcdDeviceH; + u8 iManufacturer; + u8 iProduct; + u8 iSerialNumber; + u8 bNumConfigurations; +} __attribute__ ((packed)) device_desc_t; + +typedef struct +{ + u8 bLength; + u8 bDescriptorType; + u8 wTotalLengthL; + u8 wTotalLengthH; + u8 bNumInterfaces; + u8 bConfigurationValue; + u8 iConfiguration; + u8 bmAttributes; + u8 maxPower; +} __attribute__ ((packed)) config_desc_t; + +typedef struct +{ + u8 bLength; + u8 bDescriptorType; + u8 bInterfaceNumber; + u8 bAlternateSetting; + u8 bNumEndpoints; + u8 bInterfaceClass; + u8 bInterfaceSubClass; + u8 bInterfaceProtocol; + u8 iInterface; +} __attribute__ ((packed)) intf_desc_t; + +typedef struct +{ + u8 bLength; + u8 bDescriptorType; + u8 bEndpointAddress; + u8 bmAttributes; + u8 wMaxPacketSizeL; + u8 wMaxPacketSizeH; + u8 bInterval; +} __attribute__ ((packed)) ep_desc_t; + +typedef struct +{ + u8 bmRequestType; + u8 bRequest; + u8 wValue_L; + u8 wValue_H; + u8 wIndex_L; + u8 wIndex_H; + u8 wLength_L; + u8 wLength_H; +} __attribute__ ((packed)) device_req_t; + +typedef struct +{ + device_desc_t dev; + config_desc_t config; + intf_desc_t intf; + ep_desc_t ep1; + ep_desc_t ep2; + ep_desc_t ep3; + ep_desc_t ep4; +} __attribute__ ((packed)) descriptors_t; + +typedef struct +{ + u8 Device; + u8 Interface; + u8 ep_ctrl; + u8 ep_in; + u8 ep_out; +} __attribute__ ((packed)) get_status_t; + +typedef struct +{ + u8 AlternateSetting; +} __attribute__ ((packed)) get_intf_t; + + +typedef enum +{ + USB_CPU, USB_DMA +} USB_OPMODE; + +typedef enum +{ + USB_HIGH, USB_FULL, USB_LOW +} USB_SPEED; + +typedef enum +{ + EP_TYPE_CONTROL, EP_TYPE_ISOCHRONOUS, EP_TYPE_BULK, EP_TYPE_INTERRUPT +} EP_TYPE; + + +typedef struct +{ + descriptors_t desc; + device_req_t dev_req; + + u32 ep0_state; + u32 ep0_substate; + USB_OPMODE op_mode; + USB_SPEED speed; + u32 ctrl_max_pktsize; + u32 bulkin_max_pktsize; + u32 bulkout_max_pktsize; + u32 dn_addr; + u32 dn_filesize; + u32 up_addr; + u32 up_size; + u8* dn_ptr; + u8* up_ptr; + u32 set_config; + u32 req_length; +} __attribute__ ((packed)) otg_dev_t; + +// SPEC1.1 + +// Standard bmRequestType (direction) +enum DEV_REQUEST_DIRECTION +{ + HOST_TO_DEVICE = 0x00, + DEVICE_TO_HOST = 0x80 +}; + +// Standard bmRequestType (Type) +enum DEV_REQUEST_TYPE +{ + STANDARD_TYPE = 0x00, + CLASS_TYPE = 0x20, + VENDOR_TYPE = 0x40, + RESERVED_TYPE = 0x60 +}; + +// Standard bmRequestType (Recipient) +enum DEV_REQUEST_RECIPIENT +{ + DEVICE_RECIPIENT = 0, + INTERFACE_RECIPIENT = 1, + ENDPOINT_RECIPIENT = 2, + OTHER_RECIPIENT = 3 +}; + +// Descriptor types +enum DESCRIPTOR_TYPE +{ + DEVICE_DESCRIPTOR = 1, + CONFIGURATION_DESCRIPTOR = 2, + STRING_DESCRIPTOR = 3, + INTERFACE_DESCRIPTOR = 4, + ENDPOINT_DESCRIPTOR = 5, + DEVICE_QUALIFIER = 6, + OTHER_SPEED_CONFIGURATION = 7, + INTERFACE_POWER = 8, +}; + +// configuration descriptor: bmAttributes +enum CONFIG_ATTRIBUTES +{ + CONF_ATTR_DEFAULT = 0x80, + CONF_ATTR_REMOTE_WAKEUP = 0x20, + CONF_ATTR_SELFPOWERED = 0x40 +}; + +// endpoint descriptor +enum ENDPOINT_ATTRIBUTES +{ + EP_ADDR_IN = 0x80, + EP_ADDR_OUT = 0x00, + + EP_ATTR_CONTROL = 0x0, + EP_ATTR_ISOCHRONOUS = 0x1, + EP_ATTR_BULK = 0x2, + EP_ATTR_INTERRUPT = 0x3 +}; + +// Standard bRequest codes +enum STANDARD_REQUEST_CODE +{ + STANDARD_GET_STATUS = 0, + STANDARD_CLEAR_FEATURE = 1, + STANDARD_RESERVED_1 = 2, + STANDARD_SET_FEATURE = 3, + STANDARD_RESERVED_2 = 4, + STANDARD_SET_ADDRESS = 5, + STANDARD_GET_DESCRIPTOR = 6, + STANDARD_SET_DESCRIPTOR = 7, + STANDARD_GET_CONFIGURATION = 8, + STANDARD_SET_CONFIGURATION = 9, + STANDARD_GET_INTERFACE = 10, + STANDARD_SET_INTERFACE = 11, + STANDARD_SYNCH_FRAME = 12 +}; + +int s3c_usbctl_init(void); +int s3c_usbc_activate (void); +int s3c_usb_stop( void ); +int s3c_udc_int_hndlr(void); + +/* in usbd-otg-hs.c */ +extern unsigned int s3c_usbd_dn_addr; +extern unsigned int s3c_usbd_dn_cnt; +extern int DNW; +extern int s3c_got_header; +extern int s3c_receive_done; + +#endif diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 6c9479492..d248be237 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk LIB := $(obj)libusb_host.o # ohci +COBJS-$(CONFIG_S3C_USBD) += usb_ohci.o COBJS-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o COBJS-$(CONFIG_USB_ATMEL) += ohci-at91.o COBJS-$(CONFIG_USB_OHCI_DA8XX) += ohci-da8xx.o diff --git a/drivers/usb/host/usb_ohci.c b/drivers/usb/host/usb_ohci.c new file mode 100644 index 000000000..734709c0c --- /dev/null +++ b/drivers/usb/host/usb_ohci.c @@ -0,0 +1,750 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the S3C24XX. + * + * (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 + * + * 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 NOTES + * 1 - you MUST define LITTLEENDIAN in the configuration file for the + * board or this driver will NOT work! + * 2 - this driver is intended for use with USB Mass Storage Devices + * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + */ + +#include + +#ifdef CONFIG_USB_OHCI + +#include +#include + +#include + +#include "usb_ohci.h" + + +#define OHCI_USE_NPS /* force NoPowerSwitching mode */ +#define OHCI_VERBOSE_DEBUG /* not always helpful */ + + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +//#define readl(a) (*((vu_long *)(a))) +//#define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a)) + +#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) + +#undef DEBUG_USB_OHCI +#ifdef DEBUG_USB_OHCI +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while(0) +#endif /* DEBUG_USB_OHCI */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#undef SHOW_INFO +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while(0) +#endif + +#define m16_swap(x) le16_to_cpu(x) +#define m32_swap(x) le32_to_cpu(x) + +/* global ohci_t */ +static ohci_t gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* urb_priv */ +urb_priv_t urb_priv; +/* RHSC flag */ +int got_rhsc; +/* device which was disconnected */ +struct usb_device *devgone; + +/*-------------------------------------------------------------------------*/ + +/* AMD-756 (D2 rev) reports corrupt register contents in some cases. + * The erratum (#4) description is incorrect. AMD's workaround waits + * till some bits (mostly reserved) are clear; ok for all revs. + */ +#define OHCI_QUIRK_AMD756 0xabcd +#define read_roothub(hc, register, mask) ({ \ + u32 temp = readl (&hc->regs->roothub.register); \ + if (hc->flags & OHCI_QUIRK_AMD756) \ + while (temp & mask) \ + temp = readl (&hc->regs->roothub.register); \ + temp; }) + + static inline u32 roothub_b (struct ohci *hc) + { return readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci *hc) + { return readl (&hc->regs->roothub.status); } + + +/* forward declaration */ +static void +td_submit_job (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; + int last; + struct td * td; + + 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; + } + } + } +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG_USB_OHCI +static int sohci_get_current_frame_number (struct usb_device * dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer, + int transfer_len, struct devrequest * setup, char * str, int small) +{ + urb_priv_t * purb = &urb_priv; + + dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", + str, + sohci_get_current_frame_number (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->actual_length, + transfer_len, dev->status); +#ifdef OHCI_VERBOSE_DEBUG + if (!small) { + int i, len; + + if (usb_pipecontrol (pipe)) { + printf (__FILE__ ": cmd(8):"); + for (i = 0; i < 8 ; i++) + printf (" %02x", ((__u8 *) setup) [i]); + printf ("\n"); + } + if (transfer_len > 0 && buffer) { + printf (__FILE__ ": data(%d/%d):", + purb->actual_length, + transfer_len); + len = usb_pipeout (pipe)? + transfer_len: purb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printf (" %02x", ((__u8 *) buffer) [i]); + printf ("%s\n", i < len? "...": ""); + } + } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ +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; + printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i); + while (*ed_p != 0 && j--) { + ed_t *ed = (ed_t *)m32_swap(ed_p); + printf (" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } + printf ("\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 (char *label, __u32 value) +{ + ed_t *edp = (ed_t *)value; + + if (value) { + 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; + + 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 ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("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 *controller, int verbose) +{ + dbg ("OHCI controller usb-%s state", controller->slot_name); + + /* dumps some of the state we know about */ + ohci_dump_status (controller); + if (verbose) + ep_print_int_eds (controller, "hcca"); + dbg ("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub (controller, 1); +} + + +#endif /* DEBUG_USB_OHCI */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + ohci_t *ohci; + ed_t * ed; + urb_priv_t *purb_priv; + int i, size = 0; + + ohci = &gohci; + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + err("sohci_submit_job: EPIPE"); + return -1; + } + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (dev, pipe))) { + 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; + } + + if (size >= (N_URB_TD - 1)) { + err("need %d TDs, only have %d", size, N_URB_TD); + return -1; + } + purb_priv = &urb_priv; + 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(dev, pipe, buffer, transfer_len, setup, purb_priv, interval); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG_USB_OHCI +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number (struct usb_device *usb_dev) +{ + ohci_t *ohci = &gohci; + + return m16_swap (ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * 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; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { + writel (ed, &ohci->regs->ed_controlhead); + } else { + ohci->ed_controltail->hwNextED = m32_swap (ed); + } + 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->regs->ed_bulkhead); + } else { + ohci->ed_bulktail->hwNextED = m32_swap (ed); + } + 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; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* 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 */ + +/*-------------------------------------------------------------------------*/ + +/* 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 (struct usb_device *usb_dev, unsigned long pipe) +{ + td_t *td; + ed_t *ed_ret; + volatile ed_t *ed; + + 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) { + ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */ + /* dummy td; end of td list for ed */ + td = td_alloc (usb_dev); + ed->hwTailP = m32_swap (td); + 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); + + 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); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->data = (__u32)data; +#ifdef OHCI_FILL_TRACE + if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) { + for (i = 0; i < len; i++) + printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]); + printf("\n"); + } +#endif + if (!len) + data = 0; + + td->hwINFO = m32_swap (info); + td->hwCBP = m32_swap (data); + if (data) + td->hwBE = m32_swap (data + len - 1); + else + td->hwBE = 0; + td->hwNextTD = m32_swap (td_pt); + td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ +static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) +{ + ohci_t *ohci = &gohci; + 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 = 0; + + 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) + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + break; + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill (ohci, info, setup, 8, dev, cnt++, urb); + 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); + } + 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) + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + break; + } + if (urb->length != cnt) + dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ +/* Hub class-specific descriptor is constructed dynamically */ + + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#ifdef DEBUG_USB_OHCI +#define WR_RH_STAT(x) {info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);} +#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);} +#else +#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x) writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT roothub_status(&gohci) +#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(ohci_t *controller) +{ + return 0; +} + + /*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + return 0; +} + +/* submit routines called from usb.c */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len) +{ + info("submit_bulk_msg"); + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup) +{ + return 0; +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + info("submit_int_msg"); + return -1; +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ +int usb_lowlevel_init(int index, void **controller) +{ + return 0; +} + +int usb_lowlevel_stop(int index) +{ + return 0; +} + +#endif /* CONFIG_USB_OHCI */ diff --git a/drivers/usb/host/usb_ohci.h b/drivers/usb/host/usb_ohci.h new file mode 100644 index 000000000..cadef9e22 --- /dev/null +++ b/drivers/usb/host/usb_ohci.h @@ -0,0 +1,397 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * usb-ohci.h + */ + +#include + +/* 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; + __u32 unused[3]; +} __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 */ + __u16 frame_no; /* current frame number */ + __u16 pad1; /* set to 0 on each frame_no change */ + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute((aligned(256))); + + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* + * 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[MAX_ROOT_PORTS]; + } roothub; +} __attribute((aligned(32))); + + +/* 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 */ + int state; + unsigned long pipe; + int actual_length; + td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +/* + * 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 { + struct ohci_hcca *hcca; /* hcca */ + /*dma_addr_t hcca_dma;*/ + + int irq; + int disabled; /* e.g. got a UE, we're hung */ + int sleeping; + unsigned long flags; /* for HC bugs */ + + struct ohci_regs *regs; /* OHCI controller's memory */ + + 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 usb_device *dev[32]; + struct virt_root_hub rh; + + const char *slot_name; +} ohci_t; + +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +struct ohci_device { + ed_t ed[NUM_EDS]; + int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +td_t gtd[NUM_TD+1]; +/* 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; +} -- cgit v1.2.3