diff options
58 files changed, 11396 insertions, 5 deletions
diff --git a/Documentation/usb/s3c-otg-host.txt b/Documentation/usb/s3c-otg-host.txt new file mode 100644 index 0000000..97e7f91 --- /dev/null +++ b/Documentation/usb/s3c-otg-host.txt @@ -0,0 +1,83 @@ +This file is a collection of notes on using the USB OTG port of the Samsung Galaxy Tab in HOST mode. + +1/17/12 - Zsolt Sz. Sztupak, mail@sztupy.hu, http://android.sztupy.hu + +Kevin's patch to the kernel was great, but it had some problems, namely: +* it was based on an old Froyo (or Eclair?) branch of the kernel, which still had a lot of old code, and methods + that were deprecated in later kernel versions +* the actual client/host changing code was put inside the 30pin connector file, which only exists in the Galaxy + Tab based Kernels + +The changes I've made are the following: +* port some of the deprecated code to kernel version 3.x +* change the otg detector/switcher code from the 30 pin connector module to the s3c otg gadget (client mode) module + +There is still a lof code not ported from the old kernel branch as it uses a lot of ugly old samsung kernel code, +which are non existent in later kernel versions. These include interrupt, LDO and clock switchings, which might +be the case of some of the hangups. + +8/24/11 - Kevin Hester, kevin@ridemission.com + +I'm writing this document to capture both the software and hardware changes needed for this device in one place. +If you are a brave Android kernel hacker, please try these changes out and send pull requests to github with any +fixes you add. I have been unable to find a 'master' github site where hobbyists are maintaining a master Samsung +kernel and Android OS, if you have such a site feel free to include my fixes (though credit would be appreciated). +These fixes are provided 'as-is' and you could bust your device or do any number of bad things. + +I'm going to post these notes on the xda forums, but for the latest code and documentation please see my github site. + +History: The Samsung open source kernel files (from opensource.samsung.com) contained a USB host mode driver for the +S5PC110 chipset. These drivers were located in drivers/usb/host/s3c-otg. The driver contained a number of bugs which +I've fixed and it now seems to work reasonably well for USB serial ports, flash drives etc. + +Hardware: To use USB host mode on your samsung tablet _external 5V DC seems to be required_. I have not found +turning any of the samsung LDOs on to make the unit provide USB power (if you find different, please let me know). + +To wire up a USB host mode cable you'll need the following pinout (found on a web forum): +1 Gnd P +2 Gnd P +3 USB_DP_CON I/O +4 USB_DM_CON I/O +5 IF_CON_SENSE I +6 V_ACCESSORY_5.0V P +7 V_BUS_1 P +8 V_BUS_1 P +9 VOUT_CHARGER P (gives out 4v when checked with a multimeter) +10 VOUT_CHARGER P (gives out 4v when checked with a multimeter) +11 --- -- +12 --- -- +13 ACCESSORY_ID / USB_ID I +14 ACCESSORY_INT I +15 Gnd P +16 Gnd P +17 MHL_DP I/O +18 MHL_DM I/O +19 MHL_ID I +20 IF_RXD I +21 IF_TXD O +22 --- -- +23 AP_TV_OUT O +24 REMOTE_SENSE I +25 --- -- +26 --- -- +27 EAR_L_CRADLE O +28 EAR_R_CRADLE O +29 3.5_INT_TEST I +30 Gnd P + +Your cable will need to connect the following five pins: +1 Gnd +3 USB_DP +4 USB_DM +8 5V+ (at least 1A if you want to support high speed charging of the tablet) +13 Host mode (attach to ground to run tablet as a host, or leave disconnected to run tablet as a USB target) + +A summary of my driver changes: +* Fix a nubmer of cases where TDs would be used after delete_td and the associated storage was freed (caused kernel +heap corruption) +* Allow a bit more time for some mystery Samsung LDO to power up before switching to host mode (caused failure in device detect) +* Wait for channel disabled interrupt when cancelling transactions (prevents a race condition with the ISR) +* Properly switch into USB host mode when a host mode cable is detected (see 30pin_con.c) +* Mark transfers as done when cancel_to_transfer_td is called (prevents rescheduling transactions we have freed) +* do not force is_need_to_insert_scheduler true in cancel_transfer, this caused list corruption in the ed list + diff --git a/arch/arm/configs/herring_defconfig b/arch/arm/configs/herring_defconfig index 13a1c39..141ee8d 100644 --- a/arch/arm/configs/herring_defconfig +++ b/arch/arm/configs/herring_defconfig @@ -203,7 +203,9 @@ CONFIG_PPP_BSDCOMP=y CONFIG_PPP_MPPE=y CONFIG_PPPOLAC=y CONFIG_PPPOPNS=y -# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=800 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480 CONFIG_INPUT_EVDEV=y # CONFIG_KEYBOARD_ATKBD is not set CONFIG_KEYPAD_CYPRESS_TOUCH=y @@ -334,6 +336,16 @@ CONFIG_USB_GADGET=y CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_G_ANDROID=y CONFIG_USB_ANDROID_RNDIS_DWORD_ALIGNED=y +CONFIG_USB=y +CONFIG_USB_S3C_OTG_HOST=y +CONFIG_USB_DEBUG=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_CONSOLE=y +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_FTDI_SIO=y +CONFIG_USB_ACM=y CONFIG_MMC=y CONFIG_MMC_UNSAFE_RESUME=y CONFIG_MMC_SDHCI=y diff --git a/arch/arm/mach-s5pv210/mach-herring.c b/arch/arm/mach-s5pv210/mach-herring.c index e370212..1631bd6 100755 --- a/arch/arm/mach-s5pv210/mach-herring.c +++ b/arch/arm/mach-s5pv210/mach-herring.c @@ -5514,6 +5514,9 @@ static struct platform_device *herring_devices[] __initdata = { &herring_i2c11_device, /* optical sensor */ &herring_i2c12_device, /* magnetic sensor */ &herring_i2c14_device, /* nfc sensor */ +#ifdef CONFIG_USB_S3C_OTG_HOST + &s3c_device_usb_otghcd, +#endif #ifdef CONFIG_USB_GADGET &s3c_device_usbgadget, #endif @@ -5988,10 +5991,10 @@ void otg_phy_init(void) S3C_USBOTG_PHYCLK); writel((readl(S3C_USBOTG_RSTCON) & ~(0x3<<1)) | (0x1<<0), S3C_USBOTG_RSTCON); - msleep(1); + mdelay(1); writel(readl(S3C_USBOTG_RSTCON) & ~(0x7<<0), S3C_USBOTG_RSTCON); - msleep(1); + mdelay(1); /* rising/falling time */ writel(readl(S3C_USBOTG_PHYTUNE) | (0x1<<20), @@ -6049,6 +6052,44 @@ void usb_host_phy_off(void) EXPORT_SYMBOL(usb_host_phy_off); #endif +#ifdef CONFIG_USB_S3C_OTG_HOST + +/* Initializes OTG Phy */ +void otg_host_phy_init(void) +{ + __raw_writel(__raw_readl(S5P_USB_PHY_CONTROL) + |(0x1<<0), S5P_USB_PHY_CONTROL); /*USB PHY0 Enable */ +// from galaxy tab otg host: +// __raw_writel((__raw_readl(S3C_USBOTG_PHYPWR) +// &~(0x3<<3)&~(0x1<<0))|(0x1<<5), S3C_USBOTG_PHYPWR); +// from galaxy s2 otg host: + __raw_writel((__raw_readl(S3C_USBOTG_PHYPWR) + &~(0x7<<3)&~(0x1<<0)), S3C_USBOTG_PHYPWR); + __raw_writel((__raw_readl(S3C_USBOTG_PHYCLK) + &~(0x1<<4))|(0x7<<0), S3C_USBOTG_PHYCLK); + + __raw_writel((__raw_readl(S3C_USBOTG_RSTCON) + &~(0x3<<1))|(0x1<<0), S3C_USBOTG_RSTCON); + mdelay(1); + __raw_writel((__raw_readl(S3C_USBOTG_RSTCON) + &~(0x7<<0)), S3C_USBOTG_RSTCON); + mdelay(1); + + __raw_writel((__raw_readl(S3C_UDC_OTG_GUSBCFG) + |(0x3<<8)), S3C_UDC_OTG_GUSBCFG); + +// smb136_set_otg_mode(1); + + printk("otg_host_phy_int : USBPHYCTL=0x%x,PHYPWR=0x%x,PHYCLK=0x%x,USBCFG=0x%x\n", + readl(S5P_USB_PHY_CONTROL), + readl(S3C_USBOTG_PHYPWR), + readl(S3C_USBOTG_PHYCLK), + readl(S3C_UDC_OTG_GUSBCFG) + ); +} +EXPORT_SYMBOL(otg_host_phy_init); +#endif + MACHINE_START(HERRING, "herring") .boot_params = S5P_PA_SDRAM + 0x100, .fixup = herring_fixup, diff --git a/arch/arm/plat-s5p/devs.c b/arch/arm/plat-s5p/devs.c index f961d66..f5ff6ed 100644 --- a/arch/arm/plat-s5p/devs.c +++ b/arch/arm/plat-s5p/devs.c @@ -41,6 +41,37 @@ #include <mach/media.h> #include <s3cfb.h> +#ifdef CONFIG_USB_S3C_OTG_HOST +/* USB Device (OTG hcd)*/ +static struct resource s3c_usb_otghcd_resource[] = { + [0] = { + .start = S3C_PA_OTG, + .end = S3C_PA_OTG + S3C_SZ_OTG - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_OTG, + .end = IRQ_OTG, + .flags = IORESOURCE_IRQ, + } +}; + +static u64 s3c_device_usb_otghcd_dmamask = 0xffffffffUL; + +struct platform_device s3c_device_usb_otghcd = { + .name = "s3c_otghcd", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_usb_otghcd_resource), + .resource = s3c_usb_otghcd_resource, + .dev = { + .dma_mask = &s3c_device_usb_otghcd_dmamask, + .coherent_dma_mask = 0xffffffffUL + } +}; + +EXPORT_SYMBOL(s3c_device_usb_otghcd); +#endif + /* RTC */ static struct resource s5p_rtc_resource[] = { [0] = { diff --git a/arch/arm/plat-s5p/include/plat/s5p-otghost.h b/arch/arm/plat-s5p/include/plat/s5p-otghost.h new file mode 100644 index 0000000..5e4b6a9 --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/s5p-otghost.h @@ -0,0 +1,55 @@ +/* linux/arch/arm/plat-s5p/include/plat/s5p-otghost.h + * + * Copyright 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Platform header file for Samsung OTG Host driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ +#ifndef _PLAT_S5P_OTGHOST_H +#define _PLAT_S5P_OTGHOST_H __FILE__ + +#include <linux/wakelock.h> + +/*#define CONFIG_USB_S3C_OTG_HOST_HANDLING_CLOCK*/ +#define CONFIG_USB_S3C_OTG_HOST_DTGDRVVBUS + +union port_flags_t { + /** raw register data */ + u32 d32; + /** register bits */ + struct { + unsigned port_connect_status_change:1; + unsigned port_connect_status:1; + unsigned port_reset_change:1; + unsigned port_enable_change:1; + unsigned port_suspend_change:1; + unsigned port_over_current_change:1; + unsigned reserved:26; + } b; +}; + +struct sec_otghost_data { + bool clk_usage; + void (*set_pwr_cb)(int on); + int host_notify; + int sec_whlist_table_num; +}; + +struct sec_otghost { + spinlock_t lock; + + bool ch_halt; + union port_flags_t port_flag; + struct wake_lock wake_lock; + + struct work_struct work; + struct workqueue_struct *wq; + + struct sec_otghost_data *otg_data; +}; + +#endif /*_PLAT_S5P_OTGHOST_H */ diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h index 1170fb9..ed60a6e 100644 --- a/arch/arm/plat-samsung/include/plat/devs.h +++ b/arch/arm/plat-samsung/include/plat/devs.h @@ -114,6 +114,9 @@ extern struct platform_device s3c_device_android_usb; extern struct platform_device s3c_device_usb_mass_storage; extern struct platform_device s3c_device_rndis; extern struct platform_device s3c_device_usb_hsotg; +#ifdef CONFIG_USB_S3C_OTG_HOST +extern struct platform_device s3c_device_usb_otghcd; +#endif extern struct platform_device s5p_device_rotator; extern struct platform_device s5p_device_tvout; diff --git a/drivers/misc/fsa9480.c b/drivers/misc/fsa9480.c index 8098f82..1d5e08c 100755 --- a/drivers/misc/fsa9480.c +++ b/drivers/misc/fsa9480.c @@ -106,6 +106,10 @@ #define INT_DETACH (1 << 1) #define INT_ATTACH (1 << 0) +#ifdef CONFIG_USB_S3C_OTG_HOST +extern void set_otghost_mode(int mode); +#endif + struct fsa9480_usbsw { struct i2c_client *client; struct fsa9480_platform_data *pdata; @@ -266,6 +270,16 @@ static void fsa9480_detect_dev(struct fsa9480_usbsw *usbsw) dev_err(&client->dev, "%s: err %d\n", __func__, ret); } +#ifdef CONFIG_USB_S3C_OTG_HOST +// sztupy: handle automatic otg switching + if (val1 & DEV_USB_OTG) { + // otg cable detected + set_otghost_mode(2); + } else { + // client cable detected + set_otghost_mode(1); + } +#endif /* UART */ } else if (val1 & DEV_T1_UART_MASK || val2 & DEV_T2_UART_MASK) { if (pdata->uart_cb) @@ -320,6 +334,10 @@ static void fsa9480_detect_dev(struct fsa9480_usbsw *usbsw) usbsw->dev2 & DEV_T2_USB_MASK) { if (pdata->usb_cb) pdata->usb_cb(FSA9480_DETACHED); +#ifdef CONFIG_USB_S3C_OTG_HOST + // sztupy: also switch off otg host mode + set_otghost_mode(0); +#endif /* UART */ } else if (usbsw->dev1 & DEV_T1_UART_MASK || usbsw->dev2 & DEV_T2_UART_MASK) { diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 48f1781..8bf0a0b 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -168,4 +168,6 @@ source "drivers/usb/gadget/Kconfig" source "drivers/usb/otg/Kconfig" +source "drivers/usb/notify/Kconfig" + endif # USB_SUPPORT diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 30ddf8d..707fe92 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_MON) += mon/ obj-$(CONFIG_PCI) += host/ +obj-$(CONFIG_USB_S3C_OTG_HOST) += host/ obj-$(CONFIG_USB_EHCI_HCD) += host/ obj-$(CONFIG_USB_ISP116X_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/ @@ -51,3 +52,5 @@ obj-$(CONFIG_USB_MUSB_HDRC) += musb/ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/ obj-$(CONFIG_USB_OTG_UTILS) += otg/ obj-$(CONFIG_USB_GADGET) += gadget/ + +obj-$(CONFIG_USB_HOST_NOTIFY) += notify/ diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 45e0908..2fb9a29 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -156,7 +156,11 @@ static const u8 usb2_rh_dev_descriptor [18] = { 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ 0x00, /* __u8 bDeviceSubClass; */ +#ifdef CONFIG_USB_S3C_OTG_HOST + 0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ] */ +#else 0x00, /* __u8 bDeviceProtocol; [ usb 2.0 no TT ] */ +#endif 0x40, /* __u8 bMaxPacketSize0; 64 Bytes */ 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */ diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 60e51d4..9f30cf5 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -376,7 +376,7 @@ config USB_S3C_HSUDC config USB_GADGET_S3C_OTGD boolean "S3C HS USB OTG Device" - depends on (ARCH_S5PV210) && !(USB_S3C_OTG_HOST) + depends on (ARCH_S5PV210) help Samsung's S3C64XX processors include high speed USB OTG2.0 controller. It has 15 configurable endpoints, as well as @@ -653,7 +653,7 @@ comment "NOTE: S3C OTG device role enables the controller driver below" config USB_S3C_OTGD tristate "S3C high speed(2.0, dual-speed) USB OTG device" - depends on USB_GADGET && USB_GADGET_S3C_OTGD && !(USB_S3C_OTG_HOST) + depends on USB_GADGET && USB_GADGET_S3C_OTGD default y default USB_GADGET select USB_GADGET_SELECTED diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c index 9ebb7b7..96824a4 100644 --- a/drivers/usb/gadget/s3c_udc_otg.c +++ b/drivers/usb/gadget/s3c_udc_otg.c @@ -28,6 +28,12 @@ #include <plat/regs-otg.h> #include <linux/i2c.h> #include <linux/regulator/consumer.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/vmalloc.h> +#include <linux/proc_fs.h> +#include <asm/uaccess.h> #if defined(CONFIG_USB_GADGET_S3C_OTGD_DMA_MODE) /* DMA mode */ #define OTG_DMA_MODE 1 @@ -1179,6 +1185,108 @@ static struct s3c_udc memory = { }, }; +#ifdef CONFIG_USB_S3C_OTG_HOST +atomic_t g_OtgHostMode; // actual mode: client (0) or host (1) +atomic_t g_OtgOperationMode; // operation mode: 'c'lient, 'h'ost, 'o'tg or 'a'uto-host +atomic_t g_OtgLastCableState; // last cable state: detached (0), client attached (1), otg attached (2) +extern struct platform_driver s5pc110_otg_driver; + +int s3c_is_otgmode(void) +{ + printk("otgmode = %d\n", atomic_read(&g_OtgHostMode)); + return atomic_read(&g_OtgHostMode); +} +EXPORT_SYMBOL(s3c_is_otgmode); + +void set_otghost_mode(int mode) { +//sztupy: +// this function will determine what to do with the new information according to the current mode of operation +// mode: 0: cable detached, 1: client cable attached, 2: otg cable attached, -1: operation config changed +// modes of operation: c: always client, h: always host, g: otg mode (host if otg cable), a: automatic mode (host if cable plugged in) + + struct s3c_udc *dev = the_controller; + int enable = 0; + char opmode = atomic_read(&g_OtgOperationMode); + if (mode==-1) mode = atomic_read(&g_OtgLastCableState); + atomic_set(&g_OtgLastCableState, mode); + switch (opmode) { + case 'h': enable = 1; break; + case 'o': enable = (mode==2); break; + case 'a': enable = (mode>0); break; + default: atomic_set(&g_OtgOperationMode,'c'); enable = 0; break; + } + + if (enable && !atomic_read(&g_OtgHostMode)) { + printk("Setting OTG host mode\n"); + free_irq(IRQ_OTG, dev); + s3c_vbus_enable(&dev->gadget, 1); + + if (platform_driver_register(&s5pc110_otg_driver) < 0) + { + printk("platform_driver_register failed...\n"); + atomic_set(&g_OtgHostMode , 0); + } else { + printk("platform_driver_register...\n"); + atomic_set(&g_OtgHostMode , 1); + } + } else if (!enable && atomic_read(&g_OtgHostMode)) { +// sztupy: also handle the disabling here + printk("Disabling OTG host mode\n"); + s3c_vbus_enable(&dev->gadget, 0); + platform_driver_unregister(&s5pc110_otg_driver); + printk("platform_driver_unregistered\n"); + /* irq setup after old hardware state is cleaned up */ + if (request_irq(IRQ_OTG, s3c_udc_irq, 0, driver_name, dev)) { + printk("Warning: Could not request IRQ for USB gadget!\n"); + } + atomic_set(&g_OtgHostMode , 0); + } else { + printk("OTG: no changes needed\n"); + } +} + +EXPORT_SYMBOL(set_otghost_mode); + +static ssize_t usbmode_read(struct device *dev, struct device_attribute *attr, char *buf) +{ + const char *msg = "client"; + const char *msg2 = "disconnected"; + const char *msg3 = "gadget"; + switch(atomic_read(&g_OtgOperationMode)) { + case 'o': msg = "otg"; break; + case 'h': msg = "host"; break; + case 'a': msg = "auto-host"; break; + } + switch(atomic_read(&g_OtgLastCableState)) { + case 1: msg2 = "usb connected"; break; + case 2: msg2 = "otg connected"; break; + } + switch(atomic_read(&g_OtgHostMode)) { + case 1: msg3 = "host"; break; + } + + return sprintf(buf,"%s (cable: %s; state: %s)\n", msg, msg2, msg3); +} + +static ssize_t usbmode_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + char c; + printk("input data --> %s\n", buf); + c = buf[0]; + switch(c) { + case 'a': atomic_set(&g_OtgOperationMode, 'a'); set_otghost_mode(-1);break; + case 'h': atomic_set(&g_OtgOperationMode, 'h'); set_otghost_mode(-1);break; + case 'c': atomic_set(&g_OtgOperationMode, 'c'); set_otghost_mode(-1);break; + case 'o': atomic_set(&g_OtgOperationMode, 'o'); set_otghost_mode(-1);break; + default: printk("Invalid input data\n"); + } + return size; +} + +// kevinh - Allow changing USB host/target modes on S3C android devices without a special cable +static DEVICE_ATTR(opmode, S_IRUGO | S_IWUSR, usbmode_read, usbmode_write); +#endif + /* * probe - binds to the platform device */ @@ -1232,6 +1340,10 @@ static int s3c_udc_probe(struct platform_device *pdev) disable_irq(IRQ_OTG); create_proc_files(); +#ifdef CONFIG_USB_S3C_OTG_HOST + if (device_create_file(&pdev->dev, &dev_attr_opmode) < 0) + printk("Failed to create device file(%s)!\n", dev_attr_opmode.attr.name); +#endif return retval; } diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index ab085f1..67c452d 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -578,3 +578,19 @@ config USB_OCTEON_OHCI config USB_OCTEON2_COMMON bool default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI + +config USB_S3C_OTG_HOST + tristate "S3C USB OTG Host support" + depends on USB && (PLAT_S3C64XX || PLAT_S5P) + help + Samsung's S3C64XX processors include high speed USB OTG2.0 + controller. It has 15 configurable endpoints, as well as + endpoint zero (for control transfers). + + This driver support only OTG Host role. If you want to use + OTG Device role, select USB Gadget support and S3C OTG Device. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "s3c_otg_hcd" and force all + drivers to also be dynamically linked. + diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 624a362..de59025 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -15,6 +15,7 @@ xhci-hcd-y := xhci.o xhci-mem.o xhci-pci.o xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o obj-$(CONFIG_USB_WHCI_HCD) += whci/ +obj-$(CONFIG_USB_S3C_OTG_HOST) += s3c-otg/ obj-$(CONFIG_PCI) += pci-quirks.o diff --git a/drivers/usb/host/s3c-otg/Makefile b/drivers/usb/host/s3c-otg/Makefile new file mode 100644 index 0000000..37674c8 --- /dev/null +++ b/drivers/usb/host/s3c-otg/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for USB OTG Host Controller Drivers +# + +obj-$(CONFIG_USB_S3C_OTG_HOST) += s3c_otg_hcd.o + +s3c_otg_hcd-objs += s3c-otg-hcdi-driver.o s3c-otg-hcdi-hcd.o +s3c_otg_hcd-objs += s3c-otg-transfer-common.o s3c-otg-transfer-nonperiodic.o \ + s3c-otg-transfer-periodic.o +s3c_otg_hcd-objs += s3c-otg-scheduler-ischeduler.o s3c-otg-scheduler-scheduler.o \ + s3c-otg-scheduler-readyq.o +s3c_otg_hcd-objs += s3c-otg-oci.o +s3c_otg_hcd-objs += s3c-otg-transferchecker-common.o \ + s3c-otg-transferchecker-control.o \ + s3c-otg-transferchecker-bulk.o \ + s3c-otg-transferchecker-interrupt.o +s3c_otg_hcd-objs += s3c-otg-isr.o +s3c_otg_hcd-objs += s3c-otg-roothub.o diff --git a/drivers/usb/host/s3c-otg/debug-mem.c b/drivers/usb/host/s3c-otg/debug-mem.c new file mode 100644 index 0000000..de85853 --- /dev/null +++ b/drivers/usb/host/s3c-otg/debug-mem.c @@ -0,0 +1,55 @@ +#include "s3c-otg-hcdi-hcd.h" +#include "debug-mem.h" + +#define MAX_ALLOCS 1024 + +typedef void *ElemType; + +static ElemType alloced[MAX_ALLOCS]; +static int numalloced = 0; + +#define fail(args...) ( printk(args), dump_stack() ) + +void debug_alloc(void *addr) { + ElemType *freeloc = NULL; + ElemType *c = alloced; + int i; + + if(!addr) + fail("MEMD alloc of NULL"); + + for(i = 0; i < numalloced; i++, c++) { + if(*c == NULL && freeloc == NULL) + freeloc = c; + else if(*c == addr) + fail("MEMD multiple allocs of %p", addr); + } + + if(freeloc) + *freeloc = addr; + else { + if(numalloced >= MAX_ALLOCS) + fail("MEMD too many allocs"); + else { + alloced[numalloced++] = addr; + } + } +} + +void debug_free(void *addr) { + ElemType *c = alloced; + int i; + + if(!addr) + fail("free of NULL"); + + for(i = 0; i < numalloced; i++, c++) { + if(*c == addr) { + *c = NULL; + return; + } + } + + fail("MEMD freed addr %p was never alloced\n", addr); +} + diff --git a/drivers/usb/host/s3c-otg/debug-mem.h b/drivers/usb/host/s3c-otg/debug-mem.h new file mode 100644 index 0000000..4164793 --- /dev/null +++ b/drivers/usb/host/s3c-otg/debug-mem.h @@ -0,0 +1,9 @@ +#ifndef __DEBUGMEM_H +#define __DEBUGMEM_H + +// kevinh quick hack to see if the s3c stuff is doing memory properly + +void debug_alloc(void *addr); +void debug_free(void *addr); + +#endif diff --git a/drivers/usb/host/s3c-otg/s3c-otg-common-common.h b/drivers/usb/host/s3c-otg/s3c-otg-common-common.h new file mode 100644 index 0000000..720154e --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-common-common.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * @file s3c-otg-common-common.h + * @brief it includes common header files for all modules \n + * @version + * ex)-# Jun 11,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Creating the initial version of this code \n + * @see None + ****************************************************************************/ +/**************************************************************************** + * 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 _S3C_OTG_COMMON_COMMON_H_ +#define _S3C_OTG_COMMON_COMMON_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +//#include "s3c-otg-common-typedef.h" +#include "s3c-otg-common-errorcode.h" +#include <linux/errno.h> +#include <linux/usb.h> + +//Define OS +#define LINUX 1 + +//Kernel Version +#define KERNEL_2_6_21 + +#ifdef __cplusplus +} +#endif +#endif /* _S3C_OTG_COMMON_COMMON_H_ */ diff --git a/drivers/usb/host/s3c-otg/s3c-otg-common-const.h b/drivers/usb/host/s3c-otg/s3c-otg-common-const.h new file mode 100644 index 0000000..f0f5cb9 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-common-const.h @@ -0,0 +1,167 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : s3c-otg-common-const.h + * [Description] : The Header file defines constants to be used at sub-modules of S3C6400HCD. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/03 + * [Revision History] + * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created s3c-otg-common-const.h file and defines some constants. + * + ****************************************************************************/ +/**************************************************************************** + * 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 _CONST_TYPE_DEF_H_ +#define _CONST_TYPE_DEF_H_ + +/* +// ---------------------------------------------------------------------------- +// Include files : None. +// ---------------------------------------------------------------------------- +*/ + +#include "s3c-otg-common-common.h" + +//#include "s3c-otg-common-regdef.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @def OTG_PORT_NUMBER + * + * @brief write~ description + * + * describe in detail + */ +#define OTG_PORT_NUMBER 0 + + + +//Defines Stages of Control Transfer +#define SETUP_STAGE 1 +#define DATA_STAGE 2 +#define STATUS_STAGE 3 +#define COMPLETE_STAGE 4 + + +//Defines Direction of Endpoint +#define EP_IN 1 +#define EP_OUT 0 + +//Define speed of USB Device +#define LOW_SPEED_OTG 2 +#define FULL_SPEED_OTG 1 +#define HIGH_SPEED_OTG 0 +#define SUPER_SPEED_OTG 3 + +//Define multiple count of packet in periodic transfer. +#define MULTI_COUNT_ZERO 0 +#define MULTI_COUNT_ONE 1 +#define MULTI_COUNT_TWO 2 + +//Define USB Transfer Types. +#define CONTROL_TRANSFER 0 +#define ISOCH_TRANSFER 1 +#define BULK_TRANSFER 2 +#define INT_TRANSFER 3 + +#define BULK_TIMEOUT 300 + +//Defines PID +#define DATA0 0 +#define DATA1 2 +#define DATA2 1 +#define MDATA 3 +#define SETUP 3 + +//Defines USB Transfer Request Size on USB2.0 +#define USB_20_STAND_DEV_REQUEST_SIZE 8 +//Define Max Channel Number +#define MAX_CH_NUMBER 16 +//Define Channel Number +#define CH_0 0 +#define CH_1 1 +#define CH_2 2 +#define CH_3 3 +#define CH_4 4 +#define CH_5 5 +#define CH_6 6 +#define CH_7 7 +#define CH_8 8 +#define CH_9 9 +#define CH_10 10 +#define CH_11 11 +#define CH_12 12 +#define CH_13 13 +#define CH_14 14 +#define CH_15 15 +#define CH_NONE 20 + + +// define the Constant for result of processing the USB Transfer. +#define RE_TRANSMIT 1 +#define RE_SCHEDULE 2 +#define DE_ALLOCATE 3 +#define NO_ACTION 4 + +//define the threshold value to retransmit USB Transfer +#define RETRANSMIT_THRESHOLD 2 + +//define the maximum size of data to be tranferred through channel. +#define MAX_CH_TRANSFER_SIZE 65536//65535 + +//define Max Frame Number which Synopsys OTG suppports. +#define MAX_FRAME_NUMBER 0x3FFF +// Channel Interrupt Status +#define CH_STATUS_DataTglErr (0x1<<10) +#define CH_STATUS_FrmOvrun (0x1<<9) +#define CH_STATUS_BblErr (0x1<<8) +#define CH_STATUS_XactErr (0x1<<7) +#define CH_STATUS_NYET (0x1<<6) +#define CH_STATUS_ACK (0x1<<5) +#define CH_STATUS_NAK (0x1<<4) +#define CH_STATUS_STALL (0x1<<3) +#define CH_STATUS_AHBErr (0x1<<2) +#define CH_STATUS_ChHltd (0x1<<1) +#define CH_STATUS_XferCompl (0x1<<0) +#define CH_STATUS_ALL 0x7FF + + +//Define USB Transfer Flag.. +//typedef URB_SHORT_NOT_OK USB_TRANS_FLAG_NOT_SHORT; +//typedef URB_ISO_ASAP USB_TRANS_FLAG_ISO_ASYNCH; + +#define USB_TRANS_FLAG_NOT_SHORT URB_SHORT_NOT_OK +#define USB_TRANS_FLAG_ISO_ASYNCH URB_ISO_ASAP + + +#define HFNUM_MAX_FRNUM 0x3FFF +#define SCHEDULE_SLOT 10 + +#ifdef __cplusplus +} +#endif + + +#endif + + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-common-datastruct.h b/drivers/usb/host/s3c-otg/s3c-otg-common-datastruct.h new file mode 100644 index 0000000..8165981 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-common-datastruct.h @@ -0,0 +1,785 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : s3c-otg-common-datastruct.h + * [Description] : The Header file defines Data Structures to be used at sub-modules of S3C6400HCD. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/03 + * [Revision History] + * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and defines Data Structure to be managed by Transfer. + * (2) 2008/08/18 by SeungSoo Yang ( ss1.yang@samsung.com ) + * - modifying ED structure + * + ****************************************************************************/ +/**************************************************************************** + * 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 _DATA_STRUCT_DEF_H +#define _DATA_STRUCT_DEF_H + +/* +// ---------------------------------------------------------------------------- +// Include files : None. +// ---------------------------------------------------------------------------- +*/ + +#include <linux/wakelock.h> +#include <plat/s5p-otghost.h> +//#include "s3c-otg-common-typedef.h" +#include "s3c-otg-hcdi-list.h" + +//#include "s3c-otg-common-regdef.h" +//#include "s3c-otg-common-errorcode.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef union _hcintmsk_t +{ + // raw register data + u32 d32; + + // register bits + struct + { + unsigned xfercompl : 1; + unsigned chhltd : 1; + unsigned ahberr : 1; + unsigned stall : 1; + unsigned nak : 1; + unsigned ack : 1; + unsigned nyet : 1; + unsigned xacterr : 1; + unsigned bblerr : 1; + unsigned frmovrun : 1; + unsigned datatglerr : 1; + unsigned reserved : 21; + } b; +} hcintmsk_t; + +typedef union _hcintn_t +{ + u32 d32; + struct + { + u32 xfercompl :1; + u32 chhltd :1; + u32 abherr :1; + u32 stall :1; + u32 nak :1; + u32 ack :1; + u32 nyet :1; + u32 xacterr :1; + u32 bblerr :1; + u32 frmovrun :1; + u32 datatglerr :1; + u32 reserved :21; + }b; +}hcintn_t; + + +typedef union _pcgcctl_t +{ + /** raw register data */ + u32 d32; + /** register bits */ + struct + { + unsigned stoppclk :1; + unsigned gatehclk :1; + unsigned pwrclmp :1; + unsigned rstpdwnmodule :1; + unsigned physuspended :1; + unsigned Reserved5_31 :27; + }b; +}pcgcctl_t; + + +typedef struct isoch_packet_desc +{ + u32 isoch_packiet_start_addr;// start address of buffer is buffer address + uiOffsert. + u32 buf_size; + u32 transferred_szie; + u32 isoch_status; +}isoch_packet_desc_t;//, *isoch_packet_desc_t *,**isoch_packet_desc_t **; + + +typedef struct standard_dev_req_info +{ + bool is_data_stage; + u8 conrol_transfer_stage; + u32 vir_standard_dev_req_addr; + u32 phy_standard_dev_req_addr; +}standard_dev_req_info_t; + + +typedef struct control_data_tgl_t +{ + u8 setup_tgl; + u8 data_tgl; + u8 status_tgl; +}control_data_tgl_t; + + + +typedef struct ed_status +{ + u8 data_tgl; + control_data_tgl_t control_data_tgl; + bool is_ping_enable; + bool is_in_transfer_ready_q; + bool is_in_transferring; + u32 in_transferring_td; + bool is_alloc_resource_for_ed; +}ed_status_t;//, *ed_status_t *,**ed_status_t **; + + +typedef struct ed_desc +{ + u8 device_addr; + u8 endpoint_num; + bool is_ep_in; + u8 dev_speed; + u8 endpoint_type; + u16 max_packet_size; + u8 mc; + u8 interval; + u32 sched_frame; + u32 used_bus_time; + u8 hub_addr; + u8 hub_port; + bool is_do_split; +}ed_dest_t;//, *ed_dest_t *,**ed_dest_t **; + + +//Defines the Data Structures of Transfer. +typedef struct hc_reg +{ + + hcintmsk_t hc_int_msk; + hcintn_t hc_int; + u32 dma_addr; + +}hc_reg_t;//, *hc_reg_t *, **hc_reg_t **; + + +typedef struct stransfer +{ + u32 stransfer_id; + u32 parent_td; + ed_dest_t *ed_desc_p; + ed_status_t *ed_status_p; + u32 start_vir_buf_addr; + u32 start_phy_buf_addr; + u32 buf_size; + u32 packet_cnt; + u8 alloc_chnum; + hc_reg_t hc_reg; +}stransfer_t;//, *stransfer_t *,**stransfer_t **; + + +typedef struct ed +{ + u32 ed_id; + bool is_halted; + bool is_need_to_insert_scheduler; + ed_dest_t ed_desc; + ed_status_t ed_status; + otg_list_head ed_list_entry; + otg_list_head td_list_entry; + otg_list_head trans_ready_q_list_entry; + u32 num_td; + void *ed_private; +}ed_t;//, *ed_t *, **ed_t **; + + + +typedef struct td +{ + u32 td_id; + ed_t *parent_ed_p; + void *call_back_func_p; + void *call_back_func_param_p; + bool is_transferring; + bool is_transfer_done; + u32 transferred_szie; + bool is_standard_dev_req; + standard_dev_req_info_t standard_dev_req_info; + u32 vir_buf_addr; + u32 phy_buf_addr; + u32 buf_size; + u32 transfer_flag; + stransfer_t cur_stransfer; + USB_ERROR_CODE error_code; + u32 err_cnt; + otg_list_head td_list_entry; + + //Isochronous Transfer Specific + u32 isoch_packet_num; + isoch_packet_desc_t *isoch_packet_desc_p; + u32 isoch_packet_index; + u32 isoch_packet_position; + u32 sched_frame; + u32 interval; + u32 used_total_bus_time; + + // the private data can be used by S3C6400Interface. + void *td_private; +}td_t;//, *td_t *,**td_t **; + + +//Define Data Structures of Scheduler. +typedef struct trans_ready_q +{ + bool is_periodic_transfer; + otg_list_head trans_ready_q_list_head; + u32 trans_ready_entry_num; + + //In case of Periodic Transfer + u32 total_perio_bus_bandwidth; + u8 total_alloc_chnum; +}trans_ready_q_t;//, *trans_ready_q_t *,**trans_ready_q_t **; + + +//Define USB OTG Reg Data Structure by Kyuhyeok. + +#define MAX_COUNT 10000 +#define INT_ALL 0xffffffff + +typedef union _haint_t +{ + u32 d32; + struct + { + u32 channel_intr_0 :1; + u32 channel_intr_1 :1; + u32 channel_intr_2 :1; + u32 channel_intr_3 :1; + u32 channel_intr_4 :1; + u32 channel_intr_5 :1; + u32 channel_intr_6 :1; + u32 channel_intr_7 :1; + u32 channel_intr_8 :1; + u32 channel_intr_9 :1; + u32 channel_intr_10 :1; + u32 channel_intr_11 :1; + u32 channel_intr_12 :1; + u32 channel_intr_13 :1; + u32 channel_intr_14 :1; + u32 channel_intr_15 :1; + u32 reserved1 :16; + }b; +}haint_t; + +typedef union _gresetctl_t +{ + /** raw register data */ + u32 d32; + /** register bits */ + struct + { + unsigned csftrst : 1; + unsigned hsftrst : 1; + unsigned hstfrm : 1; + unsigned intknqflsh : 1; + unsigned rxfflsh : 1; + unsigned txfflsh : 1; + unsigned txfnum : 5; + unsigned reserved11_29 : 19; + unsigned dmareq : 1; + unsigned ahbidle : 1; + } b; +} gresetctl_t; + + +typedef union _gahbcfg_t +{ + /** raw register data */ + u32 d32; + /** register bits */ + struct + { + unsigned glblintrmsk : 1; +#define GAHBCFG_GLBINT_ENABLE 1 + unsigned hburstlen : 4; +#define INT_DMA_MODE_SINGLE 00 +#define INT_DMA_MODE_INCR 01 +#define INT_DMA_MODE_INCR4 03 +#define INT_DMA_MODE_INCR8 05 +#define INT_DMA_MODE_INCR16 07 + unsigned dmaenable : 1; +#define GAHBCFG_DMAENABLE 1 + unsigned reserved : 1; + unsigned nptxfemplvl : 1; + unsigned ptxfemplvl : 1; +#define GAHBCFG_TXFEMPTYLVL_EMPTY 1 +#define GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0 + unsigned reserved9_31 : 22; + } b; +} gahbcfg_t; + +typedef union _gusbcfg_t +{ + /** raw register data */ + u32 d32; + /** register bits */ + struct + { + unsigned toutcal : 3; + unsigned phyif : 1; + unsigned ulpi_utmi_sel : 1; + unsigned fsintf : 1; + unsigned physel : 1; + unsigned ddrsel : 1; + unsigned srpcap : 1; + unsigned hnpcap : 1; + unsigned usbtrdtim : 4; + unsigned nptxfrwnden : 1; + unsigned phylpwrclksel : 1; + unsigned reserved : 13; + unsigned forcehstmode : 1; + unsigned reserved2 : 2; + } b; +} gusbcfg_t; + + +typedef union _ghwcfg2_t +{ + /** raw register data */ + u32 d32; + /** register bits */ + struct { + /* GHWCFG2 */ + unsigned op_mode : 3; +#define MODE_HNP_SRP_CAPABLE 0 +#define MODE_SRP_ONLY_CAPABLE 1 +#define MODE_NO_HNP_SRP_CAPABLE 2 +#define MODE_SRP_CAPABLE_DEVICE 3 +#define MODE_NO_SRP_CAPABLE_DEVICE 4 +#define MODE_SRP_CAPABLE_HOST 5 +#define MODE_NO_SRP_CAPABLE_HOST 6 + + unsigned architecture : 2; +#define HWCFG2_ARCH_SLAVE_ONLY 0x00 +#define HWCFG2_ARCH_EXT_DMA 0x01 +#define HWCFG2_ARCH_INT_DMA 0x02 + + unsigned point2point : 1; + unsigned hs_phy_type : 2; + unsigned fs_phy_type : 2; + unsigned num_dev_ep : 4; + unsigned num_host_chan : 4; + unsigned perio_ep_supported : 1; + unsigned dynamic_fifo : 1; + unsigned rx_status_q_depth : 2; + unsigned nonperio_tx_q_depth : 2; + unsigned host_perio_tx_q_depth : 2; + unsigned dev_token_q_depth : 5; + unsigned reserved31 : 1; + } b; +} ghwcfg2_t; + +typedef union _gintsts_t +{ + /** raw register data */ + u32 d32; +#define SOF_INTR_MASK 0x0008 + /** register bits */ + struct + { +#define HOST_MODE 1 +#define DEVICE_MODE 0 + unsigned curmode : 1; +#define OTG_HOST_MODE 1 +#define OTG_DEVICE_MODE 0 + + unsigned modemismatch : 1; + unsigned otgintr : 1; + unsigned sofintr : 1; + unsigned rxstsqlvl : 1; + unsigned nptxfempty : 1; + unsigned ginnakeff : 1; + unsigned goutnakeff : 1; + unsigned reserved8 : 1; + unsigned i2cintr : 1; + unsigned erlysuspend : 1; + unsigned usbsuspend : 1; + unsigned usbreset : 1; + unsigned enumdone : 1; + unsigned isooutdrop : 1; + unsigned eopframe : 1; + unsigned intokenrx : 1; + unsigned epmismatch : 1; + unsigned inepint : 1; + unsigned outepintr : 1; + unsigned incompisoin : 1; + unsigned incompisoout : 1; + unsigned reserved22_23 : 2; + unsigned portintr : 1; + unsigned hcintr : 1; + unsigned ptxfempty : 1; + unsigned reserved27 : 1; + unsigned conidstschng : 1; + unsigned disconnect : 1; + unsigned sessreqintr : 1; + unsigned wkupintr : 1; + } b; +} gintsts_t; + + +typedef union _hcfg_t +{ + /** raw register data */ + u32 d32; + + /** register bits */ + struct + { + /** FS/LS Phy Clock Select */ + unsigned fslspclksel : 2; +#define HCFG_30_60_MHZ 0 +#define HCFG_48_MHZ 1 +#define HCFG_6_MHZ 2 + + /** FS/LS Only Support */ + unsigned fslssupp : 1; + unsigned reserved3_31 : 29; + } b; +} hcfg_t; + +typedef union _hprt_t +{ + /** raw register data */ + u32 d32; + /** register bits */ + struct + { + unsigned prtconnsts : 1; + unsigned prtconndet : 1; + unsigned prtena : 1; + unsigned prtenchng : 1; + unsigned prtovrcurract : 1; + unsigned prtovrcurrchng : 1; + unsigned prtres : 1; + unsigned prtsusp : 1; + unsigned prtrst : 1; + unsigned reserved9 : 1; + unsigned prtlnsts : 2; + unsigned prtpwr : 1; + unsigned prttstctl : 4; + unsigned prtspd : 2; +#define HPRT0_PRTSPD_HIGH_SPEED 0 +#define HPRT0_PRTSPD_FULL_SPEED 1 +#define HPRT0_PRTSPD_LOW_SPEED 2 + unsigned reserved19_31 : 13; + } b; +} hprt_t; + + +typedef union _gintmsk_t +{ + /** raw register data */ + u32 d32; + /** register bits */ + struct + { + unsigned reserved0 : 1; + unsigned modemismatch : 1; + unsigned otgintr : 1; + unsigned sofintr : 1; + unsigned rxstsqlvl : 1; + unsigned nptxfempty : 1; + unsigned ginnakeff : 1; + unsigned goutnakeff : 1; + unsigned reserved8 : 1; + unsigned i2cintr : 1; + unsigned erlysuspend : 1; + unsigned usbsuspend : 1; + unsigned usbreset : 1; + unsigned enumdone : 1; + unsigned isooutdrop : 1; + unsigned eopframe : 1; + unsigned reserved16 : 1; + unsigned epmismatch : 1; + unsigned inepintr : 1; + unsigned outepintr : 1; + unsigned incompisoin : 1; + unsigned incompisoout : 1; + unsigned reserved22_23 : 2; + unsigned portintr : 1; + unsigned hcintr : 1; + unsigned ptxfempty : 1; + unsigned reserved27 : 1; + unsigned conidstschng : 1; + unsigned disconnect : 1; + unsigned sessreqintr : 1; + unsigned wkupintr : 1; + } b; +} gintmsk_t; + + +typedef struct _hc_t +{ + + u8 hc_num; // Host channel number used for register address lookup + + unsigned dev_addr : 7; // Device to access + unsigned ep_is_in : 1; // EP direction; 0: OUT, 1: IN + + unsigned ep_num : 4; // EP to access + unsigned low_speed : 1; // 1: Low speed, 0: Not low speed + unsigned ep_type : 2; // Endpoint type. + // One of the following values: + // - OTG_EP_TYPE_CONTROL: 0 + // - OTG_EP_TYPE_ISOC: 1 + // - OTG_EP_TYPE_BULK: 2 + // - OTG_EP_TYPE_INTR: 3 + + unsigned rsvdb1 : 1; // 8 bit padding + + u8 rsvd2; // 4 byte boundary + + unsigned max_packet : 12; // Max packet size in bytes + + unsigned data_pid_start : 2; +#define OTG_HC_PID_DATA0 0 +#define OTG_HC_PID_DATA2 1 +#define OTG_HC_PID_DATA1 2 +#define OTG_HC_PID_MDATA 3 +#define OTG_HC_PID_SETUP 3 + + unsigned multi_count : 2; // Number of periodic transactions per (micro)frame + + + // Flag to indicate whether the transfer has been started. Set to 1 if + // it has been started, 0 otherwise. + u8 xfer_started; + + + // Set to 1 to indicate that a PING request should be issued on this + // channel. If 0, process normally. + u8 do_ping; + + // Set to 1 to indicate that the error count for this transaction is + // non-zero. Set to 0 if the error count is 0. + u8 error_state; + u32 *xfer_buff; // Pointer to the current transfer buffer position. + u16 start_pkt_count; // Packet count at start of transfer. + + u32 xfer_len; // Total number of bytes to transfer. + u32 xfer_count; // Number of bytes transferred so far. + + + // Set to 1 if the host channel has been halted, but the core is not + // finished flushing queued requests. Otherwise 0. + u8 halt_pending; + u8 halt_status; // Reason for halting the host channel + u8 short_read; // Set when the host channel does a short read. + u8 rsvd3; // 4 byte boundary + +} hc_t; + + +// Port status for the HC +#define HCD_DRIVE_RESET 0x0001 +#define HCD_SEND_SETUP 0x0002 + +#define HC_MAX_PKT_COUNT 511 +#define HC_MAX_TRANSFER_SIZE 65535 +#define MAXP_SIZE_64BYTE 64 +#define MAXP_SIZE_512BYTE 512 +#define MAXP_SIZE_1024BYTE 1024 + +typedef union _hcchar_t +{ + // raw register data + u32 d32; + + // register bits + struct + { + // Maximum packet size in bytes + unsigned mps : 11; + + // Endpoint number + unsigned epnum : 4; + + // 0: OUT, 1: IN + unsigned epdir : 1; +#define HCDIR_OUT 0 +#define HCDIR_IN 1 + + unsigned reserved : 1; + + // 0: Full/high speed device, 1: Low speed device + unsigned lspddev : 1; + + // 0: Control, 1: Isoc, 2: Bulk, 3: Intr + unsigned eptype : 2; +#define OTG_EP_TYPE_CONTROL 0 +#define OTG_EP_TYPE_ISOC 1 +#define OTG_EP_TYPE_BULK 2 +#define OTG_EP_TYPE_INTR 3 + + // Packets per frame for periodic transfers. 0 is reserved. + unsigned multicnt : 2; + + // Device address + unsigned devaddr : 7; + + // Frame to transmit periodic transaction. + // 0: even, 1: odd + unsigned oddfrm : 1; + + // Channel disable + unsigned chdis : 1; + + // Channel enable + unsigned chen : 1; + } b; +} hcchar_t; + +typedef union _hctsiz_t +{ + // raw register data + u32 d32; + + // register bits + struct + { + // Total transfer size in bytes + unsigned xfersize : 19; + + // Data packets to transfer + unsigned pktcnt : 10; + + // Packet ID for next data packet + // 0: DATA0 + // 1: DATA2 + // 2: DATA1 + // 3: MDATA (non-Control), SETUP (Control) + unsigned pid : 2; +#define HCTSIZ_DATA0 0 +#define HCTSIZ_DATA1 2 +#define HCTSIZ_DATA2 1 +#define HCTSIZ_MDATA 3 +#define HCTSIZ_SETUP 3 + + // Do PING protocol when 1 + unsigned dopng : 1; + } b; +} hctsiz_t; + + + +typedef union _grxstsr_t +{ + // raw register data + u32 d32; + + // register bits + struct + { + unsigned chnum : 4; + unsigned bcnt : 11; + unsigned dpid : 2; + unsigned pktsts : 4; + unsigned Reserved : 11; + } b; +} grxstsr_t; + +typedef union _hfir_t +{ + // raw register data + u32 d32; + + // register bits + struct + { + unsigned frint : 16; + unsigned Reserved : 16; + } b; +} hfir_t; + +typedef union _hfnum_t +{ + // raw register data + u32 d32; + + // register bits + struct + { + unsigned frnum : 16; +#define HFNUM_MAX_FRNUM 0x3FFF + unsigned frrem : 16; + } b; +} hfnum_t; + +typedef union grstctl_t +{ + /** raw register data */ + u32 d32; + /** register bits */ + struct + { + unsigned csftrst : 1; + unsigned hsftrst : 1; + unsigned hstfrm : 1; + unsigned intknqflsh : 1; + unsigned rxfflsh : 1; + unsigned txfflsh : 1; + unsigned txfnum : 5; + unsigned reserved11_29 : 19; + unsigned dmareq : 1; + unsigned ahbidle : 1; + } b; +} grstctl_t; + +typedef struct hc_info +{ + hcintmsk_t hc_int_msk; + hcintn_t hc_int; + u32 dma_addr; + hcchar_t hc_char; + hctsiz_t hc_size; +}hc_info_t;//, *hc_info_t *, **hc_info_t **; + +#ifndef USB_MAXCHILDREN + #define USB_MAXCHILDREN (31) +#endif + +typedef struct _usb_hub_descriptor_t +{ + u8 desc_length; + u8 desc_type; + u8 port_number; + u16 hub_characteristics; + u8 power_on_to_power_good; + u8 hub_control_current; + /* add 1 bit for hub status change; round to bytes */ + u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8]; + u8 port_pwr_ctrl_mask[(USB_MAXCHILDREN + 1 + 7) / 8]; +}usb_hub_descriptor_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/usb/host/s3c-otg/s3c-otg-common-errorcode.h b/drivers/usb/host/s3c-otg/s3c-otg-common-errorcode.h new file mode 100644 index 0000000..b2b3bee --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-common-errorcode.h @@ -0,0 +1,115 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : s3c-otg-common-errorcode.h + * [Description] : The Header file defines Error Codes to be used at sub-modules of S3C6400HCD. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/03 + * [Revision History] + * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file. + * (2) 2008/08/18 by SeungSoo Yang ( ss1.yang@samsung.com ) + * - add HCD error code + * + ****************************************************************************/ +/**************************************************************************** + * 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 _ERROR_CODE_DEF_H +#define _ERROR_CODE_DEF_H + +/* +// ---------------------------------------------------------------------------- +// Include files : None. +// ---------------------------------------------------------------------------- +*/ + +//#include "s3c-otg-common-typedef.h" +#include "s3c-otg-common-common.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +typedef int USB_ERROR_CODE; + +//General USB Error Code. +#define USB_ERR_SUCCESS 0 +#define USB_ERR_FAIL -1 + +#define USB_ERR_NO 1 + +#define USB_ERR_NO_ENTITY -2 + +//S3CTransfer Error Code +#define USB_ERR_NODEV -ENODEV +#define USB_ERR_NOMEM -ENOMEM +#define USB_ERR_NOSPACE -ENOSPC +#define USB_ERR_NOIO -EIO + +//OTG-HCD error code +#define USB_ERR_NOELEMENT -ENOENT +#define USB_ERR_ESHUTDOWN -ESHUTDOWN /* unplug */ +#define USB_ERR_DEQUEUED -ECONNRESET /* unlink */ + + +//S3CScheduler Error Code +#define USB_ERR_ALREADY_EXIST -1 +#define USB_ERR_NO_RESOURCE -2 +#define USB_ERR_NO_CHANNEL -3 +#define USB_ERR_NO_BANDWIDTH -4 +#define USB_ERR_ALL_RESROUCE -5 + + + + +/************************************************ + *Defines the USB Error Status Code of USB Transfer. + ************************************************/ + +//#ifdef LINUX + +#define USB_ERR_STATUS_COMPLETE 0 +#define USB_ERR_STATUS_INPROGRESS -EINPROGRESS +#define USB_ERR_STATUS_CRC -EILSEQ +#define USB_ERR_STATUS_XACTERR -EPROTO +#define USB_ERR_STATUS_STALL -EPIPE +#define USB_ERR_STATUS_BBLERR -EOVERFLOW +#define USB_ERR_STATUS_AHBERR -EIO +#define USB_ERR_STATUS_FRMOVRUN_OUT -ENOSR +#define USB_ERR_STATUS_FRMOVRUN_IN -ECOMM +#define USB_ERR_STATUS_SHORTREAD -EREMOTEIO + +//#else + +//#endif + + + + + + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-common-regdef.h b/drivers/usb/host/s3c-otg/s3c-otg-common-regdef.h new file mode 100644 index 0000000..ed119d7 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-common-regdef.h @@ -0,0 +1,302 @@ +/**************************************************************************** +* (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved +* +* [File Name] : s3c-otg-common-regdef.h +* [Description] : +* +* [Author] : Kyu Hyeok Jang { kyuhyeok.jang@samsung.com } +* [Department] : System LSI Division/Embedded Software Center +* [Created Date]: 2007/12/15 +* [Revision History] +* (1) 2007/12/15 by Kyu Hyeok Jang { kyuhyeok.jang@samsung.com } +* - Created +* +****************************************************************************/ +/**************************************************************************** + * 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 _OTG_REG_DEF_H +#define _OTG_REG_DEF_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct { + u32 OPHYPWR; + u32 OPHYCLK; + u32 ORSTCON; +}OTG_PHY_REG, *PS_OTG_PHY_REG; + +#define GOTGCTL 0x000 // OTG Control & Status +#define GOTGINT 0x004 // OTG Interrupt +#define GAHBCFG 0x008 // Core AHB Configuration +#define GUSBCFG 0x00C // Core USB Configuration +#define GRSTCTL 0x010 // Core Reset +#define GINTSTS 0x014 // Core Interrupt +#define GINTMSK 0x018 // Core Interrupt Mask +#define GRXSTSR 0x01C // Receive Status Debug Read/Status Read +#define GRXSTSP 0x020 // Receive Status Debug Pop/Status Pop +#define GRXFSIZ 0x024 // Receive FIFO Size +#define GNPTXFSIZ 0x028 // Non-Periodic Transmit FIFO Size +#define GNPTXSTS 0x02C // Non-Periodic Transmit FIFO/Queue Status +#define GPVNDCTL 0x034 // PHY Vendor Control +#define GGPIO 0x038 // General Purpose I/O +#define GUID 0x03C // User ID +#define GSNPSID 0x040 // Synopsys ID +#define GHWCFG1 0x044 // User HW Config1 +#define GHWCFG2 0x048 // User HW Config2 +#define GHWCFG3 0x04C // User HW Config3 +#define GHWCFG4 0x050 // User HW Config4 + +#define HPTXFSIZ 0x100 // Host Periodic Transmit FIFO Size +#define DPTXFSIZ1 0x104 // Device Periodic Transmit FIFO-1 Size +#define DPTXFSIZ2 0x108 // Device Periodic Transmit FIFO-2 Size +#define DPTXFSIZ3 0x10C // Device Periodic Transmit FIFO-3 Size +#define DPTXFSIZ4 0x110 // Device Periodic Transmit FIFO-4 Size +#define DPTXFSIZ5 0x114 // Device Periodic Transmit FIFO-5 Size +#define DPTXFSIZ6 0x118 // Device Periodic Transmit FIFO-6 Size +#define DPTXFSIZ7 0x11C // Device Periodic Transmit FIFO-7 Size +#define DPTXFSIZ8 0x120 // Device Periodic Transmit FIFO-8 Size +#define DPTXFSIZ9 0x124 // Device Periodic Transmit FIFO-9 Size +#define DPTXFSIZ10 0x128 // Device Periodic Transmit FIFO-10 Size +#define DPTXFSIZ11 0x12C // Device Periodic Transmit FIFO-11 Size +#define DPTXFSIZ12 0x130 // Device Periodic Transmit FIFO-12 Size +#define DPTXFSIZ13 0x134 // Device Periodic Transmit FIFO-13 Size +#define DPTXFSIZ14 0x138 // Device Periodic Transmit FIFO-14 Size +#define DPTXFSIZ15 0x13C // Device Periodic Transmit FIFO-15 Size + +//********************************************************************* +// Host Mode Registers +//********************************************************************* +// Host Global Registers + +// Channel specific registers +#define HCCHAR_ADDR 0x500 +#define HCCHAR(n) 0x500 + ((n)*0x20) +#define HCSPLT(n) 0x504 + ((n)*0x20) +#define HCINT(n) 0x508 + ((n)*0x20) +#define HCINTMSK(n) 0x50C + ((n)*0x20) +#define HCTSIZ(n) 0x510 + ((n)*0x20) +#define HCDMA(n) 0x514 + ((n)*0x20) + +#define HCFG 0x400 // Host Configuration +#define HFIR 0x404 // Host Frame Interval +#define HFNUM 0x408 // Host Frame Number/Frame Time Remaining +#define HPTXSTS 0x410 // Host Periodic Transmit FIFO/Queue Status +#define HAINT 0x414 // Host All Channels Interrupt +#define HAINTMSK 0x418 // Host All Channels Interrupt Mask + +// Host Port Control & Status Registers + +#define HPRT 0x440 // Host Port Control & Status + +// Device Logical Endpoints-Specific Registers + +#define DIEPCTL 0x900 // Device IN Endpoint 0 Control +#define DOEPCTL(n) 0xB00 + ((n)*0x20)) // Device OUT Endpoint 0 Control +#define DIEPINT(n) 0x908 + ((n)*0x20)) // Device IN Endpoint 0 Interrupt +#define DOEPINT(n) 0xB08 + ((n)*0x20)) // Device OUT Endpoint 0 Interrupt +#define DIEPTSIZ(n) 0x910 + ((n)*0x20)) // Device IN Endpoint 0 Transfer Size +#define DOEPTSIZ(n) 0xB10 + ((n)*0x20)) // Device OUT Endpoint 0 Transfer Size +#define DIEPDMA(n) 0x914 + ((n)*0x20)) // Device IN Endpoint 0 DMA Address +#define DOEPDMA(n) 0xB14 + ((n)*0x20)) // Device OUT Endpoint 0 DMA Address + +#define EP_FIFO(n) 0x1000 + ((n)*0x1000)) + +#define PCGCCTL 0x0E00 + +// +#define BASE_REGISTER_OFFSET 0x0 +#define REGISTER_SET_SIZE 0x200 + +// Power Reg Bits +#define USB_RESET 0x8 +#define MCU_RESUME 0x4 +#define SUSPEND_MODE 0x2 +#define SUSPEND_MODE_ENABLE_CTRL 0x1 + +// EP0 CSR +#define EP0_OUT_PACKET_RDY 0x1 +#define EP0_IN_PACKET_RDY 0x2 +#define EP0_SENT_STALL 0x4 +#define DATA_END 0x8 +#define SETUP_END 0x10 +#define EP0_SEND_STALL 0x20 +#define SERVICED_OUT_PKY_RDY 0x40 +#define SERVICED_SETUP_END 0x80 + +// IN_CSR1_REG Bit definitions +#define IN_PACKET_READY 0x1 +#define UNDER_RUN 0x4 // Iso Mode Only +#define FLUSH_IN_FIFO 0x8 +#define IN_SEND_STALL 0x10 +#define IN_SENT_STALL 0x20 +#define IN_CLR_DATA_TOGGLE 0x40 + +// OUT_CSR1_REG Bit definitions +#define OUT_PACKET_READY 0x1 +#define FLUSH_OUT_FIFO 0x10 +#define OUT_SEND_STALL 0x20 +#define OUT_SENT_STALL 0x40 +#define OUT_CLR_DATA_TOGGLE 0x80 + +// IN_CSR2_REG Bit definitions +#define IN_DMA_INT_DISABLE 0x10 +#define SET_MODE_IN 0x20 + +#define EPTYPE (0x3<<18) +#define SET_TYPE_CONTROL (0x0<<18) +#define SET_TYPE_ISO (0x1<<18) +#define SET_TYPE_BULK (0x2<<18) +#define SET_TYPE_INTERRUPT (0x3<<18) + +#define AUTO_MODE 0x80 + +// OUT_CSR2_REG Bit definitions +#define AUTO_CLR 0x40 +#define OUT_DMA_INT_DISABLE 0x20 + +// Can be used for Interrupt and Interrupt Enable Reg - common bit def +#define EP0_IN_INT (0x1<<0) +#define EP1_IN_INT (0x1<<1) +#define EP2_IN_INT (0x1<<2) +#define EP3_IN_INT (0x1<<3) +#define EP4_IN_INT (0x1<<4) +#define EP5_IN_INT (0x1<<5) +#define EP6_IN_INT (0x1<<6) +#define EP7_IN_INT (0x1<<7) +#define EP8_IN_INT (0x1<<8) +#define EP9_IN_INT (0x1<<9) +#define EP10_IN_INT (0x1<<10) +#define EP11_IN_INT (0x1<<11) +#define EP12_IN_INT (0x1<<12) +#define EP13_IN_INT (0x1<<13) +#define EP14_IN_INT (0x1<<14) +#define EP15_IN_INT (0x1<<15) +#define EP0_OUT_INT (0x1<<16) +#define EP1_OUT_INT (0x1<<17) +#define EP2_OUT_INT (0x1<<18) +#define EP3_OUT_INT (0x1<<19) +#define EP4_OUT_INT (0x1<<20) +#define EP5_OUT_INT (0x1<<21) +#define EP6_OUT_INT (0x1<<22) +#define EP7_OUT_INT (0x1<<23) +#define EP8_OUT_INT (0x1<<24) +#define EP9_OUT_INT (0x1<<25) +#define EP10_OUT_INT (0x1<<26) +#define EP11_OUT_INT (0x1<<27) +#define EP12_OUT_INT (0x1<<28) +#define EP13_OUT_INT (0x1<<29) +#define EP14_OUT_INT (0x1<<30) +#define EP15_OUT_INT (0x1<<31) + +// GOTGINT +#define SesEndDet (0x1<<2) + +// GRSTCTL +#define TxFFlsh (0x1<<5) +#define RxFFlsh (0x1<<4) +#define INTknQFlsh (0x1<<3) +#define FrmCntrRst (0x1<<2) +#define HSftRst (0x1<<1) +#define CSftRst (0x1<<0) + +#define CLEAR_ALL_EP_INTRS 0xffffffff + +#define EP_INTERRUPT_DISABLE_ALL 0x0 // Bits to write to EP_INT_EN_REG - Use CLEAR + +// DMA control register bit definitions +#define RUN_OB 0x80 +#define STATE 0x70 +#define DEMAND_MODE 0x8 +#define OUT_DMA_RUN 0x4 +#define IN_DMA_RUN 0x2 +#define DMA_MODE_EN 0x1 + + +#define REAL_PHYSICAL_ADDR_EP0_FIFO (0x520001c0) //Endpoint 0 FIFO +#define REAL_PHYSICAL_ADDR_EP1_FIFO (0x520001c4) //Endpoint 1 FIFO +#define REAL_PHYSICAL_ADDR_EP2_FIFO (0x520001c8) //Endpoint 2 FIFO +#define REAL_PHYSICAL_ADDR_EP3_FIFO (0x520001cc) //Endpoint 3 FIFO +#define REAL_PHYSICAL_ADDR_EP4_FIFO (0x520001d0) //Endpoint 4 FIFO + +// GAHBCFG +#define MODE_DMA (1<<5) +#define MODE_SLAVE (0<<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_MASK (0<<0) +#define GBL_INT_UNMASK (1<<0) + +// For USB DMA +//BOOL InitUsbdDriverGlobals(void); //:-) +//void UsbdDeallocateVm(void); //:-) +//BOOL UsbdAllocateVm(void); //:-) +//void UsbdInitDma(int epnum, int bufIndex,int bufOffset); //:-) + + +//by Kevin + +////////////////////////////////////////////////////////////////////////// + +/* +inline u32 ReadReg( + u32 uiOffset + ) +{ + volatile u32 *pbReg = ctrlr_base_reg_addr(uiOffset); + u32 uiValue = (u32) *pbReg; + return uiValue; +} + +inline void WriteReg( + u32 uiOffset, + u32 bValue + ) +{ + volatile ULONG *pbReg = ctrlr_base_reg_addr(uiOffset); + *pbReg = (ULONG) bValue; +} + +inline void UpdateReg( + u32 uiOffset, + u32 bValue + ) +{ + WriteReg(uiOffset, (ReadReg(uiOffset) | bValue)); +}; + +inline void ClearReg( + u32 uiOffeset, + u32 bValue + ) +{ + WriteReg(uiOffeset, (ReadReg(uiOffeset) & ~bValue)); +}; +*/ + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-debug.h b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-debug.h new file mode 100644 index 0000000..20416b1 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-debug.h @@ -0,0 +1,88 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * @file s3c-otg-hcdi-debug.c + * @brief It provides debug functions for display message \n + * @version + * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Creating the initial version of this code \n + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * @see None + ****************************************************************************/ +/**************************************************************************** + * 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 _S3C_OTG_HCDI_DEBUG_H_ +#define _S3C_OTG_HCDI_DEBUG_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define OTG_DEBUG + +#ifdef OTG_DEBUG +#if 0 +#include <linux/stddef.h> +#endif + +#define OTG_DBG_OTGHCDI_DRIVER true +#define OTG_DBG_OTGHCDI_HCD false +#define OTG_DBG_OTGHCDI_KAL false +#define OTG_DBG_OTGHCDI_LIST false +#define OTG_DBG_OTGHCDI_MEM false + +#define OTG_DBG_TRANSFER false +#define OTG_DBG_SCHEDULE false +#define OTG_DBG_OCI false +#define OTG_DBG_DONETRASF false +#define OTG_DBG_ISR false +#define OTG_DBG_ROOTHUB false + + +#include <linux/kernel.h> //for printk + +#define otg_err(is_active, msg...) \ + do{ if (/*(is_active) == */true)\ + {\ + pr_err("otg_err: in %s()::%05d ", __func__ , __LINE__); \ + pr_err("=> " msg); \ + }\ + }while(0) + +#define otg_dbg(is_active, msg...) \ + do{ if ((is_active) == true)\ + {\ + pr_info("otg_dbg: in %s()::%05d ", __func__, __LINE__); \ + pr_info("=> " msg); \ + }\ + }while(0) + +#else //OTG_DEBUG + +# define otg_err(is_active, msg...) do{}while(0) +# define otg_dbg(is_active, msg...) do{}while(0) + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* _S3C_OTG_HCDI_DEBUG_H_ */ diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.c b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.c new file mode 100644 index 0000000..09ee1a3 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.c @@ -0,0 +1,379 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * @file s3c-otg-hcdi-driver.c + * @brief It provides functions related with module for OTGHCD driver. \n + * @version + * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Creating the initial version of this code \n + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * -# Aug 18,2008 v1.3 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Modifying for successful rmmod & disconnecting \n + * @see None + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-hcdi-driver.h" +extern void otg_phy_off(void); + +/** + * static int s5pc110_otg_drv_probe (struct platform_device *pdev) + * + * @brief probe function of OTG hcd platform_driver + * + * @param [in] pdev : pointer of platform_device of otg hcd platform_driver + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If fail \n + * @remark + * it allocates resources of it and call other modules' init function. + * then call usb_create_hcd, usb_add_hcd, s5pc110_otghcd_start functions + */ + +struct usb_hcd* g_pUsbHcd = NULL; + +static void otg_power_work(struct work_struct *work) +{ + struct sec_otghost *otghost = container_of(work, + struct sec_otghost, work); + struct sec_otghost_data *hdata = otghost->otg_data; + + if (hdata && hdata->set_pwr_cb) { + hdata->set_pwr_cb(0); +#ifdef CONFIG_USB_HOST_NOTIFY + if (g_pUsbHcd) + host_state_notify(&g_pUsbHcd->ndev, NOTIFY_HOST_OVERCURRENT); +#endif + } else { + otg_err(true, "invalid otghost data\n"); + } +} + +static int s5pc110_otg_drv_probe (struct platform_device *pdev) +{ + int ret_val = 0; + u32 reg_val = 0; + struct sec_otghost *otghost = NULL; + struct sec_otghost_data *otg_data = dev_get_platdata(&pdev->dev); + + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, "s3c_otg_drv_probe\n"); + + + /*init for host mode*/ + /** + Allocate memory for the base HCD & Initialize the base HCD. + */ + g_pUsbHcd = usb_create_hcd(&s5pc110_otg_hc_driver, &pdev->dev, + "s3cotg");/*pdev->dev.bus_id*/ + if (g_pUsbHcd == NULL) { + ret_val = -ENOMEM; + otg_err(OTG_DBG_OTGHCDI_DRIVER, + "failed to usb_create_hcd\n"); + goto err_out_clk; + } + + /* mapping hcd resource & device resource*/ + + g_pUsbHcd->rsrc_start = pdev->resource[0].start; + g_pUsbHcd->rsrc_len = pdev->resource[0].end - + pdev->resource[0].start + 1; + + if (!request_mem_region(g_pUsbHcd->rsrc_start, g_pUsbHcd->rsrc_len, + gHcdName)) { + otg_err(OTG_DBG_OTGHCDI_DRIVER, + "failed to request_mem_region\n"); + ret_val = -EBUSY; + goto err_out_create_hcd; + } + + + /* Physical address => Virtual address */ + g_pUsbHcd->regs = S3C_VA_OTG; + g_pUsbHcd->self.otg_port = 1; + + g_pUDCBase = (u8 *)g_pUsbHcd->regs; + + otghost = hcd_to_sec_otghost(g_pUsbHcd); + + if (otghost == NULL) { + otg_err(true, "failed to get otghost hcd\n"); + ret_val = USB_ERR_FAIL; + goto err_out_create_hcd; + } + otghost->otg_data = otg_data; + + INIT_WORK(&otghost->work, otg_power_work); + otghost->wq = create_singlethread_workqueue("sec_otghostd"); + + /* call others' init() */ + ret_val = otg_hcd_init_modules(otghost); + if (ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_DRIVER, + "failed to otg_hcd_init_modules\n"); + ret_val = USB_ERR_FAIL; + goto err_out_create_hcd; + } + + /** + * Attempt to ensure this device is really a s5pc110 USB-OTG Controller. + * Read and verify the SNPSID register contents. The value should be + * 0x45F42XXX, which corresponds to "OT2", as in "OTG version 2.XX". + */ + reg_val = read_reg_32(0x40); + if ((reg_val & 0xFFFFF000) != 0x4F542000) { + otg_err(OTG_DBG_OTGHCDI_DRIVER, + "Bad value for SNPSID: 0x%x\n", reg_val); + ret_val = -EINVAL; + goto err_out_create_hcd_init; + } +#ifdef CONFIG_USB_HOST_NOTIFY + if (otg_data->host_notify) { + g_pUsbHcd->host_notify = otg_data->host_notify; + g_pUsbHcd->ndev.name = dev_name(&pdev->dev); + ret_val = host_notify_dev_register(&g_pUsbHcd->ndev); + if (ret_val) { + otg_err(OTG_DBG_OTGHCDI_DRIVER, + "Failed to host_notify_dev_register\n"); + goto err_out_create_hcd_init; + } + } +#endif +#ifdef CONFIG_USB_SEC_WHITELIST + if (otg_data->sec_whlist_table_num) + g_pUsbHcd->sec_whlist_table_num = otg_data->sec_whlist_table_num; +#endif + + /* + * Finish generic HCD initialization and start the HCD. This function + * allocates the DMA buffer pool, registers the USB bus, requests the + * IRQ line, and calls s5pc110_otghcd_start method. + */ + ret_val = usb_add_hcd(g_pUsbHcd, + pdev->resource[1].start, IRQF_DISABLED); + if (ret_val < 0) { + goto err_out_host_notify_register; + } + + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "OTG HCD Initialized HCD, bus=%s, usbbus=%d\n", + "C110 OTG Controller", g_pUsbHcd->self.busnum); + + /* otg_print_registers(); */ + + wake_lock_init(&otghost->wake_lock, WAKE_LOCK_SUSPEND, "usb_otg"); + wake_lock(&otghost->wake_lock); + + return USB_ERR_SUCCESS; +err_out_host_notify_register: +#ifdef CONFIG_USB_HOST_NOTIFY + host_notify_dev_unregister(&g_pUsbHcd->ndev); +#endif + +err_out_create_hcd_init: + otg_hcd_deinit_modules(otghost); + release_mem_region(g_pUsbHcd->rsrc_start, g_pUsbHcd->rsrc_len); + +err_out_create_hcd: + usb_put_hcd(g_pUsbHcd); + +err_out_clk: + + return ret_val; +} + +/** + * static int s5pc110_otg_drv_remove (struct platform_device *dev) + * + * @brief remove function of OTG hcd platform_driver + * + * @param [in] pdev : pointer of platform_device of otg hcd platform_driver + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If fail \n + * @remark + * This function is called when the otg device unregistered with the + * s5pc110_otg_driver. This happens, for example, when the rmmod command is + * executed. The device may or may not be electrically present. If it is + * present, the driver stops device processing. Any resources used on behalf + * of this device are freed. + */ +static int s5pc110_otg_drv_remove (struct platform_device *dev) +{ + struct sec_otghost *otghost = NULL; + + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, "s5pc110_otg_drv_remove\n"); + + otghost = hcd_to_sec_otghost(g_pUsbHcd); + +#ifdef CONFIG_USB_HOST_NOTIFY + host_notify_dev_unregister(&g_pUsbHcd->ndev); +#endif + + otg_hcd_deinit_modules(otghost); + + destroy_workqueue(otghost->wq); + + wake_unlock(&otghost->wake_lock); + wake_lock_destroy(&otghost->wake_lock); + + usb_remove_hcd(g_pUsbHcd); + + release_mem_region(g_pUsbHcd->rsrc_start, g_pUsbHcd->rsrc_len); + + usb_put_hcd(g_pUsbHcd); + + otg_phy_off(); + + return USB_ERR_SUCCESS; +} + +/** + * @struct s5pc110_otg_driver + * + * @brief + * This structure defines the methods to be called by a bus driver + * during the lifecycle of a device on that bus. Both drivers and + * devices are registered with a bus driver. The bus driver matches + * devices to drivers based on information in the device and driver + * structures. + * + * The probe function is called when the bus driver matches a device + * to this driver. The remove function is called when a device is + * unregistered with the bus driver. + */ +struct platform_driver s5pc110_otg_driver = { + .probe = s5pc110_otg_drv_probe, + .remove = s5pc110_otg_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "s3c_otghcd", + .owner = THIS_MODULE, + }, +}; + +/** + * static int __init s5pc110_otg_module_init(void) + * + * @brief module_init function + * + * @return it returns result of platform_driver_register + * @remark + * This function is called when the s5pc110_otg_driver is installed with the + * insmod command. It registers the s5pc110_otg_driver structure with the + * appropriate bus driver. This will cause the s5pc110_otg_driver_probe function + * to be called. In addition, the bus driver will automatically expose + * attributes defined for the device and driver in the special sysfs file + * system. + */ + /* +static int __init s5pc110_otg_module_init(void) +{ + int ret_val = 0; + + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "s3c_otg_module_init \n"); + + ret_val = platform_driver_register(&s5pc110_otg_driver); + if (ret_val < 0) + { + otg_err(OTG_DBG_OTGHCDI_DRIVER, + "platform_driver_register \n"); + } + return ret_val; +} */ + +/** + * static void __exit s5pc110_otg_module_exit(void) + * + * @brief module_exit function + * + * @remark + * This function is called when the driver is removed from the kernel + * with the rmmod command. The driver unregisters itself with its bus + * driver. + */ +static void __exit s5pc110_otg_module_exit(void) +{ + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "s3c_otg_module_exit \n"); + platform_driver_unregister(&s5pc110_otg_driver); +} + +/* for debug */ +void otg_print_registers(void) +{ + /* USB PHY Control Registers */ + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "USB_CONTROL = 0x%x.\n", readl(0xfb10e80c)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "UPHYPWR = 0x%x.\n", readl(S3C_USBOTG_PHYPWR)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "UPHYCLK = 0x%x.\n", readl(S3C_USBOTG_PHYCLK)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "URSTCON = 0x%x.\n", readl(S3C_USBOTG_RSTCON)); + + /* OTG LINK Core registers (Core Global Registers) */ + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "GOTGCTL = 0x%x.\n", read_reg_32(GOTGCTL)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "GOTGINT = 0x%x.\n", read_reg_32(GOTGINT)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "GAHBCFG = 0x%x.\n", read_reg_32(GAHBCFG)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "GUSBCFG = 0x%x.\n", read_reg_32(GUSBCFG)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "GINTSTS = 0x%x.\n", read_reg_32(GINTSTS)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "GINTMSK = 0x%x.\n", read_reg_32(GINTMSK)); + + /* Host Mode Registers */ + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "HCFG = 0x%x.\n", read_reg_32(HCFG)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "HPRT = 0x%x.\n", read_reg_32(HPRT)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "HFIR = 0x%x.\n", read_reg_32(HFIR)); + + /* Synopsys ID */ + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "GSNPSID = 0x%x.\n", read_reg_32(GSNPSID)); + + /* HWCFG */ + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "GHWCFG1 = 0x%x.\n", read_reg_32(GHWCFG1)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "GHWCFG2 = 0x%x.\n", read_reg_32(GHWCFG2)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "GHWCFG3 = 0x%x.\n", read_reg_32(GHWCFG3)); + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "GHWCFG4 = 0x%x.\n", read_reg_32(GHWCFG4)); + + /* PCGCCTL */ + otg_dbg(OTG_DBG_OTGHCDI_DRIVER, + "PCGCCTL = 0x%x.\n", read_reg_32(PCGCCTL)); +} + +/* +module_init(s5pc110_otg_module_init); +module_exit(s5pc110_otg_module_exit); +*/ + +MODULE_DESCRIPTION("OTG USB HOST controller driver"); +MODULE_AUTHOR("SAMSUNG / System LSI / EMSP"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.h b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.h new file mode 100644 index 0000000..4488df2 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-driver.h @@ -0,0 +1,75 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * @file s3c-otg-hcdi-driver.h + * @brief header of s3c-otg-hcdi-driver \n + * @version + * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Creating the initial version of this code \n + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * -# Aug 18,2008 v1.3 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Modifying for successful rmmod & disconnecting \n + * @see None + ****************************************************************************/ +/**************************************************************************** + * 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 _S3C_OTG_HCDI_DRIVER_H_ +#define _S3C_OTG_HCDI_DRIVER_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <linux/module.h> +#include <linux/init.h> +//#include <linux/clk.h> //for clk_get, clk_enable etc. + +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/errno.h> +#include <linux/interrupt.h> //for SA_SHIRQ +#include <mach/map.h> //address for smdk +#include <linux/dma-mapping.h> //dma_alloc_coherent +#include <linux/ioport.h> //request_mem_request ... +#include <asm/irq.h> //for IRQ_OTG +#include <linux/clk.h> + + +#include "s3c-otg-common-common.h" +#include "s3c-otg-common-regdef.h" + +#include "s3c-otg-hcdi-debug.h" +#include "s3c-otg-hcdi-hcd.h" +#include "s3c-otg-hcdi-kal.h" + + +volatile u8 * g_pUDCBase; + +static const char gHcdName[] = "EMSP_OTG_HCD"; + +//extern int otg_hcd_init_modules(struct sec_otghost *otghost); +//extern void otg_hcd_deinit_modules(struct sec_otghost *otghost); + +//void otg_print_registers(); + +#ifdef __cplusplus +} +#endif + +#endif /* _S3C_OTG_HCDI_DRIVER_H_ */ diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.c b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.c new file mode 100644 index 0000000..fe1050d --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.c @@ -0,0 +1,706 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * @file s3c-otg-hcdi-hcd.c + * @brief implementation of structure hc_drive \n + * @version + * -# Jun 11,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Creating the initial version of this code \n + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * -# Aug 18,2008 v1.3 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Modifying for successful rmmod & disconnecting \n + * @see None + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-hcdi-hcd.h" + +/** + * otg_hcd_init_modules(struct sec_otghost *otghost) + * + * @brief call other modules' init functions + * + * @return PASS : If success \n + * FAIL : If fail \n + */ +int otg_hcd_init_modules(struct sec_otghost *otghost) +{ + unsigned long spin_lock_flag = 0; + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "otg_hcd_init_modules\n"); + + spin_lock_init(&otghost->lock); + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + + init_transfer(); + init_scheduler(); + oci_init(otghost); + + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + + return USB_ERR_SUCCESS; +}; + +/** + * void otg_hcd_deinit_modules(struct sec_otghost *otghost) + * + * @brief call other modules' de-init functions + * + * @return PASS : If success \n + * FAIL : If fail \n + */ +void otg_hcd_deinit_modules(struct sec_otghost *otghost) +{ + unsigned long spin_lock_flag = 0; + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "otg_hcd_deinit_modules \n"); + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + + deinit_transfer(otghost); + + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); +} + +/** + * irqreturn_t (*s5pc110_otghcd_irq) (struct usb_hcd *hcd) + * + * @brief interrupt handler of otg irq + * + * @param [in] hcd : pointer of usb_hcd + * + * @return IRQ_HANDLED \n + */ +irqreturn_t s5pc110_otghcd_irq(struct usb_hcd *hcd) +{ + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_irq \n"); + + spin_lock_otg(&otghost->lock); + otg_handle_interrupt(hcd); + spin_unlock_otg(&otghost->lock); + + return IRQ_HANDLED; +} + +/** + * int s5pc110_otghcd_start(struct usb_hcd *hcd) + * + * @brief initialize and start otg hcd + * + * @param [in] usb_hcd_p : pointer of usb_hcd + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + */ +int s5pc110_otghcd_start(struct usb_hcd *usb_hcd_p) +{ + struct usb_bus *usb_bus_p; + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_start \n"); + + usb_bus_p = hcd_to_bus(usb_hcd_p); + + /* Initialize and connect root hub if one is not already attached */ + if (usb_bus_p->root_hub) { + otg_dbg(OTG_DBG_OTGHCDI_HCD, "OTG HCD Has Root Hub\n"); + + /* Inform the HUB driver to resume. */ + otg_usbcore_resume_roothub(); + } else { + otg_err(OTG_DBG_OTGHCDI_HCD, + "OTG HCD Does Not Have Root Hub\n"); + return USB_ERR_FAIL; + } + + set_bit(HCD_FLAG_POLL_RH,&usb_hcd_p->flags); + usb_hcd_p->uses_new_polling = 1; + + /* init bus state before enable irq */ + usb_hcd_p->state = HC_STATE_RUNNING; + + oci_start(); /* enable irq */ + + return USB_ERR_SUCCESS; +} + +/** + * void s5pc110_otghcd_stop(struct usb_hcd *hcd) + * + * @brief deinitialize and stop otg hcd + * + * @param [in] hcd : pointer of usb_hcd + * + */ +void s5pc110_otghcd_stop(struct usb_hcd *hcd) +{ + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_stop \n"); + + otg_hcd_deinit_modules(otghost); + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + + oci_stop(); + root_hub_feature(hcd, 0, ClearPortFeature, USB_PORT_FEAT_POWER, NULL); + + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); +} + +/** + * void s5pc110_otghcd_shutdown(struct usb_hcd *hcd) + * + * @brief shutdown otg hcd + * + * @param [in] usb_hcd_p : pointer of usb_hcd + * + */ +void s5pc110_otghcd_shutdown(struct usb_hcd *usb_hcd_p) +{ + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(usb_hcd_p); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_shutdown \n"); + otg_hcd_deinit_modules(otghost); + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + + oci_stop(); + root_hub_feature(usb_hcd_p, 0, ClearPortFeature, USB_PORT_FEAT_POWER, NULL); + + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + + free_irq(IRQ_OTG, usb_hcd_p); + usb_hcd_p->state = HC_STATE_HALT; + otg_usbcore_hc_died(); +} + + +/** + * int s5pc110_otghcd_get_frame_number(struct usb_hcd *hcd) + * + * @brief get currnet frame number + * + * @param [in] hcd : pointer of usb_hcd + * + * @return ret : frame number \n + */ +int s5pc110_otghcd_get_frame_number(struct usb_hcd *hcd) +{ + int ret = 0; + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_get_frame_number \n"); + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + ret = oci_get_frame_num(); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + + return ret; +} + + +/** + * int s5pc110_otghcd_urb_enqueue() + * + * @brief enqueue a urb to otg hcd + * + * @param [in] hcd : pointer of usb_hcd + * [in] ep : pointer of usb_host_endpoint + * [in] urb : pointer of urb + * [in] mem_flags : type of gfp_t + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + */ +int s5pc110_otghcd_urb_enqueue (struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags) +{ + int ret_val = 0; + u32 trans_flag = 0; + u32 return_td_addr = 0; + u8 dev_speed, ed_type = 0, additional_multi_count; + u16 max_packet_size; + + u8 dev_addr = 0; + u8 ep_num = 0; + bool f_is_ep_in = true; + u8 interval = 0; + u32 sched_frame = 0; + u8 hub_addr = 0; + u8 hub_port = 0; + bool f_is_do_split = false; + ed_t *target_ed = NULL; + isoch_packet_desc_t *new_isoch_packet_desc = NULL; + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + if (!otghost->port_flag.b.port_connect_status) { + printk(KERN_ERR"%s %d\n", __func__, __LINE__); + return USB_ERR_NOIO; + } + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_urb_enqueue \n"); + + /* check ep has ed_t or not */ + if(!(urb->ep->hcpriv)) { + /* for getting dev_speed */ + switch (urb->dev->speed) { + case USB_SPEED_HIGH : + otg_dbg(OTG_DBG_OTGHCDI_HCD, "HS_OTG \n"); + dev_speed = HIGH_SPEED_OTG; + break; + + case USB_SPEED_FULL : + otg_dbg(OTG_DBG_OTGHCDI_HCD, "FS_OTG \n"); + dev_speed = FULL_SPEED_OTG; + break; + + case USB_SPEED_LOW : + otg_dbg(OTG_DBG_OTGHCDI_HCD, "LS_OTG \n"); + dev_speed = LOW_SPEED_OTG; + break; + + default: + otg_err(OTG_DBG_OTGHCDI_HCD, + "unKnown Device Speed \n"); + spin_unlock_irq_save_otg(&otghost->lock, + spin_lock_flag); + return USB_ERR_FAIL; + } + + /* for getting ed_type */ + switch (usb_pipetype(urb->pipe)) { + case PIPE_BULK : + otg_dbg(OTG_DBG_OTGHCDI_HCD, "bulk transfer \n"); + ed_type = BULK_TRANSFER; + break; + + case PIPE_INTERRUPT : + otg_dbg(OTG_DBG_OTGHCDI_HCD, "interrupt transfer \n"); + ed_type = INT_TRANSFER; + break; + + case PIPE_CONTROL : + otg_dbg(OTG_DBG_OTGHCDI_HCD, "control transfer \n"); + ed_type = CONTROL_TRANSFER; + break; + + case PIPE_ISOCHRONOUS : + otg_dbg(OTG_DBG_OTGHCDI_HCD, "isochronous transfer \n"); + ed_type = ISOCH_TRANSFER; + break; + default: + otg_err(OTG_DBG_OTGHCDI_HCD, "unKnown ep type \n"); + spin_unlock_irq_save_otg(&otghost->lock, + spin_lock_flag); + return USB_ERR_FAIL; + } + + max_packet_size = usb_maxpacket(urb->dev, urb->pipe, + !(usb_pipein(urb->pipe))); + additional_multi_count = ((max_packet_size) >> 11) & 0x03; + dev_addr = usb_pipedevice(urb->pipe); + ep_num = usb_pipeendpoint(urb->pipe); + f_is_ep_in = usb_pipein(urb->pipe) ? true : false; + interval = (u8)(urb->interval); + sched_frame = (u8)(urb->start_frame); + + /* check */ + if(urb->dev->tt == NULL) { + otg_dbg(OTG_DBG_OTGHCDI_HCD, "urb->dev->tt == NULL\n"); + hub_port = 0; /* u8 hub_port */ + hub_addr = 0; /* u8 hub_addr */ + } + else { + hub_port = (u8)(urb->dev->ttport); + if (urb->dev->tt->hub) { + if (((dev_speed == FULL_SPEED_OTG) || + (dev_speed == LOW_SPEED_OTG)) && + (urb->dev->tt) && (urb->dev->tt->hub->devnum != 1)) { + f_is_do_split = true; + } + hub_addr = (u8)(urb->dev->tt->hub->devnum); + } + if (urb->dev->tt->multi) { + hub_addr = 0x80; + } + } + otg_dbg(OTG_DBG_OTGHCDI_HCD, + "dev_spped =%d, hub_port=%d, hub_addr=%d\n", + dev_speed, hub_port, hub_addr); + + ret_val = create_ed(&target_ed); + if(ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, + "fail to create_ed() \n"); + spin_unlock_irq_save_otg(&otghost->lock, + spin_lock_flag); + return ret_val; + } + + ret_val = init_ed( target_ed, + dev_addr, + ep_num, + f_is_ep_in, + dev_speed, + ed_type, + max_packet_size, + additional_multi_count, + interval, + sched_frame, + hub_addr, + hub_port, + f_is_do_split, + (void *)urb->ep); + + if(ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, + "fail to init_ed() :err = %d \n",(int)ret_val); + otg_mem_free(target_ed); + spin_unlock_irq_save_otg(&otghost->lock, + spin_lock_flag); + return USB_ERR_FAIL; + } + + urb->ep->hcpriv = (void *)(target_ed); + } /* if(!(ep->hcpriv)) */ + else { + dev_addr = usb_pipedevice(urb->pipe); + if(((ed_t *)(urb->ep->hcpriv))->ed_desc.device_addr != dev_addr) { + ((ed_t *)urb->ep->hcpriv)->ed_desc.device_addr = dev_addr; + } + } + + target_ed = (ed_t *)urb->ep->hcpriv; + + if(urb->transfer_flags & URB_SHORT_NOT_OK) + trans_flag += USB_TRANS_FLAG_NOT_SHORT; + if (urb->transfer_flags & URB_ISO_ASAP) + trans_flag += USB_TRANS_FLAG_ISO_ASYNCH; + + if(ed_type == ISOCH_TRANSFER) { + otg_err(OTG_DBG_OTGHCDI_HCD, "ISO not yet supported \n"); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + return USB_ERR_FAIL; + } + + if (!HC_IS_RUNNING(hcd->state)) { + otg_err(OTG_DBG_OTGHCDI_HCD, "!HC_IS_RUNNING(hcd->state) \n"); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + return -USB_ERR_NODEV; + } + + /* in case of unlink-during-submit */ + if (urb->status != -EINPROGRESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, "urb->status is -EINPROGRESS\n"); + urb->hcpriv = NULL; + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + usb_hcd_giveback_urb(hcd, urb, urb->status); + return USB_ERR_SUCCESS; + } + + ret_val = issue_transfer(otghost, target_ed, (void *)NULL, (void *)NULL, + trans_flag, + (usb_pipetype(urb->pipe) == PIPE_CONTROL)?true:false, + (u32)urb->setup_packet, (u32)urb->setup_dma, + (u32)urb->transfer_buffer, (u32)urb->transfer_dma, + (u32)urb->transfer_buffer_length, + (u32)urb->start_frame,(u32)urb->number_of_packets, + new_isoch_packet_desc, (void *)urb, &return_td_addr); + + if(ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, "fail to issue_transfer() \n"); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + return USB_ERR_FAIL; + } + urb->hcpriv = (void *)return_td_addr; + + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + return USB_ERR_SUCCESS; +} + +/** + * int s5pc110_otghcd_urb_dequeue(struct usb_hcd *_hcd, struct urb *_urb ) + * + * @brief dequeue a urb to otg + * + * @param [in] _hcd : pointer of usb_hcd + * [in] _urb : pointer of urb + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + */ +int s5pc110_otghcd_urb_dequeue( + struct usb_hcd *_hcd, struct urb *_urb, int status) +{ + int ret_val = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(_hcd); + + unsigned long spin_lock_flag = 0; + td_t *cancel_td; + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_urb_dequeue \n"); + + /* Dequeue should be performed only if endpoint is enabled */ + if (_urb->ep->enabled == 0) { + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + usb_hcd_giveback_urb(_hcd, _urb, status); + return USB_ERR_SUCCESS; + } + + //kevinh read this from inside the spinlock + cancel_td = (td_t *)_urb->hcpriv; + + if (cancel_td == NULL) { + otg_err(OTG_DBG_OTGHCDI_HCD, "cancel_td is NULL\n"); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + return USB_ERR_FAIL; + } + otg_dbg(OTG_DBG_OTGHCDI_HCD, + "s5pc110_otghcd_urb_dequeue, status = %d\n", status); + + + ret_val = usb_hcd_check_unlink_urb(_hcd, _urb, status); + if( (ret_val) && (ret_val != -EIDRM) ) { + otg_dbg(OTG_DBG_OTGHCDI_HCD, "ret_val = %d\n", ret_val); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + usb_hcd_giveback_urb(_hcd, _urb, status); + return ret_val; + } + + if (!HC_IS_RUNNING(_hcd->state)) { + otg_err(OTG_DBG_OTGHCDI_HCD, "!HC_IS_RUNNING(hcd->state) \n"); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + otg_usbcore_giveback(cancel_td); + return USB_ERR_SUCCESS; + } + + ret_val = cancel_transfer(otghost, cancel_td->parent_ed_p, cancel_td); + if(ret_val != USB_ERR_DEQUEUED && ret_val != USB_ERR_NOELEMENT) { + otg_err(OTG_DBG_OTGHCDI_HCD, "fail to cancel_transfer() \n"); + otg_usbcore_giveback(cancel_td); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + return USB_ERR_FAIL; + } + otg_usbcore_giveback(cancel_td); + delete_td(otghost, cancel_td); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + return USB_ERR_SUCCESS; +} + +/** + * void s5pc110_otghcd_endpoint_disable( + * struct usb_hcd *hcd, + * struct usb_host_endpoint *ep) + * + * @brief disable a endpoint + * + * @param [in] hcd : pointer of usb_hcd + * [in] ep : pointer of usb_host_endpoint + */ +void s5pc110_otghcd_endpoint_disable( + struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + int ret_val = 0; + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_endpoint_disable \n"); + + if(!((ed_t *)ep->hcpriv)) + return; + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + ret_val = delete_ed(otghost, (ed_t *)ep->hcpriv); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + + if(ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, "fail to delete_ed() \n"); + return ; + } + + /* ep->hcpriv = NULL; delete_ed coveres it */ +} + +/** + * int s5pc110_otghcd_hub_status_data(struct usb_hcd *_hcd, char *_buf) + * + * @brief get status of root hub + * + * @param [in] _hcd : pointer of usb_hcd + * [inout] _buf : pointer of buffer for write a status data + * + * @return ret_val : return port status \n + */ +int s5pc110_otghcd_hub_status_data(struct usb_hcd *_hcd, char *_buf) +{ + int ret_val = 0; + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(_hcd); + + /* otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_hub_status_data \n"); */ + + /* if !USB_SUSPEND, root hub timers won't get shut down ... */ + if (!HC_IS_RUNNING(_hcd->state)) { + otg_dbg(OTG_DBG_OTGHCDI_HCD, + "_hcd->state is NOT HC_IS_RUNNING \n"); + return 0; + } + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + ret_val = get_otg_port_status(_hcd, OTG_PORT_NUMBER, _buf); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + + return (int)ret_val; +} + +/** + * int s5pc110_otghcd_hub_control() + * + * @brief control root hub + * + * @param [in] hcd : pointer of usb_hcd + * [in] typeReq : type of control request + * [in] value : value + * [in] index : index + * [in] buf_p : pointer of urb + * [in] length : type of gfp_t + * + * @return ret_val : return root_hub_feature \n + */ +int +s5pc110_otghcd_hub_control ( + struct usb_hcd *hcd, + u16 typeReq, + u16 value, + u16 index, + char* buf_p, + u16 length +) +{ + int ret_val = 0; + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_hub_control \n"); + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + + ret_val = root_hub_feature(hcd, OTG_PORT_NUMBER, + typeReq, value, (void *)buf_p); + + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + if(ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, "fail to root_hub_feature() \n"); + return ret_val; + } + return (int)ret_val; +} + +/** + * int s5pc110_otghcd_bus_suspend(struct usb_hcd *hcd) + * + * @brief suspend otg hcd + * + * @param [in] hcd : pointer of usb_hcd + * + * @return USB_ERR_SUCCESS \n + */ +int s5pc110_otghcd_bus_suspend(struct usb_hcd *hcd) +{ + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_bus_suspend \n"); + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + bus_suspend(); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + + return USB_ERR_SUCCESS; +} + +/** + * int s5pc110_otghcd_bus_resume(struct usb_hcd *hcd) + * + * @brief resume otg hcd + * + * @param [in] hcd : pointer of usb_hcd + * + * @return USB_ERR_SUCCESS \n + */ +int s5pc110_otghcd_bus_resume(struct usb_hcd *hcd) +{ + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_bus_resume \n"); + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + + if(bus_resume(otghost) != USB_ERR_SUCCESS) { + return USB_ERR_FAIL; + } + + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + return USB_ERR_SUCCESS; +} + +/** + * int s5pc110_otghcd_start_port_reset(struct usb_hcd *hcd, unsigned port) + * + * @brief reset otg port + * + * @param [in] hcd : pointer of usb_hcd + * [in] port : number of port + * + * @return USB_ERR_SUCCESS : If success \n + * ret_val : If call fail \n + */ +int s5pc110_otghcd_start_port_reset(struct usb_hcd *hcd, unsigned port) +{ + int ret_val = 0; + + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_start_port_reset \n"); + + spin_lock_irq_save_otg(&otghost->lock, spin_lock_flag); + ret_val = reset_and_enable_port(hcd, OTG_PORT_NUMBER); + spin_unlock_irq_save_otg(&otghost->lock, spin_lock_flag); + + if(ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, + "fail to reset_and_enable_port() \n"); + return ret_val; + } + return USB_ERR_SUCCESS; +} diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.h b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.h new file mode 100644 index 0000000..95764d8 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-hcd.h @@ -0,0 +1,152 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * @file s3c-otg-hcdi-hcd.h + * @brief header of s3c-otg-hcdi-hcd \n + * @version + * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Creating the initial version of this code \n + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * -# Aug 18,2008 v1.3 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Modifying for successful rmmod & disconnecting \n + * @see None + ****************************************************************************/ +/**************************************************************************** + * 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 _S3C_OTG_HCDI_HCD_H_ +#define _S3C_OTG_HCDI_HCD_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +//for IRQ_NONE (0) IRQ_HANDLED (1) IRQ_RETVAL(x) ((x) != 0) +#include <linux/interrupt.h> + +#include <linux/usb.h> + +#include "s3c-otg-hcdi-debug.h" +#include "s3c-otg-hcdi-kal.h" + +#include "s3c-otg-common-common.h" +#include "s3c-otg-common-datastruct.h" +#include "s3c-otg-common-const.h" + +#include "s3c-otg-transfer-transfer.h" +#include "s3c-otg-oci.h" +#include "s3c-otg-roothub.h" + +//placed in ISR +//void otg_handle_interrupt(struct usb_hcd *hcd); + +irqreturn_t s5pc110_otghcd_irq(struct usb_hcd *hcd); + +int s5pc110_otghcd_start(struct usb_hcd *hcd); +void s5pc110_otghcd_stop(struct usb_hcd *hcd); +void s5pc110_otghcd_shutdown(struct usb_hcd *hcd); + +int s5pc110_otghcd_get_frame_number(struct usb_hcd *hcd); + +int s5pc110_otghcd_urb_enqueue( + struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags); + +int s5pc110_otghcd_urb_dequeue( + struct usb_hcd *_hcd, + struct urb *_urb, + int status); + +void s5pc110_otghcd_endpoint_disable( + struct usb_hcd *hcd, + struct usb_host_endpoint *ep); + +int s5pc110_otghcd_hub_status_data( + struct usb_hcd *_hcd, + char *_buf); + +int s5pc110_otghcd_hub_control( + struct usb_hcd *hcd, + u16 type_req, + u16 value, + u16 index, + char * buf, + u16 length); + +int s5pc110_otghcd_bus_suspend(struct usb_hcd *hcd); +int s5pc110_otghcd_bus_resume(struct usb_hcd *hcd); +int s5pc110_otghcd_start_port_reset(struct usb_hcd *hcd, unsigned port); + +/** + * @struct hc_driver s5pc110_otg_hc_driver + * + * @brief implementation of hc_driver for OTG HCD + * + * describe in detail + */ +static const struct hc_driver s5pc110_otg_hc_driver = { + .description = "EMSP_OTGHCD", + .product_desc = "S3C OTGHCD", + .hcd_priv_size = sizeof(struct sec_otghost), + + .irq = s5pc110_otghcd_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /** basic lifecycle operations */ + //.reset = + .start = s5pc110_otghcd_start, + //.suspend = , + //.resume = , + .stop = s5pc110_otghcd_stop, + .shutdown = s5pc110_otghcd_shutdown, + + /** managing i/o requests and associated device resources */ + .urb_enqueue = s5pc110_otghcd_urb_enqueue, + .urb_dequeue = s5pc110_otghcd_urb_dequeue, + .endpoint_disable = s5pc110_otghcd_endpoint_disable, + + /** scheduling support */ + .get_frame_number = s5pc110_otghcd_get_frame_number, + + /** root hub support */ + .hub_status_data = s5pc110_otghcd_hub_status_data, + .hub_control = s5pc110_otghcd_hub_control, + //.hub_irq_enable = + .bus_suspend = s5pc110_otghcd_bus_suspend, + .bus_resume = s5pc110_otghcd_bus_resume, + .start_port_reset = s5pc110_otghcd_start_port_reset, +}; + +static inline struct sec_otghost *hcd_to_sec_otghost (struct usb_hcd *hcd) +{ + return (struct sec_otghost *) (hcd->hcd_priv); +} +static inline struct usb_hcd *sec_otghost_to_hcd (struct sec_otghost *otghost) +{ + return container_of ((void *) otghost, struct usb_hcd, hcd_priv); +} + +int otg_hcd_init_modules(struct sec_otghost *otghost); +void otg_hcd_deinit_modules(struct sec_otghost *otghost); + +#ifdef __cplusplus +} +#endif +#endif /* _S3C_OTG_HCDI_HCD_H_ */ + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-kal.h b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-kal.h new file mode 100644 index 0000000..23bded6 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-kal.h @@ -0,0 +1,420 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * @file s3c-otg-hcdi-kal.h + * @brief header of s3c-otg-hcdi-kal \n + * @version + * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Creating the initial version of this code \n + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * -# Aug 18,2008 v1.3 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Modifying for successful rmmod & disconnecting \n + * @see None + ****************************************************************************/ +/**************************************************************************** + * 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 _S3C_OTG_HCDI_KAL_H_ +#define _S3C_OTG_HCDI_KAL_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "s3c-otg-hcdi-debug.h" +#include "s3c-otg-common-common.h" +#include "s3c-otg-common-datastruct.h" +#include "s3c-otg-common-const.h" + +#include <asm/io.h> //for readl, writel +#include <linux/usb/ch9.h> //for usb_device_driver, enum usb_device_speed +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include <mach/map.h> +#include <plat/regs-otg.h> + +extern volatile u8 * g_pUDCBase; +extern struct usb_hcd* g_pUsbHcd; + +#include <linux/spinlock.h> +#define SPINLOCK_t spinlock_t +#define SPIN_LOCK_INIT SPIN_LOCK_UNLOCKED + +#define spin_lock_otg(lock) spin_lock(lock) +#define spin_lock_irg_otg(lock) spin_lock_irq(lock) +#define spin_lock_irq_save_otg(lock, flags) spin_lock_irqsave(lock, flags) + +#define spin_unlock_otg(lock) spin_unlock(lock) +#define spin_unlock_irq_otg(lock) spin_unlock_irq(lock) +#define spin_unlock_irq_save_otg(lock, flags) spin_unlock_irqrestore(lock, flags) + +#define ctrlr_base_reg_addr(offset) \ + ((volatile unsigned int *)((g_pUDCBase) + (offset))) +/** + * otg_kal_make_ep_null + * + * @brief make ep->hcpriv NULL + * + * @param [in] pdelete_ed : pointer of ed + * + * @return void \n + */ +static inline void +otg_kal_make_ep_null +( + ed_t *pdelete_ed +) +{ + ((struct usb_host_endpoint *)(pdelete_ed->ed_private))->hcpriv = NULL; +} +//--------------------------------------------------------------------------------------- + +/** + * otg_kal_is_ep_null + * + * @brief check ep->hcpriv is NULL or not + * + * @param [in] pdelete_ed : pointer of ed + * + * @return bool \n + */ +static inline bool +otg_kal_is_ep_null +( + ed_t *pdelete_ed +) +{ + if (((struct usb_host_endpoint *)(pdelete_ed->ed_private))->hcpriv == NULL) + return true; + else + return false; +} +//--------------------------------------------------------------------------------------- + + +/** + * int otg_usbcore_get_calc_bustime() + * + * @brief get bus time of usbcore + * + * @param [in] speed : usb speed + * [in] is_input : input or not + * [in] is_isoch : isochronous or not + * [in] byte_count : bytes + * + * @return bus time of usbcore \n + */ +static inline int +otg_usbcore_get_calc_bustime +( + u8 speed, + bool is_input, + bool is_isoch, + unsigned int byte_count +) +{ + unsigned int convert_speed = 0; + + otg_dbg(OTG_DBG_OTGHCDI_KAL, "otg_usbcore_get_calc_bustime \n"); +/* enum usb_device_speed { + USB_SPEED_UNKNOWN = 0, + USB_SPEED_LOW, USB_SPEED_FULL, + USB_SPEED_HIGH, + USB_SPEED_VARIABLE, };*/ + switch(speed) { + case HIGH_SPEED_OTG : + convert_speed = USB_SPEED_HIGH; + break; + + case FULL_SPEED_OTG : + convert_speed = USB_SPEED_FULL; + break; + + case LOW_SPEED_OTG : + convert_speed = USB_SPEED_LOW; + break; + + default: + convert_speed = USB_SPEED_UNKNOWN; + break; + } + return usb_calc_bus_time(convert_speed, is_input, (unsigned int)is_isoch, byte_count); +} + +//------------------------------------------------------------------------------- + +/** + * void otg_usbcore_giveback(td_t td_p) + * + * @brief give-back a td as urb + * + * @param [in] td_p : pointer of td_t to give back + * + * @return void \n + */ +static inline void +otg_usbcore_giveback(td_t * td_p) +{ + struct urb *urb_p = NULL; + + otg_dbg(OTG_DBG_OTGHCDI_KAL, "otg_usbcore_giveback \n"); + + if (td_p->td_private == NULL) + { + otg_err(OTG_DBG_OTGHCDI_KAL, + "td_p->td_private == NULL \n"); + return; + } + + urb_p = (struct urb *)td_p->td_private; + + urb_p->actual_length = (int)(td_p->transferred_szie); + urb_p->status = (int)(td_p->error_code); + urb_p->error_count = (int)(td_p->err_cnt); + urb_p->hcpriv = NULL; + + usb_hcd_giveback_urb(g_pUsbHcd, urb_p, urb_p->status); +} +//------------------------------------------------------------------------------- + +/** + * void otg_usbcore_hc_died(void) + * + * @brief inform usbcore of hc die + * + * @return void \n + */ +static inline void +otg_usbcore_hc_died(void) +{ + otg_dbg(OTG_DBG_OTGHCDI_KAL, "otg_usbcore_hc_died \n"); + usb_hc_died(g_pUsbHcd); +} +//------------------------------------------------------------------------------- + +/** + * void otg_usbcore_poll_rh_status(void) + * + * @brief invoke usbcore's usb_hcd_poll_rh_status + * + * @param void + * + * @return void \n + */ +static inline void +otg_usbcore_poll_rh_status(void) +{ + usb_hcd_poll_rh_status(g_pUsbHcd); +} +//------------------------------------------------------------------------------- + +/** + * void otg_usbcore_resume_roothub(void) + * + * @brief invoke usbcore's usb_hcd_resume_root_hub + * + * @param void + * + * @return void \n + */ +static inline void +otg_usbcore_resume_roothub(void) +{ + otg_dbg(OTG_DBG_OTGHCDI_KAL, + "otg_usbcore_resume_roothub \n"); + usb_hcd_resume_root_hub(g_pUsbHcd); +}; +//------------------------------------------------------------------------------- + +/** + * int otg_usbcore_inc_usb_bandwidth(u32 band_width) + * + * @brief increase bandwidth of usb bus + * + * @param [in] band_width : bandwidth to be increased + * + * @return USB_ERR_SUCCESS \n + */ +static inline int +otg_usbcore_inc_usb_bandwidth(u32 band_width) +{ + otg_dbg(OTG_DBG_OTGHCDI_KAL, + "otg_usbcore_inc_usb_bandwidth \n"); + hcd_to_bus(g_pUsbHcd)->bandwidth_allocated += band_width; + return USB_ERR_SUCCESS; +} +//------------------------------------------------------------------------------- + +/** + * int otg_usbcore_des_usb_bandwidth(u32 uiBandwidth) + * + * @brief decrease bandwidth of usb bus + * + * @param [in] band_width : bandwidth to be decreased + * + * @return USB_ERR_SUCCESS \n + */ +static inline int +otg_usbcore_des_usb_bandwidth(u32 band_width) +{ + otg_dbg(OTG_DBG_OTGHCDI_KAL, + "otg_usbcore_des_usb_bandwidth \n"); + hcd_to_bus(g_pUsbHcd)->bandwidth_allocated -= band_width; + return USB_ERR_SUCCESS; +} +//------------------------------------------------------------------------------- + +/** + * int otg_usbcore_inc_periodic_transfer_cnt(u8 transfer_type) + * + * @brief increase count of periodic transfer + * + * @param [in] transfer_type : type of transfer + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + */ +static inline int +otg_usbcore_inc_periodic_transfer_cnt(u8 transfer_type) +{ + otg_dbg(OTG_DBG_OTGHCDI_KAL, + "otg_usbcore_inc_periodic_transfer_cnt \n"); + + switch(transfer_type) { + case INT_TRANSFER : + hcd_to_bus(g_pUsbHcd)->bandwidth_int_reqs++; + break; + case ISOCH_TRANSFER : + hcd_to_bus(g_pUsbHcd)->bandwidth_isoc_reqs++; + break; + default: + otg_err(OTG_DBG_OTGHCDI_KAL, + "not proper TransferType for otg_usbcore_inc_periodic_transfer_cnt()\n"); + return USB_ERR_FAIL; + } + return USB_ERR_SUCCESS; +} +//------------------------------------------------------------------------------- + +/** + * int otg_usbcore_des_periodic_transfer_cnt(u8 transfer_type) + * + * @brief decrease count of periodic transfer + * + * @param [in] transfer_type : type of transfer + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + */ +static inline int +otg_usbcore_des_periodic_transfer_cnt(u8 transfer_type) +{ + otg_dbg(OTG_DBG_OTGHCDI_KAL, + "otg_usbcore_des_periodic_transfer_cnt \n"); + + switch(transfer_type) { + case INT_TRANSFER : + hcd_to_bus(g_pUsbHcd)->bandwidth_int_reqs--; + break; + case ISOCH_TRANSFER : + hcd_to_bus(g_pUsbHcd)->bandwidth_isoc_reqs--; + break; + default: + otg_err(OTG_DBG_OTGHCDI_KAL, + "not proper TransferType for otg_usbcore_des_periodic_transfer_cnt()\n"); + return USB_ERR_FAIL; + } + return USB_ERR_SUCCESS; +} +//------------------------------------------------------------------------------- + +/** + * u32 read_reg_32(u32 offset) + * + * @brief Reads the content of a register. + * + * @param [in] offset : offset of address of register to read. + * + * @return contents of the register. \n + * @remark call readl() + */ +static inline u32 read_reg_32(u32 offset) +{ + volatile unsigned int * reg_addr_p = ctrlr_base_reg_addr(offset); + + return *reg_addr_p; + //return readl(reg_addr_p); +}; +//------------------------------------------------------------------------------- + +/** + * void write_reg_32( u32 offset, const u32 value) + * + * @brief Writes a register with a 32 bit value. + * + * @param [in] offset : offset of address of register to write. + * @param [in] value : value to write + * + * @remark call writel() + */ +static inline void write_reg_32( u32 offset, const u32 value) +{ + volatile unsigned int * reg_addr_p = ctrlr_base_reg_addr(offset); + + *reg_addr_p = value; + //writel( value, reg_addr_p ); +}; +//------------------------------------------------------------------------------- + +/** + * void update_reg_32(u32 offset, u32 value) + * + * @brief logic or operation + * + * @param [in] offset : offset of address of register to write. + * @param [in] value : value to or + * + */ +static inline void update_reg_32(u32 offset, u32 value) +{ + write_reg_32(offset, (read_reg_32(offset) | value)); +} +//--------------------------------------------------------------------------------------- + +/** + * void clear_reg_32(u32 offset, u32 value) + * + * @brief logic not operation + * + * @param [in] offset : offset of address of register to write. + * @param [in] value : value to not + * + */ +static inline void clear_reg_32(u32 offset, u32 value) +{ + write_reg_32(offset, (read_reg_32(offset) & ~value)); +} +//--------------------------------------------------------------------------------------- + + +#ifdef __cplusplus +} +#endif + +#endif /* _S3C_OTG_HCDI_KAL_H_ */ + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-list.h b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-list.h new file mode 100644 index 0000000..03ae3d8 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-list.h @@ -0,0 +1,217 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * @file s3c-otg-hcdi-list.h + * @brief list functions for otg \n + * @version + * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Creating the initial version of this code \n + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * @see None + ****************************************************************************/ +/**************************************************************************** + * 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 _S3C_OTG_HCDI_LIST_H_ +#define _S3C_OTG_HCDI_LIST_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "s3c-otg-common-common.h" +#include "s3c-otg-hcdi-debug.h" + +#include <linux/list.h> + +typedef struct list_head otg_list_head; + +#define otg_list_get_node(ptr, type, member) container_of(ptr, type, member) + + +#define otg_list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + + +/** + * void otg_list_push_next(otg_list_head *new_node_p, otg_list_head *list_head_p) + * + * @brief push a list node into the next of head + * + * @param [in] new_node_p : node to be pushed + * @param [in] otg_list_head : target list head + * + * @return void \n + */ + +static inline +void otg_list_push_next(otg_list_head *new_node_p, otg_list_head *list_head_p) +{ + otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_push_next \n"); + list_add(new_node_p, list_head_p); +} +//------------------------------------------------------------------------------- + +/** + * void otg_list_push_prev(otg_list_head *new_node_p, otg_list_head *list_head_p) + * + * @brief push a list node into the previous of head + * + * @param [in] new_node_p : node to be pushed + * @param [in] otg_list_head : target list head + * + * @return void \n + */ +static inline +void otg_list_push_prev(otg_list_head *new_node_p, otg_list_head *list_head_p) +{ + otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_push_prev \n"); + list_add_tail(new_node_p, list_head_p); +} +//------------------------------------------------------------------------------- + +/** + * void otg_list_pop(otg_list_head *list_entity_p) + * + * @brief pop a list node + * + * @param [in] new_node_p : node to be poped + * @param [in] otg_list_head : target list head + * + * @return void \n + */ +static inline +void otg_list_pop(otg_list_head *list_entity_p) +{ + otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_pop \n"); + list_del(list_entity_p); +} +//------------------------------------------------------------------------------- + +/** + * void otg_list_move_next(otg_list_head *node_p, otg_list_head *list_head_p) + * + * @brief move a list to next of head + * + * @param [in] new_node_p : node to be moved + * @param [in] otg_list_head : target list head + * + * @return void \n + */ +static inline +void otg_list_move_next(otg_list_head *node_p, otg_list_head *list_head_p) +{ + otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_move_next \n"); + list_move(node_p, list_head_p); +} +//------------------------------------------------------------------------------- + +/** + * void otg_list_move_prev(otg_list_head *node_p, otg_list_head *list_head_p) + * + * @brief move a list to previous of head + * + * @param [in] new_node_p : node to be moved + * @param [in] otg_list_head : target list head + * + * @return void \n + */ +static inline +void otg_list_move_prev(otg_list_head *node_p, otg_list_head *list_head_p) +{ + otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_move_prev \n"); + list_move_tail(node_p, list_head_p); +} +//------------------------------------------------------------------------------- + +/** + * bool otg_list_empty(otg_list_head *list_head_p) + * + * @brief check a list empty or not + * + * @param [in] list_head_p : node to check + * + * @return true : empty list \n + * false : not empty list + */ +static inline +bool otg_list_empty(otg_list_head *list_head_p) +{ + + otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_empty \n"); + if(list_empty(list_head_p)) + return true; + return false; +} +//------------------------------------------------------------------------------- + +/** + * void otg_list_merge(otg_list_head *list_p, otg_list_head *head_p) + * + * @brief merge two list + * + * @param [in] list_p : a head + * @param [in] head_p : target list head + * + * @return void \n + */ +static inline +void otg_list_merge(otg_list_head *list_p, otg_list_head *head_p) +{ + otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_merge \n"); + list_splice(list_p, head_p); +} +//------------------------------------------------------------------------------- + +/** + * void otg_list_init(otg_list_head *list_p) + * + * @brief initialize a list + * + * @param [in] list_p : node to be initialized + * + * @return void \n + */ +static inline +void otg_list_init(otg_list_head *list_p) +{ + otg_dbg(OTG_DBG_OTGHCDI_LIST, "otg_list_init \n"); + list_p->next = list_p; + list_p->prev = list_p; +} +//------------------------------------------------------------------------------- + +/* +void otg_list_push_next(otg_list_head *new_node_p, otg_list_head *list_head_p ); +void otg_list_push_prev(otg_list_head *new_node_p, otg_list_head *list_head_p); +void otg_list_pop(otg_list_head *list_entity_p); + +void otg_list_move_next(otg_list_head *node_p, otg_list_head *list_head_p); +void otg_list_move_prev(otg_list_head *node_p, otg_list_head *list_head_p); + +bool otg_list_empty(otg_list_head *list_head_p); +void otg_list_merge(otg_list_head *list_p, otg_list_head *head_p); +void otg_list_init(otg_list_head *list_p); +*/ + +#ifdef __cplusplus +} +#endif +#endif /* _S3C_OTG_HCDI_LIST_H_ */ + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-hcdi-memory.h b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-memory.h new file mode 100644 index 0000000..c87f15e --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-hcdi-memory.h @@ -0,0 +1,191 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * @file s3c-otg-hcdi-memory.h + * @brief header of s3c-otg-hcdi-memory \n + * @version + * -# Jun 9,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Creating the initial version of this code \n + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * @see None + ****************************************************************************/ +/**************************************************************************** + * 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 _S3C_OTG_HCDI_MEMORY_H_ +#define _S3C_OTG_HCDI_MEMORY_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "s3c-otg-common-common.h" +#include "s3c-otg-hcdi-debug.h" + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mm.h> +/** + * @enum otg_mem_alloc_flag + * + * @brief enumeration for flag of memory allocation + */ +typedef +enum otg_mem_alloc_flag +{ + USB_MEM_SYNC, USB_MEM_ASYNC, USB_MEM_DMA +}otg_mem_alloc_flag_t; +//--------------------------------------------------------------------------------------- + +/* +inline int otg_mem_alloc(void ** addr_pp, u16 byte_size, otg_mem_alloc_flag_t type); +inline int otg_mem_copy(void * to_addr_p, void * from_addr_p, u16 byte_size); +//inline int otg_mem_free(void * addr_p); +inline int otg_mem_set(void * addr_p, char value, u16 byte_size); +*/ + +/** + * int otg_mem_alloc(void ** addr_pp, u16 byte_size, u8 ubType); + * + * @brief allocating momory specified + * + * @param [inout] addr_pp : address to be assigned + * [in] byte_size : size of memory + * [in] type : otg_mem_alloc_flag_t type + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + */ +static inline int +otg_mem_alloc ( + void ** addr_pp, + u16 byte_size, + otg_mem_alloc_flag_t type +) +{ + gfp_t flags; + otg_dbg(OTG_DBG_OTGHCDI_MEM, "otg_mem_alloc \n"); + + switch(type) { + case USB_MEM_SYNC: + flags = GFP_KERNEL; + break; + case USB_MEM_ASYNC: + flags = GFP_ATOMIC; + break; + case USB_MEM_DMA: + flags = GFP_DMA; + break; + default: + otg_err(OTG_DBG_OTGHCDI_MEM, + "not proper otg_mem_alloc_flag_t in otg_mem_alloc \n"); + return USB_ERR_FAIL; + } + + *addr_pp = kmalloc((size_t)byte_size, flags); + if(*addr_pp == 0) { + otg_err(OTG_DBG_OTGHCDI_MEM, + "kmalloc failed\n"); + return USB_ERR_FAIL; + } + return USB_ERR_SUCCESS; +} +//------------------------------------------------------------------------------- + +/** + * int otg_mem_copy(void * to_addr_p, void * from_addr_p, u16 byte_size); + * + * @brief memory copy + * + * @param [in] to_addr_p : target address + * [in] from_addr_p : source address + * [in] byte_size : size + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + */ +static inline int +otg_mem_copy +( + void * to_addr_p, + void * from_addr_p, + u16 byte_size +) +{ + otg_dbg(OTG_DBG_OTGHCDI_MEM, + "otg_mem_copy \n"); + + memcpy(to_addr_p, from_addr_p, (size_t)byte_size); + + return USB_ERR_SUCCESS; +} +//------------------------------------------------------------------------------- + +/** + * int otg_mem_free(void * addr_p); + * + * @brief de-allocating memory + * + * @param [in] addr_p : target address to be de-allocated + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + */ +static inline int +otg_mem_free(void * addr_p) +{ + otg_dbg(OTG_DBG_OTGHCDI_MEM, + "otg_mem_free \n"); + kfree(addr_p); + return USB_ERR_SUCCESS; +} + +//------------------------------------------------------------------------------- + +/** + * int otg_mem_set(void * addr_p, char value, u16 byte_size) + * + * @brief writing a value to memory + * + * @param [in] addr_p : target address + * [in] value : value to be written + * [in] byte_size : size + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + */ +static inline int +otg_mem_set +( + void * addr_p, + char value, + u16 byte_size +) +{ + otg_dbg(OTG_DBG_OTGHCDI_MEM, + "otg_mem_set \n"); + memset(addr_p, value, (size_t)byte_size); + return USB_ERR_SUCCESS; +} +//------------------------------------------------------------------------------- + + +#ifdef __cplusplus +} +#endif +#endif /* _S3C_OTG_HCDI_MEMORY_H_ */ diff --git a/drivers/usb/host/s3c-otg/s3c-otg-isr.c b/drivers/usb/host/s3c-otg/s3c-otg-isr.c new file mode 100644 index 0000000..b72b386 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-isr.c @@ -0,0 +1,294 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : Isr.c + * [Description] : The file implement the external and internal functions of ISR + * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com } + * [Department] : System LSI Division/Embedded S/W Platform + * [Created Date]: 2009/02/10 + * [Revision History] + * (1) 2008/06/13 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com } + * - Created this file and implements functions of ISR + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-isr.h" + +/** + * void otg_handle_interrupt(void) + * + * @brief Main interrupt processing routine + * + * @param None + * + * @return None + * + * @remark + * + */ + +__inline__ void otg_handle_interrupt(struct usb_hcd *hcd) +{ + gintsts_t clearIntr = {.d32 = 0}; + gintsts_t gintsts = {.d32 = 0}; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + gintsts.d32 = read_reg_32(GINTSTS) & read_reg_32(GINTMSK); + + otg_dbg(OTG_DBG_ISR, "otg_handle_interrupt - GINTSTS=0x%8x\n", + gintsts.d32); + + if (gintsts.b.wkupintr) { + otg_dbg(true, "Wakeup Interrupt\n"); + clearIntr.b.wkupintr = 1; + } + + if (gintsts.b.disconnect) { + otg_dbg(true, "Disconnect Interrupt\n"); + otghost->port_flag.b.port_connect_status_change = 1; + otghost->port_flag.b.port_connect_status = 0; + clearIntr.b.disconnect = 1; + /* + wake_unlock(&otghost->wake_lock); + */ + } + + if (gintsts.b.conidstschng) { + otg_dbg(OTG_DBG_ISR, "Connect ID Status Change Interrupt\n"); + clearIntr.b.conidstschng = 1; + oci_init_mode(); + } + + if (gintsts.b.hcintr) { + /* Mask Channel Interrupt to prevent generating interrupt */ + otg_dbg(OTG_DBG_ISR, "Channel Interrupt\n"); + if(!otghost->ch_halt) { + do_transfer_checker(otghost); + } + } + + if (gintsts.b.portintr) { + /* Read Only */ + otg_dbg(true, "Port Interrupt\n"); + process_port_intr(hcd); + } + + + if (gintsts.b.otgintr) { + /* Read Only */ + otg_dbg(OTG_DBG_ISR, "OTG Interrupt\n"); + } + + if (gintsts.b.sofintr) { + /* otg_dbg(OTG_DBG_ISR, "SOF Interrupt\n"); */ + do_schedule(otghost); + clearIntr.b.sofintr = 1; + } + + if (gintsts.b.modemismatch) { + otg_dbg(OTG_DBG_ISR, "Mode Mismatch Interrupt\n"); + clearIntr.b.modemismatch = 1; + } + update_reg_32(GINTSTS, clearIntr.d32); +} + +/** + * void mask_channel_interrupt(u32 ch_num, u32 mask_info) + * + * @brief Mask specific channel interrupt + * + * @param [IN] chnum : channel number for masking + * [IN] mask_info : mask information to write register + * + * @return None + * + * @remark + * + */ +void mask_channel_interrupt(u32 ch_num, u32 mask_info) +{ + clear_reg_32(HCINTMSK(ch_num),mask_info); +} + +/** + * void unmask_channel_interrupt(u32 ch_num, u32 mask_info) + * + * @brief Unmask specific channel interrupt + * + * @param [IN] chnum : channel number for unmasking + * [IN] mask_info : mask information to write register + * + * @return None + * + * @remark + * + */ +void unmask_channel_interrupt(u32 ch_num, u32 mask_info) +{ + update_reg_32(HCINTMSK(ch_num),mask_info); +} + +/** + * int get_ch_info(hc_info_t * hc_reg, u8 ch_num) + * + * @brief Get current channel information about specific channel + * + * @param [OUT] hc_reg : structure to write channel inforamtion value + * [IN] ch_num : channel number for unmasking + * + * @return None + * + * @remark + * + */ +int get_ch_info(hc_info_t *hc_reg, u8 ch_num) +{ + if(hc_reg !=NULL) { + hc_reg->hc_int_msk.d32 = read_reg_32(HCINTMSK(ch_num)); + hc_reg->hc_int.d32 = read_reg_32(HCINT(ch_num)); + hc_reg->dma_addr = read_reg_32(HCDMA(ch_num)); + hc_reg->hc_char.d32 = read_reg_32(HCCHAR(ch_num)); + hc_reg->hc_size.d32 = read_reg_32(HCTSIZ(ch_num)); + + return USB_ERR_SUCCESS; + } + return USB_ERR_FAIL; +} + +/** + * void get_intr_ch(u32* haint, u32* haintmsk) + * + * @brief Get Channel Interrupt Information in HAINT, HAINTMSK register + * + * @param [OUT] haint : HAINT register value + * [OUT] haintmsk : HAINTMSK register value + * + * @return None + * + * @remark + * + */ +void get_intr_ch(u32 *haint, u32 *haintmsk) +{ + *haint = read_reg_32(HAINT); + *haintmsk = read_reg_32(HAINTMSK); +} + +/** + * void clear_ch_intr(u8 ch_num, u32 clear_bit) + * + * @brief Get Channel Interrupt Information in HAINT, HAINTMSK register + * + * @param [IN] haint : HAINT register value + * [IN] haintmsk : HAINTMSK register value + * + * @return None + * + * @remark + * + */ +void clear_ch_intr(u8 ch_num, u32 clear_bit) +{ + update_reg_32(HCINT(ch_num),clear_bit); +} + +/** + * void enable_sof(void) + * + * @brief Generate SOF Interrupt. + * + * @param None + * + * @return None + * + * @remark + * + */ +void enable_sof(void) +{ + gintmsk_t gintmsk = {.d32 = 0}; + gintmsk.b.sofintr = 1; + update_reg_32(GINTMSK, gintmsk.d32); +} + +/** + * void disable_sof(void) + * + * @brief Stop to generage SOF interrupt + * + * @param None + * + * @return None + * + * @remark + * + */ +void disable_sof(void) +{ + gintmsk_t gintmsk = {.d32 = 0}; + gintmsk.b.sofintr = 1; + clear_reg_32(GINTMSK, gintmsk.d32); +} + +/*Internal function of isr */ +void process_port_intr(struct usb_hcd *hcd) +{ + hprt_t hprt; /* by ss1, clear_hprt; */ + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + hprt.d32 = read_reg_32(HPRT); + + otg_dbg(OTG_DBG_ISR, "Port Interrupt() : HPRT = 0x%x\n",hprt.d32); + + if(hprt.b.prtconndet) { + otg_dbg(true, "detect connection"); + + otghost->port_flag.b.port_connect_status_change = 1; + + if(hprt.b.prtconnsts) + otghost->port_flag.b.port_connect_status = 1; + + /* wake_lock(&otghost->wake_lock); */ + } + + + if(hprt.b.prtenchng) { + otg_dbg(true, "port enable/disable changed\n"); + otghost->port_flag.b.port_enable_change = 1; + } + + if(hprt.b.prtovrcurrchng) { + otg_dbg(true, "over current condition is changed\n"); + if(hprt.b.prtovrcurract) { + otg_dbg(true, "port_over_current_change = 1\n"); + otghost->port_flag.b.port_over_current_change = 1; + } + else { + otghost->port_flag.b.port_over_current_change = 0; + } + /* defer otg power control into a kernel thread */ + queue_work(otghost->wq, &otghost->work); + } + + hprt.b.prtena = 0; /* prtena¸¦ writeclear½ÃÅ°¸é ¾ÈµÊ. */ + /* hprt.b.prtpwr = 0; */ + hprt.b.prtrst = 0; + hprt.b.prtconnsts = 0; + + write_reg_32(HPRT, hprt.d32); +} diff --git a/drivers/usb/host/s3c-otg/s3c-otg-isr.h b/drivers/usb/host/s3c-otg/s3c-otg-isr.h new file mode 100644 index 0000000..cad4cf5 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-isr.h @@ -0,0 +1,72 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] :s3c-otg-isr.h + * [Description] : The Header file defines the external and internal functions of ISR. + * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com } + * [Department] : System LSI Division/Embedded S/W Platform + * [Created Date]: 2008/06/18 + * [Revision History] + * (1) 2008/06/18 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com } + * - Created this file and defines functions of Scheduler + * + ****************************************************************************/ +/**************************************************************************** + * 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 _ISR_H_ +#define _ISR_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "s3c-otg-common-errorcode.h" +#include "s3c-otg-common-const.h" +#include "s3c-otg-common-datastruct.h" +#include "s3c-otg-common-regdef.h" +#include "s3c-otg-hcdi-kal.h" +#include "s3c-otg-scheduler-scheduler.h" +#include "s3c-otg-transferchecker-common.h" +#include "s3c-otg-roothub.h" +#include "s3c-otg-oci.h" + +__inline__ void otg_handle_interrupt(struct usb_hcd *hcd); + +void process_port_intr(struct usb_hcd *hcd); + +void mask_channel_interrupt(u32 ch_num, u32 mask_info); + +void unmask_channel_interrupt(u32 ch_num, u32 mask_info); + +extern int get_ch_info(hc_info_t *hc_reg, u8 ch_num); + +extern void get_intr_ch(u32 *haint, u32 *haintmsk); + +extern void clear_ch_intr(u8 ch_num, u32 clear_bit); + +extern void enable_sof(void); + +extern void disable_sof(void); + +#ifdef __cplusplus +} +#endif +#endif /* _ISR_H_ */ + + + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-oci.c b/drivers/usb/host/s3c-otg/s3c-otg-oci.c new file mode 100644 index 0000000..a79c45c --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-oci.c @@ -0,0 +1,798 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : OCI.c + * [Description] : The file implement the external and internal functions of OCI + * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com } + * [Department] : System LSI Division/Embedded S/W Platform + * [Created Date]: 2009/02/10 + * [Revision History] + * (1) 2008/06/12 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com } + * - Created this file and Implement functions of OCI + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-oci.h" + +static bool ch_enable[16]; + +/** + * int oci_init(struct sec_otghost *otghost) + * + * @brief Initialize oci module. + * + * @param None + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * @remark + * + */ +int oci_init(struct sec_otghost *otghost) +{ + otg_mem_set((void*)ch_enable, true, sizeof(bool)*16); + otghost->ch_halt = false; + + if(oci_sys_init() == USB_ERR_SUCCESS) { + if(oci_core_reset() == USB_ERR_SUCCESS) { + oci_set_global_interrupt(false); + return USB_ERR_SUCCESS; + } + else { + otg_dbg(OTG_DBG_OCI, "oci_core_reset() Fail\n"); + return USB_ERR_FAIL; + } + } + + return USB_ERR_FAIL; +} + +/** + * int oci_core_init(void) + * + * @brief process core initialize as s5pc110 otg spec + * + * @param None + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * @remark + * + */ +int oci_core_init(void) +{ + gahbcfg_t ahbcfg = {.d32 = 0}; + gusbcfg_t usbcfg = {.d32 = 0}; + ghwcfg2_t hwcfg2 = {.d32 = 0}; + gintmsk_t gintmsk = {.d32 = 0}; + + otg_dbg(OTG_DBG_OCI, "oci_core_init \n"); + + usbcfg.d32 = read_reg_32(GUSBCFG); + otg_dbg(OTG_DBG_OCI, "before - GUSBCFG=0x%x, GOTGCTL=0x%x\n", + usbcfg.d32, read_reg_32(GOTGCTL)); + + /* PHY parameters */ + usbcfg.b.physel = 0; + usbcfg.b.phyif = 1; /* 16 bit */ + usbcfg.b.ulpi_utmi_sel = 0; /* UTMI */ + /* usbcfg.b.ddrsel = 1; */ /* DDR */ + usbcfg.b.usbtrdtim = 5; /* 16 bit UTMI */ + usbcfg.b.toutcal = 7; + usbcfg.b.forcehstmode = 1; + write_reg_32 (GUSBCFG, usbcfg.d32); + + // per + // http://forum.xda-developers.com/showthread.php?t=709135&page=21 + // we need a 25msec delay after setting this -kevinh + + mdelay(100); + + otg_dbg(OTG_DBG_OCI, + "after - GUSBCFG=0x%x, GOTGCTL=0x%x\n", + read_reg_32(GUSBCFG), + read_reg_32(GOTGCTL)); + + /* Reset after setting the PHY parameters */ + if(oci_core_reset() == USB_ERR_SUCCESS) { + /* Program the GAHBCFG Register.*/ + hwcfg2.d32 = read_reg_32 (GHWCFG2); + + switch (hwcfg2.b.architecture) { + case HWCFG2_ARCH_SLAVE_ONLY: + otg_dbg(OTG_DBG_OCI, "Slave Only Mode\n"); + ahbcfg.b.nptxfemplvl = 0; + ahbcfg.b.ptxfemplvl = 0; + break; + + case HWCFG2_ARCH_EXT_DMA: + otg_dbg(OTG_DBG_OCI, "External DMA Mode - TBD!\n"); + break; + + case HWCFG2_ARCH_INT_DMA: + otg_dbg(OTG_DBG_OCI, "Internal DMA Setting \n"); + ahbcfg.b.dmaenable = true; + ahbcfg.b.hburstlen = INT_DMA_MODE_INCR4; + break; + + default: + otg_dbg(OTG_DBG_OCI, "ERR> hwcfg2\n "); + break; + } + write_reg_32 (GAHBCFG, ahbcfg.d32); + + /* Program the GUSBCFG register.*/ + switch (hwcfg2.b.op_mode) { + case MODE_HNP_SRP_CAPABLE: + otg_dbg(OTG_DBG_OCI, + "GHWCFG2 OP Mode : MODE_HNP_SRP_CAPABLE \n"); + usbcfg.b.hnpcap = 1; + usbcfg.b.srpcap = 1; + + otg_dbg(OTG_DBG_OCI, + "OTG_DBG_OCI : use HNP and SRP \n"); + break; + + case MODE_SRP_ONLY_CAPABLE: + otg_dbg(OTG_DBG_OCI, + "GHWCFG2 OP Mode : MODE_SRP_ONLY_CAPABLE \n"); + usbcfg.b.srpcap = 1; + break; + + case MODE_NO_HNP_SRP_CAPABLE: + otg_dbg(OTG_DBG_OCI, + "GHWCFG2 OP Mode : MODE_NO_HNP_SRP_CAPABLE \n"); + usbcfg.b.hnpcap = 0; + break; + + case MODE_SRP_CAPABLE_DEVICE: + otg_dbg(OTG_DBG_OCI, + "GHWCFG2 OP Mode : MODE_SRP_CAPABLE_DEVICE \n"); + usbcfg.b.srpcap = 1; + break; + + case MODE_NO_SRP_CAPABLE_DEVICE: + otg_dbg(OTG_DBG_OCI, + "GHWCFG2 OP Mode : MODE_NO_SRP_CAPABLE_DEVICE \n"); + usbcfg.b.srpcap = 0; + break; + + case MODE_SRP_CAPABLE_HOST: + otg_dbg(OTG_DBG_OCI, + "GHWCFG2 OP Mode : MODE_SRP_CAPABLE_HOST \n"); + usbcfg.b.srpcap = 1; + break; + + case MODE_NO_SRP_CAPABLE_HOST: + otg_dbg(OTG_DBG_OCI, + "GHWCFG2 OP Mode : MODE_NO_SRP_CAPABLE_HOST \n"); + usbcfg.b.srpcap = 0; + break; + default : + otg_err(OTG_DBG_OCI, "ERR> hwcfg2\n "); + break; + } + write_reg_32 (GUSBCFG, usbcfg.d32); + + /* Program the GINTMSK register.*/ + gintmsk.b.modemismatch = 1; + gintmsk.b.sofintr = 1; + /*gintmsk.b.otgintr = 1; */ + gintmsk.b.conidstschng = 1; + /*gintmsk.b.wkupintr = 1;*/ + gintmsk.b.disconnect = 1; + /*gintmsk.b.usbsuspend = 1;*/ + /*gintmsk.b.sessreqintr = 1;*/ + /*gintmsk.b.portintr = 1;*/ + /*gintmsk.b.hcintr = 1; */ + write_reg_32(GINTMSK, gintmsk.d32); + + return USB_ERR_SUCCESS; + } + else { + otg_err(OTG_DBG_OCI, "Core Reset FAIL\n"); + return USB_ERR_FAIL; + } +} + +/** + * int oci_host_init(void) + * + * @brief Process host initialize as s5pc110 spec + * + * @param None + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * @remark + * + */ +int oci_host_init(void) +{ + gintmsk_t gintmsk = {.d32 = 0}; + hcfg_t hcfg = {.d32 = 0}; +#if 0 +/*#ifdef CONFIG_USB_S3C_OTG_HOST_DTGDRVVBUS*/ + hprt_t hprt; + hprt.d32 = read_reg_32(HPRT); +#endif + + otg_dbg(OTG_DBG_OCI, "oci_host_init\n"); + + gintmsk.b.portintr = 1; + update_reg_32(GINTMSK,gintmsk.d32); + + hcfg.b.fslspclksel = HCFG_30_60_MHZ; + update_reg_32(HCFG, hcfg.d32); + +#if 0 +/*#ifdef CONFIG_USB_S3C_OTG_HOST_DTGDRVVBUS*/ + /* turn on vbus */ + if(!hprt.b.prtpwr) { + hprt.b.prtpwr = 1; + write_reg_32(HPRT, hprt.d32); + + otg_dbg(true, "turn on Vbus\n"); + } +#endif + + oci_config_flush_fifo(OTG_HOST_MODE); + + return USB_ERR_SUCCESS; +} + +/** + * int oci_start(void) + * + * @brief start to operate oci module by calling oci_core_init function + * + * @param None + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * @remark + * + */ +int oci_start(void) +{ + otg_dbg(OTG_DBG_OCI, "oci_start \n"); + + if(oci_core_init() == USB_ERR_SUCCESS) { + mdelay(50); + if(oci_init_mode() == USB_ERR_SUCCESS) { + oci_set_global_interrupt(true); + return USB_ERR_SUCCESS; + } + else { + otg_dbg(OTG_DBG_OCI, "oci_init_mode() Fail\n"); + return USB_ERR_FAIL; + } + } + else { + otg_dbg(OTG_DBG_OCI, "oci_core_init() Fail\n"); + return USB_ERR_FAIL; + } +} + +/** + * int oci_stop(void) + * + * @brief stop to opearte otg core + * + * @param None + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * @remark + * + */ +int oci_stop() +{ + gintmsk_t gintmsk = {.d32 = 0}; + + otg_dbg(OTG_DBG_OCI, "oci_stop\n"); + + /* sometimes, port interrupt occured after removed + * otg host driver. so, we have to mask port interrupt. */ + write_reg_32(GINTMSK, gintmsk.d32); + + oci_set_global_interrupt(false); + + return USB_ERR_SUCCESS; +} + +/** + * oci_start_transfer(struct sec_otghost *otghost, stransfer_t *st_t) + * + * @brief start transfer by using transfer information to receive from scheduler + * + * @param [IN] *st_t - information about transfer to write register by calling oci_channel_init function + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * @remark + * + */ + +u8 oci_start_transfer(struct sec_otghost *otghost, stransfer_t *st_t) +{ + hcchar_t hcchar = {.d32 = 0}; + + otg_dbg(OTG_DBG_OCI, "oci_start_transfer \n"); + + if(st_t->alloc_chnum ==CH_NONE) { + + if( oci_channel_alloc(&(st_t->alloc_chnum)) == USB_ERR_SUCCESS) { + oci_channel_init(st_t->alloc_chnum, st_t); + + hcchar.b.chen = 1; + update_reg_32(HCCHAR(st_t->alloc_chnum), hcchar.d32); + return st_t->alloc_chnum; + } + else { + otg_dbg(OTG_DBG_OCI, + "oci_start_transfer Fail - Channel Allocation Error\n"); + return CH_NONE; + } + } + else { + oci_channel_init(st_t->alloc_chnum, st_t); + + hcchar.b.chen = 1; + update_reg_32(HCCHAR(st_t->alloc_chnum), hcchar.d32); + + return st_t->alloc_chnum; + } +} + +/** + * int oci_stop_transfer(struct sec_otghost *otghost, u8 ch_num) + * + * @brief stop to transfer even if transfering + * + * @param None + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * @remark + * + */ +int oci_stop_transfer(struct sec_otghost *otghost, u8 ch_num) +{ + hcchar_t hcchar = {.d32 = 0}; + hcintmsk_t hcintmsk = {.d32 = 0}; + int count = 0, max_error_count = 10000; + + otg_dbg(OTG_DBG_OCI, + "step1: oci_stop_transfer ch=%d, hcchar=0x%x\n", + ch_num, read_reg_32(HCCHAR(ch_num))); + + if(ch_num>16) + return USB_ERR_FAIL; + + otghost->ch_halt = true; + + hcintmsk.b.chhltd = 1; + update_reg_32(HCINTMSK(ch_num),hcintmsk.d32); + + hcchar.b.chdis = 1; + hcchar.b.chen = 1; + update_reg_32(HCCHAR(ch_num),hcchar.d32); + + /* wait for Channel Disabled Interrupt */ + do { + hcchar.d32 = read_reg_32(HCCHAR(ch_num)); + + if(count > max_error_count) { + otg_dbg(OTG_DBG_OCI, + "Warning!! oci_stop_transfer()" + "ChDis is not cleared! ch=%d, hcchar=0x%x\n", + ch_num, hcchar.d32); + break; + } + count++; + + } while(hcchar.b.chdis); + + //kevinh: wait for Channel Disabled Interrupt + count = 0; + do { + hcintmsk.d32 = read_reg_32(HCINT(ch_num)); + if(count > max_error_count) { + printk("chhltd int never occurred! ch=%d, intreg=0x%x\n", + ch_num, hcintmsk.d32); + break; + } + count++; + } while(!hcintmsk.b.chhltd); + + oci_channel_dealloc(ch_num); + + clear_reg_32(HAINTMSK,ch_num); + write_reg_32(HCINT(ch_num),INT_ALL); + clear_reg_32(HCINTMSK(ch_num), INT_ALL); + + otghost->ch_halt = false; + otg_dbg(OTG_DBG_OCI, + "step2 : oci_stop_transfer ch=%d, hcchar=0x%x\n", + ch_num, read_reg_32(HCCHAR(ch_num))); + + return USB_ERR_SUCCESS; +} + +/** + * int oci_channel_init( u8 ch_num, stransfer_t *st_t) + * + * @brief Process channel initialize to prepare starting transfer + * + * @param None + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * @remark + * + */ +int oci_channel_init( u8 ch_num, stransfer_t *st_t) +{ + u32 intr_enable = 0; + gintmsk_t gintmsk = {.d32 = 0}; + hcchar_t hcchar = {.d32 = 0}; + hctsiz_t hctsiz = {.d32 = 0}; + + otg_dbg(OTG_DBG_OCI, "oci_channel_init \n"); + + /* Clear channel information */ + write_reg_32(HCTSIZ(ch_num), 0); + write_reg_32(HCCHAR(ch_num), 0); + write_reg_32(HCINTMSK(ch_num), 0); + write_reg_32(HCINT(ch_num), INT_ALL);/*write clear*/ + write_reg_32(HCDMA(ch_num), 0); + + /* enable host channel interrupt in GINTSTS */ + gintmsk.b.hcintr =1; + update_reg_32(GINTMSK, gintmsk.d32); + /* Enable the top level host channel interrupt in HAINT */ + intr_enable = (1 << ch_num); + update_reg_32(HAINTMSK, intr_enable); + /* unmask the down level host channel interrupt in HCINT */ + write_reg_32(HCINTMSK(ch_num),st_t->hc_reg.hc_int_msk.d32); + + /* Program the HCSIZn register with the endpoint characteristics for */ + hctsiz.b.xfersize = st_t->buf_size; + hctsiz.b.pktcnt = st_t->packet_cnt; + + /* Program the HCCHARn register with the endpoint characteristics for */ + hcchar.b.mps = st_t->ed_desc_p->max_packet_size; + hcchar.b.epnum = st_t->ed_desc_p->endpoint_num; + hcchar.b.epdir = st_t->ed_desc_p->is_ep_in; + hcchar.b.lspddev = (st_t->ed_desc_p->dev_speed == LOW_SPEED_OTG); + hcchar.b.eptype = st_t->ed_desc_p->endpoint_type; + hcchar.b.multicnt = st_t->ed_desc_p->mc; + hcchar.b.devaddr = st_t->ed_desc_p->device_addr; + + if(st_t->ed_desc_p->endpoint_type == INT_TRANSFER || + st_t->ed_desc_p->endpoint_type == ISOCH_TRANSFER) { + u32 uiFrameNum = 0; + uiFrameNum = oci_get_frame_num(); + + hcchar.b.oddfrm = uiFrameNum%2?1:0; + + /* + * if transfer type is periodic transfer, + * must support sof interrupt + */ + + /* + gintmsk.b.sofintr = 1; + update_reg_32(GINTMSK, gintmsk.d32); + */ + } + + if(st_t->ed_desc_p->endpoint_type == CONTROL_TRANSFER) { + td_t *td_p; + td_p = (td_t *)st_t->parent_td; + + switch(td_p->standard_dev_req_info.conrol_transfer_stage) { + case SETUP_STAGE: + hctsiz.b.pid = st_t->ed_status_p->control_data_tgl.setup_tgl; + hcchar.b.epdir = EP_OUT; + break; + case DATA_STAGE: + hctsiz.b.pid = st_t->ed_status_p->control_data_tgl.data_tgl; + hcchar.b.epdir = st_t->ed_desc_p->is_ep_in; + break; + case STATUS_STAGE: + hctsiz.b.pid = st_t->ed_status_p->control_data_tgl.status_tgl; + + if(td_p->standard_dev_req_info.is_data_stage) { + hcchar.b.epdir = ~(st_t->ed_desc_p->is_ep_in); + } + else { + hcchar.b.epdir = EP_IN; + } + break; + default:break; + } + } + else { + hctsiz.b.pid = st_t->ed_status_p->data_tgl; + } + + hctsiz.b.dopng = st_t->ed_status_p->is_ping_enable; + write_reg_32(HCTSIZ(ch_num),hctsiz.d32); + st_t->ed_status_p->is_ping_enable = false; + + /* Write DMA Address */ + write_reg_32(HCDMA(ch_num),st_t->start_phy_buf_addr); + + /* Wrote HCCHAR Register */ + write_reg_32(HCCHAR(ch_num),hcchar.d32); + + return USB_ERR_SUCCESS; +} + +/** + * u32 oci_get_frame_num(void) + * + * @brief Get current frame number by reading register. + * + * @param None + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * @remark + * + */ +u32 oci_get_frame_num(void) +{ + hfnum_t hfnum; + hfnum.d32 = read_reg_32(HFNUM); + + otg_dbg(OTG_DBG_OCI, " oci_get_frame_num=%d\n", hfnum.b.frnum); + + return hfnum.b.frnum; +} + +/** + * u16 oci_get_frame_interval(void) + * + * @brief Get current frame interval by reading register. + * + * @param None + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * @remark + * + */ +u16 oci_get_frame_interval(void) +{ + hfir_t hfir; + hfir.d32 = read_reg_32(HFIR); + return hfir.b.frint; +} + +void oci_set_frame_interval(u16 interval) +{ + hfir_t hfir = {.d32 = 0}; + hfir.b.frint = interval; + write_reg_32(HFIR, hfir.d32); +} + +/* OCI Internal Functions */ + +int oci_channel_alloc(u8 *ch_num) +{ + u8 ch; + + hcchar_t hcchar = {.d32 = 0}; + + for(ch = 0 ; ch<16 ; ch++) { + if(ch_enable[ch] == true) { + hcchar.d32 = read_reg_32(HCCHAR(ch)); + + if(hcchar.b.chdis == 0) { + *ch_num = ch; + ch_enable[ch] = false; + return USB_ERR_SUCCESS; + } + } + } + + return USB_ERR_FAIL; +} + +int oci_channel_dealloc(u8 ch_num) +{ + if(ch_num < 16 && ch_enable[ch_num] == false) { + ch_enable[ch_num] = true; + + write_reg_32(HCTSIZ(ch_num), 0); + write_reg_32(HCCHAR(ch_num), 0); + write_reg_32(HCINTMSK(ch_num), 0); + write_reg_32(HCINT(ch_num), INT_ALL); + write_reg_32(HCDMA(ch_num), 0); + return USB_ERR_SUCCESS; + } + + return USB_ERR_FAIL; +} + +int oci_sys_init(void) +{ + otg_host_phy_init(); + + return USB_ERR_SUCCESS; +} + +void oci_set_global_interrupt(bool set) +{ + gahbcfg_t ahbcfg; + + otg_dbg(OTG_DBG_OCI, " oci_set_global_interrupt\n"); + + ahbcfg.d32 = 0; + ahbcfg.b.glblintrmsk = 1; + + if(set) { + update_reg_32(GAHBCFG,ahbcfg.d32); + } + else { + clear_reg_32(GAHBCFG,ahbcfg.d32); + } +} + +int oci_init_mode(void) +{ + gintsts_t gintsts; + + gintsts.d32 = read_reg_32(GINTSTS); + + otg_dbg(OTG_DBG_OCI, + "GINSTS = 0x%x,GINMSK = 0x%x.\n", + (unsigned int)gintsts.d32, + (unsigned int)read_reg_32(GINTMSK)); + + if(gintsts.b.curmode == OTG_HOST_MODE) { + otg_dbg(OTG_DBG_OCI, "HOST Mode\n"); + + if(oci_host_init() == USB_ERR_SUCCESS) { + return USB_ERR_SUCCESS; + } + else { + otg_dbg(OTG_DBG_OCI, "oci_host_init() Fail\n"); + return USB_ERR_FAIL; + } + } + else { /* Device Mode */ + otg_dbg(OTG_DBG_OCI, "DEVICE Mode\n"); + if(oci_dev_init() == USB_ERR_SUCCESS) { + return USB_ERR_SUCCESS; + } + else { + otg_err(OTG_DBG_OCI, "oci_dev_init() Fail\n"); + return USB_ERR_FAIL; + } + } + + return USB_ERR_SUCCESS; +} + +void oci_config_flush_fifo(u32 mode) +{ + ghwcfg2_t hwcfg2 = {.d32 = 0}; + + otg_dbg(OTG_DBG_OCI, "oci_config_flush_fifo\n"); + + hwcfg2.d32 = read_reg_32(GHWCFG2); + + /* Configure data FIFO sizes */ + if (hwcfg2.b.dynamic_fifo) { + /* Rx FIFO */ + write_reg_32(GRXFSIZ, 0x0000010D); + + /* Non-periodic Tx FIFO */ + write_reg_32(GNPTXFSIZ, 0x0080010D); + + if (mode == OTG_HOST_MODE) { + /* For Periodic transactions, */ + /* program HPTXFSIZ */ + } + } + + /* Flush the FIFOs */ + oci_flush_tx_fifo(0); + + oci_flush_rx_fifo(); +} + +void oci_flush_tx_fifo(u32 num) +{ + grstctl_t greset = {.d32 = 0}; + u32 count = 0; + + otg_dbg(OTG_DBG_OCI, "oci_flush_tx_fifo\n"); + + greset.b.txfflsh = 1; + greset.b.txfnum = num; + write_reg_32(GRSTCTL, greset.d32); + + /* wait for flush to end */ + while (greset.b.txfflsh == 1) { + greset.d32 = read_reg_32(GRSTCTL); + if (++count > MAX_COUNT) + break; + }; + + /* Wait for 3 PHY Clocks*/ + udelay(30); +} + +void oci_flush_rx_fifo(void) +{ + grstctl_t greset = {.d32 = 0}; + u32 count = 0; + + otg_dbg(OTG_DBG_OCI, "oci_flush_rx_fifo\n"); + + greset.b.rxfflsh = 1; + write_reg_32(GRSTCTL, greset.d32 ); + + do { + greset.d32 = read_reg_32(GRSTCTL); + if (++count > MAX_COUNT) + break; + } while (greset.b.rxfflsh == 1); + + /* Wait for 3 PHY Clocks*/ + udelay(30); +} + +int oci_core_reset(void) +{ + u32 count = 0; + grstctl_t greset = {.d32 = 0}; + + otg_dbg(OTG_DBG_OCI, "oci_core_reset\n"); + + /* Wait for AHB master IDLE state. */ + do { + greset.d32 = read_reg_32 (GRSTCTL); + mdelay (50); + + if(++count>100) { + otg_dbg(OTG_DBG_OCI, "AHB status is not IDLE\n"); + return USB_ERR_FAIL; + } + } while (greset.b.ahbidle != 1); + + /* Core Soft Reset */ + greset.b.csftrst = 1; + write_reg_32 (GRSTCTL, greset.d32); + + /* Wait for 3 PHY Clocks*/ + mdelay (50); + return USB_ERR_SUCCESS; +} + +int oci_dev_init(void) +{ + otg_dbg(OTG_DBG_OCI, "oci_dev_init - do nothing.\n"); + /* return USB_ERR_FAIL; */ + return USB_ERR_SUCCESS; +} diff --git a/drivers/usb/host/s3c-otg/s3c-otg-oci.h b/drivers/usb/host/s3c-otg/s3c-otg-oci.h new file mode 100644 index 0000000..fcc9f07 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-oci.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] :s3c-otg-oci.h + * [Description] : The Header file defines the external and internal functions of OCI. + * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com } + * [Department] : System LSI Division/Embedded S/W Platform + * [Created Date]: 2008/06/18 + * [Revision History] + * (1) 2008/06/25 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com } + * - Added some functions and data structure of OCI + * + ****************************************************************************/ +/**************************************************************************** + * 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 _OCI_H_ +#define _OCI_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +//#include "s3c-otg-common-const.h" +#include "s3c-otg-common-errorcode.h" +#include "s3c-otg-common-regdef.h" +#include "s3c-otg-hcdi-kal.h" +#include "s3c-otg-hcdi-memory.h" +#include "s3c-otg-hcdi-debug.h" +#include "s3c-otg-roothub.h" +#include "s3c-otg-hcdi-hcd.h" + +#include <mach/map.h> //virtual address for smdk + +extern void otg_host_phy_init(void); +#include <mach/regs-clock.h> + +//OCI interace +int oci_init(struct sec_otghost *otghost); + +int oci_start(void); +int oci_stop(void); + +u8 oci_start_transfer(struct sec_otghost *otghost, stransfer_t *st_t); +int oci_stop_transfer(struct sec_otghost *otghost, u8 ch_num); + +int oci_channel_init(u8 ch_num, stransfer_t *st_t); +u32 oci_get_frame_num(void); +u16 oci_get_frame_interval(void); +void oci_set_frame_interval(u16 intervl); + +///OCI Internal Functions +int oci_sys_init(void); +int oci_core_init(void); +int oci_init_mode(void); +int oci_host_init(void); +int oci_dev_init(void); + +int oci_channel_alloc(u8 *ch_num); +int oci_channel_dealloc(u8 ch_num); + +void oci_config_flush_fifo(u32 mode); +void oci_flush_tx_fifo(u32 num); +void oci_flush_rx_fifo(void); + +int oci_core_reset(void); +void oci_set_global_interrupt(bool set); + +#ifdef __cplusplus +} +#endif +#endif /* _OCI_H_ */ diff --git a/drivers/usb/host/s3c-otg/s3c-otg-roothub.c b/drivers/usb/host/s3c-otg/s3c-otg-roothub.c new file mode 100644 index 0000000..56eb195 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-roothub.c @@ -0,0 +1,605 @@ +/* for* ========================================================================== + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless + * otherwise expressly agreed to in writing between Synopsys and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product under + * any End User Software License Agreement or Agreement for Licensed Product + * with Synopsys or any supplement thereto. You are permitted to use and + * redistribute this Software in source and binary forms, with or without + * modification, provided that redistributions of source code must retain this + * notice. You may not view, use, disclose, copy or distribute this file or + * any information contained herein except pursuant to this license grant from + * Synopsys. If you do not agree with this notice, including the disclaimer + * below, then you are not authorized to use the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * ========================================================================== */ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : RootHub.c + * [Description] : The file implement the external and internal functions of RootHub + * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com } + * [Department] : System LSI Division/Embedded S/W Platform + * [Created Date]: 2009/02/10 + * [Revision History] + * (1) 2008/06/13 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com } + * - Created this file and implements functions of RootHub + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-roothub.h" + +static void setPortPower(bool on) +{ +#ifdef CONFIG_USB_S3C_OTG_HOST_DTGDRVVBUS + hprt_t hprt = {.d32 = 0}; + + if(on) { + hprt.d32 = read_reg_32(HPRT); + if(!hprt.b.prtpwr) { + /*hprt.d32 = 0;*/ + hprt.b.prtpwr = 1; + write_reg_32(HPRT, hprt.d32); + } + } + else { + hprt.b.prtpwr = 1; + clear_reg_32(HPRT, hprt.d32); + } +#else + otg_dbg(true, "turn %s Vbus\n", on ? "on" : "off"); +#endif +} + +/** + * int get_otg_port_status(const u8 port, char* status) + * + * @brief Get port change bitmap information + * + * @param [IN] port : port number + * [OUT] status : buffer to store bitmap information + * + * @returnUSB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * + * @remark + * + */ +__inline__ int get_otg_port_status( + struct usb_hcd *hcd, const u8 port, char *status) +{ + + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + /* return root_hub_feature(port, GetPortStatus, NULL, status); */ +#if 0 + /* for debug */ + hprt_t hprt; + + hprt.d32 = read_reg_32(HPRT); + + otg_dbg(OTG_DBG_ROOTHUB, + "HPRT:spd=%d,pwr=%d,lnsts=%d,rst=%d,susp=%d,res=%d,ovrcurract=%d,ena=%d,connsts=%d\n", + hprt.b.prtspd, + hprt.b.prtpwr, + hprt.b.prtlnsts, + hprt.b.prtrst, + hprt.b.prtsusp, + hprt.b.prtres, + hprt.b.prtovrcurract, + hprt.b.prtena, + hprt.b.prtconnsts); + +#endif + status[port] = 0; + status[port] |= (otghost->port_flag.b.port_connect_status_change || + otghost->port_flag.b.port_reset_change || + otghost->port_flag.b.port_enable_change || + otghost->port_flag.b.port_suspend_change || + otghost->port_flag.b.port_over_current_change) << 1; + +#if 0 + //* for debug */ + otg_dbg(OTG_DBG_ROOTHUB, + "connect:%d,reset:%d,enable:%d,suspend:%d,over_current:%d\n", + otghost->port_flag.b.port_connect_status_change, + otghost->port_flag.b.port_reset_change, + otghost->port_flag.b.port_enable_change, + otghost->port_flag.b.port_suspend_change, + otghost->port_flag.b.port_over_current_change); +#endif + + if (status[port]) { + otg_dbg(OTG_DBG_ROOTHUB, + " Root port status changed\n"); + otg_dbg(OTG_DBG_ROOTHUB, + " port_connect_status_change: %d\n", + otghost->port_flag.b.port_connect_status_change); + otg_dbg(OTG_DBG_ROOTHUB, + " port_reset_change: %d\n", + otghost->port_flag.b.port_reset_change); + otg_dbg(OTG_DBG_ROOTHUB, + " port_enable_change: %d\n", + otghost->port_flag.b.port_enable_change); + otg_dbg(OTG_DBG_ROOTHUB, + " port_suspend_change: %d\n", + otghost->port_flag.b.port_suspend_change); + otg_dbg(OTG_DBG_ROOTHUB, + " port_over_current_change: %d\n", + otghost->port_flag.b.port_over_current_change); + } + + return (status[port] !=0); +} + +/** + * int reset_and_enable_port(struct usb_hcd *hcd, const u8 port) + * + * @brief Reset port and make enable status the specific port + * + * @param [IN] port : port number + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * + * @remark + * + */ +int reset_and_enable_port(struct usb_hcd *hcd, const u8 port) +{ + hprt_t hprt; + u32 count = 0; + u32 max_error_count = 10000; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + hprt.d32 = read_reg_32(HPRT); + + otg_dbg(OTG_DBG_ROOTHUB, + " reset_and_enable_port\n"); + + if(hprt.b.prtconnsts==0) { + otg_dbg(OTG_DBG_ROOTHUB, + "No Attached Device, HPRT = 0x%x\n", hprt.d32); + + otghost->port_flag.b.port_connect_status_change = 1; + otghost->port_flag.b.port_connect_status = 0; + + return USB_ERR_FAIL; + } + + if(!hprt.b.prtena) { + hprt.b.prtrst = 1; /* drive reset */ + write_reg_32(HPRT, hprt.d32); + + mdelay(60); + hprt.b.prtrst = 0; + write_reg_32(HPRT, hprt.d32); + + do { + hprt.d32 = read_reg_32(HPRT); + + if(count > max_error_count) { + otg_dbg(OTG_DBG_ROOTHUB, + "Port Reset Fail : HPRT : 0x%x\n", hprt.d32); + return USB_ERR_FAIL; + } + count++; + + } while(!hprt.b.prtena); + + } + return USB_ERR_SUCCESS; +} + +/** + * int root_hub_feature( + * struct usb_hcd *hcd, + * const u8 port, + * const u16 type_req, + * const u16 feature, + * void* buf) + * + * @brief Get port change bitmap information + * + * @param [IN] port : port number + * [IN] type_req : request type of hub feature as usb 2.0 spec + * [IN] feature : hub feature as usb 2.0 spec + * [OUT] status : buffer to store results + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * + * @remark + * + */ +__inline__ int root_hub_feature( + struct usb_hcd *hcd, + const u8 port, + const u16 type_req, + const u16 feature, + void *buf) +{ + int retval = USB_ERR_SUCCESS; + usb_hub_descriptor_t *desc = NULL; + u32 port_status = 0; + hprt_t hprt = {.d32 = 0}; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_ROOTHUB, " root_hub_feature\n"); + + switch (type_req) { + case ClearHubFeature: + otg_dbg(OTG_DBG_ROOTHUB, "case ClearHubFeature\n"); + switch (feature) { + case C_HUB_LOCAL_POWER: + otg_dbg(OTG_DBG_ROOTHUB, + "case ClearHubFeature -C_HUB_LOCAL_POWER\n"); + break; + case C_HUB_OVER_CURRENT: + otg_dbg(OTG_DBG_ROOTHUB, + "case ClearHubFeature -C_HUB_OVER_CURRENT\n"); + /* Nothing required here */ + break; + default: + retval = USB_ERR_FAIL; + } + break; + + case ClearPortFeature: + otg_dbg(OTG_DBG_ROOTHUB, "case ClearPortFeature\n"); + switch (feature) { + case USB_PORT_FEAT_ENABLE: + otg_dbg(OTG_DBG_ROOTHUB, + "case ClearPortFeature -USB_PORT_FEAT_ENABLE\n"); + hprt.b.prtena = 1; + update_reg_32(HPRT, hprt.d32); + break; + + case USB_PORT_FEAT_SUSPEND: + otg_dbg(OTG_DBG_ROOTHUB, + "case ClearPortFeature -USB_PORT_FEAT_SUSPEND\n"); + bus_resume(otghost); + break; + + case USB_PORT_FEAT_POWER: + otg_dbg(OTG_DBG_ROOTHUB, + "case ClearPortFeature -USB_PORT_FEAT_POWER\n"); + setPortPower(false); + break; + + case USB_PORT_FEAT_INDICATOR: + otg_dbg(OTG_DBG_ROOTHUB, + "case ClearPortFeature -USB_PORT_FEAT_INDICATOR\n"); + /* Port inidicator not supported */ + break; + + case USB_PORT_FEAT_C_CONNECTION: + otg_dbg(OTG_DBG_ROOTHUB, + "case ClearPortFeature -USB_PORT_FEAT_C_CONNECTION\n"); + /* Clears drivers internal connect status change flag */ + otghost->port_flag.b.port_connect_status_change = 0; + break; + + case USB_PORT_FEAT_C_RESET: + otg_dbg(OTG_DBG_ROOTHUB, + "case ClearPortFeature -USB_PORT_FEAT_C_RESET\n"); + /* Clears the driver's internal Port Reset Change flag*/ + otghost->port_flag.b.port_reset_change = 0; + break; + + case USB_PORT_FEAT_C_ENABLE: + otg_dbg(OTG_DBG_ROOTHUB, + "case ClearPortFeature -USB_PORT_FEAT_C_ENABLE\n"); + /* Clears the driver's internal Port Enable/Disable Change flag */ + otghost->port_flag.b.port_enable_change = 0; + break; + + case USB_PORT_FEAT_C_SUSPEND: + otg_dbg(OTG_DBG_ROOTHUB, + "case ClearPortFeature -USB_PORT_FEAT_C_SUSPEND\n"); + /* Clears the driver's internal Port Suspend + * Change flag, which is set when resume signaling on + * the host port is complete */ + otghost->port_flag.b.port_suspend_change = 0; + break; + + case USB_PORT_FEAT_C_OVER_CURRENT: + otg_dbg(OTG_DBG_ROOTHUB, "case ClearPortFeature - \ + USB_PORT_FEAT_C_OVER_CURRENT\n"); + otghost->port_flag.b.port_over_current_change = 0; + break; + + default: + retval = USB_ERR_FAIL; + otg_dbg(OTG_DBG_ROOTHUB, + "case ClearPortFeature - FAIL\n"); + } + break; + + case GetHubDescriptor: + otg_dbg(OTG_DBG_ROOTHUB, "case GetHubDescriptor\n"); + desc = (usb_hub_descriptor_t *)buf; + desc->desc_length = 9; + desc->desc_type = 0x29; + desc->port_number = 1; + desc->hub_characteristics = 0x08; + desc->power_on_to_power_good = 1; + desc->hub_control_current = 0; + desc->DeviceRemovable[0] = 0; + desc->DeviceRemovable[1] = 0xff; + break; + + case GetHubStatus: + otg_dbg(OTG_DBG_ROOTHUB, "case GetHubStatus\n"); + otg_mem_set(buf, 0, 4); + break; + + case GetPortStatus: + otg_dbg(OTG_DBG_ROOTHUB, "case GetPortStatus\n"); + + if (otghost->port_flag.b.port_connect_status_change) { + port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); + otg_dbg(OTG_DBG_ROOTHUB, + "case GetPortStatus - USB_PORT_FEAT_C_CONNECTION\n"); + } + + if (otghost->port_flag.b.port_enable_change) { + port_status |= (1 << USB_PORT_FEAT_C_ENABLE); + otg_dbg(OTG_DBG_ROOTHUB, + "case GetPortStatus - USB_PORT_FEAT_C_ENABLE\n"); + } + + if (otghost->port_flag.b.port_suspend_change) { + port_status |= (1 << USB_PORT_FEAT_C_SUSPEND); + otg_dbg(OTG_DBG_ROOTHUB, + "case GetPortStatus - USB_PORT_FEAT_C_SUSPEND\n"); + } + + if (otghost->port_flag.b.port_reset_change) { + port_status|= (1 << USB_PORT_FEAT_C_RESET); + otg_dbg(OTG_DBG_ROOTHUB, + "case GetPortStatus - USB_PORT_FEAT_C_RESET\n"); + } + + if (otghost->port_flag.b.port_over_current_change) { + port_status |= (1 << USB_PORT_FEAT_C_OVER_CURRENT); + otg_dbg(OTG_DBG_ROOTHUB, + "case GetPortStatus - USB_PORT_FEAT_C_OVER_CURRENT\n"); + } + + if (!otghost->port_flag.b.port_connect_status) { + /* + * The port is disconnected, which means the core is + * either in device mode or it soon will be. Just + * return 0's for the remainder of the port status + * since the port register can't be read if the core + * is in device mode. + */ + otg_dbg(OTG_DBG_ROOTHUB, + "case GetPortStatus - disconnected\n"); + + *((__le32*)buf) = cpu_to_le32(port_status); + /*break;*/ + } + + hprt.d32 = read_reg_32(HPRT); + + if (hprt.b.prtconnsts) + port_status|= (1 << USB_PORT_FEAT_CONNECTION); + + if (hprt.b.prtena) + port_status |= (1 << USB_PORT_FEAT_ENABLE); + + if (hprt.b.prtsusp) + port_status |= (1 << USB_PORT_FEAT_SUSPEND); + + if (hprt.b.prtovrcurract) + port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT); + + if (hprt.b.prtrst) + port_status |= (1 << USB_PORT_FEAT_RESET); + + if (hprt.b.prtpwr) + port_status |= (1 << USB_PORT_FEAT_POWER); + + if (hprt.b.prtspd == 0) { + port_status |= USB_PORT_STAT_HIGH_SPEED; + } + else { + if (hprt.b.prtspd == 2) + port_status |= USB_PORT_STAT_LOW_SPEED; + } + + if (hprt.b.prttstctl) + port_status |= (1 << USB_PORT_FEAT_TEST); + + *((__le32*)buf) = cpu_to_le32(port_status); + + otg_dbg(OTG_DBG_ROOTHUB, "port_status=0x%x.\n", port_status); + break; + + case SetHubFeature: + otg_dbg(OTG_DBG_ROOTHUB, "case SetHubFeature\n"); + /* No HUB features supported */ + break; + + case SetPortFeature: + otg_dbg(OTG_DBG_ROOTHUB, "case SetPortFeature\n"); + if (!otghost->port_flag.b.port_connect_status) { + /* + * The port is disconnected, which means the core is + * either in device mode or it soon will be. Just + * return without doing anything since the port + * register can't be written if the core is in device + * mode. + */ + otg_dbg(OTG_DBG_ROOTHUB, + "case SetPortFeature - disconnected\n"); + + /*break;*/ + } + + switch (feature) { + case USB_PORT_FEAT_SUSPEND: + otg_dbg(true, + "case SetPortFeature -USB_PORT_FEAT_SUSPEND\n"); + bus_suspend(); + break; + + case USB_PORT_FEAT_POWER: + otg_dbg(true, + "case SetPortFeature -USB_PORT_FEAT_POWER\n"); + setPortPower(true); + break; + + case USB_PORT_FEAT_RESET: + otg_dbg(true, + "case SetPortFeature -USB_PORT_FEAT_RESET\n"); + retval = reset_and_enable_port(hcd, port); + /* + if(retval == USB_ERR_SUCCESS) + wake_lock(&otghost->wake_lock); + */ + break; + + case USB_PORT_FEAT_INDICATOR: + otg_dbg(true, + "case SetPortFeature -USB_PORT_FEAT_INDICATOR\n"); + break; + + default : + otg_dbg(true, "case SetPortFeature -USB_ERR_FAIL\n"); + retval = USB_ERR_FAIL; + break; + } + break; + + default: + retval = USB_ERR_FAIL; + otg_err(true, "root_hub_feature() Function Error\n"); + break; + } + + if(retval != USB_ERR_SUCCESS) + retval = USB_ERR_FAIL; + + return retval; +} + +/** + * void bus_suspend(void) + * + * @brief Make suspend status when this platform support PM Mode + * + * @param None + * + * @return None + * + * @remark + * + */ +void bus_suspend(void) +{ + hprt_t hprt; + pcgcctl_t pcgcctl; + + otg_dbg(OTG_DBG_ROOTHUB, " bus_suspend\n"); + + hprt.d32 = 0; + pcgcctl.d32 = 0; + + hprt.b.prtsusp = 1; + update_reg_32(HPRT, hprt.d32); + + pcgcctl.b.pwrclmp = 1; + update_reg_32(PCGCCTL,pcgcctl.d32); + udelay(1); + + pcgcctl.b.rstpdwnmodule = 1; + update_reg_32(PCGCCTL,pcgcctl.d32); + udelay(1); + + pcgcctl.b.stoppclk = 1; + update_reg_32(PCGCCTL,pcgcctl.d32); + udelay(1); +} + +/** + * int bus_resume(struct sec_otghost *otghost) + * + * @brief Make resume status when this platform support PM Mode + * + * @param None + * + * @return USB_ERR_SUCCESS : If success \n + * USB_ERR_FAIL : If call fail \n + * + * @remark + * + */ +int bus_resume(struct sec_otghost *otghost) +{ + /* + hprt_t hprt; + pcgcctl_t pcgcctl; + hprt.d32 = 0; + pcgcctl.d32 = 0; + + pcgcctl.b.stoppclk = 1; + clear_reg_32(PCGCCTL,pcgcctl.d32); + udelay(1); + + pcgcctl.b.pwrclmp = 1; + clear_reg_32(PCGCCTL,pcgcctl.d32); + udelay(1); + + pcgcctl.b.rstpdwnmodule = 1; + clear_reg_32(PCGCCTL,pcgcctl.d32); + udelay(1); + + hprt.b.prtres = 1; + update_reg_32(HPRT, hprt.d32); + mdelay(20); + + clear_reg_32(HPRT, hprt.d32); + */ + otg_dbg(OTG_DBG_ROOTHUB, "bus_resume()......\n"); + + otg_dbg(OTG_DBG_ROOTHUB, "wait for 50 ms...\n"); + + mdelay(50); + if(oci_init(otghost) == USB_ERR_SUCCESS) { + if(oci_start() == USB_ERR_SUCCESS) { + otg_dbg(OTG_DBG_ROOTHUB, "OTG Init Success\n"); + return USB_ERR_SUCCESS; + } + } + + return USB_ERR_FAIL; +} diff --git a/drivers/usb/host/s3c-otg/s3c-otg-roothub.h b/drivers/usb/host/s3c-otg/s3c-otg-roothub.h new file mode 100644 index 0000000..03883d1 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-roothub.h @@ -0,0 +1,65 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] :s3c-otg-roothub.h + * [Description] : The Header file defines the external and internal functions of RootHub. + * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com } + * [Department] : System LSI Division/Embedded S/W Platform + * [Created Date]: 2008/06/13 + * [Revision History] + * (1) 2008/06/13 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com } + * - Created this file and defines functions of RootHub + * + ****************************************************************************/ +/**************************************************************************** + * 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 _ROOTHUB_H_ +#define _ROOTHUB_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "s3c-otg-common-errorcode.h" +#include "s3c-otg-common-regdef.h" +#include "s3c-otg-common-datastruct.h" +#include "s3c-otg-hcdi-kal.h" +#include "s3c-otg-hcdi-memory.h" +#include "s3c-otg-oci.h" + +__inline__ int root_hub_feature( + struct usb_hcd *hcd, + const u8 port, + const u16 type_req, + const u16 feature, + void *buf); + +__inline__ int get_otg_port_status( + struct usb_hcd *hcd, const u8 port, char *status); + +int reset_and_enable_port(struct usb_hcd *hcd, const u8 port); +void bus_suspend(void); + +int bus_resume(struct sec_otghost *otghost); + +#ifdef __cplusplus +} +#endif +#endif /* _ROOTHUB_H_ */ + + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-scheduler-ischeduler.c b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-ischeduler.c new file mode 100644 index 0000000..d94fb1a --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-ischeduler.c @@ -0,0 +1,369 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : Scheduler.c + * [Description] : The source file implements the internal functions of Scheduler. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2009/2/10 + * [Revision History] + * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and implements functions of Scheduler + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-scheduler-scheduler.h" + +void init_scheduler(void) +{ + /*init_scheduling();*/ + init_transfer_ready_q(); +} + +/******************************************************************************/ +/*! + * @name int reserve_used_resource_for_periodic(u32 usb_time) + * + * @brief this function reserves the necessary resource of USB Transfer for Periodic Transfer. + * So, this function firstly checks there ares some available USB Time + * and Channel resource for USB Transfer. + * if there exists necessary resources for Periodic Transfer, then reserves the resource. + * + * @param [IN] usb_time = indicates the USB Time for the USB Transfer. + * + * @return USB_ERR_SUCCESS - if success to insert pInsertED to S3CScheduler. + * USB_ERR_NO_BANDWIDTH - if fail to reserve the USB Bandwidth. + * USB_ERR_NO_CHANNEL - if fail to reserve the Channel. + */ +/******************************************************************************/ + +int reserve_used_resource_for_periodic(u32 usb_time, u8 dev_speed, u8 trans_type) +{ + if(inc_perio_bus_time(usb_time,dev_speed)==USB_ERR_SUCCESS) { + if(inc_perio_chnum()==USB_ERR_SUCCESS) { + otg_usbcore_inc_usb_bandwidth(usb_time); + otg_usbcore_inc_periodic_transfer_cnt(trans_type); + return USB_ERR_SUCCESS; + } + else { + dec_perio_bus_time(usb_time); + return USB_ERR_NO_CHANNEL; + } + } + else { + return USB_ERR_NO_BANDWIDTH; + } +} + +/******************************************************************************/ +/*! + * @name int free_usb_resource_for_periodic(ed_t * pFreeED) + * + * @brief this function frees the resources to be allocated to pFreeED at S3CScheduler. + * that is, this functions only releases the resources to be allocated by S3C6400Scheduler. + * + * @param [IN] pFreeED = indicates ed_t to have the information of the resource to be released. + * + * @return USB_ERR_SUCCESS - if success to free the USB Resource. + * USB_ERR_FAIL - if fail to free the USB Resrouce. + */ +/******************************************************************************/ +int free_usb_resource_for_periodic( + u32 free_usb_time, u8 free_chnum, u8 trans_type) +{ + if(dec_perio_bus_time(free_usb_time)==USB_ERR_SUCCESS) { + if(dec_perio_chnum()==USB_ERR_SUCCESS) { + if(free_chnum!=CH_NONE) { + oci_channel_dealloc(free_chnum); + set_transferring_td_array(free_chnum, 0); + } + otg_usbcore_des_usb_bandwidth(free_usb_time); + otg_usbcore_des_periodic_transfer_cnt(trans_type); + return USB_ERR_SUCCESS; + } + } + return USB_ERR_FAIL; +} + +/******************************************************************************/ +/*! + * @name int remove_ed_from_scheduler(ed_t * remove_ed) + * + * @brief this function just remove the remove_ed from TransferReadyQ. So if you want to + * stop the USB Tranfer of remove_ed or release the releated resources. + * you should call another functions of S3CScheduler. + * + * @param [IN] remove_ed = indicates ed_t to be removed from TransferReadyQ. + * + * @return USB_ERR_SUCCESS - if success to remove the remove_ed from TransferReadyQ. + * USB_ERR_FAIL - if fail to remove the remove_ed from TransferReadyQ. + */ + /******************************************************************************/ +int remove_ed_from_scheduler(ed_t *remove_ed) +{ + if(remove_ed->ed_status.is_in_transfer_ready_q) { + remove_ed_from_ready_q(remove_ed); + remove_ed->ed_status.is_in_transfer_ready_q = false; + + return USB_ERR_SUCCESS; + } + else { + return USB_ERR_FAIL; + } +} + +/******************************************************************************/ +/*! + * @name int cancel_to_transfer_td(struct sec_otghost *otghost, td_t *cancel_td) + * + * @brief this function stop to execute the USB Transfer of cancel_td and + * release the Channel Resources to be allocated the cancel_td ,if the Transfer Type of + * cancel_td is NonPeriodic Transfer. + * this function don't release any usb resources(Channel, USB Bandwidth) for Periodic Transfer. + * if you want to release some usb resources for a periodic Transfer, you should call + * the free_usb_resource_for_periodic() + * + * @param [IN] cancel_td = indicates the td_t to be canceled. + * + * @return USB_ERR_SUCCESS - if success to cancel the USB Transfer of cancel_td. + * USB_ERR_FAIL - if fail to cancel the USB Transfer of cancel_td. + */ + /******************************************************************************/ +int cancel_to_transfer_td(struct sec_otghost *otghost, td_t *cancel_td) +{ + if(cancel_td->is_transfer_done) { + return USB_ERR_FAIL; + } + + if(cancel_td->is_transferring) { + int err; + + err = oci_stop_transfer(otghost, cancel_td->cur_stransfer.alloc_chnum); + + if(err == USB_ERR_SUCCESS) { + set_transferring_td_array(cancel_td->cur_stransfer.alloc_chnum,0); + + cancel_td->cur_stransfer.alloc_chnum = CH_NONE; + cancel_td->is_transferring = false; + cancel_td->parent_ed_p->ed_status.is_in_transferring = false; + cancel_td->parent_ed_p->ed_status.in_transferring_td = 0; + cancel_td->parent_ed_p->is_need_to_insert_scheduler = true; + + if(cancel_td->cur_stransfer.ed_desc_p->endpoint_type == BULK_TRANSFER || + cancel_td->cur_stransfer.ed_desc_p->endpoint_type == CONTROL_TRANSFER ) { + dec_nonperio_chnum(); + } + return err; + } + else { + return err; + } + } + else { + return USB_ERR_FAIL; + } + return USB_ERR_SUCCESS; +} + + +/******************************************************************************/ +/*! + * @name int retransmit(struct sec_otghost *otghost, td_t *retrasmit_td) + * + * @brief this function retransmits the retrasmit_td immediately. + * So, the Channel of pRetransmitted is reused for retransmittion. + * + * @param [IN] retrasmit_td = indicates the pointer ot the td_t to be retransmitted. + * + * @return USB_ERR_SUCCESS - if success to retransmit the retrasmit_td. + * USB_ERR_FAIL - if fail to retransmit the retrasmit_td. + */ + /******************************************************************************/ +int retransmit(struct sec_otghost *otghost, td_t *retrasmit_td) +{ + u32 td_addr=0; + + if(get_transferring_td_array(retrasmit_td->cur_stransfer.alloc_chnum,&td_addr)==USB_ERR_SUCCESS) { + if(td_addr == (u32)retrasmit_td) { + if(oci_start_transfer(otghost, &retrasmit_td->cur_stransfer) + == retrasmit_td->cur_stransfer.alloc_chnum) { + retrasmit_td->is_transferring = true; + retrasmit_td->parent_ed_p->ed_status.in_transferring_td = (u32)retrasmit_td; + retrasmit_td->parent_ed_p->ed_status.is_in_transfer_ready_q = false; + retrasmit_td->parent_ed_p->ed_status.is_in_transferring = true; + } + } + else { + return USB_ERR_FAIL; + } + } + else { + return USB_ERR_FAIL; + } + + return USB_ERR_SUCCESS; + +} + +/******************************************************************************/ +/*! + * @name int reschedule(td_t *reschedule_td) + * + * @brief this function re-schedules the reschedule_td. + * So, the Channel of pRescheuleTD is released and reschedule_td is inserted to TransferReadyQ. + * + * @param [IN] reschedule_td = indicates the pointer ot the td_t to be rescheduled. + * + * @return USB_ERR_SUCCESS - if success to re-schedule the reschedule_td. + * USB_ERR_FAIL - if fail to re-schedule the reschedule_td. + */ + /******************************************************************************/ +int reschedule(td_t *reschedule_td) +{ + u32 td_addr; + + if(get_transferring_td_array(reschedule_td->cur_stransfer.alloc_chnum, &td_addr)==USB_ERR_SUCCESS) + { + if((u32)reschedule_td == td_addr) + { + set_transferring_td_array(reschedule_td->cur_stransfer.alloc_chnum, 0); + oci_channel_dealloc(reschedule_td->cur_stransfer.alloc_chnum); + + reschedule_td->cur_stransfer.alloc_chnum = CH_NONE; + reschedule_td->parent_ed_p->is_need_to_insert_scheduler = true; + reschedule_td->parent_ed_p->ed_status.in_transferring_td = 0; + + if(reschedule_td->parent_ed_p->ed_desc.endpoint_type == BULK_TRANSFER|| + reschedule_td->parent_ed_p->ed_desc.endpoint_type == CONTROL_TRANSFER ) + { + /* Increase the available Channel */ + dec_nonperio_chnum(); + + } + + insert_ed_to_ready_q(reschedule_td->parent_ed_p, false); + reschedule_td->parent_ed_p->ed_status.is_in_transfer_ready_q =true; + + } + else + { + /* this case is not support.... */ + } + } + + return USB_ERR_SUCCESS; +} + +/******************************************************************************/ +/*! + * @name int deallocate(td_t *deallocate_td) + * + * @brief this function frees resources to be allocated deallocate_td by S3CScheduler. + * this function just free the resource by S3CScheduler. that is, Channel Resource. + * if there are another td_t at ed_t, deallocate() insert the ed_t to TransferReadyQ. + * + * @param [IN] deallocate_td = indicates the pointer ot the td_t to be deallocated. + * + * @return USB_ERR_SUCCESS - if success to dealloate the resources for the deallocate_td. + * USB_ERR_FAIL - if fail to dealloate the resources for the deallocate_td. + */ + /******************************************************************************/ +int deallocate(td_t *deallocate_td) +{ + u32 td_addr; + + if(get_transferring_td_array(deallocate_td->cur_stransfer.alloc_chnum , &td_addr)==USB_ERR_SUCCESS) + { + if((u32)deallocate_td == td_addr) + { + set_transferring_td_array(deallocate_td->cur_stransfer.alloc_chnum, 0); + oci_channel_dealloc(deallocate_td->cur_stransfer.alloc_chnum); + + deallocate_td->cur_stransfer.alloc_chnum = CH_NONE; + + if(deallocate_td->parent_ed_p->ed_desc.endpoint_type == BULK_TRANSFER|| + deallocate_td->parent_ed_p->ed_desc.endpoint_type == CONTROL_TRANSFER ) + { + /* Increase the available Channel */ + dec_nonperio_chnum(); + } + + deallocate_td->parent_ed_p->is_need_to_insert_scheduler = true; + + if(deallocate_td->parent_ed_p->num_td) + { + /* insert ed_t to TransferReadyQ. */ + insert_ed_to_ready_q(deallocate_td->parent_ed_p , false); + deallocate_td->parent_ed_p->ed_status.is_in_transfer_ready_q = true; + deallocate_td->parent_ed_p->is_need_to_insert_scheduler = false; + } + return USB_ERR_SUCCESS; + } + else + { + return USB_ERR_FAIL; + } + } + else + { + return USB_ERR_FAIL; + } + +} + +/* TBD.... */ +void do_schedule(struct sec_otghost *otghost) +{ + if(get_avail_chnum()) { + do_periodic_schedule(otghost); + do_nonperiodic_schedule(otghost); + } +} + +/******************************************************************************/ +/*! + * @name int get_td_info(u8 chnum, + * unsigned int *td_addr_p) + * + * @brief this function returns the pointer of td_t at TransferringTDArray[chnum] + * + * @param [IN] chnum = indicates the index of TransferringTDArray + * to include the address of td_t which we gets + * [OUT] td_addr_p= indicate pointer to store the address of td_t. + * + * @return USB_ERR_SUCCESS -if success to get the address of td_t. + * USB_ERR_FAIL -if fail to get the address of td_t. + */ + /******************************************************************************/ +int get_td_info( u8 chnum, + unsigned int *td_addr_p) +{ + u32 td_addr; + + if(get_transferring_td_array(chnum, &td_addr)==USB_ERR_SUCCESS) + { + *td_addr_p = td_addr; + return USB_ERR_SUCCESS; + } + + return USB_ERR_FAIL; +} + + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-scheduler-readyq.c b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-readyq.c new file mode 100644 index 0000000..7b41f0c --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-readyq.c @@ -0,0 +1,264 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : TransferReadyQ.c + * [Description] : The source file implements the internal functions of TransferReadyQ. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/04 + * [Revision History] + * (1) 2008/06/04 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and implements functions of TransferReadyQ. + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * + ****************************************************************************/ + +/**************************************************************************** + * 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 "s3c-otg-scheduler-scheduler.h" + +static trans_ready_q_t periodic_trans_ready_q; +static trans_ready_q_t nonperiodic_trans_ready_q; + +/******************************************************************************/ +/*! + * @name void init_transfer_ready_q(void) + * + * @brief this function initiates PeriodicTransferReadyQ and NonPeriodicTransferReadyQ. + * + * + * @param void + * + * @return void. + */ +/******************************************************************************/ +void init_transfer_ready_q(void) +{ + otg_dbg(OTG_DBG_SCHEDULE,"start init_transfer_ready_q\n"); + + otg_list_init(&periodic_trans_ready_q.trans_ready_q_list_head); + periodic_trans_ready_q.is_periodic_transfer = true; + periodic_trans_ready_q.trans_ready_entry_num = 0; + periodic_trans_ready_q.total_alloc_chnum = 0; + periodic_trans_ready_q.total_perio_bus_bandwidth = 0; + + otg_list_init(&nonperiodic_trans_ready_q.trans_ready_q_list_head); + nonperiodic_trans_ready_q.is_periodic_transfer = false; + nonperiodic_trans_ready_q.trans_ready_entry_num = 0; + nonperiodic_trans_ready_q.total_alloc_chnum = 0; + nonperiodic_trans_ready_q.total_perio_bus_bandwidth = 0; + + +} + + +/******************************************************************************/ +/*! + * @name int insert_ed_to_ready_q(ed_t *insert_ed, + * bool f_isfirst) + * + * @brief this function inserts ed_t * to TransferReadyQ. + * + * + * @param [IN] insert_ed = indicates the ed_t to be inserted to TransferReadyQ. + * [IN] f_isfirst = indicates whether the insert_ed is inserted as first entry of TransferReadyQ. + * + * @return USB_ERR_SUCCESS -if successes to insert the insert_ed to TransferReadyQ. + * USB_ERR_FAILl -if fails to insert the insert_ed to TransferReadyQ. + */ +/******************************************************************************/ +int insert_ed_to_ready_q(ed_t *insert_ed, + bool f_isfirst) +{ + + if(insert_ed->ed_desc.endpoint_type == BULK_TRANSFER|| + insert_ed->ed_desc.endpoint_type == CONTROL_TRANSFER) + { + if(f_isfirst) + { + otg_list_push_next(&insert_ed->trans_ready_q_list_entry,&nonperiodic_trans_ready_q.trans_ready_q_list_head); + } + else + { + otg_list_push_prev(&insert_ed->trans_ready_q_list_entry,&nonperiodic_trans_ready_q.trans_ready_q_list_head); + } + nonperiodic_trans_ready_q.trans_ready_entry_num++; + } + else + { + if(f_isfirst) + { + otg_list_push_next(&insert_ed->trans_ready_q_list_entry,&periodic_trans_ready_q.trans_ready_q_list_head); + } + else + { + otg_list_push_prev(&insert_ed->trans_ready_q_list_entry,&periodic_trans_ready_q.trans_ready_q_list_head); + } + periodic_trans_ready_q.trans_ready_entry_num++; + } + + return USB_ERR_SUCCESS; + +} + + +u32 get_periodic_ready_q_entity_num(void) +{ + return periodic_trans_ready_q.trans_ready_entry_num; +} +/******************************************************************************/ +/*! + * @name int remove_ed_from_ready_q(ed_t *remove_ed) + * + * @brief this function removes ed_t * from TransferReadyQ. + * + * + * @param [IN] remove_ed = indicate the ed_t to be removed from TransferReadyQ. + * + * @return USB_ERR_SUCCESS -if successes to remove the remove_ed from TransferReadyQ. + * USB_ERR_FAILl -if fails to remove the remove_ed from TransferReadyQ. + */ +/******************************************************************************/ +int remove_ed_from_ready_q(ed_t *remove_ed) +{ +// SPINLOCK_t SLForRemoveED_t = SPIN_LOCK_INIT; +// u32 uiSLFlag=0; + + otg_list_pop(&remove_ed->trans_ready_q_list_entry); + + if(remove_ed->ed_desc.endpoint_type == BULK_TRANSFER|| + remove_ed->ed_desc.endpoint_type == CONTROL_TRANSFER) + { +// spin_lock_irq_save_otg(&SLForRemoveED_t, uiSLFlag); +// otg_list_pop(&remove_ed->trans_ready_q_list_entry); + nonperiodic_trans_ready_q.trans_ready_entry_num--; +// spin_unlock_irq_save_otg(&SLForRemoveED_t, uiSLFlag); + } + else + { +// spin_lock_irq_save_otg(&SLForRemoveED_t, uiSLFlag); +// otg_list_pop(&remove_ed->trans_ready_q_list_entry); + periodic_trans_ready_q.trans_ready_entry_num--; +// spin_unlock_irq_save_otg(&SLForRemoveED_t, uiSLFlag); + } + + return USB_ERR_SUCCESS; + +} + +//by ss1 unused func +/* +bool check_ed_on_ready_q(ed_t *check_ed_p) +{ + + if(check_ed_p->ed_status.is_in_transfer_ready_q) + return true; + else + return false; +}*/ + +/******************************************************************************/ +/*! + * @name int get_ed_from_ready_q(bool f_isperiodic, + * td_t **get_ed) + * + * @brief this function returns the first entity of TransferReadyQ. + * if there are some ed_t on TransferReadyQ, this function pops first ed_t from TransferReadyQ. + * So, the TransferReadyQ don's has the poped ed_t. + * + * + * @param [IN] f_isperiodic = indicate whether Periodic or not + * [OUT] get_ed = indicate the double pointer to store the address of first entity + * on TransferReadyQ. + * + * @return USB_ERR_SUCCESS -if successes to get frist ed_t from TransferReadyQ. + * USB_ERR_NO_ENTITY -if fails to get frist ed_t from TransferReadyQ + * because there is no entity on TransferReadyQ. + */ +/******************************************************************************/ + +int get_ed_from_ready_q(bool f_isperiodic, + ed_t **get_ed) +{ + if(f_isperiodic) + { + otg_list_head *transreadyq_list_entity=NULL; + + if(periodic_trans_ready_q.trans_ready_entry_num==0) + { + return USB_ERR_NO_ENTITY; + } + + transreadyq_list_entity = periodic_trans_ready_q.trans_ready_q_list_head.next; + + //if(transreadyq_list_entity!= &periodic_trans_ready_q.trans_ready_q_list_head) + if(!otg_list_empty(&periodic_trans_ready_q.trans_ready_q_list_head)) + { + *get_ed = otg_list_get_node(transreadyq_list_entity,ed_t,trans_ready_q_list_entry); + if (transreadyq_list_entity->prev == LIST_POISON2 || + transreadyq_list_entity->next == LIST_POISON1) { + printk(KERN_ERR "s3c-otg-scheduler get_ed_from_ready_q error\n"); + periodic_trans_ready_q.trans_ready_entry_num =0; + } + else { + otg_list_pop(transreadyq_list_entity); + periodic_trans_ready_q.trans_ready_entry_num--; + } + + return USB_ERR_SUCCESS; + } + else + { + return USB_ERR_NO_ENTITY; + } + } + else + { + otg_list_head *transreadyq_list_entity=NULL; + + if(nonperiodic_trans_ready_q.trans_ready_entry_num==0) + { + return USB_ERR_NO_ENTITY; + } + + transreadyq_list_entity = nonperiodic_trans_ready_q.trans_ready_q_list_head.next; + + //if(transreadyq_list_entity!= &nonperiodic_trans_ready_q.trans_ready_q_list_head) + if(!otg_list_empty(&nonperiodic_trans_ready_q.trans_ready_q_list_head)) + { + *get_ed = otg_list_get_node(transreadyq_list_entity,ed_t, trans_ready_q_list_entry); + if (transreadyq_list_entity->prev == LIST_POISON2 || + transreadyq_list_entity->next == LIST_POISON1) { + printk(KERN_ERR "s3c-otg-scheduler get_ed_from_ready_q error\n"); + nonperiodic_trans_ready_q.trans_ready_entry_num =0; + } + else { + otg_list_pop(transreadyq_list_entity); + nonperiodic_trans_ready_q.trans_ready_entry_num--; + } + + return USB_ERR_SUCCESS; + } + else + { + return USB_ERR_NO_ENTITY; + } + } +} + + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.c b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.c new file mode 100644 index 0000000..81777a9 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.c @@ -0,0 +1,416 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : Scheduler.c + * [Description] : The source file implements the internal functions of Scheduler. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/04 + * [Revision History] + * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and implements functions of Scheduler + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-scheduler-scheduler.h" + +//Define constant variables + +//the max periodic bus time is 80%*125us on High Speed Mode +static const u32 perio_highbustime_threshold = 100; + +//the max periodic bus time is 90%*1000us(1ms) on Full/Low Speed Mode . +static const u32 perio_fullbustime_threshold = 900; + +static const u8 perio_chnum_threshold = 14; +//static const u8 total_chnum_threshold = 16; +static u8 total_chnum_threshold = 16; + + //Define global variables +// kevinh: add volatile +static volatile u32 perio_used_bustime = 0; +static volatile u8 perio_used_chnum = 0; +static volatile u8 nonperio_used_chnum = 0; +static volatile u8 total_used_chnum = 0; +static volatile u32 transferring_td_array[16]={0}; + + +int inc_perio_bus_time(u32 bus_time, u8 dev_speed) +{ + switch(dev_speed) { + case HIGH_SPEED_OTG: + if((bus_time+perio_used_bustime)<=perio_highbustime_threshold) { + perio_used_bustime=+bus_time; + return USB_ERR_SUCCESS; + } + else { + return USB_ERR_FAIL; + } + + case LOW_SPEED_OTG: + case FULL_SPEED_OTG: + if((bus_time+perio_used_bustime)<=perio_fullbustime_threshold) { + perio_used_bustime=+bus_time; + return USB_ERR_SUCCESS; + } + else { + return USB_ERR_FAIL; + } + case SUPER_SPEED_OTG: + break; + default: + break; + } + return USB_ERR_FAIL; +} + +int dec_perio_bus_time(u32 bus_time) +{ + if(perio_used_bustime >= bus_time ) { + perio_used_bustime =- bus_time; + return USB_ERR_SUCCESS; + } + else { + return USB_ERR_FAIL; + } +} + +int inc_perio_chnum(void) +{ + if(perio_used_chnum<perio_chnum_threshold) + { + if(total_used_chnum<total_chnum_threshold) + { + perio_used_chnum++; + total_used_chnum++; + return USB_ERR_SUCCESS; + } + } + return USB_ERR_FAIL; +} + +u8 get_avail_chnum(void) +{ + return total_chnum_threshold - total_used_chnum; +} + +int dec_perio_chnum(void) +{ + if(perio_used_chnum>0) + { + if(total_used_chnum>0) + { + perio_used_chnum--; + total_used_chnum--; + return USB_ERR_SUCCESS; + } + } + return USB_ERR_FAIL; +} + +int inc_non_perio_chnum(void) +{ + if(nonperio_used_chnum<total_chnum_threshold) + { + if(total_used_chnum<total_chnum_threshold) + { + nonperio_used_chnum++; + total_used_chnum++; + return USB_ERR_SUCCESS; + } + } + return USB_ERR_FAIL; +} + +int dec_nonperio_chnum(void) +{ + if(nonperio_used_chnum>0) + { + if(total_used_chnum>0) + { + nonperio_used_chnum--; + total_used_chnum--; + return USB_ERR_SUCCESS; + } + } + return USB_ERR_FAIL; +} + +int get_transferring_td_array(u8 chnum, unsigned int *td_addr) +{ + if(transferring_td_array[chnum]!=0) + { + *td_addr = transferring_td_array[chnum]; + return USB_ERR_SUCCESS; + } + + return USB_ERR_FAIL; +} + +int set_transferring_td_array(u8 chnum, u32 td_addr) +{ + if(td_addr ==0) + { + transferring_td_array[chnum] = td_addr; + return USB_ERR_SUCCESS; + } + + if(transferring_td_array[chnum] == 0) + { + transferring_td_array[chnum] = td_addr; + return USB_ERR_SUCCESS; + } + else + { + return USB_ERR_FAIL; + } +} + +/******************************************************************************/ +/*! + * @name int insert_ed_to_scheduler(struct sec_otghost *otghost, ed_t *insert_ed) + * + * @brief this function transfers the insert_ed to S3C6400Scheduler, and + * after that, the insert_ed is inserted to TransferReadyQ and scheduled by Scheduler. + * + * + * @param [IN] insert_ed = indicates pointer of ed_t to be inserted to TransferReadyQ. + * + * @return USB_ERR_ALREADY_EXIST - if the insert_ed is already existed. + * USB_ERR_SUCCESS - if success to insert insert_ed to S3CScheduler. + */ +/******************************************************************************/ +int insert_ed_to_scheduler(struct sec_otghost *otghost, ed_t *insert_ed) +{ + if(!insert_ed->is_need_to_insert_scheduler) + return USB_ERR_ALREADY_EXIST; + + insert_ed_to_ready_q(insert_ed, false); + insert_ed->is_need_to_insert_scheduler = false; + insert_ed->ed_status.is_in_transfer_ready_q = true; + + do_periodic_schedule(otghost); + do_nonperiodic_schedule(otghost); + + return USB_ERR_SUCCESS; +} + +/******************************************************************************/ +/*! + * @name int do_periodic_schedule(struct sec_otghost *otghost) + * + * @brief this function schedules PeriodicTransferReadyQ. + * this function checks whether PeriodicTransferReadyQ has some ed_t. + * if there are some ed_t on PeriodicTransferReadyQ + * , this function request to start USB Trasnfer to S3C6400OCI. + * + * + * @param void + * + * @return void + */ +/******************************************************************************/ +void do_periodic_schedule(struct sec_otghost *otghost) +{ + ed_t *scheduling_ed= NULL; + int err_sched = USB_ERR_SUCCESS; + u32 sched_cnt = 0; + + otg_dbg(OTG_DBG_SCHEDULE,"*******Start to DoPeriodicSchedul*********\n"); + + sched_cnt = get_periodic_ready_q_entity_num(); + + while(sched_cnt) { + //in periodic transfser, the channel resource was already reserved. + //So, we don't need this routine... + +start_sched_perio_transfer: + if(!sched_cnt) + goto end_sched_perio_transfer; + + err_sched = get_ed_from_ready_q(true, &scheduling_ed); + + if(err_sched==USB_ERR_SUCCESS) { + otg_list_head *td_list_entry; + td_t *td; + u32 cur_frame_num = 0; + + otg_dbg(OTG_DBG_SCHEDULE,"the ed_t to be scheduled :%d",(int)scheduling_ed); + sched_cnt--; + td_list_entry = scheduling_ed->td_list_entry.next; + + if(td_list_entry == &scheduling_ed->td_list_entry) { + //scheduling_ed has no td_t. so we schedules another ed_t on PeriodicTransferReadyQ. + goto start_sched_perio_transfer; + } + + if(scheduling_ed->ed_status.is_in_transferring) { + //scheduling_ed is already Scheduled. so we schedules another ed_t on PeriodicTransferReadyQ. + goto start_sched_perio_transfer; + } + + cur_frame_num = oci_get_frame_num(); + + if(((cur_frame_num-scheduling_ed->ed_desc.sched_frame)&HFNUM_MAX_FRNUM)>(HFNUM_MAX_FRNUM>>1)) { + insert_ed_to_ready_q(scheduling_ed, false); + goto start_sched_perio_transfer; + } + + td = otg_list_get_node(td_list_entry, td_t, td_list_entry); + + if((!td->is_transferring) && (!td->is_transfer_done)) { + u8 alloc_ch; + otg_dbg(OTG_DBG_SCHEDULE,"the td_t to be scheduled :%d",(int)td); + alloc_ch = oci_start_transfer(otghost, &td->cur_stransfer); + if(alloc_ch<total_chnum_threshold) { + td->cur_stransfer.alloc_chnum = alloc_ch; + set_transferring_td_array(alloc_ch, (u32)td); + + scheduling_ed->ed_status.is_in_transferring = true; + scheduling_ed->ed_status.is_in_transfer_ready_q = false; + scheduling_ed->ed_status.in_transferring_td = (u32)td; + + td->is_transferring = true; + } + else { + //we should insert the ed_t to TransferReadyQ, because the USB Transfer of the ed_t is failed. + scheduling_ed->ed_status.is_in_transferring = false; + scheduling_ed->ed_status.is_in_transfer_ready_q = true; + scheduling_ed->ed_status.in_transferring_td = 0; + + insert_ed_to_ready_q(scheduling_ed,true); + + scheduling_ed->is_need_to_insert_scheduler = false; + goto end_sched_perio_transfer; + } + + } + else { // the selected td_t was already transferring or completed to transfer. + //we should decide how to control this case. + goto end_sched_perio_transfer; + } + } + else { + // there is no ED on PeriodicTransferQ. So we finish scheduling. + goto end_sched_perio_transfer; + } + } + +end_sched_perio_transfer: + + return; +} + + +/******************************************************************************/ +/*! + * @name int do_nonperiodic_schedule(struct sec_otghost *otghost) + * + * @brief this function start to schedule thie NonPeriodicTransferReadyQ. + * this function checks whether NonPeriodicTransferReadyQ has some ed_t. + * if there are some ed_t on NonPeriodicTransferReadyQ + * , this function request to start USB Trasnfer to S3C6400OCI. + * + * + * @param void + * + * @return void + */ +/******************************************************************************/ +void do_nonperiodic_schedule(struct sec_otghost *otghost) +{ + if(total_used_chnum<total_chnum_threshold) { + ed_t *scheduling_ed; + int err_sched; + + while(1) { + +start_sched_nonperio_transfer: + + //check there is available channel resource for Non-Periodic Transfer. + if(total_used_chnum==total_chnum_threshold) { + goto end_sched_nonperio_transfer; + } + + err_sched = get_ed_from_ready_q(false, &scheduling_ed); + + if(err_sched ==USB_ERR_SUCCESS ) { + otg_list_head *td_list_entry; + td_t *td; + + td_list_entry = scheduling_ed->td_list_entry.next; + + //if(td_list_entry == &scheduling_ed->td_list_entry) + if(otg_list_empty(&scheduling_ed->td_list_entry)) { + //scheduling_ed has no td_t. so we schedules another ed_t on PeriodicTransferReadyQ. + goto start_sched_nonperio_transfer; + } + + if(scheduling_ed->ed_status.is_in_transferring) { + //scheduling_ed is already Scheduled. so we schedules another ed_t on PeriodicTransferReadyQ. + goto start_sched_nonperio_transfer; + } + + td = otg_list_get_node(td_list_entry, td_t, td_list_entry); + + if((!td->is_transferring) && (!td->is_transfer_done)) { + u8 alloc_ch; + + alloc_ch = oci_start_transfer(otghost, &td->cur_stransfer); + + if(alloc_ch<total_chnum_threshold) { + td->cur_stransfer.alloc_chnum = alloc_ch; + set_transferring_td_array(alloc_ch, (u32)td); + + inc_non_perio_chnum(); + + scheduling_ed->ed_status.is_in_transferring = true; + scheduling_ed->ed_status.is_in_transfer_ready_q = false; + scheduling_ed->ed_status.in_transferring_td =(u32)td; + td->is_transferring = true; + } + else { + //we should insert the ed_t to TransferReadyQ, because the USB Transfer of the ed_t is failed. + scheduling_ed->ed_status.is_in_transferring = false; + scheduling_ed->ed_status.in_transferring_td =0; + insert_ed_to_ready_q(scheduling_ed,true); + scheduling_ed->ed_status.is_in_transfer_ready_q = true; + + goto end_sched_nonperio_transfer; + } + } + else { + goto end_sched_nonperio_transfer; + } + } + else { //there is no ed_t on NonPeriodicTransferReadyQ. + //So, we finish do_nonperiodic_schedule(). + goto end_sched_nonperio_transfer; + } + } + } + +end_sched_nonperio_transfer: + + return; +} + + + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.h b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.h new file mode 100644 index 0000000..eb43f41 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-scheduler-scheduler.h @@ -0,0 +1,98 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : Scheduler.h + * [Description] : The Header file defines the external and internal functions of Scheduler. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/03 + * [Revision History] + * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and defines functions of Scheduler + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * + ****************************************************************************/ +/**************************************************************************** + * 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 _SCHEDULER_H +#define _SCHEDULER_H + +/* +// ---------------------------------------------------------------------------- +// Include files : None. +// ---------------------------------------------------------------------------- +*/ +//#include "s3c-otg-common-typedef.h" +#include "s3c-otg-common-const.h" +#include "s3c-otg-common-errorcode.h" +#include "s3c-otg-common-datastruct.h" +#include "s3c-otg-hcdi-memory.h" +#include "s3c-otg-hcdi-kal.h" +#include "s3c-otg-hcdi-debug.h" +#include "s3c-otg-oci.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +//Defines external functions of IScheduler.c +extern void init_scheduler(void); +extern int reserve_used_resource_for_periodic(u32 usb_time,u8 dev_speed, u8 trans_type); +extern int free_usb_resource_for_periodic(u32 free_usb_time, u8 free_chnum, u8 trans_type); +extern int remove_ed_from_scheduler(ed_t *remove_ed); +extern int cancel_to_transfer_td(struct sec_otghost *otghost, td_t *cancel_td); +extern int retransmit(struct sec_otghost *otghost, td_t *retransmit_td); +extern int reschedule(td_t *resched_td); +extern int deallocate(td_t *dealloc_td); +extern void do_schedule(struct sec_otghost *otghost); +extern int get_td_info(u8 chnum,unsigned int *td_addr); + +// Defines functiions of TranferReadyQ. +void init_transfer_ready_q(void); +int insert_ed_to_ready_q(ed_t *insert_ed, bool f_isfirst); +int remove_ed_from_ready_q(ed_t *remove_ed); +int get_ed_from_ready_q(bool f_isperiodic, ed_t **get_ed); + +//Define functions of Scheduler +void do_periodic_schedule(struct sec_otghost *otghost); +void do_nonperiodic_schedule(struct sec_otghost *otghost); +int set_transferring_td_array(u8 chnum, u32 td_addr); +int get_transferring_td_array(u8 chnum, unsigned int *td_addr); + + +//Define fuctions to manage some static global variable. +int inc_perio_bus_time(u32 uiBusTime, u8 dev_speed); +int dec_perio_bus_time(u32 uiBusTime); + +u8 get_avail_chnum(void); +int inc_perio_chnum(void); +int dec_perio_chnum(void); +int inc_non_perio_chnum(void); +int dec_nonperio_chnum(void); +u32 get_periodic_ready_q_entity_num(void); + +int insert_ed_to_scheduler(struct sec_otghost *otghost, ed_t *insert_ed); + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transfer-common.c b/drivers/usb/host/s3c-otg/s3c-otg-transfer-common.c new file mode 100644 index 0000000..3f5c9a9 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transfer-common.c @@ -0,0 +1,805 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : Commons3c-otg-transfer-transfer.h + * [Description] : This source file implements the functions to be defined at CommonTransfer Module. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/03 + * [Revision History] + * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and implements some functions of CommonTransfer. + * (2) 2008/07/15 by SeungSoo Yang ( ss1.yang@samsung.com )n + * - Optimizing for performance \n + * (3) 2008/08/18 by SeungSoo Yang ( ss1.yang@samsung.com ) + * - Modifying for successful rmmod & disconnecting \n + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-transfer-transfer.h" + +// the header pointer to indicate the ED_list to manage the ed_t to be created and initiated. +static otg_list_head ed_list_head; +static u32 ref_periodic_transfer; + +/******************************************************************************/ +/*! + * @name void init_transfer(void) + * + * @brief this function initiates the S3CTranfer module. that is, this functions initiates + * the ED_list_head OTG List which manages the all ed_t to be existed. + * + * @param void + * + * @return void + */ +/******************************************************************************/ + +void init_transfer(void) +{ + otg_dbg(OTG_DBG_TRANSFER,"start to init_transfer\n"); + otg_list_init(&ed_list_head); + ref_periodic_transfer = 0; +} + + +/******************************************************************************/ +/*! + * @name void DeInitTransfer(void) + * + * @brief this function Deinitiates the S3CTranfer module. this functions check which there are + * some ed_t on ED_list_head. if some ed_t exists, deinit_transfer() deletes the ed_t. + * + * + * @param void + * + * @return void + */ +/******************************************************************************/ +void deinit_transfer(struct sec_otghost *otghost) +{ + otg_list_head *ed_list_member; + ed_t *delete_ed_p; + + while(otg_list_empty(&ed_list_head) != true) { + ed_list_member = ed_list_head.next; + + /* otg_list_pop(ed_list_member); */ + + delete_ed_p= otg_list_get_node(ed_list_member,ed_t,ed_list_entry); + + delete_ed(otghost, delete_ed_p); + } +} + +/******************************************************************************/ +/*! + * @name int delete_ed(ed_t *delete_ed) + * + * @brief this function delete the delete_ed. + * if there is some available TD_ts on delete_ed, then this function also deletes these td_t + * + * + * @param [IN] delete_ed = indicates the address of ed_t to be deleted. + * + * @return USB_ERR_SUCCESS -if successes to delete the ed_t. + * USB_ERR_FAILl -if fails to delete the ed_t. + */ +/******************************************************************************/ +int delete_ed(struct sec_otghost *otghost, ed_t *delete_ed) +{ + otg_kal_make_ep_null(delete_ed); + + if(delete_ed->num_td) { + cancel_all_td(otghost, delete_ed); + /** + need to giveback of td's urb with considering life-cycle of + TD, ED, urb->hcpriv, td->private, ep->hcpriv, td->parentED + (commented by ss1.yang) + */ + } + + otg_list_pop(&delete_ed->ed_list_entry); + + if(delete_ed->ed_desc.endpoint_type == INT_TRANSFER || + delete_ed->ed_desc.endpoint_type == ISOCH_TRANSFER) { + ref_periodic_transfer--; + } + + if(ref_periodic_transfer==0) { + disable_sof(); + } + otg_mem_free(delete_ed); + + return USB_ERR_SUCCESS; +} + +/******************************************************************************/ +/*! + * @name int delete_td(struct sec_otghost *otghost, td_t *delete_td) + * + * @brief this function frees memory resource for the delete_td. + * and if delete_td is transferring USB Transfer, then this function request to cancel + * the USB Transfer to S3CScheduler. + * + * + * @param [OUT] new_td_p = returns the address of the new td_t . + * + * @return USB_ERR_SUCCESS -if successes to create the new td_t. + * USB_ERR_FAILl -if fails to create to new td_t. + */ +/******************************************************************************/ +int delete_td(struct sec_otghost *otghost, td_t *delete_td) +{ + if(delete_td->is_transferring) { + //at this case, we should cancel the USB Transfer. + cancel_to_transfer_td(otghost, delete_td); + } + + otg_mem_free(delete_td); + return USB_ERR_SUCCESS; +} + +int create_isoch_packet_desc(isoch_packet_desc_t **new_isoch_packet_desc, + u32 isoch_packet_num) +{ + return otg_mem_alloc((void **)new_isoch_packet_desc, + (u16)sizeof(isoch_packet_desc_t)*isoch_packet_num,USB_MEM_SYNC); +} + +int delete_isoch_packet_desc(isoch_packet_desc_t *del_isoch_packet_desc, + u32 isoch_packet_num) +{ + return otg_mem_free(del_isoch_packet_desc); +} + +/******************************************************************************/ +/*! + * @name void init_isoch_packet_desc( isoch_packet_desc_t *init_isoch_packet_desc, + * u32 isoch_packet_start_addr, + * u32 isoch_packet_size, + * u32 index) + * + * @brief this function initiates the isoch_packet_desc_t[index]. + * + * + * @param [OUT] init_isoch_packet_desc = indicates the pointer of IsochPackDesc_t to be initiated. + * [IN] isoch_packet_start_addr = indicates the start address of the buffer to be used + * at USB Isochronous Transfer. + * [IN] isoch_packet_size = indicates the size of Isochronous packet. + * [IN] index = indicates the index to be mapped with this init_isoch_packet_desc. + * + * @return void + */ +/******************************************************************************/ +void init_isoch_packet_desc(isoch_packet_desc_t *init_isoch_packet_desc, + u32 isoch_packet_start_addr, + u32 isoch_packet_size, + u32 index) +{ + init_isoch_packet_desc[index].buf_size = isoch_packet_size; + init_isoch_packet_desc[index].isoch_packiet_start_addr = isoch_packet_start_addr; + init_isoch_packet_desc[index].isoch_status = 0; + init_isoch_packet_desc[index].transferred_szie = 0; +} + +/******************************************************************************/ +/*! + * @name int create_ed(ed_t **new_ed) + * + * @brief this function creates a new ed_t and returns the ed_t to Caller + * + * + * @param [OUT] new_ed = returns the address of the new ed_t . + * + * @return USB_ERR_SUCCESS -if successes to create the new ed_t. + * USB_ERR_FAILl -if fails to create to new ed_t. + */ +/******************************************************************************/ +int create_ed(ed_t **new_ed) +{ + int err_code = USB_ERR_SUCCESS; + + err_code = otg_mem_alloc((void **)new_ed,(u16)sizeof(ed_t), USB_MEM_ASYNC); + otg_mem_set(*new_ed, 0, sizeof(ed_t)); + return err_code; +} + + +/******************************************************************************/ +/*! + * @name int init_ed( ed_t *init_ed, + * u8 dev_addr, + * u8 ep_num, + * bool f_is_ep_in, + * u8 dev_speed, + * u8 ep_type, + * u32 max_packet_size, + * u8 multi_count, + * u8 interval, + * u32 sched_frame, + * u8 hub_addr, + * u8 hub_port, + * bool f_is_do_split) + * + * @brief this function initiates the init_ed by using the another parameters. + * + * + * @param [OUT] init_ed = returns the ed_t to be initiated. + * [IN] dev_addr = inidcates the address of USB Device. + * [IN] ep_num = inidcates the number of the specific endpoint on USB Device. + * [IN] f_is_ep_in = inidcates whether the endpoint is IN or not + * [IN] dev_speed = inidcates the speed of USB Device. + * [IN] max_packet_size = inidcates the maximum packet size of a specific endpoint on USB Device. + * [IN] multi_count = if the endpoint supports periodic transfer + * , this indicates the multiple packet to be transferred on a uframe + * [IN] interval= if the endpoint support periodic transfer, this indicates the polling rate. + * [IN] sched_frame= if the endpoint supports periodic transfer, this indicates the start frame number. + * [IN] hub_addr= indicate the address of hub which the USB device attachs to. + * [IN] hub_port= inidcates the port number of the hub which the USB device attachs to. + * [IN] f_is_do_split= inidcates whether this tranfer is split transaction or not. + * + * @return USB_ERR_SUCCESS -if successes to initiate the ed_t. + * USB_ERR_FAILl -if fails to initiate the ed_t. + * USB_ERR_NOSPACE -if fails to initiate the ed_t + * because there is no USB Resource for this init_ed. + */ +/******************************************************************************/ +int init_ed(ed_t *init_ed, + u8 dev_addr, + u8 ep_num, + bool f_is_ep_in, + u8 dev_speed, + u8 ep_type, + u16 max_packet_size, + u8 multi_count, + u8 interval, + u32 sched_frame, + u8 hub_addr, + u8 hub_port, + bool f_is_do_split, + void *ep) +{ + init_ed->is_halted = false; + init_ed->is_need_to_insert_scheduler= true; + init_ed->ed_id = (u32)init_ed; + init_ed->num_td = 0; + init_ed->ed_private = ep; + + otg_list_init(&init_ed->td_list_entry); + + //start to initiate struct ed_desc.... + init_ed->ed_desc.is_do_split = f_is_do_split; + init_ed->ed_desc.is_ep_in = f_is_ep_in; + init_ed->ed_desc.dev_speed = dev_speed; + init_ed->ed_desc.hub_addr = hub_addr; + init_ed->ed_desc.hub_port = hub_port; + init_ed->ed_desc.mc = multi_count; + init_ed->ed_desc.device_addr = dev_addr; + init_ed->ed_desc.endpoint_num = ep_num; + init_ed->ed_desc.endpoint_type = ep_type; + init_ed->ed_desc.max_packet_size = max_packet_size; + init_ed->ed_desc.sched_frame = sched_frame; + + if(init_ed->ed_desc.endpoint_type == INT_TRANSFER) { + if(init_ed->ed_desc.dev_speed == LOW_SPEED_OTG ||init_ed->ed_desc.dev_speed == FULL_SPEED_OTG) { + init_ed->ed_desc.interval =interval; + } + else if(init_ed->ed_desc.dev_speed == HIGH_SPEED_OTG) { + u8 count = 0; + u8 cal_interval = 1; + + for(count = 0;count<(init_ed->ed_desc.interval-1);count++) { + cal_interval *=2; + } + + init_ed->ed_desc.interval =cal_interval; + } + else { + otg_dbg(OTG_DBG_TRANSFER,"Super-Speed is not supported\n"); + } + init_ed->ed_desc.sched_frame = (SCHEDULE_SLOT+oci_get_frame_num())&HFNUM_MAX_FRNUM; + ref_periodic_transfer++; + } + if(init_ed->ed_desc.endpoint_type==ISOCH_TRANSFER) { + u8 count = 0; + u8 cal_interval = 1; + + for(count = 0;count<(init_ed->ed_desc.interval-1);count++) + { + cal_interval *=2; + } + + init_ed->ed_desc.interval = cal_interval; + init_ed->ed_desc.sched_frame = (SCHEDULE_SLOT+oci_get_frame_num())&HFNUM_MAX_FRNUM; + ref_periodic_transfer++; + } + + //start to initiate struct ed_status.... + + //initiates PID + switch(ep_type) { + case BULK_TRANSFER: + case INT_TRANSFER: + init_ed->ed_status.data_tgl = DATA0; + break; + + case CONTROL_TRANSFER: + init_ed->ed_status.control_data_tgl.setup_tgl = SETUP; + init_ed->ed_status.control_data_tgl.data_tgl = DATA1; + init_ed->ed_status.control_data_tgl.status_tgl = DATA1; + break; + + case ISOCH_TRANSFER: + if(f_is_ep_in) { + switch(multi_count) { + case MULTI_COUNT_ZERO : + init_ed->ed_status.data_tgl = DATA0; + break; + case MULTI_COUNT_ONE : + init_ed->ed_status.data_tgl = DATA1; + break; + case MULTI_COUNT_TWO : + init_ed->ed_status.data_tgl = DATA2; + break; + default: + break; + } + } + else { + switch(multi_count) { + case MULTI_COUNT_ZERO : + init_ed->ed_status.data_tgl = DATA0; + break; + case MULTI_COUNT_ONE : + init_ed->ed_status.data_tgl = MDATA; + break; + case MULTI_COUNT_TWO : + init_ed->ed_status.data_tgl = MDATA; + break; + default: + break; + } + } + break; + default: + break; + } + + if(init_ed->ed_desc.endpoint_type == INT_TRANSFER || + init_ed->ed_desc.endpoint_type == ISOCH_TRANSFER) { + u32 usb_time = 0, byte_count = 0; + + //calculates the bytes to be transferred at one (uframe)frame. + byte_count = (init_ed->ed_desc.mc+1)*init_ed->ed_desc.max_packet_size; + + usb_time = (u32)otg_usbcore_get_calc_bustime(init_ed->ed_desc.dev_speed, + init_ed->ed_desc.is_ep_in, + (init_ed->ed_desc.endpoint_type==ISOCH_TRANSFER?true:false), + byte_count); + usb_time /= 1000; //convert nanosec unit to usec unit + + if(reserve_used_resource_for_periodic(usb_time, init_ed->ed_desc.dev_speed, init_ed->ed_desc.endpoint_type) != USB_ERR_SUCCESS) { + return USB_ERR_NOSPACE; + } + + init_ed->ed_status.is_alloc_resource_for_ed =true; + init_ed->ed_desc.used_bus_time =usb_time; + init_ed->ed_desc.mc =multi_count+1; + } + + init_ed->ed_status.is_in_transfer_ready_q =false; + init_ed->ed_status.is_in_transferring =false; + init_ed->ed_status.is_ping_enable =false; + init_ed->ed_status.in_transferring_td =0; + + //push the ed_t to ED_list. + otg_list_push_prev(&init_ed->ed_list_entry,&ed_list_head); + + if(ref_periodic_transfer) { + enable_sof(); + } + return USB_ERR_SUCCESS; +} + + +/******************************************************************************/ +/*! + * @name int create_td(td_t **new_td) + * + * @brief this function creates a new td_t and returns the td_t to Caller + * + * + * @param [OUT] new_td = returns the address of the new td_t . + * + * @return USB_ERR_SUCCESS -if successes to create the new td_t. + * USB_ERR_FAILl -if fails to create to new td_t. + */ +/******************************************************************************/ +int create_td(td_t **new_td) +{ + int err_code = USB_ERR_SUCCESS; + + err_code = otg_mem_alloc((void **)new_td,(u16)sizeof(td_t), USB_MEM_ASYNC); + otg_mem_set(*new_td, 0, sizeof(td_t)); + return err_code; +} + + +/******************************************************************************/ +/*! + * @name int init_td( td_t *init_td, + * ed_t *parent_ed, + * void *call_back_fun, + * void *call_back_param, + * u32 transfer_flag, + * bool f_is_standard_dev_req, + * u32 phy_setup, + * u32 vir_setup, + * u32 vir_buf_addr, + * u32 phy_buf_addr, + * u32 buf_size, + * u32 isoch_start_frame, + * isoch_packet_desc_t *isoch_packet_desc, + * u32 isoch_packet_num, + * void *td_priv) + * + * @brief this function initiates the init_td by using another parameter. + * + * + * @param [IN] init_td - indicate the td_t to be initiated. + * [IN] parent_ed - indicate the ed_t to manage this init_td + * [IN] call_back_func - indicate the call-back function of application. + * [IN] call_back_param - indicate the parameter of the call-back function. + * [IN] transfer_flag - indicate the transfer flag. + * [IN] f_is_standard_dev_req - indicates the issue transfer request is USB Standard Request + * [IN] phy_setup - the physical address of buffer to store the USB Standard Request. + * [IN] vir_setup - the virtual address of buffer to store the USB Standard Request. + * [IN] vir_buf_addr - the virtual address of buffer to store the data to be transferred or received. + * [IN] phy_buf_addr - the physical address of buffer to store the data to be transferred or received. + * [IN] buf_size - indicates the buffer size. + * [IN] isoch_start_frame - if this usb transfer is isochronous transfer + * , this indicates the start frame to start the usb transfer. + * [IN] isoch_packet_desc - if the usb transfer is isochronous transfer + * , this indicates the structure to describe the isochronous transfer. + * [IN] isoch_packet_num - if the usb transfer is isochronous transfer + * , this indicates the number of packet to consist of the usb transfer. + * [IN] td_priv - indicate the private data to be delivered from usb core of linux. + * td_priv stores the urb of linux. + * + * @return USB_ERR_SUCCESS -if successes to initiate the new td_t. + * USB_ERR_FAILl -if fails to create to new td_t. + */ +/******************************************************************************/ +int init_td( td_t *init_td, + ed_t *parent_ed, + void *call_back_fun, + void *call_back_param, + u32 transfer_flag, + bool f_is_standard_dev_req, + u32 phy_setup, + u32 vir_setup, + u32 vir_buf_addr, + u32 phy_buf_addr, + u32 buf_size, + u32 isoch_start_frame, + isoch_packet_desc_t *isoch_packet_desc, + u32 isoch_packet_num, + void *td_priv) +{ + if(f_is_standard_dev_req) { + if((phy_buf_addr>0) && (buf_size>0)) { + init_td->standard_dev_req_info.is_data_stage = true; + } + else { + init_td->standard_dev_req_info.is_data_stage = false; + } + init_td->standard_dev_req_info.conrol_transfer_stage = SETUP_STAGE; + init_td->standard_dev_req_info.phy_standard_dev_req_addr = phy_setup; + init_td->standard_dev_req_info.vir_standard_dev_req_addr = vir_setup; + } + + init_td->call_back_func_p = call_back_fun; + init_td->call_back_func_param_p = call_back_param; + init_td->error_code = USB_ERR_SUCCESS; + init_td->is_standard_dev_req = f_is_standard_dev_req; + init_td->is_transfer_done = false; + init_td->is_transferring = false; + init_td->td_private = td_priv; + init_td->err_cnt = 0; + init_td->parent_ed_p = parent_ed; + init_td->phy_buf_addr = phy_buf_addr; + init_td->vir_buf_addr = vir_buf_addr; + init_td->buf_size = buf_size; + init_td->isoch_packet_desc_p = isoch_packet_desc; + init_td->isoch_packet_num = isoch_packet_num; + init_td->isoch_packet_index = 0; + init_td->isoch_packet_position = 0; + init_td->sched_frame = isoch_start_frame; + init_td->used_total_bus_time = parent_ed->ed_desc.used_bus_time; + init_td->td_id = (u32)init_td; + init_td->transfer_flag = transfer_flag; + init_td->transferred_szie = 0; + + switch(parent_ed->ed_desc.endpoint_type) { + case CONTROL_TRANSFER: + init_nonperio_stransfer(true, init_td); + break; + + case BULK_TRANSFER: + init_nonperio_stransfer(false, init_td); + break; + + case INT_TRANSFER: + init_perio_stransfer(false, init_td); + break; + + case ISOCH_TRANSFER: + init_perio_stransfer(true, init_td); + break; + + default: + return USB_ERR_FAIL; + } + + //insert the td_t to parent_ed->td_list_entry. + otg_list_push_prev(&init_td->td_list_entry,&parent_ed->td_list_entry); + parent_ed->num_td++; + + return USB_ERR_SUCCESS; +} + +/******************************************************************************/ +/*! + * @name int issue_transfer(struct sec_otghost *otghost, + * ed_t *parent_ed, + * void *call_back_func, + * void *call_back_param, + * u32 transfer_flag, + * bool f_is_standard_dev_req, + * u32 setup_vir_addr, + * u32 setup_phy_addr, + * u32 vir_buf_addr, + * u32 phy_buf_addr, + * u32 buf_size, + * u32 start_frame, + * u32 isoch_packet_num, + * isoch_packet_desc_t *isoch_packet_desc, + * void *td_priv, + * unsigned int *return_td_addr) + * + * @brief this function start USB Transfer + * + * + * @param [IN] parent_ed - indicate the ed_t to manage this issue transfer. + * [IN] call_back_func - indicate the call-back function of application. + * [IN] call_back_param - indicate the parameter of the call-back function. + * [IN] transfer_flag - indicate the transfer flag. + * [IN] f_is_standard_dev_req - indicates the issue transfer request is USB Standard Request + * [IN] setup_vir_addr - the virtual address of buffer to store the USB Standard Request. + * [IN] setup_phy_addr - the physical address of buffer to store the USB Standard Request. + * [IN] vir_buf_addr - the virtual address of buffer to store the data to be transferred or received. + * [IN] phy_buf_addr - the physical address of buffer to store the data to be transferred or received. + * [IN] buf_size - indicates the buffer size. + * [IN] start_frame - if this usb transfer is isochronous transfer + * , this indicates the start frame to start the usb transfer. + * [IN] isoch_packet_num - if the usb transfer is isochronous transfer + * , this indicates the number of packet to consist of the usb transfer. + * [IN] isoch_packet_desc - if the usb transfer is isochronous transfer + * , this indicates the structure to describe the isochronous transfer. + * [IN] td_priv - indicate the private data to be delivered from usb core of linux. + * td_priv stores the urb of linux. + * [OUT] return_td_addr - indicates the variable address to store the new td_t for this transfer + * + * @return USB_ERR_SUCCESS -if successes to initiate the new td_t. + * USB_ERR_FAILl -if fails to create to new td_t. + */ +/******************************************************************************/ +int issue_transfer(struct sec_otghost *otghost, + ed_t *parent_ed, + void *call_back_func, + void *call_back_param, + u32 transfer_flag, + bool f_is_standard_dev_req, + u32 setup_vir_addr, + u32 setup_phy_addr, + u32 vir_buf_addr, + u32 phy_buf_addr, + u32 buf_size, + u32 start_frame, + u32 isoch_packet_num, + isoch_packet_desc_t *isoch_packet_desc, + void *td_priv, + unsigned int *return_td_addr) +{ + td_t *new_td_p = NULL; + + int err = USB_ERR_SUCCESS; + if(create_td(&new_td_p)==USB_ERR_SUCCESS) { + err = init_td( new_td_p, + parent_ed, + call_back_func, + call_back_param, + transfer_flag, + f_is_standard_dev_req, + setup_phy_addr, + setup_vir_addr, + vir_buf_addr, + phy_buf_addr, + buf_size, + start_frame, + isoch_packet_desc, + isoch_packet_num, + td_priv); + + if(err !=USB_ERR_SUCCESS) { + return USB_ERR_NOMEM; + } + + if(parent_ed->is_need_to_insert_scheduler) { + insert_ed_to_scheduler(otghost, parent_ed); + } + + *return_td_addr = (u32)new_td_p; + + return USB_ERR_SUCCESS; + } + else { + return USB_ERR_NOMEM; + } +} + +/******************************************************************************/ +/*! + * @name int cancel_transfer(struct sec_otghost *otghost, + * ed_t *parent_ed, + * td_t *cancel_td) + * + * @brief this function cancels to transfer USB Transfer of cancel_td. + * this function firstly check whether this cancel_td is transferring or not + * if the cancel_td is transferring, the this function requests to cancel the USB Transfer + * to S3CScheduler. if the parent_ed is for Periodic Transfer, and + * there is not any td_t at parent_ed, then this function requests to release + * some usb resources for the ed_t to S3CScheduler. finally this function deletes the cancel_td. + * + * @param [IN] pUpdateTD = indicates the pointer ot the td_t to have STransfer to be updated. + * + * @return USB_ERR_SUCCESS - if success to update the STranfer of pUpdateTD. + * USB_ERR_FAIL - if fail to update the STranfer of pUpdateTD. + */ + /******************************************************************************/ +int cancel_transfer(struct sec_otghost *otghost, + ed_t *parent_ed, + td_t *cancel_td) +{ + int err = USB_ERR_DEQUEUED; + otg_list_head *tmp_list_p, *tmp_list2_p; + bool cond_found = false; + + if(parent_ed == NULL || cancel_td == NULL) { + otg_dbg(OTG_DBG_TRANSFER, "parent_ed == NULL || cancel_td == NULL\n"); + cancel_td->error_code = USB_ERR_NOELEMENT; + otg_usbcore_giveback(cancel_td); + return cancel_td->error_code; + } + + otg_list_for_each_safe(tmp_list_p, tmp_list2_p, &parent_ed->td_list_entry) { + if(&cancel_td->td_list_entry == tmp_list_p) { + cond_found = true; + break; + } + } + + if (cond_found != true) { + otg_dbg(OTG_DBG_TRANSFER, "cond_found != true \n"); + cancel_td->error_code = USB_ERR_NOELEMENT; + otg_usbcore_giveback(cancel_td); + return cancel_td->error_code; + } + + + if(cancel_td->is_transferring) { + if(!parent_ed->ed_status.is_in_transfer_ready_q) { + err = cancel_to_transfer_td(otghost, cancel_td); + + parent_ed->ed_status.in_transferring_td = 0; + + if(err != USB_ERR_SUCCESS) { + otg_dbg(OTG_DBG_TRANSFER, "cancel_to_transfer_td \n"); + cancel_td->error_code = err; + otg_usbcore_giveback(cancel_td); + goto ErrorStatus; + } + + } + // kevinh - even if the record was in the ready queue it is important to delete it as well. We can also always remove the ed from the scheduler + // once all tds have been removed + otg_list_pop(&cancel_td->td_list_entry); + parent_ed->num_td--; + } + else { + otg_list_pop(&cancel_td->td_list_entry); + parent_ed->num_td--; + + if(parent_ed->num_td==0) { + remove_ed_from_scheduler(parent_ed); + } + } + + if(parent_ed->num_td) { + // kevinh - we do not want to force insert_scheduler, because if this endpoint _was_ already scheduled + // because the deleted td was not the active td then we will now put ed into the scheduler list twice, thus + // corrupting it. + // parent_ed->is_need_to_insert_scheduler = true; + insert_ed_to_scheduler(otghost, parent_ed); + } + else { + if(parent_ed->ed_desc.endpoint_type == INT_TRANSFER || + parent_ed->ed_desc.endpoint_type == ISOCH_TRANSFER) { + //Release channel and usb bus resource for this ed_t. + //but, not release memory for this ed_t. + free_usb_resource_for_periodic(parent_ed->ed_desc.used_bus_time, + cancel_td->cur_stransfer.alloc_chnum, + cancel_td->parent_ed_p->ed_desc.endpoint_type); + + parent_ed->ed_status.is_alloc_resource_for_ed =false; + } + } + // the caller of this functions should call otg_usbcore_giveback(cancel_td); + cancel_td->error_code = USB_ERR_DEQUEUED; + // kevinh - fixed bug, the caller should take care of calling delete_td because they might still want to do some + // operations on that memory + // delete_td(cancel_td); + // otg_usbcore_giveback(cancel_td); + +ErrorStatus: + + return err; +} + + +/******************************************************************************/ +/*! + * @name int cancel_all_td(struct sec_otghost *otghost, ed_t *parent_ed) + * + * @brief this function cancels all Transfer which parent_ed manages. + * + * @param [IN] parent_ed = indicates the pointer ot the ed_t to manage TD_ts to be canceled. + * + * @return USB_ERR_SUCCESS - if success to cancel all TD_ts of pParentsED. + * USB_ERR_FAIL - if fail to cancel all TD_ts of pParentsED. + */ + /******************************************************************************/ +int cancel_all_td(struct sec_otghost *otghost, ed_t *parent_ed) +{ + otg_list_head *cancel_td_list_entry; + td_t *cancel_td; + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "cancel_all_td \n"); + do { + cancel_td_list_entry = parent_ed->td_list_entry.next; + + cancel_td = otg_list_get_node(cancel_td_list_entry,td_t, td_list_entry); + + if(cancel_transfer(otghost, parent_ed, cancel_td) == USB_ERR_DEQUEUED) + // kevinh FIXME - do we also need to giveback? + delete_td(otghost,cancel_td); + } while(parent_ed->num_td); + + return USB_ERR_SUCCESS; +} diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transfer-nonperiodic.c b/drivers/usb/host/s3c-otg/s3c-otg-transfer-nonperiodic.c new file mode 100644 index 0000000..013a2a2 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transfer-nonperiodic.c @@ -0,0 +1,140 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : NonPeriodicTransfer.c + * [Description] : This source file implements the functions to be defined at NonPeriodicTransfer Module. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/07 + * [Revision History] + * (1) 2008/06/07 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and implements some functions of NonPeriodicTransfer. + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-transfer-transfer.h" + +/******************************************************************************/ +/*! + * @name int init_nonperio_stransfer( bool f_is_standard_dev_req, + * td_t *parent_td) + * + * @brief this function initiates the parent_td->cur_stransfer for NonPeriodic Transfer and + * inserts this init_td_p to init_td_p->parent_ed_p. + * + * @param [IN] f_is_standard_dev_req = indicates whether this transfer is Control or not. + * [IN] parent_td = indicates the address of td_t to be initiated. + * + * @return USB_ERR_SUCCESS - if success to update the STranfer of pUpdateTD. + * USB_ERR_FAIL - if fail to update the STranfer of pUpdateTD. + */ + /******************************************************************************/ +int init_nonperio_stransfer(bool f_is_standard_dev_req, + td_t *parent_td) +{ + + + parent_td->cur_stransfer.ed_desc_p = &parent_td->parent_ed_p->ed_desc; + parent_td->cur_stransfer.ed_status_p = &parent_td->parent_ed_p->ed_status; + parent_td->cur_stransfer.alloc_chnum = CH_NONE; + parent_td->cur_stransfer.parent_td = (u32)parent_td; + parent_td->cur_stransfer.stransfer_id = (u32)&parent_td->cur_stransfer; + + otg_mem_set(&(parent_td->cur_stransfer.hc_reg), 0, sizeof(hc_reg_t)); + + parent_td->cur_stransfer.hc_reg.hc_int_msk.b.chhltd = 1; + + if(f_is_standard_dev_req) + { + parent_td->cur_stransfer.buf_size = USB_20_STAND_DEV_REQUEST_SIZE; + parent_td->cur_stransfer.start_phy_buf_addr = parent_td->standard_dev_req_info.phy_standard_dev_req_addr; + parent_td->cur_stransfer.start_vir_buf_addr = parent_td->standard_dev_req_info.vir_standard_dev_req_addr; + } + else + { + parent_td->cur_stransfer.buf_size = (parent_td->buf_size>MAX_CH_TRANSFER_SIZE) + ?MAX_CH_TRANSFER_SIZE + :parent_td->buf_size; + + parent_td->cur_stransfer.start_phy_buf_addr = parent_td->phy_buf_addr; + parent_td->cur_stransfer.start_vir_buf_addr = parent_td->vir_buf_addr; + } + + parent_td->cur_stransfer.packet_cnt = calc_packet_cnt(parent_td->cur_stransfer.buf_size + , parent_td->parent_ed_p->ed_desc.max_packet_size); + + return USB_ERR_SUCCESS; +} + + +/******************************************************************************/ +/*! + * @name void update_nonperio_stransfer(td_t *parent_td) + * + * @brief this function updates the parent_td->cur_stransfer to be used by S3COCI. + * + * @param [IN/OUT]parent_td = indicates the pointer of td_t to store the STranser to be updated. + * + * @return USB_ERR_SUCCESS -if success to update the parent_td->cur_stransfer. + * USB_ERR_FAIL -if fail to update the parent_td->cur_stransfer. + */ + /******************************************************************************/ +void update_nonperio_stransfer(td_t *parent_td) +{ + switch(parent_td->parent_ed_p->ed_desc.endpoint_type) { + case BULK_TRANSFER: + parent_td->cur_stransfer.start_phy_buf_addr = parent_td->phy_buf_addr+parent_td->transferred_szie; + parent_td->cur_stransfer.start_vir_buf_addr = parent_td->vir_buf_addr+parent_td->transferred_szie; + parent_td->cur_stransfer.buf_size = ((parent_td->buf_size - parent_td->transferred_szie)>MAX_CH_TRANSFER_SIZE) + ?MAX_CH_TRANSFER_SIZE + :parent_td->buf_size - parent_td->transferred_szie; + break; + + case CONTROL_TRANSFER: + if(parent_td->standard_dev_req_info.conrol_transfer_stage == SETUP_STAGE) + { + // but, this case will not be occured...... + parent_td->cur_stransfer.start_phy_buf_addr = parent_td->standard_dev_req_info.phy_standard_dev_req_addr; + parent_td->cur_stransfer.start_vir_buf_addr = parent_td->standard_dev_req_info.vir_standard_dev_req_addr; + parent_td->cur_stransfer.buf_size = 8; + } + else if(parent_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE) + { + parent_td->cur_stransfer.start_phy_buf_addr = parent_td->phy_buf_addr+parent_td->transferred_szie; + parent_td->cur_stransfer.start_vir_buf_addr = parent_td->vir_buf_addr+parent_td->transferred_szie; + parent_td->cur_stransfer.buf_size = ((parent_td->buf_size - parent_td->transferred_szie)>MAX_CH_TRANSFER_SIZE) + ?MAX_CH_TRANSFER_SIZE + :parent_td->buf_size - parent_td->transferred_szie; + } + else + { + parent_td->cur_stransfer.start_phy_buf_addr = 0; + parent_td->cur_stransfer.start_vir_buf_addr = 0; + parent_td->cur_stransfer.buf_size = 0; + } + break; + default: + break; + } + + parent_td->cur_stransfer.packet_cnt = calc_packet_cnt(parent_td->cur_stransfer.buf_size, parent_td->parent_ed_p->ed_desc.max_packet_size); + +} diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transfer-periodic.c b/drivers/usb/host/s3c-otg/s3c-otg-transfer-periodic.c new file mode 100644 index 0000000..f254b78 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transfer-periodic.c @@ -0,0 +1,128 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : NonPeriodicTransfer.c + * [Description] : This source file implements the functions to be defined at NonPeriodicTransfer Module. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/09 + * [Revision History] + * (1) 2008/06/09 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and implements some functions of PeriodicTransfer. + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-transfer-transfer.h" + + +/******************************************************************************/ +/*! + * @name int init_perio_stransfer( bool f_is_isoch_transfer, + * td_t *parent_td) + * + * @brief this function initiates the parent_td->cur_stransfer for Periodic Transfer and + * inserts this init_td_p to init_td_p->parent_ed_p. + * + * @param [IN] f_is_isoch_transfer = indicates whether this transfer is Isochronous or not. + * [IN] parent_td = indicates the address of td_t to be initiated. + * + * @return USB_ERR_SUCCESS -if success to update the STranfer of pUpdateTD. + * USB_ERR_FAIL -if fail to update the STranfer of pUpdateTD. + */ + /******************************************************************************/ +int init_perio_stransfer( bool f_is_isoch_transfer, + td_t *parent_td) +{ + parent_td->cur_stransfer.ed_desc_p = &parent_td->parent_ed_p->ed_desc; + parent_td->cur_stransfer.ed_status_p = &parent_td->parent_ed_p->ed_status; + parent_td->cur_stransfer.alloc_chnum = CH_NONE; + parent_td->cur_stransfer.parent_td = (u32)parent_td; + parent_td->cur_stransfer.stransfer_id = (u32)&parent_td->cur_stransfer; + + otg_mem_set(&parent_td->cur_stransfer.hc_reg, 0, sizeof(hc_reg_t)); + + parent_td->cur_stransfer.hc_reg.hc_int_msk.b.chhltd = 1; + + if(f_is_isoch_transfer) + { + // initiates the STransfer usinb the IsochPacketDesc[0]. + parent_td->cur_stransfer.buf_size =parent_td->isoch_packet_desc_p[0].buf_size; + parent_td->cur_stransfer.start_phy_buf_addr =parent_td->phy_buf_addr + parent_td->isoch_packet_desc_p[0].isoch_packiet_start_addr; + parent_td->cur_stransfer.start_vir_buf_addr =parent_td->vir_buf_addr + parent_td->isoch_packet_desc_p[0].isoch_packiet_start_addr; + } + else + { + parent_td->cur_stransfer.buf_size =(parent_td->buf_size>MAX_CH_TRANSFER_SIZE) + ?MAX_CH_TRANSFER_SIZE + :parent_td->buf_size; + + parent_td->cur_stransfer.start_phy_buf_addr =parent_td->phy_buf_addr; + parent_td->cur_stransfer.start_vir_buf_addr =parent_td->vir_buf_addr; + } + + parent_td->cur_stransfer.packet_cnt = calc_packet_cnt(parent_td->cur_stransfer.buf_size, parent_td->parent_ed_p->ed_desc.max_packet_size); + + return USB_ERR_SUCCESS; +} + + +/******************************************************************************/ +/*! + * @name void update_perio_stransfer(td_t *parent_td) + * + * @brief this function updates the parent_td->cur_stransfer to be used by S3COCI. + * the STransfer of parent_td is for Periodic Transfer. + * + * @param [IN/OUT]parent_td = indicates the pointer of td_t to store the STranser to be updated. + * + * @return USB_ERR_SUCCESS -if success to update the parent_td->cur_stransfer. + * USB_ERR_FAIL -if fail to update the parent_td->cur_stransfer. + */ + /******************************************************************************/ +void update_perio_stransfer(td_t *parent_td) +{ + switch(parent_td->parent_ed_p->ed_desc.endpoint_type) { + case INT_TRANSFER: + parent_td->cur_stransfer.start_phy_buf_addr = parent_td->phy_buf_addr+parent_td->transferred_szie; + parent_td->cur_stransfer.start_vir_buf_addr = parent_td->vir_buf_addr+parent_td->transferred_szie; + parent_td->cur_stransfer.buf_size = (parent_td->buf_size>MAX_CH_TRANSFER_SIZE) + ?MAX_CH_TRANSFER_SIZE :parent_td->buf_size; + break; + + case ISOCH_TRANSFER: + parent_td->cur_stransfer.start_phy_buf_addr = parent_td->phy_buf_addr + +parent_td->isoch_packet_desc_p[parent_td->isoch_packet_index].isoch_packiet_start_addr + +parent_td->isoch_packet_position; + + parent_td->cur_stransfer.start_vir_buf_addr = parent_td->vir_buf_addr + +parent_td->isoch_packet_desc_p[parent_td->isoch_packet_index].isoch_packiet_start_addr + +parent_td->isoch_packet_position; + + parent_td->cur_stransfer.buf_size = (parent_td->isoch_packet_desc_p[parent_td->isoch_packet_index].buf_size - parent_td->isoch_packet_position)>MAX_CH_TRANSFER_SIZE + ?MAX_CH_TRANSFER_SIZE + :parent_td->isoch_packet_desc_p[parent_td->isoch_packet_index].buf_size - parent_td->isoch_packet_position; + + break; + + default: + break; + } +} + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transfer-transfer.h b/drivers/usb/host/s3c-otg/s3c-otg-transfer-transfer.h new file mode 100644 index 0000000..6e1532c --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transfer-transfer.h @@ -0,0 +1,144 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : s3c-otg-transfer-transfer.h + * [Description] : The Header file defines the external and internal functions of Transfer. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/03 + * [Revision History] + * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and defines functions of Transfer + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) \n + * : Optimizing for performance \n + * + ****************************************************************************/ +/**************************************************************************** + * 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 _TRANSFER_H +#define _TRANSFER_H + +/* +// ---------------------------------------------------------------------------- +// Include files : None. +// ---------------------------------------------------------------------------- +*/ +//#include "s3c-otg-common-const.h" +#include "s3c-otg-common-errorcode.h" +#include "s3c-otg-common-datastruct.h" +#include "s3c-otg-hcdi-memory.h" +#include "s3c-otg-hcdi-kal.h" +#include "s3c-otg-hcdi-debug.h" + +#include "s3c-otg-scheduler-scheduler.h" +#include "s3c-otg-isr.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + +void init_transfer(void); +void deinit_transfer(struct sec_otghost *otghost); + +int issue_transfer(struct sec_otghost *otghost, + ed_t *parent_ed, + void *call_back_func, + void *call_back_param, + u32 transfer_flag, + bool f_is_standard_dev_req, + u32 setup_vir_addr, + u32 setup_phy_addr, + u32 vir_buf_addr, + u32 phy_buf_addr, + u32 buf_size, + u32 start_frame, + u32 isoch_packet_num, + isoch_packet_desc_t *isoch_packet_desc, + void *td_priv, + unsigned int *return_td_addr); + +int cancel_transfer(struct sec_otghost *otghost, + ed_t *parent_ed, + td_t *cancel_td); + +int cancel_all_td(struct sec_otghost *otghost, ed_t *parent_ed); + +int create_ed(ed_t ** new_ed); + +int init_ed(ed_t * init_ed, + u8 dev_addr, + u8 ep_num, + bool f_is_ep_in, + u8 dev_speed, + u8 ep_type, + u16 max_packet_size, + u8 multi_count, + u8 interval, + u32 sched_frame, + u8 hub_addr, + u8 hub_port, + bool f_is_do_split, + void *ep); + +int delete_ed(struct sec_otghost *otghost, ed_t *delete_ed); + +int delete_td(struct sec_otghost *otghost, td_t * delete_td); + +int create_isoch_packet_desc( isoch_packet_desc_t **new_isoch_packet_desc, + u32 isoch_packet_num); + +int delete_isoch_packet_desc( isoch_packet_desc_t *del_isoch_packet_desc, + u32 isoch_packet_num); + + +void init_isoch_packet_desc( isoch_packet_desc_t *init_isoch_packet_desc, + u32 offset, + u32 isoch_packet_size, + u32 index); + +// NonPeriodicTransfer.c implements. + +int init_nonperio_stransfer(bool f_is_standard_dev_req, + td_t *parent_td); + +void update_nonperio_stransfer(td_t *parent_td); + +//PeriodicTransfer.c implements + +int init_perio_stransfer( bool f_is_isoch_transfer, + td_t *parent_td); + +void update_perio_stransfer(td_t *parent_td); + +static inline u32 calc_packet_cnt(u32 data_size, u16 max_packet_size) +{ + if(data_size != 0) + { + return (data_size%max_packet_size==0)?data_size/max_packet_size:data_size/max_packet_size+1; + } + return 1; +} + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.c b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.c new file mode 100644 index 0000000..e544f5c --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.c @@ -0,0 +1,720 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : BulkTransferChecker.c + * [Description] : The Source file implements the external and internal functions of BulkTransferChecker. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/13 + * [Revision History] + * (1) 2008/06/18 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and implements functions of BulkTransferChecker + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-transferchecker-bulk.h" +#include "s3c-otg-isr.h" + +/******************************************************************************/ +/*! + * @name u8 process_bulk_transfer(td_t *result_td, + hc_info_t *hc_reg_data) + + * + * @brief this function processes the result of the Bulk Transfer. + * firstly, this function checks the result of the Bulk Transfer. + * and according to the result, calls the sub-functions to process the result. + * + * + * @param [IN] result_td -indicates the pointer of the td_t whose channel is interruped. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_TRANSMIT -if need to retransmit the result_td. + * RE_SCHEDULE -if need to reschedule the result_td. + * DE_ALLOCATE -if USB Transfer is completed. + * NO_ACTION -if we don't need any action, + */ +/******************************************************************************/ +u8 process_bulk_transfer(td_t *result_td, + hc_info_t *hc_reg_data) +{ + hcintn_t hc_intr_info; + u8 return_val=0; + + //we just deal with the interrupts to be unmasked. + hc_intr_info.d32 = hc_reg_data->hc_int.d32 & result_td->cur_stransfer.hc_reg.hc_int_msk.d32; + + if(result_td->parent_ed_p->ed_desc.is_ep_in) + { + if(hc_intr_info.b.chhltd) + { + return_val = process_chhltd_on_bulk(result_td, hc_reg_data); + } + + else if (hc_intr_info.b.ack) + { + return_val =process_ack_on_bulk(result_td, hc_reg_data); + } + + else if (hc_intr_info.b.nak) + { + return_val =process_nak_on_bulk(result_td, hc_reg_data); + } + + else if (hc_intr_info.b.datatglerr) + { + return_val = process_datatgl_on_bulk(result_td,hc_reg_data); + } + } + else + { + if(hc_intr_info.b.chhltd) + { + return_val = process_chhltd_on_bulk(result_td, hc_reg_data); + + } + + else if(hc_intr_info.b.ack) + { + return_val =process_ack_on_bulk( result_td, hc_reg_data); + } + } + + return return_val; +} + +/******************************************************************************/ +/*! + * @name u8 process_chhltd_on_bulk(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function processes Channel Halt event according to Synopsys OTG Spec. + * firstly, this function checks the reason of the Channel Halt, and according to the reason, + * calls the sub-functions to process the result. + * + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_TRANSMIT -if need to retransmit the result_td. + * RE_SCHEDULE -if need to reschedule the result_td. + * DE_ALLOCATE -if USB Transfer is completed. + */ +/******************************************************************************/ +u8 process_chhltd_on_bulk(td_t *result_td, + hc_info_t *hc_reg_data) +{ +#if 0 + if(result_td->parent_ed_p->ed_desc.is_ep_in) + { + if(hc_reg_data->hc_int.b.xfercompl) + { + return process_xfercompl_on_bulk( result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.stall) + { + return process_stall_on_bulk(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.bblerr) + { + return process_bblerr_on_bulk(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.xacterr) + { + return process_xacterr_on_bulk(result_td, hc_reg_data); + } + else + { + //Occure Error State..... + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->err_cnt++; + if(result_td->err_cnt == 3) + { + result_td->error_code = USB_ERR_STATUS_XACTERR; + result_td->err_cnt = 0; + return DE_ALLOCATE; + } + + return RE_TRANSMIT; + } + } + else + { + if(hc_reg_data->hc_int.b.xfercompl) + { + return process_xfercompl_on_bulk(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.stall) + { + return process_stall_on_bulk(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.xacterr) + { + return process_xacterr_on_bulk(result_td, hc_reg_data); + } + + else if(hc_reg_data->hc_int.b.nak) + { + return process_nak_on_bulk(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.nyet) + { + return process_nyet_on_bulk(result_td, hc_reg_data); + } + else + { + //occur error state... + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->err_cnt++; + if(result_td->err_cnt == 3) + { + result_td->error_code = USB_ERR_STATUS_XACTERR; + result_td->err_cnt = 0; + return DE_ALLOCATE; + } + + return RE_TRANSMIT; + } + + + + } +#else + if(hc_reg_data->hc_int.b.xfercompl) + { + return process_xfercompl_on_bulk( result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.stall) + { + return process_stall_on_bulk(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.bblerr) + { + return process_bblerr_on_bulk(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.xacterr) + { + return process_xacterr_on_bulk(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.nak) + { + return process_nak_on_bulk(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.nyet) + { + return process_nyet_on_bulk(result_td, hc_reg_data); + } + else + { + //occur error state... + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->err_cnt++; + if(result_td->err_cnt == 3) + { + result_td->error_code = USB_ERR_STATUS_XACTERR; + result_td->err_cnt = 0; + return DE_ALLOCATE; + } + + return RE_TRANSMIT; + } +#endif + return USB_ERR_SUCCESS; + +} + +/******************************************************************************/ +/*! + * @name u8 process_xfercompl_on_bulk( td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the xfercompl event according to Synopsys OTG Spec. + * the procedure of this function is as following + * 1. clears all bits of the channel' HCINT by using clear_ch_intr() of S3CIsr. + * 2. masks some bit of HCINTMSK + * 3. updates the result_td fields + * err_cnt/u8/standard_dev_req_info. + * 4. updates the result_td->parent_ed_p->ed_status. + * BulkDataTgl. + * 5. calculates the tranferred size by calling calc_transferred_size() on DATA_STAGE. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return USB_ERR_SUCCESS + */ +/******************************************************************************/ +u8 process_xfercompl_on_bulk(td_t *result_td, + hc_info_t *hc_reg_data) +{ + u8 ret_val=0; + + result_td->err_cnt = 0; + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->parent_ed_p->ed_status.is_ping_enable =false; + + result_td->transferred_szie += calc_transferred_size(true,result_td, hc_reg_data); + + if(result_td->transferred_szie==result_td->buf_size) + {//at IN Transfer, short transfer is accepted. + result_td->error_code = USB_ERR_STATUS_COMPLETE; + ret_val = DE_ALLOCATE; + } + else + { + if(result_td->parent_ed_p->ed_desc.is_ep_in&& hc_reg_data->hc_size.b.xfersize) + { + if(result_td->transfer_flag&USB_TRANS_FLAG_NOT_SHORT) + { + result_td->error_code =USB_ERR_STATUS_SHORTREAD; + } + else + { + result_td->error_code =USB_ERR_STATUS_COMPLETE; + } + ret_val = DE_ALLOCATE; + } + else + { + ret_val = RE_SCHEDULE; + } + } + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + + if(hc_reg_data->hc_int.b.nyet) + { + //at OUT Transfer, we must re-transmit. + if(result_td->parent_ed_p->ed_desc.is_ep_in==false) + { + + if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG) + { + result_td->parent_ed_p->ed_status.is_ping_enable = true; + } + else + { + result_td->parent_ed_p->ed_status.is_ping_enable = false; + } + } + } + + return ret_val; + +} + +/******************************************************************************/ +/*! + * @name u8 process_ahb_on_bulk(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with theAHB Errorl event according to Synopsys OTG Spec. + * this function stop the channel to be executed + * TBD.... + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return USB_ERR_SUCCESS + */ +/******************************************************************************/ +u8 process_ahb_on_bulk(td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt =0; + result_td->error_code =USB_ERR_STATUS_AHBERR; + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_AHBErr); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + // we just calculate the size of the transferred data on Data Stage of Bulk Transfer. + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + result_td->parent_ed_p->ed_status.is_ping_enable = false; + + return DE_ALLOCATE; + +} + +/******************************************************************************/ +/*! + * @name u8 process_stall_on_bulk(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with theStall event according to Synopsys OTG Spec. + * when Stall is occured at Bulk Transfer, we should reset the PID as DATA0 + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return DE_ALLOCATE + */ +/******************************************************************************/ +u8 process_stall_on_bulk( td_t * result_td, + hc_info_t * hc_reg_data) +{ + result_td->err_cnt =0; + result_td->error_code =USB_ERR_STATUS_STALL; + + //this channel is stalled, So we don't process another interrupts. + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->parent_ed_p->ed_status.is_ping_enable = false; + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + + update_datatgl(DATA0, result_td); + + + return DE_ALLOCATE; +} + +/******************************************************************************/ +/*! + * @name u8 process_nak_on_bulk(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the nak event according to Synopsys OTG Spec. + * nak is occured at OUT/IN Transaction of Data/Status Stage, and is not occured at Setup Stage. + * If nak is occured at IN Transaction, this function processes this interrupt as following. + * 1. resets the result_td->err_cnt. + * 2. masks ack/nak/DaaTglErr bit of HCINTMSK. + * 3. clears the nak bit of HCINT + * 4. be careful, nak of IN Transaction don't require re-transmit. + * If nak is occured at OUT Transaction, this function processes this interrupt as following. + * 1. all procedures of IN Transaction are executed. + * 2. calculates the size of the transferred data. + * 3. if the speed of USB Device is High-Speed, sets the ping protocol. + * 4. update the Toggle + * at OUT Transaction, this function check whether the speed of USB Device is High-Speed or not. + * if USB Device is High-Speed, then + * this function sets the ping protocol. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_SCHEDULE -if the direction of the Transfer is OUT + * NO_ACTION -if the direction of the Transfer is IN + */ +/******************************************************************************/ +u8 process_nak_on_bulk(td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt = 0; + + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + + //at OUT Transfer, we must re-transmit. + if(result_td->parent_ed_p->ed_desc.is_ep_in==false) + { + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + + if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG) + { + result_td->parent_ed_p->ed_status.is_ping_enable = true; + } + else + { + result_td->parent_ed_p->ed_status.is_ping_enable = false; + } + + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + + return RE_TRANSMIT; + } + return NO_ACTION; + +} + +/******************************************************************************/ +/*! + * @name u8 process_ack_on_bulk(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the ack event according to Synopsys OTG Spec. + * ack of IN/OUT Transaction don't need any retransmit. + * this function just resets result_td->err_cnt and masks ack/nak/DataTgl of HCINTMSK. + * finally, this function clears ack bit of HCINT and ed_status.is_ping_enable. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return USB_ERR_SUCCESS + */ +/******************************************************************************/ +u8 process_ack_on_bulk(td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt = 0; + + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + + result_td->parent_ed_p->ed_status.is_ping_enable = false; + + return NO_ACTION; + +} + +/******************************************************************************/ +/*! + * @name u8 process_nyet_on_bulk(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the nyet event according to Synopsys OTG Spec. + * nyet is only occured at OUT Transaction. + * If nyet is occured at OUT Transaction, this function processes this interrupt as following. + * 1. resets the result_td->err_cnt. + * 2. masks ack/nak/datatglerr bit of HCINTMSK. + * 3. clears the nyet bit of HCINT + * 4. calculates the size of the transferred data. + * 5. if the speed of USB Device is High-Speed, sets the ping protocol. + * 6. update the Data Toggle. + * 7. return RE_SCHEDULE to retransmit. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_SCHEDULE + */ +/******************************************************************************/ +u8 process_nyet_on_bulk(td_t *result_td, + hc_info_t *hc_reg_data) +{ + if(result_td->parent_ed_p->ed_desc.is_ep_in) + { + // Error State.... + return NO_ACTION; + } + + result_td->err_cnt = 0; + + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NYET); + + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + + if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG) + { + result_td->parent_ed_p->ed_status.is_ping_enable = true; + } + else + { + result_td->parent_ed_p->ed_status.is_ping_enable = false; + } + + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + + return RE_TRANSMIT; + +} + +/******************************************************************************/ +/*! + * @name u8 process_xacterr_on_bulk( td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the xacterr event according to Synopsys OTG Spec. + * xacterr is occured at OUT/IN Transaction and we should retransmit the USB Transfer + * if the Error Counter is less than the RETRANSMIT_THRESHOLD. + * the reasons of xacterr is Timeout/CRC error/false EOP. + * the procedure to process xacterr is as following. + * 1. increses the result_td->err_cnt + * 2. check whether the result_td->err_cnt is equal to 3. + * 2. unmasks ack/nak/datatglerr bit of HCINTMSK. + * 3. clears the xacterr bit of HCINT + * 4. calculates the size of the transferred data. + * 5. if the speed of USB Device is High-Speed, sets the ping protocol. + * 6. update the Data Toggle. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_TRANSMIT -if the error count is less than 3 + * DE_ALLOCATE -if the error count is equal to 3 + */ +/******************************************************************************/ +u8 process_xacterr_on_bulk( td_t *result_td, + hc_info_t *hc_reg_data) +{ + u8 ret_val = 0; + + if(result_td->err_cnt<RETRANSMIT_THRESHOLD) + { + result_td->cur_stransfer.hc_reg.hc_int_msk.d32 |= (CH_STATUS_ACK+CH_STATUS_NAK+CH_STATUS_DataTglErr); + ret_val = RE_TRANSMIT; + result_td->err_cnt++ ; + } + else + { + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + ret_val = DE_ALLOCATE; + result_td->err_cnt = 0 ; + result_td->error_code = USB_ERR_STATUS_XACTERR; + } + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + + if(result_td->parent_ed_p->ed_desc.is_ep_in==false) + { + if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG) + { + result_td->parent_ed_p->ed_status.is_ping_enable = true; + } + else + { + result_td->parent_ed_p->ed_status.is_ping_enable = false; + } + } + + + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + + return ret_val; + +} + +/******************************************************************************/ +/*! + * @name void process_bblerr_on_bulk(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the Babble event according to Synopsys OTG Spec. + * babble error is occured when the buffer to receive data to be transmit is overflow. + * So, babble error can be just occured at IN Transaction. + * when Babble Error is occured, we should stop the USB Transfer, and return the fact + * to Application. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return DE_ALLOCATE + */ +/******************************************************************************/ +u8 process_bblerr_on_bulk(td_t *result_td, + hc_info_t *hc_reg_data) +{ + + if(!result_td->parent_ed_p->ed_desc.is_ep_in) + { + return NO_ACTION; + } + + result_td->err_cnt =0; + result_td->error_code =USB_ERR_STATUS_BBLERR; + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->parent_ed_p->ed_status.is_ping_enable =false; + result_td->transferred_szie += calc_transferred_size(false, result_td, hc_reg_data); + + return DE_ALLOCATE; + + +} + +/******************************************************************************/ +/*! + * @name u8 process_datatgl_on_bulk( td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the datatglerr event according to Synopsys OTG Spec. + * the datatglerr event is occured at IN Transfer, and the channel is not halted. + * this function just resets result_td->err_cnt and masks ack/nak/DataTgl of HCINTMSK. + * finally, this function clears datatglerr bit of HCINT. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return NO_ACTION + */ +/******************************************************************************/ +u8 process_datatgl_on_bulk(td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt = 0; + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + return NO_ACTION; + +} + + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.h b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.h new file mode 100644 index 0000000..e1f84ca --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-bulk.h @@ -0,0 +1,88 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : BulkTransferChecker.h + * [Description] : The Header file defines the external and internal functions of BulkTransferChecker + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/18 + * [Revision History] + * (1) 2008/06/18 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and defines functions of BulkTransferChecker + * + ****************************************************************************/ +/**************************************************************************** + * 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 _BULK_TRANSFER_CHECKER_H +#define _BULK_TRANSFER_CHECKER_H + +/* +// ---------------------------------------------------------------------------- +// Include files : None. +// ---------------------------------------------------------------------------- +*/ +//#include "s3c-otg-common-typedef.h" +#include "s3c-otg-common-const.h" +#include "s3c-otg-common-errorcode.h" +#include "s3c-otg-common-datastruct.h" + + + +#ifdef __cplusplus +extern "C" +{ +#endif +u8 process_bulk_transfer(td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_xfercompl_on_bulk(td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_chhltd_on_bulk(td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_ahb_on_bulk(td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_stall_on_bulk(td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_nak_on_bulk(td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_ack_on_bulk(td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_nyet_on_bulk(td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_xacterr_on_bulk(td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_bblerr_on_bulk(td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_datatgl_on_bulk(td_t *raw_td, + hc_info_t *hc_reg_data); + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-checker.h b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-checker.h new file mode 100644 index 0000000..cefbcaf --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-checker.h @@ -0,0 +1,66 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : DoneTransferChecker.h + * [Description] : The Header file defines the external and internal functions of S3CDoneTransferChecker. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/12 + * [Revision History] + * (1) 2008/06/12 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and defines functions of S3CDoneTransferChecker + * + ****************************************************************************/ +/**************************************************************************** + * 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 _DONE_TRANSFER_CHECKER_H +#define _DONE_TRANSFER_CHECKER_H + +/* +// ---------------------------------------------------------------------------- +// Include files : None. +// ---------------------------------------------------------------------------- +*/ + +//#include "s3c-otg-common-typedef.h" +#include "s3c-otg-common-const.h" +#include "s3c-otg-common-errorcode.h" +#include "s3c-otg-common-datastruct.h" + +#include "s3c-otg-hcdi-debug.h" + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +#ifdef __cplusplus +} +#endif + + +#endif + + + + + + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.c b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.c new file mode 100644 index 0000000..689d379 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.c @@ -0,0 +1,238 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : CommonTransferChecker.c + * [Description] : The Source file implements the external and internal functions of CommonTransferChecker. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2009/01/12 + * [Revision History] + * (1) 2008/06/12 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and implements functions of CommonTransferChecker + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-transferchecker-common.h" + +/******************************************************************************/ +/*! + * @name int init_done_transfer_checker(void) + * + * @brief this function initiates S3CDoneTransferChecker Module. + * + * + * @param void + * + * @return void + */ +/******************************************************************************/ +//ss1 +/*void init_done_transfer_checker(void) +{ + return USB_ERR_SUCCESS; +}*/ + +/******************************************************************************/ +/*! + * @name void do_transfer_checker(struct sec_otghost *otghost) + * + * @brief this function processes the result of USB Transfer. So, do_transfer_checker fistly + * check which channel occurs OTG Interrupt and gets the status information of the channel. + * do_transfer_checker requests the information of td_t to S3CScheduler. + * To process the interrupt of the channel, do_transfer_checker calls the sub-modules of + * S3CDoneTransferChecker, for example, ControlTransferChecker, BulkTransferChecker. + * according to the process result of the channel interrupt, do_transfer_checker decides + * the USB Transfer will be done or retransmitted. + * + * + * @param void + * + * @return void + */ +/*******************************************************************************/ +void do_transfer_checker (struct sec_otghost *otghost) +__releases(&otghost->lock) +__acquires(&otghost->lock) +{ + u32 hc_intr = 0; + u32 hc_intr_msk = 0; + u8 do_try_cnt = 0; + + hc_info_t ch_info; + u32 td_addr = 0; + td_t *done_td = {0}; + u8 proc_result = 0; + + //by ss1 + otg_mem_set((void *)&ch_info, 0, sizeof(hc_info_t)); + + // Get value of HAINT... + get_intr_ch(&hc_intr,&hc_intr_msk); + +start_do_transfer_checker: + + while(do_try_cnt<MAX_CH_NUMBER) { + //checks the channel number to be masked or not. + if(!(hc_intr & hc_intr_msk & (1<<do_try_cnt))) { + do_try_cnt++; + goto start_do_transfer_checker; + } + + //Gets the address of the td_t to have the channel to be interrupted. + if(get_td_info(do_try_cnt, &td_addr) == USB_ERR_SUCCESS) { + + done_td = (td_t *)td_addr; + + if(do_try_cnt != done_td->cur_stransfer.alloc_chnum) { + do_try_cnt++; + goto start_do_transfer_checker; + } + } + else { + do_try_cnt++; + goto start_do_transfer_checker; + } + + //Gets the informationof channel to be interrupted. + get_ch_info(&ch_info,do_try_cnt); + + switch(done_td->parent_ed_p->ed_desc.endpoint_type) { + case CONTROL_TRANSFER: + proc_result = process_control_transfer(done_td, &ch_info); + break; + case BULK_TRANSFER: + proc_result = process_bulk_transfer(done_td, &ch_info); + break; + case INT_TRANSFER: + proc_result = process_intr_transfer(done_td, &ch_info); + break; + case ISOCH_TRANSFER: + /* proc_result = ProcessIsochTransfer(done_td, &ch_info); */ + break; + default:break; + } + + if((proc_result == RE_TRANSMIT) || (proc_result == RE_SCHEDULE)) { + done_td->parent_ed_p->ed_status.is_in_transferring = false; + done_td->is_transfer_done = false; + done_td->is_transferring = false; + + if(done_td->parent_ed_p->ed_desc.endpoint_type == CONTROL_TRANSFER || + done_td->parent_ed_p->ed_desc.endpoint_type==BULK_TRANSFER) { + update_nonperio_stransfer(done_td); + } + else { + update_perio_stransfer(done_td); + } + + if(proc_result == RE_TRANSMIT) { + retransmit(otghost, done_td); + } + else { + reschedule(done_td); + } + } + + else if(proc_result==DE_ALLOCATE) { + done_td->parent_ed_p->ed_status.is_in_transferring = false; + done_td->parent_ed_p->ed_status.in_transferring_td = 0; + done_td->is_transfer_done = true; + done_td->is_transferring = false; + + spin_unlock_otg(&otghost->lock); + otg_usbcore_giveback( done_td); + spin_lock_otg(&otghost->lock); + release_trans_resource(otghost, done_td); + } + else { //NO_ACTION.... + done_td->parent_ed_p->ed_status.is_in_transferring = true; + done_td->parent_ed_p->ed_status.in_transferring_td = (u32)done_td; + done_td->is_transfer_done = false; + done_td->is_transferring = true; + } + do_try_cnt++; + } + // Complete to process the Channel Interrupt. + // So. we now start to scheduler of S3CScheduler. + do_schedule(otghost); +} + + +int release_trans_resource(struct sec_otghost *otghost, td_t *done_td) +{ + //remove the pDeallocateTD from parent_ed_p. + otg_list_pop(&done_td->td_list_entry); + done_td->parent_ed_p->num_td--; + + //Call deallocate to release the channel and bandwidth resource of S3CScheduler. + deallocate(done_td); + delete_td(otghost, done_td); + return USB_ERR_SUCCESS; +} + +u32 calc_transferred_size(bool f_is_complete, td_t *td, hc_info_t *hc_info) +{ + if(f_is_complete) { + if(td->parent_ed_p->ed_desc.is_ep_in) { + return td->cur_stransfer.buf_size - hc_info->hc_size.b.xfersize; + } + else { + return td->cur_stransfer.buf_size; + } + } + else { + return (td->cur_stransfer.packet_cnt - hc_info->hc_size.b.pktcnt)*td->parent_ed_p->ed_desc.max_packet_size; + } +} + +void update_frame_number(td_t *pResultTD) +{ + u32 cur_frame_num=0; + + if(pResultTD->parent_ed_p->ed_desc.endpoint_type == CONTROL_TRANSFER || + pResultTD->parent_ed_p->ed_desc.endpoint_type == BULK_TRANSFER) { + return; + } + + pResultTD->parent_ed_p->ed_desc.sched_frame+= pResultTD->parent_ed_p->ed_desc.interval; + pResultTD->parent_ed_p->ed_desc.sched_frame &= HFNUM_MAX_FRNUM; + + cur_frame_num = oci_get_frame_num(); + if(((cur_frame_num - pResultTD->parent_ed_p->ed_desc.sched_frame)&HFNUM_MAX_FRNUM) <= (HFNUM_MAX_FRNUM>>1)) { + pResultTD->parent_ed_p->ed_desc.sched_frame = cur_frame_num; + } +} + +void update_datatgl(u8 ubCurDataTgl, td_t *td) +{ + switch(td->parent_ed_p->ed_desc.endpoint_type) { + case CONTROL_TRANSFER: + if(td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE) { + td->parent_ed_p->ed_status.control_data_tgl.data_tgl = ubCurDataTgl; + } + break; + case BULK_TRANSFER: + case INT_TRANSFER: + td->parent_ed_p->ed_status.data_tgl = ubCurDataTgl; + break; + + case ISOCH_TRANSFER: + break; + default:break; + } +} diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.h b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.h new file mode 100644 index 0000000..2ea05b8 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-common.h @@ -0,0 +1,79 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : CommonTransferChecker.h + * [Description] : The Header file defines the external and internal functions of CommonTransferChecker. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/12 + * [Revision History] + * (1) 2008/06/12 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and defines functions of CommonTransferChecker + * + ****************************************************************************/ +/**************************************************************************** + * 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 _COMMON_TRANSFER_CHECKER_H +#define _COMMON_TRANSFER_CHECKER_H + +/* +// ---------------------------------------------------------------------------- +// Include files : None. +// ---------------------------------------------------------------------------- +*/ + +#include "s3c-otg-common-common.h" +//#include "s3c-otg-common-const.h" +#include "s3c-otg-common-errorcode.h" +#include "s3c-otg-common-datastruct.h" +#include "s3c-otg-common-regdef.h" +#include "s3c-otg-transfer-transfer.h" + +#include "s3c-otg-hcdi-debug.h" +#include "s3c-otg-hcdi-memory.h" +#include "s3c-otg-scheduler-scheduler.h" +#include "s3c-otg-isr.h" +#include "s3c-otg-transferchecker-control.h" +#include "s3c-otg-transferchecker-bulk.h" +#include "s3c-otg-transferchecker-interrupt.h" +//#include "s3c-otg-transferchecker-iso.h" + + + +#ifdef __cplusplus +extern "C" +{ +#endif + +//void init_done_transfer_checker (void); +void do_transfer_checker (struct sec_otghost *otghost); +int release_trans_resource(struct sec_otghost *otghost, td_t *done_td); +u32 calc_transferred_size(bool f_is_complete, + td_t *td, + hc_info_t *hc_info); +void update_frame_number(td_t *result_td); +void update_datatgl(u8 cur_data_tgl, + td_t *td); + +#ifdef __cplusplus +} +#endif + + +#endif + + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.c b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.c new file mode 100644 index 0000000..863f951 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.c @@ -0,0 +1,698 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : ControlTransferChecker.c + * [Description] : The Source file implements the external and internal functions of ControlTransferChecker. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2009/02/10 + * [Revision History] + * (1) 2008/06/13 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and implements functions of ControlTransferChecker + * (2) 2008/06/18 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Completed to implement ControlTransferChecker.c v1.0 + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-transferchecker-control.h" +#include "s3c-otg-isr.h" + + + +/******************************************************************************/ +/*! + * @name u8 process_control_transfer(td_t *result_td, + hc_info_t *hc_reg_data) + + * + * @brief this function processes the result of the Control Transfer. + * firstly, this function checks the result the Control Transfer. + * and according to the result, calls the sub-functions to process the result. + * + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] ubChNum -indicates the number of the channel to be interrupted. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_TRANSMIT -if need to retransmit the result_td. + * RE_SCHEDULE -if need to reschedule the result_td. + * DE_ALLOCATE -if USB Transfer is completed. + */ +/******************************************************************************/ +u8 process_control_transfer(td_t *result_td, + hc_info_t *hc_reg_data) +{ + hcintn_t hcintr_info; + u8 ret_val=0; + + //we just deal with the interrupts to be unmasked. + hcintr_info.d32 = hc_reg_data->hc_int.d32&result_td->cur_stransfer.hc_reg.hc_int_msk.d32; + + if(result_td->parent_ed_p->ed_desc.is_ep_in) + { + if(hcintr_info.b.chhltd) + { + ret_val = process_chhltd_on_control(result_td, hc_reg_data); + } + + else if (hcintr_info.b.ack) + { + ret_val =process_ack_on_control(result_td, hc_reg_data); + } + + else if (hcintr_info.b.nak) + { + ret_val =process_nak_on_control(result_td, hc_reg_data); + } + + else if (hcintr_info.b.datatglerr) + { + ret_val = process_datatgl_on_control(result_td,hc_reg_data); + } + } + else + { + if(hcintr_info.b.chhltd) + { + ret_val = process_chhltd_on_control(result_td, hc_reg_data); + } + + else if(hcintr_info.b.ack) + { + ret_val =process_ack_on_control( result_td, hc_reg_data); + + } + } + + return ret_val; +} + +/******************************************************************************/ +/*! + * @name u8 process_chhltd_on_control( td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function processes Channel Halt event according to Synopsys OTG Spec. + * firstly, this function checks the reason of the Channel Halt, and according to the reason, + * calls the sub-functions to process the result. + * + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_TRANSMIT -if need to retransmit the result_td. + * RE_SCHEDULE -if need to reschedule the result_td. + * DE_ALLOCATE -if USB Transfer is completed. + */ +/******************************************************************************/ +u8 process_chhltd_on_control( td_t *result_td, + hc_info_t *hc_reg_data) +{ + if(hc_reg_data->hc_int.b.xfercompl) + { + return process_xfercompl_on_control( result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.stall) + { + return process_stall_on_control(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.bblerr) + { + return process_bblerr_on_control(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.xacterr) + { + return process_xacterr_on_control(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.nak) + { + return process_nak_on_control(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.nyet) + { + return process_nyet_on_control(result_td, hc_reg_data); + } + else + { + + //Occure Error State..... + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + result_td->err_cnt++; + if(result_td->err_cnt == 3) + { + result_td->error_code = USB_ERR_STATUS_XACTERR; + result_td->err_cnt = 0; + return DE_ALLOCATE; + } + return RE_TRANSMIT; + } + return USB_ERR_SUCCESS; +} + +/******************************************************************************/ +/*! + * @name u8 process_xfercompl_on_control(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the xfercompl event according to Synopsys OTG Spec. + * the procedure of this function is as following + * 1. clears all bits of the channel' HCINT by using clear_ch_intr() of S3CIsr. + * 2. masks some bit of HCINTMSK + * 3. updates the result_td fields + * err_cnt/u8/standard_dev_req_info. + * 4. updates the result_td->parent_ed_p->ed_status. + * control_data_tgl. + * 5. calculates the tranferred size by calling calc_transferred_size() on DATA_STAGE. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return USB_ERR_SUCCESS + */ +/******************************************************************************/ +u8 process_xfercompl_on_control(td_t *result_td, hc_info_t *hc_reg_data) +{ + u8 ret_val = 0; + + result_td->err_cnt = 0; + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum,CH_STATUS_ALL); + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->parent_ed_p->ed_status.is_ping_enable = false; + + switch(result_td->standard_dev_req_info.conrol_transfer_stage) { + case SETUP_STAGE: + if(result_td->standard_dev_req_info.is_data_stage) { + result_td->standard_dev_req_info.conrol_transfer_stage = DATA_STAGE; + } + else { + result_td->standard_dev_req_info.conrol_transfer_stage = STATUS_STAGE; + } + ret_val = RE_TRANSMIT; + + break; + + case DATA_STAGE: + + result_td->transferred_szie += calc_transferred_size(true,result_td, hc_reg_data); + + /* at IN Transfer, short transfer is accepted. */ + if(result_td->transferred_szie==result_td->buf_size) { + result_td->standard_dev_req_info.conrol_transfer_stage =STATUS_STAGE; + result_td->error_code = USB_ERR_STATUS_COMPLETE; + } + else { + if(result_td->parent_ed_p->ed_desc.is_ep_in&& hc_reg_data->hc_size.b.xfersize) { + if(result_td->transfer_flag&USB_TRANS_FLAG_NOT_SHORT) { + result_td->error_code = USB_ERR_STATUS_SHORTREAD; + result_td->standard_dev_req_info.conrol_transfer_stage=STATUS_STAGE; + } + else { + result_td->error_code = USB_ERR_STATUS_COMPLETE; + result_td->standard_dev_req_info.conrol_transfer_stage =STATUS_STAGE; + } + } + else { // the Data Stage is not completed. So we need to continue Data Stage. + result_td->standard_dev_req_info.conrol_transfer_stage = DATA_STAGE; + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + } + } + + if(hc_reg_data->hc_int.b.nyet) { + /* at OUT Transfer, we must re-transmit. */ + if(result_td->parent_ed_p->ed_desc.is_ep_in==false) { + if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG) { + result_td->parent_ed_p->ed_status.is_ping_enable = true; + } + else { + result_td->parent_ed_p->ed_status.is_ping_enable = false; + } + } + } + ret_val = RE_TRANSMIT; + break; + + case STATUS_STAGE: + result_td->standard_dev_req_info.conrol_transfer_stage = COMPLETE_STAGE; + + if(hc_reg_data->hc_int.b.nyet) { + //at OUT Transfer, we must re-transmit. + if(result_td->parent_ed_p->ed_desc.is_ep_in==false) { + if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG) { + result_td->parent_ed_p->ed_status.is_ping_enable = true; + } + else { + result_td->parent_ed_p->ed_status.is_ping_enable = false; + } + } + } + + ret_val = DE_ALLOCATE; + break; + + default: + break; + } + + return ret_val; +} + +/******************************************************************************/ +/*! + * @name u8 process_ahb_on_control( td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with theAHB Errorl event according to Synopsys OTG Spec. + * this function stop the channel to be executed + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return DE_ALLOCATE + */ +/******************************************************************************/ +u8 process_ahb_on_control( td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt =0; + result_td->error_code =USB_ERR_STATUS_AHBERR; + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_AHBErr); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + // we just calculate the size of the transferred data on Data Stage of Control Transfer. + if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE) + { + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + } + + return DE_ALLOCATE; + +} + +/******************************************************************************/ +/*! + * @name u8 process_stall_on_control( td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with theStall event according to Synopsys OTG Spec. + * but USB2.0 Spec don't permit the Stall on Setup Stage of Control Transfer. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return DE_ALLOCATE + */ +/******************************************************************************/ +u8 process_stall_on_control( td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt =0; + result_td->error_code =USB_ERR_STATUS_STALL; + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->parent_ed_p->ed_status.is_ping_enable = false; + + // we just calculate the size of the transferred data on Data Stage of Control Transfer. + if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE) + { + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + } + + return DE_ALLOCATE; +} + +/******************************************************************************/ +/*! + * @name u8 process_nak_on_control( td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the nak event according to Synopsys OTG Spec. + * nak is occured at OUT/IN Transaction of Data/Status Stage, and is not occured at Setup Stage. + * If nak is occured at IN Transaction, this function processes this interrupt as following. + * 1. resets the result_td->err_cnt. + * 2. masks ack/nak/DaaTglErr bit of HCINTMSK. + * 3. clears the nak bit of HCINT + * 4. be careful, nak of IN Transaction don't require re-transmit. + * If nak is occured at OUT Transaction, this function processes this interrupt as following. + * 1. all procedures of IN Transaction are executed. + * 2. calculates the size of the transferred data. + * 3. if the speed of USB Device is High-Speed, sets the ping protocol. + * 4. update the Toggle + * at OUT Transaction, this function check whether the speed of USB Device is High-Speed or not. + * if USB Device is High-Speed, then + * this function sets the ping protocol. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_SCHEDULE + */ +/******************************************************************************/ +u8 process_nak_on_control( td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt = 0; + + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + + //at OUT Transfer, we must re-transmit. + if(result_td->parent_ed_p->ed_desc.is_ep_in==false) + { + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + } + + if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG) + { + if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE) + { + if(result_td->parent_ed_p->ed_desc.is_ep_in==false) + { + result_td->parent_ed_p->ed_status.is_ping_enable = true; + } + } + else if(result_td->standard_dev_req_info.conrol_transfer_stage == STATUS_STAGE) + { + if(result_td->parent_ed_p->ed_desc.is_ep_in==true) + { + result_td->parent_ed_p->ed_status.is_ping_enable = true; + } + } + else + { + result_td->parent_ed_p->ed_status.is_ping_enable = false; + } + + } + + return RE_SCHEDULE; + + +} + +/******************************************************************************/ +/*! + * @name u8 process_ack_on_control( td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the ack event according to Synopsys OTG Spec. + * ack of IN/OUT Transaction don't need any retransmit. + * this function just resets result_td->err_cnt and masks ack/nak/DataTgl of HCINTMSK. + * finally, this function clears ack bit of HCINT. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return NO_ACTION + */ +/******************************************************************************/ +u8 process_ack_on_control(td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt = 0; + + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + + result_td->parent_ed_p->ed_status.is_ping_enable =false; + + return NO_ACTION; + +} + +/******************************************************************************/ +/*! + * @name u8 process_nyet_on_control(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the nyet event according to Synopsys OTG Spec. + * nyet is occured at OUT Transaction of Data/Status Stage, and is not occured at Setup Stage. + * If nyet is occured at OUT Transaction, this function processes this interrupt as following. + * 1. resets the result_td->err_cnt. + * 2. masks ack/nak/datatglerr bit of HCINTMSK. + * 3. clears the nyet bit of HCINT + * 4. calculates the size of the transferred data. + * 5. if the speed of USB Device is High-Speed, sets the ping protocol. + * 6. update the Data Toggle. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_SCHEDULE + */ +/******************************************************************************/ +u8 process_nyet_on_control(td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt = 0; + + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NYET); + + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + + if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG) + { + if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE) + { + if(result_td->parent_ed_p->ed_desc.is_ep_in==false) + { + result_td->parent_ed_p->ed_status.is_ping_enable = true; + } + } + else if(result_td->standard_dev_req_info.conrol_transfer_stage == STATUS_STAGE) + { + if(result_td->parent_ed_p->ed_desc.is_ep_in==true) + { + result_td->parent_ed_p->ed_status.is_ping_enable = true; + } + } + else + { + result_td->parent_ed_p->ed_status.is_ping_enable = false; + } + + } + + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + + return RE_SCHEDULE; +} + +/******************************************************************************/ +/*! + * @name u8 process_xacterr_on_control( td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the xacterr event according to Synopsys OTG Spec. + * xacterr is occured at OUT/IN Transaction of Data/Status Stage, and is not occured at Setup Stage. + * if Timeout/CRC error/false EOP is occured, then xacterr is occured. + * the procedure to process xacterr is as following. + * 1. increses the result_td->err_cnt + * 2. check whether the result_td->err_cnt is equal to 3. + * 2. unmasks ack/nak/datatglerr bit of HCINTMSK. + * 3. clears the xacterr bit of HCINT + * 4. calculates the size of the transferred data. + * 5. if the speed of USB Device is High-Speed, sets the ping protocol. + * 6. update the Data Toggle. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_TRANSMIT -if the Error Counter is less than RETRANSMIT_THRESHOLD + * DE_ALLOCATE -if the Error Counter is equal to RETRANSMIT_THRESHOLD + */ +/******************************************************************************/ +u8 process_xacterr_on_control(td_t *result_td, + hc_info_t *hc_reg_data) +{ + u8 ret_val=0; + + if(result_td->err_cnt<RETRANSMIT_THRESHOLD) + { + result_td->cur_stransfer.hc_reg.hc_int_msk.d32 |= (CH_STATUS_ACK+CH_STATUS_NAK+CH_STATUS_DataTglErr); + ret_val = RE_TRANSMIT; + unmask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + unmask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + unmask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + result_td->err_cnt++ ; + } + else + { + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + ret_val = DE_ALLOCATE; + result_td->err_cnt = 0 ; + result_td->error_code = USB_ERR_STATUS_XACTERR; + } + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE) + { + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + + } + + if(result_td->parent_ed_p->ed_desc.dev_speed == HIGH_SPEED_OTG) + { + if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE) + { + if(result_td->parent_ed_p->ed_desc.is_ep_in==false) + { + result_td->parent_ed_p->ed_status.is_ping_enable = true; + } + } + else if(result_td->standard_dev_req_info.conrol_transfer_stage == STATUS_STAGE) + { + if(result_td->parent_ed_p->ed_desc.is_ep_in==true) + { + result_td->parent_ed_p->ed_status.is_ping_enable = true; + } + } + else + { + result_td->parent_ed_p->ed_status.is_ping_enable = false; + } + + + } + + + + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + + return ret_val; + +} + +/******************************************************************************/ +/*! + * @name void process_bblerr_on_control( td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the Babble event according to Synopsys OTG Spec. + * babble error can be just occured at IN Transaction. So if the direction of transfer is + * OUT, this function return Error Code. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return DE_ALLOCATE + * NO_ACTION -if the direction is OUT + */ +/******************************************************************************/ +u8 process_bblerr_on_control( td_t *result_td, + hc_info_t *hc_reg_data) +{ + + if(!result_td->parent_ed_p->ed_desc.is_ep_in) + { + return NO_ACTION; + } + + result_td->err_cnt = 0; + result_td->error_code =USB_ERR_STATUS_BBLERR; + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->parent_ed_p->ed_status.is_ping_enable =false; + + // we just calculate the size of the transferred data on Data Stage of Control Transfer. + if(result_td->standard_dev_req_info.conrol_transfer_stage == DATA_STAGE) + { + result_td->transferred_szie += calc_transferred_size(false, result_td, hc_reg_data); + } + + return DE_ALLOCATE; +} + +/******************************************************************************/ +/*! + * @name u8 process_datatgl_on_control(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the datatglerr event according to Synopsys OTG Spec. + * the datatglerr event is occured at IN Transfer. + * this function just resets result_td->err_cnt and masks ack/nak/DataTgl of HCINTMSK. + * finally, this function clears datatglerr bit of HCINT. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] hc_reg_data -indicates the interrupt information of the Channel to be interrupted + * + * @return NO_ACTION + */ +/******************************************************************************/ +u8 process_datatgl_on_control( td_t * result_td, + hc_info_t * hc_reg_data) +{ + result_td->err_cnt = 0; + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + return NO_ACTION; + +} + + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.h b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.h new file mode 100644 index 0000000..4d85367 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-control.h @@ -0,0 +1,99 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : ControlTransferChecker.h + * [Description] : The Header file defines the external and internal functions of ControlTransferChecker + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2009/01/12 + * [Revision History] + * (1) 2008/06/13 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and defines functions of ControlTransferChecker + * + ****************************************************************************/ +/**************************************************************************** + * 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 _CONTROL_TRANSFER_CHECKER_H +#define _CONTROL_TRANSFER_CHECKER_H + +/* +// ---------------------------------------------------------------------------- +// Include files : None. +// ---------------------------------------------------------------------------- +*/ + +#include "s3c-otg-common-common.h" +//#include "s3c-otg-common-typedef.h" +#include "s3c-otg-common-const.h" +#include "s3c-otg-common-errorcode.h" +#include "s3c-otg-common-datastruct.h" +#include "s3c-otg-common-regdef.h" + +#include "s3c-otg-hcdi-debug.h" +#include "s3c-otg-scheduler-scheduler.h" + +#include "s3c-otg-transferchecker-checker.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif +u8 process_control_transfer( td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_xfercompl_on_control(td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_chhltd_on_control( td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_ahb_on_control( td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_stall_on_control( td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_nak_on_control (td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_ack_on_control (td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_nyet_on_control( td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_xacterr_on_control(td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_bblerr_on_control( td_t *raw_td, + hc_info_t *hc_reg_data); + +u8 process_datatgl_on_control(td_t *raw_td, + hc_info_t *hc_reg_data); +u8 process_indirection_on_control( td_t *result_td, + hc_info_t *hc_reg_data); + +u8 process_outdirection_on_control(td_t *result_td, + hc_info_t *hc_reg_data); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.c b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.c new file mode 100644 index 0000000..653628e --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.c @@ -0,0 +1,574 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : IntTransferChecker.c + * [Description] : The Source file implements the external and internal functions of IntTransferChecker + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/19 + * [Revision History] + * (1) 2008/06/18 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and implements functions of IntTransferChecker + * + ****************************************************************************/ +/**************************************************************************** + * 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 "s3c-otg-transferchecker-interrupt.h" + +/******************************************************************************/ +/*! + * @name u8 process_intr_transfer(td_t *result_td, + * hc_info_t *HCRegData) + * + * + * @brief this function processes the result of the Interrupt Transfer. + * firstly, this function checks the result of the Interrupt Transfer. + * and according to the result, calls the sub-functions to process the result. + * + * + * @param [IN] result_td -indicates the pointer of the td_t whose channel is interruped. + * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_TRANSMIT -if need to retransmit the result_td. + * RE_SCHEDULE -if need to reschedule the result_td. + * DE_ALLOCATE -if USB Transfer is completed. + * NO_ACTION -if we don't need any action, + */ +/******************************************************************************/ +u8 process_intr_transfer(td_t *result_td, hc_info_t *hc_reg_data) +{ + hcintn_t hc_intr_info; + u8 ret_val=0; + + //we just deal with the interrupts to be unmasked. + hc_intr_info.d32 = hc_reg_data->hc_int.d32&result_td->cur_stransfer.hc_reg.hc_int_msk.d32; + + if(result_td->parent_ed_p->ed_desc.is_ep_in) { + if(hc_intr_info.b.chhltd) { + ret_val = process_chhltd_on_intr(result_td, hc_reg_data); + } + + else if (hc_intr_info.b.ack) { + ret_val = process_ack_on_intr(result_td, hc_reg_data); + } + } + else { + if(hc_intr_info.b.chhltd) { + ret_val = process_chhltd_on_intr(result_td, hc_reg_data); + } + + else if(hc_intr_info.b.ack) { + ret_val = process_ack_on_intr( result_td, hc_reg_data); + } + } + + return ret_val; +} + +/******************************************************************************/ +/*! + * @name u8 process_chhltd_on_intr(td_t *result_td, + * hc_info_t *HCRegData) + * + * + * @brief this function processes Channel Halt event according to Synopsys OTG Spec. + * firstly, this function checks the reason of the Channel Halt, and according to the reason, + * calls the sub-functions to process the result. + * + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_TRANSMIT -if need to retransmit the result_td. + * RE_SCHEDULE -if need to reschedule the result_td. + * DE_ALLOCATE -if USB Transfer is completed. + */ +/******************************************************************************/ +u8 process_chhltd_on_intr(td_t *result_td, + hc_info_t *hc_reg_data) +{ + if(result_td->parent_ed_p->ed_desc.is_ep_in) + { + if(hc_reg_data->hc_int.b.xfercompl) + { + return process_xfercompl_on_intr( result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.stall) + { + return process_stall_on_intr(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.bblerr) + { + return process_bblerr_on_intr(result_td, hc_reg_data); + } + else if (hc_reg_data->hc_int.b.nak) + { + return process_nak_on_intr(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.datatglerr) + { + return process_datatgl_on_intr(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.frmovrun) + { + return process_frmovrrun_on_intr(result_td,hc_reg_data); + } + else if(hc_reg_data->hc_int.b.xacterr) + { + return process_xacterr_on_intr(result_td, hc_reg_data); + } + else + { + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + return RE_TRANSMIT; + } + } + else + { + if(hc_reg_data->hc_int.b.xfercompl) + { + return process_xfercompl_on_intr( result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.stall) + { + return process_stall_on_intr(result_td, hc_reg_data); + } + else if (hc_reg_data->hc_int.b.nak) + { + return process_nak_on_intr(result_td, hc_reg_data); + } + else if(hc_reg_data->hc_int.b.frmovrun) + { + return process_frmovrrun_on_intr(result_td,hc_reg_data); + } + else if(hc_reg_data->hc_int.b.xacterr) + { + return process_xacterr_on_intr(result_td, hc_reg_data); + } + else + { + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->err_cnt++; + if(result_td->err_cnt == 3) + { + result_td->error_code = USB_ERR_STATUS_XACTERR; + result_td->err_cnt = 0; + return DE_ALLOCATE; + } + + return RE_TRANSMIT; + } + + } + + +} + +/******************************************************************************/ +/*! + * @name u8 process_xfercompl_on_intr( td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the xfercompl event according to Synopsys OTG Spec. + * the procedure of this function is as following + * 1. clears all bits of the channel' HCINT by using clear_ch_intr() of S3CIsr. + * 2. masks ack/nak(?)/datatglerr(?) bit of HCINTMSK + * 3. Resets the err_cnt of result_td. + * 4. updates the result_td->parent_ed_p->ed_status. + * IntDataTgl. + * 5. calculates the tranferred size by calling calc_transferred_size() on DATA_STAGE. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted + * + * @return DE_ALLOCATE -if USB Transfer is completed. + * RE_TRANSMIT -if need to retransmit the result_td. + */ +/******************************************************************************/ +u8 process_xfercompl_on_intr( td_t *result_td, + hc_info_t *hc_reg_data) +{ + u8 ret_val=0; + + result_td->err_cnt =0; + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->transferred_szie += calc_transferred_size(true,result_td, hc_reg_data); + + if(result_td->transferred_szie==result_td->buf_size) + {//at IN Transfer, short transfer is accepted. + result_td->error_code = USB_ERR_STATUS_COMPLETE; + ret_val = DE_ALLOCATE; + } + else + { + // this routine will not be executed on Interrupt Transfer. + // So, we should decide to remove this routine or not. + if(result_td->parent_ed_p->ed_desc.is_ep_in&& hc_reg_data->hc_size.b.xfersize) + { + if(result_td->transfer_flag&USB_TRANS_FLAG_NOT_SHORT) + { + result_td->error_code = USB_ERR_STATUS_SHORTREAD; + } + else + { + result_td->error_code = USB_ERR_STATUS_COMPLETE; + } + ret_val = DE_ALLOCATE; + } + else + { // the Data Stage is not completed. So we need to continue Data Stage. + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + ret_val = RE_TRANSMIT; + } + } + + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + + return ret_val; + +} + +/******************************************************************************/ +/*! + * @name u8 process_ahb_on_intr(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with theAHB Errorl event according to Synopsys OTG Spec. + * this function stop the channel to be executed + * + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted + * + * @return DE_ALLOCATE + */ +/******************************************************************************/ +u8 process_ahb_on_intr(td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt = 0; + result_td->error_code = USB_ERR_STATUS_AHBERR; + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_AHBErr); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + // we just calculate the size of the transferred data on Data Stage of Int Transfer. + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + result_td->parent_ed_p->ed_status.is_ping_enable =false; + + return DE_ALLOCATE; + +} + +/******************************************************************************/ +/*! + * @name u8 process_stall_on_intr(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the Stall event according to Synopsys OTG Spec. + * when Stall is occured at Int Transfer, we should reset the PID as DATA0 + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted + * + * @return DE_ALLOCATE + */ +/******************************************************************************/ +u8 process_stall_on_intr(td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt = 0; + result_td->error_code = USB_ERR_STATUS_STALL; + + //this channel is stalled, So we don't process another interrupts. + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + + update_datatgl(DATA0, result_td); + + return DE_ALLOCATE; +} + +/******************************************************************************/ +/*! + * @name u8 process_nak_on_intr(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the nak event according to Synopsys OTG Spec. + * nak is occured at OUT/IN Transaction of Interrupt Transfer. + * we can't use ping protocol on Interrupt Transfer. and Syonopsys OTG IP occures + * chhltd interrupt on nak of IN/OUT Transaction. So we should retransmit the transfer + * on IN Transfer. + * If nak is occured at IN Transaction, this function processes this interrupt as following. + * 1. resets the result_td->err_cnt. + * 2. masks ack/nak/DaaTglErr bit of HCINTMSK. + * 3. clears the nak bit of HCINT + * 4. calculates frame number to retransmit this Interrupt Transfer. + * + * If nak is occured at OUT Transaction, this function processes this interrupt as following. + * 1. all procedures of IN Transaction are executed. + * 2. calculates the size of the transferred data. + * 3. if the speed of USB Device is High-Speed, sets the ping protocol. + * 4. update the Toggle + * at OUT Transaction, this function check whether the speed of USB Device is High-Speed or not. + * if USB Device is High-Speed, then + * this function sets the ping protocol. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_SCHEDULE -if the direction of the Transfer is OUT + * NO_ACTION -if the direction of the Transfer is IN + */ +/******************************************************************************/ +u8 process_nak_on_intr(td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt = 0; + + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + + + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + + update_frame_number(result_td); + + return RE_SCHEDULE; +// return RE_TRANSMIT; + + +} + +/******************************************************************************/ +/*! + * @name u8 process_ack_on_intr(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the ack event according to Synopsys OTG Spec. + * ack of IN/OUT Transaction don't need any retransmit. + * this function just resets result_td->err_cnt and masks ack/nak/DataTgl of HCINTMSK. + * finally, this function clears ack bit of HCINT and ed_status.is_ping_enable. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted + * + * @return NO_ACTION + */ +/******************************************************************************/ +u8 process_ack_on_intr(td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt = 0; + + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + + return NO_ACTION; +} + +/******************************************************************************/ +/*! + * @name u8 process_xacterr_on_intr(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * @brief this function deals with the xacterr event according to Synopsys OTG Spec. + * xacterr is occured at OUT/IN Transaction and we should retransmit the USB Transfer + * if the Error Counter is less than the RETRANSMIT_THRESHOLD. + * the reasons of xacterr is Timeout/CRC error/false EOP. + * the procedure to process xacterr is as following. + * 1. increses the result_td->err_cnt + * 2. check whether the result_td->err_cnt is equal to 3. + * 2. unmasks ack/nak/datatglerr bit of HCINTMSK. + * 3. clears the xacterr bit of HCINT + * 4. calculates the size of the transferred data. + * 5. update the Data Toggle. + * 6. update the frame number to start retransmitting the Interrupt Transfer. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_SCHEDULE -if the error count is less than 3 + * DE_ALLOCATE -if the error count is equal to 3 + */ +/******************************************************************************/ +u8 process_xacterr_on_intr(td_t *result_td, + hc_info_t *hc_reg_data) +{ + u8 ret_val = 0; + + if(result_td->err_cnt<RETRANSMIT_THRESHOLD) + { + result_td->cur_stransfer.hc_reg.hc_int_msk.d32 |=(CH_STATUS_ACK+CH_STATUS_NAK+CH_STATUS_DataTglErr); + ret_val = RE_SCHEDULE; + result_td->err_cnt++ ; + } + else + { + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + ret_val = DE_ALLOCATE; + result_td->err_cnt = 0 ; + } + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + + if(ret_val == RE_SCHEDULE) + { //Calculates the frame number + update_frame_number(result_td); + } + + return ret_val; +} + +/******************************************************************************/ +/*! + * @name void process_bblerr_on_intr(td_t *result_td, + * hc_info_t *hc_reg_data) + * + * + * @brief this function deals with the Babble event according to Synopsys OTG Spec. + * babble error is occured when the USB device continues to send packets + * althrough EOP is occured. So Babble error is only occured at IN Transfer. + * when Babble Error is occured, we should stop the USB Transfer, and return the fact + * to Application. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted + * + * @return DE_ALLOCATE + */ +/******************************************************************************/ +u8 process_bblerr_on_intr(td_t *result_td, + hc_info_t *hc_reg_data) +{ + + if(!result_td->parent_ed_p->ed_desc.is_ep_in) + { + return NO_ACTION; + } + + result_td->err_cnt = 0; + result_td->error_code =USB_ERR_STATUS_BBLERR; + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL); + + //Mask ack Interrupt.. + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + result_td->transferred_szie += calc_transferred_size(false, result_td, hc_reg_data); + return DE_ALLOCATE; +} + +/******************************************************************************/ +/*! + * @name u8 process_datatgl_on_intr( td_t *result_td, + * hc_info_t *hc_reg_data) + * + * @brief this function deals with the datatglerr event according to Synopsys OTG Spec. + * the datatglerr event is occured at IN Transfer, and the channel is not halted. + * this function just resets result_td->err_cnt and masks ack/nak/DataTgl of HCINTMSK. + * finally, this function clears datatglerr bit of HCINT. + * + * @param [IN] result_td -indicates the pointer of the td_t to be mapped with the uChNum. + * [IN] HCRegData -indicates the interrupt information of the Channel to be interrupted + * + * @return RE_SCHEDULE + */ +/******************************************************************************/ +u8 process_datatgl_on_intr(td_t *result_td, + hc_info_t *hc_reg_data) +{ + result_td->err_cnt = 0; + + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ACK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum, CH_STATUS_DataTglErr); + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + + result_td->transferred_szie += calc_transferred_size(false,result_td, hc_reg_data); + + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + + update_frame_number(result_td); + + return RE_SCHEDULE; +} + +u8 process_frmovrrun_on_intr(td_t *result_td, + hc_info_t *hc_reg_data) +{ + + clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_NAK); + + update_datatgl(hc_reg_data->hc_size.b.pid, result_td); + + update_frame_number(result_td); + + return RE_TRANSMIT; +} + diff --git a/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.h b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.h new file mode 100644 index 0000000..0a735a2 --- /dev/null +++ b/drivers/usb/host/s3c-otg/s3c-otg-transferchecker-interrupt.h @@ -0,0 +1,97 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : IntTransferChecker.h + * [Description] : The Header file defines the external and internal functions of IntTransferChecker + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/19 + * [Revision History] + * (1) 2008/06/18 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and defines functions of IntTransferChecker + * + ****************************************************************************/ +/**************************************************************************** + * 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 _INT_TRANSFER_CHECKER_H +#define _INT_TRANSFER_CHECKER_H + +/* +// ---------------------------------------------------------------------------- +// Include files : None. +// ---------------------------------------------------------------------------- +*/ + +#include "s3c-otg-common-common.h" +//#include "s3c-otg-common-typedef.h" +#include "s3c-otg-common-const.h" +#include "s3c-otg-common-errorcode.h" +#include "s3c-otg-common-datastruct.h" +#include "s3c-otg-common-regdef.h" + +#include "s3c-otg-hcdi-debug.h" +#include "s3c-otg-scheduler-scheduler.h" +#include "s3c-otg-isr.h" +#include "s3c-otg-transferchecker-checker.h" + + + +#ifdef __cplusplus +extern "C" +{ +#endif + +u8 process_intr_transfer(td_t *pRawTD, + hc_info_t *pHCRegData); + +u8 process_xfercompl_on_intr(td_t *pRawTD, + hc_info_t *pHCRegData); + +u8 process_chhltd_on_intr(td_t *pRawTD, + hc_info_t *pHCRegData); + +u8 process_ahb_on_intr(td_t *pRawTD, + hc_info_t *pHCRegData); + +u8 process_stall_on_intr(td_t *pRawTD, + hc_info_t *pHCRegData); + +u8 process_nak_on_intr(td_t *pRawTD, + hc_info_t *pHCRegData); + +u8 process_frmovrrun_on_intr(td_t *pRawTD, + hc_info_t *pHCRegData); + +u8 process_ack_on_intr(td_t *pRawTD, + hc_info_t *pHCRegData); + +u8 process_xacterr_on_intr(td_t *pRawTD, + hc_info_t *pHCRegData); + +u8 process_bblerr_on_intr(td_t *pRawTD, + hc_info_t *pHCRegData); + +u8 process_datatgl_on_intr(td_t *pRawTD, + hc_info_t *pHCRegData); + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/drivers/usb/notify/Kconfig b/drivers/usb/notify/Kconfig new file mode 100755 index 0000000..159affa --- /dev/null +++ b/drivers/usb/notify/Kconfig @@ -0,0 +1,11 @@ +# +# USB Host notify configuration +# + +config USB_HOST_NOTIFY + boolean "USB Host notify Driver" + depends on USB + help + Android framework needs uevents for usb host operation. + Host notify Driver serves uevent format + that is used by usb host or otg.
\ No newline at end of file diff --git a/drivers/usb/notify/Makefile b/drivers/usb/notify/Makefile new file mode 100755 index 0000000..996eaa3 --- /dev/null +++ b/drivers/usb/notify/Makefile @@ -0,0 +1,4 @@ + +# host notify driver +obj-y += host_notify_class.o + diff --git a/drivers/usb/notify/host_notify_class.c b/drivers/usb/notify/host_notify_class.c new file mode 100755 index 0000000..09ad529 --- /dev/null +++ b/drivers/usb/notify/host_notify_class.c @@ -0,0 +1,262 @@ +/* + * drivers/usb/notify/host_notify_class.c + * + * Copyright (C) 2011 Samsung, Inc. + * Author: Dongrak Shin <dongrak.shin@samsung.com> + * +*/ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/err.h> +#include <linux/host_notify.h> + +struct notify_data { + struct class *host_notify_class; + atomic_t device_count; +}; + +static struct notify_data host_notify; + +static ssize_t mode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct host_notify_dev *ndev = (struct host_notify_dev *) + dev_get_drvdata(dev); + char *mode; + + switch (ndev->mode) { + case NOTIFY_HOST_MODE: + mode = "HOST"; + break; + case NOTIFY_PERIPHERAL_MODE: + mode = "PERIPHERAL"; + break; + case NOTIFY_TEST_MODE: + mode = "TEST"; + break; + case NOTIFY_NONE_MODE: + default: + mode = "NONE"; + break; + } + + return sprintf(buf, "%s\n", mode); +} + +static ssize_t mode_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct host_notify_dev *ndev = (struct host_notify_dev *) + dev_get_drvdata(dev); + + char *mode; + size_t ret = -ENOMEM; + + mode = kzalloc(size+1, GFP_KERNEL); + if (!mode) + goto error; + + sscanf(buf, "%s", mode); + + if (ndev->set_mode) { + if (!strcmp(mode, "HOST")) + ndev->set_mode(NOTIFY_SET_ON); + else if (!strcmp(mode, "NONE")) + ndev->set_mode(NOTIFY_SET_OFF); + printk(KERN_INFO "host_notify: set mode %s\n", mode); + } + ret = size; + kfree(mode); +error: + return ret; +} + +static ssize_t booster_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct host_notify_dev *ndev = (struct host_notify_dev *) + dev_get_drvdata(dev); + char *booster; + + switch (ndev->booster) { + case NOTIFY_POWER_ON: + booster = "ON"; + break; + case NOTIFY_POWER_OFF: + default: + booster = "OFF"; + break; + } + + return sprintf(buf, "%s\n", booster); +} + +static ssize_t booster_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct host_notify_dev *ndev = (struct host_notify_dev *) + dev_get_drvdata(dev); + + char *booster; + size_t ret = -ENOMEM; + + booster = kzalloc(size+1, GFP_KERNEL); + if (!booster) + goto error; + + sscanf(buf, "%s", booster); + + if (ndev->set_booster) { + if (!strcmp(booster, "ON")) { + ndev->set_booster(NOTIFY_SET_ON); + ndev->mode = NOTIFY_TEST_MODE; + } else if (!strcmp(booster, "OFF")) { + ndev->set_booster(NOTIFY_SET_OFF); + ndev->mode = NOTIFY_NONE_MODE; + } + printk(KERN_INFO "host_notify: set booster %s\n", booster); + } + ret = size; + kfree(booster); +error: + return ret; +} + +static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR | S_IWGRP, mode_show, mode_store); +static DEVICE_ATTR(booster, S_IRUGO | S_IWUSR | S_IWGRP, + booster_show, booster_store); + +static struct attribute *host_notify_attrs[] = { + &dev_attr_mode.attr, + &dev_attr_booster.attr, + NULL, +}; + +static struct attribute_group host_notify_attr_grp = { + .attrs = host_notify_attrs, +}; + +void host_state_notify(struct host_notify_dev *ndev, int state) +{ + printk(KERN_INFO "host_notify: ndev name=%s: from state=%d -> to state=%d\n", + ndev->name, ndev->state, state); + if (ndev->state != state) { + ndev->state = state; + kobject_uevent(&ndev->dev->kobj, KOBJ_CHANGE); + } +} +EXPORT_SYMBOL_GPL(host_state_notify); + +static int +host_notify_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct host_notify_dev *ndev = (struct host_notify_dev *) + dev_get_drvdata(dev); + char *state; + + if (!ndev) { + /* this happens when the device is first created */ + return 0; + } + switch (ndev->state) { + case NOTIFY_HOST_ADD: + state = "ADD"; + break; + case NOTIFY_HOST_REMOVE: + state = "REMOVE"; + break; + case NOTIFY_HOST_OVERCURRENT: + state = "OVERCURRENT"; + break; + case NOTIFY_HOST_LOWBATT: + state = "LOWBATT"; + break; + case NOTIFY_HOST_UNKNOWN: + state = "UNKNOWN"; + break; + case NOTIFY_HOST_NONE: + default: + return 0; + } + if (add_uevent_var(env, "DEVNAME=%s", ndev->dev->kobj.name)) + return -ENOMEM; + if (add_uevent_var(env, "STATE=%s", state)) + return -ENOMEM; + return 0; +} + +static int create_notify_class(void) +{ + if (!host_notify.host_notify_class) { + host_notify.host_notify_class + = class_create(THIS_MODULE, "host_notify"); + if (IS_ERR(host_notify.host_notify_class)) + return PTR_ERR(host_notify.host_notify_class); + atomic_set(&host_notify.device_count, 0); + host_notify.host_notify_class->dev_uevent = host_notify_uevent; + } + + return 0; +} + +int host_notify_dev_register(struct host_notify_dev *ndev) +{ + int ret; + + if (!host_notify.host_notify_class) { + ret = create_notify_class(); + if (ret < 0) + return ret; + } + + ndev->index = atomic_inc_return(&host_notify.device_count); + ndev->dev = device_create(host_notify.host_notify_class, NULL, + MKDEV(0, ndev->index), NULL, ndev->name); + if (IS_ERR(ndev->dev)) + return PTR_ERR(ndev->dev); + + ret = sysfs_create_group(&ndev->dev->kobj, &host_notify_attr_grp); + if (ret < 0) { + device_destroy(host_notify.host_notify_class, + MKDEV(0, ndev->index)); + return ret; + } + + dev_set_drvdata(ndev->dev, ndev); + ndev->state = 0; + return 0; +} +EXPORT_SYMBOL_GPL(host_notify_dev_register); + +void host_notify_dev_unregister(struct host_notify_dev *ndev) +{ + ndev->state = NOTIFY_HOST_NONE; + sysfs_remove_group(&ndev->dev->kobj, &host_notify_attr_grp); + device_destroy(host_notify.host_notify_class, MKDEV(0, ndev->index)); + dev_set_drvdata(ndev->dev, NULL); +} +EXPORT_SYMBOL_GPL(host_notify_dev_unregister); + +static int __init notify_class_init(void) +{ + return create_notify_class(); +} + +static void __exit notify_class_exit(void) +{ + class_destroy(host_notify.host_notify_class); +} + +module_init(notify_class_init); +module_exit(notify_class_exit); + +MODULE_AUTHOR("Dongrak Shin <dongrak.shin@samsung.com>"); +MODULE_DESCRIPTION("Usb host notify driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/host_notify.h b/include/linux/host_notify.h new file mode 100755 index 0000000..1ea0cd3 --- /dev/null +++ b/include/linux/host_notify.h @@ -0,0 +1,53 @@ +/* + * Host notify class driver + * + * Copyright (C) 2011 Samsung, Inc. + * Author: Dongrak Shin <dongrak.shin@samsung.com> + * +*/ + +#ifndef __LINUX_HOST_NOTIFY_H__ +#define __LINUX_HOST_NOTIFY_H__ + +enum host_uevent_state { + NOTIFY_HOST_NONE, + NOTIFY_HOST_ADD, + NOTIFY_HOST_REMOVE, + NOTIFY_HOST_OVERCURRENT, + NOTIFY_HOST_LOWBATT, + NOTIFY_HOST_UNKNOWN, +}; + +enum otg_mode { + NOTIFY_NONE_MODE, + NOTIFY_HOST_MODE, + NOTIFY_PERIPHERAL_MODE, + NOTIFY_TEST_MODE, +}; + +enum booster_power{ + NOTIFY_POWER_OFF, + NOTIFY_POWER_ON, +}; + +enum set_command{ + NOTIFY_SET_OFF, + NOTIFY_SET_ON, +}; + +struct host_notify_dev { + const char *name; + struct device *dev; + int index; + int state; + int mode; + int booster; + void (*set_mode)(int); + void (*set_booster)(int); +}; + +extern void host_state_notify(struct host_notify_dev *ndev, int state); +extern int host_notify_dev_register(struct host_notify_dev *ndev); +extern void host_notify_dev_unregister(struct host_notify_dev *ndev); + +#endif /* __LINUX_HOST_NOTIFY_H__ */ diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 32ba8c5..f54e9ef 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -22,6 +22,7 @@ #ifdef __KERNEL__ #include <linux/rwsem.h> +#include <linux/host_notify.h> #define MAX_TOPO_LEVEL 6 @@ -175,6 +176,14 @@ struct usb_hcd { * input size of periodic table to an interrupt scheduler. * (ohci 32, uhci 1024, ehci 256/512/1024). */ +#ifdef CONFIG_USB_HOST_NOTIFY + struct host_notify_dev ndev; + int host_notify; +#endif +#ifdef CONFIG_USB_SEC_WHITELIST + int sec_whlist_table_num; +#endif + /* The HC driver's private data is stored at the end of * this structure. |