r583 - in trunk/src/target/kernel: . patches

laforge at sita.openmoko.org laforge at sita.openmoko.org
Mon Jan 22 20:29:07 CET 2007


Author: laforge
Date: 2007-01-22 20:29:05 +0100 (Mon, 22 Jan 2007)
New Revision: 583

Added:
   trunk/src/target/kernel/patches/
   trunk/src/target/kernel/patches/2.6.17-arm-usb_gadget-ether-link.patch
   trunk/src/target/kernel/patches/2.6.17-s3c2410-spi-mode23.patch
   trunk/src/target/kernel/patches/g_ether-highpower.patch
   trunk/src/target/kernel/patches/gta01-backlight.patch
   trunk/src/target/kernel/patches/gta01-core.patch
   trunk/src/target/kernel/patches/gta01-inputdevice.patch
   trunk/src/target/kernel/patches/gta01-jbt6k74.patch
   trunk/src/target/kernel/patches/gta01-pcf50606.patch
   trunk/src/target/kernel/patches/gta01-power_control.patch
   trunk/src/target/kernel/patches/gta01-s3c_mci-pdata.patch
   trunk/src/target/kernel/patches/gta01-vbus_draw.patch
   trunk/src/target/kernel/patches/gta01_vibrator.patch
   trunk/src/target/kernel/patches/mmc-fixdebugp.patch
   trunk/src/target/kernel/patches/mmc-fixdmalockup.patch
   trunk/src/target/kernel/patches/mmc-get_ro.patch
   trunk/src/target/kernel/patches/mmc-sdipre_limit.patch
   trunk/src/target/kernel/patches/mmc.patch
   trunk/src/target/kernel/patches/qt2410-base.patch
   trunk/src/target/kernel/patches/qt2410-biglcd.patch
   trunk/src/target/kernel/patches/qt2410-cs8900.patch
   trunk/src/target/kernel/patches/qt2410-s3c_mci-pdata.patch
   trunk/src/target/kernel/patches/qt2410-touchscreen.patch
   trunk/src/target/kernel/patches/s3c2410-bbt.patch
   trunk/src/target/kernel/patches/s3c2410_serial-nodebug.patch
   trunk/src/target/kernel/patches/s3c2410_udc-vbus_draw_pdata.patch
   trunk/src/target/kernel/patches/s3c_mci.patch
   trunk/src/target/kernel/patches/s3c_mci_platform.patch
   trunk/src/target/kernel/patches/s3cmci-dma-free.patch
   trunk/src/target/kernel/patches/s3cmci_dbg.patch
   trunk/src/target/kernel/patches/series
   trunk/src/target/kernel/patches/series.old
   trunk/src/target/kernel/patches/ts-debug.patch
   trunk/src/target/kernel/patches/ts0710.patch
   trunk/src/target/kernel/patches/udc-nomodule-misccr.patch
   trunk/src/target/kernel/patches/udc.patch
Log:
add patches to svn


Added: trunk/src/target/kernel/patches/2.6.17-arm-usb_gadget-ether-link.patch
===================================================================
--- trunk/src/target/kernel/patches/2.6.17-arm-usb_gadget-ether-link.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/2.6.17-arm-usb_gadget-ether-link.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,23 @@
+diff --exclude patches --exclude .pc -Nru linux-2.6.17.7-fic1/drivers/usb/gadget/ether.c linux-2.6.17.7-new/drivers/usb/gadget/ether.c
+--- linux-2.6.17.7-fic1/drivers/usb/gadget/ether.c	2006-07-25 05:36:01.000000000 +0200
++++ linux-2.6.17.7-new/drivers/usb/gadget/ether.c	2006-10-27 16:12:27.000000000 +0200
+@@ -2132,7 +2132,7 @@
+ }
+ 
+ 
+-static void __exit
++static void 
+ eth_unbind (struct usb_gadget *gadget)
+ {
+ 	struct eth_dev		*dev = get_gadget_data (gadget);
+@@ -2190,7 +2190,8 @@
+ 	return 1;
+ }
+ 
+-static int __init
++//static int __init
++static int
+ eth_bind (struct usb_gadget *gadget)
+ {
+ 	struct eth_dev		*dev;
+

Added: trunk/src/target/kernel/patches/2.6.17-s3c2410-spi-mode23.patch
===================================================================
--- trunk/src/target/kernel/patches/2.6.17-s3c2410-spi-mode23.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/2.6.17-s3c2410-spi-mode23.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,33 @@
+diff --exclude patches --exclude .pc -Nru linux-2.6.17.7-fic1/drivers/spi/spi_s3c24xx_gpio.c linux-2.6.17.7-new/drivers/spi/spi_s3c24xx_gpio.c
+--- linux-2.6.17.7-fic1/drivers/spi/spi_s3c24xx_gpio.c	2006-07-25 05:36:01.000000000 +0200
++++ linux-2.6.17.7-new/drivers/spi/spi_s3c24xx_gpio.c	2006-10-28 16:53:36.000000000 +0200
+@@ -73,6 +73,19 @@
+ 	return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits);
+ }
+ 
++static u32 s3c2410_spigpio_txrx_mode2(struct spi_device *spi,
++				      unsigned nsecs, u32 word, u8 bits)
++{
++	return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits);
++}
++
++static u32 s3c2410_spigpio_txrx_mode3(struct spi_device *spi,
++				      unsigned nsecs, u32 word, u8 bits)
++{
++	return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits);
++}
++
++
+ static void s3c2410_spigpio_chipselect(struct spi_device *dev, int value)
+ {
+ 	struct s3c2410_spigpio *sg = spidev_to_sg(dev);
+@@ -108,6 +121,8 @@
+ 
+ 	sp->bitbang.txrx_word[SPI_MODE_0] = s3c2410_spigpio_txrx_mode0;
+ 	sp->bitbang.txrx_word[SPI_MODE_1] = s3c2410_spigpio_txrx_mode1;
++	sp->bitbang.txrx_word[SPI_MODE_2] = s3c2410_spigpio_txrx_mode2;
++	sp->bitbang.txrx_word[SPI_MODE_3] = s3c2410_spigpio_txrx_mode3;
+ 
+ 	/* set state of spi pins */
+ 	s3c2410_gpio_setpin(sp->info->pin_clk, 0);
+

Added: trunk/src/target/kernel/patches/g_ether-highpower.patch
===================================================================
--- trunk/src/target/kernel/patches/g_ether-highpower.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/g_ether-highpower.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,22 @@
+Index: linux-2.6.17.14-fic4.test/drivers/usb/gadget/ether.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/usb/gadget/ether.c	2007-01-21 21:31:48.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/usb/gadget/ether.c	2007-01-21 21:34:31.000000000 +0100
+@@ -462,7 +462,7 @@
+ 	.bConfigurationValue =	DEV_CONFIG_VALUE,
+ 	.iConfiguration =	STRING_CDC,
+ 	.bmAttributes =		USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+-	.bMaxPower =		50,
++	.bMaxPower =		250,
+ };
+ 
+ #ifdef	CONFIG_USB_ETH_RNDIS
+@@ -476,7 +476,7 @@
+ 	.bConfigurationValue =  DEV_RNDIS_CONFIG_VALUE,
+ 	.iConfiguration =       STRING_RNDIS,
+ 	.bmAttributes =		USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+-	.bMaxPower =            50,
++	.bMaxPower =            250,
+ };
+ #endif
+ 

Added: trunk/src/target/kernel/patches/gta01-backlight.patch
===================================================================
--- trunk/src/target/kernel/patches/gta01-backlight.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/gta01-backlight.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,312 @@
+This is a backlight driver for FIC's Neo1973 Phone (codename GTA01)
+
+Index: linux-2.6.17.14-fic4.test/drivers/video/backlight/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/video/backlight/Kconfig	2007-01-15 02:29:56.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/video/backlight/Kconfig	2007-01-15 02:40:37.000000000 +0100
+@@ -50,6 +50,14 @@
+ 	  If you have a Sharp Zaurus SL-C7xx, SL-Cxx00 or SL-6000x say y to enable the
+ 	  backlight driver.
+ 
++config BACKLIGHT_GTA01
++	tristate "FIC GTA01 Backlight Driver"
++	depends on BACKLIGHT_DEVICE && MACH_GTA01
++	default y
++	help
++	  If you have a FIC GTA01 say y to enable the backlight driver.
++
++
+ config BACKLIGHT_HP680
+ 	tristate "HP Jornada 680 Backlight Driver"
+ 	depends on BACKLIGHT_DEVICE && SH_HP6XX
+Index: linux-2.6.17.14-fic4.test/drivers/video/backlight/Makefile
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/video/backlight/Makefile	2007-01-15 02:29:56.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/video/backlight/Makefile	2007-01-15 02:40:37.000000000 +0100
+@@ -3,5 +3,6 @@
+ obj-$(CONFIG_LCD_CLASS_DEVICE)     += lcd.o
+ obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
+ obj-$(CONFIG_BACKLIGHT_CORGI)	+= corgi_bl.o
++obj-$(CONFIG_BACKLIGHT_GTA01)	+= gta01_bl.o
+ obj-$(CONFIG_BACKLIGHT_HP680)	+= hp680_bl.o
+ obj-$(CONFIG_SHARP_LOCOMO)	+= locomolcd.o
+Index: linux-2.6.17.14-fic4.test/drivers/video/backlight/gta01_bl.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/drivers/video/backlight/gta01_bl.c	2007-01-21 17:10:38.000000000 +0100
+@@ -0,0 +1,275 @@
++/*
++ *  Backlight Driver for FIC GTA01 (Neo1973) GSM Phone
++ *
++ * Copyright (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ *  based on corgi_cl.c, Copyright (c) 2004-2006 Richard Purdie
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation, version 2.
++ *
++ * 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
++ *
++ * TODO: implement PWM, instead of simple on/off switching
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/mutex.h>
++#include <linux/fb.h>
++#include <linux/backlight.h>
++#include <linux/clk.h>
++
++#include <asm/arch/hardware.h>
++#include <asm/arch/regs-timer.h>
++#include <asm/arch/gta01.h>
++
++static struct backlight_properties gta01bl_prop;
++static struct backlight_device *gta01_backlight_device;
++static struct gta01bl_machinfo *bl_machinfo;
++
++static unsigned long gta01bl_flags;
++
++struct gta01bl_data {
++	int intensity;
++	struct mutex mutex;
++	struct clk *clk;
++};
++
++static struct gta01bl_data gta01bl;
++
++#define GTA01BL_SUSPENDED     0x01
++#define GTA01BL_BATTLOW       0x02
++
++#define GTA01BL_FREQ	400
++
++/* On the GTA01 / Neo1973, we use a 50 or 66MHz PCLK, which gives
++ * us a 6.25..8.25MHz DIV8 clock, which is further divided by a
++ * prescaler of 4, resulting in a 1.56..2.06MHz tick.  This results in a
++ * minimum frequency of 24..31Hz.  At 400Hz, we need to set the count
++ * to something like 3906..5156, providing us a way sufficient resolution
++ * for display brightness adjustment. */
++
++static int gta01bl_send_intensity(struct backlight_device *bd)
++{
++	int intensity = bd->props->brightness;
++
++	if (bd->props->power != FB_BLANK_UNBLANK)
++		intensity = 0;
++	if (bd->props->fb_blank != FB_BLANK_UNBLANK)
++		intensity = 0;
++	if (gta01bl_flags & GTA01BL_SUSPENDED)
++		intensity = 0;
++	if (gta01bl_flags & GTA01BL_BATTLOW)
++		intensity &= bl_machinfo->limit_mask;
++
++ 	mutex_lock(&gta01bl.mutex);
++#ifdef GTA01_BACKLIGHT_ONOFF_ONLY
++	if (intensity)
++		s3c2410_gpio_setpin(GTA01_GPIO_BACKLIGHT, 1);
++	else
++		s3c2410_gpio_setpin(GTA01_GPIO_BACKLIGHT, 0);
++#else
++	if (intensity == bd->props->max_brightness) {
++		s3c2410_gpio_setpin(GTA01_GPIO_BACKLIGHT, 1);
++		s3c2410_gpio_cfgpin(GTA01_GPIO_BACKLIGHT, S3C2410_GPIO_OUTPUT);
++	} else  {
++		__raw_writel(intensity & 0xffff, S3C2410_TCMPB(0));
++		s3c2410_gpio_cfgpin(GTA01_GPIO_BACKLIGHT, S3C2410_GPB0_TOUT0);
++	}
++#endif
++	mutex_unlock(&gta01bl.mutex);
++
++	gta01bl.intensity = intensity;
++
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int gta01bl_suspend(struct platform_device *dev, pm_message_t state)
++{
++	gta01bl_flags |= GTA01BL_SUSPENDED;
++	gta01bl_send_intensity(gta01_backlight_device);
++	return 0;
++}
++
++static int gta01bl_resume(struct platform_device *dev)
++{
++	gta01bl_flags &= ~GTA01BL_SUSPENDED;
++	gta01bl_send_intensity(gta01_backlight_device);
++	return 0;
++}
++#else
++#define gta01bl_suspend	NULL
++#define gta01bl_resume	NULL
++#endif
++
++static int gta01bl_get_intensity(struct backlight_device *bd)
++{
++	return gta01bl.intensity;
++}
++
++static int gta01bl_set_intensity(struct backlight_device *bd)
++{
++	gta01bl_send_intensity(gta01_backlight_device);
++	return 0;
++}
++
++/*
++ * Called when the battery is low to limit the backlight intensity.
++ * If limit==0 clear any limit, otherwise limit the intensity
++ */
++void gta01bl_limit_intensity(int limit)
++{
++	if (limit)
++		gta01bl_flags |= GTA01BL_BATTLOW;
++	else
++		gta01bl_flags &= ~GTA01BL_BATTLOW;
++	gta01bl_send_intensity(gta01_backlight_device);
++}
++EXPORT_SYMBOL(gta01bl_limit_intensity);
++
++
++static struct backlight_properties gta01bl_prop = {
++	.owner          = THIS_MODULE,
++	.get_brightness = gta01bl_get_intensity,
++	.update_status  = gta01bl_set_intensity,
++};
++
++static int __init gta01bl_probe(struct platform_device *pdev)
++{
++	struct gta01bl_machinfo *machinfo = pdev->dev.platform_data;
++
++#ifdef GTA01_BACKLIGHT_ONOFF_ONLY
++	s3c2410_gpio_cfgpin(GTA01_GPIO_BACKLIGHT, S3C2410_GPIO_OUTPUT);
++	gta01bl_prop.max_brightness = 1;
++#else
++	unsigned long tcon, tcfg0, tcfg1, tcnt, pclk;
++
++	/* use s3c_device_timer0 for PWM */
++	gta01bl.clk = clk_get(NULL, "timers");
++	if (IS_ERR(gta01bl.clk))
++		return PTR_ERR(gta01bl.clk);
++
++	clk_enable(gta01bl.clk);
++
++	pclk = clk_get_rate(gta01bl.clk);
++
++	tcon = __raw_readl(S3C2410_TCON);
++	tcfg1 = __raw_readl(S3C2410_TCFG1);
++	tcfg0 = __raw_readl(S3C2410_TCFG0);
++
++	tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;
++	tcfg1 |= S3C2410_TCFG1_MUX0_DIV8;
++
++	tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
++	tcfg0 |= (4 - 1);
++
++	tcnt = (pclk / 32) / GTA01BL_FREQ;
++	tcnt--;
++
++	__raw_writel(tcfg1, S3C2410_TCFG1);
++	__raw_writel(tcfg0, S3C2410_TCFG0);
++
++	__raw_writel(tcnt, S3C2410_TCNTB(0));
++	__raw_writel(tcon, S3C2410_TCON);
++	__raw_writel(tcnt, S3C2410_TCNTB(0));
++
++	/* ensure timer is stopped */
++
++	tcon &= 0xffffff00;
++	tcon |= S3C2410_TCON_T0RELOAD;
++	tcon |= S3C2410_TCON_T0MANUALUPD;
++
++	__raw_writel(tcon, S3C2410_TCON);
++	__raw_writel(tcnt, S3C2410_TCNTB(0));
++	__raw_writel(tcnt, S3C2410_TCMPB(0));
++
++	/* start the timer */
++	tcon |= S3C2410_TCON_T0START;
++	tcon &= ~S3C2410_TCON_T0MANUALUPD;
++	__raw_writel(tcon, S3C2410_TCON);
++
++	gta01bl_prop.max_brightness = tcnt;
++#endif
++	mutex_init(&gta01bl.mutex);
++
++	if (!machinfo->limit_mask)
++		machinfo->limit_mask = -1;
++
++	gta01_backlight_device = backlight_device_register("gta01-bl", NULL,
++							   &gta01bl_prop);
++	if (IS_ERR(gta01_backlight_device))
++		return PTR_ERR(gta01_backlight_device);
++
++	gta01bl_prop.power = FB_BLANK_UNBLANK;
++	gta01bl_prop.brightness = machinfo->default_intensity;
++	gta01bl_send_intensity(gta01_backlight_device);
++
++	printk("GTA01 Backlight Driver Initialized.\n");
++	return 0;
++}
++
++static int gta01bl_remove(struct platform_device *dev)
++{
++#ifndef GTA01_BACKLIGHT_ONOFF_ONLY
++	unsigned long tcon;
++
++	/* stop this timer */
++	tcon = __raw_readl(S3C2410_TCON);
++	tcon &= 0xffffff00;
++	__raw_writel(tcon, S3C2410_TCON);
++
++	clk_disable(gta01bl.clk);
++	clk_put(gta01bl.clk);
++#endif
++	backlight_device_unregister(gta01_backlight_device);
++	mutex_destroy(&gta01bl.mutex);
++
++	printk("GTA01 Backlight Driver Unloaded, constant backlight\n");
++	s3c2410_gpio_cfgpin(GTA01_GPIO_BACKLIGHT, S3C2410_GPIO_OUTPUT);
++	s3c2410_gpio_setpin(GTA01_GPIO_BACKLIGHT, 1);
++
++	return 0;
++}
++
++static struct platform_driver gta01bl_driver = {
++	.probe		= gta01bl_probe,
++	.remove		= gta01bl_remove,
++	.suspend	= gta01bl_suspend,
++	.resume		= gta01bl_resume,
++	.driver		= {
++		.name	= "gta01-bl",
++	},
++};
++
++static int __init gta01bl_init(void)
++{
++	return platform_driver_register(&gta01bl_driver);
++}
++
++static void __exit gta01bl_exit(void)
++{
++	platform_driver_unregister(&gta01bl_driver);
++}
++
++module_init(gta01bl_init);
++module_exit(gta01bl_exit);
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("FIC GTA01 (Neo1973) Backlight Driver");
++MODULE_LICENSE("GPL");

Added: trunk/src/target/kernel/patches/gta01-core.patch
===================================================================
--- trunk/src/target/kernel/patches/gta01-core.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/gta01-core.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,597 @@
+This patch adds support for the FIC GTA01 machine type to the ARM port of
+the linux kernel.
+
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/Kconfig	2007-01-08 22:46:57.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/Kconfig	2007-01-08 22:47:27.000000000 +0100
+@@ -69,6 +69,13 @@
+ 	help
+ 	   Say Y here if you are using the Armzone QT2410
+ 
++config MACH_GTA01
++	bool "GTA01"
++	select CPU_S3C2410
++	help
++	   Say Y here if you are using the FIC GTA01
++
++
+ config ARCH_S3C2440
+ 	bool "SMDK2440"
+ 	select CPU_S3C2440
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/Makefile
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/Makefile	2007-01-08 22:46:57.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/Makefile	2007-01-08 22:47:27.000000000 +0100
+@@ -44,6 +44,7 @@
+ obj-$(CONFIG_MACH_N30)		+= mach-n30.o
+ obj-$(CONFIG_ARCH_SMDK2410)	+= mach-smdk2410.o
+ obj-$(CONFIG_MACH_QT2410)	+= mach-qt2410.o
++obj-$(CONFIG_MACH_GTA01)	+= mach-gta01.o
+ obj-$(CONFIG_ARCH_S3C2440)	+= mach-smdk2440.o
+ obj-$(CONFIG_MACH_VR1000)	+= mach-vr1000.o usb-simtec.o
+ obj-$(CONFIG_MACH_RX3715)	+= mach-rx3715.o
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-gta01.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-gta01.c	2007-01-10 01:10:54.000000000 +0100
+@@ -0,0 +1,490 @@
++/*
++ * linux/arch/arm/mach-s3c2410/mach-gta01.c
++ *
++ * S3C2410 Machine Support for the FIC GTA01 (Neo1973)
++ *
++ * Copyright (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; 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 <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/list.h>
++#include <linux/timer.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/spi_bitbang.h>
++#include <linux/mmc/protocol.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/mach/arch.h>
++#include <asm/mach/map.h>
++#include <asm/mach/irq.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++
++#include <asm/arch/regs-serial.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/fb.h>
++#include <asm/arch/udc.h>
++#include <asm/arch/nand.h>
++#include <asm/arch/ts.h>
++#include <asm/arch/spi.h>
++#include <asm/arch/spi-gpio.h>
++
++#include <asm/arch/gta01.h>
++
++#include "devs.h"
++#include "cpu.h"
++
++#include "pm.h"
++
++static struct map_desc gta01_iodesc[] __initdata = {
++	IODESC_ENT(SDI),
++	IODESC_ENT(IIS),
++	{ 0xe0000000, __phys_to_pfn(S3C2410_CS3+0x01000000), SZ_1M, MT_DEVICE }
++};
++
++#define UCON S3C2410_UCON_DEFAULT
++#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
++#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
++
++static struct s3c2410_uartcfg gta01_uartcfgs[] = {
++	[0] = {
++		.hwport	     = 0,
++		.flags	     = 0,
++		.ucon	     = UCON,
++		.ulcon	     = ULCON,
++		.ufcon	     = UFCON,
++	},
++	[1] = {
++		.hwport	     = 1,
++		.flags	     = 0,
++		.ucon	     = UCON,
++		.ulcon	     = ULCON,
++		.ufcon	     = UFCON,
++	},
++#if 0
++	[2] = {
++		.hwport	     = 2,
++		.flags	     = 0,
++		.ucon	     = UCON,
++		.ulcon	     = ULCON,
++		.ufcon	     = UFCON,
++	}
++#endif
++};
++
++/* LCD driver info */
++
++
++/* Configuration for 480x640 toppoly TD028TTEC1 */
++static struct s3c2410fb_mach_info gta01_lcd_cfg __initdata = {
++	.regs	= {
++
++		.lcdcon1	= S3C2410_LCDCON1_TFT16BPP |
++				  S3C2410_LCDCON1_TFT |
++				  S3C2410_LCDCON1_CLKVAL(0x01),	/* HCLK/4 */
++
++		.lcdcon2	= S3C2410_LCDCON2_VBPD(1) |	/* 2 */
++				  S3C2410_LCDCON2_LINEVAL(639) |/* 640 */
++				  S3C2410_LCDCON2_VFPD(15) |	/* 16 */
++				  S3C2410_LCDCON2_VSPW(1),	/* 2 */
++
++		.lcdcon3	= S3C2410_LCDCON3_HBPD(7) |	/* 8 */
++				  S3C2410_LCDCON3_HOZVAL(479) |	/* 480 */
++				  S3C2410_LCDCON3_HFPD(103),	/* 104 */
++
++		.lcdcon4	= S3C2410_LCDCON4_MVAL(0) |
++				  S3C2410_LCDCON4_HSPW(7),	/* 8 */
++
++		.lcdcon5	= S3C2410_LCDCON5_FRM565 |
++				  S3C2410_LCDCON5_INVVLINE |
++				  S3C2410_LCDCON5_INVVFRAME |
++				  S3C2410_LCDCON5_PWREN |
++				  S3C2410_LCDCON5_HWSWP,
++	},
++
++#if 0
++	/* currently setup by downloader */
++	.gpccon		= 0xaa940659,
++	.gpccon_mask	= 0xffffffff,
++	.gpcup		= 0x0000ffff,
++	.gpcup_mask	= 0xffffffff,
++	.gpdcon		= 0xaa84aaa0,
++	.gpdcon_mask	= 0xffffffff,
++	.gpdup		= 0x0000faff,
++	.gpdup_mask	= 0xffffffff,
++#endif
++
++	.lpcsel		= ((0xCE6) & ~7) | 1<<4,
++
++	.width		= 480,
++	.height		= 640,
++
++	.xres		= {
++		.min	= 480,
++		.max	= 480,
++		.defval	= 480,
++	},
++
++	.yres		= {
++		.min	= 640,
++		.max	= 640,
++		.defval = 640,
++	},
++
++	.bpp		= {
++		.min	= 16,
++		.max	= 16,
++		.defval = 16,
++	},
++};
++static struct platform_device *gta01_devices[] __initdata = {
++	&s3c_device_usb,
++	&s3c_device_lcd,
++	&s3c_device_wdt,
++	&s3c_device_i2c,
++	&s3c_device_iis,
++	&s3c_device_sdi,
++	&s3c_device_usbgadget,
++	&s3c_device_nand,
++	&s3c_device_ts,
++};
++
++static struct s3c24xx_board gta01_board __initdata = {
++	.devices       = gta01_devices,
++	.devices_count = ARRAY_SIZE(gta01_devices)
++};
++
++static struct mtd_partition gta01_nand_part[] = {
++	[0] = {
++		.name	= "U-Boot",
++		.size	= 0x30000,
++		.offset	= 0,
++	},
++	[1] = {
++		.name	= "U-Boot environment",
++		.offset = 0x30000,
++		.size	= 0x4000,
++	},
++	[2] = {
++		.name	= "kernel",
++		.offset = 0x34000,
++		.size	= SZ_2M,
++	},
++	[3] = {
++		.name	= "initrd",
++		.offset	= 0x234000,
++		.size	= SZ_4M,
++	},
++	[4] = {
++		.name	= "jffs2",
++		.offset = 0x634000,
++		.size	= 0x39cc000,
++	},
++};
++
++static struct s3c2410_nand_set gta01_nand_sets[] = {
++	[0] = {
++		.name		= "NAND",
++		.nr_chips	= 1,
++		.nr_partitions	= ARRAY_SIZE(gta01_nand_part),
++		.partitions	= gta01_nand_part,
++	},
++};
++
++/* choose a set of timings which should suit most 512Mbit
++ * chips and beyond.
++ */
++
++static struct s3c2410_platform_nand gta01_nand_info = {
++	.tacls		= 20,
++	.twrph0		= 60,
++	.twrph1		= 20,
++	.nr_sets	= ARRAY_SIZE(gta01_nand_sets),
++	.sets		= gta01_nand_sets,
++};
++
++#if 0
++static unsigned int mmc_millivolts[] = {
++	[MMC_VDD_145_150]	= 1500,
++	[MMC_VDD_150_155]	= 1500,
++	[MMC_VDD_155_160]	= 1600,
++	[MMC_VDD_160_165]	= 1600,
++	[MMC_VDD_165_170]	= 1700,
++	[MMC_VDD_17_18]		= 1800,
++	[MMC_VDD_18_19]		= 1900,
++	[MMC_VDD_19_20]		= 2000,
++	[MMC_VDD_20_21]		= 2100,
++	[MMC_VDD_21_22]		= 2200,
++	[MMC_VDD_22_23]		= 2300,
++	[MMC_VDD_23_24]		= 2400,
++	[MMC_VDD_24_25]		= 2500,
++	[MMC_VDD_25_26]		= 2600,
++	[MMC_VDD_26_27]		= 2700,
++	[MMC_VDD_27_28]		= 2800,
++	[MMC_VDD_28_29]		= 2900,
++	[MMC_VDD_29_30]		= 3000,
++	[MMC_VDD_30_31]		= 3100,
++	[MMC_VDD_31_32]		= 3200,
++	[MMC_VDD_32_33]		= 3300,
++};
++
++static void gta01_mmc_set_power(unsigned int to)
++{
++	/* FIXME: configure D2REG of PCF50606 using D2REGC1 */
++	//pcf50606_voltage_set(mmc_millivolts[to]);
++}
++
++static struct s3c24xx_mmc_platdata gta01_mmc_cfg = {
++	.gpio_detect	= S3C2410_GPF5,
++	.set_power	= &gta01_mmc_set_power,
++	.ocr_avail	= MMC_VDD_145_150|MMC_VDD_150_155|MMC_VDD_155_160|
++			  MMC_VDD_160_165| MMC_VDD_165_170|MMC_VDD_17_18|
++			  MMC_VDD_18_19|MMC_VDD_19_20|MMC_VDD_20_21|
++			  MMC_VDD_21_22|MMC_VDD_22_23|MMC_VDD_23_24|
++			  MMC_VDD_24_25|MMC_VDD_25_26|MMC_VDD_26_27|
++			  MMC_VDD_27_28|MMC_VDD_28_29|MMC_VDD_29_30|
++			  MMC_VDD_30_31|MMC_VDD_31_32|MMC_VDD_32_33,
++};
++#endif
++
++static void gta01_udc_pullup(enum s3c2410_udc_cmd_e cmd)
++{
++	printk(KERN_DEBUG "udc: pullup(%d)\n", cmd);
++
++	if (system_rev == GTA01v4_SYSTEM_REV ||
++	    system_rev == GTA01Bv2_SYSTEM_REV) {
++		switch (cmd) {
++		case S3C2410_UDC_P_ENABLE:
++			s3c2410_gpio_setpin(GTA01_GPIO_USB_PULLUP, 1);
++			break;
++		case S3C2410_UDC_P_DISABLE:
++			s3c2410_gpio_setpin(GTA01_GPIO_USB_PULLUP, 0);
++			break;
++		case S3C2410_UDC_P_RESET:
++			/* FIXME! */
++			break;
++		default:
++			break;
++		}
++	}
++}
++
++static struct s3c2410_udc_mach_info gta01_udc_cfg = {
++	.udc_command	= gta01_udc_pullup,
++};
++
++static struct s3c2410_ts_mach_info gta01_ts_cfg = {
++	.delay = 10000,
++	.presc = 49,
++	.oversampling_shift = 2,
++};
++
++/* SPI */
++
++static struct spi_board_info gta01_spi_board_info[] __initdata = {
++	{
++		.modalias	= "jbt6k74",
++		/* platform_data */
++		/* controller_data */
++		/* irq */
++		.max_speed_hz	= 10 * 1000 * 1000,
++		.bus_num	= 1,
++		/* chip_select */
++	},
++};
++
++
++#ifdef SPI_HARD
++static struct s3c2410_spi_info spi_cfg = {
++	.pin_cs		= S3C2410_GPG3,
++	.board_size	= sizeof(&gta01_spi_board_info),
++	.board_info	= &gta01_spi_board_info,
++};
++#else
++static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int cs)
++{
++	switch (cs) {
++	case BITBANG_CS_ACTIVE:
++		s3c2410_gpio_setpin(S3C2410_GPG3, 0);
++		break;
++	case BITBANG_CS_INACTIVE:
++		s3c2410_gpio_setpin(S3C2410_GPG3, 1);
++		break;
++	}
++}
++
++static struct s3c2410_spigpio_info spi_gpio_cfg = {
++	.pin_clk	= S3C2410_GPG7,
++	.pin_mosi	= S3C2410_GPG6,
++	.pin_miso	= S3C2410_GPG5,
++	.board_size	= ARRAY_SIZE(gta01_spi_board_info),
++	.board_info	= &gta01_spi_board_info,
++	.chip_select	= &spi_gpio_cs,
++};
++
++static struct resource s3c_spi_lcm_resource[] = {
++	[0] = {
++		.start = S3C2410_GPG3,
++		.end   = S3C2410_GPG3,
++		.flags = IORESOURCE_MEM,
++	},
++	[1] = {
++		.start = S3C2410_GPG5,
++		.end   = S3C2410_GPG5,
++		.flags = IORESOURCE_MEM,
++	},
++	[2] = {
++		.start = S3C2410_GPG6,
++		.end   = S3C2410_GPG6,
++		.flags = IORESOURCE_MEM,
++	},
++	[3] = {
++		.start = S3C2410_GPG7,
++		.end   = S3C2410_GPG7,
++		.flags = IORESOURCE_MEM,
++	},
++};
++
++struct platform_device s3c_device_spi_lcm = {
++	.name		  = "s3c24xx-spi-gpio",
++	.id		  = 1,
++	.num_resources	  = ARRAY_SIZE(s3c_spi_lcm_resource),
++	.resource	  = s3c_spi_lcm_resource,
++	.dev = {
++		.platform_data = &spi_gpio_cfg,
++	},
++};
++#endif
++
++static struct gta01bl_machinfo backlight_machinfo = {
++	.default_intensity	= 1,
++	.max_intensity		= 1,
++	.limit_mask		= 1,
++};
++
++static struct resource gta01_bl_resources[] = {
++	[0] = {
++		.start	= GTA01_GPIO_BACKLIGHT,
++		.end	= GTA01_GPIO_BACKLIGHT,
++		.flags	= IORESOURCE_MEM,
++	},
++};
++
++struct platform_device gta01_bl_dev = {
++	.name 		= "gta01-bl",
++	.num_resources	= ARRAY_SIZE(gta01_bl_resources),
++	.resource	= gta01_bl_resources,
++	.dev		= {
++		.platform_data = &backlight_machinfo,
++	},
++};
++
++static struct resource gta01_button_resources[] = {
++	[0] = {
++		.start = GTA01_GPIO_911_KEY,
++		.end   = GTA01_GPIO_911_KEY,
++		.flags = IORESOURCE_MEM,
++	},
++};
++
++struct platform_device gta01_button_dev = {
++	.name		="gta01-button",
++	.num_resources	= ARRAY_SIZE(gta01_button_resources),
++	.resource	= gta01_button_resources,
++};
++
++struct platform_device gta01_pm_gsm_dev = {
++	.name		="gta01-pm-gsm",
++};
++
++struct platform_device gta01_pm_gps_dev = {
++	.name		="gta01-pm-gps",
++};
++
++struct platform_device gta01_pm_bt_dev = {
++	.name		="gta01-pm-bt",
++};
++
++static void __init gta01_map_io(void)
++{
++	s3c24xx_init_io(gta01_iodesc, ARRAY_SIZE(gta01_iodesc));
++	s3c24xx_init_clocks(12*1000*1000);
++	s3c24xx_init_uarts(gta01_uartcfgs, ARRAY_SIZE(gta01_uartcfgs));
++	s3c24xx_set_board(&gta01_board);
++}
++
++static void __init gta01_machine_init(void)
++{
++	s3c_device_nand.dev.platform_data = &gta01_nand_info;
++	//s3c_device_sdi.dev.platform_data = &gta01_mmc_cfg;
++
++	s3c24xx_fb_set_platdata(&gta01_lcd_cfg);
++
++	s3c24xx_udc_set_platdata(&gta01_udc_cfg);
++	set_s3c2410ts_info(&gta01_ts_cfg);
++
++	/* Set LCD_RESET / XRES to high */
++	s3c2410_gpio_cfgpin(S3C2410_GPC6, S3C2410_GPIO_OUTPUT);
++	s3c2410_gpio_setpin(S3C2410_GPC6, 1);
++
++#ifdef SPI_HARD
++#else
++	/* SPI chip select is gpio output */
++	s3c2410_gpio_cfgpin(S3C2410_GPG3, S3C2410_GPIO_OUTPUT);
++
++	spi_register_board_info(gta01_spi_board_info,
++				ARRAY_SIZE(gta01_spi_board_info));
++	platform_device_register(&s3c_device_spi_lcm);
++#endif
++	platform_device_register(&gta01_bl_dev);
++	platform_device_register(&gta01_button_dev);
++	platform_device_register(&gta01_pm_gsm_dev);
++	platform_device_register(&gta01_pm_gps_dev);
++
++	if (system_rev == GTA01Bv2_SYSTEM_REV)
++		platform_device_register(&gta01_pm_bt_dev);
++
++	s3c2410_pm_init();
++
++}
++
++MACHINE_START(GTA01, "GTA01")
++	.phys_io	= S3C2410_PA_UART,
++	.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
++	.boot_params	= S3C2410_SDRAM_PA + 0x100,
++	.map_io		= gta01_map_io,
++	.init_irq	= s3c24xx_init_irq,
++	.init_machine	= gta01_machine_init,
++	.timer		= &s3c24xx_timer,
++MACHINE_END
++
++
+Index: linux-2.6.17.14-fic4.test/arch/arm/tools/mach-types
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/tools/mach-types	2007-01-08 22:46:57.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/tools/mach-types	2007-01-08 22:47:27.000000000 +0100
+@@ -1041,3 +1041,4 @@
+ ixp465			MACH_IXP465		IXP465			1028
+ balloon3		MACH_BALLOON3		BALLOON3		1029
+ qt2410			MACH_QT2410		QT2410			1108
++gta01			MACH_GTA01		GTA01			1182
+Index: linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/gta01.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/gta01.h	2007-01-09 02:07:15.000000000 +0100
+@@ -0,0 +1,55 @@
++#ifndef _GTA01_H
++#define _GTA01_H
++
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/irqs.h>
++
++/* Different hardware revisions, passed in ATAG_REVISION by u-boot */
++#define GTA01v3_SYSTEM_REV	0x00000130
++#define GTA01v4_SYSTEM_REV	0x00000140
++#define GTA01Bv2_SYSTEM_REV	0x00000220
++
++/* Backlight */
++struct gta01bl_machinfo {
++	unsigned int default_intensity;
++	unsigned int max_intensity;
++	unsigned int limit_mask;
++};
++
++/* Definitions common to all revisions */
++#define GTA01_GPIO_BACKLIGHT	S3C2410_GPB0
++#define GTA01_GPIO_GPS_PWRON	S3C2410_GPB1
++#define GTA01_GPIO_MODEM_RST	S3C2410_GPB6
++#define GTA01_GPIO_MODEM_ON	S3C2410_GPB7
++#define GTA01_GPIO_LCD_RESET	S3C2410_GPC6
++#define GTA01_GPIO_JACK_INSERT	S3C2410_GPF4
++#define GTA01_GPIO_nSD_DETECT	S3C2410_GPF5
++#define GTA01_GPIO_911_KEY	S3C2410_GPF6
++#define GTA01_GPIO_VIBRATOR_ON	S3C2410_GPG11
++
++#define GTA01_IRQ_JACK_INSERT	IRQ_EINT4
++#define GTA01_IRQ_nSD_DETECT	IRQ_EINT5
++#define GTA01_IRQ_911_KEY	IRQ_EINT6
++#define GTA01_IRQ_PCF50606      IRQ_EINT16
++
++/* GTA01v3 */
++#define GTA01v3_GPIO_nGSM_EN	S3C2410_GPG9
++
++/* GTA01v4 */
++#define GTA01_GPIO_MODEM_DNLOAD	S3C2410_GPG0
++
++/* GTA01Bv2 */
++#define GTA01Bv2_GPIO_nGSM_EN	S3C2410_GPF2
++/* #define GTA01_GPIO_MODEM_DNLOAD	S3C2410_GPG0 */
++
++#define GTA01_GPIO_SDMMC_ON	S3C2410_GPB2
++#define GTA01_GPIO_BT_EN	S3C2410_GPB5
++#define GTA01_GPIO_AB_DETECT	S3C2410_GPB8
++#define GTA01_GPIO_USB_PULLUP	S3C2410_GPB9
++#define GTA01_GPIO_USB_ATTACH	S3C2410_GPB10
++
++#define GTA01_GPIO_GPS_EN_2V8	S3C2410_GPG9
++#define GTA01_GPIO_GPS_EN_3V	S3C2410_GPG10
++#define GTA01_GPIO_GPS_RESET	S3C2410_GPC0
++
++#endif /* _GTA01_H */

Added: trunk/src/target/kernel/patches/gta01-inputdevice.patch
===================================================================
--- trunk/src/target/kernel/patches/gta01-inputdevice.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/gta01-inputdevice.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,492 @@
+This provides support for the GTA01 keyboard
+
+Index: linux-2.6.17.14-fic4.test/drivers/input/keyboard/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/input/keyboard/Kconfig	2007-01-08 21:34:30.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/input/keyboard/Kconfig	2007-01-08 22:51:40.000000000 +0100
+@@ -183,4 +183,16 @@
+ 	  This driver implements support for HIL-keyboards attached
+ 	  to your machine, so normally you should say Y here.
+ 
++config KEYBOARD_GTA01
++	tristate "GTA01 buttons"
++	depends on MACH_GTA01
++	default y
++	help
++	  Say Y here to enable the buttons on the FIC Neo1973 (GTA01)
++	  GSM phone.
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called gta01kbd.
++
++
+ endif
+Index: linux-2.6.17.14-fic4.test/drivers/input/keyboard/Makefile
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/input/keyboard/Makefile	2007-01-08 21:34:30.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/input/keyboard/Makefile	2007-01-08 22:51:40.000000000 +0100
+@@ -12,6 +12,7 @@
+ obj-$(CONFIG_KEYBOARD_LOCOMO)		+= locomokbd.o
+ obj-$(CONFIG_KEYBOARD_NEWTON)		+= newtonkbd.o
+ obj-$(CONFIG_KEYBOARD_CORGI)		+= corgikbd.o
++obj-$(CONFIG_KEYBOARD_GTA01)		+= gta01kbd.o
+ obj-$(CONFIG_KEYBOARD_SPITZ)		+= spitzkbd.o
+ obj-$(CONFIG_KEYBOARD_HIL)		+= hil_kbd.o
+ obj-$(CONFIG_KEYBOARD_HIL_OLD)		+= hilkbd.o
+Index: linux-2.6.17.14-fic4.test/drivers/input/keyboard/gta01kbd.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/drivers/input/keyboard/gta01kbd.c	2007-01-08 22:52:23.000000000 +0100
+@@ -0,0 +1,452 @@
++/*
++ * Keyboard driver for FIC GTA01 (Neo1973) GSM phone
++ *
++ * (C) 2006 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/interrupt.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++
++#include <asm/arch/hardware.h>
++#include <asm/arch/gta01.h>
++
++struct gta01kbd {
++	struct input_dev *input;
++	unsigned int suspended;
++	unsigned long suspend_jiffies;
++};
++
++#if 0
++#include <asm/arch/corgi.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/hardware/scoop.h>
++
++#define KB_ROWS				8
++#define KB_COLS				12
++#define KB_ROWMASK(r)		(1 << (r))
++#define SCANCODE(r,c)		( ((r)<<4) + (c) + 1 )
++/* zero code, 124 scancodes */
++#define	NR_SCANCODES		( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 )
++
++#define SCAN_INTERVAL		(50) /* ms */
++#define HINGE_SCAN_INTERVAL	(250) /* ms */
++
++#define CORGI_KEY_CALENDER	KEY_F1
++#define CORGI_KEY_ADDRESS	KEY_F2
++#define CORGI_KEY_FN		KEY_F3
++#define CORGI_KEY_CANCEL	KEY_F4
++#define CORGI_KEY_OFF		KEY_SUSPEND
++#define CORGI_KEY_EXOK		KEY_F5
++#define CORGI_KEY_EXCANCEL	KEY_F6
++#define CORGI_KEY_EXJOGDOWN	KEY_F7
++#define CORGI_KEY_EXJOGUP	KEY_F8
++#define CORGI_KEY_JAP1		KEY_LEFTCTRL
++#define CORGI_KEY_JAP2		KEY_LEFTALT
++#define CORGI_KEY_MAIL		KEY_F10
++#define CORGI_KEY_OK		KEY_F11
++#define CORGI_KEY_MENU		KEY_F12
++
++static unsigned char corgikbd_keycode[NR_SCANCODES] = {
++	0,                                                                                                                /* 0 */
++	0, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, 0, 0, 0, 0, 0, 0, 0, 	                      /* 1-16 */
++	0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, 0, 0, 0, 0, 0, 0, 0,                                   /* 17-32 */
++	KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0,                                 /* 33-48 */
++	CORGI_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0,         /* 49-64 */
++	CORGI_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, 0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 	  /* 65-80 */
++	CORGI_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0,            /* 81-96 */
++	KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, CORGI_KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0,  /* 97-112 */
++	CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0,   /* 113-124 */
++};
++
++
++struct corgikbd {
++	unsigned char keycode[ARRAY_SIZE(corgikbd_keycode)];
++	struct input_dev *input;
++
++	spinlock_t lock;
++	struct timer_list timer;
++	struct timer_list htimer;
++
++	unsigned int suspended;
++	unsigned long suspend_jiffies;
++};
++
++#define KB_DISCHARGE_DELAY	10
++#define KB_ACTIVATE_DELAY	10
++
++/* Helper functions for reading the keyboard matrix
++ * Note: We should really be using pxa_gpio_mode to alter GPDR but it
++ *       requires a function call per GPIO bit which is excessive
++ *       when we need to access 12 bits at once multiple times.
++ * These functions must be called within local_irq_save()/local_irq_restore()
++ * or similar.
++ */
++static inline void corgikbd_discharge_all(void)
++{
++	/* STROBE All HiZ */
++	GPCR2  = CORGI_GPIO_ALL_STROBE_BIT;
++	GPDR2 &= ~CORGI_GPIO_ALL_STROBE_BIT;
++}
++
++static inline void corgikbd_activate_all(void)
++{
++	/* STROBE ALL -> High */
++	GPSR2  = CORGI_GPIO_ALL_STROBE_BIT;
++	GPDR2 |= CORGI_GPIO_ALL_STROBE_BIT;
++
++	udelay(KB_DISCHARGE_DELAY);
++
++	/* Clear any interrupts we may have triggered when altering the GPIO lines */
++	GEDR1 = CORGI_GPIO_HIGH_SENSE_BIT;
++	GEDR2 = CORGI_GPIO_LOW_SENSE_BIT;
++}
++
++static inline void corgikbd_activate_col(int col)
++{
++	/* STROBE col -> High, not col -> HiZ */
++	GPSR2 = CORGI_GPIO_STROBE_BIT(col);
++	GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
++}
++
++static inline void corgikbd_reset_col(int col)
++{
++	/* STROBE col -> Low */
++	GPCR2 = CORGI_GPIO_STROBE_BIT(col);
++	/* STROBE col -> out, not col -> HiZ */
++	GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
++}
++
++#define GET_ROWS_STATUS(c)	(((GPLR1 & CORGI_GPIO_HIGH_SENSE_BIT) >> CORGI_GPIO_HIGH_SENSE_RSHIFT) | ((GPLR2 & CORGI_GPIO_LOW_SENSE_BIT) << CORGI_GPIO_LOW_SENSE_LSHIFT))
++
++/*
++ * The corgi keyboard only generates interrupts when a key is pressed.
++ * When a key is pressed, we enable a timer which then scans the
++ * keyboard to detect when the key is released.
++ */
++
++/* Scan the hardware keyboard and push any changes up through the input layer */
++static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data, struct pt_regs *regs)
++{
++	unsigned int row, col, rowd;
++	unsigned long flags;
++	unsigned int num_pressed;
++
++	if (corgikbd_data->suspended)
++		return;
++
++	spin_lock_irqsave(&corgikbd_data->lock, flags);
++
++	if (regs)
++		input_regs(corgikbd_data->input, regs);
++
++	num_pressed = 0;
++	for (col = 0; col < KB_COLS; col++) {
++		/*
++		 * Discharge the output driver capacitatance
++		 * in the keyboard matrix. (Yes it is significant..)
++		 */
++
++		corgikbd_discharge_all();
++		udelay(KB_DISCHARGE_DELAY);
++
++		corgikbd_activate_col(col);
++		udelay(KB_ACTIVATE_DELAY);
++
++		rowd = GET_ROWS_STATUS(col);
++		for (row = 0; row < KB_ROWS; row++) {
++			unsigned int scancode, pressed;
++
++			scancode = SCANCODE(row, col);
++			pressed = rowd & KB_ROWMASK(row);
++
++			input_report_key(corgikbd_data->input, corgikbd_data->keycode[scancode], pressed);
++
++			if (pressed)
++				num_pressed++;
++
++			if (pressed && (corgikbd_data->keycode[scancode] == CORGI_KEY_OFF)
++					&& time_after(jiffies, corgikbd_data->suspend_jiffies + HZ)) {
++				input_event(corgikbd_data->input, EV_PWR, CORGI_KEY_OFF, 1);
++				corgikbd_data->suspend_jiffies=jiffies;
++			}
++		}
++		corgikbd_reset_col(col);
++	}
++
++	corgikbd_activate_all();
++
++	input_sync(corgikbd_data->input);
++
++	/* if any keys are pressed, enable the timer */
++	if (num_pressed)
++		mod_timer(&corgikbd_data->timer, jiffies + msecs_to_jiffies(SCAN_INTERVAL));
++
++	spin_unlock_irqrestore(&corgikbd_data->lock, flags);
++}
++
++/*
++ * corgi keyboard interrupt handler.
++ */
++static irqreturn_t corgikbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++	struct corgikbd *corgikbd_data = dev_id;
++
++	if (!timer_pending(&corgikbd_data->timer)) {
++		/** wait chattering delay **/
++		udelay(20);
++		corgikbd_scankeyboard(corgikbd_data, regs);
++	}
++
++	return IRQ_HANDLED;
++}
++
++/*
++ * corgi timer checking for released keys
++ */
++static void corgikbd_timer_callback(unsigned long data)
++{
++	struct corgikbd *corgikbd_data = (struct corgikbd *) data;
++	corgikbd_scankeyboard(corgikbd_data, NULL);
++}
++
++/*
++ * The hinge switches generate no interrupt so they need to be
++ * monitored by a timer.
++ *
++ * We debounce the switches and pass them to the input system.
++ *
++ *  gprr == 0x00 - Keyboard with Landscape Screen
++ *          0x08 - No Keyboard with Portrait Screen
++ *          0x0c - Keyboard and Screen Closed
++ */
++
++#define READ_GPIO_BIT(x)    (GPLR(x) & GPIO_bit(x))
++#define HINGE_STABLE_COUNT 2
++static int sharpsl_hinge_state;
++static int hinge_count;
++
++static void corgikbd_hinge_timer(unsigned long data)
++{
++	struct corgikbd *corgikbd_data = (struct corgikbd *) data;
++	unsigned long gprr;
++	unsigned long flags;
++
++	gprr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_GPRR) & (CORGI_SCP_SWA | CORGI_SCP_SWB);
++	gprr |= (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0);
++	if (gprr != sharpsl_hinge_state) {
++		hinge_count = 0;
++		sharpsl_hinge_state = gprr;
++	} else if (hinge_count < HINGE_STABLE_COUNT) {
++		hinge_count++;
++		if (hinge_count >= HINGE_STABLE_COUNT) {
++			spin_lock_irqsave(&corgikbd_data->lock, flags);
++
++			input_report_switch(corgikbd_data->input, SW_LID, ((sharpsl_hinge_state & CORGI_SCP_SWA) != 0));
++			input_report_switch(corgikbd_data->input, SW_TABLET_MODE, ((sharpsl_hinge_state & CORGI_SCP_SWB) != 0));
++			input_report_switch(corgikbd_data->input, SW_HEADPHONE_INSERT, (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0));
++			input_sync(corgikbd_data->input);
++
++			spin_unlock_irqrestore(&corgikbd_data->lock, flags);
++		}
++	}
++	mod_timer(&corgikbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
++}
++#endif
++
++static irqreturn_t gta01kbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++	struct gta01kbd *gta01kbd_data = dev_id;
++
++	/* FIXME: use GPIO from platform_dev resources */
++	if (s3c2410_gpio_getpin(GTA01_GPIO_911_KEY))
++		input_report_key(gta01kbd_data->input, KEY_PHONE, 1);
++	else
++		input_report_key(gta01kbd_data->input, KEY_PHONE, 0);
++
++	input_sync(gta01kbd_data->input);
++
++	return IRQ_HANDLED;
++}
++
++
++#ifdef CONFIG_PM
++static int gta01kbd_suspend(struct platform_device *dev, pm_message_t state)
++{
++	int i;
++	struct gta01kbd *gta01kbd = platform_get_drvdata(dev);
++
++	gta01kbd->suspended = 1;
++
++	return 0;
++}
++
++static int gta01kbd_resume(struct platform_device *dev)
++{
++	int i;
++	struct gta01kbd *gta01kbd = platform_get_drvdata(dev);
++
++	gta01kbd->suspended = 0;
++
++	return 0;
++}
++#else
++#define gta01kbd_suspend	NULL
++#define gta01kbd_resume		NULL
++#endif
++
++static int gta01kbd_probe(struct platform_device *pdev)
++{
++	struct gta01kbd *gta01kbd;
++	struct input_dev *input_dev;
++	int irq;
++
++	gta01kbd = kzalloc(sizeof(struct gta01kbd), GFP_KERNEL);
++	input_dev = input_allocate_device();
++	if (!gta01kbd || !input_dev) {
++		kfree(gta01kbd);
++		input_free_device(input_dev);
++		return -ENOMEM;
++	}
++
++	if (pdev->resource[0].flags != IORESOURCE_MEM)
++		return -EINVAL;
++
++	irq = s3c2410_gpio_getirq(pdev->resource[0].start);
++	if (irq < 0)
++		return -EINVAL;
++
++	platform_set_drvdata(pdev, gta01kbd);
++
++	gta01kbd->input = input_dev;
++
++#if 0
++	spin_lock_init(&gta01kbd->lock);
++	/* Init Keyboard rescan timer */
++	init_timer(&corgikbd->timer);
++	corgikbd->timer.function = corgikbd_timer_callback;
++	corgikbd->timer.data = (unsigned long) corgikbd;
++
++	/* Init Hinge Timer */
++	init_timer(&corgikbd->htimer);
++	corgikbd->htimer.function = corgikbd_hinge_timer;
++	corgikbd->htimer.data = (unsigned long) corgikbd;
++
++	corgikbd->suspend_jiffies=jiffies;
++
++	memcpy(corgikbd->keycode, corgikbd_keycode, sizeof(corgikbd->keycode));
++#endif
++
++	input_dev->name = "GTA01 Buttons";
++	input_dev->phys = "gta01kbd/input0";
++	input_dev->id.bustype = BUS_HOST;
++	input_dev->id.vendor = 0x0001;
++	input_dev->id.product = 0x0001;
++	input_dev->id.version = 0x0100;
++	input_dev->cdev.dev = &pdev->dev;
++	input_dev->private = gta01kbd;
++
++	input_dev->evbit[0] = BIT(EV_KEY);
++#if 0
++	input_dev->keycode = gta01kbd->keycode;
++	input_dev->keycodesize = sizeof(unsigned char);
++	input_dev->keycodemax = ARRAY_SIZE(corgikbd_keycode);
++
++	for (i = 0; i < ARRAY_SIZE(corgikbd_keycode); i++)
++		set_bit(corgikbd->keycode[i], input_dev->keybit);
++	clear_bit(0, input_dev->keybit);
++	set_bit(SW_LID, input_dev->swbit);
++	set_bit(SW_TABLET_MODE, input_dev->swbit);
++	set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
++#endif
++
++	input_register_device(gta01kbd->input);
++
++	if (request_irq(irq, gta01kbd_interrupt,
++			SA_INTERRUPT | SA_TRIGGER_RISING | SA_TRIGGER_FALLING,
++			"gta01kbd", gta01kbd))
++		printk(KERN_WARNING "gta01kbd: Can't get IRQ\n");
++
++#if 0
++	mod_timer(&corgikbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
++
++	/* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
++	for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) {
++		pxa_gpio_mode(CORGI_GPIO_KEY_SENSE(i) | GPIO_IN);
++		if (request_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd_interrupt,
++				SA_INTERRUPT | SA_TRIGGER_RISING,
++				"corgikbd", corgikbd))
++			printk(KERN_WARNING "corgikbd: Can't get IRQ: %d!\n", i);
++	}
++
++	/* Set Strobe lines as outputs - set high */
++	for (i = 0; i < CORGI_KEY_STROBE_NUM; i++)
++		pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
++
++	/* Setup the headphone jack as an input */
++	pxa_gpio_mode(CORGI_GPIO_AK_INT | GPIO_IN);
++#endif
++
++	return 0;
++}
++
++static int gta01kbd_remove(struct platform_device *pdev)
++{
++	struct gta01kbd *gta01kbd = platform_get_drvdata(pdev);
++
++	free_irq(s3c2410_gpio_getirq(pdev->resource[0].start), gta01kbd);
++#if 0
++	int i;
++
++	for (i = 0; i < CORGI_KEY_SENSE_NUM; i++)
++		free_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd);
++
++	del_timer_sync(&corgikbd->htimer);
++	del_timer_sync(&corgikbd->timer);
++#endif
++	input_unregister_device(gta01kbd->input);
++
++	kfree(gta01kbd);
++
++	return 0;
++}
++
++static struct platform_driver gta01kbd_driver = {
++	.probe		= gta01kbd_probe,
++	.remove		= gta01kbd_remove,
++	.suspend	= gta01kbd_suspend,
++	.resume		= gta01kbd_resume,
++	.driver		= {
++		.name	= "gta01-button",
++	},
++};
++
++static int __devinit gta01kbd_init(void)
++{
++	return platform_driver_register(&gta01kbd_driver);
++}
++
++static void __exit gta01kbd_exit(void)
++{
++	platform_driver_unregister(&gta01kbd_driver);
++}
++
++module_init(gta01kbd_init);
++module_exit(gta01kbd_exit);
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("FIC GTA01 (Neo1973) Buttons Driver");
++MODULE_LICENSE("GPL");

Added: trunk/src/target/kernel/patches/gta01-jbt6k74.patch
===================================================================
--- trunk/src/target/kernel/patches/gta01-jbt6k74.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/gta01-jbt6k74.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,566 @@
+This driver adds support for LCM initialization of the JBT6K74 LCM
+as found on the FIC GTA01 hardware
+
+Index: linux-2.6.17.14-fic4.test/drivers/spi/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/spi/Kconfig	2007-01-10 01:10:12.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/spi/Kconfig	2007-01-10 01:10:59.000000000 +0100
+@@ -129,5 +129,9 @@
+ 
+ # (slave support would go here)
+ 
++config SPI_SLAVE_JBT6K74
++	tristate "tpo JP6K74 LCM ASIC"
++	depends on SPI_MASTER && (MACH_GTA01 || MACH_QT2410)
++
+ endmenu # "SPI support"
+ 
+Index: linux-2.6.17.14-fic4.test/drivers/spi/Makefile
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/spi/Makefile	2007-01-10 01:10:12.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/spi/Makefile	2007-01-10 01:10:59.000000000 +0100
+@@ -26,4 +26,5 @@
+ # 	... add above this line ...
+ 
+ # SPI slave drivers (protocol for that link)
++obj-$(CONFIG_SPI_SLAVE_JBT6K74)		+= jbt6k74.o
+ # 	... add above this line ...
+Index: linux-2.6.17.14-fic4.test/drivers/spi/jbt6k74.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/drivers/spi/jbt6k74.c	2007-01-15 02:41:26.000000000 +0100
+@@ -0,0 +1,534 @@
++/* Linux kernel driver for the tpo JBT6K74-AS LCM ASIC
++ *
++ * Copyright (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; 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 <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++
++#include <linux/spi/spi.h>
++
++enum jbt_register {
++	JBT_REG_SLEEP_IN		= 0x10,
++	JBT_REG_SLEEP_OUT		= 0x11,
++
++	JBT_REG_DISPLAY_OFF		= 0x28,
++	JBT_REG_DISPLAY_ON		= 0x29,
++
++	JBT_REG_RGB_FORMAT		= 0x3a,
++	JBT_REG_QUAD_RATE		= 0x3b,
++
++	JBT_REG_POWER_ON_OFF		= 0xb0,
++	JBT_REG_BOOSTER_OP		= 0xb1,
++	JBT_REG_BOOSTER_MODE		= 0xb2,
++	JBT_REG_BOOSTER_FREQ		= 0xb3,
++	JBT_REG_OPAMP_SYSCLK		= 0xb4,
++	JBT_REG_VSC_VOLTAGE		= 0xb5,
++	JBT_REG_VCOM_VOLTAGE		= 0xb6,
++	JBT_REG_EXT_DISPL		= 0xb7,
++	JBT_REG_OUTPUT_CONTROL		= 0xb8,
++	JBT_REG_DCCLK_DCEV		= 0xb9,
++	JBT_REG_DISPLAY_MODE1		= 0xba,
++	JBT_REG_DISPLAY_MODE2		= 0xbb,
++	JBT_REG_DISPLAY_MODE		= 0xbc,
++	JBT_REG_ASW_SLEW		= 0xbd,
++	JBT_REG_DUMMY_DISPLAY		= 0xbe,
++	JBT_REG_DRIVE_SYSTEM		= 0xbf,
++
++	JBT_REG_SLEEP_OUT_FR_A		= 0xc0,
++	JBT_REG_SLEEP_OUT_FR_B		= 0xc1,
++	JBT_REG_SLEEP_OUT_FR_C		= 0xc2,
++	JBT_REG_SLEEP_IN_LCCNT_D	= 0xc3,
++	JBT_REG_SLEEP_IN_LCCNT_E	= 0xc4,
++	JBT_REG_SLEEP_IN_LCCNT_F	= 0xc5,
++	JBT_REG_SLEEP_IN_LCCNT_G	= 0xc6,
++
++	JBT_REG_GAMMA1_FINE_1		= 0xc7,
++	JBT_REG_GAMMA1_FINE_2		= 0xc8,
++	JBT_REG_GAMMA1_INCLINATION	= 0xc9,
++	JBT_REG_GAMMA1_BLUE_OFFSET	= 0xca,
++
++	JBT_REG_BLANK_CONTROL		= 0xcf,
++	JBT_REG_BLANK_TH_TV		= 0xd0,
++	JBT_REG_CKV_ON_OFF		= 0xd1,
++	JBT_REG_CKV_1_2			= 0xd2,
++	JBT_REG_OEV_TIMING		= 0xd3,
++	JBT_REG_ASW_TIMING_1		= 0xd4,
++	JBT_REG_ASW_TIMING_2		= 0xd5,
++
++	JBT_REG_HCLOCK_VGA		= 0xec,
++	JBT_REG_HCLOCK_QVGA		= 0xed,
++
++};
++
++enum jbt_state {
++	JBT_STATE_DEEP_STANDBY,
++	JBT_STATE_SLEEP,
++	JBT_STATE_NORMAL,
++};
++
++static const char *jbt_state_names[] = {
++	[JBT_STATE_DEEP_STANDBY]	= "deep-standby",
++	[JBT_STATE_SLEEP]		= "sleep",
++	[JBT_STATE_NORMAL]		= "normal",
++};
++
++#if 1
++#define DEBUGP(x, args...) printk(KERN_ERR "%s: " x, __FUNCTION__, ## args);
++#else
++#define DEBUGP(x, args...) do { } while (0)
++#endif
++
++
++#define JBT_TX_BUF_SIZE
++struct jbt_info {
++	enum jbt_state state;
++	u_int16_t tx_buf[8];
++	struct spi_device *spi_dev;
++	u_int16_t reg_cache[0xEE];
++};
++
++#define JBT_COMMAND	0x000
++#define JBT_DATA	0x100
++
++static int jbt_reg_write_nodata(struct jbt_info *jbt, u_int8_t reg)
++{
++	int rc;
++
++	jbt->tx_buf[0] = JBT_COMMAND | reg;
++
++	rc = spi_write(jbt->spi_dev, (u_int8_t *)jbt->tx_buf, 1*sizeof(u_int16_t));
++	if (rc == 0)
++		jbt->reg_cache[reg] = 0;
++
++	return rc;
++}
++
++
++static int jbt_reg_write(struct jbt_info *jbt, u_int8_t reg, u_int8_t data)
++{
++	int rc;
++
++	jbt->tx_buf[0] = JBT_COMMAND | reg;
++	jbt->tx_buf[1] = JBT_DATA | data;
++
++	rc = spi_write(jbt->spi_dev, (u_int8_t *)jbt->tx_buf, 2*sizeof(u_int16_t));
++	if (rc == 0)
++		jbt->reg_cache[reg] = data;
++
++	return rc;
++}
++
++static int jbt_reg_write16(struct jbt_info *jbt, u_int8_t reg, u_int16_t data)
++{
++	int rc;
++
++	jbt->tx_buf[0] = JBT_COMMAND | reg;
++	jbt->tx_buf[1] = JBT_DATA | (data >> 8);
++	jbt->tx_buf[2] = JBT_DATA | (data & 0xff);
++
++	rc = spi_write(jbt->spi_dev, (u_int8_t *)jbt->tx_buf, 3*sizeof(u_int16_t));
++	if (rc == 0)
++		jbt->reg_cache[reg] = data;
++
++	return rc;
++}
++
++static int jbt_init_regs(struct jbt_info *jbt)
++{
++	int rc;
++
++	DEBUGP("entering\n");
++
++	rc = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE1, 0x01);
++	rc |= jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE2, 0x00);
++	rc |= jbt_reg_write(jbt, JBT_REG_RGB_FORMAT, 0x60);
++	rc |= jbt_reg_write(jbt, JBT_REG_DRIVE_SYSTEM, 0x10);
++	rc |= jbt_reg_write(jbt, JBT_REG_BOOSTER_OP, 0x56);
++	rc |= jbt_reg_write(jbt, JBT_REG_BOOSTER_MODE, 0x33);
++	rc |= jbt_reg_write(jbt, JBT_REG_BOOSTER_FREQ, 0x11);
++	rc |= jbt_reg_write(jbt, JBT_REG_BOOSTER_FREQ, 0x11);
++	rc |= jbt_reg_write(jbt, JBT_REG_OPAMP_SYSCLK, 0x02);
++	rc |= jbt_reg_write(jbt, JBT_REG_VSC_VOLTAGE, 0x2b);
++	rc |= jbt_reg_write(jbt, JBT_REG_VCOM_VOLTAGE, 0x40);
++	rc |= jbt_reg_write(jbt, JBT_REG_EXT_DISPL, 0x03);
++	rc |= jbt_reg_write(jbt, JBT_REG_DCCLK_DCEV, 0x04);
++	rc |= jbt_reg_write(jbt, JBT_REG_ASW_SLEW, 0x02);
++	rc |= jbt_reg_write(jbt, JBT_REG_DUMMY_DISPLAY, 0x00);
++
++	rc |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_A, 0x11);
++	rc |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_B, 0x11);
++	rc |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_C, 0x11);
++	rc |= jbt_reg_write16(jbt, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040);
++	rc |= jbt_reg_write16(jbt, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0);
++	rc |= jbt_reg_write16(jbt, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020);
++	rc |= jbt_reg_write16(jbt, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0);
++
++	rc |= jbt_reg_write16(jbt, JBT_REG_GAMMA1_FINE_1, 0x5533);
++	rc |= jbt_reg_write(jbt, JBT_REG_GAMMA1_FINE_2, 0x00);
++	rc |= jbt_reg_write(jbt, JBT_REG_GAMMA1_INCLINATION, 0x00);
++	rc |= jbt_reg_write(jbt, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
++	rc |= jbt_reg_write(jbt, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
++
++	rc |= jbt_reg_write16(jbt, JBT_REG_HCLOCK_VGA, 0x1f0);
++	rc |= jbt_reg_write(jbt, JBT_REG_BLANK_CONTROL, 0x02);
++	rc |= jbt_reg_write16(jbt, JBT_REG_BLANK_TH_TV, 0x0804);
++	rc |= jbt_reg_write16(jbt, JBT_REG_BLANK_TH_TV, 0x0804);
++
++	rc |= jbt_reg_write(jbt, JBT_REG_CKV_ON_OFF, 0x01);
++	rc |= jbt_reg_write16(jbt, JBT_REG_CKV_1_2, 0x0000);
++
++	rc |= jbt_reg_write16(jbt, JBT_REG_OEV_TIMING, 0x0d0e);
++	rc |= jbt_reg_write16(jbt, JBT_REG_ASW_TIMING_1, 0x11a4);
++	rc |= jbt_reg_write(jbt, JBT_REG_ASW_TIMING_2, 0x0e);
++
++#if 0
++	rc |= jbt_reg_write16(jbt, JBT_REG_HCLOCK_QVGA, 0x00ff);
++	rc |= jbt_reg_write16(jbt, JBT_REG_HCLOCK_QVGA, 0x00ff);
++#endif
++
++	return rc;
++}
++
++static int standby_to_sleep(struct jbt_info *jbt)
++{
++	int rc;
++
++	DEBUGP("entering\n");
++
++	/* three times command zero */
++	rc = jbt_reg_write_nodata(jbt, 0x00);
++	mdelay(1);
++	rc = jbt_reg_write_nodata(jbt, 0x00);
++	mdelay(1);
++	rc = jbt_reg_write_nodata(jbt, 0x00);
++	mdelay(1);
++
++	/* deep standby out */
++	rc |= jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x17);
++
++	return rc;
++}
++
++static int sleep_to_normal(struct jbt_info *jbt)
++{
++	int rc;
++	DEBUGP("entering\n");
++
++	/* RGB I/F on, RAM wirte off, QVGA through, SIGCON enable */
++	rc = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x80);
++
++	/* Quad mode off */
++	rc |= jbt_reg_write(jbt, JBT_REG_QUAD_RATE, 0x00);
++
++	/* AVDD on, XVDD on */
++	rc |= jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x16);
++
++	/* Output control */
++	rc |= jbt_reg_write16(jbt, JBT_REG_OUTPUT_CONTROL, 0xfff9);
++
++	/* Sleep mode off */
++	rc |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_OUT);
++
++	/* initialize register set */
++	rc |= jbt_init_regs(jbt);
++	return rc;
++}
++
++static int normal_to_sleep(struct jbt_info *jbt)
++{
++	int rc;
++	DEBUGP("entering\n");
++
++	rc = jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_OFF);
++	rc |= jbt_reg_write16(jbt, JBT_REG_OUTPUT_CONTROL, 0x8002);
++	rc |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_IN);
++
++	return rc;
++}
++
++static int sleep_to_standby(struct jbt_info *jbt)
++{
++	DEBUGP("entering\n");
++	return jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x00);
++}
++
++/* frontend function */
++int jbt6k74_enter_state(struct jbt_info *jbt, enum jbt_state new_state)
++{
++	int rc = -EINVAL;
++
++	DEBUGP("entering(old_state=%u, new_state=%u)\n", jbt->state, new_state);
++
++	switch (jbt->state) {
++	case JBT_STATE_DEEP_STANDBY:
++		switch (new_state) {
++		case JBT_STATE_DEEP_STANDBY:
++			rc = 0;
++			break;
++		case JBT_STATE_SLEEP:
++			rc = standby_to_sleep(jbt);
++			break;
++		case JBT_STATE_NORMAL:
++			/* first transition into sleep */
++			rc = standby_to_sleep(jbt);
++			/* then transition into normal */
++			rc |= sleep_to_normal(jbt);
++			break;
++		}
++		break;
++	case JBT_STATE_SLEEP:
++		switch (new_state) {
++		case JBT_STATE_SLEEP:
++			rc = 0;
++			break;
++		case JBT_STATE_DEEP_STANDBY:
++			rc = sleep_to_standby(jbt);
++			break;
++		case JBT_STATE_NORMAL:
++			rc = sleep_to_normal(jbt);
++			break;
++		}
++		break;
++	case JBT_STATE_NORMAL:
++		switch (new_state) {
++		case JBT_STATE_NORMAL:
++			rc = 0;
++			break;
++		case JBT_STATE_DEEP_STANDBY:
++			/* first transition into sleep */
++			rc = normal_to_sleep(jbt);
++			/* then transition into deep standby */
++			rc |= sleep_to_standby(jbt);
++			break;
++		case JBT_STATE_SLEEP:
++			rc = normal_to_sleep(jbt);
++			break;
++		}
++		break;
++	}
++
++	return rc;
++}
++EXPORT_SYMBOL(jbt6k74_enter_state);
++
++int jbt6k74_display_onoff(struct jbt_info *jbt, int on)
++{
++	DEBUGP("entering\n");
++	if (on)
++		return jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_ON);
++	else
++		return jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_OFF);
++}
++EXPORT_SYMBOL(jbt6k74_display_onoff);
++
++static ssize_t state_read(struct device *dev, struct device_attribute *attr,
++			  char *buf)
++{
++	struct jbt_info *jbt = dev_get_drvdata(dev);
++
++	if (jbt->state >= ARRAY_SIZE(jbt_state_names))
++		return -EIO;
++
++	return sprintf(buf, "%s\n", jbt_state_names[jbt->state]);
++}
++
++static ssize_t state_write(struct device *dev, struct device_attribute *attr,
++			   const char *buf, size_t count)
++{
++	struct jbt_info *jbt = dev_get_drvdata(dev);
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(jbt_state_names); i++) {
++		if (!strcmp(buf, jbt_state_names[i])) {
++			jbt6k74_enter_state(jbt, i);
++			return count;
++		}
++	}
++
++	return -EINVAL;
++}
++
++static DEVICE_ATTR(state, 0644, state_read, state_write);
++
++static int reg_by_string(const char *name)
++{
++	if (!strcmp(name, "gamma_fine1"))
++		return JBT_REG_GAMMA1_FINE_1;
++	else if (!strcmp(name, "gamma_fine2"))
++		return JBT_REG_GAMMA1_FINE_2;
++	else if (!strcmp(name, "gamma_inclination"))
++		return JBT_REG_GAMMA1_INCLINATION;
++	else
++		return JBT_REG_GAMMA1_BLUE_OFFSET;
++}
++
++static ssize_t gamma_read(struct device *dev, struct device_attribute *attr,
++			  char *buf)
++{
++	int reg = reg_by_string(attr->attr.name);
++
++	return strlcpy(buf, "N/A\n", PAGE_SIZE);
++}
++
++static ssize_t gamma_write(struct device *dev, struct device_attribute *attr,
++			   const char *buf, size_t count)
++{
++	struct jbt_info *jbt = dev_get_drvdata(dev);
++	int reg = reg_by_string(attr->attr.name);
++	unsigned long val = simple_strtoul(buf, NULL, 10);
++
++	jbt_reg_write(jbt, reg, val & 0xff);
++	
++	return count;
++}
++
++static DEVICE_ATTR(gamma_fine1, 0644, gamma_read, gamma_write);
++static DEVICE_ATTR(gamma_fine2, 0644, gamma_read, gamma_write);
++static DEVICE_ATTR(gamma_inclination, 0644, gamma_read, gamma_write);
++static DEVICE_ATTR(gamma_blue_offset, 0644, gamma_read, gamma_write);
++
++/* linux device model infrastructure */
++
++static int __devinit jbt_probe(struct spi_device *spi)
++{
++	int rc;
++	struct jbt_info *jbt;
++	DEBUGP("entering\n");
++
++	jbt = kzalloc(sizeof(*jbt), GFP_KERNEL);
++	if (!jbt)
++		return -ENOMEM;
++
++	jbt->spi_dev = spi;
++	jbt->state = JBT_STATE_DEEP_STANDBY;
++
++	/* since we don't have MISO connected, we can't do detection */
++
++	dev_set_drvdata(&spi->dev, jbt);
++
++	spi->mode = SPI_CPOL | SPI_CPHA;
++	spi->bits_per_word = 9;
++
++	rc = spi_setup(spi);
++	if (rc < 0) {
++		printk(KERN_ERR "error during spi_setup of jbt6k74 driver\n");
++		dev_set_drvdata(&spi->dev, NULL);
++		kfree(jbt);
++		return rc;
++	}
++
++	rc = jbt6k74_enter_state(jbt, JBT_STATE_NORMAL);
++	if (rc < 0) {
++		printk(KERN_WARNING "jbt6k74: cannot enter NORMAL state\n");
++	}
++	jbt6k74_display_onoff(jbt, 1);
++
++	device_create_file(&spi->dev, &dev_attr_state);
++	device_create_file(&spi->dev, &dev_attr_gamma_fine1);
++	device_create_file(&spi->dev, &dev_attr_gamma_fine2);
++	device_create_file(&spi->dev, &dev_attr_gamma_inclination);
++	device_create_file(&spi->dev, &dev_attr_gamma_blue_offset);
++
++	return 0;
++}
++
++static int __devexit jbt_remove(struct spi_device *spi)
++{
++	struct jbt_info *jbt = dev_get_drvdata(&spi->dev);
++
++	DEBUGP("entering\n");
++
++	device_remove_file(&spi->dev, &dev_attr_state);
++	device_remove_file(&spi->dev, &dev_attr_gamma_fine1);
++	device_remove_file(&spi->dev, &dev_attr_gamma_fine2);
++	device_remove_file(&spi->dev, &dev_attr_gamma_inclination);
++	device_remove_file(&spi->dev, &dev_attr_gamma_blue_offset);
++
++	kfree(jbt);
++
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int jbt_suspend(struct spi_device *spi, pm_message_t state)
++{
++	struct jbt_info *jbt = dev_get_drvdata(&spi->dev);
++	DEBUGP("entering\n");
++
++	switch (state.event) {
++	case PM_EVENT_SUSPEND:
++	case 3:
++		jbt6k74_enter_state(jbt, JBT_STATE_DEEP_STANDBY);
++		return 0;
++	default:
++		return -1;
++	}
++}
++
++static int jbt_resume(struct spi_device *spi)
++{
++	struct jbt_info *jbt = dev_get_drvdata(&spi->dev);
++	DEBUGP("entering\n");
++
++	jbt6k74_enter_state(jbt, JBT_STATE_NORMAL);
++
++	return 0;
++}
++#else
++#define jbt_suspend	NULL
++#define jbt_resume	NULL
++#endif
++
++static struct spi_driver jbt6k74_driver = {
++	.driver = {
++		.name	= "jbt6k74",
++		.bus	= &spi_bus_type,
++		.owner	= THIS_MODULE,
++	},
++
++	.probe 	 = jbt_probe,
++	.remove	 = __devexit_p(jbt_remove),
++	.suspend = jbt_suspend,
++	.resume	 = jbt_resume,
++};
++
++static int __init jbt_init(void)
++{
++	DEBUGP("entering\n");
++	return spi_register_driver(&jbt6k74_driver);
++}
++
++static void __exit jbt_exit(void)
++{
++	DEBUGP("entering\n");
++	spi_unregister_driver(&jbt6k74_driver);
++}
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_LICENSE("GPL");
++
++module_init(jbt_init);
++module_exit(jbt_exit);

Added: trunk/src/target/kernel/patches/gta01-pcf50606.patch
===================================================================
--- trunk/src/target/kernel/patches/gta01-pcf50606.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/gta01-pcf50606.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,1800 @@
+This is the PCF50606 power management unit driver for FIC GTA01
+
+Index: linux-2.6.17.14-fic4.test/drivers/i2c/chips/pcf50606.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/drivers/i2c/chips/pcf50606.c	2007-01-21 21:23:04.000000000 +0100
+@@ -0,0 +1,1423 @@
++/* Philips PCF50606 Power Management Unit (PMU) driver
++ *
++ * (C) 2006 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; 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
++ *
++ * This driver is a monster ;) It provides the following features
++ * - voltage control for a dozen different voltage domains
++ * - charging control for main and backup battery
++ * - rtc / alarm
++ * - watchdog
++ * - adc driver (hw_sensors like)
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/i2c.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/workqueue.h>
++#include <linux/rtc.h>
++#include <linux/bcd.h>
++#include <linux/watchdog.h>
++#include <linux/miscdevice.h>
++#include <linux/input.h>
++#include <linux/pcf50606.h>
++
++#include <asm/apm.h>
++#include <asm/arch/gta01.h>
++
++#include "pcf50606.h"
++
++#if 1
++#define DEBUGP(x, args ...) printk("%s: " x, __FUNCTION__, ## args)
++#define DEBUGPC(x, args ...) printk(x, ## args)
++#else
++#define DEBUGP(x, args ...)
++#define DEBUGPC(x, args ...)
++#endif
++
++/***********************************************************************
++ * Static data / structures
++ ***********************************************************************/
++
++static unsigned short normal_i2c[] = { 0x08, I2C_CLIENT_END };
++
++I2C_CLIENT_INSMOD_1(pcf50606);
++
++#define PCF50606_F_CHG_PRESENT	0x00000001
++#define PCF50606_F_CHG_FOK	0x00000002
++#define PCF50606_F_CHG_FAST	0x00000004
++
++enum close_state {
++	CLOSE_STATE_NOT,
++	CLOSE_STATE_ALLOW = 0x2342,
++};
++
++struct pcf50606_data {
++	struct i2c_client client;
++	spinlock_t lock;
++	unsigned int flags;
++	unsigned int working;
++	struct work_struct work;
++	struct rtc_device *rtc;
++	struct input_dev *input_dev;
++	int allow_close;
++};
++
++struct pcf50606_data *pcf50606_global;
++EXPORT_SYMBOL(pcf50606_global);
++
++static const u_int8_t initial_register_set[__NUM_PCF50606_REGS] = {
++	[PCF50606_REG_OOCS] 	= 0x00,
++	/* gap */
++	[PCF50606_REG_INT1M]	= PCF50606_INT1_SECOND,
++	[PCF50606_REG_INT2M]	= 0x00,
++	[PCF50606_REG_INT3M]	= PCF50606_INT3_TSCPRES,
++	[PCF50606_REG_OOCC1] 	= PCF50606_OOCC1_CLK32ON |
++				  PCF50606_OOCC1_RTCWAK |
++				  PCF50606_OOCC1_CHGWAK |
++				  PCF50606_OOCC1_EXTONWAK_HIGH,
++	[PCF50606_REG_OOCC2]	= PCF50606_OOCC2_ONKEYDB_14ms |
++				  PCF50606_OOCC2_EXTONDB_14ms,
++	/* gap */
++	[PCF50606_REG_PSSC]	= 0x00,
++	[PCF50606_REG_PWROKM]	= 0x00,
++	/* gap */
++	[PCF50606_REG_DCDC1]	= 0x00,	/* GL_3V3: off */
++	[PCF50606_REG_DCDC2]	= 0x00,
++	[PCF50606_REG_DCDC3]	= 0x00,
++	[PCF50606_REG_DCDC4]	= 0x30, /* 1.25A */
++	[PCF50606_REG_DCDEC1]	= 0xe8, /* IO_3V3: 3.3V */
++	[PCF50606_REG_DCDEC2]	= 0x00,
++	[PCF50606_REG_DCUDC1]	= 0xe3, /* CORE_1V8: 1.8V */
++	[PCF50606_REG_DCUDC2]	= 0x30, /* 1.25A current limit */
++	[PCF50606_REG_IOREGC]	= 0x00, /* CODEC_3V3: off */
++	[PCF50606_REG_D1REGC1]	= 0x00, /* BT_3V15: off */
++	[PCF50606_REG_D2REGC1]	= 0x00, /* GL_2V5: off */
++	[PCF50606_REG_D3REGC1]	= 0x00, /* USER1: off */
++	[PCF50606_REG_LPREGC1]	= 0xf8, /* LCM_3V3: on */
++	[PCF50606_REG_LPREGC2]	= 0x00,
++	[PCF50606_REG_MBCC1]	= 0x01,
++	[PCF50606_REG_MBCC2]	= 0x00,	/* unlimited charging */
++	[PCF50606_REG_MBCC3]	= 0x1a, /* 0.2*Ifast, 4.20V */
++	[PCF50606_REG_BBCC]	= 0x13, /* 50uA */
++	[PCF50606_REG_ADCC1]	= 0x00,
++	[PCF50606_REG_ADCC2]	= 0x00,
++	/* gap */
++	[PCF50606_REG_ACDC1]	= 0x00,
++	[PCF50606_REG_BVMC]	= PCF50606_BVMC_THRSHLD_2V8 |
++				  PCF50606_BVMC_DISDB,
++	[PCF50606_REG_PWMC1]	= 0x00,
++	[PCF50606_REG_LEDC1]	= 0x00,
++	[PCF50606_REG_LEDC2]	= 0x00,
++	[PCF50606_REG_GPOC1]	= 0x00,
++	[PCF50606_REG_GPOC2]	= 0x00,
++	[PCF50606_REG_GPOC3]	= 0x00,
++	[PCF50606_REG_GPOC4]	= 0x00,
++	[PCF50606_REG_GPOC5]	= 0x00,
++};
++
++/* This is a mitsubishi TN11-3H103J T,B NTC Thermistor -10..79 centigrade */
++static const u_int16_t ntc_table_tn11_3h103j[] = {
++	/* -10 */
++	40260, 38560, 36950, 35410, 33950, 32550, 31220, 29960, 28750, 27590,
++	26490, 25440, 24440, 23480, 22560, 21680, 20830, 20020, 19240, 18500,
++	17780, 17710, 16440, 15810, 15210, 14630, 14070, 13540, 13030, 12540,
++	12070, 11620, 11190, 10780, 10380, 10000, 9635, 9286, 8950, 8629,
++	8320, 8024, 7740, 7467, 7205, 6954, 6713, 6481, 6258, 6044,
++	5839, 5641, 5451, 5269, 5093, 4924, 4762, 4605, 4455, 4310,
++	4171, 4037, 3908, 3784, 3664, 3549, 3438, 3313, 3227, 3128,
++	3032, 2939, 2850, 2763, 2680, 2600, 2522, 2448, 2375, 2306,
++	2239, 2174, 2111, 2050, 1922, 1935, 1881, 1828, 1776, 1727,
++};
++
++
++/***********************************************************************
++ * Low-Level routines
++ ***********************************************************************/
++
++static inline int __reg_write(struct pcf50606_data *pcf, u_int8_t reg, u_int8_t val)
++{
++	DEBUGP("(pcf=%p, reg=0x%02x, val=0x%02x)\n", pcf, reg, val);
++
++	return i2c_smbus_write_byte_data(&pcf->client, reg, val);
++}
++
++static int reg_write(struct pcf50606_data *pcf, u_int8_t reg, u_int8_t val)
++{
++	int ret;
++
++	spin_lock_irq(&pcf->lock);
++	ret = __reg_write(pcf, reg, val);
++	spin_unlock_irq(&pcf->lock);
++
++	return ret;
++}
++
++static inline int32_t __reg_read(struct pcf50606_data *pcf, u_int8_t reg)
++{
++	int32_t ret;
++
++	ret = i2c_smbus_read_byte_data(&pcf->client, reg);
++	DEBUGP("(pcf=%p, reg=0x%02x, val=0x%02x)\n", pcf, reg, ret);
++
++	return ret;
++}
++
++static u_int8_t reg_read(struct pcf50606_data *pcf, u_int8_t reg)
++{
++	int32_t ret;
++
++	spin_lock_irq(&pcf->lock);
++	ret = __reg_read(pcf, reg);
++	spin_unlock_irq(&pcf->lock);
++
++	return ret & 0xff;
++}
++
++static int reg_set_bit_mask(struct pcf50606_data *pcf,
++			    u_int8_t reg, u_int8_t mask, u_int8_t val)
++{
++	int ret;
++	u_int8_t tmp;
++
++	DEBUGP("(pcf=%p, reg=0x%02x, val=0x%02x, mask=0x%02x)\n",
++		pcf, reg, val, mask);
++
++	val &= mask;
++
++	spin_lock_irq(&pcf->lock);
++
++	tmp = __reg_read(pcf, reg);
++	tmp &= ~mask;
++	tmp |= val;
++	ret = __reg_write(pcf, reg, tmp);
++
++	spin_unlock_irq(&pcf->lock);
++
++	return ret;
++}
++
++static int reg_clear_bits(struct pcf50606_data *pcf, u_int8_t reg, u_int8_t val)
++{
++	int ret;
++	u_int8_t tmp;
++
++	spin_lock_irq(&pcf->lock);
++
++	tmp = __reg_read(pcf, reg);
++	tmp &= ~val;
++	ret = __reg_write(pcf, reg, tmp);
++
++	spin_unlock_irq(&pcf->lock);
++
++	return ret;
++}
++
++/* synchronously read one ADC channel (busy-wait for result to be complete) */
++static u_int16_t adc_read(struct pcf50606_data *pcf,  int channel,
++			  u_int16_t *data2)
++{
++	u_int8_t adcs2, adcs1;
++	u_int16_t ret;
++
++	DEBUGP("entering (pcf=%p, channel=%u, data2=%p)\n",
++		pcf, channel, data2);
++
++	channel &= PCF50606_ADCC2_ADCMUX_MASK;
++
++	spin_lock_irq(&pcf->lock);
++
++	/* start ADC conversion of selected channel */
++	__reg_write(pcf, PCF50606_REG_ADCC2, channel |
++		    PCF50606_ADCC2_ADCSTART | PCF50606_ADCC2_RES_10BIT);
++
++	do {
++		adcs2 = __reg_read(pcf, PCF50606_REG_ADCS2);
++	} while (!(adcs2 & PCF50606_ADCS2_ADCRDY));
++
++	adcs1 = __reg_read(pcf, PCF50606_REG_ADCS1);
++	ret = (adcs1 << 2) | (adcs2 & 0x03);
++
++	if (data2) {
++		adcs1 = __reg_read(pcf, PCF50606_REG_ADCS3);
++		*data2 = (adcs1 << 2) | (adcs2 & 0x0c);
++	}
++
++	spin_unlock_irq(&pcf->lock);
++
++	DEBUGP("returnung %u\n", ret);
++
++	return ret;
++}
++
++/***********************************************************************
++ * Voltage / ADC
++ ***********************************************************************/
++
++static u_int8_t dcudc_voltage(unsigned int millivolts)
++{
++	if (millivolts < 900)
++		return 0;
++	if (millivolts > 5500)
++		return 0x1f;
++	if (millivolts <= 3300) {
++		millivolts -= 900;
++		return millivolts/300;
++	}
++	if (millivolts < 4000)
++		return 0x0f;
++	else {
++		millivolts -= 4000;
++		return millivolts/100;
++	}
++}
++
++static unsigned int dcudc_2voltage(u_int8_t bits)
++{
++	bits &= 0x1f;
++	if (bits < 0x08)
++		return 900 + bits * 300;
++	else if (bits < 0x10)
++		return 3300;
++	else
++		return 4000 + bits * 100;
++}
++
++static u_int8_t dcdec_voltage(unsigned int millivolts)
++{
++	if (millivolts < 900)
++		return 0;
++	else if (millivolts > 3300)
++		return 0x0f;
++
++	millivolts -= 900;
++	return millivolts/300;
++}
++
++static unsigned int dcdec_2voltage(u_int8_t bits)
++{
++	bits &= 0x0f;
++	return 900 + bits*300;
++}
++
++static u_int8_t dcdc_voltage(unsigned int millivolts)
++{
++	if (millivolts < 900)
++		return 0;
++	else if (millivolts > 3600)
++		return 0x1f;
++
++	if (millivolts < 1500) {
++		millivolts -= 900;
++		return millivolts/25;
++	} else {
++		millivolts -= 1500;
++		return 0x18 + millivolts/300;
++	}
++}
++
++static unsigned int dcdc_2voltage(u_int8_t bits)
++{
++	bits &= 0x1f;
++	if ((bits & 0x18) == 0x18)
++		return 1500 + ((bits & 0x7) * 300);
++	else
++		return 900 + (bits * 25);
++}
++
++static u_int8_t dx_voltage(unsigned int millivolts)
++{
++	DEBUGP("(mvolts=%u)\n", millivolts);
++
++	if (millivolts < 900)
++		return 0;
++	else if (millivolts > 3300)
++		return 0x18;
++
++	millivolts -= 900;
++	return millivolts/100;
++}
++
++static unsigned int dx_2voltage(u_int8_t bits)
++{
++	bits &= 0x1f;
++	return 900 + (bits * 100);
++}
++
++static const u_int8_t regulator_registers[__NUM_PCF50606_REGULATORS] = {
++	[PCF50606_REGULATOR_DCD]	= PCF50606_REG_DCDC1,
++	[PCF50606_REGULATOR_DCDE]	= PCF50606_REG_DCDEC1,
++	[PCF50606_REGULATOR_DCUD]	= PCF50606_REG_DCUDC1,
++	[PCF50606_REGULATOR_D1REG]	= PCF50606_REG_D1REGC1,
++	[PCF50606_REGULATOR_D2REG]	= PCF50606_REG_D2REGC1,
++	[PCF50606_REGULATOR_D3REG]	= PCF50606_REG_D3REGC1,
++	[PCF50606_REGULATOR_LPREG]	= PCF50606_REG_LPREGC1,
++	[PCF50606_REGULATOR_IOREG]	= PCF50606_REG_IOREGC,
++};
++
++int pcf50606_onoff_set(struct pcf50606_data *pcf,
++		       enum pcf50606_regulator_id reg, int on)
++{
++	u_int8_t addr;
++
++	if (reg >= __NUM_PCF50606_REGULATORS)
++		return -EINVAL;
++
++	addr = regulator_registers[reg];
++
++	if (on == 0)
++		reg_set_bit_mask(pcf, addr, 0xe0, 0x00);
++	else
++		reg_set_bit_mask(pcf, addr, 0xe0, 0xe0);
++
++	return 0;
++}
++EXPORT_SYMBOL(pcf50606_onoff_set);
++
++int pcf50606_onoff_get(struct pcf50606_data *pcf,
++		       enum pcf50606_regulator_id reg)
++{
++	u_int8_t val, addr;
++
++	if (reg >= __NUM_PCF50606_REGULATORS)
++		return -EINVAL;
++
++	addr = regulator_registers[reg];
++	val = (reg_read(pcf, addr) & 0xe0) >> 5;
++
++	/* PWREN1 = 1, PWREN2 = 1, see table 16 of datasheet */
++	switch (val) {
++		case 0:
++		case 5:
++			return 0;
++		default:
++			return 1;
++	}
++}
++EXPORT_SYMBOL(pcf50606_onoff_get);
++
++int pcf50606_voltage_set(struct pcf50606_data *pcf,
++			 enum pcf50606_regulator_id reg,
++			 unsigned int millivolts)
++{
++	u_int8_t volt_bits;
++	u_int8_t regnr;
++	int rc;
++
++	DEBUGP("pcf=%p, reg=%d, mvolts=%d\n", pcf, reg, millivolts);
++
++	if (reg >= __NUM_PCF50606_REGULATORS)
++		return -EINVAL;
++
++	switch (reg) {
++	case PCF50606_REGULATOR_DCD:
++		volt_bits = dcdc_voltage(millivolts);
++		rc = reg_set_bit_mask(pcf, PCF50606_REG_DCDC1, 0x1f,
++				      volt_bits);
++		break;
++	case PCF50606_REGULATOR_DCDE:
++		volt_bits = dcdec_voltage(millivolts);
++		rc = reg_set_bit_mask(pcf, PCF50606_REG_DCDEC1, 0x0f,
++				      volt_bits);
++		break;
++	case PCF50606_REGULATOR_DCUD:
++		volt_bits = dcudc_voltage(millivolts);
++		rc = reg_set_bit_mask(pcf, PCF50606_REG_DCUDC1, 0x1f,
++				      volt_bits);
++		break;
++	case PCF50606_REGULATOR_D1REG:
++	case PCF50606_REGULATOR_D2REG:
++	case PCF50606_REGULATOR_D3REG:
++		regnr = PCF50606_REG_D1REGC1 + (reg - PCF50606_REGULATOR_D1REG);
++		volt_bits = dx_voltage(millivolts);
++		DEBUGP("dx_voltage(0x%x)=%u\n", millivolts, volt_bits);
++		rc = reg_set_bit_mask(pcf, regnr, 0x1f, volt_bits);
++		break;
++	case PCF50606_REGULATOR_LPREG:
++		volt_bits = dx_voltage(millivolts);
++		rc = reg_set_bit_mask(pcf, PCF50606_REG_LPREGC1, 0x1f,
++				      volt_bits);
++		break;
++	case PCF50606_REGULATOR_IOREG:
++		if (millivolts < 1800)
++			return -EINVAL;
++		volt_bits = dx_voltage(millivolts);
++		rc = reg_set_bit_mask(pcf, PCF50606_REG_IOREGC, 0x1f,
++				      volt_bits);
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	return rc;
++}
++EXPORT_SYMBOL(pcf50606_voltage_set);
++
++unsigned int pcf50606_voltage_get(struct pcf50606_data *pcf,
++			 enum pcf50606_regulator_id reg)
++{
++	u_int8_t volt_bits;
++	u_int8_t regnr;
++	unsigned int rc = 0;
++
++	if (reg >= __NUM_PCF50606_REGULATORS)
++		return -EINVAL;
++
++	switch (reg) {
++	case PCF50606_REGULATOR_DCD:
++		volt_bits = reg_read(pcf, PCF50606_REG_DCDC1) & 0x1f;
++		rc = dcdc_2voltage(volt_bits);
++		break;
++	case PCF50606_REGULATOR_DCDE:
++		volt_bits = reg_read(pcf, PCF50606_REG_DCDEC1) & 0x0f;
++		rc = dcdec_2voltage(volt_bits);
++		break;
++	case PCF50606_REGULATOR_DCUD:
++		volt_bits = reg_read(pcf, PCF50606_REG_DCUDC1) & 0x1f;
++		rc = dcudc_2voltage(volt_bits);
++		break;
++	case PCF50606_REGULATOR_D1REG:
++	case PCF50606_REGULATOR_D2REG:
++	case PCF50606_REGULATOR_D3REG:
++		regnr = PCF50606_REG_D1REGC1 + (reg - PCF50606_REGULATOR_D1REG);
++		volt_bits = reg_read(pcf, regnr) & 0x1f;
++		if (volt_bits > 0x18)
++			volt_bits = 0x18;
++		rc = dx_2voltage(volt_bits);
++		break;
++	case PCF50606_REGULATOR_LPREG:
++		volt_bits = reg_read(pcf, PCF50606_REG_LPREGC1) & 0x1f;
++		if (volt_bits > 0x18)
++			volt_bits = 0x18;
++		rc = dx_2voltage(volt_bits);
++		break;
++	case PCF50606_REGULATOR_IOREG:
++		volt_bits = reg_read(pcf, PCF50606_REG_IOREGC) & 0x1f;
++		if (volt_bits > 0x18)
++			volt_bits = 0x18;
++		rc = dx_2voltage(volt_bits);
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	return rc;
++}
++EXPORT_SYMBOL(pcf50606_voltage_get);
++
++/* go into 'STANDBY' mode, i.e. power off the main CPU and peripherals */
++void pcf50606_go_standby(void)
++{
++	reg_write(pcf50606_global, PCF50606_REG_OOCC1,
++		  PCF50606_OOCC1_GOSTDBY);
++}
++EXPORT_SYMBOL(pcf50606_go_standby);
++
++void pcf50606_gpo0_set(struct pcf50606_data *pcf, int on)
++{
++	u_int8_t val;
++
++	if (on)
++		val = 0x07;
++	else
++		val = 0x0f;
++
++	reg_set_bit_mask(pcf, PCF50606_REG_GPOC1, 0x0f, val);
++}
++EXPORT_SYMBOL(pcf50606_gpo0_set);
++
++int pcf50606_gpo0_get(struct pcf50606_data *pcf)
++{
++	u_int8_t reg = reg_read(pcf, PCF50606_REG_GPOC1) & 0x0f;
++
++	if (reg == 0x07 || reg == 0x08)
++		return 1;
++
++	return 0;
++}
++EXPORT_SYMBOL(pcf50606_gpo0_get);
++
++static void pcf50606_work(void *data)
++{
++	struct pcf50606_data *pcf = data;
++	u_int8_t int1, int2, int3;
++
++	pcf->working = 1;
++
++	int1 = __reg_read(pcf, PCF50606_REG_INT1);
++	int2 = __reg_read(pcf, PCF50606_REG_INT2);
++	int3 = __reg_read(pcf, PCF50606_REG_INT3);
++
++	DEBUGP("INT1=0x%02x INT2=0x%02x INT3=0x%02x:", int1, int2, int3);
++
++	if (int1 & PCF50606_INT1_ONKEYR) {
++		/* ONKEY rising edge */
++		DEBUGPC("ONKEYR ");
++		input_report_key(pcf->input_dev, KEY_POWER, 1);
++	}
++	if (int1 & PCF50606_INT1_ONKEYF) {
++		/* ONKEY falling edge */
++		DEBUGPC("ONKEYF ");
++		input_report_key(pcf->input_dev, KEY_POWER, 0);
++	}
++	if (int1 & PCF50606_INT1_ONKEY1S) {
++		DEBUGPC("ONKEY1S ");
++		/* ONKEY pressed for more than 1 second */
++	}
++	if (int1 & PCF50606_INT1_EXTONR) {
++		DEBUGPC("EXTONR ");
++		input_report_key(pcf->input_dev, KEY_POWER2, 1);
++	}
++	if (int1 & PCF50606_INT1_EXTONF) {
++		DEBUGPC("EXTONF ");
++		input_report_key(pcf->input_dev, KEY_POWER2, 0);
++	}
++	if (int1 & PCF50606_INT1_SECOND) {
++		DEBUGPC("SECOND ");
++		rtc_update_irq(&pcf->rtc->class_dev, 1, RTC_PF | RTC_IRQF);
++	}
++	if (int1 & PCF50606_INT1_ALARM) {
++		DEBUGPC("ALARM ");
++		rtc_update_irq(&pcf->rtc->class_dev, 1, RTC_AF | RTC_IRQF);
++	}
++
++	if (int2 & PCF50606_INT2_CHGINS) {
++		/* Charger inserted */
++		DEBUGPC("CHGINS ");
++		input_report_key(pcf->input_dev, KEY_BATTERY, 1);
++		apm_queue_event(APM_POWER_STATUS_CHANGE);
++		pcf->flags |= PCF50606_F_CHG_PRESENT;
++		//kobject_uevent( ,KOBJ_ADD);
++	}
++	if (int2 & PCF50606_INT2_CHGRM) {
++		/* Charger removed */
++		DEBUGPC("CHGRM ");
++		input_report_key(pcf->input_dev, KEY_BATTERY, 0);
++		apm_queue_event(APM_POWER_STATUS_CHANGE);
++		pcf->flags &= ~PCF50606_F_CHG_PRESENT;
++		//kobject_uevent( ,KOBJ_REMOVE);
++	}
++	if (int2 & PCF50606_INT2_CHGFOK) {
++		/* Battery ready for fast charging */
++		DEBUGPC("CHGFOK ");
++		pcf->flags |= PCF50606_F_CHG_FOK;
++	}
++	if (int2 & PCF50606_INT2_CHGERR) {
++		/* Error in charge mode */
++		DEBUGPC("CHGERR ");
++	}
++	if (int2 & PCF50606_INT2_CHGFRDY) {
++		/* Fast charge completed */
++		DEBUGPC("CHGFRDY ");
++	}
++	if (int2 & PCF50606_INT2_CHGPROT) {
++		/* Charging protection interrupt */
++		DEBUGPC("CHGPROT ");
++	}
++	if (int2 & PCF50606_INT2_CHGWD10S) {
++		/* Charger watchdog will expire in 10 seconds */
++		DEBUGPC("CHGWD10S ");
++	}
++	if (int2 & PCF50606_INT2_CHGWDEXP) {
++		/* Charger watchdog expires */
++		DEBUGPC("CHGWDEXP ");
++	}
++
++	if (int3 & PCF50606_INT3_ADCRDY) {
++		/* ADC result ready */
++		DEBUGPC("ADCRDY ");
++	}
++	/* FIXME: ACDINS, ACDREM, TSCPRES */
++	if (int3 & PCF50606_INT3_LOWBAT) {
++		/* Low battery voltage */
++		DEBUGPC("LOWBAT ");
++		apm_queue_event(APM_LOW_BATTERY);
++	}
++	if (int3 & PCF50606_INT3_HIGHTMP) {
++		/* High temperature */
++		DEBUGPC("HIGHTMP ");
++		apm_queue_event(APM_CRITICAL_SUSPEND);
++	}
++
++	DEBUGPC("\n");
++
++	pcf->working = 0;
++	input_sync(pcf->input_dev);
++	put_device(&pcf->client.dev);
++}
++
++static void pcf50606_schedule_work(struct pcf50606_data *pcf)
++{
++	int status;
++
++	get_device(&pcf->client.dev);
++	status = schedule_work(&pcf->work);
++	if (!status && !pcf->working)
++		dev_dbg(&pcf->client.dev, "work item may be lost\n");
++}
++
++
++static irqreturn_t pcf50606_irq(int irq, void *_pcf, struct pt_regs *regs)
++{
++	struct pcf50606_data *pcf = _pcf;
++
++	DEBUGP("entering(irq=%u, pcf=%p, regs=%p): scheduling work\n",
++		irq, _pcf, regs);
++	pcf50606_schedule_work(pcf);
++
++	return IRQ_HANDLED;
++}
++
++static u_int16_t adc_to_batt_millivolts(u_int16_t adc)
++{
++	u_int16_t mvolts;
++
++	mvolts = (adc * 6000) / 1024;
++
++	return mvolts;
++}
++
++#define BATTVOLT_SCALE_START 2800
++#define BATTVOLT_SCALE_END 4200
++#define BATTVOLT_SCALE_DIVIDER ((BATTVOLT_SCALE_END - BATTVOLT_SCALE_START)/10)
++
++static u_int8_t battvolt_scale(u_int16_t battvolt)
++{
++	/* FIXME: this linear scale is completely bogus */
++	u_int16_t battvolt_relative = battvolt - BATTVOLT_SCALE_START;
++	unsigned int percent = battvolt_relative / BATTVOLT_SCALE_DIVIDER;
++
++	return percent;
++}
++
++u_int16_t pcf50606_battvolt(struct pcf50606_data *pcf)
++{
++	u_int16_t adc;
++	adc = adc_read(pcf, PCF50606_ADCMUX_BATVOLT_RES, NULL);
++
++	return adc_to_batt_millivolts(adc);
++}
++EXPORT_SYMBOL(pcf50606_battvolt);
++
++static ssize_t show_battvolt(struct device *dev, struct device_attribute *attr,
++			     char *buf)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct pcf50606_data *pcf = i2c_get_clientdata(client);
++
++	return sprintf(buf, "%u\n", pcf50606_battvolt(pcf));
++}
++static DEVICE_ATTR(battvolt, S_IRUGO | S_IWUSR, show_battvolt, NULL);
++
++static int reg_id_by_name(const char *name)
++{
++	int reg_id;
++
++	if (!strcmp(name, "voltage_dcd"))
++		reg_id = PCF50606_REGULATOR_DCD;
++	else if (!strcmp(name, "voltage_dcde"))
++		reg_id = PCF50606_REGULATOR_DCDE;
++	else if (!strcmp(name, "voltage_dcud"))
++		reg_id = PCF50606_REGULATOR_DCUD;
++	else if (!strcmp(name, "voltage_d1reg"))
++		reg_id = PCF50606_REGULATOR_D1REG;
++	else if (!strcmp(name, "voltage_d2reg"))
++		reg_id = PCF50606_REGULATOR_D2REG;
++	else if (!strcmp(name, "voltage_d3reg"))
++		reg_id = PCF50606_REGULATOR_D3REG;
++	else if (!strcmp(name, "voltage_lpreg"))
++		reg_id = PCF50606_REGULATOR_LPREG;
++	else if (!strcmp(name, "voltage_ioreg"))
++		reg_id = PCF50606_REGULATOR_IOREG;
++	else
++		reg_id = -1;
++
++	return reg_id;
++}
++
++static ssize_t show_vreg(struct device *dev, struct device_attribute *attr,
++			 char *buf)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct pcf50606_data *pcf = i2c_get_clientdata(client);
++	unsigned int reg_id;
++
++	reg_id = reg_id_by_name(attr->attr.name);
++	if (reg_id < 0)
++		return 0;
++
++	if (pcf50606_onoff_get(pcf, reg_id) > 0)
++		return sprintf(buf, "%u\n", pcf50606_voltage_get(pcf, reg_id));
++	else
++		return strlcpy(buf, "0\n", PAGE_SIZE);
++}
++
++static ssize_t set_vreg(struct device *dev, struct device_attribute *attr,
++			const char *buf, size_t count)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct pcf50606_data *pcf = i2c_get_clientdata(client);
++	unsigned long mvolts = simple_strtoul(buf, NULL, 10);
++	unsigned int reg_id;
++
++	reg_id = reg_id_by_name(attr->attr.name);
++	if (reg_id < 0)
++		return -EIO;
++
++	/* FIXME: consult platform-specific table to make sure we don't
++	 * set a too high voltage on any regulator! */
++
++	DEBUGP("setting %s(%d) to %lu mvolts\n", attr->attr.name, reg_id,
++		mvolts);
++
++	if (mvolts == 0) {
++		pcf50606_onoff_set(pcf, reg_id, 0);
++	} else {
++		pcf50606_voltage_set(pcf, reg_id, mvolts);
++		pcf50606_onoff_set(pcf, reg_id, 1);
++	}
++
++	return count;
++}
++
++static DEVICE_ATTR(voltage_dcd, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_dcde, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_dcud, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_d1reg, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_d2reg, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_d3reg, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_lpreg, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ioreg, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++
++/***********************************************************************
++ * Charger Control
++ ***********************************************************************/
++
++/* Enable/disable fast charging (500mA in the GTA01) */
++void pcf50606_charge_fast(struct pcf50606_data *pcf, int on)
++{
++	if (on) {
++		/* We can allow PCF to automatically charge
++		 * using Ifast */
++		pcf->flags |= PCF50606_F_CHG_FAST;
++		reg_set_bit_mask(pcf, PCF50606_REG_MBCC1,
++				 PCF50606_MBCC1_AUTOFST,
++				 PCF50606_MBCC1_AUTOFST);
++	} else {
++		pcf->flags &= ~PCF50606_F_CHG_FAST;
++		/* disable automatic fast-charge */
++		reg_clear_bits(pcf, PCF50606_REG_MBCC1,
++				PCF50606_MBCC1_AUTOFST);
++		/* switch to idle mode to abort existing charge
++		 * process */
++		reg_set_bit_mask(pcf, PCF50606_REG_MBCC1,
++				 PCF50606_MBCC1_CHGMOD_MASK,
++				 PCF50606_MBCC1_CHGMOD_IDLE);
++	}
++}
++EXPORT_SYMBOL(pcf50606_charge_fast);
++
++#define PCF50606_R_FIXBATT	10000	/* 10 kOhms */
++static inline u_int16_t adc_to_rntc(u_int16_t adc)
++{
++	return (adc * PCF50606_R_FIXBATT) / (1023 - adc);
++}
++
++static inline int16_t rntc_to_temp(u_int16_t rntc)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(ntc_table_tn11_3h103j); i++) {
++		if (rntc > ntc_table_tn11_3h103j[i])
++			return i - 10;
++	}
++	return 2342;
++}
++
++static ssize_t show_battemp(struct device *dev, struct device_attribute *attr,
++			    char *buf)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct pcf50606_data *pcf = i2c_get_clientdata(client);
++	u_int16_t adc;
++
++	adc = adc_read(pcf, PCF50606_ADCMUX_BATTEMP, NULL);
++
++	return sprintf(buf, "%d\n", rntc_to_temp(adc_to_rntc(adc)));
++}
++static DEVICE_ATTR(battemp, S_IRUGO | S_IWUSR, show_battemp, NULL);
++
++#define MULT_R_SENSE_1024 225	/* 0.22 * 1024 */
++static inline u_int16_t adc_to_chg_milliamps(u_int16_t adc_adcin1,
++					     u_int16_t adc_batvolt)
++{
++	return (((adc_adcin1 - adc_batvolt) * 24) / 10) / MULT_R_SENSE_1024;
++}
++
++static ssize_t show_chgcur(struct device *dev, struct device_attribute *attr,
++			   char *buf)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct pcf50606_data *pcf = i2c_get_clientdata(client);
++	u_int16_t adc_batvolt, adc_adcin1;
++	u_int16_t ma;
++
++	adc_batvolt = adc_read(pcf, PCF50606_ADCMUX_BATVOLT_ADCIN1,
++			       &adc_adcin1);
++	ma = adc_to_chg_milliamps(adc_adcin1, adc_batvolt);
++
++	return sprintf(buf, "%u\n", ma);
++}
++static DEVICE_ATTR(chgcur, S_IRUGO | S_IWUSR, show_chgcur, NULL);
++
++static const char *chgstate_names[] = {
++	[PCF50606_MBCC1_CHGMOD_QUAL]		= "qualification",
++	[PCF50606_MBCC1_CHGMOD_PRE]		= "pre",
++	[PCF50606_MBCC1_CHGMOD_TRICKLE]		= "trickle",
++	[PCF50606_MBCC1_CHGMOD_FAST_CCCV]	= "fast_cccv",
++	[PCF50606_MBCC1_CHGMOD_FAST_NOCC]	= "fast_nocc",
++	[PCF50606_MBCC1_CHGMOD_FAST_NOCV]	= "fast_nocv",
++	[PCF50606_MBCC1_CHGMOD_FAST_SW]		= "fast_switch",
++	[PCF50606_MBCC1_CHGMOD_IDLE]		= "idle",
++};
++
++static ssize_t show_chgstate(struct device *dev, struct device_attribute *attr,
++			     char *buf)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct pcf50606_data *pcf = i2c_get_clientdata(client);
++	u_int8_t mbcc1 = reg_read(pcf, PCF50606_REG_MBCC1);
++	u_int8_t chgmod = (mbcc1 & PCF50606_MBCC1_CHGMOD_MASK);
++
++	return sprintf(buf, "%s\n", chgstate_names[chgmod]);
++}
++static DEVICE_ATTR(chgstate, S_IRUGO | S_IWUSR, show_chgstate, NULL);
++
++/***********************************************************************
++ * APM emulation
++ ***********************************************************************/
++
++extern void (*apm_get_power_status)(struct apm_power_info *);
++
++static void pcf50606_get_power_status(struct apm_power_info *info)
++{
++        struct pcf50606_data *pcf = pcf50606_global;
++	u_int8_t mbcc1 = reg_read(pcf, PCF50606_REG_MBCC1);
++	u_int8_t chgmod = mbcc1 & PCF50606_MBCC1_CHGMOD_MASK;
++	u_int16_t battvolt = pcf50606_battvolt(pcf);
++
++	if (reg_read(pcf, PCF50606_REG_OOCS) & PCF50606_OOCS_EXTON)
++		info->ac_line_status = APM_AC_ONLINE;
++	else
++		info->ac_line_status = APM_AC_OFFLINE;
++
++	switch (chgmod) {
++	case PCF50606_MBCC1_CHGMOD_QUAL:
++	case PCF50606_MBCC1_CHGMOD_PRE:
++	case PCF50606_MBCC1_CHGMOD_IDLE:
++		info->battery_life = battvolt_scale(battvolt);
++		break;
++	default:
++		info->battery_status = APM_BATTERY_STATUS_CHARGING;
++		info->battery_flag = APM_BATTERY_FLAG_CHARGING;
++		break;
++	}
++}
++
++/***********************************************************************
++ * RTC
++ ***********************************************************************/
++
++struct pcf50606_time {
++	u_int8_t sec;
++	u_int8_t min;
++	u_int8_t hour;
++	u_int8_t wkday;
++	u_int8_t day;
++	u_int8_t month;
++	u_int8_t year;
++};
++
++static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50606_time *pcf)
++{
++	rtc->tm_sec = BCD2BIN(pcf->sec);
++	rtc->tm_min = BCD2BIN(pcf->min);
++	rtc->tm_hour = BCD2BIN(pcf->hour);
++	rtc->tm_wday = BCD2BIN(pcf->wkday);
++	rtc->tm_mday = BCD2BIN(pcf->day);
++	rtc->tm_mon = BCD2BIN(pcf->month);
++	rtc->tm_year = BCD2BIN(pcf->year) + 100;
++}
++
++static void rtc2pcf_time(struct pcf50606_time *pcf, struct rtc_time *rtc)
++{
++	pcf->sec = BIN2BCD(rtc->tm_sec);
++	pcf->min = BIN2BCD(rtc->tm_min);
++	pcf->hour = BIN2BCD(rtc->tm_hour);
++	pcf->wkday = BIN2BCD(rtc->tm_wday);
++	pcf->day = BIN2BCD(rtc->tm_mday);
++	pcf->month = BIN2BCD(rtc->tm_mon) + 1;
++	pcf->year = BIN2BCD(rtc->tm_year - 100);
++}
++
++static int pcf50606_rtc_ioctl(struct device *dev, unsigned int cmd,
++			      unsigned long arg)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct pcf50606_data *pcf = i2c_get_clientdata(client);
++	switch (cmd) {
++	case RTC_PIE_OFF:
++		/* disable periodic interrupt (hz tick) */
++		reg_set_bit_mask(pcf, PCF50606_REG_INT1M,
++				 PCF50606_INT1_SECOND, PCF50606_INT1_SECOND);
++		return 0;
++	case RTC_PIE_ON:
++		/* ensable periodic interrupt (hz tick) */
++		reg_clear_bits(pcf, PCF50606_REG_INT1M, PCF50606_INT1_SECOND);
++		return 0;
++	}
++	return -ENOIOCTLCMD;
++}
++
++static int pcf50606_rtc_read_time(struct device *dev, struct rtc_time *tm)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct pcf50606_data *pcf = i2c_get_clientdata(client);
++	struct pcf50606_time pcf_tm;
++
++	spin_lock_irq(&pcf->lock);
++	pcf_tm.sec = __reg_read(pcf, PCF50606_REG_RTCSC);
++	pcf_tm.min = __reg_read(pcf, PCF50606_REG_RTCMN);
++	pcf_tm.hour = __reg_read(pcf, PCF50606_REG_RTCHR);
++	pcf_tm.wkday = __reg_read(pcf, PCF50606_REG_RTCWD);
++	pcf_tm.day = __reg_read(pcf, PCF50606_REG_RTCDT);
++	pcf_tm.month = __reg_read(pcf, PCF50606_REG_RTCMT);
++	pcf_tm.year = __reg_read(pcf, PCF50606_REG_RTCYR);
++	spin_unlock_irq(&pcf->lock);
++
++	DEBUGP("PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
++		pcf_tm.day, pcf_tm.month, pcf_tm.year,
++		pcf_tm.hour, pcf_tm.min, pcf_tm.sec);
++
++	pcf2rtc_time(tm, &pcf_tm);
++
++	DEBUGP("RTC_TIME: %u.%u.%u %u:%u:%u\n",
++		tm->tm_mday, tm->tm_mon, tm->tm_year,
++		tm->tm_hour, tm->tm_min, tm->tm_sec);
++
++	return 0;
++}
++
++static int pcf50606_rtc_set_time(struct device *dev, struct rtc_time *tm)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct pcf50606_data *pcf = i2c_get_clientdata(client);
++	struct pcf50606_time pcf_tm;
++
++	DEBUGP("RTC_TIME: %u.%u.%u %u:%u:%u\n",
++		tm->tm_mday, tm->tm_mon, tm->tm_year,
++		tm->tm_hour, tm->tm_min, tm->tm_sec);
++	rtc2pcf_time(&pcf_tm, tm);
++	DEBUGP("PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
++		pcf_tm.day, pcf_tm.month, pcf_tm.year,
++		pcf_tm.hour, pcf_tm.min, pcf_tm.sec);
++
++	spin_lock_irq(&pcf->lock);
++	/* FIXME: disable second interrupt */
++	__reg_write(pcf, PCF50606_REG_RTCSC, pcf_tm.sec);
++	__reg_write(pcf, PCF50606_REG_RTCMN, pcf_tm.min);
++	__reg_write(pcf, PCF50606_REG_RTCHR, pcf_tm.hour);
++	__reg_write(pcf, PCF50606_REG_RTCWD, pcf_tm.wkday);
++	__reg_write(pcf, PCF50606_REG_RTCDT, pcf_tm.day);
++	__reg_write(pcf, PCF50606_REG_RTCMT, pcf_tm.month);
++	__reg_write(pcf, PCF50606_REG_RTCYR, pcf_tm.year);
++	/* FIXME: re-enable second interrupt */
++	spin_unlock_irq(&pcf->lock);
++
++	return 0;
++}
++
++static int pcf50606_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct pcf50606_data *pcf = i2c_get_clientdata(client);
++	struct pcf50606_time pcf_tm;
++
++	spin_lock_irq(&pcf->lock);
++	pcf_tm.sec = __reg_read(pcf, PCF50606_REG_RTCSCA);
++	pcf_tm.min = __reg_read(pcf, PCF50606_REG_RTCMNA);
++	pcf_tm.hour = __reg_read(pcf, PCF50606_REG_RTCHRA);
++	pcf_tm.wkday = __reg_read(pcf, PCF50606_REG_RTCWDA);
++	pcf_tm.day = __reg_read(pcf, PCF50606_REG_RTCDTA);
++	pcf_tm.month = __reg_read(pcf, PCF50606_REG_RTCMTA);
++	pcf_tm.year = __reg_read(pcf, PCF50606_REG_RTCYRA);
++	spin_unlock_irq(&pcf->lock);
++
++	pcf2rtc_time(&alrm->time, &pcf_tm);
++
++	return 0;
++}
++
++static int pcf50606_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct pcf50606_data *pcf = i2c_get_clientdata(client);
++	struct pcf50606_time pcf_tm;
++	u_int8_t irqmask;
++
++	rtc2pcf_time(&pcf_tm, &alrm->time);
++
++	spin_lock_irq(&pcf->lock);
++
++	/* disable alarm interrupt */
++	irqmask = __reg_read(pcf, PCF50606_REG_INT1M);
++	irqmask |= PCF50606_INT1_ALARM;
++	__reg_write(pcf, PCF50606_REG_INT1M, irqmask);
++
++	__reg_write(pcf, PCF50606_REG_RTCSCA, pcf_tm.sec);
++	__reg_write(pcf, PCF50606_REG_RTCMNA, pcf_tm.min);
++	__reg_write(pcf, PCF50606_REG_RTCHRA, pcf_tm.hour);
++	__reg_write(pcf, PCF50606_REG_RTCWDA, pcf_tm.wkday);
++	__reg_write(pcf, PCF50606_REG_RTCDTA, pcf_tm.day);
++	__reg_write(pcf, PCF50606_REG_RTCMNA, pcf_tm.month);
++	__reg_write(pcf, PCF50606_REG_RTCYRA, pcf_tm.year);
++
++	if (alrm->enabled) {
++		/* (re-)enaable alarm interrupt */
++		irqmask = __reg_read(pcf, PCF50606_REG_INT1M);
++		irqmask &= ~PCF50606_INT1_ALARM;
++		__reg_write(pcf, PCF50606_REG_INT1M, irqmask);
++	}
++
++	spin_unlock_irq(&pcf->lock);
++
++	/* FIXME */
++	return 0;
++}
++
++/***********************************************************************
++ * Watchdog
++ ***********************************************************************/
++
++static void pcf50606_wdt_start(struct pcf50606_data *pcf)
++{
++	reg_set_bit_mask(pcf, PCF50606_REG_OOCC1, PCF50606_OOCC1_WDTRST,
++			 PCF50606_OOCC1_WDTRST);
++}
++
++static void pcf50606_wdt_stop(struct pcf50606_data *pcf)
++{
++	reg_clear_bits(pcf, PCF50606_REG_OOCS, PCF50606_OOCS_WDTEXP);
++}
++
++static void pcf50606_wdt_keepalive(struct pcf50606_data *pcf)
++{
++	pcf50606_wdt_start(pcf);
++}
++
++static int pcf50606_wdt_open(struct inode *inode, struct file *file)
++{
++	struct pcf50606_data *pcf = pcf50606_global;
++
++	file->private_data = pcf;
++
++	/* start the timer */
++	pcf50606_wdt_start(pcf);
++
++	return nonseekable_open(inode, file);
++}
++
++static int pcf50606_wdt_release(struct inode *inode, struct file *file)
++{
++	struct pcf50606_data *pcf = file->private_data;
++
++	if (pcf->allow_close == CLOSE_STATE_ALLOW)
++		pcf50606_wdt_stop(pcf);
++	else {
++		printk(KERN_CRIT "Unexpected close, not stopping watchdog!\n");
++		pcf50606_wdt_keepalive(pcf);
++	}
++
++	pcf->allow_close = CLOSE_STATE_NOT;
++
++	return 0;
++}
++
++static ssize_t pcf50606_wdt_write(struct file *file, const char __user *data,
++				  size_t len, loff_t *ppos)
++{
++	struct pcf50606_data *pcf = file->private_data;
++	if (len) {
++		size_t i;
++
++		for (i = 0; i != len; i++) {
++			char c;
++			if (get_user(c, data + i))
++				return -EFAULT;
++			if (c == 'V')
++				pcf->allow_close = CLOSE_STATE_ALLOW;
++		}
++		pcf50606_wdt_keepalive(pcf);
++	}
++
++	return len;
++}
++
++static struct watchdog_info pcf50606_wdt_ident = {
++	.options	= WDIOF_MAGICCLOSE,
++	.firmware_version = 0,
++	.identity	= "PCF50606 Watchdog",
++};
++
++static int pcf50606_wdt_ioctl(struct inode *inode, struct file *file,
++			      unsigned int cmd, unsigned long arg)
++{
++	struct pcf50606_data *pcf = file->private_data;
++	void __user *argp = (void __user *)arg;
++	int __user *p = argp;
++
++	switch (cmd) {
++	case WDIOC_GETSUPPORT:
++		return copy_to_user(argp, &pcf50606_wdt_ident,
++				    sizeof(pcf50606_wdt_ident)) ? -EFAULT : 0;
++		break;
++	case WDIOC_GETSTATUS:
++	case WDIOC_GETBOOTSTATUS:
++		return put_user(0, p);
++	case WDIOC_KEEPALIVE:
++		pcf50606_wdt_keepalive(pcf);
++		return 0;
++	case WDIOC_GETTIMEOUT:
++		return put_user(8, p);
++	default:
++		return -ENOIOCTLCMD;
++	}
++}
++
++static struct file_operations pcf50606_wdt_fops = {
++	.owner		= THIS_MODULE,
++	.llseek		= no_llseek,
++	.write		= &pcf50606_wdt_write,
++	.ioctl		= &pcf50606_wdt_ioctl,
++	.open		= &pcf50606_wdt_open,
++	.release	= &pcf50606_wdt_release,
++};
++
++static struct miscdevice pcf50606_wdt_miscdev = {
++	.minor		= WATCHDOG_MINOR,
++	.name		= "watchdog",
++	.fops		= &pcf50606_wdt_fops,
++};
++
++static struct i2c_driver pcf50606_driver;
++
++static struct rtc_class_ops pcf50606_rtc_ops = {
++	.ioctl		= pcf50606_rtc_ioctl,
++	.read_time	= pcf50606_rtc_read_time,
++	.set_time	= pcf50606_rtc_set_time,
++	.read_alarm	= pcf50606_rtc_read_alarm,
++	.set_alarm	= pcf50606_rtc_set_alarm,
++};
++
++/***********************************************************************
++ * Input device
++ ***********************************************************************/
++
++static int pcf50606_detect(struct i2c_adapter *adapter, int address, int kind)
++{
++	struct i2c_client *new_client;
++	struct pcf50606_data *data;
++	int err = 0;
++
++	DEBUGP("entering\n");
++
++	/* At the moment, we only support one PCF50606 in a system */
++	if (pcf50606_global) {
++		printk(KERN_ERR
++			"pcf50606: currently only one chip supported\n");
++		return -EBUSY;
++	}
++
++	if (!(data = kzalloc(sizeof(*data), GFP_KERNEL)))
++		return -ENOMEM;
++
++
++	INIT_WORK(&data->work, pcf50606_work, data);
++	data->working = 0;
++	new_client = &data->client;
++	i2c_set_clientdata(new_client, data);
++	new_client->addr = address;
++	new_client->adapter = adapter;
++	new_client->driver = &pcf50606_driver;
++	new_client->flags = 0;
++	strlcpy(new_client->name, "pcf50606", I2C_NAME_SIZE);
++
++	/* now we try to detect the chip */
++
++	/* register with i2c core */
++	if ((err = i2c_attach_client(new_client))) {
++		DEBUGP("error during i2c_attach_client()\n");
++		goto exit_free;
++	}
++
++	pcf50606_global = data;
++
++	device_create_file(&new_client->dev, &dev_attr_battvolt);
++	device_create_file(&new_client->dev, &dev_attr_battemp);
++	device_create_file(&new_client->dev, &dev_attr_chgcur);
++	device_create_file(&new_client->dev, &dev_attr_chgstate);
++	device_create_file(&new_client->dev, &dev_attr_voltage_dcd);
++	device_create_file(&new_client->dev, &dev_attr_voltage_dcde);
++	device_create_file(&new_client->dev, &dev_attr_voltage_dcud);
++	device_create_file(&new_client->dev, &dev_attr_voltage_d1reg);
++	device_create_file(&new_client->dev, &dev_attr_voltage_d2reg);
++	device_create_file(&new_client->dev, &dev_attr_voltage_d3reg);
++	device_create_file(&new_client->dev, &dev_attr_voltage_lpreg);
++	device_create_file(&new_client->dev, &dev_attr_voltage_ioreg);
++
++	/* create virtual charger 'device' */
++
++	/* register power off handler with core power management */
++	pm_power_off = &pcf50606_go_standby;
++
++	/* configure interrupt mask */
++	reg_write(data, PCF50606_REG_INT1M, PCF50606_INT1_SECOND);
++	reg_write(data, PCF50606_REG_INT2M, 0x00);
++	reg_write(data, PCF50606_REG_INT3M, PCF50606_INT3_TSCPRES);
++
++	/* FIXME: make this IRQ a resource */
++	set_irq_type(GTA01_IRQ_PCF50606, IRQT_FALLING);
++	err = request_irq(GTA01_IRQ_PCF50606, pcf50606_irq, SA_INTERRUPT,
++			  "pcf50606", data);
++	if (err < 0)
++		goto exit_detach;
++
++	data->rtc = rtc_device_register("pcf50606", &new_client->dev,
++					&pcf50606_rtc_ops, THIS_MODULE);
++	if (IS_ERR(data->rtc)) {
++		err = PTR_ERR(data->rtc);
++		goto exit_irq;
++	}
++
++	err = misc_register(&pcf50606_wdt_miscdev);
++	if (err) {
++		printk(KERN_ERR "cannot register miscdev on minor=%d (%d)\n",
++			WATCHDOG_MINOR, err);
++		goto exit_rtc;
++	}
++
++	data->input_dev = input_allocate_device();
++	if (!data->input_dev)
++		goto exit_misc;
++
++	data->input_dev->name = "FIC Neo1973 PMU events";
++	data->input_dev->phys = "FIXME";
++	data->input_dev->id.bustype = BUS_I2C;
++	data->input_dev->cdev.dev = &new_client->dev;
++
++	data->input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR);
++	set_bit(KEY_POWER, data->input_dev->keybit);
++	set_bit(KEY_POWER2, data->input_dev->keybit);
++	set_bit(KEY_BATTERY, data->input_dev->keybit);
++
++	input_register_device(data->input_dev);
++
++	apm_get_power_status = pcf50606_get_power_status;
++
++	return 0;
++exit_misc:
++	misc_deregister(&pcf50606_wdt_miscdev);
++exit_rtc:
++	rtc_device_unregister(pcf50606_global->rtc);
++exit_irq:
++	free_irq(GTA01_IRQ_PCF50606, pcf50606_global);
++exit_detach:
++	i2c_detach_client(new_client);
++exit_free:
++	kfree(data);
++	pcf50606_global = NULL;
++	return err;
++}
++
++static int pcf50606_attach_adapter(struct i2c_adapter *adapter)
++{
++	DEBUGP("entering, calling i2c_probe\n");
++	return i2c_probe(adapter, &addr_data, &pcf50606_detect);
++}
++
++static int pcf50606_detach_client(struct i2c_client *client)
++{
++	struct pcf50606_data *pcf = i2c_get_clientdata(client);
++
++	DEBUGP("entering\n");
++
++	apm_get_power_status = NULL;
++	input_unregister_device(pcf->input_dev);
++	misc_deregister(&pcf50606_wdt_miscdev);
++	free_irq(GTA01_IRQ_PCF50606, pcf);
++	rtc_device_unregister(pcf->rtc);
++	pm_power_off = NULL;
++
++	device_remove_file(&client->dev, &dev_attr_battvolt);
++	device_remove_file(&client->dev, &dev_attr_battemp);
++	device_remove_file(&client->dev, &dev_attr_chgcur);
++	device_remove_file(&client->dev, &dev_attr_chgstate);
++	device_remove_file(&client->dev, &dev_attr_voltage_dcd);
++	device_remove_file(&client->dev, &dev_attr_voltage_dcde);
++	device_remove_file(&client->dev, &dev_attr_voltage_dcud);
++	device_remove_file(&client->dev, &dev_attr_voltage_d1reg);
++	device_remove_file(&client->dev, &dev_attr_voltage_d2reg);
++	device_remove_file(&client->dev, &dev_attr_voltage_d3reg);
++	device_remove_file(&client->dev, &dev_attr_voltage_lpreg);
++	device_remove_file(&client->dev, &dev_attr_voltage_ioreg);
++
++	kfree(pcf);
++
++	return 0;
++}
++
++static struct i2c_driver pcf50606_driver = {
++	.driver = {
++		.name	= "pcf50606",
++	},
++	.id		= I2C_DRIVERID_PCF50606,
++	.attach_adapter	= &pcf50606_attach_adapter,
++	.detach_client	= &pcf50606_detach_client,
++};
++
++static int __init pcf50606_init(void)
++{
++	DEBUGP("entering, calling i2c_add_driver\n");
++	return i2c_add_driver(&pcf50606_driver);
++}
++
++static void __exit pcf50606_exit(void)
++{
++	i2c_del_driver(&pcf50606_driver);
++}
++
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_LICENSE("GPL");
++
++module_init(pcf50606_init);
++module_exit(pcf50606_exit);
+Index: linux-2.6.17.14-fic4.test/drivers/i2c/chips/pcf50606.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/drivers/i2c/chips/pcf50606.h	2007-01-21 13:18:11.000000000 +0100
+@@ -0,0 +1,259 @@
++#ifndef _PCF50606_H
++#define _PCF50606_H
++
++/* Philips PCF50606 Power Managemnt Unit (PMU) driver
++ * (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ *
++ */
++
++enum pfc50606_regs {
++	PCF50606_REG_ID		= 0x00,
++	PCF50606_REG_OOCS	= 0x01,
++	PCF50606_REG_INT1	= 0x02,	/* Interrupt Status */
++	PCF50606_REG_INT2	= 0x03,	/* Interrupt Status */
++	PCF50606_REG_INT3	= 0x04,	/* Interrupt Status */
++	PCF50606_REG_INT1M	= 0x05,	/* Interrupt Mask */
++	PCF50606_REG_INT2M	= 0x06,	/* Interrupt Mask */
++	PCF50606_REG_INT3M	= 0x07,	/* Interrupt Mask */
++	PCF50606_REG_OOCC1	= 0x08,
++	PCF50606_REG_OOCC2	= 0x09,
++	PCF50606_REG_RTCSC	= 0x0a,	/* Second */
++	PCF50606_REG_RTCMN	= 0x0b,	/* Minute */
++	PCF50606_REG_RTCHR	= 0x0c,	/* Hour */
++	PCF50606_REG_RTCWD	= 0x0d,	/* Weekday */
++	PCF50606_REG_RTCDT	= 0x0e,	/* Day */
++	PCF50606_REG_RTCMT	= 0x0f,	/* Month */
++	PCF50606_REG_RTCYR	= 0x10,	/* Year */
++	PCF50606_REG_RTCSCA	= 0x11, /* Alarm Second */
++	PCF50606_REG_RTCMNA	= 0x12, /* Alarm Minute */
++	PCF50606_REG_RTCHRA	= 0x13, /* Alarm Hour */
++	PCF50606_REG_RTCWDA	= 0x14, /* Alarm Weekday */
++	PCF50606_REG_RTCDTA	= 0x15, /* Alarm Day */
++	PCF50606_REG_RTCMTA	= 0x16, /* Alarm Month */
++	PCF50606_REG_RTCYRA	= 0x17, /* Alarm Year */
++	PCF50606_REG_PSSC	= 0x18,	/* Power sequencing */
++	PCF50606_REG_PWROKM	= 0x19,	/* PWROK mask */
++	PCF50606_REG_PWROKS	= 0x1a,	/* PWROK status */
++	PCF50606_REG_DCDC1	= 0x1b,
++	PCF50606_REG_DCDC2	= 0x1c,
++	PCF50606_REG_DCDC3	= 0x1d,
++	PCF50606_REG_DCDC4	= 0x1e,
++	PCF50606_REG_DCDEC1	= 0x1f,
++	PCF50606_REG_DCDEC2	= 0x20,
++	PCF50606_REG_DCUDC1	= 0x21,
++	PCF50606_REG_DCUDC2	= 0x22,
++	PCF50606_REG_IOREGC	= 0x23,
++	PCF50606_REG_D1REGC1	= 0x24,
++	PCF50606_REG_D2REGC1	= 0x25,
++	PCF50606_REG_D3REGC1	= 0x26,
++	PCF50606_REG_LPREGC1	= 0x27,
++	PCF50606_REG_LPREGC2	= 0x28,
++	PCF50606_REG_MBCC1	= 0x29,
++	PCF50606_REG_MBCC2	= 0x2a,
++	PCF50606_REG_MBCC3	= 0x2b,
++	PCF50606_REG_MBCS1	= 0x2c,
++	PCF50606_REG_BBCC	= 0x2d,
++	PCF50606_REG_ADCC1	= 0x2e,
++	PCF50606_REG_ADCC2	= 0x2f,
++	PCF50606_REG_ADCS1	= 0x30,
++	PCF50606_REG_ADCS2	= 0x31,
++	PCF50606_REG_ADCS3	= 0x32,
++	PCF50606_REG_ACDC1	= 0x33,
++	PCF50606_REG_BVMC	= 0x34,
++	PCF50606_REG_PWMC1	= 0x35,
++	PCF50606_REG_LEDC1	= 0x36,
++	PCF50606_REG_LEDC2	= 0x37,
++	PCF50606_REG_GPOC1	= 0x38,
++	PCF50606_REG_GPOC2	= 0x39,
++	PCF50606_REG_GPOC3	= 0x3a,
++	PCF50606_REG_GPOC4	= 0x3b,
++	PCF50606_REG_GPOC5	= 0x3c,
++	__NUM_PCF50606_REGS
++};
++
++enum pcf50606_reg_oocs {
++	PFC50606_OOCS_ONKEY	= 0x01,
++	PCF50606_OOCS_EXTON	= 0x02,
++	PCF50606_OOCS_PWROKRST	= 0x04,
++	PCF50606_OOCS_BATOK	= 0x08,
++	PCF50606_OOCS_BACKOK	= 0x10,
++	PCF50606_OOCS_CHGOK	= 0x20,
++	PCF50606_OOCS_TEMPOK	= 0x40,
++	PCF50606_OOCS_WDTEXP	= 0x80,
++};
++
++enum pcf50606_reg_oocc1 {
++	PCF50606_OOCC1_GOSTDBY	= 0x01,
++	PCF50606_OOCC1_TOTRST	= 0x02,
++	PCF50606_OOCC1_CLK32ON	= 0x04,
++	PCF50606_OOCC1_WDTRST	= 0x08,
++	PCF50606_OOCC1_RTCWAK	= 0x10,
++	PCF50606_OOCC1_CHGWAK	= 0x20,
++	PCF50606_OOCC1_EXTONWAK_HIGH	= 0x40,
++	PCF50606_OOCC1_EXTONWAK_LOW	= 0x80,
++};
++
++enum pcf50606_reg_oocc2 {
++	PCF50606_OOCC2_ONKEYDB_NONE	= 0x00,
++	PCF50606_OOCC2_ONKEYDB_14ms	= 0x01,
++	PCF50606_OOCC2_ONKEYDB_62ms	= 0x02,
++	PCF50606_OOCC2_ONKEYDB_500ms	= 0x03,
++	PCF50606_OOCC2_EXTONDB_NONE	= 0x00,
++	PCF50606_OOCC2_EXTONDB_14ms	= 0x04,
++	PCF50606_OOCC2_EXTONDB_62ms	= 0x08,
++	PCF50606_OOCC2_EXTONDB_500ms	= 0x0c,
++};
++
++enum pcf50606_reg_int1 {
++	PCF50606_INT1_ONKEYR	= 0x01,	/* ONKEY rising edge */
++	PCF50606_INT1_ONKEYF	= 0x02,	/* ONKEY falling edge */
++	PCF50606_INT1_ONKEY1S	= 0x04,	/* OMKEY at least 1sec low */
++	PCF50606_INT1_EXTONR	= 0x08,	/* EXTON rising edge */
++	PCF50606_INT1_EXTONF	= 0x10,	/* EXTON falling edge */
++	PCF50606_INT1_SECOND	= 0x40,	/* RTC periodic second interrupt */
++	PCF50606_INT1_ALARM	= 0x80, /* RTC alarm time is reached */
++};
++
++enum pcf50606_reg_int2 {
++	PCF50606_INT2_CHGINS	= 0x01, /* Charger inserted */
++	PCF50606_INT2_CHGRM	= 0x02, /* Charger removed */
++	PCF50606_INT2_CHGFOK	= 0x04,	/* Fast charging OK */
++	PCF50606_INT2_CHGERR	= 0x08,	/* Error in charging mode */
++	PCF50606_INT2_CHGFRDY	= 0x10,	/* Fast charge completed */
++	PCF50606_INT2_CHGPROT	= 0x20,	/* Charging protection interrupt */
++	PCF50606_INT2_CHGWD10S	= 0x40,	/* Charger watchdig expires in 10s */
++	PCF50606_INT2_CHGWDEXP	= 0x80,	/* Charger watchdog expires */
++};
++
++enum pcf50606_reg_int3 {
++	PCF50606_INT3_ADCRDY	= 0x01,	/* ADC conversion finished */
++	PCF50606_INT3_ACDINS	= 0x02,	/* Accessory inserted */
++	PCF50606_INT3_ACDREM	= 0x04, /* Accessory removed */
++	PCF50606_INT3_TSCPRES	= 0x08,	/* Touch screen pressed */
++	PCF50606_INT3_LOWBAT	= 0x40,	/* Low battery voltage */
++	PCF50606_INT3_HIGHTMP	= 0x80, /* High temperature */
++};
++
++/* used by PSSC, PWROKM, PWROKS, */
++enum pcf50606_regu {
++	PCF50606_REGU_DCD	= 0x01,	/* DCD in phase 2 */
++	PCF50606_REGU_DCDE	= 0x02,	/* DCDE in phase 2 */
++	PCF50606_REGU_DCUD	= 0x04,	/* DCDU in phase 2 */
++	PCF50606_REGU_IO	= 0x08,	/* IO in phase 2 */
++	PCF50606_REGU_D1	= 0x10, /* D1 in phase 2 */
++	PCF50606_REGU_D2	= 0x20,	/* D2 in phase 2 */
++	PCF50606_REGU_D3	= 0x40,	/* D3 in phase 2 */
++	PCF50606_REGU_LP	= 0x80,	/* LP in phase 2 */
++};
++
++enum pcf50606_reg_dcdc4 {
++	PCF50606_DCDC4_MODE_AUTO	= 0x00,
++	PCF50606_DCDC4_MODE_PWM		= 0x01,
++	PCF50606_DCDC4_MODE_PCF		= 0x02,
++	PCF50606_DCDC4_OFF_FLOAT	= 0x00,
++	PCF50606_DCDC4_OFF_BYPASS	= 0x04,
++	PCF50606_DCDC4_OFF_PULLDOWN	= 0x08,
++	PCF50606_DCDC4_CURLIM_500mA	= 0x00,
++	PCF50606_DCDC4_CURLIM_750mA	= 0x10,
++	PCF50606_DCDC4_CURLIM_1000mA	= 0x20,
++	PCF50606_DCDC4_CURLIM_1250mA	= 0x30,
++	PCF50606_DCDC4_TOGGLE		= 0x40,
++	PCF50606_DCDC4_REGSEL_DCDC2	= 0x80,
++};
++
++enum pcf50606_reg_dcdec2 {
++	PCF50606_DCDEC2_MODE_AUTO	= 0x00,
++	PCF50606_DCDEC2_MODE_PWM	= 0x01,
++	PCF50606_DCDEC2_MODE_PCF	= 0x02,
++	PCF50606_DCDEC2_OFF_FLOAT	= 0x00,
++	PCF50606_DCDEC2_OFF_BYPASS	= 0x04,
++};
++
++enum pcf50606_reg_dcudc2 {
++	PCF50606_DCUDC2_MODE_AUTO	= 0x00,
++	PCF50606_DCUDC2_MODE_PWM	= 0x01,
++	PCF50606_DCUDC2_MODE_PCF	= 0x02,
++	PCF50606_DCUDC2_OFF_FLOAT	= 0x00,
++	PCF50606_DCUDC2_OFF_BYPASS	= 0x04,
++};
++
++enum pcf50606_reg_adcc1 {
++	PCF50606_ADCC1_TSCMODACT	= 0x01,
++	PCF50606_ADCC1_TSCMODSTB	= 0x02,
++	PCF50606_ADCC1_TRATSET		= 0x04,
++	PCF50606_ADCC1_NTCSWAPE		= 0x08,
++	PCF50606_ADCC1_NTCSWAOFF	= 0x10,
++	PCF50606_ADCC1_EXTSYNCBREAK	= 0x20,
++	/* reserved */
++	PCF50606_ADCC1_TSCINT		= 0x80,
++};
++
++enum pcf50606_reg_adcc2 {
++	PCF50606_ADCC2_ADCSTART		= 0x01,
++	/* see enum pcf50606_adcc2_adcmux */
++	PCF50606_ADCC2_SYNC_NONE	= 0x00,
++	PCF50606_ADCC2_SYNC_TXON	= 0x20,
++	PCF50606_ADCC2_SYNC_PWREN1	= 0x40,
++	PCF50606_ADCC2_SYNC_PWREN2	= 0x60,
++	PCF50606_ADCC2_RES_10BIT	= 0x00,
++	PCF50606_ADCC2_RES_8BIT		= 0x80,
++};
++
++#define PCF50606_ADCC2_ADCMUX_MASK	(0xf << 1)
++
++#define ADCMUX_SHIFT	1
++enum pcf50606_adcc2_adcmux {
++	PCF50606_ADCMUX_BATVOLT_RES	= 0x0 << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_BATVOLT_SUBTR	= 0x1 << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_ADCIN1_RES	= 0x2 << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_ADCIN1_SUBTR	= 0x3 << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_BATTEMP		= 0x4 << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_ADCIN2		= 0x5 << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_ADCIN3		= 0x6 << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_ADCIN3_RATIO	= 0x7 << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_XPOS		= 0x8 << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_YPOS		= 0x9 << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_P1		= 0xa << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_P2		= 0xb << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_BATVOLT_ADCIN1	= 0xc << ADCMUX_SHIFT,
++	PCF50606_ADCMUX_XY_SEQUENCE	= 0xe << ADCMUX_SHIFT,
++	PCF50606_P1_P2_RESISTANCE	= 0xf << ADCMUX_SHIFT,
++};
++
++enum pcf50606_adcs2 {
++	PCF50606_ADCS2_ADCRDY		= 0x80,
++};
++
++enum pcf50606_reg_mbcc1 {
++	PCF50606_MBCC1_CHGAPE		= 0x01,
++	PCF50606_MBCC1_AUTOFST		= 0x02,
++#define	PCF50606_MBCC1_CHGMOD_MASK	  0x1c
++#define	PCF50606_MBCC1_CHGMOD_SHIFT	  2
++	PCF50606_MBCC1_CHGMOD_QUAL	= 0x00,
++	PCF50606_MBCC1_CHGMOD_PRE	= 0x04,
++	PCF50606_MBCC1_CHGMOD_TRICKLE	= 0x08,
++	PCF50606_MBCC1_CHGMOD_FAST_CCCV	= 0x0c,
++	PCF50606_MBCC1_CHGMOD_FAST_NOCC	= 0x10,
++	PCF50606_MBCC1_CHGMOD_FAST_NOCV	= 0x14,
++	PCF50606_MBCC1_CHGMOD_FAST_SW	= 0x18,
++	PCF50606_MBCC1_CHGMOD_IDLE	= 0x1c,
++	PCF50606_MBCC1_DETMOD_LOWCHG	= 0x20,
++	PCF50606_MBCC1_DETMOD_WDRST	= 0x40,
++};
++
++enum pcf50606_reg_bvmc {
++	PCF50606_BVMC_LOWBAT		= 0x01,
++	PCF50606_BVMC_THRSHLD_NULL	= 0x00,
++	PCF50606_BVMC_THRSHLD_2V8	= 0x02,
++	PCF50606_BVMC_THRSHLD_2V9	= 0x04,
++	PCF50606_BVMC_THRSHLD_3V	= 0x08,
++	PCF50606_BVMC_THRSHLD_3V1	= 0x08,
++	PCF50606_BVMC_THRSHLD_3V2	= 0x0a,
++	PCF50606_BVMC_THRSHLD_3V3	= 0x0c,
++	PCF50606_BVMC_THRSHLD_3V4	= 0x0e,
++	PCF50606_BVMC_DISDB		= 0x10,
++};
++
++#endif /* _PCF50606_H */
++
+Index: linux-2.6.17.14-fic4.test/drivers/i2c/chips/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/i2c/chips/Kconfig	2007-01-21 00:39:53.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/i2c/chips/Kconfig	2007-01-21 01:40:22.000000000 +0100
+@@ -36,6 +36,17 @@
+ 	  This driver can also be built as a module.  If so, the module
+ 	  will be called eeprom.
+ 
++config SENSORS_PCF50606
++	tristate "Philips PCF50606"
++	depends on I2C
++	help
++	  If you say yes here you get support for Philips PCF50606
++	  PMU (Power Management Unit) chips.
++
++	  This driver can also be built as a module.  If so, the module
++	  will be called pcf50606.
++
++
+ config SENSORS_PCF8574
+ 	tristate "Philips PCF8574 and PCF8574A"
+ 	depends on I2C && EXPERIMENTAL
+Index: linux-2.6.17.14-fic4.test/drivers/i2c/chips/Makefile
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/i2c/chips/Makefile	2007-01-21 00:39:53.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/i2c/chips/Makefile	2007-01-21 01:40:22.000000000 +0100
+@@ -8,6 +8,7 @@
+ obj-$(CONFIG_SENSORS_MAX6875)	+= max6875.o
+ obj-$(CONFIG_SENSORS_M41T00)	+= m41t00.o
+ obj-$(CONFIG_SENSORS_PCA9539)	+= pca9539.o
++obj-$(CONFIG_SENSORS_PCF50606)	+= pcf50606.o
+ obj-$(CONFIG_SENSORS_PCF8574)	+= pcf8574.o
+ obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
+ obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
+Index: linux-2.6.17.14-fic4.test/include/linux/i2c-id.h
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/include/linux/i2c-id.h	2007-01-21 00:39:53.000000000 +0100
++++ linux-2.6.17.14-fic4.test/include/linux/i2c-id.h	2007-01-21 01:40:22.000000000 +0100
+@@ -155,6 +155,7 @@
+ #define I2C_DRIVERID_ASB100 1043
+ #define I2C_DRIVERID_FSCHER 1046
+ #define I2C_DRIVERID_W83L785TS 1047
++#define I2C_DRIVERID_PCF50606 1048
+ 
+ /*
+  * ---- Adapter types ----------------------------------------------------
+Index: linux-2.6.17.14-fic4.test/include/linux/pcf50606.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/include/linux/pcf50606.h	2007-01-21 13:11:52.000000000 +0100
+@@ -0,0 +1,55 @@
++#ifndef _LINUX_PCF50606_H
++#define _LINUX_PCF50606_H
++
++/* public in-kernel pcf50606 api */
++enum pcf50606_regulator_id {
++	PCF50606_REGULATOR_DCD,
++	PCF50606_REGULATOR_DCDE,
++	PCF50606_REGULATOR_DCUD,
++	PCF50606_REGULATOR_D1REG,
++	PCF50606_REGULATOR_D2REG,
++	PCF50606_REGULATOR_D3REG,
++	PCF50606_REGULATOR_LPREG,
++	PCF50606_REGULATOR_IOREG,
++	__NUM_PCF50606_REGULATORS
++};
++
++struct pcf50606_data;
++extern struct pcf50606_data *pcf50606_global;
++
++extern void
++pcf50606_go_standby(void);
++
++extern void
++pcf50606_gpo0_set(struct pcf50606_data *pcf, int on);
++
++extern int
++pcf50606_gpo0_get(struct pcf50606_data *pcf);
++
++extern int
++pcf50606_voltage_set(struct pcf50606_data *pcf,
++		     enum pcf50606_regulator_id reg,
++		     unsigned int millivolts);
++extern unsigned int
++pcf50606_voltage_get(struct pcf50606_data *pcf,
++		     enum pcf50606_regulator_id reg);
++extern int
++pcf50606_onoff_get(struct pcf50606_data *pcf,
++		   enum pcf50606_regulator_id reg);
++
++extern int
++pcf50606_onoff_set(struct pcf50606_data *pcf,
++		   enum pcf50606_regulator_id reg, int on);
++
++extern void
++pcf50606_charge_fast(struct pcf50606_data *pcf, int on);
++
++struct pcf50606_platform_data {
++	unsigned int initial_voltage[__NUM_PCF50606_REGULATORS];
++
++	struct {
++		u_int8_t mbcc3; /* charger voltage / current */
++	} charger;
++};
++
++#endif

Added: trunk/src/target/kernel/patches/gta01-power_control.patch
===================================================================
--- trunk/src/target/kernel/patches/gta01-power_control.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/gta01-power_control.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,767 @@
+Index: linux-2.6.17.14-fic4.test/arch/arm/common/Makefile
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/common/Makefile	2007-01-21 19:31:56.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/common/Makefile	2007-01-21 20:25:50.000000000 +0100
+@@ -16,3 +16,5 @@
+ obj-$(CONFIG_SHARPSL_PM)	+= sharpsl_pm.o
+ obj-$(CONFIG_SHARP_SCOOP)	+= scoop.o
+ obj-$(CONFIG_ARCH_IXP2000)	+= uengine.o
++#obj-$(CONFIG_MACH_GTA01)	+= gta01_pm_gsm.o gta01_pm_gps.o gta01_pm_bt.o
++obj-m 				+= gta01_pm_gsm.o gta01_pm_gps.o gta01_pm_bt.o
+Index: linux-2.6.17.14-fic4.test/arch/arm/common/gta01_pm_gps.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/arch/arm/common/gta01_pm_gps.c	2007-01-21 19:35:30.000000000 +0100
+@@ -0,0 +1,446 @@
++/*
++ * GPS Power Management code for the FIC Neo1973 GSM Phone
++ *
++ * (C) 2007 by OpenMoko Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++
++#include <linux/pcf50606.h>
++
++#include <asm/arch/gta01.h>
++#include <asm/arch/hardware.h>
++
++/* This is the 2.8V supply for the RTC crystal, the mail clock crystal and
++ * the input to VDD_RF */
++static void gps_power_2v8_set(int on)
++{
++	switch (system_rev) {
++	case GTA01v3_SYSTEM_REV:
++	case GTA01v4_SYSTEM_REV:
++		if (on)
++			pcf50606_voltage_set(pcf50606_global,
++					     PCF50606_REGULATOR_IOREG, 2800);
++		pcf50606_onoff_set(pcf50606_global,
++				   PCF50606_REGULATOR_IOREG, on);
++		break;
++	case GTA01Bv2_SYSTEM_REV:
++		s3c2410_gpio_setpin(GTA01_GPIO_GPS_EN_2V8, on);
++			break;
++	}
++}
++
++static int gps_power_2v8_get(void)
++{
++	int ret = 0;
++
++	switch (system_rev) {
++	case GTA01v3_SYSTEM_REV:
++	case GTA01v4_SYSTEM_REV:
++		if (pcf50606_onoff_get(pcf50606_global,
++					PCF50606_REGULATOR_IOREG) &&
++		    pcf50606_voltage_get(pcf50606_global,
++					 PCF50606_REGULATOR_IOREG) == 2800)
++			ret = 1;
++		break;
++	case GTA01Bv2_SYSTEM_REV:
++		if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_EN_2V8))
++			ret = 1;
++		break;
++	}
++
++	return ret;
++}
++
++/* This is the 3V supply (AVDD) for the external RF frontend (LNA bias) */
++static void gps_power_3v_set(int on)
++{
++	switch (system_rev) {
++	case GTA01v3_SYSTEM_REV:
++	case GTA01v4_SYSTEM_REV:
++		if (on)
++			pcf50606_voltage_set(pcf50606_global,
++					     PCF50606_REGULATOR_D1REG, 3000);
++		pcf50606_onoff_set(pcf50606_global,
++				   PCF50606_REGULATOR_D1REG, on);
++		break;
++	case GTA01Bv2_SYSTEM_REV:
++		s3c2410_gpio_setpin(GTA01_GPIO_GPS_EN_3V, on);
++		break;
++	}
++}
++
++static int gps_power_3v_get(void)
++{
++	int ret = 0;
++
++	switch (system_rev) {
++	case GTA01v3_SYSTEM_REV:
++	case GTA01v4_SYSTEM_REV:
++		if (pcf50606_onoff_get(pcf50606_global,
++				       PCF50606_REGULATOR_D1REG) &&
++		    pcf50606_voltage_get(pcf50606_global,
++					 PCF50606_REGULATOR_D1REG) == 3000)
++			ret = 1;
++		break;
++	case GTA01Bv2_SYSTEM_REV:
++		if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_EN_3V))
++			ret = 1;
++		break;
++	}
++
++	return ret;
++}
++
++/* This is the 3.3V supply for VDD_IO and VDD_LPREG input */
++static void gps_power_3v3_set(int on)
++{
++	switch (system_rev) {
++	case GTA01v3_SYSTEM_REV:
++	case GTA01v4_SYSTEM_REV:
++	case GTA01Bv2_SYSTEM_REV:
++		if (on)
++			pcf50606_voltage_set(pcf50606_global,
++					     PCF50606_REGULATOR_DCD, 3300);
++		pcf50606_onoff_set(pcf50606_global,
++				   PCF50606_REGULATOR_DCD, on);
++	}
++}
++
++static int gps_power_3v3_get(void)
++{
++	int ret = 0;
++
++	switch (system_rev) {
++	case GTA01v3_SYSTEM_REV:
++	case GTA01v4_SYSTEM_REV:
++	case GTA01Bv2_SYSTEM_REV:
++		if (pcf50606_onoff_get(pcf50606_global,
++				       PCF50606_REGULATOR_DCD) &&
++		    pcf50606_voltage_get(pcf50606_global,
++					 PCF50606_REGULATOR_DCD) == 3300)
++			ret = 1;
++	}
++
++	return ret;
++}
++
++/* This is the 2.5V supply for VDD_PLLREG and VDD_COREREG input */
++static void gps_power_2v5_set(int on)
++{
++	switch (system_rev) {
++	case GTA01v3_SYSTEM_REV:
++		/* This is CORE_1V8 and cannot be disabled */
++		break;
++	case GTA01v4_SYSTEM_REV:
++	case GTA01Bv2_SYSTEM_REV:
++		if (on)
++			pcf50606_voltage_set(pcf50606_global,
++					     PCF50606_REGULATOR_D2REG, 2500);
++		pcf50606_onoff_set(pcf50606_global,
++				   PCF50606_REGULATOR_D2REG, on);
++		break;
++	}
++}
++
++static int gps_power_2v5_get(void)
++{
++	int ret = 0;
++
++	switch (system_rev) {
++	case GTA01v3_SYSTEM_REV:
++		/* This is CORE_1V8 and cannot be disabled */
++		ret = 1;
++		break;
++	case GTA01v4_SYSTEM_REV:
++	case GTA01Bv2_SYSTEM_REV:
++		if (pcf50606_onoff_get(pcf50606_global,
++				       PCF50606_REGULATOR_D2REG) &&
++		    pcf50606_voltage_get(pcf50606_global,
++					 PCF50606_REGULATOR_D2REG) == 2500)
++			ret = 1;
++		break;
++	}
++
++	return ret;
++}
++
++/* This is the POWERON pin */
++static void gps_pwron_set(int on)
++{
++	s3c2410_gpio_setpin(GTA01_GPIO_GPS_PWRON, on);
++}
++
++static int gps_pwron_get(void)
++{
++	if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_PWRON))
++		return 1;
++	else
++		return 0;
++}
++
++/* This is the nRESET pin */
++static void gps_rst_set(int on)
++{
++	switch (system_rev) {
++	case GTA01v3_SYSTEM_REV:
++		pcf50606_gpo0_set(pcf50606_global, on);
++		break;
++	case GTA01v4_SYSTEM_REV:
++	case GTA01Bv2_SYSTEM_REV:
++		s3c2410_gpio_setpin(GTA01_GPIO_GPS_RESET, on);
++		break;
++	}
++}
++
++static int gps_rst_get(void)
++{
++	switch (system_rev) {
++	case GTA01v3_SYSTEM_REV:
++		if (pcf50606_gpo0_get(pcf50606_global))
++			return 1;
++		break;
++	case GTA01v4_SYSTEM_REV:
++	case GTA01Bv2_SYSTEM_REV:
++		if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_RESET))
++			return 1;
++		break;
++	}
++
++	return 0;
++}
++
++static ssize_t power_gps_read(struct device *dev,
++			      struct device_attribute *attr, char *buf)
++{
++	int ret = 0;
++
++	if (!strcmp(attr->attr.name, "power_vtxco_2v8")) {
++		ret = gps_power_2v8_get();
++	} else if (!strcmp(attr->attr.name, "power_avdd_3v")) {
++		ret = gps_power_3v_get();
++	} else if (!strcmp(attr->attr.name, "pwron")) {
++		ret = gps_pwron_get();
++	} else if (!strcmp(attr->attr.name, "reset")) {
++		ret = gps_rst_get();
++	} else if (!strcmp(attr->attr.name, "power_lp_io_3v3")) {
++		ret = gps_power_3v3_get();
++	} else if (!strcmp(attr->attr.name, "power_pll_core_2v5")) {
++		ret = gps_power_2v5_get();
++	}
++
++	if (ret)
++		return strlcpy(buf, "1\n", 3);
++	else
++		return strlcpy(buf, "0\n", 3);
++}
++
++static ssize_t power_gps_write(struct device *dev,
++			       struct device_attribute *attr, const char *buf,
++			       size_t count)
++{
++	unsigned long on = simple_strtoul(buf, NULL, 10);
++
++	if (!strcmp(attr->attr.name, "power_vtxco_2v8")) {
++		gps_power_2v8_set(on);
++	} else if (!strcmp(attr->attr.name, "power_avdd_3v")) {
++		gps_power_3v_set(on);
++	} else if (!strcmp(attr->attr.name, "pwron")) {
++		gps_pwron_set(on);
++	} else if (!strcmp(attr->attr.name, "reset")) {
++		gps_rst_set(on);
++	} else if (!strcmp(attr->attr.name, "power_lp_io_3v3")) {
++		gps_power_3v3_set(on);
++	} else if (!strcmp(attr->attr.name, "power_pll_core_2v5")) {
++		gps_power_2v5_set(on);
++	}
++
++	return count;
++}
++
++static void gps_power_sequence_up(void)
++{
++	/* According to PMB2520 Data Sheet, Rev. 2006-06-05,
++	 * Chapter 4.2.2 */
++
++	/* nRESET must be asserted low */
++	gps_rst_set(0);
++
++	/* POWERON must be de-asserted (low) */
++	gps_pwron_set(0);
++
++	/* Apply VDD_IO and VDD_LPREG_IN */
++	gps_power_3v3_set(1);
++
++	/* VDD_COREREG_IN, VDD_PLLREG_IN */
++	gps_power_2v5_set(1);
++
++	/* and VDD_RF may be applied */
++	gps_power_2v8_set(1);
++
++	msleep(3); 	/* Is 3ms enough? */
++
++	/* De-asert nRESET */
++	gps_rst_set(1);
++
++	/* Switch power on */
++	gps_pwron_set(1);
++
++	/* power up external LNA */
++	gps_power_3v_set(1);
++}
++
++static void gps_power_sequence_down(void)
++{
++	gps_power_3v_set(0);
++
++	/* According to PMB2520 Data Sheet, Rev. 2006-06-05,
++	 * Chapter 4.2.3.1 */
++	gps_pwron_set(0);
++
++	/* Remove VDD_COREREG_IN, VDD_PLLREG_IN and VDD_REFREG_IN */
++	gps_power_2v5_set(0);
++	gps_power_2v8_set(0);
++
++	/* Remove VDD_LPREG_IN and VDD_IO */
++	gps_power_3v3_set(0);
++}
++
++
++static ssize_t power_sequence_read(struct device *dev,
++				   struct device_attribute *attr,
++				   char *buf)
++{
++	return strlcpy(buf, "power_up power_down\n", PAGE_SIZE);
++}
++
++static ssize_t power_sequence_write(struct device *dev,
++				    struct device_attribute *attr,
++				    const char *buf, size_t count)
++{
++	dev_dbg(dev, "wrote: '%s'\n", buf);
++
++	if (!strncmp(buf, "power_up", 8))
++		gps_power_sequence_up();
++	else if (!strncmp(buf, "power_down", 10))
++		gps_power_sequence_down();
++	else
++		return -EINVAL;
++
++	return count;
++}
++
++static DEVICE_ATTR(power_vtxco_2v8, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(power_avdd_3v, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(pwron, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(reset, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(power_lp_io_3v3, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(power_pll_core_2v5, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(power_sequence, 0644, power_sequence_read, power_sequence_write);
++
++#ifdef CONFIG_PM
++static int gta01_pm_gps_suspend(struct platform_device *pdev, pm_message_t state)
++{
++	/* FIXME */
++
++	return 0;
++}
++
++static int gta01_pm_gps_resume(struct platform_device *pdev)
++{
++	/* FIXME */
++
++	return 0;
++}
++#else
++#define gta01_pm_gps_suspend	NULL
++#define gta01_pm_gps_resume	NULL
++#endif
++
++static int __init gta01_pm_gps_probe(struct platform_device *pdev)
++{
++
++	s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_PWRON, S3C2410_GPIO_OUTPUT);
++
++	switch (system_rev) {
++	case GTA01v3_SYSTEM_REV:
++		break;
++	case GTA01v4_SYSTEM_REV:
++		s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_RESET, S3C2410_GPIO_OUTPUT);
++		break;
++	case GTA01Bv2_SYSTEM_REV:
++		s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_EN_2V8, S3C2410_GPIO_OUTPUT);
++		s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_EN_3V, S3C2410_GPIO_OUTPUT);
++		s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_RESET, S3C2410_GPIO_OUTPUT);
++		break;
++	default:
++		dev_warn(&pdev->dev, "Unknown GTA01 Revision 0x%x, "
++			 "AGPS PM features not available!!!\n",
++			 system_rev);
++		return -1;
++		break;
++	}
++
++	gps_power_sequence_down();
++
++	device_create_file(&pdev->dev, &dev_attr_power_vtxco_2v8);
++	device_create_file(&pdev->dev, &dev_attr_power_avdd_3v);
++	device_create_file(&pdev->dev, &dev_attr_pwron);
++	device_create_file(&pdev->dev, &dev_attr_reset);
++	device_create_file(&pdev->dev, &dev_attr_power_lp_io_3v3);
++	device_create_file(&pdev->dev, &dev_attr_power_pll_core_2v5);
++	device_create_file(&pdev->dev, &dev_attr_power_sequence);
++
++	return 0;
++}
++
++static int gta01_pm_gps_remove(struct platform_device *pdev)
++{
++	device_remove_file(&pdev->dev, &dev_attr_power_sequence);
++	device_remove_file(&pdev->dev, &dev_attr_power_pll_core_2v5);
++	device_remove_file(&pdev->dev, &dev_attr_power_lp_io_3v3);
++	device_remove_file(&pdev->dev, &dev_attr_reset);
++	device_remove_file(&pdev->dev, &dev_attr_pwron);
++	device_remove_file(&pdev->dev, &dev_attr_power_avdd_3v);
++	device_remove_file(&pdev->dev, &dev_attr_power_vtxco_2v8);
++
++	return 0;
++}
++
++static struct platform_driver gta01_pm_gps_driver = {
++	.probe		= gta01_pm_gps_probe,
++	.remove		= gta01_pm_gps_remove,
++	.suspend	= gta01_pm_gps_suspend,
++	.resume		= gta01_pm_gps_resume,
++	.driver		= {
++		.name		= "gta01-pm-gps",
++	},
++};
++
++static int __devinit gta01_pm_gps_init(void)
++{
++	return platform_driver_register(&gta01_pm_gps_driver);
++}
++
++static void gta01_pm_gps_exit(void)
++{
++	platform_driver_unregister(&gta01_pm_gps_driver);
++}
++
++module_init(gta01_pm_gps_init);
++module_exit(gta01_pm_gps_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("FIC GTA01 (Neo1973) GPS Power Management");
+Index: linux-2.6.17.14-fic4.test/arch/arm/common/gta01_pm_gsm.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/arch/arm/common/gta01_pm_gsm.c	2007-01-21 19:35:30.000000000 +0100
+@@ -0,0 +1,163 @@
++/*
++ * GSM Management code for the FIC Neo1973 GSM Phone
++ *
++ * (C) 2007 by OpenMoko Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++
++#include <asm/arch/gta01.h>
++#include <asm/arch/hardware.h>
++
++struct gta01pm_priv {
++	int gpio_ngsm_en;
++};
++
++static struct gta01pm_priv gta01_gsm;
++
++static ssize_t gsm_read(struct device *dev, struct device_attribute *attr,
++			char *buf)
++{
++	if (!strcmp(attr->attr.name, "power_on")) {
++		if (s3c2410_gpio_getpin(GTA01_GPIO_MODEM_ON))
++			goto out_1;
++	} else if (!strcmp(attr->attr.name, "reset")) {
++		if (s3c2410_gpio_getpin(GTA01_GPIO_MODEM_RST))
++			goto out_1;
++	} else if (!strcmp(attr->attr.name, "download")) {
++		if (s3c2410_gpio_getpin(GTA01_GPIO_MODEM_DNLOAD))
++			goto out_1;
++	}
++
++	return strlcpy(buf, "0\n", 3);
++out_1:
++	return strlcpy(buf, "1\n", 3);
++}
++
++static ssize_t gsm_write(struct device *dev, struct device_attribute *attr,
++			 const char *buf, size_t count)
++{
++	unsigned long on = simple_strtoul(buf, NULL, 10);
++
++	if (!strcmp(attr->attr.name, "power_on")) {
++		if (on) {
++			dev_info(dev, "powering up GSM, thus disconnecting "
++				 "serial console\n");
++
++			if (gta01_gsm.gpio_ngsm_en)
++				s3c2410_gpio_setpin(gta01_gsm.gpio_ngsm_en, 0);
++
++			s3c2410_gpio_setpin(GTA01_GPIO_MODEM_ON, 1);
++		} else {
++			s3c2410_gpio_setpin(GTA01_GPIO_MODEM_ON, 0);
++
++			if (gta01_gsm.gpio_ngsm_en)
++				s3c2410_gpio_setpin(gta01_gsm.gpio_ngsm_en, 1);
++
++			dev_info(dev, "powered down GSM, thus enabling "
++				 "serial console\n");
++		}
++	} else if (!strcmp(attr->attr.name, "reset")) {
++		s3c2410_gpio_setpin(GTA01_GPIO_MODEM_RST, on);
++	} else if (!strcmp(attr->attr.name, "download")) {
++		s3c2410_gpio_setpin(GTA01_GPIO_MODEM_DNLOAD, on);
++	}
++
++	return count;
++}
++
++static DEVICE_ATTR(power_on, 0644, gsm_read, gsm_write);
++static DEVICE_ATTR(reset, 0644, gsm_read, gsm_write);
++static DEVICE_ATTR(download, 0644, gsm_read, gsm_write);
++
++#ifdef CONFIG_PM
++static int gta01_gsm_suspend(struct platform_device *pdev, pm_message_t state)
++{
++	/* FIXME */
++
++	return 0;
++}
++
++static int gta01_gsm_resume(struct platform_device *pdev)
++{
++	/* FIXME */
++
++	return 0;
++}
++#else
++#define gta01_gsm_suspend	NULL
++#define gta01_gsm_resume		NULL
++#endif
++
++static int __init gta01_gsm_probe(struct platform_device *pdev)
++{
++	switch (system_rev) {
++		case GTA01v3_SYSTEM_REV:
++			gta01_gsm.gpio_ngsm_en = GTA01v3_GPIO_nGSM_EN;
++			break;
++		case GTA01v4_SYSTEM_REV:
++			gta01_gsm.gpio_ngsm_en = 0;
++			break;
++		case GTA01Bv2_SYSTEM_REV:
++			gta01_gsm.gpio_ngsm_en = GTA01Bv2_GPIO_nGSM_EN;
++			s3c2410_gpio_setpin(GTA01v3_GPIO_nGSM_EN, 0);
++			break;
++		default:
++			dev_warn(&pdev->dev, "Unknown GTA01 Revision 0x%x, "
++				 "some PM features not available!!!\n",
++				 system_rev);
++			break;
++	}
++
++	device_create_file(&pdev->dev, &dev_attr_power_on);
++	device_create_file(&pdev->dev, &dev_attr_reset);
++	device_create_file(&pdev->dev, &dev_attr_download);
++
++	return 0;
++}
++
++static int gta01_gsm_remove(struct platform_device *pdev)
++{
++	device_remove_file(&pdev->dev, &dev_attr_download);
++	device_remove_file(&pdev->dev, &dev_attr_power_on);
++	device_remove_file(&pdev->dev, &dev_attr_reset);
++
++	return 0;
++}
++
++static struct platform_driver gta01_gsm_driver = {
++	.probe		= gta01_gsm_probe,
++	.remove		= gta01_gsm_remove,
++	.suspend	= gta01_gsm_suspend,
++	.resume		= gta01_gsm_resume,
++	.driver		= {
++		.name		= "gta01-pm-gsm",
++	},
++};
++
++static int __devinit gta01_gsm_init(void)
++{
++	return platform_driver_register(&gta01_gsm_driver);
++}
++
++static void gta01_gsm_exit(void)
++{
++	platform_driver_unregister(&gta01_gsm_driver);
++}
++
++module_init(gta01_gsm_init);
++module_exit(gta01_gsm_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("FIC GTA01 (Neo1973) GSM Management");
+Index: linux-2.6.17.14-fic4.test/arch/arm/common/gta01_pm_bt.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/arch/arm/common/gta01_pm_bt.c	2007-01-21 19:35:30.000000000 +0100
+@@ -0,0 +1,133 @@
++/*
++ * Bluetooth PM code for the FIC Neo1973 GSM Phone
++ *
++ * (C) 2007 by OpenMoko Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++
++#include <linux/pcf50606.h>
++
++#include <asm/arch/gta01.h>
++#include <asm/arch/hardware.h>
++
++#define DRVMSG "FIC GTA01 (Neo1973) Bluetooth Power Management"
++
++static ssize_t bt_read(struct device *dev, struct device_attribute *attr,
++		       char *buf)
++{
++	if (!strcmp(attr->attr.name, "power_on")) {
++		if (pcf50606_onoff_get(pcf50606_global,
++					PCF50606_REGULATOR_D1REG) &&
++		    pcf50606_voltage_get(pcf50606_global,
++					 PCF50606_REGULATOR_D1REG) == 3100)
++			goto out_1;
++	} else if (!strcmp(attr->attr.name, "reset")) {
++		if (s3c2410_gpio_getpin(GTA01_GPIO_BT_EN))
++			goto out_1;
++	}
++
++	return strlcpy(buf, "0\n", 3);
++out_1:
++	return strlcpy(buf, "1\n", 3);
++}
++
++static ssize_t bt_write(struct device *dev, struct device_attribute *attr,
++			const char *buf, size_t count)
++{
++	unsigned long on = simple_strtoul(buf, NULL, 10);
++
++	if (!strcmp(attr->attr.name, "power_on")) {
++		if (on)
++			pcf50606_voltage_set(pcf50606_global,
++					     PCF50606_REGULATOR_D1REG,
++					     3100);
++		pcf50606_onoff_set(pcf50606_global,
++				   PCF50606_REGULATOR_D1REG, on);
++	} else if (!strcmp(attr->attr.name, "reset")) {
++		s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, on);
++	}
++
++	return count;
++}
++
++static DEVICE_ATTR(power_on, 0644, bt_read, bt_write);
++static DEVICE_ATTR(reset, 0644, bt_read, bt_write);
++
++#ifdef CONFIG_PM
++static int gta01_bt_suspend(struct platform_device *pdev, pm_message_t state)
++{
++	dev_info(&pdev->dev, DRVMSG ": suspending\n");
++	/* FIXME */
++
++	return 0;
++}
++
++static int gta01_bt_resume(struct platform_device *pdev)
++{
++	dev_info(&pdev->dev, DRVMSG ": resuming\n");
++	/* FIXME */
++
++	return 0;
++}
++#else
++#define gta01_bt_suspend	NULL
++#define gta01_bt_resume		NULL
++#endif
++
++static int __init gta01_bt_probe(struct platform_device *pdev)
++{
++	dev_info(&pdev->dev, DRVMSG ": starting\n");
++
++	device_create_file(&pdev->dev, &dev_attr_power_on);
++	device_create_file(&pdev->dev, &dev_attr_reset);
++
++	return 0;
++}
++
++static int gta01_bt_remove(struct platform_device *pdev)
++{
++	dev_info(&pdev->dev, DRVMSG ": ending\n");
++
++	device_remove_file(&pdev->dev, &dev_attr_power_on);
++	device_remove_file(&pdev->dev, &dev_attr_reset);
++
++	return 0;
++}
++
++static struct platform_driver gta01_bt_driver = {
++	.probe		= gta01_bt_probe,
++	.remove		= gta01_bt_remove,
++	.suspend	= gta01_bt_suspend,
++	.resume		= gta01_bt_resume,
++	.driver		= {
++		.name		= "gta01-pm-bt",
++	},
++};
++
++static int __devinit gta01_bt_init(void)
++{
++	return platform_driver_register(&gta01_bt_driver);
++}
++
++static void gta01_bt_exit(void)
++{
++	platform_driver_unregister(&gta01_bt_driver);
++}
++
++module_init(gta01_bt_init);
++module_exit(gta01_bt_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION(DRVMSG);

Added: trunk/src/target/kernel/patches/gta01-s3c_mci-pdata.patch
===================================================================
--- trunk/src/target/kernel/patches/gta01-s3c_mci-pdata.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/gta01-s3c_mci-pdata.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,57 @@
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-gta01.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/mach-gta01.c	2007-01-08 22:47:48.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-gta01.c	2007-01-08 22:51:22.000000000 +0100
+@@ -57,6 +57,7 @@
+ #include <asm/arch/ts.h>
+ #include <asm/arch/spi.h>
+ #include <asm/arch/spi-gpio.h>
++#include <asm/arch/mci.h>
+ 
+ #include <asm/arch/gta01.h>
+ 
+@@ -232,7 +233,6 @@
+ 	.sets		= gta01_nand_sets,
+ };
+ 
+-#if 0
+ static unsigned int mmc_millivolts[] = {
+ 	[MMC_VDD_145_150]	= 1500,
+ 	[MMC_VDD_150_155]	= 1500,
+@@ -257,14 +257,15 @@
+ 	[MMC_VDD_32_33]		= 3300,
+ };
+ 
+-static void gta01_mmc_set_power(unsigned int to)
++static void gta01_mmc_set_power(unsigned short vdd)
+ {
+ 	/* FIXME: configure D2REG of PCF50606 using D2REGC1 */
+-	//pcf50606_voltage_set(mmc_millivolts[to]);
++	/* FIXME: set GTA01_GPIO_SDMMC_ON on GTA01v4 */
++	//pcf50606_voltage_set(mmc_millivolts[vdd]);
+ }
+ 
+-static struct s3c24xx_mmc_platdata gta01_mmc_cfg = {
+-	.gpio_detect	= S3C2410_GPF5,
++static struct s3c24xx_mci_pdata gta01_mmc_cfg = {
++	.gpio_detect	= GTA01_GPIO_nSD_DETECT,
+ 	.set_power	= &gta01_mmc_set_power,
+ 	.ocr_avail	= MMC_VDD_145_150|MMC_VDD_150_155|MMC_VDD_155_160|
+ 			  MMC_VDD_160_165| MMC_VDD_165_170|MMC_VDD_17_18|
+@@ -274,7 +275,6 @@
+ 			  MMC_VDD_27_28|MMC_VDD_28_29|MMC_VDD_29_30|
+ 			  MMC_VDD_30_31|MMC_VDD_31_32|MMC_VDD_32_33,
+ };
+-#endif
+ 
+ static void gta01_udc_pullup(enum s3c2410_udc_cmd_e cmd)
+ {
+@@ -441,7 +441,7 @@
+ static void __init gta01_machine_init(void)
+ {
+ 	s3c_device_nand.dev.platform_data = &gta01_nand_info;
+-	//s3c_device_sdi.dev.platform_data = &gta01_mmc_cfg;
++	s3c_device_sdi.dev.platform_data = &gta01_mmc_cfg;
+ 
+ 	s3c24xx_fb_set_platdata(&gta01_lcd_cfg);
+ 

Added: trunk/src/target/kernel/patches/gta01-vbus_draw.patch
===================================================================
--- trunk/src/target/kernel/patches/gta01-vbus_draw.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/gta01-vbus_draw.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,36 @@
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-gta01.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/mach-gta01.c	2007-01-21 21:35:02.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-gta01.c	2007-01-21 21:36:21.000000000 +0100
+@@ -40,6 +40,8 @@
+ #include <linux/mtd/nand_ecc.h>
+ #include <linux/mtd/partitions.h>
+ 
++#include <linux/pcf50606.h>
++
+ #include <asm/mach/arch.h>
+ #include <asm/mach/map.h>
+ #include <asm/mach/irq.h>
+@@ -298,8 +300,22 @@
+ 	}
+ }
+ 
++static void gta01_udc_vbus_draw(unsigned int ma)
++{
++	if (ma >= 500) {
++		/* enable fast charge */
++		printk(KERN_DEBUG "udc: enabling fast charge\n");
++		pcf50606_charge_fast(&pcf50606_global, 1);
++	} else {
++		/* disable fast charge */
++		printk(KERN_DEBUG "udc: disabling fast charge\n");
++		pcf50606_charge_fast(&pcf50606_global, 0);
++	}
++}
++
+ static struct s3c2410_udc_mach_info gta01_udc_cfg = {
+ 	.udc_command	= gta01_udc_pullup,
++	.vbus_draw	= gta01_udc_vbus_draw,
+ };
+ 
+ static struct s3c2410_ts_mach_info gta01_ts_cfg = {

Added: trunk/src/target/kernel/patches/gta01_vibrator.patch
===================================================================
--- trunk/src/target/kernel/patches/gta01_vibrator.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/gta01_vibrator.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,104 @@
+This patch adds driver support for the FIC GTA01 vibrator device.  The driver
+uses the existing LED class driver framework, since there's a lot of
+similarity between the LED and the vibrator function.
+
+Index: linux-2.6.17.14-fic4.test/drivers/leds/leds-gta01.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/drivers/leds/leds-gta01.c	2007-01-08 22:51:15.000000000 +0100
+@@ -0,0 +1,95 @@
++/*
++ * LED driver for the FIC GTA01 (Neo1973) GSM Phone
++ *
++ * (C) 2006 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/leds.h>
++#include <asm/hardware/scoop.h>
++#include <asm/mach-types.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/gta01.h>
++
++static void gta01led_vib_set(struct led_classdev *led_cdev, enum led_brightness value)
++{
++	if (value)
++		s3c2410_gpio_setpin(GTA01_GPIO_VIBRATOR_ON, 1);
++	else
++		s3c2410_gpio_setpin(GTA01_GPIO_VIBRATOR_ON, 0);
++}
++
++static struct led_classdev gta01_vib_led = {
++	.name			= "gta01:vibrator",
++	.brightness_set		= gta01led_vib_set,
++};
++
++#ifdef CONFIG_PM
++static int gta01led_suspend(struct platform_device *dev, pm_message_t state)
++{
++	led_classdev_suspend(&gta01_vib_led);
++	return 0;
++}
++
++static int gta01led_resume(struct platform_device *dev)
++{
++	led_classdev_resume(&gta01_vib_led);
++	return 0;
++}
++#endif
++
++static int gta01led_probe(struct platform_device *pdev)
++{
++	int ret;
++
++	if (!machine_is_gta01())
++		return -EIO;
++
++	return led_classdev_register(&pdev->dev, &gta01_vib_led);
++}
++
++static int gta01led_remove(struct platform_device *pdev)
++{
++	led_classdev_unregister(&gta01_vib_led);
++
++	return 0;
++}
++
++static struct platform_driver gta01led_driver = {
++	.probe		= gta01led_probe,
++	.remove		= gta01led_remove,
++#ifdef CONFIG_PM
++	.suspend	= gta01led_suspend,
++	.resume		= gta01led_resume,
++#endif
++	.driver		= {
++		.name		= "gta01-led",
++	},
++};
++
++static int __init spitzled_init(void)
++{
++	return platform_driver_register(&gta01led_driver);
++}
++
++static void __exit spitzled_exit(void)
++{
++ 	platform_driver_unregister(&gta01led_driver);
++}
++
++module_init(spitzled_init);
++module_exit(spitzled_exit);
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("FIC GTA01 LED driver");
++MODULE_LICENSE("GPL");

Added: trunk/src/target/kernel/patches/mmc-fixdebugp.patch
===================================================================
--- trunk/src/target/kernel/patches/mmc-fixdebugp.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/mmc-fixdebugp.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,15 @@
+Index: linux-2.6.17.7-new/drivers/mmc/s3c2410mci.c
+===================================================================
+--- linux-2.6.17.7-new.orig/drivers/mmc/s3c2410mci.c	2006-08-07 09:37:38.000000000 +0200
++++ linux-2.6.17.7-new/drivers/mmc/s3c2410mci.c	2006-08-07 09:38:15.000000000 +0200
+@@ -300,8 +300,8 @@
+ 	u32 sdi_bsize, sdi_dcon, sdi_imsk;
+ 	int dma_len = 0;
+ 
+-	DBG(KERN_DEBUG PFX "request: [CMD] opcode:0x%02x arg:0x%08x flags:%x retries:%u\n",
+-		mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags, mrq->cmd->retries);
++	DBG(PFX "request: [CMD] opcode:0x%02x arg:0x%08x flags:%x retries:%u\n",
++	    mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags, mrq->cmd->retries);
+ 	DBG(PFX "request : %s mode\n",mmc->mode == MMC_MODE_MMC ? "mmc" : "sd");
+ 
+ 

Added: trunk/src/target/kernel/patches/mmc-fixdmalockup.patch
===================================================================
--- trunk/src/target/kernel/patches/mmc-fixdmalockup.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/mmc-fixdmalockup.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,13 @@
+diff -Nru --exclude-from=/sunbeam/home/laforge/scripts/dontdiff linux-2.6.17.7-new/drivers/mmc/s3c2410mci.c linux-2.6.17.7-qt/drivers/mmc/s3c2410mci.c
+--- linux-2.6.17.7-new/drivers/mmc/s3c2410mci.c	2006-08-07 09:25:32.000000000 +0200
++++ linux-2.6.17.7-qt/drivers/mmc/s3c2410mci.c	2006-08-07 00:55:23.000000000 +0200
+@@ -206,6 +206,8 @@
+ 	host->complete_what = COMPLETION_NONE;
+ 	complete(&host->complete_request);
+ 	writel(0, host->base + S3C2410_SDIIMSK);
++	if (host->mrq->data && host->mrq->data->error != MMC_ERR_NONE)
++		complete(&host->complete_dma);
+ 	spin_unlock_irqrestore( &host->complete_lock, iflags);
+ 	DBG(PFX "IRQ transfer closed.\n");
+ 	return IRQ_HANDLED;
+

Added: trunk/src/target/kernel/patches/mmc-get_ro.patch
===================================================================
--- trunk/src/target/kernel/patches/mmc-get_ro.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/mmc-get_ro.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,33 @@
+diff -Nru --exclude-from=/sunbeam/home/laforge/scripts/dontdiff linux-2.6.17.7-new/drivers/mmc/s3c2410mci.c linux-2.6.17.7-qt/drivers/mmc/s3c2410mci.c
+--- linux-2.6.17.7-new/drivers/mmc/s3c2410mci.c	2006-08-07 09:25:32.000000000 +0200
++++ linux-2.6.17.7-qt/drivers/mmc/s3c2410mci.c	2006-08-07 00:55:23.000000000 +0200
+@@ -515,9 +518,19 @@
+ 
+ }
+ 
++static int s3c2410sdi_get_ro(struct mmc_host *mmc)
++{
++	struct s3c2410sdi_host *host = mmc_priv(mmc);
++	if (s3c2410_gpio_getpin(host->pdata->gpio_wprotect) != 0)
++		return 1;
++	else
++		return 0;
++}	
++
+ static struct mmc_host_ops s3c2410sdi_ops = {
+ 	.request	= s3c2410sdi_request,
+ 	.set_ios	= s3c2410sdi_set_ios,
++	.get_ro		= s3c2410sdi_get_ro,
+ };
+ 
+ static void s3c2410_mmc_def_setpower(unsigned int to)
+@@ -565,6 +578,8 @@
+ 
+ 	host->irq_cd = s3c2410_gpio_getirq(pdata->gpio_detect);
+ 	s3c2410_gpio_cfgpin(pdata->gpio_detect, S3C2410_GPIO_IRQ);
++	if (pdata->gpio_wprotect)
++		s3c2410_gpio_cfgpin(pdata->gpio_wprotect, S3C2410_GPIO_INPUT);
+ 
+ 	host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ 	if (!host->mem) {
+

Added: trunk/src/target/kernel/patches/mmc-sdipre_limit.patch
===================================================================
--- trunk/src/target/kernel/patches/mmc-sdipre_limit.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/mmc-sdipre_limit.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,12 @@
+diff -Nru --exclude-from=/sunbeam/home/laforge/scripts/dontdiff linux-2.6.17.7-new/drivers/mmc/s3c2410mci.c linux-2.6.17.7-qt/drivers/mmc/s3c2410mci.c
+--- linux-2.6.17.7-new/drivers/mmc/s3c2410mci.c	2006-08-07 09:25:32.000000000 +0200
++++ linux-2.6.17.7-qt/drivers/mmc/s3c2410mci.c	2006-08-07 00:55:23.000000000 +0200
+@@ -503,6 +505,7 @@
+ 	}
+ 
+ 	if(sdi_psc > 255) sdi_psc = 255;
++	if (sdi_psc < 8) sdi_psc = 8;
+ 	writel(sdi_psc, host->base + S3C2410_SDIPRE);
+ 
+ 	/* Set CLOCK_ENABLE */
+

Added: trunk/src/target/kernel/patches/mmc.patch
===================================================================
--- trunk/src/target/kernel/patches/mmc.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/mmc.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,996 @@
+Index: linux-2.6.17.7-new/drivers/mmc/Kconfig
+===================================================================
+--- linux-2.6.17.7-new.orig/drivers/mmc/Kconfig	2006-08-07 09:32:34.000000000 +0200
++++ linux-2.6.17.7-new/drivers/mmc/Kconfig	2006-08-07 09:33:51.000000000 +0200
+@@ -82,6 +82,15 @@
+ 
+ 	  If unsure, say N.
+ 
++config MMC_S3C2410
++	tristate "Samsung S3C2410 Multimedia Card Interface support"
++	depends on ARCH_S3C2410 && MMC
++	help
++	  This selects the Samsung S3C2410 Multimedia Card Interface
++	  support.
++
++	  If unsure, say N.
++
+ config MMC_AU1X
+ 	tristate "Alchemy AU1XX0 MMC Card Interface support"
+ 	depends on MMC && SOC_AU1200
+Index: linux-2.6.17.7-new/drivers/mmc/Makefile
+===================================================================
+--- linux-2.6.17.7-new.orig/drivers/mmc/Makefile	2006-08-07 09:32:34.000000000 +0200
++++ linux-2.6.17.7-new/drivers/mmc/Makefile	2006-08-07 09:33:51.000000000 +0200
+@@ -20,6 +20,7 @@
+ obj-$(CONFIG_MMC_IMX)		+= imxmmc.o
+ obj-$(CONFIG_MMC_SDHCI)		+= sdhci.o
+ obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
++obj-$(CONFIG_MMC_S3C2410)	+= s3c2410mci.o
+ obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
+ obj-$(CONFIG_MMC_OMAP)		+= omap.o
+ obj-$(CONFIG_MMC_AT91RM9200)	+= at91_mci.o
+Index: linux-2.6.17.7-new/drivers/mmc/s3c2410mci.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.7-new/drivers/mmc/s3c2410mci.c	2006-08-07 09:33:51.000000000 +0200
+@@ -0,0 +1,781 @@
++/*
++ *  linux/drivers/mmc/s3c2410mci.h - Samsung S3C2410 SDI Interface driver
++ *
++ *  Copyright (C) 2004 Thomas Kleffel, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/ioport.h>
++#include <linux/platform_device.h>
++#include <linux/interrupt.h>
++#include <linux/blkdev.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/dma-mapping.h>
++#include <linux/mmc/host.h>
++#include <linux/mmc/protocol.h>
++#include <linux/clk.h>
++
++#include <asm/dma.h>
++#include <asm/dma-mapping.h>
++#include <asm/arch/dma.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach/mmc.h>
++
++#include <asm/arch/regs-sdi.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/mmc.h>
++
++#ifdef CONFIG_MMC_DEBUG
++#define DBG(x...)       printk(KERN_INFO x)
++#else
++#define DBG(x...)       do { } while (0)
++#endif
++
++#include "s3c2410mci.h"
++
++#define DRIVER_NAME "mmci-s3c2410"
++#define PFX DRIVER_NAME ": "
++
++#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
++
++static struct s3c2410_dma_client s3c2410sdi_dma_client = {
++	.name		= "s3c2410-sdi",
++};
++
++/*
++ * ISR for SDI Interface IRQ
++ * Communication between driver and ISR works as follows:
++ *   host->mrq 			points to current request
++ *   host->complete_what	tells the ISR when the request is considered done
++ *     COMPLETION_CMDSENT	  when the command was sent
++ *     COMPLETION_RSPFIN          when a response was received
++ *     COMPLETION_XFERFINISH	  when the data transfer is finished
++ *     COMPLETION_XFERFINISH_RSPFIN both of the above.
++ *   host->complete_request	is the completion-object the driver waits for
++ *
++ * 1) Driver sets up host->mrq and host->complete_what
++ * 2) Driver prepares the transfer
++ * 3) Driver enables interrupts
++ * 4) Driver starts transfer
++ * 5) Driver waits for host->complete_rquest
++ * 6) ISR checks for request status (errors and success)
++ * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
++ * 7) ISR completes host->complete_request
++ * 8) ISR disables interrupts
++ * 9) Driver wakes up and takes care of the request
++*/
++
++static irqreturn_t s3c2410sdi_irq(int irq, void *dev_id, struct pt_regs *regs)
++{
++	struct s3c2410sdi_host *host;
++	u32 sdi_csta, sdi_dsta, sdi_dcnt;
++	u32 sdi_cclear, sdi_dclear;
++	unsigned long iflags;
++
++	host = (struct s3c2410sdi_host *)dev_id;
++
++	/* Check for things not supposed to happen */
++	if(!host) return IRQ_HANDLED;
++	
++	sdi_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
++	sdi_dsta 	= readl(host->base + S3C2410_SDIDSTA);
++	sdi_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
++	
++	DBG(PFX "IRQ csta=0x%08x dsta=0x%08x dcnt:0x%08x\n", sdi_csta, sdi_dsta, sdi_dcnt);
++		
++	spin_lock_irqsave( &host->complete_lock, iflags);
++	
++	if( host->complete_what==COMPLETION_NONE ) {
++		goto clear_imask;
++	}
++	
++	if(!host->mrq) { 
++		goto clear_imask;
++	}
++
++	
++	sdi_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
++	sdi_dsta 	= readl(host->base + S3C2410_SDIDSTA);
++	sdi_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
++	sdi_cclear	= 0;
++	sdi_dclear	= 0;
++	
++	
++	if(sdi_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
++		host->mrq->cmd->error = MMC_ERR_TIMEOUT;
++		goto transfer_closed;
++	}
++
++	if(sdi_csta & S3C2410_SDICMDSTAT_CMDSENT) {
++		if(host->complete_what == COMPLETION_CMDSENT) {
++			host->mrq->cmd->error = MMC_ERR_NONE;
++			goto transfer_closed;
++		}
++
++		sdi_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
++	}
++
++	if(sdi_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
++		if (host->mrq->cmd->flags & MMC_RSP_136) {
++			DBG(PFX "s3c2410 fixup : ignore CRC fail with long rsp\n");
++		}
++		else {
++			DBG(PFX "COMMAND CRC FAILED %x\n", sdi_csta);
++			if(host->mrq->cmd->flags & MMC_RSP_CRC) {
++				host->mrq->cmd->error = MMC_ERR_BADCRC;
++				goto transfer_closed;
++			}
++		}
++		sdi_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
++	}
++
++	if(sdi_csta & S3C2410_SDICMDSTAT_RSPFIN) {
++		if(host->complete_what == COMPLETION_RSPFIN) {
++			host->mrq->cmd->error = MMC_ERR_NONE;
++			goto transfer_closed;
++		}
++
++		if(host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {
++			host->mrq->cmd->error = MMC_ERR_NONE;
++			host->complete_what = COMPLETION_XFERFINISH;
++		}
++
++		sdi_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
++	}
++
++	if(sdi_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
++		host->mrq->cmd->error = MMC_ERR_NONE;
++		host->mrq->data->error = MMC_ERR_FIFO;
++		goto transfer_closed;
++	}
++
++	if(sdi_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
++		host->mrq->cmd->error = MMC_ERR_NONE;
++		host->mrq->data->error = MMC_ERR_BADCRC;
++		goto transfer_closed;
++	}
++
++	if(sdi_dsta & S3C2410_SDIDSTA_CRCFAIL) {
++		DBG(PFX "DATA CRC FAILED %u\n", sdi_csta);
++		host->mrq->cmd->error = MMC_ERR_NONE;
++		host->mrq->data->error = MMC_ERR_BADCRC;
++		goto transfer_closed;
++	}
++
++	if(sdi_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
++		host->mrq->cmd->error = MMC_ERR_NONE;
++		host->mrq->data->error = MMC_ERR_TIMEOUT;
++		goto transfer_closed;
++	}
++
++	if(sdi_dsta & S3C2410_SDIDSTA_XFERFINISH) {
++		if(host->complete_what == COMPLETION_XFERFINISH) {
++			host->mrq->cmd->error = MMC_ERR_NONE;
++			host->mrq->data->error = MMC_ERR_NONE;
++			goto transfer_closed;
++		}
++
++		if(host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {
++			host->mrq->data->error = MMC_ERR_NONE;
++			host->complete_what = COMPLETION_RSPFIN;
++		}
++
++		sdi_dclear |= S3C2410_SDIDSTA_XFERFINISH;
++	}
++
++	writel(sdi_cclear, host->base + S3C2410_SDICMDSTAT);
++	writel(sdi_dclear, host->base + S3C2410_SDIDSTA);
++
++	spin_unlock_irqrestore( &host->complete_lock, iflags);
++	DBG(PFX "IRQ still waiting.\n");
++	return IRQ_HANDLED;
++
++
++transfer_closed:
++	writel(sdi_cclear, host->base + S3C2410_SDICMDSTAT);
++	writel(sdi_dclear, host->base + S3C2410_SDIDSTA);
++	host->complete_what = COMPLETION_NONE;
++	complete(&host->complete_request);
++	writel(0, host->base + S3C2410_SDIIMSK);
++	spin_unlock_irqrestore( &host->complete_lock, iflags);
++	DBG(PFX "IRQ transfer closed.\n");
++	return IRQ_HANDLED;
++	
++clear_imask:
++	writel(0, host->base + S3C2410_SDIIMSK);
++	spin_unlock_irqrestore( &host->complete_lock, iflags);
++	DBG(PFX "IRQ clear imask.\n");
++	return IRQ_HANDLED;
++
++}
++
++
++/*
++ * ISR for the CardDetect Pin
++*/
++
++static irqreturn_t s3c2410sdi_irq_cd(int irq, void *dev_id, struct pt_regs *regs)
++{
++	struct s3c2410sdi_host *host = (struct s3c2410sdi_host *)dev_id;
++	mmc_detect_change(host->mmc, S3C2410SDI_CDLATENCY);
++
++	return IRQ_HANDLED;
++}
++
++
++
++static void s3c2410sdi_dma_done_callback(s3c2410_dma_chan_t *dma_ch, void *buf_id,
++	int size, s3c2410_dma_buffresult_t result)
++{	unsigned long iflags;
++	u32 sdi_csta, sdi_dsta,sdi_dcnt;
++	struct s3c2410sdi_host *host = (struct s3c2410sdi_host *)buf_id;
++	
++	sdi_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
++	sdi_dsta 	= readl(host->base + S3C2410_SDIDSTA);
++	sdi_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
++	
++	DBG(PFX "DMAD csta=0x%08x dsta=0x%08x dcnt:0x%08x result:0x%08x\n", sdi_csta, sdi_dsta, sdi_dcnt, result);
++	
++	spin_lock_irqsave( &host->complete_lock, iflags);
++	
++	if(!host->mrq) goto out;
++	if(!host->mrq->data) goto out;
++	
++	
++	sdi_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
++	sdi_dsta 	= readl(host->base + S3C2410_SDIDSTA);
++	sdi_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
++		
++	if( result!=S3C2410_RES_OK ) {
++		goto fail_request;
++	}
++	
++	
++	if(host->mrq->data->flags & MMC_DATA_READ) {
++		if( sdi_dcnt>0 ) {
++			goto fail_request;
++		}
++	}
++	
++out:	
++	complete(&host->complete_dma);
++	spin_unlock_irqrestore( &host->complete_lock, iflags);
++	return;
++
++
++fail_request:
++	host->mrq->data->error = MMC_ERR_FAILED;
++	host->complete_what = COMPLETION_NONE;
++	complete(&host->complete_request);
++	writel(0, host->base + S3C2410_SDIIMSK);
++	goto out;
++
++}
++
++
++static void s3c2410sdi_dma_setup(struct s3c2410sdi_host *host, s3c2410_dmasrc_t source) {
++	
++	s3c2410_dma_devconfig(host->dma, source, 3, host->mem->start + S3C2410_SDIDATA);
++	s3c2410_dma_config(host->dma, 4, (1<<23) | (2<<24));
++	s3c2410_dma_set_buffdone_fn(host->dma, s3c2410sdi_dma_done_callback);
++	s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
++}
++
++static void s3c2410sdi_request(struct mmc_host *mmc, struct mmc_request *mrq) {
++ 	struct s3c2410sdi_host *host = mmc_priv(mmc);
++	struct device *dev = mmc_dev(host->mmc);
++	struct platform_device *pdev = to_platform_device(dev);
++	u32 sdi_carg, sdi_ccon, sdi_timer;
++	u32 sdi_bsize, sdi_dcon, sdi_imsk;
++	int dma_len = 0;
++
++	DBG(KERN_DEBUG PFX "request: [CMD] opcode:0x%02x arg:0x%08x flags:%x retries:%u\n",
++		mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags, mrq->cmd->retries);
++	DBG(PFX "request : %s mode\n",mmc->mode == MMC_MODE_MMC ? "mmc" : "sd");
++
++
++	sdi_ccon = mrq->cmd->opcode & S3C2410_SDICMDCON_INDEX;
++	sdi_ccon|= S3C2410_SDICMDCON_SENDERHOST;
++	sdi_ccon|= S3C2410_SDICMDCON_CMDSTART;
++
++	sdi_carg = mrq->cmd->arg;
++
++	sdi_timer= 0xFFFF;
++
++	sdi_bsize= 0;
++	sdi_dcon = 0;
++	sdi_imsk = 0;
++
++	/* enable interrupts for transmission errors */
++	sdi_imsk |= S3C2410_SDIIMSK_RESPONSEND;
++	sdi_imsk |= S3C2410_SDIIMSK_CRCSTATUS;
++
++	host->complete_what = COMPLETION_CMDSENT;
++
++	if (mrq->cmd->flags & MMC_RSP_PRESENT) {
++		host->complete_what = COMPLETION_RSPFIN;
++
++		sdi_ccon |= S3C2410_SDICMDCON_WAITRSP;
++		sdi_imsk |= S3C2410_SDIIMSK_CMDTIMEOUT;
++
++	} else {
++		/* We need the CMDSENT-Interrupt only if we want are not waiting
++		 * for a response
++		 */
++		sdi_imsk |= S3C2410_SDIIMSK_CMDSENT;
++	}
++
++	if(mrq->cmd->flags & MMC_RSP_136) {
++		sdi_ccon|= S3C2410_SDICMDCON_LONGRSP;
++	}
++
++	if(mrq->cmd->flags & MMC_RSP_CRC) {
++			sdi_imsk |= S3C2410_SDIIMSK_RESPONSECRC;
++	}
++
++
++	if (mrq->data) {
++		host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
++
++		sdi_bsize = (1 << mrq->data->blksz_bits);
++		host->size = mrq->data->blocks << mrq->data->blksz_bits;
++
++		sdi_dcon  = (mrq->data->blocks & S3C2410_SDIDCON_BLKNUM_MASK);
++		sdi_dcon |= S3C2410_SDIDCON_DMAEN;
++
++		sdi_imsk |= S3C2410_SDIIMSK_FIFOFAIL;
++		sdi_imsk |= S3C2410_SDIIMSK_DATACRC;
++		sdi_imsk |= S3C2410_SDIIMSK_DATATIMEOUT;
++		sdi_imsk |= S3C2410_SDIIMSK_DATAFINISH;
++		sdi_imsk |= 0xFFFFFFE0;
++
++		DBG(PFX "request: [DAT] bsize:%u blocks:%u bytes:%u\n",
++			sdi_bsize, mrq->data->blocks, mrq->data->blocks * sdi_bsize);
++
++		if (host->bus_width == MMC_BUS_WIDTH_4) {
++			sdi_dcon |= S3C2410_SDIDCON_WIDEBUS;
++		}
++
++		if(!(mrq->data->flags & MMC_DATA_STREAM)) {
++			sdi_dcon |= S3C2410_SDIDCON_BLOCKMODE;
++		}
++
++		if(mrq->data->flags & MMC_DATA_WRITE) {
++			sdi_dcon |= S3C2410_SDIDCON_TXAFTERRESP;
++			sdi_dcon |= S3C2410_SDIDCON_XFER_TXSTART;
++		}
++
++		if(mrq->data->flags & MMC_DATA_READ) {
++			sdi_dcon |= S3C2410_SDIDCON_RXAFTERCMD;
++			sdi_dcon |= S3C2410_SDIDCON_XFER_RXSTART;
++		}
++
++		s3c2410sdi_dma_setup(host, mrq->data->flags & MMC_DATA_WRITE ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW);
++
++		/* see DMA-API.txt */
++		dma_len = dma_map_sg(&pdev->dev, mrq->data->sg, \
++				mrq->data->sg_len, \
++				mrq->data->flags & MMC_DATA_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
++
++		/* start DMA */
++		s3c2410_dma_enqueue(host->dma, (void *) host,
++			sg_dma_address(&mrq->data->sg[0]),
++			(mrq->data->blocks << mrq->data->blksz_bits) );
++	}
++
++	host->mrq = mrq;
++
++	init_completion(&host->complete_request);
++	init_completion(&host->complete_dma);
++
++	/* Clear command and data status registers */
++	writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
++	writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
++
++	/* Setup SDI controller */
++	writel(sdi_bsize,host->base + S3C2410_SDIBSIZE);
++	writel(sdi_timer,host->base + S3C2410_SDITIMER);
++	writel(sdi_imsk,host->base + S3C2410_SDIIMSK);
++
++	/* Setup SDI command argument and data control */
++	writel(sdi_carg, host->base + S3C2410_SDICMDARG);
++	writel(sdi_dcon, host->base + S3C2410_SDIDCON);
++
++	/* This initiates transfer */
++	writel(sdi_ccon, host->base + S3C2410_SDICMDCON);
++
++	/* Wait for transfer to complete */
++	wait_for_completion(&host->complete_request);
++	DBG(PFX "[CMD] request complete.\n");
++	if(mrq->data) {
++		wait_for_completion(&host->complete_dma);
++		DBG(PFX "[DAT] DMA complete.\n");
++	}
++	
++	/* Cleanup controller */
++	writel(0, host->base + S3C2410_SDICMDARG);
++	writel(0, host->base + S3C2410_SDIDCON);
++	writel(0, host->base + S3C2410_SDICMDCON);
++	writel(0, host->base + S3C2410_SDIIMSK);
++
++	/*  Read response */
++	mrq->cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0);
++	mrq->cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1);
++	mrq->cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2);
++	mrq->cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);
++
++	host->mrq = NULL;
++
++	DBG(PFX "request done.\n");
++
++	/* If we have no data transfer we are finished here */
++	if (!mrq->data) goto request_done;
++
++	dma_unmap_sg(&pdev->dev, mrq->data->sg, dma_len, \
++			mrq->data->flags & MMC_DATA_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
++
++	/* Calulate the amout of bytes transfer, but only if there was
++	 * no error
++	 */
++	if(mrq->data->error == MMC_ERR_NONE) {
++		mrq->data->bytes_xfered = (mrq->data->blocks << mrq->data->blksz_bits);
++	} else {
++		mrq->data->bytes_xfered = 0;
++	}
++
++	/* If we had an error while transfering data we flush the
++	 * DMA channel to clear out any garbage
++	 */
++	if(mrq->data->error != MMC_ERR_NONE) {
++		s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
++		DBG(PFX "flushing DMA.\n");		
++	}
++
++	if(mrq->data->stop) mmc_wait_for_cmd(mmc, mrq->data->stop, 3);
++
++request_done:
++
++	mrq->done(mrq);
++}
++
++static void s3c2410sdi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) {
++	struct s3c2410sdi_host *host = mmc_priv(mmc);
++	u32 sdi_psc, sdi_con;
++
++	/* Set power */
++	sdi_con = readl(host->base + S3C2410_SDICON);
++	switch(ios->power_mode) {
++		case MMC_POWER_ON:
++		case MMC_POWER_UP:
++			DBG(PFX "power on\n");
++			s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);
++			s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);
++			s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);
++			s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
++			s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
++			s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
++
++			if (host->pdata->set_power)
++				(host->pdata->set_power)(1);
++
++			sdi_con|= S3C2410_SDICON_FIFORESET;
++			break;
++
++		case MMC_POWER_OFF:
++		default:
++			if (host->pdata->set_power)
++				(host->pdata->set_power)(0);
++			break;
++	}
++
++	/* Set clock */
++	for(sdi_psc=0;sdi_psc<255;sdi_psc++) {
++		if( (clk_get_rate(host->clk) / (2*(sdi_psc+1))) <= ios->clock) break;
++	}
++
++	if(sdi_psc > 255) sdi_psc = 255;
++	writel(sdi_psc, host->base + S3C2410_SDIPRE);
++
++	/* Set CLOCK_ENABLE */
++	if(ios->clock) 	sdi_con |= S3C2410_SDICON_CLOCKTYPE;
++	else		sdi_con &=~S3C2410_SDICON_CLOCKTYPE;
++
++	writel(sdi_con, host->base + S3C2410_SDICON);
++
++	host->bus_width = ios->bus_width;
++
++}
++
++static struct mmc_host_ops s3c2410sdi_ops = {
++	.request	= s3c2410sdi_request,
++	.set_ios	= s3c2410sdi_set_ios,
++};
++
++static void s3c2410_mmc_def_setpower(unsigned int to)
++{
++	s3c2410_gpio_cfgpin(S3C2410_GPA17, S3C2410_GPIO_OUTPUT);
++	s3c2410_gpio_setpin(S3C2410_GPA17, to);
++}
++
++static struct s3c24xx_mmc_platdata s3c2410_mmc_defplat = {
++	.gpio_detect	= S3C2410_GPF2,
++	.set_power	= s3c2410_mmc_def_setpower,
++	.f_max		= 3000000,
++	.ocr_avail	= MMC_VDD_32_33,
++};
++
++static int s3c2410sdi_probe(struct platform_device *pdev)
++{
++	struct mmc_host 	*mmc;
++	s3c24xx_mmc_pdata_t	*pdata;
++	struct s3c2410sdi_host 	*host;
++
++
++	int ret;
++
++	mmc = mmc_alloc_host(sizeof(struct s3c2410sdi_host), &pdev->dev);
++	if (!mmc) {
++		ret = -ENOMEM;
++		goto probe_out;
++	}
++
++	host = mmc_priv(mmc);
++
++	spin_lock_init( &host->complete_lock );
++	host->complete_what 	= COMPLETION_NONE;
++	host->mmc 		= mmc;
++	host->dma		= S3C2410SDI_DMA;
++
++	pdata = pdev->dev.platform_data;
++	if (!pdata) {
++		pdev->dev.platform_data = &s3c2410_mmc_defplat;
++		pdata = &s3c2410_mmc_defplat;
++	}
++
++	host->pdata = pdata;
++
++	host->irq_cd = s3c2410_gpio_getirq(pdata->gpio_detect);
++	s3c2410_gpio_cfgpin(pdata->gpio_detect, S3C2410_GPIO_IRQ);
++
++	host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	if (!host->mem) {
++		printk(KERN_ERR PFX "failed to get io memory region resouce.\n");
++		ret = -ENOENT;
++		goto probe_free_host;
++	}
++
++	host->mem = request_mem_region(host->mem->start,
++		RESSIZE(host->mem), pdev->name);
++
++	if (!host->mem) {
++		printk(KERN_ERR PFX "failed to request io memory region.\n");
++		ret = -ENOENT;
++		goto probe_free_host;
++	}
++
++	host->base = ioremap(host->mem->start, RESSIZE(host->mem));
++	if (host->base == 0) {
++		printk(KERN_ERR PFX "failed to ioremap() io memory region.\n");
++		ret = -EINVAL;
++		goto probe_free_mem_region;
++	}
++
++	host->irq = platform_get_irq(pdev, 0);
++	if (host->irq == 0) {
++		printk(KERN_ERR PFX "failed to get interrupt resouce.\n");
++		ret = -EINVAL;
++		goto probe_iounmap;
++	}
++
++	if(request_irq(host->irq, s3c2410sdi_irq, 0, DRIVER_NAME, host)) {
++		printk(KERN_ERR PFX "failed to request sdi interrupt.\n");
++		ret = -ENOENT;
++		goto probe_iounmap;
++	}
++
++	set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
++	if(request_irq(host->irq_cd, s3c2410sdi_irq_cd, 0, DRIVER_NAME, host)) {
++		printk(KERN_ERR PFX "failed to request card detect interrupt.\n" );
++		ret = -ENOENT;
++		goto probe_free_irq;
++	}
++
++	if(s3c2410_dma_request(S3C2410SDI_DMA, &s3c2410sdi_dma_client, NULL)) {
++		printk(KERN_ERR PFX "unable to get DMA channel.\n" );
++		ret = -EBUSY;
++		goto probe_free_irq_cd;
++	}
++
++	host->clk = clk_get(&pdev->dev, "sdi");
++	if (IS_ERR(host->clk)) {
++		printk(KERN_ERR PFX "failed to find clock source.\n");
++		ret = PTR_ERR(host->clk);
++		host->clk = NULL;
++		goto probe_free_host;
++	}
++
++	if((ret = clk_enable(host->clk))) {
++		printk(KERN_ERR PFX "failed to enable clock source.\n");
++		goto clk_unuse;
++	}
++
++
++	mmc->ops 	= &s3c2410sdi_ops;
++	mmc->ocr_avail	= pdata->ocr_avail;
++	mmc->f_min 	= clk_get_rate(host->clk) / 512;
++	mmc->f_max 	= clk_get_rate(host->clk) / 2;
++	mmc->caps	= MMC_CAP_4_BIT_DATA;
++
++	if(pdata->f_max && (mmc->f_max>pdata->f_max))
++		mmc->f_max = pdata->f_max;
++
++	/*
++	 * Since we only have a 16-bit data length register, we must
++	 * ensure that we don't exceed 2^16-1 bytes in a single request.
++	 * Choose 64 (512-byte) sectors as the limit.
++	 */
++	mmc->max_sectors = 64;
++
++	/*
++	 * Set the maximum segment size.  Since we aren't doing DMA
++	 * (yet) we are only limited by the data length register.
++	 */
++
++	mmc->max_seg_size = mmc->max_sectors << 9;
++	printk(KERN_INFO PFX "probe: mapped sdi_base=%p irq=%u irq_cd=%u \n",
++		host->base, host->irq, host->irq_cd);
++
++	if((ret = mmc_add_host(mmc))) {
++		printk(KERN_ERR PFX "failed to add mmc host.\n");
++		goto clk_disable;
++	}
++
++	platform_set_drvdata(pdev, mmc);
++
++	mmc_detect_change(mmc, 0);
++
++	printk(KERN_INFO PFX "initialisation done.\n");
++	return 0;
++	
++ clk_disable:
++	clk_disable(host->clk);
++
++ clk_unuse:
++	clk_put(host->clk);
++
++ probe_free_irq_cd:
++ 	free_irq(host->irq_cd, host);
++
++ probe_free_irq:
++ 	free_irq(host->irq, host);
++
++ probe_iounmap:
++	iounmap(host->base);
++
++ probe_free_mem_region:
++	release_mem_region(host->mem->start, RESSIZE(host->mem));
++
++ probe_free_host:
++	mmc_free_host(mmc);
++ probe_out:
++	return ret;
++}
++
++static int s3c2410sdi_remove(struct platform_device *pdev)
++{
++	struct mmc_host 	*mmc  = platform_get_drvdata(pdev);
++	struct s3c2410sdi_host 	*host = mmc_priv(mmc);
++
++	mmc_remove_host(mmc);
++	clk_disable(host->clk);
++	clk_put(host->clk);
++ 	free_irq(host->irq_cd, host);
++ 	free_irq(host->irq, host);
++	iounmap(host->base);
++	release_mem_region(host->mem->start, RESSIZE(host->mem));
++	mmc_free_host(mmc);
++
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int s3c2410mci_suspend(struct platform_device *dev, pm_message_t state)
++{
++	struct mmc_host *mmc = platform_get_drvdata(dev);
++	struct s3c2410sdi_host  *host;
++	int ret = 0;
++
++	if (mmc) {
++		host = mmc_priv(mmc);
++
++		ret = mmc_suspend_host(mmc, state);
++
++		clk_disable(host->clk);
++
++		disable_irq(host->irq_cd);
++		disable_irq(host->irq);
++	}
++
++	return ret;
++}
++
++static int s3c2410mci_resume(struct platform_device *dev)
++{
++	struct mmc_host *mmc = platform_get_drvdata(dev);
++	struct s3c2410sdi_host  *host;
++	int ret = 0;
++
++	if (mmc) {
++		host = mmc_priv(mmc);
++
++		enable_irq(host->irq_cd);
++		enable_irq(host->irq);
++
++		clk_enable(host->clk);
++
++		ret = mmc_resume_host(mmc);
++	}
++
++	return ret;
++}
++#else
++#define s3c2410mci_suspend	NULL
++#define s3c2410mci_resume	NULL
++#endif
++
++static struct platform_driver s3c2410sdi_driver =
++{
++	.driver		= {
++        	.name	= "s3c2410-sdi",
++		.owner	= THIS_MODULE,
++	},
++        .probe          = s3c2410sdi_probe,
++        .remove         = s3c2410sdi_remove,
++	.suspend	= s3c2410mci_suspend,
++	.resume		= s3c2410mci_resume,
++};
++
++static int __init s3c2410sdi_init(void)
++{
++	return platform_driver_register(&s3c2410sdi_driver);
++}
++
++static void __exit s3c2410sdi_exit(void)
++{
++	platform_driver_unregister(&s3c2410sdi_driver);
++}
++
++module_init(s3c2410sdi_init);
++module_exit(s3c2410sdi_exit);
++
++MODULE_DESCRIPTION("Samsung S3C2410 Multimedia Card Interface driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17.7-new/drivers/mmc/s3c2410mci.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.7-new/drivers/mmc/s3c2410mci.h	2006-08-07 09:33:51.000000000 +0200
+@@ -0,0 +1,55 @@
++/*
++ *  linux/drivers/mmc/s3c2410mci.h - Samsung S3C2410 SDI Interface driver
++ *
++ *  Copyright (C) 2004 Thomas Kleffel, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++struct clk;
++
++#define S3C2410SDI_DMA 0
++
++#define S3C2410SDI_CDLATENCY 50
++
++enum s3c2410sdi_waitfor {
++	COMPLETION_NONE,
++	COMPLETION_CMDSENT,
++	COMPLETION_RSPFIN,
++	COMPLETION_XFERFINISH,
++	COMPLETION_XFERFINISH_RSPFIN,
++};
++
++typedef struct s3c24xx_mmc_platdata s3c24xx_mmc_pdata_t;
++
++struct s3c2410sdi_host {
++	struct mmc_host		*mmc;
++	s3c24xx_mmc_pdata_t	*pdata;
++
++	struct resource		*mem;
++	struct clk		*clk;
++	void __iomem		*base;
++	int			irq;
++	int			irq_cd;
++	int			dma;
++
++	struct scatterlist*	cur_sg;		/* Current SG entry */
++	unsigned int		num_sg;		/* Number of entries left */
++	void*			mapped_sg;	/* vaddr of mapped sg */
++
++	unsigned int		offset;		/* Offset into current entry */
++	unsigned int		remain;		/* Data left in curren entry */
++
++	int			size;		/* Total size of transfer */
++
++	struct mmc_request	*mrq;
++
++	unsigned char		bus_width;	/* Current bus width */
++
++	spinlock_t		complete_lock;
++	struct completion	complete_request;
++	struct completion	complete_dma;
++	enum s3c2410sdi_waitfor	complete_what;
++};
+Index: linux-2.6.17.7-new/include/asm-arm/arch-s3c2410/mmc.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.7-new/include/asm-arm/arch-s3c2410/mmc.h	2006-08-07 09:33:51.000000000 +0200
+@@ -0,0 +1,32 @@
++/* linux/include/asm-arm/arch-s3c2410/mmc.h
++ *
++ * (c) 2004-2005 Simtec Electronics
++ *	http://www.simtec.co.uk/products/SWLINUX/
++ *	Ben Dooks <ben at simtec.co.uk>
++ *
++ * S3C24XX - MMC/SD platform data
++ *
++ * 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.
++ *
++ * Changelog:
++ *	26-Oct-2005 BJD  Created file
++*/
++
++#ifndef __ASM_ARCH_MMC_H
++#define __ASM_ARCH_MMC_H __FILE__
++
++struct s3c24xx_mmc_platdata {
++	unsigned int	gpio_detect;
++	unsigned int	gpio_wprotect;
++	unsigned int	detect_polarity;
++	unsigned int	wprotect_polarity;
++
++	unsigned long	f_max;
++	unsigned long	ocr_avail;
++
++	void		(*set_power)(unsigned int to);
++};
++
++#endif /* __ASM_ARCH_MMC_H */
+Index: linux-2.6.17.7-new/include/asm-arm/arch-s3c2410/regs-sdi.h
+===================================================================
+--- linux-2.6.17.7-new.orig/include/asm-arm/arch-s3c2410/regs-sdi.h	2006-08-07 09:32:34.000000000 +0200
++++ linux-2.6.17.7-new/include/asm-arm/arch-s3c2410/regs-sdi.h	2006-08-07 09:33:51.000000000 +0200
+@@ -47,7 +47,8 @@
+ #define S3C2410_SDICMDCON_LONGRSP     (1<<10)
+ #define S3C2410_SDICMDCON_WAITRSP     (1<<9)
+ #define S3C2410_SDICMDCON_CMDSTART    (1<<8)
+-#define S3C2410_SDICMDCON_INDEX       (0xff)
++#define S3C2410_SDICMDCON_SENDERHOST  (1<<6)
++#define S3C2410_SDICMDCON_INDEX       (0x3f)
+ 
+ #define S3C2410_SDICMDSTAT_CRCFAIL    (1<<12)
+ #define S3C2410_SDICMDSTAT_CMDSENT    (1<<11)
+@@ -73,6 +74,7 @@
+ #define S3C2410_SDIDCON_XFER_RXSTART  (2<<12)
+ #define S3C2410_SDIDCON_XFER_TXSTART  (3<<12)
+ 
++#define S3C2410_SDIDCON_BLKNUM_MASK   (0xFFF)
+ #define S3C2410_SDIDCNT_BLKNUM_SHIFT  (12)
+ 
+ #define S3C2410_SDIDSTA_RDYWAITREQ    (1<<10)
+@@ -115,4 +117,14 @@
+ #define S3C2410_SDIIMSK_RXFIFOFULL     (1<<1)
+ #define S3C2410_SDIIMSK_RXFIFOHALF     (1<<0)
+ 
++#define S3C2410_SDICMDCON_ABORT       (1<<12)
++#define S3C2410_SDICMDCON_WITHDATA    (1<<11)
++#define S3C2410_SDICMDCON_LONGRSP     (1<<10)
++#define S3C2410_SDICMDCON_WAITRSP     (1<<9)
++#define S3C2410_SDICMDCON_CMDSTART    (1<<8)
++#define S3C2410_SDICMDCON_SENDERHOST  (1<<6)
++
++#define S3C2410_SDIDCON_BLKNUM_MASK   (0xFFF)
++#define S3C2410_SDIDCNT_BLKNUM_SHIFT  (12)
++
+ #endif /* __ASM_ARM_REGS_SDI */
+Index: linux-2.6.17.7-new/arch/arm/mach-s3c2410/mach-qt2410.c
+===================================================================
+--- linux-2.6.17.7-new.orig/arch/arm/mach-s3c2410/mach-qt2410.c	2006-08-07 09:33:27.000000000 +0200
++++ linux-2.6.17.7-new/arch/arm/mach-s3c2410/mach-qt2410.c	2006-08-07 09:34:20.000000000 +0200
+@@ -29,6 +29,7 @@
+ #include <linux/timer.h>
+ #include <linux/init.h>
+ #include <linux/platform_device.h>
++#include <linux/mmc/protocol.h>
+ 
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+@@ -45,7 +46,9 @@
+ #include <asm/mach-types.h>
+ 
+ #include <asm/arch/regs-serial.h>
++#include <asm/arch/regs-gpio.h>
+ #include <asm/arch/fb.h>
++#include <asm/arch/mmc.h>
+ #include <asm/arch/nand.h>
+ 
+ #include "devs.h"
+@@ -236,6 +239,13 @@
+ 	.sets		= qt2410_nand_sets,
+ };
+ 
++static struct s3c24xx_mmc_platdata qt2410_mmc_cfg = {
++	.gpio_detect	= S3C2410_GPG10,
++	.gpio_wprotect	= S3C2410_GPH8,
++	.set_power	= NULL,
++	.ocr_avail	= MMC_VDD_32_33,
++};
++
+ static void __init qt2410_map_io(void)
+ {
+ 	s3c24xx_init_io(qt2410_iodesc, ARRAY_SIZE(qt2410_iodesc));
+@@ -259,6 +269,7 @@
+ 	s3c2410_gpio_setpin(S3C2410_GPF7, 1);
+ 
+ 	s3c_device_nand.dev.platform_data = &qt2410_nand_info;
++	s3c_device_sdi.dev.platform_data = &qt2410_mmc_cfg;
+ 	s3c24xx_fb_set_platdata(&qt2410_lcd_cfg);
+ 
+ 	s3c2410_pm_init();

Added: trunk/src/target/kernel/patches/qt2410-base.patch
===================================================================
--- trunk/src/target/kernel/patches/qt2410-base.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/qt2410-base.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,416 @@
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/Kconfig	2007-01-08 21:51:20.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/Kconfig	2007-01-08 21:51:22.000000000 +0100
+@@ -63,6 +63,12 @@
+ 	   Say Y here if you are using the SMDK2410 or the derived module A9M2410
+            <http://www.fsforth.de>
+ 
++config MACH_QT2410
++	bool "QT2410"
++	select CPU_S3C2410
++	help
++	   Say Y here if you are using the Armzone QT2410
++
+ config ARCH_S3C2440
+ 	bool "SMDK2440"
+ 	select CPU_S3C2440
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/Makefile
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/Makefile	2007-01-08 21:51:20.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/Makefile	2007-01-08 21:51:22.000000000 +0100
+@@ -43,10 +43,11 @@
+ obj-$(CONFIG_ARCH_H1940)	+= mach-h1940.o
+ obj-$(CONFIG_MACH_N30)		+= mach-n30.o
+ obj-$(CONFIG_ARCH_SMDK2410)	+= mach-smdk2410.o
++obj-$(CONFIG_MACH_QT2410)	+= mach-qt2410.o
+ obj-$(CONFIG_ARCH_S3C2440)	+= mach-smdk2440.o
+ obj-$(CONFIG_MACH_VR1000)	+= mach-vr1000.o usb-simtec.o
+ obj-$(CONFIG_MACH_RX3715)	+= mach-rx3715.o
+ obj-$(CONFIG_MACH_OTOM)		+= mach-otom.o
+ obj-$(CONFIG_MACH_NEXCODER_2440) += mach-nexcoder.o
+ 
+-obj-$(CONFIG_MACH_SMDK)		+= common-smdk.o
+\ No newline at end of file
++obj-$(CONFIG_MACH_SMDK)		+= common-smdk.o
+Index: linux-2.6.17.14-fic4.test/arch/arm/tools/mach-types
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/tools/mach-types	2007-01-08 21:51:20.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/tools/mach-types	2007-01-08 21:51:22.000000000 +0100
+@@ -1040,3 +1040,4 @@
+ ai2410			MACH_AI2410		AI2410			1027
+ ixp465			MACH_IXP465		IXP465			1028
+ balloon3		MACH_BALLOON3		BALLOON3		1029
++qt2410			MACH_QT2410		QT2410			1108
+Index: linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/map.h
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/include/asm-arm/arch-s3c2410/map.h	2007-01-08 21:51:20.000000000 +0100
++++ linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/map.h	2007-01-08 21:51:22.000000000 +0100
+@@ -225,6 +225,7 @@
+ #define S3C24XX_PA_RTC      S3C2410_PA_RTC
+ #define S3C24XX_PA_ADC      S3C2410_PA_ADC
+ #define S3C24XX_PA_SPI      S3C2410_PA_SPI
++#define S3C24XX_PA_SDI      S3C2410_PA_SDI
+ #endif
+ 
+ #endif /* __ASM_ARCH_MAP_H */
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-qt2410.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-qt2410.c	2007-01-08 21:51:49.000000000 +0100
+@@ -0,0 +1,355 @@
++/*
++ *
++ * linux/arch/arm/mach-s3c2410/mach-qt2410.c
++ *
++ * Copyright (C) 2006 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; 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 <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/list.h>
++#include <linux/timer.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/spi_bitbang.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/mach/arch.h>
++#include <asm/mach/map.h>
++#include <asm/mach/irq.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/regs-serial.h>
++#include <asm/arch/fb.h>
++#include <asm/arch/nand.h>
++#include <asm/arch/spi.h>
++#include <asm/arch/spi-gpio.h>
++
++#include "devs.h"
++#include "cpu.h"
++
++#include "pm.h"
++
++static struct map_desc qt2410_iodesc[] __initdata = {
++	IODESC_ENT(SDI),
++	IODESC_ENT(IIS),
++	{ 0xe0000000, __phys_to_pfn(S3C2410_CS3+0x01000000), SZ_1M, MT_DEVICE }
++};
++
++#define UCON S3C2410_UCON_DEFAULT
++#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
++#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
++
++static struct s3c2410_uartcfg smdk2410_uartcfgs[] = {
++	[0] = {
++		.hwport	     = 0,
++		.flags	     = 0,
++		.ucon	     = UCON,
++		.ulcon	     = ULCON,
++		.ufcon	     = UFCON,
++	},
++	[1] = {
++		.hwport	     = 1,
++		.flags	     = 0,
++		.ucon	     = UCON,
++		.ulcon	     = ULCON,
++		.ufcon	     = UFCON,
++	},
++	[2] = {
++		.hwport	     = 2,
++		.flags	     = 0,
++		.ucon	     = UCON,
++		.ulcon	     = ULCON,
++		.ufcon	     = UFCON,
++	}
++};
++
++/* LCD driver info */
++
++static struct s3c2410fb_mach_info qt2410_lcd_cfg __initdata = {
++	.regs	= {
++
++		.lcdcon1	= S3C2410_LCDCON1_TFT16BPP |
++				  S3C2410_LCDCON1_TFT |
++				  S3C2410_LCDCON1_CLKVAL(0x04),
++
++		.lcdcon2	= S3C2410_LCDCON2_VBPD(1) |
++				  S3C2410_LCDCON2_LINEVAL(319) |
++				  S3C2410_LCDCON2_VFPD(6) |
++				  S3C2410_LCDCON2_VSPW(3),
++
++		.lcdcon3	= S3C2410_LCDCON3_HBPD(12) |
++				  S3C2410_LCDCON3_HOZVAL(239) |
++				  S3C2410_LCDCON3_HFPD(7),
++
++		.lcdcon4	= S3C2410_LCDCON4_MVAL(0) |
++				  S3C2410_LCDCON4_HSPW(3),
++
++		.lcdcon5	= S3C2410_LCDCON5_FRM565 |
++				  S3C2410_LCDCON5_INVVLINE |
++				  S3C2410_LCDCON5_INVVFRAME |
++				  S3C2410_LCDCON5_PWREN |
++				  S3C2410_LCDCON5_HWSWP,
++	},
++
++#if 0
++	/* currently setup by downloader */
++	.gpccon		= 0xaa940659,
++	.gpccon_mask	= 0xffffffff,
++	.gpcup		= 0x0000ffff,
++	.gpcup_mask	= 0xffffffff,
++	.gpdcon		= 0xaa84aaa0,
++	.gpdcon_mask	= 0xffffffff,
++	.gpdup		= 0x0000faff,
++	.gpdup_mask	= 0xffffffff,
++#endif
++
++	.lpcsel		= ((0xCE6) & ~7) | 1<<4,
++
++	.width		= 240,
++	.height		= 320,
++
++	.xres		= {
++		.min	= 240,
++		.max	= 240,
++		.defval	= 240,
++	},
++
++	.yres		= {
++		.min	= 320,
++		.max	= 320,
++		.defval = 320,
++	},
++
++	.bpp		= {
++		.min	= 16,
++		.max	= 16,
++		.defval = 16,
++	},
++};
++
++static struct resource cs89x0_resources[] = {
++	[0] = {
++		.start	= 0x19000000,
++		.end	= 0x19000000 + 16,
++		.flags	= IORESOURCE_MEM,
++	},
++	[1] = {
++		.start	= IRQ_EINT9,
++		.end	= IRQ_EINT9,
++		.flags	= IORESOURCE_IRQ,
++	},
++};
++
++static struct platform_device cs89x0_device = {
++	.name		= "cirrus-cs89x0",
++	.num_resources	= ARRAY_SIZE(cs89x0_resources),
++	.resource	= cs89x0_resources,
++};
++
++static struct platform_device *qt2410_devices[] __initdata = {
++	&s3c_device_usb,
++	&s3c_device_lcd,
++	&s3c_device_wdt,
++	&s3c_device_i2c,
++	&s3c_device_iis,
++	&s3c_device_sdi,
++	&s3c_device_usbgadget,
++	&s3c_device_nand,
++	&cs89x0_device,
++};
++
++static struct s3c24xx_board qt2410_board __initdata = {
++	.devices       = qt2410_devices,
++	.devices_count = ARRAY_SIZE(qt2410_devices)
++};
++
++static struct mtd_partition qt2410_nand_part[] = {
++	[0] = {
++		.name	= "U-Boot",
++		.size	= 0x30000,
++		.offset	= 0,
++	},
++	[1] = {
++		.name	= "U-Boot environment",
++		.offset = 0x30000,
++		.size	= 0x4000,
++	},
++	[2] = {
++		.name	= "kernel",
++		.offset = 0x34000,
++		.size	= SZ_2M,
++	},
++	[3] = {
++		.name	= "initrd",
++		.offset	= 0x234000,
++		.size	= SZ_4M,
++	},
++	[4] = {
++		.name	= "jffs2",
++		.offset = 0x634000,
++		.size	= 0x39cc000,
++	},
++};
++
++static struct s3c2410_nand_set qt2410_nand_sets[] = {
++	[0] = {
++		.name		= "NAND",
++		.nr_chips	= 1,
++		.nr_partitions	= ARRAY_SIZE(qt2410_nand_part),
++		.partitions	= qt2410_nand_part,
++	},
++};
++
++/* choose a set of timings which should suit most 512Mbit
++ * chips and beyond.
++ */
++
++static struct s3c2410_platform_nand qt2410_nand_info = {
++	.tacls		= 20,
++	.twrph0		= 60,
++	.twrph1		= 20,
++	.nr_sets	= ARRAY_SIZE(qt2410_nand_sets),
++	.sets		= qt2410_nand_sets,
++};
++
++/* SPI */
++static struct spi_board_info qt2410_spi_board_info[] __initdata = {
++	{
++		.modalias	= "jbt6k74",
++		/* platform_data */
++		/* controller_data */
++		/* irq */
++		.max_speed_hz	= 10 * 1000 * 1000,
++		.bus_num	= 1,
++		/* chip_select */
++	},
++};
++
++static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int cs)
++{
++	switch (cs) {
++	case BITBANG_CS_ACTIVE:
++		s3c2410_gpio_setpin(S3C2410_GPB5, 0);
++		break;
++	case BITBANG_CS_INACTIVE:
++		s3c2410_gpio_setpin(S3C2410_GPB5, 1);
++		break;
++	}
++}
++
++static struct s3c2410_spigpio_info spi_gpio_cfg = {
++	.pin_clk	= S3C2410_GPG7,
++	.pin_mosi	= S3C2410_GPG6,
++	.pin_miso	= S3C2410_GPG5,
++	.board_size	= ARRAY_SIZE(qt2410_spi_board_info),
++	.board_info	= qt2410_spi_board_info,
++	.chip_select	= &spi_gpio_cs,
++};
++
++static struct resource s3c_spi_lcm_resource[] = {
++	[0] = {
++		.start = S3C2410_GPB5,
++		.end   = S3C2410_GPB5,
++		.flags = IORESOURCE_MEM,
++	},
++	[1] = {
++		.start = S3C2410_GPG5,
++		.end   = S3C2410_GPG5,
++		.flags = IORESOURCE_MEM,
++	},
++	[2] = {
++		.start = S3C2410_GPG6,
++		.end   = S3C2410_GPG6,
++		.flags = IORESOURCE_MEM,
++	},
++	[3] = {
++		.start = S3C2410_GPG7,
++		.end   = S3C2410_GPG7,
++		.flags = IORESOURCE_MEM,
++	},
++};
++
++static struct platform_device s3c_device_spi_lcm = {
++	.name		  = "s3c24xx-spi-gpio",
++	.id		  = 1,
++	.num_resources	  = ARRAY_SIZE(s3c_spi_lcm_resource),
++	.resource	  = s3c_spi_lcm_resource,
++	.dev = {
++		.platform_data = &spi_gpio_cfg,
++	},
++};
++
++static void __init qt2410_map_io(void)
++{
++	s3c24xx_init_io(qt2410_iodesc, ARRAY_SIZE(qt2410_iodesc));
++	s3c24xx_init_clocks(12*1000*1000);
++	s3c24xx_init_uarts(smdk2410_uartcfgs, ARRAY_SIZE(smdk2410_uartcfgs));
++	s3c24xx_set_board(&qt2410_board);
++}
++
++static void __init qt2410_machine_init(void)
++{
++	/* Configure the LEDs (even if we have no LED support)*/
++
++	s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
++	s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
++	s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);
++	s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF7_OUTP);
++
++	s3c2410_gpio_setpin(S3C2410_GPF4, 1);
++	s3c2410_gpio_setpin(S3C2410_GPF5, 1);
++	s3c2410_gpio_setpin(S3C2410_GPF6, 1);
++	s3c2410_gpio_setpin(S3C2410_GPF7, 1);
++
++	s3c_device_nand.dev.platform_data = &qt2410_nand_info;
++	s3c24xx_fb_set_platdata(&qt2410_lcd_cfg);
++
++	s3c2410_gpio_cfgpin(S3C2410_GPB5, S3C2410_GPIO_OUTPUT);
++	spi_register_board_info(qt2410_spi_board_info,
++				ARRAY_SIZE(qt2410_spi_board_info));
++	platform_device_register(&s3c_device_spi_lcm);
++
++	s3c2410_pm_init();
++}
++
++MACHINE_START(QT2410, "QT2410") 
++	.phys_io	= S3C2410_PA_UART,
++	.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
++	.boot_params	= S3C2410_SDRAM_PA + 0x100,
++	.map_io		= qt2410_map_io,
++	.init_irq	= s3c24xx_init_irq,
++	.init_machine	= qt2410_machine_init,
++	.timer		= &s3c24xx_timer,
++MACHINE_END
++
++

Added: trunk/src/target/kernel/patches/qt2410-biglcd.patch
===================================================================
--- trunk/src/target/kernel/patches/qt2410-biglcd.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/qt2410-biglcd.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,178 @@
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-qt2410.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/mach-qt2410.c	2006-12-11 21:44:34.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-qt2410.c	2006-12-11 21:44:38.000000000 +0100
+@@ -95,6 +95,133 @@
+ 
+ /* LCD driver info */
+ 
++/* Configuration for 640x480 SHARP LQ080V3DG01 */
++static struct s3c2410fb_mach_info qt2410_biglcd_cfg __initdata = {
++	.regs	= {
++
++		.lcdcon1	= S3C2410_LCDCON1_TFT16BPP |
++				  S3C2410_LCDCON1_TFT |
++				  S3C2410_LCDCON1_CLKVAL(0x01),	/* HCLK/4 */
++
++		.lcdcon2	= S3C2410_LCDCON2_VBPD(18) |	/* 19 */
++				  S3C2410_LCDCON2_LINEVAL(479) |
++				  S3C2410_LCDCON2_VFPD(10) |	/* 11 */
++				  S3C2410_LCDCON2_VSPW(14),	/* 15 */
++
++		.lcdcon3	= S3C2410_LCDCON3_HBPD(43) |	/* 44 */
++				  S3C2410_LCDCON3_HOZVAL(639) |	/* 640 */
++				  S3C2410_LCDCON3_HFPD(115),	/* 116 */
++
++		.lcdcon4	= S3C2410_LCDCON4_MVAL(0) |
++				  S3C2410_LCDCON4_HSPW(95),	/* 96 */
++
++		.lcdcon5	= S3C2410_LCDCON5_FRM565 |
++				  S3C2410_LCDCON5_INVVLINE |
++				  S3C2410_LCDCON5_INVVFRAME |
++				  S3C2410_LCDCON5_PWREN |
++				  S3C2410_LCDCON5_HWSWP,
++	},
++
++#if 0
++	/* currently setup by downloader */
++	.gpccon		= 0xaa940659,
++	.gpccon_mask	= 0xffffffff,
++	.gpcup		= 0x0000ffff,
++	.gpcup_mask	= 0xffffffff,
++	.gpdcon		= 0xaa84aaa0,
++	.gpdcon_mask	= 0xffffffff,
++	.gpdup		= 0x0000faff,
++	.gpdup_mask	= 0xffffffff,
++#endif
++
++	.lpcsel		= ((0xCE6) & ~7) | 1<<4,
++
++	.width		= 640,
++	.height		= 480,
++
++	.xres		= {
++		.min	= 640,
++		.max	= 640,
++		.defval	= 640,
++	},
++
++	.yres		= {
++		.min	= 480,
++		.max	= 480,
++		.defval = 480,
++	},
++
++	.bpp		= {
++		.min	= 16,
++		.max	= 16,
++		.defval = 16,
++	},
++};
++
++/* Configuration for 480x640 toppoly TD028TTEC1 */
++static struct s3c2410fb_mach_info qt2410_prodlcd_cfg __initdata = {
++	.regs	= {
++
++		.lcdcon1	= S3C2410_LCDCON1_TFT16BPP |
++				  S3C2410_LCDCON1_TFT |
++				  S3C2410_LCDCON1_CLKVAL(0x01),	/* HCLK/4 */
++
++		.lcdcon2	= S3C2410_LCDCON2_VBPD(1) |	/* 2 */
++				  S3C2410_LCDCON2_LINEVAL(639) |/* 640 */
++				  S3C2410_LCDCON2_VFPD(3) |	/* 4 */
++				  S3C2410_LCDCON2_VSPW(1),	/* 2 */
++
++		.lcdcon3	= S3C2410_LCDCON3_HBPD(7) |	/* 8 */
++				  S3C2410_LCDCON3_HOZVAL(479) |	/* 479 */
++				  S3C2410_LCDCON3_HFPD(23),	/* 24 */
++
++		.lcdcon4	= S3C2410_LCDCON4_MVAL(0) |
++				  S3C2410_LCDCON4_HSPW(7),	/* 8 */
++
++		.lcdcon5	= S3C2410_LCDCON5_FRM565 |
++				  S3C2410_LCDCON5_INVVLINE |
++				  S3C2410_LCDCON5_INVVFRAME |
++				  S3C2410_LCDCON5_PWREN |
++				  S3C2410_LCDCON5_HWSWP,
++	},
++
++#if 0
++	/* currently setup by downloader */
++	.gpccon		= 0xaa940659,
++	.gpccon_mask	= 0xffffffff,
++	.gpcup		= 0x0000ffff,
++	.gpcup_mask	= 0xffffffff,
++	.gpdcon		= 0xaa84aaa0,
++	.gpdcon_mask	= 0xffffffff,
++	.gpdup		= 0x0000faff,
++	.gpdup_mask	= 0xffffffff,
++#endif
++
++	.lpcsel		= ((0xCE6) & ~7) | 1<<4,
++
++	.width		= 480,
++	.height		= 640,
++
++	.xres		= {
++		.min	= 480,
++		.max	= 480,
++		.defval	= 480,
++	},
++
++	.yres		= {
++		.min	= 640,
++		.max	= 640,
++		.defval = 640,
++	},
++
++	.bpp		= {
++		.min	= 16,
++		.max	= 16,
++		.defval = 16,
++	},
++};
++
++/* Config for 240x320 LCD */
+ static struct s3c2410fb_mach_info qt2410_lcd_cfg __initdata = {
+ 	.regs	= {
+ 
+@@ -329,6 +456,17 @@
+ 	.udc_command	= qt2410_udc_pullup,
+ };
+ 
++static char tft_type = 's';
++
++int __init tft_setup(char *str)
++{
++	tft_type = str[0];
++	return 1;
++}
++
++__setup("tft=", tft_setup);
++
++
+ static void __init qt2410_map_io(void)
+ {
+ 	s3c24xx_init_io(qt2410_iodesc, ARRAY_SIZE(qt2410_iodesc));
+@@ -352,7 +490,20 @@
+ 	s3c2410_gpio_setpin(S3C2410_GPF7, 1);
+ 
+ 	s3c_device_nand.dev.platform_data = &qt2410_nand_info;
+-	s3c24xx_fb_set_platdata(&qt2410_lcd_cfg);
++
++	switch (tft_type) {
++	case 'p': /* production */
++		s3c24xx_fb_set_platdata(&qt2410_prodlcd_cfg);
++		break;
++	case 'b': /* big */
++		s3c24xx_fb_set_platdata(&qt2410_biglcd_cfg);
++		break;
++	case 's': /* small */
++	default:
++		s3c24xx_fb_set_platdata(&qt2410_lcd_cfg);
++		break;
++	}
++
+ 	s3c24xx_udc_set_platdata(&qt2410_udc_cfg);
+ 
+ 	s3c2410_gpio_cfgpin(S3C2410_GPB5, S3C2410_GPIO_OUTPUT);

Added: trunk/src/target/kernel/patches/qt2410-cs8900.patch
===================================================================
--- trunk/src/target/kernel/patches/qt2410-cs8900.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/qt2410-cs8900.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,61 @@
+Index: linux-2.6.17.14-fic3/drivers/net/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic3.orig/drivers/net/Kconfig	2006-12-07 16:16:46.000000000 +0100
++++ linux-2.6.17.14-fic3/drivers/net/Kconfig	2006-12-07 16:17:50.000000000 +0100
+@@ -1246,7 +1246,7 @@
+ 
+ config NET_PCI
+ 	bool "EISA, VLB, PCI and on board controllers"
+-	depends on NET_ETHERNET && (ISA || EISA || PCI)
++	depends on NET_ETHERNET && (ISA || EISA || PCI || MACH_QT2410)
+ 	help
+ 	  This is another class of network cards which attach directly to the
+ 	  bus. If you have one of those, say Y and read the Ethernet-HOWTO,
+@@ -1386,7 +1386,7 @@
+ 
+ config CS89x0
+ 	tristate "CS89x0 support"
+-	depends on NET_PCI && (ISA || MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X)
++	depends on NET_PCI && (ISA || MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X || MACH_QT2410)
+ 	---help---
+ 	  Support for CS89x0 chipset based Ethernet cards. If you have a
+ 	  network (Ethernet) card of this type, say Y and read the
+Index: linux-2.6.17.14-fic3/drivers/net/cs89x0.c
+===================================================================
+--- linux-2.6.17.14-fic3.orig/drivers/net/cs89x0.c	2006-12-07 16:16:46.000000000 +0100
++++ linux-2.6.17.14-fic3/drivers/net/cs89x0.c	2006-12-07 16:17:50.000000000 +0100
+@@ -195,6 +195,10 @@
+ #define CIRRUS_DEFAULT_IRQ	VH_INTC_INT_NUM_CASCADED_INTERRUPT_1 /* Event inputs bank 1 - ID 35/bit 3 */
+ static unsigned int netcard_portlist[] __initdata = {CIRRUS_DEFAULT_BASE, 0};
+ static unsigned int cs8900_irq_map[] = {CIRRUS_DEFAULT_IRQ, 0, 0, 0};
++#elif defined(CONFIG_MACH_QT2410)
++#include <asm/arch/irqs.h>
++static unsigned int netcard_portlist [] __initdata = { 0xe0000300, 0 };
++static unsigned int cs8900_irq_map[] = { IRQ_EINT9, 0, 0, 0 };
+ #else
+ static unsigned int netcard_portlist[] __initdata =
+    { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
+@@ -830,6 +834,14 @@
+ 
+ 	printk(" IRQ %d", dev->irq);
+ 
++	dev->dev_addr[0] = 0x00;
++	dev->dev_addr[1] = 0x00;
++	dev->dev_addr[2] = 0xc0;
++	dev->dev_addr[3] = 0xff;
++	dev->dev_addr[4] = 0xee;
++	dev->dev_addr[5] = 0x08;
++	set_mac_address(dev, dev->dev_addr);
++
+ #if ALLOW_DMA
+ 	if (lp->use_dma) {
+ 		get_dma_channel(dev);
+@@ -1310,7 +1322,7 @@
+ 	else
+ #endif
+ 	{
+-#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01) && !defined(CONFIG_ARCH_PNX010X)
++#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01) && !defined(CONFIG_ARCH_PNX010X) && !defined(CONFIG_MACH_QT2410)
+ 		if (((1 << dev->irq) & lp->irq_map) == 0) {
+ 			printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
+                                dev->name, dev->irq, lp->irq_map);

Added: trunk/src/target/kernel/patches/qt2410-s3c_mci-pdata.patch
===================================================================
--- trunk/src/target/kernel/patches/qt2410-s3c_mci-pdata.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/qt2410-s3c_mci-pdata.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,45 @@
+This patch adds platform data to support the SD/MMC slot of the Armzone
+S3C2410 development board.
+
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-qt2410.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/mach-qt2410.c	2007-01-08 22:46:39.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-qt2410.c	2007-01-08 22:51:34.000000000 +0100
+@@ -30,6 +30,7 @@
+ #include <linux/timer.h>
+ #include <linux/init.h>
+ #include <linux/platform_device.h>
++#include <linux/mmc/protocol.h>
+ #include <linux/spi/spi.h>
+ #include <linux/spi/spi_bitbang.h>
+ 
+@@ -55,6 +56,7 @@
+ #include <asm/arch/ts.h>
+ #include <asm/arch/spi.h>
+ #include <asm/arch/spi-gpio.h>
++#include <asm/arch/mci.h>
+ 
+ #include "devs.h"
+ #include "cpu.h"
+@@ -476,6 +478,13 @@
+ 	.oversampling_shift = 2,
+ };
+ 
++static struct s3c24xx_mci_pdata qt2410_mmc_cfg = {
++	.gpio_wprotect	= S3C2410_GPH8,
++	.gpio_detect	= S3C2410_GPG10,
++	.set_power	= NULL,
++	.ocr_avail 	= MMC_VDD_32_33,
++};
++
+ static void __init qt2410_map_io(void)
+ {
+ 	s3c24xx_init_io(qt2410_iodesc, ARRAY_SIZE(qt2410_iodesc));
+@@ -499,6 +508,7 @@
+ 	s3c2410_gpio_setpin(S3C2410_GPF7, 1);
+ 
+ 	s3c_device_nand.dev.platform_data = &qt2410_nand_info;
++	s3c_device_sdi.dev.platform_data = &qt2410_mmc_cfg;
+ 
+ 	switch (tft_type) {
+ 	case 'p': /* production */

Added: trunk/src/target/kernel/patches/qt2410-touchscreen.patch
===================================================================
--- trunk/src/target/kernel/patches/qt2410-touchscreen.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/qt2410-touchscreen.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,530 @@
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/devs.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/devs.c	2006-12-11 21:44:31.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/devs.c	2006-12-11 21:44:43.000000000 +0100
+@@ -37,6 +37,7 @@
+ 
+ #include <asm/arch/regs-serial.h>
+ #include <asm/arch/udc.h>
++#include <asm/arch/ts.h>
+ 
+ #include "devs.h"
+ 
+@@ -137,6 +138,23 @@
+ 
+ EXPORT_SYMBOL(s3c_device_nand);
+ 
++/* Touchscreen */
++struct platform_device s3c_device_ts = {
++	.name		  = "s3c2410-ts",
++	.id		  = -1,
++};
++
++EXPORT_SYMBOL(s3c_device_ts);
++
++static struct s3c2410_ts_mach_info s3c2410ts_info;
++
++void __init set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info)
++{
++	memcpy(&s3c2410ts_info,hard_s3c2410ts_info,sizeof(struct s3c2410_ts_mach_info));
++	s3c_device_ts.dev.platform_data = &s3c2410ts_info;
++}
++EXPORT_SYMBOL(set_s3c2410ts_info);
++
+ /* USB Device (Gadget)*/
+ 
+ static struct resource s3c_usbgadget_resource[] = {
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/devs.h
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/devs.h	2006-12-11 21:44:29.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/devs.h	2006-12-11 21:44:43.000000000 +0100
+@@ -39,6 +39,7 @@
+ extern struct platform_device s3c_device_timer3;
+ 
+ extern struct platform_device s3c_device_usbgadget;
++extern struct platform_device s3c_device_ts;
+ 
+ /* s3c2440 specific devices */
+ 
+Index: linux-2.6.17.14-fic4.test/drivers/input/touchscreen/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/input/touchscreen/Kconfig	2006-12-11 21:44:29.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/input/touchscreen/Kconfig	2006-12-11 21:44:43.000000000 +0100
+@@ -49,6 +49,24 @@
+ 	  To compile this driver as a module, choose M here: the
+ 	  module will be called corgi_ts.
+ 
++config TOUCHSCREEN_S3C2410
++	tristate "Samsung S3C2410 touchscreen input driver"
++	depends on ARCH_S3C2410 && INPUT && INPUT_TOUCHSCREEN
++	select SERIO
++	help
++	  Say Y here if you have the s3c2410 touchscreen.
++
++	  If unsure, say N.
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called s3c2410_ts.
++
++config TOUCHSCREEN_S3C2410_DEBUG
++	boolean "Samsung S3C2410 touchscreen debug messages"
++	depends on TOUCHSCREEN_S3C2410
++	help
++	  Select this if you want debug messages
++
+ config TOUCHSCREEN_GUNZE
+ 	tristate "Gunze AHL-51S touchscreen"
+ 	select SERIO
+Index: linux-2.6.17.14-fic4.test/drivers/input/touchscreen/Makefile
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/input/touchscreen/Makefile	2006-12-11 21:44:29.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/input/touchscreen/Makefile	2006-12-11 21:44:43.000000000 +0100
+@@ -12,3 +12,5 @@
+ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
+ obj-$(CONFIG_TOUCHSCREEN_MK712)	+= mk712.o
+ obj-$(CONFIG_TOUCHSCREEN_HP600)	+= hp680_ts_input.o
++obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
++
+Index: linux-2.6.17.14-fic4.test/drivers/input/touchscreen/s3c2410_ts.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/drivers/input/touchscreen/s3c2410_ts.c	2006-12-11 21:44:43.000000000 +0100
+@@ -0,0 +1,351 @@
++/*
++ * 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
++ *
++ * Copyright (c) 2004 Arnaud Patard <arnaud.patard at rtp-net.org>
++ * iPAQ H1940 touchscreen support
++ *
++ * ChangeLog
++ *
++ * 2004-09-05: Herbert Pötzl <herbert at 13thfloor.at>
++ *	- added clock (de-)allocation code
++ *
++ * 2005-03-06: Arnaud Patard <arnaud.patard at rtp-net.org>
++ *      - h1940_ -> s3c2410 (this driver is now also used on the n30
++ *        machines :P)
++ *      - Debug messages are now enabled with the config option
++ *        TOUCHSCREEN_S3C2410_DEBUG
++ *      - Changed the way the value are read
++ *      - Input subsystem should now work
++ *      - Use ioremap and readl/writel
++ *
++ * 2005-03-23: Arnaud Patard <arnaud.patard at rtp-net.org>
++ *      - Make use of some undocumented features of the touchscreen
++ *        controller
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/input.h>
++#include <linux/init.h>
++#include <linux/serio.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/clk.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++
++#include <asm/arch/regs-adc.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/ts.h>
++
++/* For ts.dev.id.version */
++#define S3C2410TSVERSION	0x0101
++
++#define WAIT4INT(x)  (((x)<<8) | \
++		     S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
++		     S3C2410_ADCTSC_XY_PST(3))
++
++#define AUTOPST	     (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
++		     S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
++
++#define DEBUG_LVL    KERN_DEBUG
++
++MODULE_AUTHOR("Arnaud Patard <arnaud.patard at rtp-net.org>");
++MODULE_DESCRIPTION("s3c2410 touchscreen driver");
++MODULE_LICENSE("GPL");
++
++/*
++ * Definitions & global arrays.
++ */
++
++
++static char *s3c2410ts_name = "s3c2410 TouchScreen";
++
++/*
++ * Per-touchscreen data.
++ */
++
++struct s3c2410ts {
++	struct input_dev *dev;
++	long xp;
++	long yp;
++	int count;
++	int shift;
++};
++
++static struct s3c2410ts ts;
++static void __iomem *base_addr;
++
++static inline void s3c2410_ts_connect(void)
++{
++	s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
++	s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);
++	s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);
++	s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
++}
++
++static void touch_timer_fire(unsigned long data)
++{
++  	unsigned long data0;
++  	unsigned long data1;
++	int updown;
++
++  	data0 = readl(base_addr+S3C2410_ADCDAT0);
++  	data1 = readl(base_addr+S3C2410_ADCDAT1);
++
++ 	updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
++
++ 	if (updown) {
++ 		if (ts.count != 0) {
++ 			ts.xp >>= ts.shift;
++ 			ts.yp >>= ts.shift;
++
++#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
++ 			{
++ 				struct timeval tv;
++ 				do_gettimeofday(&tv);
++ 				printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts.xp, ts.yp);
++ 			}
++#endif
++
++ 			input_report_abs(ts.dev, ABS_X, ts.xp);
++ 			input_report_abs(ts.dev, ABS_Y, ts.yp);
++
++ 			input_report_key(ts.dev, BTN_TOUCH, 1);
++ 			input_report_abs(ts.dev, ABS_PRESSURE, 1);
++ 			input_sync(ts.dev);
++ 		}
++
++ 		ts.xp = 0;
++ 		ts.yp = 0;
++ 		ts.count = 0;
++
++ 		writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
++ 		writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
++ 	} else {
++ 		ts.count = 0;
++
++ 		input_report_key(ts.dev, BTN_TOUCH, 0);
++ 		input_report_abs(ts.dev, ABS_PRESSURE, 0);
++ 		input_sync(ts.dev);
++
++ 		writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
++ 	}
++}
++
++static struct timer_list touch_timer =
++		TIMER_INITIALIZER(touch_timer_fire, 0, 0);
++
++static irqreturn_t stylus_updown(int irq, void *dev_id, struct pt_regs *regs)
++{
++	unsigned long data0;
++	unsigned long data1;
++	int updown;
++
++	data0 = readl(base_addr+S3C2410_ADCDAT0);
++	data1 = readl(base_addr+S3C2410_ADCDAT1);
++
++	updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
++
++	/* TODO we should never get an interrupt with updown set while
++	 * the timer is running, but maybe we ought to verify that the
++	 * timer isn't running anyways. */
++
++	if (updown)
++		touch_timer_fire(0);
++
++	return IRQ_HANDLED;
++}
++
++
++static irqreturn_t stylus_action(int irq, void *dev_id, struct pt_regs *regs)
++{
++	unsigned long data0;
++	unsigned long data1;
++
++	data0 = readl(base_addr+S3C2410_ADCDAT0);
++	data1 = readl(base_addr+S3C2410_ADCDAT1);
++
++	ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
++	ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
++	ts.count++;
++
++        if (ts.count < (1<<ts.shift)) {
++		writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
++		writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
++	} else {
++		mod_timer(&touch_timer, jiffies+1);
++		writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
++	}
++
++	return IRQ_HANDLED;
++}
++
++static struct clk	*adc_clock;
++
++/*
++ * The functions for inserting/removing us as a module.
++ */
++
++static int __init s3c2410ts_probe(struct platform_device *pdev)
++{
++	struct s3c2410_ts_mach_info *info;
++	struct input_dev *input_dev;
++
++	info = ( struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
++
++	if (!info)
++	{
++		printk(KERN_ERR "Hm... too bad : no platform data for ts\n");
++		return -EINVAL;
++	}
++
++#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
++	printk(DEBUG_LVL "Entering s3c2410ts_init\n");
++#endif
++
++	adc_clock = clk_get(NULL, "adc");
++	if (!adc_clock) {
++		printk(KERN_ERR "failed to get adc clock source\n");
++		return -ENOENT;
++	}
++	clk_enable(adc_clock);
++
++#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
++	printk(DEBUG_LVL "got and enabled clock\n");
++#endif
++
++	base_addr=ioremap(S3C2410_PA_ADC,0x20);
++	if (base_addr == NULL) {
++		printk(KERN_ERR "Failed to remap register block\n");
++		return -ENOMEM;
++	}
++
++
++	/* Configure GPIOs */
++	s3c2410_ts_connect();
++
++	if ((info->presc&0xff) > 0)
++		writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
++			     base_addr+S3C2410_ADCCON);
++	else
++		writel(0,base_addr+S3C2410_ADCCON);
++
++
++	/* Initialise registers */
++	if ((info->delay&0xffff) > 0)
++		writel(info->delay & 0xffff,  base_addr+S3C2410_ADCDLY);
++
++	writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
++
++	/* Initialise input stuff */
++	memset(&ts, 0, sizeof(struct s3c2410ts));
++	input_dev = input_allocate_device();
++
++	if (!input_dev) {
++		printk(KERN_ERR "Unable to allocate the input device !!\n");
++		return -ENOMEM;
++	}
++
++	ts.dev = input_dev;
++	ts.dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
++	ts.dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
++	input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
++	input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
++	input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
++
++	ts.dev->private = &ts;
++	ts.dev->name = s3c2410ts_name;
++	ts.dev->id.bustype = BUS_RS232;
++	ts.dev->id.vendor = 0xDEAD;
++	ts.dev->id.product = 0xBEEF;
++	ts.dev->id.version = S3C2410TSVERSION;
++
++	ts.shift = info->oversampling_shift;
++
++	/* Get irqs */
++	if (request_irq(IRQ_ADC, stylus_action, SA_SAMPLE_RANDOM,
++		"s3c2410_action", ts.dev)) {
++		printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");
++		iounmap(base_addr);
++		return -EIO;
++	}
++	if (request_irq(IRQ_TC, stylus_updown, SA_SAMPLE_RANDOM,
++			"s3c2410_action", ts.dev)) {
++		printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");
++		iounmap(base_addr);
++		return -EIO;
++	}
++
++	printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);
++
++	/* All went ok, so register to the input system */
++	input_register_device(ts.dev);
++
++	return 0;
++}
++
++static int s3c2410ts_remove(struct platform_device *pdev)
++{
++	disable_irq(IRQ_ADC);
++	disable_irq(IRQ_TC);
++	free_irq(IRQ_TC,ts.dev);
++	free_irq(IRQ_ADC,ts.dev);
++
++	if (adc_clock) {
++		clk_disable(adc_clock);
++		clk_put(adc_clock);
++		adc_clock = NULL;
++	}
++
++	input_unregister_device(ts.dev);
++	iounmap(base_addr);
++
++	return 0;
++}
++
++static struct platform_driver s3c2410ts_driver = {
++       .driver         = {
++	       .name   = "s3c2410-ts",
++	       .owner  = THIS_MODULE,
++       },
++       .probe          = s3c2410ts_probe,
++       .remove         = s3c2410ts_remove,
++};
++
++
++static int __init s3c2410ts_init(void)
++{
++	return platform_driver_register(&s3c2410ts_driver);
++}
++
++static void __exit s3c2410ts_exit(void)
++{
++	platform_driver_unregister(&s3c2410ts_driver);
++}
++
++module_init(s3c2410ts_init);
++module_exit(s3c2410ts_exit);
++
++/*
++    Local variables:
++        compile-command: "make ARCH=arm CROSS_COMPILE=/usr/local/arm/3.3.2/bin/arm-linux- -k -C ../../.."
++        c-basic-offset: 8
++    End:
++*/
+Index: linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/regs-adc.h
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/include/asm-arm/arch-s3c2410/regs-adc.h	2006-12-11 21:44:29.000000000 +0100
++++ linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/regs-adc.h	2006-12-11 21:44:43.000000000 +0100
+@@ -44,7 +44,7 @@
+ #define S3C2410_ADCTSC_XP_SEN		(1<<4)
+ #define S3C2410_ADCTSC_PULL_UP_DISABLE	(1<<3)
+ #define S3C2410_ADCTSC_AUTO_PST		(1<<2)
+-#define S3C2410_ADCTSC_XY_PST		(0x3<<0)
++#define S3C2410_ADCTSC_XY_PST(x)	(((x)&0x3)<<0)
+ 
+ /* ADCDAT0 Bits */
+ #define S3C2410_ADCDAT0_UPDOWN		(1<<15)
+Index: linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/ts.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/ts.h	2006-12-11 21:44:43.000000000 +0100
+@@ -0,0 +1,28 @@
++/* linux/include/asm/arch-s3c2410/ts.h
++ *
++ * Copyright (c) 2005 Arnaud Patard <arnaud.patard at rtp-net.org>
++ *
++ *
++ * 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.
++ *
++ *
++ *  Changelog:
++ *     24-Mar-2005     RTP     Created file
++ *     03-Aug-2005     RTP     Renamed to ts.h
++ */
++
++#ifndef __ASM_ARM_TS_H
++#define __ASM_ARM_TS_H
++
++struct s3c2410_ts_mach_info {
++       int             delay;
++       int             presc;
++       int             oversampling_shift;
++};
++
++void __init set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info);
++
++#endif /* __ASM_ARM_TS_H */
++
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-qt2410.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/mach-qt2410.c	2006-12-11 21:44:38.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-qt2410.c	2006-12-11 21:44:43.000000000 +0100
+@@ -51,6 +51,7 @@
+ #include <asm/arch/fb.h>
+ #include <asm/arch/nand.h>
+ #include <asm/arch/udc.h>
++#include <asm/arch/ts.h>
+ #include <asm/arch/spi.h>
+ #include <asm/arch/spi-gpio.h>
+ 
+@@ -312,6 +313,7 @@
+ 	&s3c_device_sdi,
+ 	&s3c_device_usbgadget,
+ 	&s3c_device_nand,
++	&s3c_device_ts,
+ 	&cs89x0_device,
+ };
+ 
+@@ -467,6 +469,12 @@
+ __setup("tft=", tft_setup);
+ 
+ 
++static struct s3c2410_ts_mach_info qt2410_ts_cfg = {
++	.delay = 10000,
++	.presc = 49,
++	.oversampling_shift = 2,
++};
++
+ static void __init qt2410_map_io(void)
+ {
+ 	s3c24xx_init_io(qt2410_iodesc, ARRAY_SIZE(qt2410_iodesc));
+@@ -505,6 +513,7 @@
+ 	}
+ 
+ 	s3c24xx_udc_set_platdata(&qt2410_udc_cfg);
++	set_s3c2410ts_info(&qt2410_ts_cfg);
+ 
+ 	s3c2410_gpio_cfgpin(S3C2410_GPB5, S3C2410_GPIO_OUTPUT);
+ 	spi_register_board_info(qt2410_spi_board_info,

Added: trunk/src/target/kernel/patches/s3c2410-bbt.patch
===================================================================
--- trunk/src/target/kernel/patches/s3c2410-bbt.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/s3c2410-bbt.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,13 @@
+Index: linux-2.6.17.14-fic4.test/drivers/mtd/nand/s3c2410.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/mtd/nand/s3c2410.c	2007-01-14 01:27:27.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/mtd/nand/s3c2410.c	2007-01-14 01:29:01.000000000 +0100
+@@ -524,7 +524,7 @@
+ 	chip->select_chip  = s3c2410_nand_select_chip;
+ 	chip->chip_delay   = 50;
+ 	chip->priv	   = nmtd;
+-	chip->options	   = 0;
++	chip->options	   = NAND_USE_FLASH_BBT;
+ 	chip->controller   = &info->controller;
+ 
+ 	if (info->is_s3c2440) {

Added: trunk/src/target/kernel/patches/s3c2410_serial-nodebug.patch
===================================================================
--- trunk/src/target/kernel/patches/s3c2410_serial-nodebug.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/s3c2410_serial-nodebug.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,28 @@
+Index: linux-2.6.17.7-new/drivers/serial/s3c2410.c
+===================================================================
+--- linux-2.6.17.7-new.orig/drivers/serial/s3c2410.c	2006-08-12 01:43:50.000000000 +0530
++++ linux-2.6.17.7-new/drivers/serial/s3c2410.c	2006-08-12 01:44:30.000000000 +0530
+@@ -710,10 +710,6 @@
+ 		int calc_deviation;
+ 
+ 		for (sptr = res; sptr < resptr; sptr++) {
+-			printk(KERN_DEBUG
+-			       "found clk %p (%s) quot %d, calc %d\n",
+-			       sptr->clksrc, sptr->clksrc->name,
+-			       sptr->quot, sptr->calc);
+ 
+ 			calc_deviation = baud - sptr->calc;
+ 			if (calc_deviation < 0)
+@@ -725,12 +721,8 @@
+ 			}
+ 		}
+ 
+-		printk(KERN_DEBUG "best %p (deviation %d)\n", best, deviation);
+ 	}
+ 
+-	printk(KERN_DEBUG "selected clock %p (%s) quot %d, calc %d\n",
+-	       best->clksrc, best->clksrc->name, best->quot, best->calc);
+-
+ 	/* store results to pass back */
+ 
+ 	*clksrc = best->clksrc;

Added: trunk/src/target/kernel/patches/s3c2410_udc-vbus_draw_pdata.patch
===================================================================
--- trunk/src/target/kernel/patches/s3c2410_udc-vbus_draw_pdata.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/s3c2410_udc-vbus_draw_pdata.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,51 @@
+Introduce a platform_device (machine) specific callback function
+which gets called when the amount of power we can draw from Vbus
+has changed.
+
+Index: linux-2.6.17.14-fic4.test/drivers/usb/gadget/s3c2410_udc.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/usb/gadget/s3c2410_udc.c	2007-01-21 13:12:03.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/usb/gadget/s3c2410_udc.c	2007-01-21 13:29:44.000000000 +0100
+@@ -1370,11 +1370,22 @@
+ 	return 0;
+ }
+ 
++static void s3c2410_vbus_draw(struct usb_gadget *_gadget, int ma)
++{
++	struct s3c2410_udc  *udc;
++
++	dprintk(DEBUG_NORMAL, "s3c2410_vbus_draw()\n");
++
++	if (udc_info && udc_info->vbus_draw)
++		udc_info->vbus_draw(ma);
++}
++
+ static const struct usb_gadget_ops s3c2410_ops = {
+ 	.get_frame          = s3c2410_g_get_frame,
+ 	.wakeup             = s3c2410_wakeup,
+ 	.set_selfpowered    = s3c2410_set_selfpowered,
+         .pullup             = s3c2410_pullup,
++	.vbus_draw	    = s3c2410_vbus_draw,
+ };
+ 
+ 
+Index: linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/udc.h
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/include/asm-arm/arch-s3c2410/udc.h	2007-01-21 13:06:57.000000000 +0100
++++ linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/udc.h	2007-01-21 13:12:03.000000000 +0100
+@@ -12,6 +12,7 @@
+  *	14-Mar-2005	RTP	Created file
+  *	02-Aug-2005	RTP	File rename
+  *	07-Sep-2005	BJD	Minor cleanups, changed cmd to enum
++ *	18-Jan-2007	HMW	Add per-platform vbus_draw function
+ */
+ 
+ #ifndef __ASM_ARM_ARCH_UDC_H
+@@ -25,6 +26,7 @@
+ 
+ struct s3c2410_udc_mach_info {
+ 	void	(*udc_command)(enum s3c2410_udc_cmd_e);
++	void	(*vbus_draw)(unsigned int ma);
+ };
+ 
+ extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *);

Added: trunk/src/target/kernel/patches/s3c_mci.patch
===================================================================
--- trunk/src/target/kernel/patches/s3c_mci.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/s3c_mci.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,1644 @@
+This is the latest S3C MMC/SD driver by Thomas Kleffel
+
+Index: linux-2.6.17.7-fic1/drivers/mmc/Kconfig
+===================================================================
+--- linux-2.6.17.7-fic1.orig/drivers/mmc/Kconfig	2006-11-02 12:48:23.000000000 +0100
++++ linux-2.6.17.7-fic1/drivers/mmc/Kconfig	2006-11-02 12:48:50.000000000 +0100
+@@ -109,4 +109,16 @@
+ 
+ 	  If unsure, say N.
+ 
++config MMC_S3C
++	tristate "Samsung S3C SD/MMC Card Interface support"
++	depends on ARCH_S3C2410 && MMC
++	help
++	  This selects a driver for the MCI interface found in
++          Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
++	  If you have a board based on one of those and a MMC/SD
++	  slot, say Y or M here.
++
++	  If unsure, say N.
++
++
+ endmenu
+Index: linux-2.6.17.7-fic1/drivers/mmc/Makefile
+===================================================================
+--- linux-2.6.17.7-fic1.orig/drivers/mmc/Makefile	2006-11-02 12:48:23.000000000 +0100
++++ linux-2.6.17.7-fic1/drivers/mmc/Makefile	2006-11-02 12:48:50.000000000 +0100
+@@ -23,9 +23,11 @@
+ obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
+ obj-$(CONFIG_MMC_OMAP)		+= omap.o
+ obj-$(CONFIG_MMC_AT91RM9200)	+= at91_mci.o
++obj-$(CONFIG_MMC_S3C)		+= s3cmci.o
+ 
+ mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o
+ 
+ ifeq ($(CONFIG_MMC_DEBUG),y)
++obj-$(CONFIG_MMC)		+= mmc_debug.o
+ EXTRA_CFLAGS += -DDEBUG
+ endif
+Index: linux-2.6.17.7-fic1/drivers/mmc/mmc_debug.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.7-fic1/drivers/mmc/mmc_debug.c	2006-11-02 12:48:50.000000000 +0100
+@@ -0,0 +1,59 @@
++/*
++ *  linux/drivers/mmc/mmc_debug.c
++ *
++ *  Copyright (C) 2003 maintech GmbH, Thomas Kleffel <tk at maintech.de>
++ *
++ * This file contains debug helper functions for the MMC/SD stack
++ *
++ * 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.
++ *
++ */
++
++#include <linux/mmc/mmc.h>
++#include "mmc_debug.h"
++
++char *mmc_cmd2str(int cmd)
++{
++	switch(cmd) {
++		case  0: return "GO_IDLE_STATE";
++		case  1: return "ALL_SEND_OCR";
++		case  2: return "ALL_SEND_CID";
++		case  3: return "ALL_SEND_RELATIVE_ADD";
++		case  6: return "ACMD: SD_SET_BUSWIDTH";
++		case  7: return "SEL_DESEL_CARD";
++		case  9: return "SEND_CSD";
++		case 10: return "SEND_CID";
++		case 11: return "READ_UNTIL_STOP";
++		case 12: return "STOP_TRANSMISSION";
++		case 13: return "SEND_STATUS";
++		case 15: return "GO_INACTIVE_STATE";
++		case 16: return "SET_BLOCKLEN";
++		case 17: return "READ_SINGLE_BLOCK";
++		case 18: return "READ_MULTIPLE_BLOCK";
++		case 24: return "WRITE_SINGLE_BLOCK";
++		case 25: return "WRITE_MULTIPLE_BLOCK";
++		case 41: return "ACMD: SD_APP_OP_COND";
++		case 55: return "APP_CMD";
++		default: return "UNKNOWN";
++	}
++}
++EXPORT_SYMBOL(mmc_cmd2str);
++
++char *mmc_err2str(int err)
++{
++	switch(err) {
++		case MMC_ERR_NONE:	return "OK";
++		case MMC_ERR_TIMEOUT:	return "TIMEOUT";
++		case MMC_ERR_BADCRC: 	return "BADCRC";
++		case MMC_ERR_FIFO: 	return "FIFO";
++		case MMC_ERR_FAILED: 	return "FAILED";
++		case MMC_ERR_INVALID: 	return "INVALID";
++		case MMC_ERR_BUSY:	return "BUSY";
++		case MMC_ERR_DMA:	return "DMA";
++		case MMC_ERR_CANCELED:	return "CANCELED";
++		default: return "UNKNOWN";
++	}
++}
++EXPORT_SYMBOL(mmc_err2str);
+Index: linux-2.6.17.7-fic1/drivers/mmc/mmc_debug.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.7-fic1/drivers/mmc/mmc_debug.h	2006-11-02 12:48:50.000000000 +0100
+@@ -0,0 +1,7 @@
++#ifndef MMC_DEBUG_H
++#define MMC_DEBUG_H
++
++char *mmc_cmd2str(int err);
++char *mmc_err2str(int err);
++
++#endif /* MMC_DEBUG_H */
+Index: linux-2.6.17.7-fic1/drivers/mmc/s3cmci.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.7-fic1/drivers/mmc/s3cmci.c	2006-11-02 21:06:00.000000000 +0100
+@@ -0,0 +1,1339 @@
++/*
++ *  linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
++ *
++ *  Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk at maintech.de>
++ *
++ * 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.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/dma-mapping.h>
++#include <linux/clk.h>
++#include <linux/mmc/host.h>
++#include <linux/mmc/protocol.h>
++#include <linux/platform_device.h>
++
++#include <asm/dma.h>
++#include <asm/dma-mapping.h>
++
++#include <asm/io.h>
++#include <asm/arch/regs-sdi.h>
++#include <asm/arch/regs-gpio.h>
++
++#include "mmc_debug.h"
++#include "s3cmci.h"
++
++#define DRIVER_NAME "s3c-mci"
++
++enum dbg_channels {
++	dbg_err   = (1 << 0),
++	dbg_debug = (1 << 1),
++	dbg_info  = (1 << 2),
++	dbg_irq   = (1 << 3),
++	dbg_sg    = (1 << 4),
++	dbg_dma   = (1 << 5),
++	dbg_pio   = (1 << 6),
++	dbg_fail  = (1 << 7),
++	dbg_conf  = (1 << 8),
++};
++
++static const int dbgmap_err   = dbg_err | dbg_fail;
++static const int dbgmap_info  = dbg_info | dbg_conf;
++static const int dbgmap_debug = dbg_debug;
++
++#define dbg(host, channels, args...)		 \
++	if (dbgmap_err & channels) 		 \
++		dev_err(&host->pdev->dev, args); \
++	else if (dbgmap_info & channels)	 \
++		dev_info(&host->pdev->dev, args);\
++	else if (dbgmap_debug & channels)	 \
++		dev_dbg(&host->pdev->dev, args);
++
++#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
++
++static struct s3c2410_dma_client s3cmci_dma_client = {
++	.name		= "s3c-mci",
++};
++
++static void finalize_request(struct s3cmci_host *host);
++static void s3cmci_send_request(struct mmc_host *mmc);
++static void s3cmci_reset(struct s3cmci_host *host);
++
++#ifdef CONFIG_MMC_DEBUG
++
++static inline void dbg_dumpregs(struct s3cmci_host *host, char *prefix)
++{
++	u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize;
++	u32 datcon, datcnt, datsta, fsta, imask;
++
++	con 	= readl(host->base + S3C2410_SDICON);
++	pre 	= readl(host->base + S3C2410_SDIPRE);
++	cmdarg 	= readl(host->base + S3C2410_SDICMDARG);
++	cmdcon 	= readl(host->base + S3C2410_SDICMDCON);
++	cmdsta 	= readl(host->base + S3C2410_SDICMDSTAT);
++	r0 	= readl(host->base + S3C2410_SDIRSP0);
++	r1 	= readl(host->base + S3C2410_SDIRSP1);
++	r2 	= readl(host->base + S3C2410_SDIRSP2);
++	r3 	= readl(host->base + S3C2410_SDIRSP3);
++	timer 	= readl(host->base + S3C2410_SDITIMER);
++	bsize 	= readl(host->base + S3C2410_SDIBSIZE);
++	datcon 	= readl(host->base + S3C2410_SDIDCON);
++	datcnt 	= readl(host->base + S3C2410_SDIDCNT);
++	datsta 	= readl(host->base + S3C2410_SDIDSTA);
++	fsta 	= readl(host->base + S3C2410_SDIFSTA);
++	imask   = readl(host->base + host->sdiimsk);
++
++	dbg(host, dbg_debug, "%s  CON:[%08x]  PRE:[%08x]  TMR:[%08x]\n",
++				prefix, con, pre, timer);
++
++	dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n",
++				prefix, cmdcon, cmdarg, cmdsta);
++
++	dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]"
++			       " DSTA:[%08x] DCNT:[%08x]\n",
++				prefix, datcon, fsta, datsta, datcnt);
++
++	dbg(host, dbg_debug, "%s   R0:[%08x]   R1:[%08x]"
++			       "   R2:[%08x]   R3:[%08x]\n",
++				prefix, r0, r1, r2, r3);
++}
++
++static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd,
++								int stop)
++{
++ 	snprintf(host->dbgmsg_cmd, 300,
++		"#%u%s op:%s(%i) arg:0x%08x flags:0x08%x retries:%u",
++		host->ccnt, (stop?" (STOP)":""), mmc_cmd2str(cmd->opcode),
++		cmd->opcode, cmd->arg, cmd->flags, cmd->retries);
++
++	if (cmd->data) {
++		snprintf(host->dbgmsg_dat, 300,
++			"#%u bsize:%u blocks:%u bytes:%u",
++			host->dcnt, (1 << cmd->data->blksz_bits),
++			cmd->data->blocks,
++			cmd->data->blocks * (1 << cmd->data->blksz_bits));
++	} else {
++		host->dbgmsg_dat[0] = '\0';
++	}
++}
++
++static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd,
++								int fail)
++{
++	unsigned int dbglvl = fail?dbg_fail:dbg_debug;
++
++	if (!cmd)
++		return;
++
++	if (cmd->error == MMC_ERR_NONE) {
++
++		dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n",
++			host->dbgmsg_cmd, cmd->resp[0]);
++	} else {
++		dbg(host, dbglvl, "CMD[%s] %s Status:%s\n",
++			mmc_err2str(cmd->error), host->dbgmsg_cmd,
++			host->status);
++	}
++
++	if (!cmd->data)
++		return;
++
++	if (cmd->data->error == MMC_ERR_NONE) {
++		dbg(host, dbglvl, "DAT[%s] %s\n",
++			mmc_err2str(cmd->data->error), host->dbgmsg_dat);
++	} else {
++		dbg(host, dbglvl, "DAT[%s] %s DCNT:0x%08x\n",
++			mmc_err2str(cmd->data->error), host->dbgmsg_dat,
++			readl(host->base + S3C2410_SDIDCNT));
++	}
++}
++#endif
++
++static inline u32 enable_imask(struct s3cmci_host *host, u32 imask)
++{
++	u32 newmask;
++
++	newmask = readl(host->base + host->sdiimsk);
++	newmask|= imask;
++
++	writel(newmask, host->base + host->sdiimsk);
++
++	return newmask;
++}
++
++static inline u32 disable_imask(struct s3cmci_host *host, u32 imask)
++{
++	u32 newmask;
++
++	newmask = readl(host->base + host->sdiimsk);
++	newmask&= ~imask;
++
++	writel(newmask, host->base + host->sdiimsk);
++
++	return newmask;
++}
++
++static inline void clear_imask(struct s3cmci_host *host)
++{
++	writel(0, host->base + host->sdiimsk);
++}
++
++static inline int get_data_buffer(struct s3cmci_host *host,
++			volatile u32 *words, volatile u32 **pointer)
++{
++	struct scatterlist *sg;
++
++	if (host->pio_active == XFER_NONE)
++		return -EINVAL;
++
++	if ((!host->mrq) || (!host->mrq->data))
++		return -EINVAL;
++
++	if (host->pio_sgptr >= host->mrq->data->sg_len) {
++		dbg(host, dbg_debug, "no more buffers (%i/%i)\n",
++		      host->pio_sgptr, host->mrq->data->sg_len);
++		return -EBUSY;
++	}
++	sg = &host->mrq->data->sg[host->pio_sgptr];
++
++	*words	= sg->length >> 2;
++	*pointer= page_address(sg->page) + sg->offset;
++
++	host->pio_sgptr++;
++
++	dbg(host, dbg_sg, "new buffer (%i/%i)\n",
++	      host->pio_sgptr, host->mrq->data->sg_len);
++
++	return 0;
++}
++
++#define FIFO_FILL(host) ((readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK) >> 2)
++#define FIFO_FREE(host) ((63 - (readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK)) >> 2)
++
++static inline void do_pio_read(struct s3cmci_host *host)
++{
++	int res;
++	u32 fifo;
++	void __iomem *from_ptr;
++
++	//write real prescaler to host, it might be set slow to fix
++	writel(host->prescaler, host->base + S3C2410_SDIPRE);
++
++	from_ptr = host->base + host->sdidata;
++
++	while ((fifo = FIFO_FILL(host))) {
++		if (!host->pio_words) {
++			res = get_data_buffer(host, &host->pio_words,
++							&host->pio_ptr);
++			if (res) {
++				host->pio_active = XFER_NONE;
++				host->complete_what = COMPLETION_FINALIZE;
++
++				dbg(host, dbg_pio, "pio_read(): "
++					"complete (no more data).\n");
++				return;
++			}
++
++			dbg(host, dbg_pio, "pio_read(): new target: [%i]@[%p]\n",
++			       host->pio_words, host->pio_ptr);
++		}
++
++		dbg(host, dbg_pio, "pio_read(): fifo:[%02i] "
++				   "buffer:[%03i] dcnt:[%08X]\n",
++				   fifo, host->pio_words,
++				   readl(host->base + S3C2410_SDIDCNT));
++
++		if (fifo > host->pio_words)
++			fifo = host->pio_words;
++
++		host->pio_words-= fifo;
++		host->pio_count+= fifo;
++
++		while(fifo--) {
++			*(host->pio_ptr++) = readl(from_ptr);
++		}
++	}
++
++	if (!host->pio_words) {
++		res = get_data_buffer(host, &host->pio_words, &host->pio_ptr);
++		if (res) {
++			dbg(host, dbg_pio, "pio_read(): "
++				"complete (no more buffers).\n");
++			host->pio_active = XFER_NONE;
++			host->complete_what = COMPLETION_FINALIZE;
++
++			return;
++		}
++	}
++
++	enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
++}
++
++static inline void do_pio_write(struct s3cmci_host *host)
++{
++	int res;
++	u32 fifo;
++
++	void __iomem *to_ptr;
++
++	to_ptr = host->base + host->sdidata;
++
++	while ((fifo = FIFO_FREE(host))) {
++		if (!host->pio_words) {
++			res = get_data_buffer(host, &host->pio_words,
++							&host->pio_ptr);
++			if (res) {
++				dbg(host, dbg_pio, "pio_write(): "
++					"complete (no more data).\n");
++				host->pio_active = XFER_NONE;
++
++				return;
++			}
++
++			dbg(host, dbg_pio, "pio_write(): "
++				"new source: [%i]@[%p]\n",
++				host->pio_words, host->pio_ptr);
++
++		}
++
++		if (fifo > host->pio_words)
++			fifo = host->pio_words;
++
++		host->pio_words-= fifo;
++		host->pio_count+= fifo;
++
++		while(fifo--) {
++			writel(*(host->pio_ptr++), to_ptr);
++		}
++	}
++
++	enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
++}
++
++static void pio_tasklet(unsigned long data)
++{
++	struct s3cmci_host *host = (struct s3cmci_host *) data;
++
++
++	if (host->pio_active == XFER_WRITE)
++		do_pio_write(host);
++
++	if (host->pio_active == XFER_READ)
++		do_pio_read(host);
++
++	if (host->complete_what == COMPLETION_FINALIZE) {
++		clear_imask(host);
++		if (host->pio_active != XFER_NONE) {
++			dbg(host, dbg_err, "unfinished %s "
++				"- pio_count:[%u] pio_words:[%u]\n",
++				(host->pio_active == XFER_READ)?"read":"write",
++				host->pio_count, host->pio_words);
++
++			host->mrq->data->error = MMC_ERR_DMA;
++		}
++
++		disable_irq(host->irq);
++		finalize_request(host);
++	}
++}
++
++/*
++ * ISR for SDI Interface IRQ
++ * Communication between driver and ISR works as follows:
++ *   host->mrq 			points to current request
++ *   host->complete_what	tells the ISR when the request is considered done
++ *     COMPLETION_CMDSENT	  when the command was sent
++ *     COMPLETION_RSPFIN          when a response was received
++ *     COMPLETION_XFERFINISH	  when the data transfer is finished
++ *     COMPLETION_XFERFINISH_RSPFIN both of the above.
++ *   host->complete_request	is the completion-object the driver waits for
++ *
++ * 1) Driver sets up host->mrq and host->complete_what
++ * 2) Driver prepares the transfer
++ * 3) Driver enables interrupts
++ * 4) Driver starts transfer
++ * 5) Driver waits for host->complete_rquest
++ * 6) ISR checks for request status (errors and success)
++ * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
++ * 7) ISR completes host->complete_request
++ * 8) ISR disables interrupts
++ * 9) Driver wakes up and takes care of the request
++ *
++ * Note: "->error"-fields are expected to be set to 0 before the request
++ *       was issued by mmc.c - therefore they are only set, when an error
++ *       contition comes up
++ */
++
++static irqreturn_t s3cmci_irq(int irq, void *dev_id, struct pt_regs *regs)
++{
++	struct s3cmci_host *host;
++	struct mmc_command *cmd;
++	u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
++	u32 mci_cclear, mci_dclear;
++	unsigned long iflags;
++
++	host = (struct s3cmci_host *)dev_id;
++
++	spin_lock_irqsave(&host->complete_lock, iflags);
++
++	mci_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
++	mci_dsta 	= readl(host->base + S3C2410_SDIDSTA);
++	mci_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
++	mci_fsta 	= readl(host->base + S3C2410_SDIFSTA);
++	mci_imsk	= readl(host->base + host->sdiimsk);
++	mci_cclear	= 0;
++	mci_dclear	= 0;
++
++	if ((host->complete_what == COMPLETION_NONE) ||
++			(host->complete_what == COMPLETION_FINALIZE)) {
++		host->status = "nothing to complete";
++		clear_imask(host);
++		goto irq_out;
++	}
++
++	if (!host->mrq) {
++		host->status = "no active mrq";
++		clear_imask(host);
++		goto irq_out;
++	}
++
++	cmd = host->cmd_is_stop?host->mrq->stop:host->mrq->cmd;
++
++	if (!cmd) {
++		host->status = "no active cmd";
++		clear_imask(host);
++		goto irq_out;
++	}
++
++	if (!host->dodma) {
++		if ((host->pio_active == XFER_WRITE) &&
++				(mci_fsta & S3C2410_SDIFSTA_TFDET)) {
++
++			disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
++			tasklet_schedule(&host->pio_tasklet);
++			host->status = "pio tx";
++		}
++
++		if ((host->pio_active == XFER_READ) &&
++				(mci_fsta & S3C2410_SDIFSTA_RFDET)) {
++
++			disable_imask(host,
++				S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
++
++			tasklet_schedule(&host->pio_tasklet);
++			host->status = "pio rx";
++		}
++	}
++
++	if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
++		cmd->error = MMC_ERR_TIMEOUT;
++		host->status = "error: command timeout";
++		goto fail_transfer;
++	}
++
++	if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) {
++		if (host->complete_what == COMPLETION_CMDSENT) {
++			host->status = "ok: command sent";
++			goto close_transfer;
++		}
++
++		mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
++	}
++
++	if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
++		if (cmd->flags & MMC_RSP_CRC) {
++			cmd->error = MMC_ERR_BADCRC;
++			host->status = "error: bad command crc";
++			goto fail_transfer;
++		}
++
++		mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
++	}
++
++	if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) {
++		if (host->complete_what == COMPLETION_RSPFIN) {
++			host->status = "ok: command response received";
++			goto close_transfer;
++		}
++
++		if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
++			host->complete_what = COMPLETION_XFERFINISH;
++
++		mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
++	}
++
++	/* errors handled after this point are only relevant
++	   when a data transfer is in progress */
++
++	if (!cmd->data)
++		goto clear_status_bits;
++
++	/* Check for FIFO failure */
++	if (host->is2440) {
++		if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) {
++			host->mrq->data->error = MMC_ERR_FIFO;
++			host->status = "error: 2440 fifo failure";
++			goto fail_transfer;
++		}
++	} else {
++		if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
++			cmd->data->error = MMC_ERR_FIFO;
++			host->status = "error:  fifo failure";
++			goto fail_transfer;
++		}
++	}
++
++	if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
++		cmd->data->error = MMC_ERR_BADCRC;
++		host->status = "error: bad data crc (outgoing)";
++		goto fail_transfer;
++	}
++
++	if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {
++		cmd->data->error = MMC_ERR_BADCRC;
++		host->status = "error: bad data crc (incoming)";
++		goto fail_transfer;
++	}
++
++	if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
++		cmd->data->error = MMC_ERR_TIMEOUT;
++		host->status = "error: data timeout";
++		goto fail_transfer;
++	}
++
++	if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) {
++		if (host->complete_what == COMPLETION_XFERFINISH) {
++			host->status = "ok: data transfer completed";
++			goto close_transfer;
++		}
++
++		if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {
++			host->complete_what = COMPLETION_RSPFIN;
++		}
++
++		mci_dclear |= S3C2410_SDIDSTA_XFERFINISH;
++	}
++
++clear_status_bits:
++	writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);
++	writel(mci_dclear, host->base + S3C2410_SDIDSTA);
++
++	goto irq_out;
++
++fail_transfer:
++	host->pio_active = XFER_NONE;
++
++close_transfer:
++	host->complete_what = COMPLETION_FINALIZE;
++
++	clear_imask(host);
++	tasklet_schedule(&host->pio_tasklet);
++
++	goto irq_out;
++
++irq_out:
++	dbg(host, dbg_irq, "csta:0x%08x dsta:0x%08x "
++			   "fsta:0x%08x dcnt:0x%08x status:%s.\n",
++				mci_csta, mci_dsta, mci_fsta,
++				mci_dcnt, host->status);
++
++	spin_unlock_irqrestore(&host->complete_lock, iflags);
++	return IRQ_HANDLED;
++
++}
++
++/*
++ * ISR for the CardDetect Pin
++*/
++
++static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id, struct pt_regs *regs)
++{
++	struct s3cmci_host *host = (struct s3cmci_host *)dev_id;
++
++	dbg(host, dbg_irq, "card detect\n");
++
++	mmc_detect_change(host->mmc, 500);
++
++	return IRQ_HANDLED;
++}
++
++void s3cmci_dma_done_callback(s3c2410_dma_chan_t *dma_ch, void *buf_id,
++	int size, s3c2410_dma_buffresult_t result)
++{
++	unsigned long iflags;
++	u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt;
++	struct s3cmci_host *host = (struct s3cmci_host *)buf_id;
++
++	mci_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
++	mci_dsta 	= readl(host->base + S3C2410_SDIDSTA);
++	mci_fsta 	= readl(host->base + S3C2410_SDIFSTA);
++	mci_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
++
++	if ((!host->mrq) || (!host->mrq) || (!host->mrq->data))
++		return;
++
++	if (!host->dmatogo)
++		return;
++
++	spin_lock_irqsave(&host->complete_lock, iflags);
++
++	if (result != S3C2410_RES_OK) {
++		dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "
++			"fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",
++			mci_csta, mci_dsta, mci_fsta,
++			mci_dcnt, result, host->dmatogo);
++
++		goto fail_request;
++	}
++
++	host->dmatogo--;
++	if (host->dmatogo) {
++		dbg(host, dbg_dma, "DMA DONE  Size:%i DSTA:[%08x] "
++			"DCNT:[%08x] toGo:%u\n",
++			size, mci_dsta, mci_dcnt, host->dmatogo);
++
++		goto out;
++	}
++
++	dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
++		size, mci_dsta, mci_dcnt);
++
++	host->complete_what = COMPLETION_FINALIZE;
++
++out:
++	tasklet_schedule(&host->pio_tasklet);
++	spin_unlock_irqrestore(&host->complete_lock, iflags);
++	return;
++
++
++fail_request:
++	host->mrq->data->error = MMC_ERR_DMA;
++	host->complete_what = COMPLETION_FINALIZE;
++	writel(0, host->base + host->sdiimsk);
++	goto out;
++
++}
++
++static void finalize_request(struct s3cmci_host *host)
++{
++	struct mmc_request *mrq = host->mrq;
++	struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd;
++	int debug_as_failure = 0;
++
++	if (host->complete_what != COMPLETION_FINALIZE)
++		return;
++
++	if (!mrq)
++		return;
++
++	if (cmd->data && (cmd->error == MMC_ERR_NONE) &&
++		  (cmd->data->error == MMC_ERR_NONE)) {
++
++		if (host->dodma && (!host->dma_complete)) {
++			dbg(host, dbg_dma, "DMA Missing!\n");
++			return;
++		}
++	}
++
++	// Read response
++	cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0);
++	cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1);
++	cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2);
++	cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);
++
++	// reset clock speed, as it could still be set low for
++	writel(host->prescaler, host->base + S3C2410_SDIPRE);
++
++	if (cmd->error)
++		debug_as_failure = 1;
++
++	if (cmd->data && cmd->data->error)
++		debug_as_failure = 1;
++
++	//if(cmd->flags & MMC_RSP_MAYFAIL) debug_as_failure = 0;
++
++#ifdef CONFIG_MMC_DEBUG
++	dbg_dumpcmd(host, cmd, debug_as_failure);
++#endif
++	//Cleanup controller
++	writel(0, host->base + S3C2410_SDICMDARG);
++	writel(0, host->base + S3C2410_SDIDCON);
++	writel(0, host->base + S3C2410_SDICMDCON);
++	writel(0, host->base + host->sdiimsk);
++
++	if (cmd->data && cmd->error)
++		cmd->data->error = cmd->error;
++
++	if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) {
++		host->cmd_is_stop = 1;
++		s3cmci_send_request(host->mmc);
++		return;
++	}
++
++	// If we have no data transfer we are finished here
++	if (!mrq->data)
++		goto request_done;
++
++	// Calulate the amout of bytes transfer, but only if there was
++	// no error
++	if (mrq->data->error == MMC_ERR_NONE) {
++		mrq->data->bytes_xfered =
++			(mrq->data->blocks << mrq->data->blksz_bits);
++	} else {
++		mrq->data->bytes_xfered = 0;
++	}
++
++	// If we had an error while transfering data we flush the
++	// DMA channel and the fifo to clear out any garbage
++	if (mrq->data->error != MMC_ERR_NONE) {
++		if (host->dodma)
++			s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
++
++		if (host->is2440) {
++			//Clear failure register and reset fifo
++			writel(S3C2440_SDIFSTA_FIFORESET |
++			       S3C2440_SDIFSTA_FIFOFAIL,
++			       host->base + S3C2410_SDIFSTA);
++		} else {
++			u32 mci_con;
++
++			//reset fifo
++			mci_con = readl(host->base + S3C2410_SDICON);
++			mci_con|= S3C2410_SDICON_FIFORESET;
++
++			writel(mci_con, host->base + S3C2410_SDICON);
++		}
++	}
++
++request_done:
++	host->complete_what = COMPLETION_NONE;
++	host->mrq = NULL;
++	mmc_request_done(host->mmc, mrq);
++}
++
++
++void s3cmci_dma_setup(struct s3cmci_host *host, s3c2410_dmasrc_t source)
++{
++	static int setup_ok = 0;
++	static s3c2410_dmasrc_t last_source = -1;
++
++	if (last_source == source)
++		return;
++
++	last_source = source;
++
++	s3c2410_dma_devconfig(host->dma, source, 3,
++		host->mem->start + host->sdidata);
++
++	if (!setup_ok) {
++		s3c2410_dma_config(host->dma, 4,
++			(S3C2410_DCON_HWTRIG | S3C2410_DCON_CH0_SDI));
++		s3c2410_dma_set_buffdone_fn(host->dma, s3cmci_dma_done_callback);
++		s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
++		setup_ok = 1;
++	}
++}
++
++static void s3cmci_send_command(struct s3cmci_host *host,
++					struct mmc_command *cmd)
++{
++	u32 ccon, imsk;
++
++	imsk  = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
++		S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
++		S3C2410_SDIIMSK_RESPONSECRC;
++
++	enable_imask(host, imsk);
++
++	if (cmd->data) {
++		host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
++	} else if (cmd->flags & MMC_RSP_PRESENT) {
++		host->complete_what = COMPLETION_RSPFIN;
++	} else {
++		host->complete_what = COMPLETION_CMDSENT;
++	}
++
++	writel(cmd->arg, host->base + S3C2410_SDICMDARG);
++
++	ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;
++	ccon|= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;
++
++	if (cmd->flags & MMC_RSP_PRESENT)
++		ccon |= S3C2410_SDICMDCON_WAITRSP;
++
++	if (cmd->flags & MMC_RSP_136)
++		ccon|= S3C2410_SDICMDCON_LONGRSP;
++
++	writel(ccon, host->base + S3C2410_SDICMDCON);
++}
++
++static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
++{
++	u32 dcon, imsk, stoptries=3;
++
++	/* write DCON register */
++
++	if (!data) {
++		writel(0, host->base + S3C2410_SDIDCON);
++		return 0;
++	}
++
++	while(readl(host->base + S3C2410_SDIDSTA) &
++		(S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
++
++		dbg(host, dbg_err,
++			"mci_setup_data() transfer stillin progress.\n");
++
++		writel(0, host->base + S3C2410_SDIDCON);
++		s3cmci_reset(host);
++
++		if (0 == (stoptries--)) {
++#ifdef CONFIG_MMC_DEBUG
++			dbg_dumpregs(host, "DRF");
++#endif
++
++			return -EINVAL;
++		}
++	}
++
++	dcon  = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;
++
++	if (host->dodma) {
++		dcon |= S3C2410_SDIDCON_DMAEN;
++	}
++
++	if (host->bus_width == MMC_BUS_WIDTH_4) {
++		dcon |= S3C2410_SDIDCON_WIDEBUS;
++	}
++
++	if (!(data->flags & MMC_DATA_STREAM)) {
++		dcon |= S3C2410_SDIDCON_BLOCKMODE;
++	}
++
++	if (data->flags & MMC_DATA_WRITE) {
++		dcon |= S3C2410_SDIDCON_TXAFTERRESP;
++		dcon |= S3C2410_SDIDCON_XFER_TXSTART;
++	}
++
++	if (data->flags & MMC_DATA_READ) {
++		dcon |= S3C2410_SDIDCON_RXAFTERCMD;
++		dcon |= S3C2410_SDIDCON_XFER_RXSTART;
++	}
++
++	if (host->is2440) {
++		dcon |= S3C2440_SDIDCON_DS_WORD;
++		dcon |= S3C2440_SDIDCON_DATSTART;
++	}
++
++	writel(dcon, host->base + S3C2410_SDIDCON);
++
++	/* write BSIZE register */
++
++	writel((1 << data->blksz_bits), host->base + S3C2410_SDIBSIZE);
++
++	/* add to IMASK register */
++	imsk =	S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
++		S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
++
++	enable_imask(host, imsk);
++
++	/* write TIMER register */
++
++	if (host->is2440) {
++		writel(0x007FFFFF, host->base + S3C2410_SDITIMER);
++	} else {
++		writel(0x0000FFFF, host->base + S3C2410_SDITIMER);
++
++		//FIX: set slow clock to prevent timeouts on read
++		if (data->flags & MMC_DATA_READ) {
++			writel(0xFF, host->base + S3C2410_SDIPRE);
++		}
++	}
++
++	//debug_dump_registers(host, "Data setup:");
++
++	return 0;
++}
++
++static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data)
++{
++	int rw = (data->flags & MMC_DATA_WRITE)?1:0;
++
++	if (rw != ((data->flags & MMC_DATA_READ)?0:1))
++		return -EINVAL;
++
++	host->pio_sgptr = 0;
++	host->pio_words = 0;
++	host->pio_count = 0;
++	host->pio_active = rw?XFER_WRITE:XFER_READ;
++
++	if (rw) {
++		do_pio_write(host);
++		enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
++	} else {
++		enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF
++			| S3C2410_SDIIMSK_RXFIFOLAST);
++	}
++
++	return 0;
++}
++
++static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
++{
++	int dma_len, i;
++
++	int rw = (data->flags & MMC_DATA_WRITE)?1:0;
++
++	if (rw != ((data->flags & MMC_DATA_READ)?0:1))
++		return -EINVAL;
++
++	s3cmci_dma_setup(host, rw?S3C2410_DMASRC_MEM:S3C2410_DMASRC_HW);
++	s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
++
++	dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
++				(rw)?DMA_TO_DEVICE:DMA_FROM_DEVICE);
++
++
++	if (dma_len == 0)
++		return -ENOMEM;
++
++	host->dma_complete = 0;
++	host->dmatogo = dma_len;
++
++	for (i = 0; i < dma_len; i++) {
++		int res;
++
++		dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,
++			sg_dma_address(&data->sg[i]),
++			sg_dma_len(&data->sg[i]));
++
++		res = s3c2410_dma_enqueue(host->dma, (void *) host,
++				sg_dma_address(&data->sg[i]),
++				sg_dma_len(&data->sg[i]));
++
++		if (res) {
++			s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
++			return -EBUSY;
++		}
++ 	}
++
++	s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);
++
++	return 0;
++}
++
++static void s3cmci_send_request(struct mmc_host *mmc)
++{
++	struct s3cmci_host *host = mmc_priv(mmc);
++	struct mmc_request *mrq = host->mrq;
++	struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd;
++
++	host->ccnt++;
++#ifdef CONFIG_MMC_DEBUG
++	prepare_dbgmsg(host, cmd, host->cmd_is_stop);
++#endif
++	//Clear command, data and fifo status registers
++	//Fifo clear only necessary on 2440, but doesn't hurt on 2410
++	writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
++	writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
++	writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);
++
++	if (cmd->data) {
++		int res;
++		res = s3cmci_setup_data(host, cmd->data);
++
++		host->dcnt++;
++
++		if (res) {
++			cmd->error = MMC_ERR_DMA;
++			cmd->data->error = MMC_ERR_DMA;
++
++			mmc_request_done(mmc, mrq);
++			return;
++		}
++
++
++		if (host->dodma) {
++			res = s3cmci_prepare_dma(host, cmd->data);
++		} else {
++			res = s3cmci_prepare_pio(host, cmd->data);
++		}
++
++		if (res) {
++			cmd->error = MMC_ERR_DMA;
++			cmd->data->error = MMC_ERR_DMA;
++
++			mmc_request_done(mmc, mrq);
++			return;
++		}
++
++	}
++
++	// Send command
++	s3cmci_send_command(host, cmd);
++
++	// Enable Interrupt
++	enable_irq(host->irq);
++}
++
++static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
++{
++ 	struct s3cmci_host *host = mmc_priv(mmc);
++
++	host->cmd_is_stop = 0;
++	host->mrq = mrq;
++
++	s3cmci_send_request(mmc);
++}
++
++static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
++{
++	struct s3cmci_host *host = mmc_priv(mmc);
++	u32 mci_psc, mci_con;
++
++	//Set power
++	mci_con = readl(host->base + S3C2410_SDICON);
++	switch(ios->power_mode) {
++		case MMC_POWER_ON:
++		case MMC_POWER_UP:
++			s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);
++			s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);
++			s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);
++			s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
++			s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
++			s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
++
++			if (!host->is2440)
++				mci_con|=S3C2410_SDICON_FIFORESET;
++
++			break;
++
++		case MMC_POWER_OFF:
++		default:
++			s3c2410_gpio_setpin(S3C2410_GPE5, 0);
++			s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
++
++			if (host->is2440)
++				mci_con|=S3C2440_SDICON_SDRESET;
++
++			break;
++	}
++
++	//Set clock
++	for (mci_psc=0; mci_psc<255; mci_psc++) {
++		host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
++
++		if (host->real_rate <= ios->clock)
++			break;
++	}
++
++	if(mci_psc > 255) mci_psc = 255;
++	host->prescaler = mci_psc;
++
++	writel(host->prescaler, host->base + S3C2410_SDIPRE);
++
++	//If requested clock is 0, real_rate will be 0, too
++	if (ios->clock == 0)
++		host->real_rate = 0;
++
++	//Set CLOCK_ENABLE
++	if (ios->clock)
++		mci_con |= S3C2410_SDICON_CLOCKTYPE;
++	else
++		mci_con &=~S3C2410_SDICON_CLOCKTYPE;
++
++	writel(mci_con, host->base + S3C2410_SDICON);
++
++	if ((ios->power_mode==MMC_POWER_ON)
++		|| (ios->power_mode==MMC_POWER_UP)) {
++
++		dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n",
++			host->real_rate/1000, ios->clock/1000);
++	} else {
++		dbg(host, dbg_conf, "powered down.\n");
++	}
++
++	host->bus_width = ios->bus_width;
++
++}
++
++static void s3cmci_reset(struct s3cmci_host *host)
++{
++	u32 con = readl(host->base + S3C2410_SDICON);
++
++	con |= S3C2440_SDICON_SDRESET;
++
++	writel(con, host->base + S3C2410_SDICON);
++}
++
++static struct mmc_host_ops s3cmci_ops = {
++	.request	= s3cmci_request,
++	.set_ios	= s3cmci_set_ios,
++};
++
++static int s3cmci_probe(struct platform_device *pdev, int is2440)
++{
++	struct mmc_host 	*mmc;
++	struct s3cmci_host 	*host;
++
++	int ret;
++
++	mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
++	if (!mmc) {
++		ret = -ENOMEM;
++		goto probe_out;
++	}
++
++	host = mmc_priv(mmc);
++	host->mmc 	= mmc;
++	host->pdev	= pdev;
++
++	spin_lock_init(&host->complete_lock);
++	tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
++	if (is2440) {
++		host->is2440	= 1;
++		host->sdiimsk	= S3C2440_SDIIMSK;
++		host->sdidata	= S3C2440_SDIDATA;
++		host->clk_div	= 1;
++	} else {
++		host->is2440	= 0;
++		host->sdiimsk	= S3C2410_SDIIMSK;
++		host->sdidata	= S3C2410_SDIDATA;
++		host->clk_div	= 2;
++	}
++	host->dodma		= 0;
++	host->complete_what 	= COMPLETION_NONE;
++	host->pio_active 	= XFER_NONE;
++
++	host->dma		= S3CMCI_DMA;
++	host->irq_cd		= IRQ_EINT2;
++
++	host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	if (!host->mem) {
++		dev_err(&pdev->dev,
++			"failed to get io memory region resouce.\n");
++
++		ret = -ENOENT;
++		goto probe_free_host;
++	}
++
++	host->mem = request_mem_region(host->mem->start,
++		RESSIZE(host->mem), pdev->name);
++
++	if (!host->mem) {
++		dev_err(&pdev->dev, "failed to request io memory region.\n");
++		ret = -ENOENT;
++		goto probe_free_host;
++	}
++
++	host->base = ioremap(host->mem->start, RESSIZE(host->mem));
++	if (host->base == 0) {
++		dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
++		ret = -EINVAL;
++		goto probe_free_mem_region;
++	}
++
++	host->irq = platform_get_irq(pdev, 0);
++	if (host->irq == 0) {
++		dev_err(&pdev->dev, "failed to get interrupt resouce.\n");
++		ret = -EINVAL;
++		goto probe_iounmap;
++	}
++
++	if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
++		dev_err(&pdev->dev, "failed to request mci interrupt.\n");
++		ret = -ENOENT;
++		goto probe_iounmap;
++	}
++
++	disable_irq(host->irq);
++
++	s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
++	set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
++
++	if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
++		dev_err(&pdev->dev,
++			"failed to request card detect interrupt.\n");
++
++		ret = -ENOENT;
++		goto probe_free_irq;
++	}
++
++	if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
++		dev_err(&pdev->dev, "unable to get DMA channel.\n");
++		ret = -EBUSY;
++		goto probe_free_irq_cd;
++	}
++
++	host->clk = clk_get(&pdev->dev, "sdi");
++	if (IS_ERR(host->clk)) {
++		dev_err(&pdev->dev, "failed to find clock source.\n");
++		ret = PTR_ERR(host->clk);
++		host->clk = NULL;
++		goto probe_free_host;
++	}
++
++	if ((ret = clk_enable(host->clk))) {
++		dev_err(&pdev->dev, "failed to enable clock source.\n");
++		goto clk_free;
++	}
++
++	host->clk_rate = clk_get_rate(host->clk);
++
++	mmc->ops 	= &s3cmci_ops;
++	mmc->ocr_avail	= MMC_VDD_32_33;
++	mmc->caps	= MMC_CAP_4_BIT_DATA;
++	mmc->f_min 	= host->clk_rate / (host->clk_div * 256);
++	mmc->f_max 	= host->clk_rate / host->clk_div;
++
++	mmc->max_sectors	= 4095;
++	mmc->max_seg_size	= mmc->max_sectors << 9;
++
++	mmc->max_phys_segs	= 128;
++	mmc->max_hw_segs	= 128;
++
++	dbg(host, dbg_debug, "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
++		(host->is2440?"2440":""),
++		host->base, host->irq, host->irq_cd, host->dma);
++
++	if ((ret = mmc_add_host(mmc))) {
++		dev_err(&pdev->dev, "failed to add mmc host.\n");
++		goto free_dmabuf;
++	}
++
++	platform_set_drvdata(pdev, mmc);
++
++	dev_info(&pdev->dev,"initialisation done.\n");
++	return 0;
++
++ free_dmabuf:
++	clk_disable(host->clk);
++
++ clk_free:
++	clk_put(host->clk);
++
++ probe_free_irq_cd:
++ 	free_irq(host->irq_cd, host);
++
++ probe_free_irq:
++ 	free_irq(host->irq, host);
++
++ probe_iounmap:
++	iounmap(host->base);
++
++ probe_free_mem_region:
++	release_mem_region(host->mem->start, RESSIZE(host->mem));
++
++ probe_free_host:
++	mmc_free_host(mmc);
++ probe_out:
++	return ret;
++}
++
++static int s3cmci_remove(struct platform_device *pdev)
++{
++	struct mmc_host 	*mmc  = platform_get_drvdata(pdev);
++	struct s3cmci_host 	*host = mmc_priv(mmc);
++
++	mmc_remove_host(mmc);
++	clk_disable(host->clk);
++	clk_put(host->clk);
++ 	free_irq(host->irq_cd, host);
++ 	free_irq(host->irq, host);
++	iounmap(host->base);
++	release_mem_region(host->mem->start, RESSIZE(host->mem));
++	mmc_free_host(mmc);
++
++	return 0;
++}
++
++static int s3cmci_probe_2410(struct platform_device *dev)
++{
++	return s3cmci_probe(dev, 0);
++}
++
++static int s3cmci_probe_2412(struct platform_device *dev)
++{
++	return s3cmci_probe(dev, 1);
++}
++
++static int s3cmci_probe_2440(struct platform_device *dev)
++{
++	return s3cmci_probe(dev, 1);
++}
++
++#ifdef CONFIG_PM
++
++static int s3cmci_suspend(struct platform_device *dev, pm_message_t state)
++{
++	struct mmc_host *mmc = platform_get_drvdata(dev);
++
++	return  mmc_suspend_host(mmc, state);
++}
++
++static int s3cmci_resume(struct platform_device *dev)
++{
++	struct mmc_host *mmc = platform_get_drvdata(dev);
++
++	return mmc_resume_host(mmc);
++}
++
++#else /* CONFIG_PM */
++#define s3cmci_suspend NULL
++#define s3cmci_resume NULL
++#endif /* CONFIG_PM */
++
++
++static struct platform_driver s3cmci_driver_2410 =
++{
++	.driver.name	= "s3c2410-sdi",
++	.probe		= s3cmci_probe_2410,
++	.remove		= s3cmci_remove,
++	.suspend	= s3cmci_suspend,
++	.resume		= s3cmci_resume,
++};
++
++static struct platform_driver s3cmci_driver_2412 =
++{
++	.driver.name	= "s3c2412-sdi",
++	.probe		= s3cmci_probe_2412,
++	.remove		= s3cmci_remove,
++	.suspend	= s3cmci_suspend,
++	.resume		= s3cmci_resume,
++};
++
++static struct platform_driver s3cmci_driver_2440 =
++{
++	.driver.name	= "s3c2440-sdi",
++	.probe		= s3cmci_probe_2440,
++	.remove		= s3cmci_remove,
++	.suspend	= s3cmci_suspend,
++	.resume		= s3cmci_resume,
++};
++
++
++static int __init s3cmci_init(void)
++{
++	platform_driver_register(&s3cmci_driver_2410);
++	platform_driver_register(&s3cmci_driver_2412);
++	platform_driver_register(&s3cmci_driver_2440);
++	return 0;
++}
++
++static void __exit s3cmci_exit(void)
++{
++	platform_driver_unregister(&s3cmci_driver_2410);
++	platform_driver_unregister(&s3cmci_driver_2412);
++	platform_driver_unregister(&s3cmci_driver_2440);
++}
++
++module_init(s3cmci_init);
++module_exit(s3cmci_exit);
++
++MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Thomas Kleffel <tk at maintech.de>");
++
+Index: linux-2.6.17.7-fic1/drivers/mmc/s3cmci.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.7-fic1/drivers/mmc/s3cmci.h	2006-11-02 21:05:15.000000000 +0100
+@@ -0,0 +1,71 @@
++/*
++ *  linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
++ *
++ *  Copyright (C) 2004-2006 Thomas Kleffel, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++//FIXME: DMA Resource management ?!
++#define S3CMCI_DMA 0
++
++enum s3cmci_waitfor {
++	COMPLETION_NONE,
++	COMPLETION_FINALIZE,
++	COMPLETION_CMDSENT,
++	COMPLETION_RSPFIN,
++	COMPLETION_XFERFINISH,
++	COMPLETION_XFERFINISH_RSPFIN,
++};
++
++struct s3cmci_host {
++	struct platform_device	*pdev;
++	struct mmc_host		*mmc;
++	struct resource		*mem;
++	struct clk		*clk;
++	void __iomem		*base;
++	int			irq;
++	int			irq_cd;
++	int			dma;
++
++	unsigned long		clk_rate;
++	unsigned long		clk_div;
++	unsigned long		real_rate;
++	u8			prescaler;
++
++	int			is2440;
++	unsigned		sdiimsk;
++	unsigned		sdidata;
++	int			dodma;
++
++	volatile int		dmatogo;
++
++	struct mmc_request	*mrq;
++	int			cmd_is_stop;
++
++	spinlock_t		complete_lock;
++	volatile enum s3cmci_waitfor
++				complete_what;
++
++	volatile int		dma_complete;
++
++	volatile u32		pio_sgptr;
++	volatile u32		pio_words;
++	volatile u32		pio_count;
++	volatile u32		*pio_ptr;
++#define XFER_NONE 0
++#define XFER_READ 1
++#define XFER_WRITE 2
++	volatile u32		pio_active;
++
++	int			bus_width;
++
++	char 			dbgmsg_cmd[301];
++	char 			dbgmsg_dat[301];
++	volatile char		*status;
++
++	unsigned int		ccnt, dcnt;
++	struct tasklet_struct	pio_tasklet;
++};
+Index: linux-2.6.17.7-fic1/include/asm-arm/arch-s3c2410/regs-sdi.h
+===================================================================
+--- linux-2.6.17.7-fic1.orig/include/asm-arm/arch-s3c2410/regs-sdi.h	2006-11-02 12:48:24.000000000 +0100
++++ linux-2.6.17.7-fic1/include/asm-arm/arch-s3c2410/regs-sdi.h	2006-11-02 12:48:50.000000000 +0100
+@@ -7,9 +7,10 @@
+  * it under the terms of the GNU General Public License version 2 as
+  * published by the Free Software Foundation.
+  *
+- * S3C2410 MMC/SDIO register definitions
++ * Samsung S3C MCI register definitions
+  *
+  *  Changelog:
++ *    09-May-2006 Thomas Kleffel Added defines for 2440
+  *    18-Aug-2004 Ben Dooks      Created initial file
+  *    29-Nov-2004 Koen Martens   Added some missing defines, fixed duplicates
+  *    29-Nov-2004 Ben Dooks	 Updated Koen's patch
+@@ -33,9 +34,15 @@
+ #define S3C2410_SDIDCNT               (0x30)
+ #define S3C2410_SDIDSTA               (0x34)
+ #define S3C2410_SDIFSTA               (0x38)
++
+ #define S3C2410_SDIDATA               (0x3C)
+ #define S3C2410_SDIIMSK               (0x40)
+ 
++#define S3C2440_SDIDATA               (0x40)
++#define S3C2440_SDIIMSK               (0x3C)
++
++#define S3C2440_SDICON_SDRESET        (1<<8)
++#define S3C2440_SDICON_MMCCLOCK       (1<<5)
+ #define S3C2410_SDICON_BYTEORDER      (1<<4)
+ #define S3C2410_SDICON_SDIOIRQ        (1<<3)
+ #define S3C2410_SDICON_RWAITEN        (1<<2)
+@@ -47,7 +54,8 @@
+ #define S3C2410_SDICMDCON_LONGRSP     (1<<10)
+ #define S3C2410_SDICMDCON_WAITRSP     (1<<9)
+ #define S3C2410_SDICMDCON_CMDSTART    (1<<8)
+-#define S3C2410_SDICMDCON_INDEX       (0xff)
++#define S3C2410_SDICMDCON_SENDERHOST  (1<<6)
++#define S3C2410_SDICMDCON_INDEX       (0x3f)
+ 
+ #define S3C2410_SDICMDSTAT_CRCFAIL    (1<<12)
+ #define S3C2410_SDICMDSTAT_CMDSENT    (1<<11)
+@@ -56,6 +64,9 @@
+ #define S3C2410_SDICMDSTAT_XFERING    (1<<8)
+ #define S3C2410_SDICMDSTAT_INDEX      (0xff)
+ 
++#define S3C2440_SDIDCON_DS_BYTE       (0<<22)
++#define S3C2440_SDIDCON_DS_HALFWORD   (1<<22)
++#define S3C2440_SDIDCON_DS_WORD       (2<<22)
+ #define S3C2410_SDIDCON_IRQPERIOD     (1<<21)
+ #define S3C2410_SDIDCON_TXAFTERRESP   (1<<20)
+ #define S3C2410_SDIDCON_RXAFTERCMD    (1<<19)
+@@ -64,6 +75,7 @@
+ #define S3C2410_SDIDCON_WIDEBUS       (1<<16)
+ #define S3C2410_SDIDCON_DMAEN         (1<<15)
+ #define S3C2410_SDIDCON_STOP          (1<<14)
++#define S3C2440_SDIDCON_DATSTART      (1<<14)
+ #define S3C2410_SDIDCON_DATMODE	      (3<<12)
+ #define S3C2410_SDIDCON_BLKNUM        (0x7ff)
+ 
+@@ -73,6 +85,7 @@
+ #define S3C2410_SDIDCON_XFER_RXSTART  (2<<12)
+ #define S3C2410_SDIDCON_XFER_TXSTART  (3<<12)
+ 
++#define S3C2410_SDIDCON_BLKNUM_MASK   (0xFFF)
+ #define S3C2410_SDIDCNT_BLKNUM_SHIFT  (12)
+ 
+ #define S3C2410_SDIDSTA_RDYWAITREQ    (1<<10)
+@@ -87,10 +100,12 @@
+ #define S3C2410_SDIDSTA_TXDATAON      (1<<1)
+ #define S3C2410_SDIDSTA_RXDATAON      (1<<0)
+ 
++#define S3C2440_SDIFSTA_FIFORESET      (1<<16)
++#define S3C2440_SDIFSTA_FIFOFAIL       (3<<14)  /* 3 is correct (2 bits) */
+ #define S3C2410_SDIFSTA_TFDET          (1<<13)
+ #define S3C2410_SDIFSTA_RFDET          (1<<12)
+-#define S3C2410_SDIFSTA_TXHALF         (1<<11)
+-#define S3C2410_SDIFSTA_TXEMPTY        (1<<10)
++#define S3C2410_SDIFSTA_TFHALF         (1<<11)
++#define S3C2410_SDIFSTA_TFEMPTY        (1<<10)
+ #define S3C2410_SDIFSTA_RFLAST         (1<<9)
+ #define S3C2410_SDIFSTA_RFFULL         (1<<8)
+ #define S3C2410_SDIFSTA_RFHALF         (1<<7)
+Index: linux-2.6.17.7-fic1/include/linux/mmc/mmc.h
+===================================================================
+--- linux-2.6.17.7-fic1.orig/include/linux/mmc/mmc.h	2006-11-02 12:48:24.000000000 +0100
++++ linux-2.6.17.7-fic1/include/linux/mmc/mmc.h	2006-11-02 12:48:50.000000000 +0100
+@@ -54,12 +54,15 @@
+ 	unsigned int		retries;	/* max number of retries */
+ 	unsigned int		error;		/* command error */
+ 
+-#define MMC_ERR_NONE	0
+-#define MMC_ERR_TIMEOUT	1
+-#define MMC_ERR_BADCRC	2
+-#define MMC_ERR_FIFO	3
+-#define MMC_ERR_FAILED	4
+-#define MMC_ERR_INVALID	5
++#define MMC_ERR_NONE		0
++#define MMC_ERR_TIMEOUT		1
++#define MMC_ERR_BADCRC		2
++#define MMC_ERR_FIFO		3
++#define MMC_ERR_DMA		4
++#define MMC_ERR_BUSY		5
++#define MMC_ERR_FAILED		6
++#define MMC_ERR_INVALID		7
++#define MMC_ERR_CANCELED	8
+ 
+ 	struct mmc_data		*data;		/* data segment associated with cmd */
+ 	struct mmc_request	*mrq;		/* associated request */

Added: trunk/src/target/kernel/patches/s3c_mci_platform.patch
===================================================================
--- trunk/src/target/kernel/patches/s3c_mci_platform.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/s3c_mci_platform.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,132 @@
+This patch adds platform data support to the s3mci driver.  This allows
+flexible board-specific configuration of set_power, card detect and read only
+pins.
+Index: linux-2.6.17.7-fic1/drivers/mmc/s3cmci.c
+===================================================================
+--- linux-2.6.17.7-fic1.orig/drivers/mmc/s3cmci.c	2006-11-02 22:54:00.000000000 +0100
++++ linux-2.6.17.7-fic1/drivers/mmc/s3cmci.c	2006-11-02 22:55:49.000000000 +0100
+@@ -22,6 +22,7 @@
+ #include <asm/io.h>
+ #include <asm/arch/regs-sdi.h>
+ #include <asm/arch/regs-gpio.h>
++#include <asm/arch/mci.h>
+ 
+ #include "mmc_debug.h"
+ #include "s3cmci.h"
+@@ -1006,6 +1007,9 @@
+ 			s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
+ 			s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
+ 
++			if (host->pdata->set_power)
++				host->pdata->set_power(ios->vdd);
++
+ 			if (!host->is2440)
+ 				mci_con|=S3C2410_SDICON_FIFORESET;
+ 
+@@ -1069,9 +1073,26 @@
+ 	writel(con, host->base + S3C2410_SDICON);
+ }
+ 
++static int s3cmci_get_ro(struct mmc_host *mmc)
++{
++	struct s3cmci_host *host = mmc_priv(mmc);
++
++	if (host->pdata->gpio_wprotect == 0)
++		return 0;
++
++	return s3c2410_gpio_getpin(host->pdata->gpio_wprotect);
++}
++
+ static struct mmc_host_ops s3cmci_ops = {
+ 	.request	= s3cmci_request,
+ 	.set_ios	= s3cmci_set_ios,
++	.get_ro		= s3cmci_get_ro,
++};
++
++static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
++	.gpio_detect	= 0,
++	.set_power	= NULL,
++	.ocr_avail	= MMC_VDD_32_33,
+ };
+ 
+ static int s3cmci_probe(struct platform_device *pdev, int is2440)
+@@ -1091,6 +1112,12 @@
+ 	host->mmc 	= mmc;
+ 	host->pdev	= pdev;
+ 
++	host->pdata = pdev->dev.platform_data;
++	if (!host->pdata) {
++		pdev->dev.platform_data = &s3cmci_def_pdata;
++		host->pdata = &s3cmci_def_pdata;
++	}
++
+ 	spin_lock_init(&host->complete_lock);
+ 	tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
+ 	if (is2440) {
+@@ -1109,7 +1136,8 @@
+ 	host->pio_active 	= XFER_NONE;
+ 
+ 	host->dma		= S3CMCI_DMA;
+-	host->irq_cd		= IRQ_EINT2;
++	host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
++	s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ);
+ 
+ 	host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ 	if (!host->mem) {
+@@ -1151,7 +1179,7 @@
+ 
+ 	disable_irq(host->irq);
+ 
+-	s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
++	s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ);
+ 	set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
+ 
+ 	if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
+@@ -1162,6 +1190,10 @@
+ 		goto probe_free_irq;
+ 	}
+ 
++	if (host->pdata->gpio_wprotect)
++		s3c2410_gpio_cfgpin(host->pdata->gpio_wprotect,
++				    S3C2410_GPIO_INPUT);
++
+ 	if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
+ 		dev_err(&pdev->dev, "unable to get DMA channel.\n");
+ 		ret = -EBUSY;
+@@ -1184,7 +1216,7 @@
+ 	host->clk_rate = clk_get_rate(host->clk);
+ 
+ 	mmc->ops 	= &s3cmci_ops;
+-	mmc->ocr_avail	= MMC_VDD_32_33;
++	mmc->ocr_avail	= host->pdata->ocr_avail;
+ 	mmc->caps	= MMC_CAP_4_BIT_DATA;
+ 	mmc->f_min 	= host->clk_rate / (host->clk_div * 256);
+ 	mmc->f_max 	= host->clk_rate / host->clk_div;
+Index: linux-2.6.17.7-fic1/drivers/mmc/s3cmci.h
+===================================================================
+--- linux-2.6.17.7-fic1.orig/drivers/mmc/s3cmci.h	2006-11-02 22:54:00.000000000 +0100
++++ linux-2.6.17.7-fic1/drivers/mmc/s3cmci.h	2006-11-02 22:54:03.000000000 +0100
+@@ -22,6 +22,7 @@
+ 
+ struct s3cmci_host {
+ 	struct platform_device	*pdev;
++	struct s3c24xx_mci_pdata *pdata;
+ 	struct mmc_host		*mmc;
+ 	struct resource		*mem;
+ 	struct clk		*clk;
+Index: linux-2.6.17.7-fic1/include/asm-arm/arch-s3c2410/mci.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.7-fic1/include/asm-arm/arch-s3c2410/mci.h	2006-11-02 22:54:03.000000000 +0100
+@@ -0,0 +1,11 @@
++#ifndef _ARCH_MCI_H
++#define _ARCH_MCI_H
++
++struct s3c24xx_mci_pdata {
++	unsigned int	gpio_detect;
++	unsigned int	gpio_wprotect;
++	unsigned long	ocr_avail;
++	void		(*set_power)(unsigned short vdd);
++};
++
++#endif /* _ARCH_NCI_H */

Added: trunk/src/target/kernel/patches/s3cmci-dma-free.patch
===================================================================
--- trunk/src/target/kernel/patches/s3cmci-dma-free.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/s3cmci-dma-free.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,12 @@
+Index: linux-2.6.17.14-fic4.test/drivers/mmc/s3cmci.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/mmc/s3cmci.c	2007-01-21 19:32:36.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/mmc/s3cmci.c	2007-01-21 19:35:20.000000000 +0100
+@@ -1279,6 +1279,7 @@
+ 	mmc_remove_host(mmc);
+ 	clk_disable(host->clk);
+ 	clk_put(host->clk);
++	s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client);
+  	free_irq(host->irq_cd, host);
+  	free_irq(host->irq, host);
+ 	iounmap(host->base);

Added: trunk/src/target/kernel/patches/s3cmci_dbg.patch
===================================================================
--- trunk/src/target/kernel/patches/s3cmci_dbg.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/s3cmci_dbg.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,28 @@
+This patch is a workaround of some S3C2410 MMC chip bug
+
+Index: linux-2.6.17.7-fic1/drivers/mmc/s3cmci.c
+===================================================================
+--- linux-2.6.17.7-fic1.orig/drivers/mmc/s3cmci.c	2006-11-02 21:56:27.000000000 +0100
++++ linux-2.6.17.7-fic1/drivers/mmc/s3cmci.c	2006-11-02 22:44:28.000000000 +0100
+@@ -446,11 +446,17 @@
+ 
+ 	if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
+ 		if (cmd->flags & MMC_RSP_CRC) {
+-			cmd->error = MMC_ERR_BADCRC;
+-			host->status = "error: bad command crc";
+-			goto fail_transfer;
++			if (host->mrq->cmd->flags & MMC_RSP_136) {
++				dbg(host, dbg_irq, 
++				    "fixup: ignore CRC fail with long rsp\n");
++			} else {
++#if 0
++				cmd->error = MMC_ERR_BADCRC;
++				host->status = "error: bad command crc";
++				goto fail_transfer;
++#endif
++			}
+ 		}
+-
+ 		mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
+ 	}
+ 

Added: trunk/src/target/kernel/patches/series
===================================================================
--- trunk/src/target/kernel/patches/series	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/series	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,27 @@
+qt2410-base.patch
+qt2410-cs8900.patch
+udc.patch
+qt2410-biglcd.patch
+s3c_mci.patch
+qt2410-touchscreen.patch
+s3c2410_serial-nodebug.patch
+2.6.17-arm-usb_gadget-ether-link.patch
+2.6.17-s3c2410-spi-mode23.patch
+gta01-core.patch
+gta01-jbt6k74.patch
+gta01-backlight.patch
+gta01-pcf50606.patch
+ts0710.patch
+gta01_vibrator.patch
+s3c_mci_platform.patch
+gta01-s3c_mci-pdata.patch
+s3cmci_dbg.patch
+s3cmci-dma-free.patch
+qt2410-s3c_mci-pdata.patch
+udc-nomodule-misccr.patch
+gta01-inputdevice.patch
+gta01-power_control.patch
+s3c2410-bbt.patch
+s3c2410_udc-vbus_draw_pdata.patch
+g_ether-highpower.patch
+gta01-vbus_draw.patch

Added: trunk/src/target/kernel/patches/series.old
===================================================================
--- trunk/src/target/kernel/patches/series.old	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/series.old	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,17 @@
+qt2410-base.patch
+qt2410-cs8900.patch
+mmc.patch
+udc.patch
+mmc-get_ro.patch
+mmc-sdipre_limit.patch
+mmc-fixdmalockup.patch
+mmc-fixdebugp.patch
+qt2410-biglcd.patch
+qt2410-touchscreen.patch
+s3c2410_serial-nodebug.patch
+2.6.17-arm-usb_gadget-ether-link.patch
+2.6.17-s3c2410-spi-mode23.patch
+gta01-core.patch
+gta01-jbt6k74.patch
+gta01-backlight.patch
+gta01-pcf50606.patch

Added: trunk/src/target/kernel/patches/ts-debug.patch
===================================================================
--- trunk/src/target/kernel/patches/ts-debug.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/ts-debug.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,14 @@
+Index: linux-2.6.17.14-fic4.test/drivers/input/touchscreen/s3c2410_ts.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/input/touchscreen/s3c2410_ts.c	2007-01-15 01:07:12.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/input/touchscreen/s3c2410_ts.c	2007-01-15 01:07:44.000000000 +0100
+@@ -190,7 +190,8 @@
+ 		writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
+ 		writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
+ 	} else {
+-		mod_timer(&touch_timer, jiffies+1);
++		//mod_timer(&touch_timer, jiffies+1);
++		mod_timer(&touch_timer, jiffies+(HZ/2));
+ 		writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
+ 	}
+ 

Added: trunk/src/target/kernel/patches/ts0710.patch
===================================================================
--- trunk/src/target/kernel/patches/ts0710.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/ts0710.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,5717 @@
+Index: linux-2.6.17.14-fic2/drivers/char/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic2.orig/drivers/char/Kconfig	2006-12-02 11:20:42.000000000 +0100
++++ linux-2.6.17.14-fic2/drivers/char/Kconfig	2006-12-02 11:23:49.000000000 +0100
+@@ -1034,5 +1034,17 @@
+ 	  sysfs directory, /sys/devices/platform/telco_clock, with a number of
+ 	  files for controlling the behavior of this hardware.
+ 
++config TS0710_MUX
++	tristate "GSM TS 07.10 Multiplex driver"
++	help
++	  This implements the GSM 07.10 multiplex protocol.
++
++config TS0710_MUX_USB_MOTO
++	tristate "Motorola USB support for TS 07.10 Multiplex driver"
++	depends on TS0710_MUX && USB
++	help
++	  This addrs support for the TS07.10 over USB, as found in Motorola
++	  Smartphones.
++
+ endmenu
+ 
+Index: linux-2.6.17.14-fic2/drivers/char/Makefile
+===================================================================
+--- linux-2.6.17.14-fic2.orig/drivers/char/Makefile	2006-12-02 11:20:46.000000000 +0100
++++ linux-2.6.17.14-fic2/drivers/char/Makefile	2006-12-02 11:24:55.000000000 +0100
+@@ -97,6 +97,9 @@
+ obj-$(CONFIG_HANGCHECK_TIMER)	+= hangcheck-timer.o
+ obj-$(CONFIG_TCG_TPM)		+= tpm/
+ 
++obj-$(CONFIG_TS0710_MUX)	+= ts0710_mux.o
++obj-$(CONFIG_TS0710_MUX_USB)	+= ts0710_mux_usb.o
++
+ # Files generated that shall be removed upon make clean
+ clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c
+ 
+Index: linux-2.6.17.14-fic2/drivers/char/ts0710.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic2/drivers/char/ts0710.h	2006-12-02 11:21:56.000000000 +0100
+@@ -0,0 +1,368 @@
++/*
++ * File: ts0710.h
++ *
++ * Portions derived from rfcomm.c, original header as follows:
++ *
++ * Copyright (C) 2000, 2001  Axis Communications AB
++ *
++ * Author: Mats Friden <mats.friden at axis.com>
++ *
++ * 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.
++ *
++ * Exceptionally, Axis Communications AB grants discretionary and
++ * conditional permissions for additional use of the text contained
++ * in the company's release of the AXIS OpenBT Stack under the
++ * provisions set forth hereunder.
++ *
++ * Provided that, if you use the AXIS OpenBT Stack with other files,
++ * that do not implement functionality as specified in the Bluetooth
++ * System specification, to produce an executable, this does not by
++ * itself cause the resulting executable to be covered by the GNU
++ * General Public License. Your use of that executable is in no way
++ * restricted on account of using the AXIS OpenBT Stack code with it.
++ *
++ * This exception does not however invalidate any other reasons why
++ * the executable file might be covered by the provisions of the GNU
++ * General Public License.
++ *
++ */
++/*
++ * Copyright (C) 2002  Motorola
++ *
++ *  07/28/2002  Initial version based on rfcomm.c
++ *  11/18/2002  Modified
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/fcntl.h>
++#include <linux/string.h>
++#include <linux/major.h>
++#include <linux/mm.h>
++#include <linux/init.h>
++#include <linux/devfs_fs_kernel.h>
++
++#include <asm/uaccess.h>
++#include <asm/system.h>
++#include <asm/bitops.h>
++
++#include <asm/byteorder.h>
++#include <asm/types.h>
++
++#define TS0710_MAX_CHN 14
++
++#define SET_PF(ctr) ((ctr) | (1 << 4))
++#define CLR_PF(ctr) ((ctr) & 0xef)
++#define GET_PF(ctr) (((ctr) >> 4) & 0x1)
++
++#define GET_PN_MSG_FRAME_SIZE(pn) ( ((pn)->frame_sizeh << 8) | ((pn)->frame_sizel))
++#define SET_PN_MSG_FRAME_SIZE(pn, size) ({ (pn)->frame_sizel = (size) & 0xff; \
++                                           (pn)->frame_sizeh = (size) >> 8; })
++
++#define GET_LONG_LENGTH(a) ( ((a).h_len << 7) | ((a).l_len) )
++#define SET_LONG_LENGTH(a, length) ({ (a).ea = 0; \
++                                      (a).l_len = length & 0x7F; \
++                                      (a).h_len = (length >> 7) & 0xFF; })
++
++#define SHORT_CRC_CHECK 3
++#define LONG_CRC_CHECK 4
++
++/* FIXME: Should thsi one be define here? */
++#define SHORT_PAYLOAD_SIZE 127
++
++#define EA 1
++#define FCS_SIZE 1
++#define FLAG_SIZE 2
++
++#define TS0710_MAX_HDR_SIZE 5
++#define DEF_TS0710_MTU 256
++
++#define TS0710_BASIC_FLAG 0xF9
++/* the control field */
++#define SABM 0x2f
++#define SABM_SIZE 4
++#define UA 0x63
++#define UA_SIZE 4
++#define DM 0x0f
++#define DISC 0x43
++#define UIH 0xef
++
++/* the type field in a multiplexer command packet */
++#define TEST 0x8
++#define FCON 0x28
++#define FCOFF 0x18
++#define MSC 0x38
++#define RPN 0x24
++#define RLS 0x14
++#define PN 0x20
++#define NSC 0x4
++
++/* V.24 modem control signals */
++#define FC 0x2
++#define RTC 0x4
++#define RTR 0x8
++#define IC 0x40
++#define DV 0x80
++
++#define CTRL_CHAN 0		/* The control channel is defined as DLCI 0 */
++#define MCC_CMD 1		/* Multiplexer command cr */
++#define MCC_RSP 0		/* Multiplexer response cr */
++
++#ifdef __LITTLE_ENDIAN_BITFIELD
++
++typedef struct {
++	__u8 ea:1;
++	__u8 cr:1;
++	__u8 d:1;
++	__u8 server_chn:5;
++} __attribute__ ((packed)) address_field;
++
++typedef struct {
++	__u8 ea:1;
++	__u8 len:7;
++} __attribute__ ((packed)) short_length;
++
++typedef struct {
++	__u8 ea:1;
++	__u8 l_len:7;
++	__u8 h_len;
++} __attribute__ ((packed)) long_length;
++
++typedef struct {
++	address_field addr;
++	__u8 control;
++	short_length length;
++} __attribute__ ((packed)) short_frame_head;
++
++typedef struct {
++	short_frame_head h;
++	__u8 data[0];
++} __attribute__ ((packed)) short_frame;
++
++typedef struct {
++	address_field addr;
++	__u8 control;
++	long_length length;
++	__u8 data[0];
++} __attribute__ ((packed)) long_frame_head;
++
++typedef struct {
++	long_frame_head h;
++	__u8 data[0];
++} __attribute__ ((packed)) long_frame;
++
++/* Typedefinitions for structures used for the multiplexer commands */
++typedef struct {
++	__u8 ea:1;
++	__u8 cr:1;
++	__u8 type:6;
++} __attribute__ ((packed)) mcc_type;
++
++typedef struct {
++	mcc_type type;
++	short_length length;
++	__u8 value[0];
++} __attribute__ ((packed)) mcc_short_frame_head;
++
++typedef struct {
++	mcc_short_frame_head h;
++	__u8 value[0];
++} __attribute__ ((packed)) mcc_short_frame;
++
++typedef struct {
++	mcc_type type;
++	long_length length;
++	__u8 value[0];
++} __attribute__ ((packed)) mcc_long_frame_head;
++
++typedef struct {
++	mcc_long_frame_head h;
++	__u8 value[0];
++} __attribute__ ((packed)) mcc_long_frame;
++
++/* MSC-command */
++typedef struct {
++	__u8 ea:1;
++	__u8 fc:1;
++	__u8 rtc:1;
++	__u8 rtr:1;
++	__u8 reserved:2;
++	__u8 ic:1;
++	__u8 dv:1;
++} __attribute__ ((packed)) v24_sigs;
++
++typedef struct {
++	__u8 ea:1;
++	__u8 b1:1;
++	__u8 b2:1;
++	__u8 b3:1;
++	__u8 len:4;
++} __attribute__ ((packed)) brk_sigs;
++
++typedef struct {
++	short_frame_head s_head;
++	mcc_short_frame_head mcc_s_head;
++	address_field dlci;
++	__u8 v24_sigs;
++	//brk_sigs break_signals;
++	__u8 fcs;
++} __attribute__ ((packed)) msc_msg;
++
++#if 0
++/* conflict with termios.h */
++/* RPN command */
++#define B2400 0
++#define B4800 1
++#define B7200 2
++#define B9600 3
++#define B19200 4
++#define B38400 5
++#define B57600 6
++#define B115200 7
++#define D230400 8
++#endif
++
++/*
++typedef struct{
++  __u8 bit_rate:1;
++  __u8 data_bits:1;
++  __u8 stop_bit:1;
++  __u8 parity:1;
++  __u8 parity_type:1;
++  __u8 xon_u8:1;
++  __u8 xoff_u8:1;
++  __u8 res1:1;
++  __u8 xon_input:1;
++  __u8 xon_output:1;
++  __u8 rtr_input:1;
++  __u8 rtr_output:1;
++  __u8 rtc_input:1;
++  __u8 rtc_output:1;
++  __u8 res2:2;
++} __attribute__((packed)) parameter_mask;
++
++typedef struct{
++  __u8 bit_rate;
++  __u8 data_bits:2;
++  __u8 stop_bit:1;
++  __u8 parity:1;
++  __u8 parity_type:2;
++  __u8 res1:2;
++  __u8 xon_input:1;
++  __u8 xon_output:1;
++  __u8 rtr_input:1;
++  __u8 rtr_output:1;
++  __u8 rtc_input:1;
++  __u8 rtc_output:1;
++  __u8 res2:2;
++  __u8 xon_u8;
++  __u8 xoff_u8;
++  parameter_mask pm;
++} __attribute__((packed)) rpn_values;
++
++typedef struct{
++  short_frame_head s_head;
++  mcc_short_frame_head mcc_s_head;
++  address_field dlci;
++  rpn_values rpn_val;
++  __u8 fcs;
++} __attribute__((packed)) rpn_msg;
++*/
++
++/* RLS-command */
++/*
++typedef struct{
++  short_frame_head s_head;
++  mcc_short_frame_head mcc_s_head;
++  address_field dlci;
++  __u8 error:4;
++  __u8 res:4;
++  __u8 fcs;
++} __attribute__((packed)) rls_msg;
++*/
++
++/* PN-command */
++typedef struct {
++	short_frame_head s_head;
++	mcc_short_frame_head mcc_s_head;
++	__u8 dlci:6;
++	__u8 res1:2;
++	__u8 frame_type:4;
++	__u8 credit_flow:4;
++	__u8 prior:6;
++	__u8 res2:2;
++	__u8 ack_timer;
++	__u8 frame_sizel;
++	__u8 frame_sizeh;
++	__u8 max_nbrof_retrans;
++	__u8 credits;
++	__u8 fcs;
++} __attribute__ ((packed)) pn_msg;
++
++/* NSC-command */
++typedef struct {
++	short_frame_head s_head;
++	mcc_short_frame_head mcc_s_head;
++	mcc_type command_type;
++	__u8 fcs;
++} __attribute__ ((packed)) nsc_msg;
++
++#else
++#error Only littel-endianess supported now!
++#endif
++
++enum {
++	REJECTED = 0,
++	DISCONNECTED,
++	CONNECTING,
++	NEGOTIATING,
++	CONNECTED,
++	DISCONNECTING,
++	FLOW_STOPPED
++};
++
++enum ts0710_events {
++	CONNECT_IND,
++	CONNECT_CFM,
++	DISCONN_CFM
++};
++
++typedef struct {
++	volatile __u8 state;
++	volatile __u8 flow_control;
++	volatile __u8 initiated;
++	volatile __u8 initiator;
++	volatile __u16 mtu;
++	wait_queue_head_t open_wait;
++	wait_queue_head_t close_wait;
++} dlci_struct;
++
++/* user space interfaces */
++typedef struct {
++	volatile __u8 initiator;
++	volatile __u8 c_dlci;
++	volatile __u16 mtu;
++	volatile __u8 be_testing;
++	volatile __u32 test_errs;
++	wait_queue_head_t test_wait;
++
++	dlci_struct dlci[TS0710_MAX_CHN];
++} ts0710_con;
+Index: linux-2.6.17.14-fic2/drivers/char/ts0710_mux.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic2/drivers/char/ts0710_mux.c	2006-12-03 09:20:01.000000000 +0100
+@@ -0,0 +1,3978 @@
++/*
++ * File: mux_driver.c
++ *
++ * Portions derived from rfcomm.c, original header as follows:
++ *
++ * Copyright (C) 2000, 2001  Axis Communications AB
++ *
++ * Author: Mats Friden <mats.friden at axis.com>
++ *
++ * 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.
++ *
++ * Exceptionally, Axis Communications AB grants discretionary and
++ * conditional permissions for additional use of the text contained
++ * in the company's release of the AXIS OpenBT Stack under the
++ * provisions set forth hereunder.
++ *
++ * Provided that, if you use the AXIS OpenBT Stack with other files,
++ * that do not implement functionality as specified in the Bluetooth
++ * System specification, to produce an executable, this does not by
++ * itself cause the resulting executable to be covered by the GNU
++ * General Public License. Your use of that executable is in no way
++ * restricted on account of using the AXIS OpenBT Stack code with it.
++ *
++ * This exception does not however invalidate any other reasons why
++ * the executable file might be covered by the provisions of the GNU
++ * General Public License.
++ *
++ */
++
++/*
++ * Copyright (C) 2002-2004  Motorola
++ * Copyright (C) 2006 Harald Welte <laforge at openezx.org>
++ *
++ *  07/28/2002  Initial version
++ *  11/18/2002  Second version
++ *  04/21/2004  Add GPRS PROC
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++
++#include <linux/kernel.h>
++
++#include <linux/errno.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/fcntl.h>
++#include <linux/string.h>
++#include <linux/major.h>
++#include <linux/init.h>
++
++
++#if 0
++#include <linux/proc_fs.h>
++
++#define USB_FOR_MUX
++
++#ifndef USB_FOR_MUX
++#include <linux/serial.h>
++#endif
++
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/devfs_fs_kernel.h>
++#include <asm/uaccess.h>
++#include <asm/system.h>
++#include <asm/bitops.h>
++
++#endif
++
++#ifdef USB_FOR_MUX
++//#include <linux/usb.h>
++#include "ts0710_mux_usb.h"
++#endif
++
++#include "ts0710.h"
++#include "ts0710_mux.h"
++
++#define TS0710MUX_GPRS_SESSION_MAX 2
++#define TS0710MUX_MAJOR 250
++#define TS0710MUX_MINOR_START 0
++#define NR_MUXS 16
++
++				  /*#define TS0710MUX_TIME_OUT 30 *//* 300ms  */
++#define TS0710MUX_TIME_OUT 250	/* 2500ms, for BP UART hardware flow control AP UART  */
++
++#define TS0710MUX_IO_DLCI_FC_ON 0x54F2
++#define TS0710MUX_IO_DLCI_FC_OFF 0x54F3
++#define TS0710MUX_IO_FC_ON 0x54F4
++#define TS0710MUX_IO_FC_OFF 0x54F5
++
++#define TS0710MUX_MAX_BUF_SIZE 2048
++
++#define TS0710MUX_SEND_BUF_OFFSET 10
++#define TS0710MUX_SEND_BUF_SIZE (DEF_TS0710_MTU + TS0710MUX_SEND_BUF_OFFSET + 34)
++#define TS0710MUX_RECV_BUF_SIZE TS0710MUX_SEND_BUF_SIZE
++
++/*For BP UART problem Begin*/
++#ifdef TS0710SEQ2
++#define ACK_SPACE 66		/* 6 * 11(ACK frame size)  */
++#else
++#define ACK_SPACE 42		/* 6 * 7(ACK frame size)  */
++#endif
++/*For BP UART problem End*/
++
++									     /*#define TS0710MUX_SERIAL_BUF_SIZE (DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE)*//* For BP UART problem  */
++#define TS0710MUX_SERIAL_BUF_SIZE (DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE + ACK_SPACE)	/* For BP UART problem: ACK_SPACE  */
++
++#define TS0710MUX_MAX_TOTAL_FRAME_SIZE (DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE + FLAG_SIZE)
++#define TS0710MUX_MAX_CHARS_IN_BUF 65535
++#define TS0710MUX_THROTTLE_THRESHOLD DEF_TS0710_MTU
++
++#define TEST_PATTERN_SIZE 250
++
++#define CMDTAG 0x55
++#define DATATAG 0xAA
++
++#define ACK 0x4F		/*For BP UART problem */
++
++/*For BP UART problem Begin*/
++#ifdef TS0710SEQ2
++#define FIRST_BP_SEQ_OFFSET 1	/*offset from start flag */
++#define SECOND_BP_SEQ_OFFSET 2	/*offset from start flag */
++#define FIRST_AP_SEQ_OFFSET 3	/*offset from start flag */
++#define SECOND_AP_SEQ_OFFSET 4	/*offset from start flag */
++#define SLIDE_BP_SEQ_OFFSET 5	/*offset from start flag */
++#define SEQ_FIELD_SIZE 5
++#else
++#define SLIDE_BP_SEQ_OFFSET 1	/*offset from start flag */
++#define SEQ_FIELD_SIZE 1
++#endif
++
++#define ADDRESS_FIELD_OFFSET (1 + SEQ_FIELD_SIZE)	/*offset from start flag */
++/*For BP UART problem End*/
++
++#ifndef UNUSED_PARAM
++#define UNUSED_PARAM(v) (void)(v)
++#endif
++
++#define TS0710MUX_GPRS1_DLCI 7
++#define TS0710MUX_GPRS2_DLCI 8
++
++#define TS0710MUX_GPRS1_RECV_COUNT_IDX 0
++#define TS0710MUX_GPRS1_SEND_COUNT_IDX 1
++#define TS0710MUX_GPRS2_RECV_COUNT_IDX 2
++#define TS0710MUX_GPRS2_SEND_COUNT_IDX 3
++#define TS0710MUX_COUNT_MAX_IDX        3
++#define TS0710MUX_COUNT_IDX_NUM (TS0710MUX_COUNT_MAX_IDX + 1)
++
++#if 0
++static volatile int mux_data_count[TS0710MUX_COUNT_IDX_NUM] = { 0, 0, 0, 0 };
++static volatile int mux_data_count2[TS0710MUX_COUNT_IDX_NUM] = { 0, 0, 0, 0 };
++static struct semaphore mux_data_count_mutex[TS0710MUX_COUNT_IDX_NUM];
++static volatile __u8 post_recv_count_flag = 0;
++
++/*PROC file*/
++struct proc_dir_entry *gprs_proc_file = NULL;
++ssize_t file_proc_read(struct file *file, char *buf, size_t size,
++		       loff_t * ppos);
++ssize_t file_proc_write(struct file *file, const char *buf, size_t count,
++			loff_t * ppos);
++struct file_operations file_proc_operations = {
++      read:file_proc_read,
++      write:file_proc_write,
++};
++typedef struct {
++	int recvBytes;
++	int sentBytes;
++} gprs_bytes;
++
++static __u8 tty2dlci[NR_MUXS] =
++    { 1, 2, 3, 4, 5, 6, 7, 8, 6, 7, 8, 9, 10, 11, 12, 13 };
++static __u8 iscmdtty[NR_MUXS] =
++    { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
++typedef struct {
++	__u8 cmdtty;
++	__u8 datatty;
++} dlci_tty;
++static dlci_tty dlci2tty[] = { {0, 0},	/* DLCI 0 */
++{0, 0},				/* DLCI 1 */
++{1, 1},				/* DLCI 2 */
++{2, 2},				/* DLCI 3 */
++{3, 3},				/* DLCI 4 */
++{4, 4},				/* DLCI 5 */
++{5, 8},				/* DLCI 6 */
++{6, 9},				/* DLCI 7 */
++{7, 10},			/* DLCI 8 */
++{11, 11},			/* DLCI 9 */
++{12, 12},			/* DLCI 10 */
++{13, 13},			/* DLCI 11 */
++{14, 14},			/* DLCI 12 */
++{15, 15}
++};				/* DLCI 13 */
++
++typedef struct {
++	volatile __u8 buf[TS0710MUX_SEND_BUF_SIZE];
++	volatile __u8 *frame;
++	unsigned long flags;
++	volatile __u16 length;
++	volatile __u8 filled;
++	volatile __u8 dummy;	/* Allignment to 4*n bytes */
++} mux_send_struct;
++
++/* Bit number in flags of mux_send_struct */
++#define BUF_BUSY 0
++
++struct mux_recv_packet_tag {
++	__u8 *data;
++	__u32 length;
++	struct mux_recv_packet_tag *next;
++};
++typedef struct mux_recv_packet_tag mux_recv_packet;
++
++struct mux_recv_struct_tag {
++	__u8 data[TS0710MUX_RECV_BUF_SIZE];
++	__u32 length;
++	__u32 total;
++	mux_recv_packet *mux_packet;
++	struct mux_recv_struct_tag *next;
++	int no_tty;
++	volatile __u8 post_unthrottle;
++};
++typedef struct mux_recv_struct_tag mux_recv_struct;
++
++#define RECV_RUNNING 0
++static unsigned long mux_recv_flags = 0;
++
++static mux_send_struct *mux_send_info[NR_MUXS];
++static volatile __u8 mux_send_info_flags[NR_MUXS];
++static volatile __u8 mux_send_info_idx = NR_MUXS;
++
++static mux_recv_struct *mux_recv_info[NR_MUXS];
++static volatile __u8 mux_recv_info_flags[NR_MUXS];
++static mux_recv_struct *mux_recv_queue = NULL;
++
++static struct tty_driver mux_driver;
++
++#ifdef USB_FOR_MUX
++#define COMM_FOR_MUX_DRIVER usb_for_mux_driver
++#define COMM_FOR_MUX_TTY usb_for_mux_tty
++#define COMM_MUX_DISPATCHER usb_mux_dispatcher
++#define COMM_MUX_SENDER usb_mux_sender
++#else
++#define COMM_FOR_MUX_DRIVER serial_for_mux_driver
++#define COMM_FOR_MUX_TTY serial_for_mux_tty
++#define COMM_MUX_DISPATCHER serial_mux_dispatcher
++#define COMM_MUX_SENDER serial_mux_sender
++
++extern struct list_head *tq_serial_for_mux;
++#endif
++
++extern struct tty_driver *COMM_FOR_MUX_DRIVER;
++extern struct tty_struct *COMM_FOR_MUX_TTY;
++extern void (*COMM_MUX_DISPATCHER) (struct tty_struct * tty);
++extern void (*COMM_MUX_SENDER) (void);
++
++static struct work_struct send_tqueue;
++static struct work_struct receive_tqueue;
++static struct work_struct post_recv_tqueue;
++
++static struct tty_struct *mux_table[NR_MUXS];
++static struct termios *mux_termios[NR_MUXS];
++static struct termios *mux_termios_locked[NR_MUXS];
++static volatile short int mux_tty[NR_MUXS];
++
++#ifdef min
++#undef min
++#define min(a,b)    ( (a)<(b) ? (a):(b) )
++#endif
++
++static int get_count(__u8 idx);
++static int set_count(__u8 idx, int count);
++static int add_count(__u8 idx, int count);
++
++static int send_ua(ts0710_con * ts0710, __u8 dlci);
++static int send_dm(ts0710_con * ts0710, __u8 dlci);
++static int send_sabm(ts0710_con * ts0710, __u8 dlci);
++static int send_disc(ts0710_con * ts0710, __u8 dlci);
++static void queue_uih(mux_send_struct * send_info, __u16 len,
++		      ts0710_con * ts0710, __u8 dlci);
++static int send_pn_msg(ts0710_con * ts0710, __u8 prior, __u32 frame_size,
++		       __u8 credit_flow, __u8 credits, __u8 dlci, __u8 cr);
++static int send_nsc_msg(ts0710_con * ts0710, mcc_type cmd, __u8 cr);
++static void set_uih_hdr(short_frame * uih_pkt, __u8 dlci, __u32 len, __u8 cr);
++
++static __u32 crc_check(__u8 * data, __u32 length, __u8 check_sum);
++static __u8 crc_calc(__u8 * data, __u32 length);
++static void create_crctable(__u8 table[]);
++
++static void mux_sched_send(void);
++
++static __u8 crctable[256];
++
++static ts0710_con ts0710_connection;
++/*
++static rpn_values rpn_val;
++*/
++
++static int valid_dlci(__u8 dlci)
++{
++	if ((dlci < TS0710_MAX_CHN) && (dlci > 0))
++		return 1;
++	else
++		return 0;
++}
++
++#ifdef TS0710DEBUG
++
++#ifdef PRINT_OUTPUT_PRINTK
++#define TS0710_DEBUG(fmt, arg...) printk(KERN_INFO "MUX " __FUNCTION__ ": " fmt "\n" , ## arg)
++#else
++#include "ezxlog.h"
++static __u8 strDebug[256];
++#define TS0710_DEBUG(fmt, arg...) ({ snprintf(strDebug, sizeof(strDebug), "MUX " __FUNCTION__ ": " fmt "\n" , ## arg); \
++                                     /*printk("%s", strDebug)*/ezxlogk("MX", strDebug, strlen(strDebug)); })
++#endif				/* End #ifdef PRINT_OUTPUT_PRINTK */
++
++#else
++#define TS0710_DEBUG(fmt...)
++#endif				/* End #ifdef TS0710DEBUG */
++
++#ifdef TS0710LOG
++static unsigned char g_tbuf[TS0710MUX_MAX_BUF_SIZE];
++#ifdef PRINT_OUTPUT_PRINTK
++#define TS0710_LOG(fmt, arg...) printk(fmt, ## arg)
++#define TS0710_PRINTK(fmt, arg...) printk(fmt, ## arg)
++#else
++#include "ezxlog.h"
++static __u8 strLog[256];
++#define TS0710_LOG(fmt, arg...) ({ snprintf(strLog, sizeof(strLog), fmt, ## arg); \
++                                     /*printk("%s", strLog)*/ezxlogk("MX", strLog, strlen(strLog)); })
++#define TS0710_PRINTK(fmt, arg...) ({ printk(fmt, ## arg); \
++                                      TS0710_LOG(fmt, ## arg); })
++#endif				/* End #ifdef PRINT_OUTPUT_PRINTK */
++
++#else
++#define TS0710_LOG(fmt...)
++#define TS0710_PRINTK(fmt, arg...) printk(fmt, ## arg)
++#endif				/* End #ifdef TS0710LOG */
++
++#ifdef TS0710DEBUG
++static void TS0710_DEBUGHEX(__u8 * buf, int len)
++{
++	static unsigned char tbuf[TS0710MUX_MAX_BUF_SIZE];
++
++	int i;
++	int c;
++
++	if (len <= 0) {
++		return;
++	}
++
++	c = 0;
++	for (i = 0; (i < len) && (c < (TS0710MUX_MAX_BUF_SIZE - 3)); i++) {
++		sprintf(&tbuf[c], "%02x ", buf[i]);
++		c += 3;
++	}
++	tbuf[c] = 0;
++
++#ifdef PRINT_OUTPUT_PRINTK
++	TS0710_DEBUG("%s", tbuf);
++#else
++	/*printk("%s\n", tbuf) */ ezxlogk("MX", tbuf, c);
++#endif
++}
++static void TS0710_DEBUGSTR(__u8 * buf, int len)
++{
++	static unsigned char tbuf[TS0710MUX_MAX_BUF_SIZE];
++
++	if (len <= 0) {
++		return;
++	}
++
++	if (len > (TS0710MUX_MAX_BUF_SIZE - 1)) {
++		len = (TS0710MUX_MAX_BUF_SIZE - 1);
++	}
++
++	memcpy(tbuf, buf, len);
++	tbuf[len] = 0;
++
++#ifdef PRINT_OUTPUT_PRINTK
++	/* 0x00 byte in the string pointed by tbuf may truncate the print result */
++	TS0710_DEBUG("%s", tbuf);
++#else
++	/*printk("%s\n", tbuf) */ ezxlogk("MX", tbuf, len);
++#endif
++}
++#else
++#define TS0710_DEBUGHEX(buf, len)
++#define TS0710_DEBUGSTR(buf, len)
++#endif				/* End #ifdef TS0710DEBUG */
++
++#ifdef TS0710LOG
++static void TS0710_LOGSTR_FRAME(__u8 send, __u8 * data, int len)
++{
++	short_frame *short_pkt;
++	long_frame *long_pkt;
++	__u8 *uih_data_start;
++	__u32 uih_len;
++	__u8 dlci;
++	int pos;
++
++	if (len <= 0) {
++		return;
++	}
++
++	pos = 0;
++	if (send) {
++		pos += sprintf(&g_tbuf[pos], "<");
++		short_pkt = (short_frame *) (data + 1);	/*For BP UART problem */
++	} else {
++		/*For BP UART problem */
++		/*pos += sprintf(&g_tbuf[pos], ">"); */
++		pos += sprintf(&g_tbuf[pos], ">%d ", *(data + SLIDE_BP_SEQ_OFFSET));	/*For BP UART problem */
++
++#ifdef TS0710SEQ2
++		pos += sprintf(&g_tbuf[pos], "%02x %02x %02x %02x ", *(data + FIRST_BP_SEQ_OFFSET), *(data + SECOND_BP_SEQ_OFFSET), *(data + FIRST_AP_SEQ_OFFSET), *(data + SECOND_AP_SEQ_OFFSET));	/*For BP UART problem */
++#endif
++
++		short_pkt = (short_frame *) (data + ADDRESS_FIELD_OFFSET);	/*For BP UART problem */
++	}
++
++	/*For BP UART problem */
++	/*short_pkt = (short_frame *)(data + 1); */
++
++	dlci = short_pkt->h.addr.server_chn << 1 | short_pkt->h.addr.d;
++	switch (CLR_PF(short_pkt->h.control)) {
++	case SABM:
++		pos += sprintf(&g_tbuf[pos], "C SABM %d ::", dlci);
++		break;
++	case UA:
++		pos += sprintf(&g_tbuf[pos], "C UA %d ::", dlci);
++		break;
++	case DM:
++		pos += sprintf(&g_tbuf[pos], "C DM %d ::", dlci);
++		break;
++	case DISC:
++		pos += sprintf(&g_tbuf[pos], "C DISC %d ::", dlci);
++		break;
++
++		/*For BP UART problem Begin */
++	case ACK:
++		pos += sprintf(&g_tbuf[pos], "C ACK %d ", short_pkt->data[0]);
++
++#ifdef TS0710SEQ2
++		pos += sprintf(&g_tbuf[pos], "%02x %02x %02x %02x ", short_pkt->data[1], short_pkt->data[2], short_pkt->data[3], short_pkt->data[4]);	/*For BP UART problem */
++#endif
++
++		pos += sprintf(&g_tbuf[pos], "::");
++		break;
++		/*For BP UART problem End */
++
++	case UIH:
++		if (!dlci) {
++			pos += sprintf(&g_tbuf[pos], "C MCC %d ::", dlci);
++		} else {
++
++			if ((short_pkt->h.length.ea) == 0) {
++				long_pkt = (long_frame *) short_pkt;
++				uih_len = GET_LONG_LENGTH(long_pkt->h.length);
++				uih_data_start = long_pkt->h.data;
++			} else {
++				uih_len = short_pkt->h.length.len;
++				uih_data_start = short_pkt->data;
++			}
++			switch (*uih_data_start) {
++			case CMDTAG:
++				pos +=
++				    sprintf(&g_tbuf[pos], "I %d A %d ::", dlci,
++					    uih_len);
++				break;
++			case DATATAG:
++			default:
++				pos +=
++				    sprintf(&g_tbuf[pos], "I %d D %d ::", dlci,
++					    uih_len);
++				break;
++			}
++
++		}
++		break;
++	default:
++		pos += sprintf(&g_tbuf[pos], "N!!! %d ::", dlci);
++		break;
++	}
++
++	if (len > (sizeof(g_tbuf) - pos - 1)) {
++		len = (sizeof(g_tbuf) - pos - 1);
++	}
++
++	memcpy(&g_tbuf[pos], data, len);
++	pos += len;
++	g_tbuf[pos] = 0;
++
++#ifdef PRINT_OUTPUT_PRINTK
++	/* 0x00 byte in the string pointed by g_tbuf may truncate the print result */
++	TS0710_LOG("%s\n", g_tbuf);
++#else
++	/*printk("%s\n", g_tbuf) */ ezxlogk("MX", g_tbuf, pos);
++#endif
++}
++#else
++#define TS0710_LOGSTR_FRAME(send, data, len)
++#endif
++
++#ifdef TS0710SIG
++#define my_for_each_task(p) \
++        for ((p) = current; ((p) = (p)->next_task) != current; )
++
++static void TS0710_SIG2APLOGD(void)
++{
++	struct task_struct *p;
++	static __u8 sig = 0;
++
++	if (sig) {
++		return;
++	}
++
++	read_lock(&tasklist_lock);
++	my_for_each_task(p) {
++		if (strncmp(p->comm, "aplogd", 6) == 0) {
++			sig = 1;
++			if (send_sig(SIGUSR2, p, 1) == 0) {
++				TS0710_PRINTK
++				    ("MUX: success to send SIGUSR2 to aplogd!\n");
++			} else {
++				TS0710_PRINTK
++				    ("MUX: failure to send SIGUSR2 to aplogd!\n");
++			}
++			break;
++		}
++	}
++	read_unlock(&tasklist_lock);
++
++	if (!sig) {
++		TS0710_PRINTK("MUX: not found aplogd!\n");
++	}
++}
++#else
++#define TS0710_SIG2APLOGD()
++#endif
++
++static int basic_write(ts0710_con * ts0710, __u8 * buf, int len)
++{
++	int res;
++
++	UNUSED_PARAM(ts0710);
++
++	buf[0] = TS0710_BASIC_FLAG;
++	buf[len + 1] = TS0710_BASIC_FLAG;
++
++	if ((COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)) {
++		TS0710_PRINTK
++		    ("MUX basic_write: (COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)\n");
++
++#ifndef USB_FOR_MUX
++		TS0710_PRINTK
++		    ("MUX basic_write: tapisrv might be down!!! (serial_for_mux_driver == 0) || (serial_for_mux_tty == 0)\n");
++		TS0710_SIG2APLOGD();
++#endif
++
++		return -1;
++	}
++
++	TS0710_LOGSTR_FRAME(1, buf, len + 2);
++	TS0710_DEBUGHEX(buf, len + 2);
++
++	res = COMM_FOR_MUX_DRIVER->write(COMM_FOR_MUX_TTY, buf, len + 2);
++
++	if (res != len + 2) {
++		TS0710_PRINTK("MUX basic_write: Write Error!\n");
++		return -1;
++	}
++
++	return len + 2;
++}
++
++/* Functions for the crc-check and calculation */
++
++#define CRC_VALID 0xcf
++
++static __u32 crc_check(__u8 * data, __u32 length, __u8 check_sum)
++{
++	__u8 fcs = 0xff;
++
++	while (length--) {
++		fcs = crctable[fcs ^ *data++];
++	}
++	fcs = crctable[fcs ^ check_sum];
++	TS0710_DEBUG("fcs : %d\n", fcs);
++	if (fcs == (uint) 0xcf) {	/*CRC_VALID) */
++		TS0710_DEBUG("crc_check: CRC check OK\n");
++		return 0;
++	} else {
++		TS0710_PRINTK("MUX crc_check: CRC check failed\n");
++		return 1;
++	}
++}
++
++/* Calculates the checksum according to the ts0710 specification */
++
++static __u8 crc_calc(__u8 * data, __u32 length)
++{
++	__u8 fcs = 0xff;
++
++	while (length--) {
++		fcs = crctable[fcs ^ *data++];
++	}
++
++	return 0xff - fcs;
++}
++
++/* Calulates a reversed CRC table for the FCS check */
++
++static void create_crctable(__u8 table[])
++{
++	int i, j;
++
++	__u8 data;
++	__u8 code_word = (__u8) 0xe0;
++	__u8 sr = (__u8) 0;
++
++	for (j = 0; j < 256; j++) {
++		data = (__u8) j;
++
++		for (i = 0; i < 8; i++) {
++			if ((data & 0x1) ^ (sr & 0x1)) {
++				sr >>= 1;
++				sr ^= code_word;
++			} else {
++				sr >>= 1;
++			}
++
++			data >>= 1;
++			sr &= 0xff;
++		}
++
++		table[j] = sr;
++		sr = 0;
++	}
++}
++
++static void ts0710_reset_dlci(__u8 j)
++{
++	if (j >= TS0710_MAX_CHN)
++		return;
++
++	ts0710_connection.dlci[j].state = DISCONNECTED;
++	ts0710_connection.dlci[j].flow_control = 0;
++	ts0710_connection.dlci[j].mtu = DEF_TS0710_MTU;
++	ts0710_connection.dlci[j].initiated = 0;
++	ts0710_connection.dlci[j].initiator = 0;
++	init_waitqueue_head(&ts0710_connection.dlci[j].open_wait);
++	init_waitqueue_head(&ts0710_connection.dlci[j].close_wait);
++}
++
++static void ts0710_reset_con(void)
++{
++	__u8 j;
++
++	ts0710_connection.initiator = 0;
++	ts0710_connection.mtu = DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE;
++	ts0710_connection.be_testing = 0;
++	ts0710_connection.test_errs = 0;
++	init_waitqueue_head(&ts0710_connection.test_wait);
++
++	for (j = 0; j < TS0710_MAX_CHN; j++) {
++		ts0710_reset_dlci(j);
++	}
++}
++
++static void ts0710_init(void)
++{
++	create_crctable(crctable);
++
++	ts0710_reset_con();
++
++	/* Set the values in the rpn octets */
++/*
++  rpn_val.bit_rate  = 7;
++  rpn_val.data_bits = 3;
++  rpn_val.stop_bit  = 0;
++  rpn_val.parity    = 0;
++  rpn_val.parity_type = 0;
++  rpn_val.res1    = 0;
++  rpn_val.xon_input = 0;
++  rpn_val.xon_output  = 0;
++  rpn_val.rtr_input = 0;
++  rpn_val.rtr_output  = 0;
++  rpn_val.rtc_input = 0;
++  rpn_val.rtc_output  = 0;
++  rpn_val.res2    = 0;
++  rpn_val.xon_u8  = 0x11;
++  rpn_val.xoff_u8 = 0x13;
++	  memset(&rpn_val.pm, 0 , 2); *//* Set the mask to zero */
++}
++
++static void ts0710_upon_disconnect(void)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	__u8 j;
++
++	for (j = 0; j < TS0710_MAX_CHN; j++) {
++		ts0710->dlci[j].state = DISCONNECTED;
++		wake_up_interruptible(&ts0710->dlci[j].open_wait);
++		wake_up_interruptible(&ts0710->dlci[j].close_wait);
++	}
++	ts0710->be_testing = 0;
++	wake_up_interruptible(&ts0710->test_wait);
++	ts0710_reset_con();
++}
++
++/* Sending packet functions */
++
++/* Creates a UA packet and puts it at the beginning of the pkt pointer */
++
++static int send_ua(ts0710_con * ts0710, __u8 dlci)
++{
++	__u8 buf[sizeof(short_frame) + FCS_SIZE + FLAG_SIZE];
++	short_frame *ua;
++
++	TS0710_DEBUG("send_ua: Creating UA packet to DLCI %d\n", dlci);
++
++	ua = (short_frame *) (buf + 1);
++	ua->h.addr.ea = 1;
++	ua->h.addr.cr = ((~(ts0710->initiator)) & 0x1);
++	ua->h.addr.d = (dlci) & 0x1;
++	ua->h.addr.server_chn = (dlci) >> 0x1;
++	ua->h.control = SET_PF(UA);
++	ua->h.length.ea = 1;
++	ua->h.length.len = 0;
++	ua->data[0] = crc_calc((__u8 *) ua, SHORT_CRC_CHECK);
++
++	return basic_write(ts0710, buf, sizeof(short_frame) + FCS_SIZE);
++}
++
++/* Creates a DM packet and puts it at the beginning of the pkt pointer */
++
++static int send_dm(ts0710_con * ts0710, __u8 dlci)
++{
++	__u8 buf[sizeof(short_frame) + FCS_SIZE + FLAG_SIZE];
++	short_frame *dm;
++
++	TS0710_DEBUG("send_dm: Creating DM packet to DLCI %d\n", dlci);
++
++	dm = (short_frame *) (buf + 1);
++	dm->h.addr.ea = 1;
++	dm->h.addr.cr = ((~(ts0710->initiator)) & 0x1);
++	dm->h.addr.d = dlci & 0x1;
++	dm->h.addr.server_chn = dlci >> 0x1;
++	dm->h.control = SET_PF(DM);
++	dm->h.length.ea = 1;
++	dm->h.length.len = 0;
++	dm->data[0] = crc_calc((__u8 *) dm, SHORT_CRC_CHECK);
++
++	return basic_write(ts0710, buf, sizeof(short_frame) + FCS_SIZE);
++}
++
++static int send_sabm(ts0710_con * ts0710, __u8 dlci)
++{
++	__u8 buf[sizeof(short_frame) + FCS_SIZE + FLAG_SIZE];
++	short_frame *sabm;
++
++	TS0710_DEBUG("send_sabm: Creating SABM packet to DLCI %d\n", dlci);
++
++	sabm = (short_frame *) (buf + 1);
++	sabm->h.addr.ea = 1;
++	sabm->h.addr.cr = ((ts0710->initiator) & 0x1);
++	sabm->h.addr.d = dlci & 0x1;
++	sabm->h.addr.server_chn = dlci >> 0x1;
++	sabm->h.control = SET_PF(SABM);
++	sabm->h.length.ea = 1;
++	sabm->h.length.len = 0;
++	sabm->data[0] = crc_calc((__u8 *) sabm, SHORT_CRC_CHECK);
++
++	return basic_write(ts0710, buf, sizeof(short_frame) + FCS_SIZE);
++}
++
++static int send_disc(ts0710_con * ts0710, __u8 dlci)
++{
++	__u8 buf[sizeof(short_frame) + FCS_SIZE + FLAG_SIZE];
++	short_frame *disc;
++
++	TS0710_DEBUG("send_disc: Creating DISC packet to DLCI %d\n", dlci);
++
++	disc = (short_frame *) (buf + 1);
++	disc->h.addr.ea = 1;
++	disc->h.addr.cr = ((ts0710->initiator) & 0x1);
++	disc->h.addr.d = dlci & 0x1;
++	disc->h.addr.server_chn = dlci >> 0x1;
++	disc->h.control = SET_PF(DISC);
++	disc->h.length.ea = 1;
++	disc->h.length.len = 0;
++	disc->data[0] = crc_calc((__u8 *) disc, SHORT_CRC_CHECK);
++
++	return basic_write(ts0710, buf, sizeof(short_frame) + FCS_SIZE);
++}
++
++static void queue_uih(mux_send_struct * send_info, __u16 len,
++		      ts0710_con * ts0710, __u8 dlci)
++{
++	__u32 size;
++
++	TS0710_DEBUG
++	    ("queue_uih: Creating UIH packet with %d bytes data to DLCI %d\n",
++	     len, dlci);
++
++	if (len > SHORT_PAYLOAD_SIZE) {
++		long_frame *l_pkt;
++
++		size = sizeof(long_frame) + len + FCS_SIZE;
++		l_pkt = (long_frame *) (send_info->frame - sizeof(long_frame));
++		set_uih_hdr((void *)l_pkt, dlci, len, ts0710->initiator);
++		l_pkt->data[len] = crc_calc((__u8 *) l_pkt, LONG_CRC_CHECK);
++		send_info->frame = ((__u8 *) l_pkt) - 1;
++	} else {
++		short_frame *s_pkt;
++
++		size = sizeof(short_frame) + len + FCS_SIZE;
++		s_pkt =
++		    (short_frame *) (send_info->frame - sizeof(short_frame));
++		set_uih_hdr((void *)s_pkt, dlci, len, ts0710->initiator);
++		s_pkt->data[len] = crc_calc((__u8 *) s_pkt, SHORT_CRC_CHECK);
++		send_info->frame = ((__u8 *) s_pkt) - 1;
++	}
++	send_info->length = size;
++}
++
++/* Multiplexer command packets functions */
++
++/* Turns on the ts0710 flow control */
++
++static int ts0710_fcon_msg(ts0710_con * ts0710, __u8 cr)
++{
++	__u8 buf[30];
++	mcc_short_frame *mcc_pkt;
++	short_frame *uih_pkt;
++	__u32 size;
++
++	size = sizeof(short_frame) + sizeof(mcc_short_frame) + FCS_SIZE;
++	uih_pkt = (short_frame *) (buf + 1);
++	set_uih_hdr(uih_pkt, CTRL_CHAN, sizeof(mcc_short_frame),
++		    ts0710->initiator);
++	uih_pkt->data[sizeof(mcc_short_frame)] =
++	    crc_calc((__u8 *) uih_pkt, SHORT_CRC_CHECK);
++	mcc_pkt = (mcc_short_frame *) (uih_pkt->data);
++
++	mcc_pkt->h.type.ea = EA;
++	mcc_pkt->h.type.cr = cr;
++	mcc_pkt->h.type.type = FCON;
++	mcc_pkt->h.length.ea = EA;
++	mcc_pkt->h.length.len = 0;
++
++	return basic_write(ts0710, buf, size);
++}
++
++/* Turns off the ts0710 flow control */
++
++static int ts0710_fcoff_msg(ts0710_con * ts0710, __u8 cr)
++{
++	__u8 buf[30];
++	mcc_short_frame *mcc_pkt;
++	short_frame *uih_pkt;
++	__u32 size;
++
++	size = (sizeof(short_frame) + sizeof(mcc_short_frame) + FCS_SIZE);
++	uih_pkt = (short_frame *) (buf + 1);
++	set_uih_hdr(uih_pkt, CTRL_CHAN, sizeof(mcc_short_frame),
++		    ts0710->initiator);
++	uih_pkt->data[sizeof(mcc_short_frame)] =
++	    crc_calc((__u8 *) uih_pkt, SHORT_CRC_CHECK);
++	mcc_pkt = (mcc_short_frame *) (uih_pkt->data);
++
++	mcc_pkt->h.type.ea = 1;
++	mcc_pkt->h.type.cr = cr;
++	mcc_pkt->h.type.type = FCOFF;
++	mcc_pkt->h.length.ea = 1;
++	mcc_pkt->h.length.len = 0;
++
++	return basic_write(ts0710, buf, size);
++}
++
++/*
++static int ts0710_rpn_msg(ts0710_con *ts0710, __u8 cr, __u8 dlci, __u8 req)
++{
++  char buf[100];
++  rpn_msg* rpn_pkt;
++  __u32 fsize;
++  __u32 psize;
++
++  fsize = sizeof(rpn_msg);
++
++  if (req) {
++    fsize -= sizeof(rpn_values);
++  }
++
++  psize = (fsize - sizeof(short_frame) - FCS_SIZE);
++
++  rpn_pkt = (rpn_msg *) buf;
++
++  set_uih_hdr((short_frame *) rpn_pkt, CTRL_CHAN, psize, ts0710->initiator);
++
++  rpn_pkt->fcs = crc_calc((__u8*) rpn_pkt, SHORT_CRC_CHECK);
++
++  rpn_pkt->mcc_s_head.type.ea = EA;
++  rpn_pkt->mcc_s_head.type.cr = cr;
++  rpn_pkt->mcc_s_head.type.type = RPN;
++  rpn_pkt->mcc_s_head.length.ea = EA;
++
++  rpn_pkt->dlci.ea = EA;
++  rpn_pkt->dlci.cr = 1;
++  rpn_pkt->dlci.d = dlci & 1;
++  rpn_pkt->dlci.server_chn = (dlci >> 1);
++
++  if (req) {
++    rpn_pkt->mcc_s_head.length.len = 1;
++    rpn_pkt->rpn_val.bit_rate = rpn_pkt->fcs;
++  } else {
++    rpn_pkt->mcc_s_head.length.len = 8;
++    memcpy(&(rpn_pkt->rpn_val), &rpn_val, sizeof(rpn_values));
++  }
++  return basic_write(ts0710, buf, fsize);
++}
++*/
++/*
++static int ts0710_rls_msg(ts0710_con *ts0710, __u8 cr, __u8 dlci, __u8 err_code)
++{
++  char buf[100];
++  rls_msg *rls_pkt;
++  __u32 fsize;
++  __u32 psize;
++
++  fsize = sizeof(rls_msg);
++  psize = fsize - sizeof(short_frame) - FCS_SIZE;
++  rls_pkt = (rls_msg *) buf;
++
++  set_uih_hdr((short_frame *) rls_pkt, CTRL_CHAN, psize, ts0710->initiator);
++  rls_pkt->fcs = crc_calc((__u8*) rls_pkt, SHORT_CRC_CHECK);
++
++  rls_pkt->mcc_s_head.type.ea = EA;
++  rls_pkt->mcc_s_head.type.cr = cr;
++  rls_pkt->mcc_s_head.type.type = RLS;
++  rls_pkt->mcc_s_head.length.ea = EA;
++  rls_pkt->mcc_s_head.length.len = 2;
++
++  rls_pkt->dlci.ea = EA;
++  rls_pkt->dlci.cr = 1;
++  rls_pkt->dlci.d = dlci & 1;
++  rls_pkt->dlci.server_chn = dlci >> 1;
++  rls_pkt->error = err_code;
++  rls_pkt->res = 0;
++
++  return basic_write(ts0710, buf, fsize);
++}
++*/
++
++/* Sends an PN-messages and sets the not negotiable parameters to their
++   default values in ts0710 */
++
++static int send_pn_msg(ts0710_con * ts0710, __u8 prior, __u32 frame_size,
++		       __u8 credit_flow, __u8 credits, __u8 dlci, __u8 cr)
++{
++	__u8 buf[30];
++	pn_msg *pn_pkt;
++	__u32 size;
++	TS0710_DEBUG
++	    ("send_pn_msg: DLCI 0x%02x, prior:0x%02x, frame_size:%d, credit_flow:%x, credits:%d, cr:%x\n",
++	     dlci, prior, frame_size, credit_flow, credits, cr);
++
++	size = sizeof(pn_msg);
++	pn_pkt = (pn_msg *) (buf + 1);
++
++	set_uih_hdr((void *)pn_pkt, CTRL_CHAN,
++		    size - (sizeof(short_frame) + FCS_SIZE), ts0710->initiator);
++	pn_pkt->fcs = crc_calc((__u8 *) pn_pkt, SHORT_CRC_CHECK);
++
++	pn_pkt->mcc_s_head.type.ea = 1;
++	pn_pkt->mcc_s_head.type.cr = cr;
++	pn_pkt->mcc_s_head.type.type = PN;
++	pn_pkt->mcc_s_head.length.ea = 1;
++	pn_pkt->mcc_s_head.length.len = 8;
++
++	pn_pkt->res1 = 0;
++	pn_pkt->res2 = 0;
++	pn_pkt->dlci = dlci;
++	pn_pkt->frame_type = 0;
++	pn_pkt->credit_flow = credit_flow;
++	pn_pkt->prior = prior;
++	pn_pkt->ack_timer = 0;
++	SET_PN_MSG_FRAME_SIZE(pn_pkt, frame_size);
++	pn_pkt->credits = credits;
++	pn_pkt->max_nbrof_retrans = 0;
++
++	return basic_write(ts0710, buf, size);
++}
++
++/* Send a Not supported command - command, which needs 3 bytes */
++
++static int send_nsc_msg(ts0710_con * ts0710, mcc_type cmd, __u8 cr)
++{
++	__u8 buf[30];
++	nsc_msg *nsc_pkt;
++	__u32 size;
++
++	size = sizeof(nsc_msg);
++	nsc_pkt = (nsc_msg *) (buf + 1);
++
++	set_uih_hdr((void *)nsc_pkt, CTRL_CHAN,
++		    sizeof(nsc_msg) - sizeof(short_frame) - FCS_SIZE,
++		    ts0710->initiator);
++
++	nsc_pkt->fcs = crc_calc((__u8 *) nsc_pkt, SHORT_CRC_CHECK);
++
++	nsc_pkt->mcc_s_head.type.ea = 1;
++	nsc_pkt->mcc_s_head.type.cr = cr;
++	nsc_pkt->mcc_s_head.type.type = NSC;
++	nsc_pkt->mcc_s_head.length.ea = 1;
++	nsc_pkt->mcc_s_head.length.len = 1;
++
++	nsc_pkt->command_type.ea = 1;
++	nsc_pkt->command_type.cr = cmd.cr;
++	nsc_pkt->command_type.type = cmd.type;
++
++	return basic_write(ts0710, buf, size);
++}
++
++static int ts0710_msc_msg(ts0710_con * ts0710, __u8 value, __u8 cr, __u8 dlci)
++{
++	__u8 buf[30];
++	msc_msg *msc_pkt;
++	__u32 size;
++
++	size = sizeof(msc_msg);
++	msc_pkt = (msc_msg *) (buf + 1);
++
++	set_uih_hdr((void *)msc_pkt, CTRL_CHAN,
++		    sizeof(msc_msg) - sizeof(short_frame) - FCS_SIZE,
++		    ts0710->initiator);
++
++	msc_pkt->fcs = crc_calc((__u8 *) msc_pkt, SHORT_CRC_CHECK);
++
++	msc_pkt->mcc_s_head.type.ea = 1;
++	msc_pkt->mcc_s_head.type.cr = cr;
++	msc_pkt->mcc_s_head.type.type = MSC;
++	msc_pkt->mcc_s_head.length.ea = 1;
++	msc_pkt->mcc_s_head.length.len = 2;
++
++	msc_pkt->dlci.ea = 1;
++	msc_pkt->dlci.cr = 1;
++	msc_pkt->dlci.d = dlci & 1;
++	msc_pkt->dlci.server_chn = (dlci >> 1) & 0x1f;
++
++	msc_pkt->v24_sigs = value;
++
++	return basic_write(ts0710, buf, size);
++}
++
++static int ts0710_test_msg(ts0710_con * ts0710, __u8 * test_pattern, __u32 len,
++			   __u8 cr, __u8 * f_buf /*Frame buf */ )
++{
++	__u32 size;
++
++	if (len > SHORT_PAYLOAD_SIZE) {
++		long_frame *uih_pkt;
++		mcc_long_frame *mcc_pkt;
++
++		size =
++		    (sizeof(long_frame) + sizeof(mcc_long_frame) + len +
++		     FCS_SIZE);
++		uih_pkt = (long_frame *) (f_buf + 1);
++
++		set_uih_hdr((short_frame *) uih_pkt, CTRL_CHAN, len +
++			    sizeof(mcc_long_frame), ts0710->initiator);
++		uih_pkt->data[GET_LONG_LENGTH(uih_pkt->h.length)] =
++		    crc_calc((__u8 *) uih_pkt, LONG_CRC_CHECK);
++		mcc_pkt = (mcc_long_frame *) uih_pkt->data;
++
++		mcc_pkt->h.type.ea = EA;
++		/* cr tells whether it is a commmand (1) or a response (0) */
++		mcc_pkt->h.type.cr = cr;
++		mcc_pkt->h.type.type = TEST;
++		SET_LONG_LENGTH(mcc_pkt->h.length, len);
++		memcpy(mcc_pkt->value, test_pattern, len);
++	} else if (len > (SHORT_PAYLOAD_SIZE - sizeof(mcc_short_frame))) {
++		long_frame *uih_pkt;
++		mcc_short_frame *mcc_pkt;
++
++		/* Create long uih packet and short mcc packet */
++		size =
++		    (sizeof(long_frame) + sizeof(mcc_short_frame) + len +
++		     FCS_SIZE);
++		uih_pkt = (long_frame *) (f_buf + 1);
++
++		set_uih_hdr((short_frame *) uih_pkt, CTRL_CHAN,
++			    len + sizeof(mcc_short_frame), ts0710->initiator);
++		uih_pkt->data[GET_LONG_LENGTH(uih_pkt->h.length)] =
++		    crc_calc((__u8 *) uih_pkt, LONG_CRC_CHECK);
++		mcc_pkt = (mcc_short_frame *) uih_pkt->data;
++
++		mcc_pkt->h.type.ea = EA;
++		mcc_pkt->h.type.cr = cr;
++		mcc_pkt->h.type.type = TEST;
++		mcc_pkt->h.length.ea = EA;
++		mcc_pkt->h.length.len = len;
++		memcpy(mcc_pkt->value, test_pattern, len);
++	} else {
++		short_frame *uih_pkt;
++		mcc_short_frame *mcc_pkt;
++
++		size =
++		    (sizeof(short_frame) + sizeof(mcc_short_frame) + len +
++		     FCS_SIZE);
++		uih_pkt = (short_frame *) (f_buf + 1);
++
++		set_uih_hdr((void *)uih_pkt, CTRL_CHAN, len
++			    + sizeof(mcc_short_frame), ts0710->initiator);
++		uih_pkt->data[uih_pkt->h.length.len] =
++		    crc_calc((__u8 *) uih_pkt, SHORT_CRC_CHECK);
++		mcc_pkt = (mcc_short_frame *) uih_pkt->data;
++
++		mcc_pkt->h.type.ea = EA;
++		mcc_pkt->h.type.cr = cr;
++		mcc_pkt->h.type.type = TEST;
++		mcc_pkt->h.length.ea = EA;
++		mcc_pkt->h.length.len = len;
++		memcpy(mcc_pkt->value, test_pattern, len);
++
++	}
++	return basic_write(ts0710, f_buf, size);
++}
++
++static void set_uih_hdr(short_frame * uih_pkt, __u8 dlci, __u32 len, __u8 cr)
++{
++	uih_pkt->h.addr.ea = 1;
++	uih_pkt->h.addr.cr = cr;
++	uih_pkt->h.addr.d = dlci & 0x1;
++	uih_pkt->h.addr.server_chn = dlci >> 1;
++	uih_pkt->h.control = CLR_PF(UIH);
++
++	if (len > SHORT_PAYLOAD_SIZE) {
++		SET_LONG_LENGTH(((long_frame *) uih_pkt)->h.length, len);
++	} else {
++		uih_pkt->h.length.ea = 1;
++		uih_pkt->h.length.len = len;
++	}
++}
++
++/* Parses a multiplexer control channel packet */
++
++void process_mcc(__u8 * data, __u32 len, ts0710_con * ts0710, int longpkt)
++{
++	__u8 *tbuf = NULL;
++	mcc_short_frame *mcc_short_pkt;
++	int j;
++
++	if (longpkt) {
++		mcc_short_pkt =
++		    (mcc_short_frame *) (((long_frame *) data)->data);
++	} else {
++		mcc_short_pkt =
++		    (mcc_short_frame *) (((short_frame *) data)->data);
++	}
++
++	switch (mcc_short_pkt->h.type.type) {
++	case TEST:
++		if (mcc_short_pkt->h.type.cr == MCC_RSP) {
++			TS0710_DEBUG("Received test command response\n");
++
++			if (ts0710->be_testing) {
++				if ((mcc_short_pkt->h.length.ea) == 0) {
++					mcc_long_frame *mcc_long_pkt;
++					mcc_long_pkt =
++					    (mcc_long_frame *) mcc_short_pkt;
++					if (GET_LONG_LENGTH
++					    (mcc_long_pkt->h.length) !=
++					    TEST_PATTERN_SIZE) {
++						ts0710->test_errs =
++						    TEST_PATTERN_SIZE;
++						TS0710_DEBUG
++						    ("Err: received test pattern is %d bytes long, not expected %d\n",
++						     GET_LONG_LENGTH
++						     (mcc_long_pkt->h.length),
++						     TEST_PATTERN_SIZE);
++					} else {
++						ts0710->test_errs = 0;
++						for (j = 0;
++						     j < TEST_PATTERN_SIZE;
++						     j++) {
++							if (mcc_long_pkt->
++							    value[j] !=
++							    (j & 0xFF)) {
++								(ts0710->
++								 test_errs)++;
++							}
++						}
++					}
++
++				} else {
++
++#if TEST_PATTERN_SIZE < 128
++					if (mcc_short_pkt->h.length.len !=
++					    TEST_PATTERN_SIZE) {
++#endif
++
++						ts0710->test_errs =
++						    TEST_PATTERN_SIZE;
++						TS0710_DEBUG
++						    ("Err: received test pattern is %d bytes long, not expected %d\n",
++						     mcc_short_pkt->h.length.
++						     len, TEST_PATTERN_SIZE);
++
++#if TEST_PATTERN_SIZE < 128
++					} else {
++						ts0710->test_errs = 0;
++						for (j = 0;
++						     j < TEST_PATTERN_SIZE;
++						     j++) {
++							if (mcc_short_pkt->
++							    value[j] !=
++							    (j & 0xFF)) {
++								(ts0710->
++								 test_errs)++;
++							}
++						}
++					}
++#endif
++
++				}
++
++				ts0710->be_testing = 0;	/* Clear the flag */
++				wake_up_interruptible(&ts0710->test_wait);
++			} else {
++				TS0710_DEBUG
++				    ("Err: shouldn't or late to get test cmd response\n");
++			}
++		} else {
++			tbuf = (__u8 *) kmalloc(len + 32, GFP_ATOMIC);
++			if (!tbuf) {
++				break;
++			}
++
++			if ((mcc_short_pkt->h.length.ea) == 0) {
++				mcc_long_frame *mcc_long_pkt;
++				mcc_long_pkt = (mcc_long_frame *) mcc_short_pkt;
++				ts0710_test_msg(ts0710, mcc_long_pkt->value,
++						GET_LONG_LENGTH(mcc_long_pkt->h.
++								length),
++						MCC_RSP, tbuf);
++			} else {
++				ts0710_test_msg(ts0710, mcc_short_pkt->value,
++						mcc_short_pkt->h.length.len,
++						MCC_RSP, tbuf);
++			}
++
++			kfree(tbuf);
++		}
++		break;
++
++	case FCON:		/*Flow control on command */
++		TS0710_PRINTK
++		    ("MUX Received Flow control(all channels) on command\n");
++		if (mcc_short_pkt->h.type.cr == MCC_CMD) {
++			ts0710->dlci[0].state = CONNECTED;
++			ts0710_fcon_msg(ts0710, MCC_RSP);
++			mux_sched_send();
++		}
++		break;
++
++	case FCOFF:		/*Flow control off command */
++		TS0710_PRINTK
++		    ("MUX Received Flow control(all channels) off command\n");
++		if (mcc_short_pkt->h.type.cr == MCC_CMD) {
++			for (j = 0; j < TS0710_MAX_CHN; j++) {
++				ts0710->dlci[j].state = FLOW_STOPPED;
++			}
++			ts0710_fcoff_msg(ts0710, MCC_RSP);
++		}
++		break;
++
++	case MSC:		/*Modem status command */
++		{
++			__u8 dlci;
++			__u8 v24_sigs;
++
++			dlci = (mcc_short_pkt->value[0]) >> 2;
++			v24_sigs = mcc_short_pkt->value[1];
++
++			if ((ts0710->dlci[dlci].state != CONNECTED)
++			    && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
++				send_dm(ts0710, dlci);
++				break;
++			}
++			if (mcc_short_pkt->h.type.cr == MCC_CMD) {
++				TS0710_DEBUG("Received Modem status command\n");
++				if (v24_sigs & 2) {
++					if (ts0710->dlci[dlci].state ==
++					    CONNECTED) {
++						TS0710_LOG
++						    ("MUX Received Flow off on dlci %d\n",
++						     dlci);
++						ts0710->dlci[dlci].state =
++						    FLOW_STOPPED;
++					}
++				} else {
++					if (ts0710->dlci[dlci].state ==
++					    FLOW_STOPPED) {
++						ts0710->dlci[dlci].state =
++						    CONNECTED;
++						TS0710_LOG
++						    ("MUX Received Flow on on dlci %d\n",
++						     dlci);
++						mux_sched_send();
++					}
++				}
++
++				ts0710_msc_msg(ts0710, v24_sigs, MCC_RSP, dlci);
++/*
++          if (!(ts0710->dlci[dlci].initiated) && !(ts0710->dlci[dlci].initiator)) {
++            ts0710_msc_msg(ts0710, EA | RTR | RTC | DV, MCC_CMD, dlci);
++            ts0710->dlci[dlci].initiated = 1;
++          }
++*/
++			} else {
++				TS0710_DEBUG
++				    ("Received Modem status response\n");
++
++				if (v24_sigs & 2) {
++					TS0710_DEBUG("Flow stop accepted\n");
++				}
++			}
++			break;
++		}
++
++		/*    case RPN:  *//*Remote port negotiation command */
++
++/*      {
++        __u8 dlci;
++
++        dlci = (mcc_short_pkt->value[0]) >> 2;
++
++        if (mcc_short_pkt->h.type.cr == MCC_CMD) {
++          if (mcc_short_pkt->h.length.len == 1) {
++            TS0710_DEBUG("Received Remote port negotiation command\n");
++            ts0710_rpn_msg(ts0710, MCC_RSP, dlci, 0);
++          } else {
++*/
++		/* Accept the other sides settings (accept all for now) */
++/*            TS0710_DEBUG("Received Remote port negotiation respons\n");
++            memcpy(&rpn_val, &mcc_short_pkt->value[1], 8);
++            ts0710_rpn_msg(ts0710, MCC_RSP, dlci, 0);
++*/
++		/* Zero the parametermask after response */
++/*            memset(&rpn_val.pm, 0, 2);
++          }
++        }
++        break;
++      }
++*/
++/*
++		    case RLS: *//*Remote line status */
++/*      {
++        __u8 dlci;
++        __u8 err_code;
++
++        TS0710_DEBUG("Received Remote line status\n");
++        if (mcc_short_pkt->h.type.cr == MCC_CMD) {
++          dlci = mcc_short_pkt->value[0] >> 2;
++          err_code = mcc_short_pkt->value[1];
++
++          ts0710_rls_msg(ts0710, MCC_RSP, dlci, err_code);
++        }
++        break;
++      }
++*/
++	case PN:		/*DLC parameter negotiation */
++		{
++			__u8 dlci;
++			__u16 frame_size;
++			pn_msg *pn_pkt;
++
++			pn_pkt = (pn_msg *) data;
++			dlci = pn_pkt->dlci;
++			frame_size = GET_PN_MSG_FRAME_SIZE(pn_pkt);
++			TS0710_DEBUG
++			    ("Received DLC parameter negotiation, PN\n");
++			if (pn_pkt->mcc_s_head.type.cr == MCC_CMD) {
++				TS0710_DEBUG("received PN command with:\n");
++				TS0710_DEBUG("Frame size:%d\n", frame_size);
++
++				frame_size =
++				    min(frame_size, ts0710->dlci[dlci].mtu);
++				send_pn_msg(ts0710, pn_pkt->prior, frame_size,
++					    0, 0, dlci, MCC_RSP);
++				ts0710->dlci[dlci].mtu = frame_size;
++				TS0710_DEBUG("process_mcc : mtu set to %d\n",
++					     ts0710->dlci[dlci].mtu);
++			} else {
++				TS0710_DEBUG("received PN response with:\n");
++				TS0710_DEBUG("Frame size:%d\n", frame_size);
++
++				frame_size =
++				    min(frame_size, ts0710->dlci[dlci].mtu);
++				ts0710->dlci[dlci].mtu = frame_size;
++
++				TS0710_DEBUG
++				    ("process_mcc : mtu set on dlci:%d to %d\n",
++				     dlci, ts0710->dlci[dlci].mtu);
++
++				if (ts0710->dlci[dlci].state == NEGOTIATING) {
++					ts0710->dlci[dlci].state = CONNECTING;
++					wake_up_interruptible(&ts0710->
++							      dlci[dlci].
++							      open_wait);
++				}
++			}
++			break;
++		}
++
++	case NSC:		/*Non supported command resonse */
++		TS0710_LOG("MUX Received Non supported command response\n");
++		break;
++
++	default:		/*Non supported command received */
++		TS0710_LOG("MUX Received a non supported command\n");
++		send_nsc_msg(ts0710, mcc_short_pkt->h.type, MCC_RSP);
++		break;
++	}
++}
++
++static mux_recv_packet *get_mux_recv_packet(__u32 size)
++{
++	mux_recv_packet *recv_packet;
++
++	TS0710_DEBUG("Enter into get_mux_recv_packet");
++
++	recv_packet =
++	    (mux_recv_packet *) kmalloc(sizeof(mux_recv_packet), GFP_ATOMIC);
++	if (!recv_packet) {
++		return 0;
++	}
++
++	recv_packet->data = (__u8 *) kmalloc(size, GFP_ATOMIC);
++	if (!(recv_packet->data)) {
++		kfree(recv_packet);
++		return 0;
++	}
++	recv_packet->length = 0;
++	recv_packet->next = 0;
++	return recv_packet;
++}
++
++static void free_mux_recv_packet(mux_recv_packet * recv_packet)
++{
++	TS0710_DEBUG("Enter into free_mux_recv_packet");
++
++	if (!recv_packet) {
++		return;
++	}
++
++	if (recv_packet->data) {
++		kfree(recv_packet->data);
++	}
++	kfree(recv_packet);
++}
++
++static void free_mux_recv_struct(mux_recv_struct * recv_info)
++{
++	mux_recv_packet *recv_packet1, *recv_packet2;
++
++	if (!recv_info) {
++		return;
++	}
++
++	recv_packet1 = recv_info->mux_packet;
++	while (recv_packet1) {
++		recv_packet2 = recv_packet1->next;
++		free_mux_recv_packet(recv_packet1);
++		recv_packet1 = recv_packet2;
++	}
++
++	kfree(recv_info);
++}
++
++static inline void add_post_recv_queue(mux_recv_struct ** head,
++				       mux_recv_struct * new_item)
++{
++	new_item->next = *head;
++	*head = new_item;
++}
++
++static void ts0710_flow_on(__u8 dlci, ts0710_con * ts0710)
++{
++	int i;
++	__u8 cmdtty;
++	__u8 datatty;
++	struct tty_struct *tty;
++	mux_recv_struct *recv_info;
++
++	if ((ts0710->dlci[0].state != CONNECTED)
++	    && (ts0710->dlci[0].state != FLOW_STOPPED)) {
++		return;
++	} else if ((ts0710->dlci[dlci].state != CONNECTED)
++		   && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
++		return;
++	}
++
++	if (!(ts0710->dlci[dlci].flow_control)) {
++		return;
++	}
++
++	cmdtty = dlci2tty[dlci].cmdtty;
++	datatty = dlci2tty[dlci].datatty;
++
++	if (cmdtty != datatty) {
++		/* Check AT cmd tty */
++		tty = mux_table[cmdtty];
++		if (mux_tty[cmdtty] && tty) {
++			if (test_bit(TTY_THROTTLED, &tty->flags)) {
++				return;
++			}
++		}
++		recv_info = mux_recv_info[cmdtty];
++		if (mux_recv_info_flags[cmdtty] && recv_info) {
++			if (recv_info->total) {
++				return;
++			}
++		}
++
++		/* Check data tty */
++		tty = mux_table[datatty];
++		if (mux_tty[datatty] && tty) {
++			if (test_bit(TTY_THROTTLED, &tty->flags)) {
++				return;
++			}
++		}
++		recv_info = mux_recv_info[datatty];
++		if (mux_recv_info_flags[datatty] && recv_info) {
++			if (recv_info->total) {
++				return;
++			}
++		}
++	}
++
++	for (i = 0; i < 3; i++) {
++		if (ts0710_msc_msg(ts0710, EA | RTC | RTR | DV, MCC_CMD, dlci) <
++		    0) {
++			continue;
++		} else {
++			TS0710_LOG("MUX send Flow on on dlci %d\n", dlci);
++			ts0710->dlci[dlci].flow_control = 0;
++			break;
++		}
++	}
++}
++
++static void ts0710_flow_off(struct tty_struct *tty, __u8 dlci,
++			    ts0710_con * ts0710)
++{
++	int i;
++
++	if (test_and_set_bit(TTY_THROTTLED, &tty->flags)) {
++		return;
++	}
++
++	if ((ts0710->dlci[0].state != CONNECTED)
++	    && (ts0710->dlci[0].state != FLOW_STOPPED)) {
++		return;
++	} else if ((ts0710->dlci[dlci].state != CONNECTED)
++		   && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
++		return;
++	}
++
++	if (ts0710->dlci[dlci].flow_control) {
++		return;
++	}
++
++	for (i = 0; i < 3; i++) {
++		if (ts0710_msc_msg
++		    (ts0710, EA | FC | RTC | RTR | DV, MCC_CMD, dlci) < 0) {
++			continue;
++		} else {
++			TS0710_LOG("MUX send Flow off on dlci %d\n", dlci);
++			ts0710->dlci[dlci].flow_control = 1;
++			break;
++		}
++	}
++}
++
++int ts0710_recv_data(ts0710_con * ts0710, char *data, int len)
++{
++	short_frame *short_pkt;
++	long_frame *long_pkt;
++	__u8 *uih_data_start;
++	__u32 uih_len;
++	__u8 dlci;
++	__u8 be_connecting;
++#ifdef TS0710DEBUG
++	unsigned long t;
++#endif
++
++	short_pkt = (short_frame *) data;
++
++	dlci = short_pkt->h.addr.server_chn << 1 | short_pkt->h.addr.d;
++	switch (CLR_PF(short_pkt->h.control)) {
++	case SABM:
++		TS0710_DEBUG("SABM-packet received\n");
++
++/*For BP UART problem
++      if( crc_check((__u8*) short_pkt, SHORT_CRC_CHECK, short_pkt->data[0]) )
++        break;
++*/
++
++		if (!dlci) {
++			TS0710_DEBUG("server channel == 0\n");
++			ts0710->dlci[0].state = CONNECTED;
++
++			TS0710_DEBUG("sending back UA - control channel\n");
++			send_ua(ts0710, dlci);
++			wake_up_interruptible(&ts0710->dlci[0].open_wait);
++
++		} else if (valid_dlci(dlci)) {
++
++			TS0710_DEBUG("Incomming connect on channel %d\n", dlci);
++
++			TS0710_DEBUG("sending UA, dlci %d\n", dlci);
++			send_ua(ts0710, dlci);
++
++			ts0710->dlci[dlci].state = CONNECTED;
++			wake_up_interruptible(&ts0710->dlci[dlci].open_wait);
++
++		} else {
++			TS0710_DEBUG("invalid dlci %d, sending DM\n", dlci);
++			send_dm(ts0710, dlci);
++		}
++
++		break;
++
++	case UA:
++		TS0710_DEBUG("UA packet received\n");
++
++/*For BP UART problem
++      if( crc_check((__u8*) short_pkt, SHORT_CRC_CHECK, short_pkt->data[0]) )
++        break;
++*/
++
++		if (!dlci) {
++			TS0710_DEBUG("server channel == 0\n");
++
++			if (ts0710->dlci[0].state == CONNECTING) {
++				ts0710->dlci[0].state = CONNECTED;
++				wake_up_interruptible(&ts0710->dlci[0].
++						      open_wait);
++			} else if (ts0710->dlci[0].state == DISCONNECTING) {
++				ts0710_upon_disconnect();
++			} else {
++				TS0710_DEBUG
++				    (" Something wrong receiving UA packet\n");
++			}
++		} else if (valid_dlci(dlci)) {
++			TS0710_DEBUG("Incomming UA on channel %d\n", dlci);
++
++			if (ts0710->dlci[dlci].state == CONNECTING) {
++				ts0710->dlci[dlci].state = CONNECTED;
++				wake_up_interruptible(&ts0710->dlci[dlci].
++						      open_wait);
++			} else if (ts0710->dlci[dlci].state == DISCONNECTING) {
++				ts0710->dlci[dlci].state = DISCONNECTED;
++				wake_up_interruptible(&ts0710->dlci[dlci].
++						      open_wait);
++				wake_up_interruptible(&ts0710->dlci[dlci].
++						      close_wait);
++				ts0710_reset_dlci(dlci);
++			} else {
++				TS0710_DEBUG
++				    (" Something wrong receiving UA packet\n");
++			}
++		} else {
++			TS0710_DEBUG("invalid dlci %d\n", dlci);
++		}
++
++		break;
++
++	case DM:
++		TS0710_DEBUG("DM packet received\n");
++
++/*For BP UART problem
++      if( crc_check((__u8*) short_pkt, SHORT_CRC_CHECK, short_pkt->data[0]) )
++        break;
++*/
++
++		if (!dlci) {
++			TS0710_DEBUG("server channel == 0\n");
++
++			if (ts0710->dlci[0].state == CONNECTING) {
++				be_connecting = 1;
++			} else {
++				be_connecting = 0;
++			}
++			ts0710_upon_disconnect();
++			if (be_connecting) {
++				ts0710->dlci[0].state = REJECTED;
++			}
++		} else if (valid_dlci(dlci)) {
++			TS0710_DEBUG("Incomming DM on channel %d\n", dlci);
++
++			if (ts0710->dlci[dlci].state == CONNECTING) {
++				ts0710->dlci[dlci].state = REJECTED;
++			} else {
++				ts0710->dlci[dlci].state = DISCONNECTED;
++			}
++			wake_up_interruptible(&ts0710->dlci[dlci].open_wait);
++			wake_up_interruptible(&ts0710->dlci[dlci].close_wait);
++			ts0710_reset_dlci(dlci);
++		} else {
++			TS0710_DEBUG("invalid dlci %d\n", dlci);
++		}
++
++		break;
++
++	case DISC:
++		TS0710_DEBUG("DISC packet received\n");
++
++/*For BP UART problem
++      if( crc_check((__u8*) short_pkt, SHORT_CRC_CHECK, short_pkt->data[0]) )
++        break;
++*/
++
++		if (!dlci) {
++			TS0710_DEBUG("server channel == 0\n");
++
++			send_ua(ts0710, dlci);
++			TS0710_DEBUG("DISC, sending back UA\n");
++
++			ts0710_upon_disconnect();
++		} else if (valid_dlci(dlci)) {
++			TS0710_DEBUG("Incomming DISC on channel %d\n", dlci);
++
++			send_ua(ts0710, dlci);
++			TS0710_DEBUG("DISC, sending back UA\n");
++
++			ts0710->dlci[dlci].state = DISCONNECTED;
++			wake_up_interruptible(&ts0710->dlci[dlci].open_wait);
++			wake_up_interruptible(&ts0710->dlci[dlci].close_wait);
++			ts0710_reset_dlci(dlci);
++		} else {
++			TS0710_DEBUG("invalid dlci %d\n", dlci);
++		}
++
++		break;
++
++	case UIH:
++		TS0710_DEBUG("UIH packet received\n");
++
++		if ((dlci >= TS0710_MAX_CHN)) {
++			TS0710_DEBUG("invalid dlci %d\n", dlci);
++			send_dm(ts0710, dlci);
++			break;
++		}
++
++		if (GET_PF(short_pkt->h.control)) {
++			TS0710_LOG
++			    ("MUX Error %s: UIH packet with P/F set, discard it!\n",
++			     __FUNCTION__);
++			break;
++		}
++
++		if ((ts0710->dlci[dlci].state != CONNECTED)
++		    && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
++			TS0710_LOG
++			    ("MUX Error %s: DLCI %d not connected, discard it!\n",
++			     __FUNCTION__, dlci);
++			send_dm(ts0710, dlci);
++			break;
++		}
++
++		if ((short_pkt->h.length.ea) == 0) {
++			TS0710_DEBUG("Long UIH packet received\n");
++			long_pkt = (long_frame *) data;
++			uih_len = GET_LONG_LENGTH(long_pkt->h.length);
++			uih_data_start = long_pkt->h.data;
++			TS0710_DEBUG("long packet length %d\n", uih_len);
++
++/*For BP UART problem
++        if (crc_check(data, LONG_CRC_CHECK, *(uih_data_start + uih_len)))
++          break;
++*/
++		} else {
++			TS0710_DEBUG("Short UIH pkt received\n");
++			uih_len = short_pkt->h.length.len;
++			uih_data_start = short_pkt->data;
++
++/*For BP UART problem
++        if (crc_check(data, SHORT_CRC_CHECK, *(uih_data_start + uih_len)))
++          break;
++*/
++		}
++
++		if (dlci == 0) {
++			TS0710_DEBUG("UIH on serv_channel 0\n");
++			process_mcc(data, len, ts0710,
++				    !(short_pkt->h.length.ea));
++		} else if (valid_dlci(dlci)) {
++			/* do tty dispatch */
++			__u8 tag;
++			__u8 tty_idx;
++			struct tty_struct *tty;
++			__u8 queue_data;
++			__u8 post_recv;
++			__u8 flow_control;
++			mux_recv_struct *recv_info;
++			int recv_room;
++			mux_recv_packet *recv_packet, *recv_packet2;
++
++			TS0710_DEBUG("UIH on channel %d\n", dlci);
++
++			if (uih_len > ts0710->dlci[dlci].mtu) {
++				TS0710_PRINTK
++				    ("MUX Error:  DLCI:%d, uih_len:%d is bigger than mtu:%d, discard data!\n",
++				     dlci, uih_len, ts0710->dlci[dlci].mtu);
++				break;
++			}
++
++			tag = *uih_data_start;
++			uih_data_start++;
++			uih_len--;
++
++			if (!uih_len) {
++				break;
++			}
++
++			switch (tag) {
++			case CMDTAG:
++				tty_idx = dlci2tty[dlci].cmdtty;
++				TS0710_DEBUG("CMDTAG on DLCI:%d, /dev/mux%d\n",
++					     dlci, tty_idx);
++				TS0710_DEBUGSTR(uih_data_start, uih_len);
++				if (!(iscmdtty[tty_idx])) {
++					TS0710_PRINTK
++					    ("MUX Error: %s: Wrong CMDTAG on DLCI:%d, /dev/mux%d\n",
++					     __FUNCTION__, dlci, tty_idx);
++				}
++				break;
++			case DATATAG:
++			default:
++				tty_idx = dlci2tty[dlci].datatty;
++				TS0710_DEBUG
++				    ("NON-CMDTAG on DLCI:%d, /dev/mux%d\n",
++				     dlci, tty_idx);
++				if (iscmdtty[tty_idx]) {
++					TS0710_PRINTK
++					    ("MUX Error: %s: Wrong NON-CMDTAG on DLCI:%d, /dev/mux%d\n",
++					     __FUNCTION__, dlci, tty_idx);
++				}
++				break;
++			}
++			tty = mux_table[tty_idx];
++			if ((!mux_tty[tty_idx]) || (!tty)) {
++				TS0710_PRINTK
++				    ("MUX: No application waiting for, discard it! /dev/mux%d\n",
++				     tty_idx);
++			} else {	/* Begin processing received data */
++				if ((!mux_recv_info_flags[tty_idx])
++				    || (!mux_recv_info[tty_idx])) {
++					TS0710_PRINTK
++					    ("MUX Error: No mux_recv_info, discard it! /dev/mux%d\n",
++					     tty_idx);
++					break;
++				}
++
++				recv_info = mux_recv_info[tty_idx];
++				if (recv_info->total > 8192) {
++					TS0710_PRINTK
++					    ("MUX : discard data for tty_idx:%d, recv_info->total > 8192 \n",
++					     tty_idx);
++					break;
++				}
++
++				queue_data = 0;
++				post_recv = 0;
++				flow_control = 0;
++				recv_room = 65535;
++				if (tty->receive_room)
++					recv_room = tty->receive_room;
++
++				if (test_bit(TTY_THROTTLED, &tty->flags)) {
++					queue_data = 1;
++				} else {
++					if (test_bit
++					    (TTY_DONT_FLIP, &tty->flags)) {
++						queue_data = 1;
++						post_recv = 1;
++					} else if (recv_info->total) {
++						queue_data = 1;
++						post_recv = 1;
++					} else if (recv_room < uih_len) {
++						queue_data = 1;
++						flow_control = 1;
++					}
++
++					if ((recv_room -
++					     (uih_len + recv_info->total)) <
++					    ts0710->dlci[dlci].mtu) {
++						flow_control = 1;
++					}
++				}
++
++				if (!queue_data) {
++					/* Put received data into read buffer of tty */
++					TS0710_DEBUG
++					    ("Put received data into read buffer of /dev/mux%d",
++					     tty_idx);
++
++#ifdef TS0710DEBUG
++					t = jiffies;
++#endif
++
++					(tty->ldisc.receive_buf) (tty,
++								  uih_data_start,
++								  NULL,
++								  uih_len);
++
++#ifdef TS0710DEBUG
++					TS0710_DEBUG
++					    ("tty->ldisc.receive_buf take ticks: %lu",
++					     (jiffies - t));
++#endif
++
++				} else {	/* Queue data */
++
++					TS0710_DEBUG
++					    ("Put received data into recv queue of /dev/mux%d",
++					     tty_idx);
++					if (recv_info->total) {
++						/* recv_info is already linked into mux_recv_queue */
++
++						recv_packet =
++						    get_mux_recv_packet
++						    (uih_len);
++						if (!recv_packet) {
++							TS0710_PRINTK
++							    ("MUX %s: no memory\n",
++							     __FUNCTION__);
++							break;
++						}
++
++						memcpy(recv_packet->data,
++						       uih_data_start, uih_len);
++						recv_packet->length = uih_len;
++						recv_info->total += uih_len;
++						recv_packet->next = NULL;
++
++						if (!(recv_info->mux_packet)) {
++							recv_info->mux_packet =
++							    recv_packet;
++						} else {
++							recv_packet2 =
++							    recv_info->
++							    mux_packet;
++							while (recv_packet2->
++							       next) {
++								recv_packet2 =
++								    recv_packet2->
++								    next;
++							}
++							recv_packet2->next =
++							    recv_packet;
++						}	/* End if( !(recv_info->mux_packet) ) */
++					} else {	/* recv_info->total == 0 */
++						if (uih_len >
++						    TS0710MUX_RECV_BUF_SIZE) {
++							TS0710_PRINTK
++							    ("MUX Error:  tty_idx:%d, uih_len == %d is too big\n",
++							     tty_idx, uih_len);
++							uih_len =
++							    TS0710MUX_RECV_BUF_SIZE;
++						}
++						memcpy(recv_info->data,
++						       uih_data_start, uih_len);
++						recv_info->length = uih_len;
++						recv_info->total = uih_len;
++
++						add_post_recv_queue
++						    (&mux_recv_queue,
++						     recv_info);
++					}	/* End recv_info->total == 0 */
++				}	/* End Queue data */
++
++				if (flow_control) {
++					/* Do something for flow control */
++					ts0710_flow_off(tty, dlci, ts0710);
++				}
++
++				if (tty_idx ==
++				    dlci2tty[TS0710MUX_GPRS1_DLCI].datatty) {
++					if (add_count
++					    (TS0710MUX_GPRS1_RECV_COUNT_IDX,
++					     uih_len) < 0) {
++						post_recv_count_flag = 1;
++						post_recv = 1;
++						mux_data_count2
++						    [TS0710MUX_GPRS1_RECV_COUNT_IDX]
++						    += uih_len;
++					}
++				} else if (tty_idx ==
++					   dlci2tty[TS0710MUX_GPRS2_DLCI].
++					   datatty) {
++					if (add_count
++					    (TS0710MUX_GPRS2_RECV_COUNT_IDX,
++					     uih_len) < 0) {
++						post_recv_count_flag = 1;
++						post_recv = 1;
++						mux_data_count2
++						    [TS0710MUX_GPRS2_RECV_COUNT_IDX]
++						    += uih_len;
++					}
++				}
++
++				if (post_recv) 
++					schedule_work(&post_recv_tqueue);
++			}	/* End processing received data */
++		} else {
++			TS0710_DEBUG("invalid dlci %d\n", dlci);
++		}
++
++		break;
++
++	default:
++		TS0710_DEBUG("illegal packet\n");
++		break;
++	}
++	return 0;
++}
++
++/*
++int ts0710_send_data(ts0710_con *ts0710, __u8 dlci, __u8 *data, __u32 count)
++{
++  __u32 c, total = 0;
++  __u8 tag, first;
++
++  if( ts0710->dlci[0].state == FLOW_STOPPED ){
++    TS0710_DEBUG("Flow stopped on all channels, returning zero\n");
++*/
++/*
++    return -EFLOWSTOPPED;
++  } else if( ts0710->dlci[dlci].state == FLOW_STOPPED ){
++    TS0710_DEBUG("Flow stopped, returning zero\n");
++*/
++/*
++    return -EFLOWSTOPPED;
++  } else if( ts0710->dlci[dlci].state == CONNECTED ){
++
++    TS0710_DEBUG("trying to send %d bytes\n", count);
++    tag = *data;
++    first = 1;
++*/
++    /* The first byte is always a Cmd/Data tag */
++/*
++    while( count > 1 ){
++
++      c = min(count, ts0710->dlci[dlci].mtu);
++      if( queue_uih(data, c, ts0710, dlci) <= 0 ) {
++        break;
++      }
++
++      total += (c - 1);
++      data += (c - 1);
++      *data = tag;
++      count -= (c - 1);
++
++      if( first ) {
++        first = 0;
++	total++;
++      }
++    }
++    TS0710_DEBUG("sent %d bytes\n", total);
++    return total;
++  } else {
++    TS0710_DEBUG("DLCI %d not connected\n", dlci);
++    return -EDISCONNECTED;
++  }
++}
++*/
++
++/* Close ts0710 channel */
++static void ts0710_close_channel(__u8 dlci)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	int try;
++	unsigned long t;
++
++	TS0710_DEBUG("ts0710_disc_command on channel %d\n", dlci);
++
++	if ((ts0710->dlci[dlci].state == DISCONNECTED)
++	    || (ts0710->dlci[dlci].state == REJECTED)) {
++		return;
++	} else if (ts0710->dlci[dlci].state == DISCONNECTING) {
++		/* Reentry */
++		return;
++	} else {
++		ts0710->dlci[dlci].state = DISCONNECTING;
++		try = 3;
++		while (try--) {
++			t = jiffies;
++			send_disc(ts0710, dlci);
++			interruptible_sleep_on_timeout(&ts0710->dlci[dlci].
++						       close_wait,
++						       TS0710MUX_TIME_OUT);
++			if (ts0710->dlci[dlci].state == DISCONNECTED) {
++				break;
++			} else if (signal_pending(current)) {
++				TS0710_PRINTK
++				    ("MUX DLCI %d Send DISC got signal!\n",
++				     dlci);
++				break;
++			} else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
++				TS0710_PRINTK
++				    ("MUX DLCI %d Send DISC timeout!\n", dlci);
++				continue;
++			}
++		}
++
++		if (ts0710->dlci[dlci].state != DISCONNECTED) {
++			if (dlci == 0) {	/* Control Channel */
++				ts0710_upon_disconnect();
++			} else {	/* Other Channel */
++				ts0710->dlci[dlci].state = DISCONNECTED;
++				wake_up_interruptible(&ts0710->dlci[dlci].
++						      close_wait);
++				ts0710_reset_dlci(dlci);
++			}
++		}
++	}
++}
++
++int ts0710_open_channel(__u8 dlci)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	int try;
++	int retval;
++	unsigned long t;
++
++	retval = -ENODEV;
++	if (dlci == 0) {	// control channel
++		if ((ts0710->dlci[0].state == CONNECTED)
++		    || (ts0710->dlci[0].state == FLOW_STOPPED)) {
++			return 0;
++		} else if (ts0710->dlci[0].state == CONNECTING) {
++			/* Reentry */
++			TS0710_PRINTK
++			    ("MUX DLCI: 0, reentry to open DLCI 0, pid: %d, %s !\n",
++			     current->pid, current->comm);
++			try = 11;
++			while (try--) {
++				t = jiffies;
++				interruptible_sleep_on_timeout(&ts0710->dlci[0].
++							       open_wait,
++							       TS0710MUX_TIME_OUT);
++				if ((ts0710->dlci[0].state == CONNECTED)
++				    || (ts0710->dlci[0].state ==
++					FLOW_STOPPED)) {
++					retval = 0;
++					break;
++				} else if (ts0710->dlci[0].state == REJECTED) {
++					retval = -EREJECTED;
++					break;
++				} else if (ts0710->dlci[0].state ==
++					   DISCONNECTED) {
++					break;
++				} else if (signal_pending(current)) {
++					TS0710_PRINTK
++					    ("MUX DLCI:%d Wait for connecting got signal!\n",
++					     dlci);
++					retval = -EAGAIN;
++					break;
++				} else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
++					TS0710_PRINTK
++					    ("MUX DLCI:%d Wait for connecting timeout!\n",
++					     dlci);
++					continue;
++				} else if (ts0710->dlci[0].state == CONNECTING) {
++					continue;
++				}
++			}
++
++			if (ts0710->dlci[0].state == CONNECTING) {
++				ts0710->dlci[0].state = DISCONNECTED;
++			}
++		} else if ((ts0710->dlci[0].state != DISCONNECTED)
++			   && (ts0710->dlci[0].state != REJECTED)) {
++			TS0710_PRINTK("MUX DLCI:%d state is invalid!\n", dlci);
++			return retval;
++		} else {
++			ts0710->initiator = 1;
++			ts0710->dlci[0].state = CONNECTING;
++			ts0710->dlci[0].initiator = 1;
++			try = 10;
++			while (try--) {
++				t = jiffies;
++				send_sabm(ts0710, 0);
++				interruptible_sleep_on_timeout(&ts0710->dlci[0].
++							       open_wait,
++							       TS0710MUX_TIME_OUT);
++				if ((ts0710->dlci[0].state == CONNECTED)
++				    || (ts0710->dlci[0].state ==
++					FLOW_STOPPED)) {
++					retval = 0;
++					break;
++				} else if (ts0710->dlci[0].state == REJECTED) {
++					TS0710_PRINTK
++					    ("MUX DLCI:%d Send SABM got rejected!\n",
++					     dlci);
++					retval = -EREJECTED;
++					break;
++				} else if (signal_pending(current)) {
++					TS0710_PRINTK
++					    ("MUX DLCI:%d Send SABM got signal!\n",
++					     dlci);
++					retval = -EAGAIN;
++					break;
++				} else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
++					TS0710_PRINTK
++					    ("MUX DLCI:%d Send SABM timeout!\n",
++					     dlci);
++					continue;
++				}
++			}
++
++			if (ts0710->dlci[0].state == CONNECTING) {
++				ts0710->dlci[0].state = DISCONNECTED;
++			}
++			wake_up_interruptible(&ts0710->dlci[0].open_wait);
++		}
++	} else {		// other channel
++		if ((ts0710->dlci[0].state != CONNECTED)
++		    && (ts0710->dlci[0].state != FLOW_STOPPED)) {
++			return retval;
++		} else if ((ts0710->dlci[dlci].state == CONNECTED)
++			   || (ts0710->dlci[dlci].state == FLOW_STOPPED)) {
++			return 0;
++		} else if ((ts0710->dlci[dlci].state == NEGOTIATING)
++			   || (ts0710->dlci[dlci].state == CONNECTING)) {
++			/* Reentry */
++			try = 8;
++			while (try--) {
++				t = jiffies;
++				interruptible_sleep_on_timeout(&ts0710->
++							       dlci[dlci].
++							       open_wait,
++							       TS0710MUX_TIME_OUT);
++				if ((ts0710->dlci[dlci].state == CONNECTED)
++				    || (ts0710->dlci[dlci].state ==
++					FLOW_STOPPED)) {
++					retval = 0;
++					break;
++				} else if (ts0710->dlci[dlci].state == REJECTED) {
++					retval = -EREJECTED;
++					break;
++				} else if (ts0710->dlci[dlci].state ==
++					   DISCONNECTED) {
++					break;
++				} else if (signal_pending(current)) {
++					TS0710_PRINTK
++					    ("MUX DLCI:%d Wait for connecting got signal!\n",
++					     dlci);
++					retval = -EAGAIN;
++					break;
++				} else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
++					TS0710_PRINTK
++					    ("MUX DLCI:%d Wait for connecting timeout!\n",
++					     dlci);
++					continue;
++				} else
++				    if ((ts0710->dlci[dlci].state ==
++					 NEGOTIATING)
++					|| (ts0710->dlci[dlci].state ==
++					    CONNECTING)) {
++					continue;
++				}
++			}
++
++			if ((ts0710->dlci[dlci].state == NEGOTIATING)
++			    || (ts0710->dlci[dlci].state == CONNECTING)) {
++				ts0710->dlci[dlci].state = DISCONNECTED;
++			}
++		} else if ((ts0710->dlci[dlci].state != DISCONNECTED)
++			   && (ts0710->dlci[dlci].state != REJECTED)) {
++			TS0710_PRINTK("MUX DLCI:%d state is invalid!\n", dlci);
++			return retval;
++		} else {
++			ts0710->dlci[dlci].state = NEGOTIATING;
++			ts0710->dlci[dlci].initiator = 1;
++			try = 3;
++			while (try--) {
++				t = jiffies;
++				send_pn_msg(ts0710, 7, ts0710->dlci[dlci].mtu,
++					    0, 0, dlci, 1);
++				interruptible_sleep_on_timeout(&ts0710->
++							       dlci[dlci].
++							       open_wait,
++							       TS0710MUX_TIME_OUT);
++				if (ts0710->dlci[dlci].state == CONNECTING) {
++					break;
++				} else if (signal_pending(current)) {
++					TS0710_PRINTK
++					    ("MUX DLCI:%d Send pn_msg got signal!\n",
++					     dlci);
++					retval = -EAGAIN;
++					break;
++				} else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
++					TS0710_PRINTK
++					    ("MUX DLCI:%d Send pn_msg timeout!\n",
++					     dlci);
++					continue;
++				}
++			}
++
++			if (ts0710->dlci[dlci].state == CONNECTING) {
++				try = 3;
++				while (try--) {
++					t = jiffies;
++					send_sabm(ts0710, dlci);
++					interruptible_sleep_on_timeout(&ts0710->
++								       dlci
++								       [dlci].
++								       open_wait,
++								       TS0710MUX_TIME_OUT);
++					if ((ts0710->dlci[dlci].state ==
++					     CONNECTED)
++					    || (ts0710->dlci[dlci].state ==
++						FLOW_STOPPED)) {
++						retval = 0;
++						break;
++					} else if (ts0710->dlci[dlci].state ==
++						   REJECTED) {
++						TS0710_PRINTK
++						    ("MUX DLCI:%d Send SABM got rejected!\n",
++						     dlci);
++						retval = -EREJECTED;
++						break;
++					} else if (signal_pending(current)) {
++						TS0710_PRINTK
++						    ("MUX DLCI:%d Send SABM got signal!\n",
++						     dlci);
++						retval = -EAGAIN;
++						break;
++					} else if ((jiffies - t) >=
++						   TS0710MUX_TIME_OUT) {
++						TS0710_PRINTK
++						    ("MUX DLCI:%d Send SABM timeout!\n",
++						     dlci);
++						continue;
++					}
++				}
++			}
++
++			if ((ts0710->dlci[dlci].state == NEGOTIATING)
++			    || (ts0710->dlci[dlci].state == CONNECTING)) {
++				ts0710->dlci[dlci].state = DISCONNECTED;
++			}
++			wake_up_interruptible(&ts0710->dlci[dlci].open_wait);
++		}
++	}
++	return retval;
++}
++
++static int ts0710_exec_test_cmd(void)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	__u8 *f_buf;		/* Frame buffer */
++	__u8 *d_buf;		/* Data buffer */
++	int retval = -EFAULT;
++	int j;
++	unsigned long t;
++
++	if (ts0710->be_testing) {
++		/* Reentry */
++		t = jiffies;
++		interruptible_sleep_on_timeout(&ts0710->test_wait,
++					       3 * TS0710MUX_TIME_OUT);
++		if (ts0710->be_testing == 0) {
++			if (ts0710->test_errs == 0) {
++				retval = 0;
++			} else {
++				retval = -EFAULT;
++			}
++		} else if (signal_pending(current)) {
++			TS0710_DEBUG
++			    ("Wait for Test_cmd response got signal!\n");
++			retval = -EAGAIN;
++		} else if ((jiffies - t) >= 3 * TS0710MUX_TIME_OUT) {
++			TS0710_DEBUG("Wait for Test_cmd response timeout!\n");
++			retval = -EFAULT;
++		}
++	} else {
++		ts0710->be_testing = 1;	/* Set the flag */
++
++		f_buf = (__u8 *) kmalloc(TEST_PATTERN_SIZE + 32, GFP_KERNEL);
++		d_buf = (__u8 *) kmalloc(TEST_PATTERN_SIZE + 32, GFP_KERNEL);
++		if ((!f_buf) || (!d_buf)) {
++			if (f_buf) {
++				kfree(f_buf);
++			}
++			if (d_buf) {
++				kfree(d_buf);
++			}
++
++			ts0710->be_testing = 0;	/* Clear the flag */
++			ts0710->test_errs = TEST_PATTERN_SIZE;
++			wake_up_interruptible(&ts0710->test_wait);
++			return -ENOMEM;
++		}
++
++		for (j = 0; j < TEST_PATTERN_SIZE; j++) {
++			d_buf[j] = j & 0xFF;
++		}
++
++		t = jiffies;
++		ts0710_test_msg(ts0710, d_buf, TEST_PATTERN_SIZE, MCC_CMD,
++				f_buf);
++		interruptible_sleep_on_timeout(&ts0710->test_wait,
++					       2 * TS0710MUX_TIME_OUT);
++		if (ts0710->be_testing == 0) {
++			if (ts0710->test_errs == 0) {
++				retval = 0;
++			} else {
++				retval = -EFAULT;
++			}
++		} else if (signal_pending(current)) {
++			TS0710_DEBUG("Send Test_cmd got signal!\n");
++			retval = -EAGAIN;
++		} else if ((jiffies - t) >= 2 * TS0710MUX_TIME_OUT) {
++			TS0710_DEBUG("Send Test_cmd timeout!\n");
++			ts0710->test_errs = TEST_PATTERN_SIZE;
++			retval = -EFAULT;
++		}
++
++		ts0710->be_testing = 0;	/* Clear the flag */
++		wake_up_interruptible(&ts0710->test_wait);
++
++		/* Release buffer */
++		if (f_buf) {
++			kfree(f_buf);
++		}
++		if (d_buf) {
++			kfree(d_buf);
++		}
++	}
++
++	return retval;
++}
++
++static void mux_sched_send(void)
++{
++
++#ifdef USB_FOR_MUX
++	schedule_work(&send_tqueue);
++#else
++	if (!tq_serial_for_mux) {
++		TS0710_PRINTK("MUX Error: %s: tq_serial_for_mux == 0\n",
++			      __FUNCTION__);
++		return;
++	}
++	schedule_work(&send_tqueue);
++	mark_bh(SERIAL_BH);
++#endif
++
++}
++
++/****************************
++ * TTY driver routines
++*****************************/
++
++static void mux_close(struct tty_struct *tty, struct file *filp)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	int line;
++	__u8 dlci;
++	__u8 cmdtty;
++	__u8 datatty;
++
++	UNUSED_PARAM(filp);
++
++	if (!tty) {
++		return;
++	}
++	line = tty->index;
++	if ((line < 0) || (line >= NR_MUXS)) {
++		return;
++	}
++	if (mux_tty[line] > 0)
++		mux_tty[line]--;
++
++	dlci = tty2dlci[line];
++	cmdtty = dlci2tty[dlci].cmdtty;
++	datatty = dlci2tty[dlci].datatty;
++	if ((mux_tty[cmdtty] == 0) && (mux_tty[datatty] == 0)) {
++		if (dlci == 1) {
++			ts0710_close_channel(0);
++			TS0710_PRINTK
++			    ("MUX mux_close: tapisrv might be down!!! Close DLCI 1\n");
++			TS0710_SIG2APLOGD();
++		}
++		ts0710_close_channel(dlci);
++	}
++
++	if (mux_tty[line] == 0) {
++		if ((mux_send_info_flags[line])
++		    && (mux_send_info[line])
++		    /*&& (mux_send_info[line]->filled == 0) */
++		    ) {
++			mux_send_info_flags[line] = 0;
++			kfree(mux_send_info[line]);
++			mux_send_info[line] = 0;
++			TS0710_DEBUG("Free mux_send_info for /dev/mux%d", line);
++		}
++
++		if ((mux_recv_info_flags[line])
++		    && (mux_recv_info[line])
++		    && (mux_recv_info[line]->total == 0)) {
++			mux_recv_info_flags[line] = 0;
++			free_mux_recv_struct(mux_recv_info[line]);
++			mux_recv_info[line] = 0;
++			TS0710_DEBUG("Free mux_recv_info for /dev/mux%d", line);
++		}
++
++		ts0710_flow_on(dlci, ts0710);
++		schedule_work(&post_recv_tqueue);
++
++		wake_up_interruptible(&tty->read_wait);
++		wake_up_interruptible(&tty->write_wait);
++		tty->packet = 0;
++	}
++}
++
++static void mux_throttle(struct tty_struct *tty)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	int line;
++	int i;
++	__u8 dlci;
++
++	if (!tty) {
++		return;
++	}
++
++	line = tty->index;
++	if ((line < 0) || (line >= NR_MUXS)) {
++		return;
++	}
++
++	TS0710_DEBUG("Enter into %s, minor number is: %d\n", __FUNCTION__,
++		     line);
++
++	dlci = tty2dlci[line];
++	if ((ts0710->dlci[0].state != CONNECTED)
++	    && (ts0710->dlci[0].state != FLOW_STOPPED)) {
++		return;
++	} else if ((ts0710->dlci[dlci].state != CONNECTED)
++		   && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
++		return;
++	}
++
++	if (ts0710->dlci[dlci].flow_control) {
++		return;
++	}
++
++	for (i = 0; i < 3; i++) {
++		if (ts0710_msc_msg
++		    (ts0710, EA | FC | RTC | RTR | DV, MCC_CMD, dlci) < 0) {
++			continue;
++		} else {
++			TS0710_LOG("MUX Send Flow off on dlci %d\n", dlci);
++			ts0710->dlci[dlci].flow_control = 1;
++			break;
++		}
++	}
++}
++
++static void mux_unthrottle(struct tty_struct *tty)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	int line;
++	__u8 dlci;
++	mux_recv_struct *recv_info;
++
++	if (!tty) {
++		return;
++	}
++	line = tty->index;
++	if ((line < 0) || (line >= NR_MUXS)) {
++		return;
++	}
++
++	if ((!mux_recv_info_flags[line]) || (!mux_recv_info[line])) {
++		return;
++	}
++
++	TS0710_DEBUG("Enter into %s, minor number is: %d\n", __FUNCTION__,
++		     line);
++
++	recv_info = mux_recv_info[line];
++	dlci = tty2dlci[line];
++
++	if (recv_info->total) {
++		recv_info->post_unthrottle = 1;
++		schedule_work(&post_recv_tqueue);
++	} else {
++		ts0710_flow_on(dlci, ts0710);
++	}
++}
++
++static int mux_chars_in_buffer(struct tty_struct *tty)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	int retval;
++	int line;
++	__u8 dlci;
++	mux_send_struct *send_info;
++
++	retval = TS0710MUX_MAX_CHARS_IN_BUF;
++	if (!tty) {
++		goto out;
++	}
++	line = tty->index;
++	if ((line < 0) || (line >= NR_MUXS)) {
++		goto out;
++	}
++
++	dlci = tty2dlci[line];
++	if (ts0710->dlci[0].state == FLOW_STOPPED) {
++		TS0710_DEBUG
++		    ("Flow stopped on all channels, returning MAX chars in buffer\n");
++		goto out;
++	} else if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
++		TS0710_DEBUG("Flow stopped, returning MAX chars in buffer\n");
++		goto out;
++	} else if (ts0710->dlci[dlci].state != CONNECTED) {
++		TS0710_DEBUG("DLCI %d not connected\n", dlci);
++		goto out;
++	}
++
++	if (!(mux_send_info_flags[line])) {
++		goto out;
++	}
++	send_info = mux_send_info[line];
++	if (!send_info) {
++		goto out;
++	}
++	if (send_info->filled) {
++		goto out;
++	}
++
++	retval = 0;
++
++      out:
++	return retval;
++}
++
++static int mux_chars_in_serial_buffer(struct tty_struct *tty)
++{
++	UNUSED_PARAM(tty);
++
++	if ((COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)) {
++		TS0710_PRINTK
++		    ("MUX %s: (COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)\n",
++		     __FUNCTION__);
++
++#ifndef USB_FOR_MUX
++		TS0710_PRINTK
++		    ("MUX %s: tapisrv might be down!!! (serial_for_mux_driver == 0) || (serial_for_mux_tty == 0)\n",
++		     __FUNCTION__);
++		TS0710_SIG2APLOGD();
++#endif
++
++		return 0;
++	}
++	return COMM_FOR_MUX_DRIVER->chars_in_buffer(COMM_FOR_MUX_TTY);
++}
++
++static int mux_write(struct tty_struct *tty,
++		     const unsigned char *buf, int count)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	int line;
++	__u8 dlci;
++	mux_send_struct *send_info;
++	__u8 *d_buf;
++	__u16 c;
++	__u8 post_recv;
++
++	if (count <= 0) {
++		return 0;
++	}
++
++	if (!tty) {
++		return 0;
++	}
++
++	line = tty->index;
++	if ((line < 0) || (line >= NR_MUXS))
++		return -ENODEV;
++
++	dlci = tty2dlci[line];
++	if (ts0710->dlci[0].state == FLOW_STOPPED) {
++		TS0710_DEBUG
++		    ("Flow stopped on all channels, returning zero /dev/mux%d\n",
++		     line);
++		return 0;
++	} else if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
++		TS0710_DEBUG("Flow stopped, returning zero /dev/mux%d\n", line);
++		return 0;
++	} else if (ts0710->dlci[dlci].state == CONNECTED) {
++
++		if (!(mux_send_info_flags[line])) {
++			TS0710_PRINTK
++			    ("MUX Error: mux_write: mux_send_info_flags[%d] == 0\n",
++			     line);
++			return -ENODEV;
++		}
++		send_info = mux_send_info[line];
++		if (!send_info) {
++			TS0710_PRINTK
++			    ("MUX Error: mux_write: mux_send_info[%d] == 0\n",
++			     line);
++			return -ENODEV;
++		}
++
++		c = min(count, (ts0710->dlci[dlci].mtu - 1));
++		if (c <= 0) {
++			return 0;
++		}
++
++		if (test_and_set_bit(BUF_BUSY, &send_info->flags))
++			return 0;
++
++		if (send_info->filled) {
++			clear_bit(BUF_BUSY, &send_info->flags);
++			return 0;
++		}
++
++		d_buf = ((__u8 *) send_info->buf) + TS0710MUX_SEND_BUF_OFFSET;
++		memcpy(&d_buf[1], buf, c);
++
++		TS0710_DEBUG("Prepare to send %d bytes from /dev/mux%d", c,
++			     line);
++		if (iscmdtty[line]) {
++			TS0710_DEBUGSTR(&d_buf[1], c);
++			TS0710_DEBUG("CMDTAG");
++			d_buf[0] = CMDTAG;
++		} else {
++			TS0710_DEBUG("DATATAG");
++			d_buf[0] = DATATAG;
++		}
++
++		TS0710_DEBUGHEX(d_buf, c + 1);
++
++		send_info->frame = d_buf;
++		queue_uih(send_info, c + 1, ts0710, dlci);
++		send_info->filled = 1;
++		clear_bit(BUF_BUSY, &send_info->flags);
++
++		post_recv = 0;
++		if (dlci == TS0710MUX_GPRS1_DLCI) {
++			if (add_count
++			    (TS0710MUX_GPRS1_SEND_COUNT_IDX, c) < 0) {
++				post_recv_count_flag = 1;
++				post_recv = 1;
++				mux_data_count2[TS0710MUX_GPRS1_SEND_COUNT_IDX]
++				    += c;
++			}
++		} else if (dlci == TS0710MUX_GPRS2_DLCI) {
++			if (add_count
++			    (TS0710MUX_GPRS2_SEND_COUNT_IDX, c) < 0) {
++				post_recv_count_flag = 1;
++				post_recv = 1;
++				mux_data_count2[TS0710MUX_GPRS2_SEND_COUNT_IDX]
++				    += c;
++			}
++		}
++
++		if (post_recv)
++			schedule_work(&post_recv_tqueue);
++
++		if (mux_chars_in_serial_buffer(COMM_FOR_MUX_TTY) == 0) {
++			/* Sending bottom half should be
++			   run after return from this function */
++			mux_sched_send();
++		}
++		return c;
++	} else {
++		TS0710_PRINTK("MUX mux_write: DLCI %d not connected\n", dlci);
++		return -EDISCONNECTED;
++	}
++}
++
++static int mux_write_room(struct tty_struct *tty)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	int retval;
++	int line;
++	__u8 dlci;
++	mux_send_struct *send_info;
++
++	retval = 0;
++	if (!tty) {
++		goto out;
++	}
++	line = tty->index;
++	if ((line < 0) || (line >= NR_MUXS)) {
++		goto out;
++	}
++
++	dlci = tty2dlci[line];
++	if (ts0710->dlci[0].state == FLOW_STOPPED) {
++		TS0710_DEBUG("Flow stopped on all channels, returning ZERO\n");
++		goto out;
++	} else if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
++		TS0710_DEBUG("Flow stopped, returning ZERO\n");
++		goto out;
++	} else if (ts0710->dlci[dlci].state != CONNECTED) {
++		TS0710_DEBUG("DLCI %d not connected\n", dlci);
++		goto out;
++	}
++
++	if (!(mux_send_info_flags[line])) {
++		goto out;
++	}
++	send_info = mux_send_info[line];
++	if (!send_info) {
++		goto out;
++	}
++	if (send_info->filled) {
++		goto out;
++	}
++
++	retval = ts0710->dlci[dlci].mtu - 1;
++
++      out:
++	return retval;
++}
++
++static int mux_ioctl(struct tty_struct *tty, struct file *file,
++		     unsigned int cmd, unsigned long arg)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	int line;
++	__u8 dlci;
++
++	UNUSED_PARAM(file);
++	UNUSED_PARAM(arg);
++
++	if (!tty) {
++		return -EIO;
++	}
++	line = tty->index;
++	if ((line < 0) || (line >= NR_MUXS)) {
++		return -ENODEV;
++	}
++
++	dlci = tty2dlci[line];
++	switch (cmd) {
++	case TS0710MUX_IO_MSC_HANGUP:
++		if (ts0710_msc_msg(ts0710, EA | RTR | DV, MCC_CMD, dlci) < 0) {
++			return -EAGAIN;
++		} else {
++			return 0;
++		}
++
++	case TS0710MUX_IO_TEST_CMD:
++		return ts0710_exec_test_cmd();
++/*
++    case TS0710MUX_IO_DLCI_FC_ON:
++      if( line == 0 ) {
++        break;
++      }
++      if( ts0710_msc_msg(ts0710, EA | RTC | RTR | DV, MCC_CMD, (__u8)line) < 0) {
++        return -EAGAIN;
++      } else {
++        return 0;
++      }
++
++    case TS0710MUX_IO_DLCI_FC_OFF:
++      if( line == 0 ) {
++        break;
++      }
++      if( ts0710_msc_msg(ts0710, EA | FC | RTC | RTR | DV, MCC_CMD, (__u8)line) < 0) {
++        return -EAGAIN;
++      } else {
++        return 0;
++      }
++
++    case TS0710MUX_IO_FC_ON:
++      if( line != 0 ) {
++        break;
++      }
++      if( ts0710_fcon_msg(ts0710, MCC_CMD) < 0) {
++        return -EAGAIN;
++      } else {
++        return 0;
++      }
++
++    case TS0710MUX_IO_FC_OFF:
++      if( line != 0 ) {
++        break;
++      }
++      if( ts0710_fcoff_msg(ts0710, MCC_CMD) < 0) {
++        return -EAGAIN;
++      } else {
++        return 0;
++      }
++*/
++	default:
++		break;
++	}
++	return -ENOIOCTLCMD;
++}
++
++static void mux_flush_buffer(struct tty_struct *tty)
++{
++	int line;
++
++	if (!tty) {
++		return;
++	}
++
++	line = tty->index;
++	if ((line < 0) || (line >= NR_MUXS)) {
++		return;
++	}
++
++	TS0710_PRINTK("MUX %s: line is:%d\n", __FUNCTION__, line);
++
++	if ((mux_send_info_flags[line])
++	    && (mux_send_info[line])
++	    && (mux_send_info[line]->filled)) {
++
++		mux_send_info[line]->filled = 0;
++	}
++
++	wake_up_interruptible(&tty->write_wait);
++#ifdef SERIAL_HAVE_POLL_WAIT
++	wake_up_interruptible(&tty->poll_wait);
++#endif
++	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
++	    tty->ldisc.write_wakeup) {
++		(tty->ldisc.write_wakeup) (tty);
++	}
++
++/*
++  if( (COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0) ) {
++    TS0710_PRINTK("MUX %s: (COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)\n", __FUNCTION__);
++
++#ifndef USB_FOR_MUX
++    TS0710_PRINTK("MUX %s: tapisrv might be down!!! (serial_for_mux_driver == 0) || (serial_for_mux_tty == 0)\n", __FUNCTION__);
++    TS0710_SIG2APLOGD();
++#endif
++
++    return;
++  }
++  return COMM_FOR_MUX_DRIVER->flush_buffer(COMM_FOR_MUX_TTY);
++*/
++}
++
++static int mux_open(struct tty_struct *tty, struct file *filp)
++{
++	int retval;
++	int line;
++	__u8 dlci;
++	__u8 cmdtty;
++	__u8 datatty;
++	mux_send_struct *send_info;
++	mux_recv_struct *recv_info;
++
++	UNUSED_PARAM(filp);
++
++	retval = -ENODEV;
++	if ((COMM_FOR_MUX_DRIVER == NULL) || (COMM_FOR_MUX_TTY == NULL)) {
++
++#ifdef USB_FOR_MUX
++		TS0710_PRINTK("MUX: please install and open IPC-USB first\n");
++#else
++		TS0710_PRINTK("MUX: please install and open ttyS0 first\n");
++#endif
++
++		goto out;
++	}
++
++	if (!tty) {
++		goto out;
++	}
++	line = tty->index;
++	if ((line < 0) || (line >= NR_MUXS)) {
++		goto out;
++	}
++#ifdef TS0710SERVER
++	/* do nothing as a server */
++	mux_tty[line]++;
++	retval = 0;
++#else
++	mux_tty[line]++;
++	dlci = tty2dlci[line];
++
++/*  if( dlci == 1 ) { */
++	/* Open server channel 0 first */
++	if ((retval = ts0710_open_channel(0)) != 0) {
++		TS0710_PRINTK("MUX: Can't connect server channel 0!\n");
++		ts0710_init();
++
++		mux_tty[line]--;
++		goto out;
++	}
++/*  } */
++
++	/* Allocate memory first. As soon as connection has been established, MUX may receive */
++	if (mux_send_info_flags[line] == 0) {
++		send_info =
++		    (mux_send_struct *) kmalloc(sizeof(mux_send_struct),
++						GFP_KERNEL);
++		if (!send_info) {
++			retval = -ENOMEM;
++
++			mux_tty[line]--;
++			goto out;
++		}
++		send_info->length = 0;
++		send_info->flags = 0;
++		send_info->filled = 0;
++		mux_send_info[line] = send_info;
++		mux_send_info_flags[line] = 1;
++		TS0710_DEBUG("Allocate mux_send_info for /dev/mux%d", line);
++	}
++
++	if (mux_recv_info_flags[line] == 0) {
++		recv_info =
++		    (mux_recv_struct *) kmalloc(sizeof(mux_recv_struct),
++						GFP_KERNEL);
++		if (!recv_info) {
++			mux_send_info_flags[line] = 0;
++			kfree(mux_send_info[line]);
++			mux_send_info[line] = 0;
++			TS0710_DEBUG("Free mux_send_info for /dev/mux%d", line);
++			retval = -ENOMEM;
++
++			mux_tty[line]--;
++			goto out;
++		}
++		recv_info->length = 0;
++		recv_info->total = 0;
++		recv_info->mux_packet = 0;
++		recv_info->next = 0;
++		recv_info->no_tty = line;
++		recv_info->post_unthrottle = 0;
++		mux_recv_info[line] = recv_info;
++		mux_recv_info_flags[line] = 1;
++		TS0710_DEBUG("Allocate mux_recv_info for /dev/mux%d", line);
++	}
++
++	/* Now establish DLCI connection */
++	cmdtty = dlci2tty[dlci].cmdtty;
++	datatty = dlci2tty[dlci].datatty;
++	if ((mux_tty[cmdtty] > 0) || (mux_tty[datatty] > 0)) {
++		if ((retval = ts0710_open_channel(dlci)) != 0) {
++			TS0710_PRINTK("MUX: Can't connected channel %d!\n",
++				      dlci);
++			ts0710_reset_dlci(dlci);
++
++			mux_send_info_flags[line] = 0;
++			kfree(mux_send_info[line]);
++			mux_send_info[line] = 0;
++			TS0710_DEBUG("Free mux_send_info for /dev/mux%d", line);
++
++			mux_recv_info_flags[line] = 0;
++			free_mux_recv_struct(mux_recv_info[line]);
++			mux_recv_info[line] = 0;
++			TS0710_DEBUG("Free mux_recv_info for /dev/mux%d", line);
++
++			mux_tty[line]--;
++			goto out;
++		}
++	}
++
++	retval = 0;
++#endif
++      out:
++	return retval;
++}
++
++/* mux dispatcher, call from serial.c receiver_chars() */
++void mux_dispatcher(struct tty_struct *tty)
++{
++	UNUSED_PARAM(tty);
++
++	schedule_work(&receive_tqueue);
++}
++
++/*For BP UART problem Begin*/
++#ifdef TS0710SEQ2
++static int send_ack(ts0710_con * ts0710, __u8 seq_num, __u8 bp_seq1,
++		    __u8 bp_seq2)
++#else
++static int send_ack(ts0710_con * ts0710, __u8 seq_num)
++#endif
++{
++	__u8 buf[20];
++	short_frame *ack;
++
++#ifdef TS0710SEQ2
++	static __u16 ack_seq = 0;
++#endif
++
++	ack = (short_frame *) (buf + 1);
++	ack->h.addr.ea = 1;
++	ack->h.addr.cr = ((ts0710->initiator) & 0x1);
++	ack->h.addr.d = 0;
++	ack->h.addr.server_chn = 0;
++	ack->h.control = ACK;
++	ack->h.length.ea = 1;
++
++#ifdef TS0710SEQ2
++	ack->h.length.len = 5;
++	ack->data[0] = seq_num;
++	ack->data[1] = bp_seq1;
++	ack->data[2] = bp_seq2;
++	ack->data[3] = (ack_seq & 0xFF);
++	ack->data[4] = (ack_seq >> 8) & 0xFF;
++	ack_seq++;
++	ack->data[5] = crc_calc((__u8 *) ack, SHORT_CRC_CHECK);
++#else
++	ack->h.length.len = 1;
++	ack->data[0] = seq_num;
++	ack->data[1] = crc_calc((__u8 *) ack, SHORT_CRC_CHECK);
++#endif
++
++	return basic_write(ts0710, buf,
++			   (sizeof(short_frame) + FCS_SIZE +
++			    ack->h.length.len));
++}
++
++/*For BP UART problem End*/
++
++static void receive_worker(void *private_)
++{
++	struct tty_struct *tty = COMM_FOR_MUX_TTY;
++	int i, count;
++	static unsigned char tbuf[TS0710MUX_MAX_BUF_SIZE];
++	static unsigned char *tbuf_ptr = &tbuf[0];
++	static unsigned char *start_flag = 0;
++	unsigned char *search, *to, *from;
++	short_frame *short_pkt;
++	long_frame *long_pkt;
++	static int framelen = -1;
++
++	/*For BP UART problem Begin */
++	static __u8 expect_seq = 0;
++	__u32 crc_error;
++	__u8 *uih_data_start;
++	__u32 uih_len;
++	/*For BP UART problem End */
++
++	UNUSED_PARAM(private_);
++
++	if (!tty)
++		return;
++
++#ifdef USB_FOR_MUX
++	TS0710_DEBUG("Receive following bytes from IPC-USB");
++#else
++	TS0710_DEBUG("Receive following bytes from UART");
++#endif
++
++	TS0710_DEBUGHEX(cp, count);
++
++	if (count > (TS0710MUX_MAX_BUF_SIZE - (tbuf_ptr - tbuf))) {
++		TS0710_PRINTK
++		    ("MUX receive_worker: !!!!! Exceed buffer boundary !!!!!\n");
++		count = (TS0710MUX_MAX_BUF_SIZE - (tbuf_ptr - tbuf));
++	}
++
++	count = tty_buffer_request_room(tty, count);
++
++	for (i = 0; i < count; i++)
++		tty_insert_flip_char(tty, tbuf_ptr[i], TTY_NORMAL);
++
++	tbuf_ptr += count;
++	search = &tbuf[0];
++
++	if (test_and_set_bit(RECV_RUNNING, &mux_recv_flags)) {
++		schedule_work(&receive_tqueue);
++		return;
++	}
++
++	if ((start_flag != 0) && (framelen != -1)) {
++		if ((tbuf_ptr - start_flag) < framelen) {
++			clear_bit(RECV_RUNNING, &mux_recv_flags);
++			return;
++		}
++	}
++
++	while (1) {
++		if (start_flag == 0) {	/* Frame Start Flag not found */
++			framelen = -1;
++			while (search < tbuf_ptr) {
++				if (*search == TS0710_BASIC_FLAG) {
++					start_flag = search;
++					break;
++				}
++#ifdef TS0710LOG
++				else {
++					TS0710_LOG(">S %02x %c\n", *search,
++						   *search);
++				}
++#endif
++
++				search++;
++			}
++
++			if (start_flag == 0) {
++				tbuf_ptr = &tbuf[0];
++				break;
++			}
++		} else {	/* Frame Start Flag found */
++			/* 1 start flag + 1 address + 1 control + 1 or 2 length + lengths data + 1 FCS + 1 end flag */
++			/* For BP UART problem 1 start flag + 1 seq_num + 1 address + ...... */
++			/*if( (framelen == -1) && ((tbuf_ptr - start_flag) > TS0710_MAX_HDR_SIZE) ) */
++			if ((framelen == -1) && ((tbuf_ptr - start_flag) > (TS0710_MAX_HDR_SIZE + SEQ_FIELD_SIZE))) {	/*For BP UART problem */
++				/*short_pkt = (short_frame *) (start_flag + 1); */
++				short_pkt = (short_frame *) (start_flag + ADDRESS_FIELD_OFFSET);	/*For BP UART problem */
++				if (short_pkt->h.length.ea == 1) {	/* short frame */
++					/*framelen = TS0710_MAX_HDR_SIZE + short_pkt->h.length.len + 1; */
++					framelen = TS0710_MAX_HDR_SIZE + short_pkt->h.length.len + 1 + SEQ_FIELD_SIZE;	/*For BP UART problem */
++				} else {	/* long frame */
++					/*long_pkt = (long_frame *) (start_flag + 1); */
++					long_pkt = (long_frame *) (start_flag + ADDRESS_FIELD_OFFSET);	/*For BP UART problem */
++					/*framelen = TS0710_MAX_HDR_SIZE + GET_LONG_LENGTH( long_pkt->h.length ) + 2; */
++					framelen = TS0710_MAX_HDR_SIZE + GET_LONG_LENGTH(long_pkt->h.length) + 2 + SEQ_FIELD_SIZE;	/*For BP UART problem */
++				}
++
++				/*if( framelen > TS0710MUX_MAX_TOTAL_FRAME_SIZE ) { */
++				if (framelen > (TS0710MUX_MAX_TOTAL_FRAME_SIZE + SEQ_FIELD_SIZE)) {	/*For BP UART problem */
++					TS0710_LOGSTR_FRAME(0, start_flag,
++							    (tbuf_ptr -
++							     start_flag));
++					TS0710_PRINTK
++					    ("MUX Error: %s: frame length:%d is bigger than Max total frame size:%d\n",
++		 /*__FUNCTION__, framelen, TS0710MUX_MAX_TOTAL_FRAME_SIZE);*/
++					     __FUNCTION__, framelen, (TS0710MUX_MAX_TOTAL_FRAME_SIZE + SEQ_FIELD_SIZE));	/*For BP UART problem */
++					search = start_flag + 1;
++					start_flag = 0;
++					framelen = -1;
++					continue;
++				}
++			}
++
++			if ((framelen != -1)
++			    && ((tbuf_ptr - start_flag) >= framelen)) {
++				if (*(start_flag + framelen - 1) == TS0710_BASIC_FLAG) {	/* OK, We got one frame */
++
++					/*For BP UART problem Begin */
++					TS0710_LOGSTR_FRAME(0, start_flag,
++							    framelen);
++					TS0710_DEBUGHEX(start_flag, framelen);
++
++					short_pkt =
++					    (short_frame *) (start_flag +
++							     ADDRESS_FIELD_OFFSET);
++					if ((short_pkt->h.length.ea) == 0) {
++						long_pkt =
++						    (long_frame *) (start_flag +
++								    ADDRESS_FIELD_OFFSET);
++						uih_len =
++						    GET_LONG_LENGTH(long_pkt->h.
++								    length);
++						uih_data_start =
++						    long_pkt->h.data;
++
++						crc_error =
++						    crc_check((__u8
++							       *) (start_flag +
++								   SLIDE_BP_SEQ_OFFSET),
++							      LONG_CRC_CHECK +
++							      1,
++							      *(uih_data_start +
++								uih_len));
++					} else {
++						uih_len =
++						    short_pkt->h.length.len;
++						uih_data_start =
++						    short_pkt->data;
++
++						crc_error =
++						    crc_check((__u8
++							       *) (start_flag +
++								   SLIDE_BP_SEQ_OFFSET),
++							      SHORT_CRC_CHECK +
++							      1,
++							      *(uih_data_start +
++								uih_len));
++					}
++
++					if (!crc_error) {
++						if (expect_seq ==
++						    *(start_flag +
++						      SLIDE_BP_SEQ_OFFSET)) {
++							expect_seq++;
++							if (expect_seq >= 4) {
++								expect_seq = 0;
++							}
++#ifdef TS0710SEQ2
++							send_ack
++							    (&ts0710_connection,
++							     expect_seq,
++							     *(start_flag +
++							       FIRST_BP_SEQ_OFFSET),
++							     *(start_flag +
++							       SECOND_BP_SEQ_OFFSET));
++#else
++							send_ack
++							    (&ts0710_connection,
++							     expect_seq);
++#endif
++
++							ts0710_recv_data
++							    (&ts0710_connection,
++							     start_flag +
++							     ADDRESS_FIELD_OFFSET,
++							     framelen - 2 -
++							     SEQ_FIELD_SIZE);
++						} else {
++
++#ifdef TS0710DEBUG
++							if (*
++							    (start_flag +
++							     SLIDE_BP_SEQ_OFFSET)
++							    != 0x9F) {
++#endif
++
++								TS0710_LOG
++								    ("MUX sequence number %d is not expected %d, discard data!\n",
++								     *
++								     (start_flag
++								      +
++								      SLIDE_BP_SEQ_OFFSET),
++								     expect_seq);
++
++#ifdef TS0710SEQ2
++								send_ack
++								    (&ts0710_connection,
++								     expect_seq,
++								     *
++								     (start_flag
++								      +
++								      FIRST_BP_SEQ_OFFSET),
++								     *
++								     (start_flag
++								      +
++								      SECOND_BP_SEQ_OFFSET));
++#else
++								send_ack
++								    (&ts0710_connection,
++								     expect_seq);
++#endif
++
++#ifdef TS0710DEBUG
++							} else {
++								*(uih_data_start
++								  + uih_len) =
++						     0;
++								TS0710_PRINTK
++								    ("MUX bp log: %s\n",
++								     uih_data_start);
++							}
++#endif
++
++						}
++					} else {	/* crc_error */
++						search = start_flag + 1;
++						start_flag = 0;
++						framelen = -1;
++						continue;
++					}	/*End if(!crc_error) */
++
++					/*For BP UART problem End */
++
++/*For BP UART problem
++          TS0710_LOGSTR_FRAME(0, start_flag, framelen);
++          TS0710_DEBUGHEX(start_flag, framelen);
++	  ts0710_recv_data(&ts0710_connection, start_flag + 1, framelen - 2);
++*/
++					search = start_flag + framelen;
++				} else {
++					TS0710_LOGSTR_FRAME(0, start_flag,
++							    framelen);
++					TS0710_DEBUGHEX(start_flag, framelen);
++					TS0710_PRINTK
++					    ("MUX: Lost synchronization!\n");
++					search = start_flag + 1;
++				}
++
++				start_flag = 0;
++				framelen = -1;
++				continue;
++			}
++
++			if (start_flag != &tbuf[0]) {
++				to = tbuf;
++				from = start_flag;
++				count = tbuf_ptr - start_flag;
++				while (count--) {
++					*to++ = *from++;
++				}
++
++				tbuf_ptr -= (start_flag - tbuf);
++				start_flag = tbuf;
++			}
++			break;
++		}		/* End Frame Start Flag found */
++	}			/* End while(1) */
++
++	clear_bit(RECV_RUNNING, &mux_recv_flags);
++}
++
++static void post_recv_worker(void *private_)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	int tty_idx;
++	struct tty_struct *tty;
++	__u8 post_recv;
++	__u8 flow_control;
++	__u8 dlci;
++	mux_recv_struct *recv_info, *recv_info2, *post_recv_q;
++	int recv_room;
++	mux_recv_packet *recv_packet, *recv_packet2;
++	__u8 j;
++
++	UNUSED_PARAM(private_);
++
++	if (test_and_set_bit(RECV_RUNNING, &mux_recv_flags)) {
++		schedule_work(&post_recv_tqueue);
++		return;
++	}
++
++	TS0710_DEBUG("Enter into post_recv_worker");
++
++	post_recv = 0;
++	if (!mux_recv_queue) {
++		goto out;
++	}
++
++	post_recv_q = NULL;
++	recv_info2 = mux_recv_queue;
++	while ((recv_info = recv_info2)) {
++		recv_info2 = recv_info->next;
++
++		if (!(recv_info->total)) {
++			TS0710_PRINTK
++			    ("MUX Error: %s: Should not get here, recv_info->total == 0 \n",
++			     __FUNCTION__);
++			continue;
++		}
++
++		tty_idx = recv_info->no_tty;
++		dlci = tty2dlci[tty_idx];
++		tty = mux_table[tty_idx];
++		if ((!mux_tty[tty_idx]) || (!tty)) {
++			TS0710_PRINTK
++			    ("MUX: No application waiting for, free recv_info! tty_idx:%d\n",
++			     tty_idx);
++			mux_recv_info_flags[tty_idx] = 0;
++			free_mux_recv_struct(mux_recv_info[tty_idx]);
++			mux_recv_info[tty_idx] = 0;
++			ts0710_flow_on(dlci, ts0710);
++			continue;
++		}
++
++		TS0710_DEBUG("/dev/mux%d recv_info->total is: %d", tty_idx,
++			     recv_info->total);
++
++		if (test_bit(TTY_THROTTLED, &tty->flags)) {
++			add_post_recv_queue(&post_recv_q, recv_info);
++			continue;
++		} else if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
++			post_recv = 1;
++			add_post_recv_queue(&post_recv_q, recv_info);
++			continue;
++		}
++
++		flow_control = 0;
++		recv_packet2 = recv_info->mux_packet;
++		while (recv_info->total) {
++			recv_room = 65535;
++			if (tty->receive_room)
++				recv_room = tty->receive_room;
++
++			if (recv_info->length) {
++				if (recv_room < recv_info->length) {
++					flow_control = 1;
++					break;
++				}
++
++				/* Put queued data into read buffer of tty */
++				TS0710_DEBUG
++				    ("Put queued recv data into read buffer of /dev/mux%d",
++				     tty_idx);
++				TS0710_DEBUGHEX(recv_info->data,
++						recv_info->length);
++				(tty->ldisc.receive_buf) (tty, recv_info->data,
++							  NULL,
++							  recv_info->length);
++				recv_info->total -= recv_info->length;
++				recv_info->length = 0;
++			} else {	/* recv_info->length == 0 */
++				if ((recv_packet = recv_packet2)) {
++					recv_packet2 = recv_packet->next;
++
++					if (recv_room < recv_packet->length) {
++						flow_control = 1;
++						recv_info->mux_packet =
++						    recv_packet;
++						break;
++					}
++
++					/* Put queued data into read buffer of tty */
++					TS0710_DEBUG
++					    ("Put queued recv data into read buffer of /dev/mux%d",
++					     tty_idx);
++					TS0710_DEBUGHEX(recv_packet->data,
++							recv_packet->length);
++					(tty->ldisc.receive_buf) (tty,
++								  recv_packet->
++								  data, NULL,
++								  recv_packet->
++								  length);
++					recv_info->total -= recv_packet->length;
++					free_mux_recv_packet(recv_packet);
++				} else {
++					TS0710_PRINTK
++					    ("MUX Error: %s: Should not get here, recv_info->total is:%u \n",
++					     __FUNCTION__, recv_info->total);
++				}
++			}	/* End recv_info->length == 0 */
++		}		/* End while( recv_info->total ) */
++
++		if (!(recv_info->total)) {
++			/* Important clear */
++			recv_info->mux_packet = 0;
++
++			if (recv_info->post_unthrottle) {
++				/* Do something for post_unthrottle */
++				ts0710_flow_on(dlci, ts0710);
++				recv_info->post_unthrottle = 0;
++			}
++		} else {
++			add_post_recv_queue(&post_recv_q, recv_info);
++
++			if (flow_control) {
++				/* Do something for flow control */
++				if (recv_info->post_unthrottle) {
++					set_bit(TTY_THROTTLED, &tty->flags);
++					recv_info->post_unthrottle = 0;
++				} else {
++					ts0710_flow_off(tty, dlci, ts0710);
++				}
++			}	/* End if( flow_control ) */
++		}
++	}			/* End while( (recv_info = recv_info2) ) */
++
++	mux_recv_queue = post_recv_q;
++
++      out:
++	if (post_recv_count_flag) {
++		post_recv_count_flag = 0;
++		for (j = 0; j < TS0710MUX_COUNT_IDX_NUM; j++) {
++			if (mux_data_count2[j] > 0) {
++				if (add_count(j, mux_data_count2[j]) == 0) {
++					mux_data_count2[j] = 0;
++				} else {
++					post_recv_count_flag = 1;
++					post_recv = 1;
++				}
++			}
++		}		/* End for (j = 0; j < TS0710MUX_COUNT_IDX_NUM; j++) */
++	}
++	/* End if( post_recv_count_flag ) */
++	if (post_recv)
++		schedule_work(&post_recv_tqueue);
++	clear_bit(RECV_RUNNING, &mux_recv_flags);
++}
++
++/* mux sender, call from serial.c transmit_chars() */
++void mux_sender(void)
++{
++	mux_send_struct *send_info;
++	int chars;
++	__u8 idx;
++
++	chars = mux_chars_in_serial_buffer(COMM_FOR_MUX_TTY);
++	if (!chars) {
++		/* chars == 0 */
++		TS0710_LOG("<[]\n");
++		mux_sched_send();
++		return;
++	}
++
++	idx = mux_send_info_idx;
++	if ((idx < NR_MUXS) && (mux_send_info_flags[idx])) {
++		send_info = mux_send_info[idx];
++		if ((send_info)
++		    && (send_info->filled)
++		    && (send_info->length <=
++			(TS0710MUX_SERIAL_BUF_SIZE - chars))) {
++
++			mux_sched_send();
++		}
++	}
++}
++
++static void send_worker(void *private_)
++{
++	ts0710_con *ts0710 = &ts0710_connection;
++	__u8 j;
++	mux_send_struct *send_info;
++	int chars;
++	struct tty_struct *tty;
++	__u8 dlci;
++
++	UNUSED_PARAM(private_);
++
++	TS0710_DEBUG("Enter into send_worker");
++
++	mux_send_info_idx = NR_MUXS;
++
++	if (ts0710->dlci[0].state == FLOW_STOPPED) {
++		TS0710_DEBUG("Flow stopped on all channels\n");
++		return;
++	}
++
++	for (j = 0; j < NR_MUXS; j++) {
++
++		if (!(mux_send_info_flags[j])) {
++			continue;
++		}
++
++		send_info = mux_send_info[j];
++		if (!send_info) {
++			continue;
++		}
++
++		if (!(send_info->filled)) {
++			continue;
++		}
++
++		dlci = tty2dlci[j];
++		if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
++			TS0710_DEBUG("Flow stopped on channel DLCI: %d\n",
++				     dlci);
++			continue;
++		} else if (ts0710->dlci[dlci].state != CONNECTED) {
++			TS0710_DEBUG("DLCI %d not connected\n", dlci);
++			send_info->filled = 0;
++			continue;
++		}
++
++		chars = mux_chars_in_serial_buffer(COMM_FOR_MUX_TTY);
++		if (send_info->length <= (TS0710MUX_SERIAL_BUF_SIZE - chars)) {
++			TS0710_DEBUG("Send queued UIH for /dev/mux%d", j);
++			basic_write(ts0710, (__u8 *) send_info->frame,
++				    send_info->length);
++			send_info->length = 0;
++			send_info->filled = 0;
++		} else {
++			mux_send_info_idx = j;
++			break;
++		}
++	}			/* End for() loop */
++
++	/* Queue UIH data to be transmitted */
++	for (j = 0; j < NR_MUXS; j++) {
++
++		if (!(mux_send_info_flags[j])) {
++			continue;
++		}
++
++		send_info = mux_send_info[j];
++		if (!send_info) {
++			continue;
++		}
++
++		if (send_info->filled) {
++			continue;
++		}
++
++		/* Now queue UIH data to send_info->buf */
++
++		if (!mux_tty[j]) {
++			continue;
++		}
++
++		tty = mux_table[j];
++		if (!tty) {
++			continue;
++		}
++
++		dlci = tty2dlci[j];
++		if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
++			TS0710_DEBUG("Flow stopped on channel DLCI: %d\n",
++				     dlci);
++			continue;
++		} else if (ts0710->dlci[dlci].state != CONNECTED) {
++			TS0710_DEBUG("DLCI %d not connected\n", dlci);
++			continue;
++		}
++
++		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
++		    && tty->ldisc.write_wakeup) {
++			(tty->ldisc.write_wakeup) (tty);
++		}
++		wake_up_interruptible(&tty->write_wait);
++
++#ifdef SERIAL_HAVE_POLL_WAIT
++		wake_up_interruptible(&tty->poll_wait);
++#endif
++
++		if (send_info->filled) {
++			if (j < mux_send_info_idx) {
++				mux_send_info_idx = j;
++			}
++		}
++	}			/* End for() loop */
++}
++
++static int get_count(__u8 idx)
++{
++	int ret;
++
++	if (idx > TS0710MUX_COUNT_MAX_IDX) {
++		TS0710_PRINTK("MUX get_count: invalid idx: %d!\n", idx);
++		return -1;
++	}
++
++	down(&mux_data_count_mutex[idx]);
++	ret = mux_data_count[idx];
++	up(&mux_data_count_mutex[idx]);
++
++	return ret;
++}
++
++static int set_count(__u8 idx, int count)
++{
++	if (idx > TS0710MUX_COUNT_MAX_IDX) {
++		TS0710_PRINTK("MUX set_count: invalid idx: %d!\n", idx);
++		return -1;
++	}
++	if (count < 0) {
++		TS0710_PRINTK("MUX set_count: invalid count: %d!\n", count);
++		return -1;
++	}
++
++	down(&mux_data_count_mutex[idx]);
++	mux_data_count[idx] = count;
++	up(&mux_data_count_mutex[idx]);
++
++	return 0;
++}
++
++static int add_count(__u8 idx, int count)
++{
++	if (idx > TS0710MUX_COUNT_MAX_IDX) {
++		TS0710_PRINTK("MUX add_count: invalid idx: %d!\n", idx);
++		return -1;
++	}
++	if (count <= 0) {
++		TS0710_PRINTK("MUX add_count: invalid count: %d!\n", count);
++		return -1;
++	}
++
++	if (down_trylock(&mux_data_count_mutex[idx]))
++		return -1;
++	mux_data_count[idx] += count;
++	up(&mux_data_count_mutex[idx]);
++
++	return 0;
++}
++
++ssize_t file_proc_read(struct file * file, char *buf, size_t size,
++		       loff_t * ppos)
++{
++	gprs_bytes gprsData[TS0710MUX_GPRS_SESSION_MAX];
++	int bufLen = sizeof(gprs_bytes) * TS0710MUX_GPRS_SESSION_MAX;
++
++	UNUSED_PARAM(file);
++	UNUSED_PARAM(size);
++	UNUSED_PARAM(ppos);
++
++	gprsData[0].recvBytes = get_count(TS0710MUX_GPRS1_RECV_COUNT_IDX);
++	gprsData[0].sentBytes = get_count(TS0710MUX_GPRS1_SEND_COUNT_IDX);
++	gprsData[TS0710MUX_GPRS_SESSION_MAX - 1].recvBytes =
++	    get_count(TS0710MUX_GPRS2_RECV_COUNT_IDX);
++	gprsData[TS0710MUX_GPRS_SESSION_MAX - 1].sentBytes =
++	    get_count(TS0710MUX_GPRS2_SEND_COUNT_IDX);
++
++	copy_to_user(buf, gprsData, bufLen);
++
++	return bufLen;
++}
++
++ssize_t file_proc_write(struct file * file, const char *buf, size_t count,
++			loff_t * ppos)
++{
++	gprs_bytes gprsData[TS0710MUX_GPRS_SESSION_MAX];
++	int bufLen = sizeof(gprs_bytes) * TS0710MUX_GPRS_SESSION_MAX;
++
++	UNUSED_PARAM(file);
++	UNUSED_PARAM(count);
++	UNUSED_PARAM(ppos);
++
++	memset(gprsData, 0, bufLen);
++
++	copy_from_user(gprsData, buf, bufLen);
++
++	set_count(TS0710MUX_GPRS1_RECV_COUNT_IDX, gprsData[0].recvBytes);
++	set_count(TS0710MUX_GPRS1_SEND_COUNT_IDX, gprsData[0].sentBytes);
++	set_count(TS0710MUX_GPRS2_RECV_COUNT_IDX,
++		  gprsData[TS0710MUX_GPRS_SESSION_MAX - 1].recvBytes);
++	set_count(TS0710MUX_GPRS2_SEND_COUNT_IDX,
++		  gprsData[TS0710MUX_GPRS_SESSION_MAX - 1].sentBytes);
++
++	return bufLen;
++}
++
++static void gprs_proc_init(void)
++{
++	gprs_proc_file =
++	    create_proc_entry("gprsbytes", S_IRUSR | S_IWUSR, NULL);
++	gprs_proc_file->proc_fops = &file_proc_operations;
++}
++
++static void gprs_proc_exit(void)
++{
++	remove_proc_entry("gprsbytes", gprs_proc_file);
++}
++#endif
++
++static int __init mux_init(void)
++{
++	unsigned int j;
++
++	ts0710_init();
++#if 0
++	if (COMM_FOR_MUX_DRIVER == NULL) {
++
++#ifdef USB_FOR_MUX
++		panic("please install IPC-USB first\n");
++#else
++		panic("please install ttyS0 first\n");
++#endif
++
++	}
++
++	for (j = 0; j < NR_MUXS; j++) {
++		mux_send_info_flags[j] = 0;
++		mux_send_info[j] = 0;
++		mux_recv_info_flags[j] = 0;
++		mux_recv_info[j] = 0;
++	}
++	mux_send_info_idx = NR_MUXS;
++	mux_recv_queue = NULL;
++	mux_recv_flags = 0;
++
++	for (j = 0; j < TS0710MUX_COUNT_IDX_NUM; j++) {
++		mux_data_count[j] = 0;
++		mux_data_count2[j] = 0;
++		init_MUTEX(&mux_data_count_mutex[j]);
++	}
++	post_recv_count_flag = 0;
++
++	INIT_WORK(&send_tqueue, send_worker, NULL);
++	INIT_WORK(&receive_tqueue, receive_worker, NULL);
++	INIT_WORK(&post_recv_tqueue, post_recv_worker, NULL);
++#endif
++
++	memset(&mux_driver, 0, sizeof(struct tty_driver));
++	memset(&mux_tty, 0, sizeof(mux_tty));
++	mux_driver.magic = TTY_DRIVER_MAGIC;
++	mux_driver.driver_name = "ts0710mux";
++	mux_driver.name = "ts0710mux";
++	mux_driver.major = TS0710MUX_MAJOR;
++	mux_driver.minor_start = TS0710MUX_MINOR_START;
++	mux_driver.num = NR_MUXS;
++	mux_driver.type = TTY_DRIVER_TYPE_SERIAL;
++	mux_driver.subtype = SERIAL_TYPE_NORMAL;
++	mux_driver.init_termios = tty_std_termios;
++	mux_driver.init_termios.c_iflag = 0;
++	mux_driver.init_termios.c_oflag = 0;
++	mux_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
++	mux_driver.init_termios.c_lflag = 0;
++	mux_driver.flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
++
++	mux_driver.ttys = mux_table;
++	mux_driver.termios = mux_termios;
++	mux_driver.termios_locked = mux_termios_locked;
++//  mux_driver.driver_state = mux_state;
++	mux_driver.other = NULL;
++
++	mux_driver.open = mux_open;
++	mux_driver.close = mux_close;
++	mux_driver.write = mux_write;
++	mux_driver.write_room = mux_write_room;
++	mux_driver.flush_buffer = mux_flush_buffer;
++	mux_driver.chars_in_buffer = mux_chars_in_buffer;
++	mux_driver.throttle = mux_throttle;
++	mux_driver.unthrottle = mux_unthrottle;
++	mux_driver.ioctl = mux_ioctl;
++	mux_driver.owner = THIS_MODULE;
++
++	if (tty_register_driver(&mux_driver))
++		panic("Couldn't register mux driver");
++
++#if 0
++	COMM_MUX_DISPATCHER = mux_dispatcher;
++	COMM_MUX_SENDER = mux_sender;
++
++	gprs_proc_init();
++#endif
++
++	return 0;
++}
++
++static void __exit mux_exit(void)
++{
++	int j;
++
++#if 0
++	COMM_MUX_DISPATCHER = NULL;
++	COMM_MUX_SENDER = NULL;
++
++	gprs_proc_exit();
++
++	mux_send_info_idx = NR_MUXS;
++	mux_recv_queue = NULL;
++	for (j = 0; j < NR_MUXS; j++) {
++		if ((mux_send_info_flags[j]) && (mux_send_info[j])) {
++			kfree(mux_send_info[j]);
++		}
++		mux_send_info_flags[j] = 0;
++		mux_send_info[j] = 0;
++
++		if ((mux_recv_info_flags[j]) && (mux_recv_info[j])) {
++			free_mux_recv_struct(mux_recv_info[j]);
++		}
++		mux_recv_info_flags[j] = 0;
++		mux_recv_info[j] = 0;
++	}
++#endif
++
++	if (tty_unregister_driver(&mux_driver))
++		panic("Couldn't unregister mux driver");
++}
++
++module_init(mux_init);
++module_exit(mux_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge at openezx.org>");
++MODULE_DESCRIPTION("GSM TS 07.10 Multiplexer");
+Index: linux-2.6.17.14-fic2/drivers/char/ts0710_mux.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic2/drivers/char/ts0710_mux.h	2006-12-02 11:21:56.000000000 +0100
+@@ -0,0 +1,103 @@
++/*
++ * mux_macro.h
++ *
++ * Copyright (C) 2002 2005 Motorola
++ *
++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ *
++ *  11/18/2002  (Motorola) - Initial version
++ *
++ */
++
++/*
++* This header file should be included by both MUX and other applications
++* which access MUX device files. It gives the additional macro definitions
++* shared between MUX and applications.
++*/
++
++/* MUX DLCI(Data Link Connection Identifier) Configuration */
++/*
++*  DLCI     Service
++*   0    Control Channel
++*   1    Voice Call & Network-related
++*   2    SMS MO
++*   3    SMS MT
++*   4    Phonebook & related
++*   5    MISC
++*   6    CSD/FAX
++*   7    GPRS1
++*   8    GPRS2
++*   9    Logger CMD
++*   10   Logger Data
++*   11   Test CMD
++*   12   AGPS
++*   13   Net Monitor
++*/
++
++/* Mapping between DLCI and MUX device files */
++/*
++*   File Name   Minor  DLCI  AT Command/Data
++*   /dev/mux0     0     1     AT Command
++*   /dev/mux1     1     2     AT Command
++*   /dev/mux2     2     3     AT Command
++*   /dev/mux3     3     4     AT Command
++*   /dev/mux4     4     5     AT Command
++*   /dev/mux5     5     6     AT Command
++*   /dev/mux6     6     7     AT Command
++*   /dev/mux7     7     8     AT Command
++*   /dev/mux8     8     6     Data
++*   /dev/mux9     9     7     Data
++*   /dev/mux10    10    8     Data
++*   /dev/mux11    11    9     Data
++*   /dev/mux12    12    10    Data
++*   /dev/mux13    13    11    Data
++*   /dev/mux14    14    12    Data
++*   /dev/mux15    15    13    Data
++*/
++
++#define MUX_CMD_FILE_VOICE_CALL   "/dev/mux0"
++#define MUX_CMD_FILE_SMS_MO       "/dev/mux1"
++#define MUX_CMD_FILE_SMS_MT       "/dev/mux2"
++#define MUX_CMD_FILE_PHONEBOOK    "/dev/mux3"
++#define MUX_CMD_FILE_MISC         "/dev/mux4"
++#define MUX_CMD_FILE_CSD          "/dev/mux5"
++#define MUX_CMD_FILE_GPRS1        "/dev/mux6"
++#define MUX_CMD_FILE_GPRS2        "/dev/mux7"
++
++#define MUX_DATA_FILE_CSD         "/dev/mux8"
++#define MUX_DATA_FILE_GPRS1       "/dev/mux9"
++#define MUX_DATA_FILE_GPRS2       "/dev/mux10"
++#define MUX_DATA_FILE_LOGGER_CMD  "/dev/mux11"
++#define MUX_DATA_FILE_LOGGER_DATA "/dev/mux12"
++#define MUX_DATA_FILE_TEST_CMD    "/dev/mux13"
++#define MUX_DATA_FILE_AGPS        "/dev/mux14"
++#define MUX_DATA_FILE_NET_MONITOR "/dev/mux15"
++
++#define NUM_MUX_CMD_FILES 8
++#define NUM_MUX_DATA_FILES 8
++#define NUM_MUX_FILES ( NUM_MUX_CMD_FILES  +  NUM_MUX_DATA_FILES )
++
++/* Special ioctl() upon a MUX device file for hanging up a call */
++#define TS0710MUX_IO_MSC_HANGUP 0x54F0
++
++/* Special ioctl() upon a MUX device file for MUX loopback test */
++#define TS0710MUX_IO_TEST_CMD 0x54F1
++
++/* Special Error code might be return from write() to a MUX device file  */
++#define EDISCONNECTED 900	/* Logical data link is disconnected */
++
++/* Special Error code might be return from open() to a MUX device file  */
++#define EREJECTED 901		/* Logical data link connection request is rejected */
+Index: linux-2.6.17.14-fic2/drivers/char/ts0710_mux_usb.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic2/drivers/char/ts0710_mux_usb.c	2006-12-02 11:21:56.000000000 +0100
+@@ -0,0 +1,868 @@
++/*
++ * linux/drivers/usb/ipcusb.c
++ *
++ * Implementation of a ipc driver based Intel's Bulverde USB Host 
++ * Controller.
++ *
++ * Copyright (C) 2003-2005 Motorola
++ * Copyright (C) 2006 Harald Welte <laforge at openezx.org>
++ *
++ *  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
++ * 
++ *  2003-Nov-03 - (Motorola) created
++ *  2004-Feb-20 - (Motorola) Add Power Manager codes
++ *  2004-Apr-14 - (Motorola) Update Suspend/Resume codes
++ *  2004-May-10 - (Motorola) Add unlink_urbs codes and do some updates of send
++ *			     out urb sequence
++ *  2006-Jun-22 - (Harald Welte) port to Linux 2.6.x
++ *  
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/errno.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch-pxa/pxa-regs.h>
++#include <asm/arch-pxa/ezx.h>
++#include <linux/slab.h>
++#include <linux/miscdevice.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/tty.h>
++#include <linux/tty_driver.h>
++#include <linux/tty_flip.h>
++#include <linux/circ_buf.h>
++#include <linux/usb.h>
++
++#include "ts0710_mux_usb.h"
++
++/*Macro defined for this driver*/
++#define DRIVER_VERSION "1.0alpha1"
++#define DRIVER_AUTHOR "Motorola / Harald Welte <laforge at openezx.org>"
++#define DRIVER_DESC "USB IPC Driver (TS07.10 lowlevel)"
++#define MOTO_IPC_VID		0x22b8
++#define MOTO_IPC_PID		0x3006
++#define IBUF_SIZE 		32		/*urb size*/	
++#define IPC_USB_XMIT_SIZE	1024
++#define IPC_URB_SIZE		32	
++#define IPC_USB_WRITE_INIT 	0
++#define IPC_USB_WRITE_XMIT	1
++#define IPC_USB_PROBE_READY	3
++#define IPC_USB_PROBE_NOT_READY	4
++#define DBG_MAX_BUF_SIZE	1024
++#define ICL_EVENT_INTERVAL	(HZ) 
++#undef BVD_DEBUG		
++
++#define IS_EP_BULK(ep)  ((ep).bmAttributes == USB_ENDPOINT_XFER_BULK ? 1 : 0)
++#define IS_EP_BULK_IN(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
++#define IS_EP_BULK_OUT(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
++/*End defined macro*/
++
++/*global values defined*/
++static struct usb_driver 		usb_ipc_driver;
++static struct timer_list 		ipcusb_timer;
++static struct timer_list 		suspend_timer;
++static struct timer_list 		wakeup_timer;
++static struct tty_struct		ipcusb_tty;		/* the coresponding tty struct, we just use flip buffer here. */
++static struct tty_driver		ipcusb_tty_driver;	/* the coresponding tty driver, we just use write and chars in buff here*/
++struct tty_driver *usb_for_mux_driver = NULL;
++struct tty_struct *usb_for_mux_tty = NULL;
++void (*usb_mux_dispatcher)(struct tty_struct *tty) = NULL;
++void (*usb_mux_sender)(void) = NULL;
++void (*ipcusb_ap_to_bp)(unsigned char*, int) = NULL;
++void (*ipcusb_bp_to_ap)(unsigned char*, int) = NULL;
++EXPORT_SYMBOL(usb_for_mux_driver);	
++EXPORT_SYMBOL(usb_for_mux_tty);	
++EXPORT_SYMBOL(usb_mux_dispatcher);	
++EXPORT_SYMBOL(usb_mux_sender);
++EXPORT_SYMBOL(ipcusb_ap_to_bp);
++EXPORT_SYMBOL(ipcusb_bp_to_ap);
++static int sumbit_times = 0; 	
++static int callback_times = 0; 
++//static unsigned long last_jiff = 0;
++extern int usbh_finished_resume;
++/*end global values defined*/
++
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_LICENSE("GPL");
++
++#ifdef BVD_DEBUG
++#define bvd_dbg(format, arg...) printk(__FILE__ ": " format "\n" , ## arg)
++#else
++#define bvd_dbg(format, arg...) do {} while (0)
++#endif
++
++/* USB device context */
++typedef struct {
++	struct list_head list;
++	int size;
++	char *body; 
++} buf_list_t;
++
++struct ipc_usb_data {
++	u_int8_t 		write_finished_flag;
++	u_int8_t		write_flag,
++				ipc_flag,
++				suspend_flag;
++	struct usb_device 	*ipc_dev;
++	struct urb 		readurb_mux,
++				writeurb_mux,
++				writeurb_dsplog;
++	char 			*obuf, *ibuf;
++	int			writesize;	/* max packet size for the
++						   output bulk endpoint *
++						   transfer buffers */
++
++	struct circ_buf		xmit;		/* write cric bufffer */
++  	struct list_head 	in_buf_list;
++	char 			bulk_in_ep_mux, 
++				bulk_out_ep_mux, 
++				bulk_in_ep_dsplog;
++	unsigned int 		ifnum;
++
++	struct tasklet_struct	bh,
++				bh_bp;
++
++	spinlock_t		lock;
++}; 
++
++struct ipc_usb_data *bvd_ipc;
++
++#ifdef BVD_DEBUG
++static void bvd_dbg_hex(__u8 *buf, int len)
++{
++	static unsigned char tbuf[DBG_MAX_BUF_SIZE];
++	int i, c;
++
++	if (len <= 0)
++		return;
++
++	c = 0;  
++	for (i=0; (i < len) && (c < (DBG_MAX_BUF_SIZE - 3)); i++) {
++		sprintf(&tbuf[c], "%02x ",buf[i]);
++		c += 3;
++	}
++	tbuf[c] = 0;
++
++	printk("%s: %s\n", __FUNCTION__, tbuf);
++}
++#else
++#define bvd_dbg_hex(buf, len)
++#endif
++
++static int unlink_urbs(struct urb *urb)
++{	
++	unsigned long flags;
++	int retval;
++
++	spin_lock_irqsave(&bvd_ipc->lock, flags);
++	
++	retval = usb_unlink_urb(urb);
++	if (retval != -EINPROGRESS && retval != 0)
++		printk("unlink urb err, %d", retval);
++	
++	spin_unlock_irqrestore(&bvd_ipc->lock, flags);
++	return retval;
++}
++
++static void append_to_inbuf_list(struct urb *urb)
++{
++	buf_list_t *inbuf;
++	int count = urb->actual_length;
++	
++	inbuf = kmalloc(sizeof(buf_list_t), GFP_KERNEL);
++	if (!inbuf) {
++		printk("append_to_inbuf_list: (%d) out of memory!\n",
++			sizeof(buf_list_t));
++		return;
++	}
++	
++	inbuf->size = count;
++	inbuf->body = kmalloc(sizeof(char)*count, GFP_KERNEL);
++	if (!inbuf->body) {
++		kfree(inbuf);
++		printk("append_to_inbuf_list: (%d) out of memory!\n",
++			sizeof(char)*count);
++		return;
++	}
++	memcpy(inbuf->body, (unsigned char*)urb->transfer_buffer, count);
++	list_add_tail(&inbuf->list, &bvd_ipc->in_buf_list);
++}
++
++static void ipcusb_timeout(unsigned long data)
++{
++	struct tty_struct *tty = &ipcusb_tty;	
++	struct urb *urb = (struct urb *)data;
++	
++	bvd_dbg("ipcusb_timeout***");
++
++	while (!(list_empty(&bvd_ipc->in_buf_list))) {
++		int count;
++		buf_list_t *inbuf;
++		struct list_head *ptr = NULL;			
++		
++		ptr = bvd_ipc->in_buf_list.next;
++		inbuf = list_entry (ptr, buf_list_t, list);			
++		count = inbuf->size;
++		if (tty_insert_flip_string(tty, inbuf->body, count) >= count) {
++			list_del(ptr);
++			kfree(inbuf->body);
++			inbuf->body = NULL;
++			kfree(inbuf);				
++		} else {
++			bvd_dbg("ipcusb_timeout: bvd_ipc->in_buf_list empty!");
++			break;
++		}
++	}
++
++	if (usb_mux_dispatcher)
++		usb_mux_dispatcher(tty);	/**call Liu changhui's func.**/	
++	
++	if (list_empty(&bvd_ipc->in_buf_list)) {
++		urb->actual_length = 0;
++		urb->dev = bvd_ipc->ipc_dev;
++		if (usb_submit_urb(urb, GFP_ATOMIC))
++			bvd_dbg("ipcusb_timeout: failed resubmitting read urb");
++		bvd_dbg("ipcusb_timeout: resubmited read urb");
++	} else {
++		ipcusb_timer.data = (unsigned long)urb;
++		mod_timer(&ipcusb_timer, jiffies+(10*HZ/1000));
++	}
++}
++
++static void usb_ipc_read_bulk(struct urb *urb, struct pt_regs *regs)
++{	
++	buf_list_t *inbuf;
++	int count = urb->actual_length;
++	struct tty_struct *tty = &ipcusb_tty;	
++	
++ 	bvd_dbg("usb_ipc_read_bulk: begining!");
++	if (urb->status)
++		printk("nonzero read bulk status received: %d\n", urb->status);
++	
++ 	bvd_dbg("usb_ipc_read_bulk: urb->actual_length=%d", urb->actual_length);
++ 	bvd_dbg("usb_ipc_read_bulk: urb->transfer_buffer:");	
++
++	bvd_dbg_hex((unsigned char*)urb->transfer_buffer, urb->actual_length);
++
++	if (count > 0 && ((*ipcusb_bp_to_ap) != NULL))
++		(*ipcusb_bp_to_ap)(urb->transfer_buffer, urb->actual_length);
++ 
++ 	if (!(list_empty(&bvd_ipc->in_buf_list))) {
++		int need_mux = 0;
++
++ 		bvd_dbg("usb_ipc_read_bulk: some urbs in_buf_list");	
++		if (count > 0) {
++			bvd_ipc->suspend_flag = 1;
++			append_to_inbuf_list(urb); /* append the current received urb */
++#if 0		
++			if(jiffies - last_jiff > ICL_EVENT_INTERVAL) 
++			{
++				last_jiff = jiffies;
++				queue_apm_event(KRNL_ICL, NULL);
++			}	
++#endif
++		}
++		
++		while (!(list_empty(&bvd_ipc->in_buf_list))) {
++			struct list_head* ptr = NULL;	
++			ptr = bvd_ipc->in_buf_list.next;
++			inbuf = list_entry(ptr, buf_list_t, list);			
++			count = inbuf->size;
++			need_mux = 1;
++
++			tty_insert_flip_string(tty, inbuf->body, count);
++
++			list_del(ptr);
++			kfree(inbuf->body);
++			inbuf->body = NULL;
++			kfree(inbuf);				
++		}	
++
++		if (usb_mux_dispatcher && need_mux) 
++			usb_mux_dispatcher(tty); /* call Liu changhui's func. */
++
++		if (list_empty(&bvd_ipc->in_buf_list)) {
++			urb->actual_length = 0;
++			urb->dev = bvd_ipc->ipc_dev;
++			if (usb_submit_urb(urb, GFP_ATOMIC))
++				bvd_dbg("usb_ipc_read_bulk: "
++					"failed resubmitting read urb");
++			bvd_dbg("usb_ipc_read_bulk: resubmited read urb");
++		} else {
++			ipcusb_timer.data = (unsigned long)urb;
++			mod_timer(&ipcusb_timer, jiffies+(10*HZ/1000));
++		}
++	} else if (count > 0) {
++ 		bvd_dbg("usb_ipc_read_bulk: no urbs in_buf_list");	
++		bvd_ipc->suspend_flag = 1;
++
++		if (tty_insert_flip_string(tty, urb->transfer_buffer, 
++					   count) < count) {
++			bvd_ipc->suspend_flag = 1;
++			append_to_inbuf_list(urb);
++			ipcusb_timer.data = (unsigned long)urb;
++			mod_timer(&ipcusb_timer, jiffies+(10*HZ/1000));
++#if 0			
++			if(jiffies - last_jiff > ICL_EVENT_INTERVAL) 
++			{
++				last_jiff = jiffies;
++				queue_apm_event(KRNL_ICL, NULL);
++			}
++#endif	
++		}
++
++		if (usb_mux_dispatcher)
++			usb_mux_dispatcher(tty); /* call Liu changhui's func. */
++
++		urb->actual_length = 0;
++		urb->dev = bvd_ipc->ipc_dev;
++		if (usb_submit_urb(urb, GFP_ATOMIC))
++			bvd_dbg("failed resubmitting read urb");
++#if 0
++		if(jiffies - last_jiff > ICL_EVENT_INTERVAL) 
++		{
++			last_jiff = jiffies;
++			queue_apm_event(KRNL_ICL, NULL);
++		}
++#endif	
++		bvd_dbg("usb_ipc_read_bulk: resubmited read urb");
++	}
++
++	bvd_dbg("usb_ipc_read_bulk: completed!!!");		
++}
++
++static void usb_ipc_write_bulk(struct urb *urb, struct pt_regs *regs)
++{
++	callback_times++;
++	bvd_ipc->write_finished_flag = 1;
++	
++	bvd_dbg("usb_ipc_write_bulk: begining!");
++	//printk("%s: write_finished_flag=%d\n", __FUNCTION__, bvd_ipc->write_finished_flag);
++	
++	if (urb->status)
++		printk("nonzero write bulk status received: %d\n", urb->status);
++
++	if (usb_mux_sender)
++		usb_mux_sender();		/**call Liu changhui's func**/
++
++	//printk("usb_ipc_write_bulk: mark ipcusb_softint!\n");
++	tasklet_schedule(&bvd_ipc->bh);
++
++	bvd_dbg("usb_ipc_write_bulk: finished!");
++}
++
++static void wakeup_timeout(unsigned long data)
++{
++	GPSR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
++	bvd_dbg("wakup_timeout: send GPIO_MCU_INT_SW signal!");	
++}
++
++static void suspend_timeout(unsigned long data)
++{
++	if (bvd_ipc->suspend_flag == 1) {
++		bvd_ipc->suspend_flag = 0;
++		mod_timer(&suspend_timer, jiffies+(5000*HZ/1000));
++		bvd_dbg("suspend_timeout: add the suspend timer again");	
++	} else {
++		unlink_urbs(&bvd_ipc->readurb_mux);
++		UHCRHPS3 = 0x4;
++		mdelay(40);
++		bvd_dbg("suspend_timeout: send SUSPEND signal! UHCRHPS3=0x%x",
++			UHCRHPS3);
++	}
++}
++
++static void ipcusb_xmit_data(void)
++{	
++	int c, count = IPC_URB_SIZE;
++	int result = 0;
++	int buf_flag = 0;
++	int buf_num = 0;
++
++	//printk("%s: sumbit_times=%d, callback_times=%d\n", __FUNCTION__, sumbit_times, callback_times);
++	if (bvd_ipc->write_finished_flag == 0)
++		return;
++ 
++	while (1) {
++		c = CIRC_CNT_TO_END(bvd_ipc->xmit.head, bvd_ipc->xmit.tail, 
++				    IPC_USB_XMIT_SIZE);
++		if (count < c)
++			c = count;
++		if (c <= 0)
++			break;
++
++		memcpy(bvd_ipc->obuf+buf_num, 
++		       bvd_ipc->xmit.buf + bvd_ipc->xmit.tail, c);
++		buf_flag = 1;	
++		bvd_ipc->xmit.tail = ((bvd_ipc->xmit.tail + c) 
++						& (IPC_USB_XMIT_SIZE-1));
++		count -= c;
++		buf_num += c;
++	}    
++
++	if (buf_num == 0) {
++		bvd_dbg("ipcusb_xmit_data: buf_num=%d, add suspend_timer",
++			buf_num);
++		bvd_ipc->suspend_flag = 0;
++		mod_timer(&suspend_timer, jiffies+(5000*HZ/1000));
++	}
++
++	bvd_dbg("ipcusb_xmit_data: buf_num=%d", buf_num);
++	bvd_dbg("ipcusb_xmit_data: bvd_ipc->obuf: ");
++
++	bvd_dbg_hex((bvd_ipc->obuf)-buf_num, buf_num);
++
++	if (buf_flag) {
++		bvd_ipc->writeurb_mux.transfer_buffer_length = buf_num;
++		bvd_dbg("ipcusb_xmit_data: copy data to write urb finished! ");
++		
++		if ((UHCRHPS3 & 0x4) == 0x4) {
++			static int ret;
++			int time = 0;	
++
++			/* if BP sleep, wake up BP first */
++			pxa_gpio_mode(GPIO_IN | 41);
++			if (GPIO_is_high(41)) {
++				if (GPIO_is_high(GPIO_MCU_INT_SW))
++					GPCR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
++				else
++					GPSR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
++
++				time = jiffies;
++				while (GPIO_is_high(41) && (jiffies < (time+HZ)));
++
++				if (GPIO_is_high(41)) {
++					printk("%s: Wakeup BP timeout! BP state is %d\n",
++						__FUNCTION__, GPIO_is_high(41));
++				}
++				if (GPIO_is_high(GPIO_MCU_INT_SW))
++					GPCR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
++				else
++					GPSR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
++			}
++
++			/* Resume BP */ 
++			UHCRHPS3 = 0x8;
++			mdelay(40);
++			bvd_dbg("ipcusb_xmit_data: Send RESUME signal! UHCRHPS3=0x%x",
++				 UHCRHPS3);
++			/*send IN token*/
++			bvd_ipc->readurb_mux.actual_length = 0;
++			bvd_ipc->readurb_mux.dev = bvd_ipc->ipc_dev;
++			if (ret = usb_submit_urb(&bvd_ipc->readurb_mux, GFP_ATOMIC))
++				printk("ipcusb_xmit_data: usb_submit_urb(read mux bulk)"
++					"failed! status=%d\n", ret);
++			bvd_dbg("ipcusb_xmit_data: Send a IN token successfully!");
++		}
++
++		sumbit_times++;
++		bvd_ipc->write_finished_flag = 0;
++		//printk("%s: clear write_finished_flag:%d\n", __FUNCTION__, bvd_ipc->write_finished_flag);
++		bvd_ipc->writeurb_mux.dev = bvd_ipc->ipc_dev;
++		if (result = usb_submit_urb(&bvd_ipc->writeurb_mux, GFP_ATOMIC))
++			warn("ipcusb_xmit_data: funky result! result=%d\n", result);
++
++		bvd_dbg("ipcusb_xmit_data: usb_submit_urb finished! result:%d", result);
++	
++	}		
++}
++
++static void usbipc_bh_func(unsigned long param)
++{
++	ipcusb_xmit_data();
++}
++
++extern void get_halted_bit(void);
++
++static void usbipc_bh_bp_func(unsigned long param)
++{
++	if ((UHCRHPS3 & 0x4) == 0x4) {
++		UHCRHPS3 = 0x8;
++		mdelay(40);
++		bvd_dbg("ipcusb_softint_send_readurb: Send RESUME signal! "
++			"UHCRHPS3=0x%x", UHCRHPS3);
++	}
++	if (bvd_ipc->ipc_flag == IPC_USB_PROBE_READY) { 
++		get_halted_bit();
++
++		/*send a IN token*/
++		bvd_ipc->readurb_mux.dev = bvd_ipc->ipc_dev;
++		if (usb_submit_urb(&bvd_ipc->readurb_mux, GFP_ATOMIC)) {
++			bvd_dbg("ipcusb_softint_send_readurb: "
++				"usb_submit_urb(read mux bulk) failed!");
++		}
++		bvd_dbg("ipcusb_softint_send_readurb: Send a IN token successfully!");
++		bvd_ipc->suspend_flag = 0;
++		bvd_dbg("ipcusb_softint_send_readurb: add suspend_timer");
++		mod_timer(&suspend_timer, jiffies+(5000*HZ/1000));		
++	}
++}
++
++static int usb_ipc_write(struct tty_struct *tty, 
++			 const unsigned char *buf, int count)
++{	
++	int c, ret = 0;
++
++	bvd_dbg("usb_ipc_write: count=%d, buf: ", count);
++	bvd_dbg_hex(buf, count);
++
++	if (count <= 0)
++		return 0;
++
++	if (*ipcusb_ap_to_bp != NULL)
++		(*ipcusb_ap_to_bp)(buf, count);
++
++	bvd_ipc->suspend_flag = 1;
++	
++	if ((bvd_ipc->ipc_flag == IPC_USB_PROBE_READY) && 
++	    (bvd_ipc->xmit.head == bvd_ipc->xmit.tail)) {
++		bvd_dbg("usb_ipc_write: set write_flag");
++		bvd_ipc->write_flag = IPC_USB_WRITE_XMIT;
++	}
++		
++	while (1) {
++		c = CIRC_SPACE_TO_END(bvd_ipc->xmit.head, 
++				      bvd_ipc->xmit.tail, IPC_USB_XMIT_SIZE);
++		if (count < c)
++			c = count;
++		if (c <= 0)
++			break;
++
++		memcpy(bvd_ipc->xmit.buf + bvd_ipc->xmit.head, buf, c);
++		bvd_ipc->xmit.head = ((bvd_ipc->xmit.head + c) 
++						& (IPC_USB_XMIT_SIZE-1));
++		buf += c;
++		count -= c;
++		ret += c;
++	}
++	bvd_dbg("usb_ipc_write: ret=%d, bvd_ipc->xmit.buf: ", ret);
++
++	bvd_dbg_hex(bvd_ipc->xmit.buf, ret);
++
++	if (bvd_ipc->write_flag == IPC_USB_WRITE_XMIT) {
++		bvd_ipc->write_flag = IPC_USB_WRITE_INIT;
++		bvd_dbg("usb_ipc_write: mark ipcusb_softint");
++		tasklet_schedule(&bvd_ipc->bh);
++	}
++
++	bvd_dbg("usb_ipc_write: ret=%d\n", ret);
++	return ret;
++}
++
++static int usb_ipc_chars_in_buffer(struct tty_struct *tty)
++{		
++	return CIRC_CNT(bvd_ipc->xmit.head, bvd_ipc->xmit.tail, IPC_USB_XMIT_SIZE);
++}
++
++void usb_send_readurb(void)
++{
++	//printk("usb_send_readurb: begining!UHCRHPS3=0x%x, usbh_finished_resume=%d\n", UHCRHPS3, usbh_finished_resume);
++	
++	if (usbh_finished_resume == 0)
++		return;
++
++	tasklet_schedule(&bvd_ipc->bh_bp);
++}
++
++static int usb_ipc_probe(struct usb_interface *intf,
++			 const struct usb_device_id *id)
++{	
++	struct usb_device *usbdev = interface_to_usbdev(intf);
++	struct usb_config_descriptor *ipccfg;
++	struct usb_interface_descriptor *interface;
++	struct usb_endpoint_descriptor *endpoint;		
++	int ep_cnt, readsize, writesize;
++	char have_bulk_in_mux, have_bulk_out_mux;
++
++	bvd_dbg("usb_ipc_probe: vendor id 0x%x, device id 0x%x",
++		usbdev->descriptor.idVendor, usbdev->descriptor.idProduct);
++
++	if ((usbdev->descriptor.idVendor != MOTO_IPC_VID) ||
++	    (usbdev->descriptor.idProduct != MOTO_IPC_PID))
++ 		return -ENODEV;
++
++	/* a2590c : dsplog interface is not supported by this driver */
++	if (intf->minor == 2)	/* dsplog interface number is 2 */
++		return -1;
++
++	bvd_dbg("usb_ipc_probe: USB dev address:%p", usbdev);
++	bvd_dbg("usb_ipc_probe: ifnum:%u", intf->minor);
++	
++	ipccfg = &usbdev->actconfig->desc;
++	bvd_dbg("usb_ipc_prob: config%d", ipccfg->bConfigurationValue);
++	bvd_dbg("usb_ipc_prob: bNumInterfaces = %d", ipccfg->bNumInterfaces);
++
++	/* After this point we can be a little noisy about what we are trying
++	 * to configure, hehe.  */
++	if (usbdev->descriptor.bNumConfigurations != 1) {
++		info("usb_ipc_probe: Only one device configuration "
++		     "is supported.");
++		return -1;
++	}
++
++	if (usbdev->config[0].desc.bNumInterfaces != 3) {
++		info("usb_ipc_probe: Only three device interfaces are "
++		     "supported.");
++		return -1;
++	}
++	
++	interface = &intf->cur_altsetting->desc;
++	endpoint = &intf->cur_altsetting->endpoint[0].desc;
++	/* Start checking for two bulk endpoints or ... FIXME: This is a future
++	 * enhancement...*/
++	bvd_dbg("usb_ipc_probe: Number of Endpoints:%d", 
++		(int) interface->bNumEndpoints);
++	if (interface->bNumEndpoints != 2) {
++		info("usb_ipc_probe: Only two endpoints supported.");
++		return -1;
++	}
++
++	ep_cnt = have_bulk_in_mux = have_bulk_out_mux = 0;	
++
++	bvd_dbg("usb_ipc_probe: endpoint[0] is:%x",
++		(&endpoint[0])->bEndpointAddress);
++	bvd_dbg("usb_ipc_probe: endpoint[1] is:%x ",
++		(&endpoint[1])->bEndpointAddress);
++	
++	while (ep_cnt < interface->bNumEndpoints) {
++
++		if (!have_bulk_in_mux && IS_EP_BULK_IN(endpoint[ep_cnt])) {
++			bvd_dbg("usb_ipc_probe: bEndpointAddress(IN) is:%x ", 
++				(&endpoint[ep_cnt])->bEndpointAddress);
++			have_bulk_in_mux = 
++					(&endpoint[ep_cnt])->bEndpointAddress;
++			readsize = (&endpoint[ep_cnt])->wMaxPacketSize;	
++			bvd_dbg("usb_ipc_probe: readsize=%d", readsize);			
++			ep_cnt++;
++			continue;
++		}
++
++		if (!have_bulk_out_mux && IS_EP_BULK_OUT(endpoint[ep_cnt])) {
++			bvd_dbg("usb_ipc_probe: bEndpointAddress(OUT) is:%x ",
++				(&endpoint[ep_cnt])->bEndpointAddress);
++			have_bulk_out_mux =
++				(&endpoint[ep_cnt])->bEndpointAddress;
++			writesize = (&endpoint[ep_cnt])->wMaxPacketSize;
++			bvd_dbg("usb_ipc_probe: writesize=%d", writesize);			
++			ep_cnt++;
++			continue;
++		}
++		
++		info("usb_ipc_probe: Undetected endpoint ^_^ ");
++		/* Shouldn't ever get here unless we have something weird */
++		return -1;
++	}
++
++	/* Perform a quick check to make sure that everything worked as it
++	 * should have.  */
++
++	switch (interface->bNumEndpoints) {
++	case 2:
++		if (!have_bulk_in_mux || !have_bulk_out_mux) {
++			info("usb_ipc_probe: Two bulk endpoints required.");
++			return -1;
++		}
++		break;	
++	default:
++		info("usb_ipc_probe: Endpoint determination failed ^_^ ");
++		return -1;
++	}	
++
++	/* Ok, now initialize all the relevant values */
++	if (!(bvd_ipc->obuf = (char *)kmalloc(writesize, GFP_KERNEL))) {
++		err("usb_ipc_probe: Not enough memory for the output buffer.");
++		kfree(bvd_ipc);		
++		return -1;
++	}
++	bvd_dbg("usb_ipc_probe: obuf address:%p", bvd_ipc->obuf);
++
++	if (!(bvd_ipc->ibuf = (char *)kmalloc(readsize, GFP_KERNEL))) {
++		err("usb_ipc_probe: Not enough memory for the input buffer.");
++		kfree(bvd_ipc->obuf);
++		kfree(bvd_ipc);		
++		return -1;
++	}
++	bvd_dbg("usb_ipc_probe: ibuf address:%p", bvd_ipc->ibuf);
++
++	bvd_ipc->ipc_flag = IPC_USB_PROBE_READY;	
++	bvd_ipc->write_finished_flag = 1;
++	bvd_ipc->suspend_flag = 1;
++	bvd_ipc->bulk_in_ep_mux= have_bulk_in_mux;
++	bvd_ipc->bulk_out_ep_mux= have_bulk_out_mux;		
++	bvd_ipc->ipc_dev = usbdev;	
++	bvd_ipc->writesize = writesize;	
++	INIT_LIST_HEAD (&bvd_ipc->in_buf_list);
++
++	bvd_ipc->bh.func = usbipc_bh_func;
++	bvd_ipc->bh.data = (unsigned long) bvd_ipc;
++
++	bvd_ipc->bh_bp.func = usbipc_bh_bp_func;
++	bvd_ipc->bh_bp.data = (unsigned long) bvd_ipc;
++	
++	/*Build a write urb*/
++	usb_fill_bulk_urb(&bvd_ipc->writeurb_mux, usbdev, 
++			  usb_sndbulkpipe(bvd_ipc->ipc_dev, 
++			  		  bvd_ipc->bulk_out_ep_mux), 
++			  bvd_ipc->obuf, writesize, usb_ipc_write_bulk,
++			  bvd_ipc);
++	//bvd_ipc->writeurb_mux.transfer_flags |= USB_ASYNC_UNLINK;
++
++	/*Build a read urb and send a IN token first time*/
++	usb_fill_bulk_urb(&bvd_ipc->readurb_mux, usbdev,
++			  usb_rcvbulkpipe(usbdev, bvd_ipc->bulk_in_ep_mux),
++			  bvd_ipc->ibuf, readsize, usb_ipc_read_bulk, bvd_ipc);
++	//bvd_ipc->readurb_mux.transfer_flags |= USB_ASYNC_UNLINK;	
++	
++	usb_driver_claim_interface(&usb_ipc_driver, intf, bvd_ipc);
++	//usb_driver_claim_interface(&usb_ipc_driver, &ipccfg->interface[1], bvd_ipc);
++	
++        // a2590c: dsplog is not supported by this driver
++	//	usb_driver_claim_interface(&usb_ipc_driver, 
++	//				   &ipccfg->interface[2], bvd_ipc);
++	/*send a IN token first time*/
++	bvd_ipc->readurb_mux.dev = bvd_ipc->ipc_dev;
++	if (usb_submit_urb(&bvd_ipc->readurb_mux, GFP_ATOMIC))
++		printk("usb_ipc_prob: usb_submit_urb(read mux bulk) failed!\n");
++
++	bvd_dbg("usb_ipc_prob: Send a IN token successfully!");
++	
++	if (bvd_ipc->xmit.head != bvd_ipc->xmit.tail) {
++		printk("usb_ipc_probe: mark ipcusb_softint!\n");
++		tasklet_schedule(&bvd_ipc->bh);
++	}
++
++	printk("usb_ipc_probe: completed probe!");
++	usb_set_intfdata(intf, &bvd_ipc);
++	return 0;
++}
++
++static void usb_ipc_disconnect(struct usb_interface *intf)
++{
++	//struct usb_device *usbdev = interface_to_usbdev(intf);
++	struct ipc_usb_data *bvd_ipc_disconnect = usb_get_intfdata(intf);
++		
++	printk("usb_ipc_disconnect:*** \n");
++
++	if ((UHCRHPS3 & 0x4) == 0)
++		usb_unlink_urb(&bvd_ipc_disconnect->readurb_mux);
++
++	usb_unlink_urb(&bvd_ipc_disconnect->writeurb_mux);
++        
++	bvd_ipc_disconnect->ipc_flag = IPC_USB_PROBE_NOT_READY;
++	kfree(bvd_ipc_disconnect->ibuf);
++	kfree(bvd_ipc_disconnect->obuf);
++	
++	usb_driver_release_interface(&usb_ipc_driver, 
++			bvd_ipc_disconnect->ipc_dev->actconfig->interface[0]);
++        usb_driver_release_interface(&usb_ipc_driver, 
++			bvd_ipc_disconnect->ipc_dev->actconfig->interface[1]);
++        
++	//a2590c: dsplog interface is not supported by this driver
++	//usb_driver_release_interface(&usb_ipc_driver, &bvd_ipc_disconnect->ipc_dev->actconfig->interface[2]);
++
++	bvd_ipc_disconnect->ipc_dev = NULL;
++
++	usb_set_intfdata(intf, NULL);
++
++	printk("usb_ipc_disconnect completed!\n");
++}
++
++static struct usb_device_id usb_ipc_id_table[] = {
++	{ USB_DEVICE(MOTO_IPC_VID, MOTO_IPC_PID) },
++	{ }						/* Terminating entry */
++};
++
++static struct usb_driver usb_ipc_driver = {
++	.name		= "usb ipc",
++	.probe		= usb_ipc_probe,
++	.disconnect	= usb_ipc_disconnect,
++	.id_table	= usb_ipc_id_table,
++};
++
++static int __init usb_ipc_init(void)
++{
++	int result;
++
++	bvd_dbg("init usb_ipc");
++	/* register driver at the USB subsystem */
++	result = usb_register(&usb_ipc_driver);
++	if (result < 0) {
++		err ("usb ipc driver could not be registered");
++		return result;
++	}
++
++	/*init the related mux interface*/
++	if (!(bvd_ipc = kzalloc(sizeof(struct ipc_usb_data), GFP_KERNEL))) {
++		err("usb_ipc_init: Out of memory.");		
++		usb_deregister(&usb_ipc_driver);
++		return -ENOMEM;
++	}
++	bvd_dbg("usb_ipc_init: Address of bvd_ipc:%p", bvd_ipc);
++
++	if (!(bvd_ipc->xmit.buf = kmalloc(IPC_USB_XMIT_SIZE, GFP_KERNEL))) {
++		err("usb_ipc_init: Not enough memory for the input buffer.");
++		kfree(bvd_ipc);
++		usb_deregister(&usb_ipc_driver);
++		return -ENOMEM;
++	}
++	bvd_dbg("usb_ipc_init: bvd_ipc->xmit.buf address:%p",
++		bvd_ipc->xmit.buf);
++	bvd_ipc->ipc_dev = NULL;
++	bvd_ipc->xmit.head = bvd_ipc->xmit.tail = 0;
++	bvd_ipc->write_flag = IPC_USB_WRITE_INIT;
++
++	ipcusb_tty_driver.write = usb_ipc_write;
++	ipcusb_tty_driver.chars_in_buffer = usb_ipc_chars_in_buffer;
++
++	usb_for_mux_driver = &ipcusb_tty_driver;
++	usb_for_mux_tty = &ipcusb_tty;
++	
++	/* init timers for ipcusb read process and usb suspend */
++	init_timer(&ipcusb_timer);
++	ipcusb_timer.function = ipcusb_timeout;
++
++	init_timer(&suspend_timer);
++	suspend_timer.function = suspend_timeout;
++
++	init_timer(&wakeup_timer);
++	wakeup_timer.function = wakeup_timeout;
++	
++	info("USB Host(Bulverde) IPC driver registered.");
++	info(DRIVER_VERSION ":" DRIVER_DESC);
++
++	return 0;
++}
++
++static void __exit usb_ipc_exit(void)
++{
++	bvd_dbg("cleanup bvd_ipc");
++
++	kfree(bvd_ipc->xmit.buf);
++	kfree(bvd_ipc);
++	usb_deregister(&usb_ipc_driver);
++
++	info("USB Host(Bulverde) IPC driver deregistered.");
++}
++
++module_init(usb_ipc_init);
++module_exit(usb_ipc_exit);
++EXPORT_SYMBOL(usb_send_readurb);
+Index: linux-2.6.17.14-fic2/drivers/char/ts0710_mux_usb.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic2/drivers/char/ts0710_mux_usb.h	2006-12-02 11:21:56.000000000 +0100
+@@ -0,0 +1,29 @@
++/*
++ * linux/drivers/usb/ipcusb.h
++ *
++ * Implementation of a ipc driver based Intel's Bulverde USB Host 
++ * Controller.
++ *
++ * Copyright (C) 2003-2005 Motorola
++ *
++ *  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
++ * 
++ *  2003-Nov-18 - (Motorola) created
++ * 
++ */
++extern struct tty_driver *usb_for_mux_driver;
++extern struct tty_struct *usb_for_mux_tty;
++extern void (*usb_mux_dispatcher)(struct tty_struct *tty);
++extern void (*usb_mux_sender)(void);
+Index: linux-2.6.17.14-fic2/drivers/char/gsmmux/gsmmux_ldisc.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic2/drivers/char/gsmmux/gsmmux_ldisc.c	2006-12-04 16:50:05.000000000 +0100
+@@ -0,0 +1,291 @@
++/* Linux kernel GSM multiplex implementation
++ *
++ * (C) 2006 by Harald Welte <hwelte at gnumonks.org>
++ *
++ *  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 <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/poll.h>
++#include <linux/ioctl.h>
++#include <linux/list.h>
++#include <linux/tty.h>
++#include <linux/string.h>
++#include <linux/tty_ldisc.h>
++
++/* this structure resembles a (de)multiplexer implementation */
++struct gsm_muxer {
++	struct list_head list;
++	char *name;
++	struct module *owner;
++	int (input)(struct gsmmux_instance *mi, const unsigned char *buf,
++		    char *flags, int count);
++};
++
++/* one instance of gsm_muxer */
++struct gsmmux_instance {
++	struct gsm_muxer *muxer;
++}
++
++
++/* muxer core */
++
++static list_head gsm_muxers;
++spinlock_t gsm_muxers_lock;
++
++int gsmmux_register(struct gsm_muxer *muxer)
++{
++	spin_lock(&gsm_muxers_lock);
++	list_add(&muxer->list, &gsm_muxers);
++	spin_unlock(&gsm_muxers_lock);
++}
++EXPORT_SYMBOL_GPL(gsmmux_register);
++
++void gsmmux_unregister(struct gsm_muxer *muxer)
++{
++	spin_lock(&gsm_muxers_lock);
++	list_del(&muxer->list);
++	spin_unlock(&gsm_muxers_lock);
++}
++EXPORT_SYMBOL_GPL(gsmmux_unregister);
++
++/* called by lower layer (e.g. ldisc) if new data arrives */
++int gsmmmux_input(struct gsmmux_instance *mi, const unsigned char *buf,
++		  char *flags, int count)
++{
++	return mi->muxer->input(mi, buf, flags, count);
++}
++EXPORT_SYMBOL_GPL(gsmmux_input);
++
++/* UART ops */
++
++static void gsmmux_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
++{
++}
++
++static void gsmmux_uart_break_ctl(struct uart_port *port, int break_state)
++{
++
++}
++
++static void gsmmux_uart_shutdown(struct uart_port *)
++{
++
++}
++
++static int gsmmux_uart_startup(struct uart_port *port)
++{
++	int ret = 0;
++
++	dbg("gsmmux_uart_startup: port=%s\n", port);
++
++	return ret;
++}
++
++static void gsmmux_uart_pm(struct uart_port *port, unsigned int level,
++			   unsigned int old)
++{
++	switch (level) {
++	case 3:
++		break;
++	case 0:
++		break;
++	default:
++	}
++}
++
++static const char *gsmmux_uart_type(struct uart_port *port)
++{
++	return "gsmmux";
++}
++
++static void gsmmux_uart_release_port(struct uart_port *port)
++{
++}
++
++static int gsmmux_uart_request_port(struct uart_port *port)
++{
++	return 0;
++}
++
++static void gsmmux_uart_config_port(struct uart_port *port, int flags)
++{
++}
++
++static int gsmmux_uart_verify_port(struct uart_port *port, 
++				   struct serial_struct *ser)
++{
++	return 0;
++}
++
++static struct uart_ops gsmmux_uart_ops = {
++	.pm		= gsmmux_uart_pm,
++	.tx_empty	= gsmmux_uart_tx_empty,
++	.get_mctrl	= gsmmux_uart_get_mctrl,
++	.set_mctrl	= gsmmux_uart_set_mctrl,
++	.stop_tx	= gsmmux_uart_stop_tx,
++	.enable_ms	= gsmmux_uart_enable_ms,
++	.break_ctl	= gsmmux_uart_break_ctl,
++	.startup	= gsmmux_uart_startup,
++	.shutdown	= gsmmux_uart_shutdown,
++	.set_termios	= gsmmux_uart_set_termios,
++	.type		= gsmmux_uart_type,
++	.release_port	= gsmmux_uart_release_port,
++	.request_port	= gsmmux_uart_request_port,
++	.config_port	= gsmmux_uart_config_port,
++	.verify_port	= gsmmux_uart_verify_port,
++};
++
++static struct uart_driver gsmmux_uart_drv = {
++	.owner			= THIS_MODULE,
++	.driver_name		= "ttyGSM",
++	.devfs_name		= "gsm/",
++	.dev_name		= "gsmmux_serial",
++	.major			= 240, /* local/unassigned range */
++	.nr			= NUM_MUX,
++};
++
++static struct uart_port gsmmux_uart_ports[NUM_MUX] = {
++	{
++		.type		= PORT_GSMMUX,
++	},
++};
++
++
++/* This is the gsmmux ldisc instance-specific data structure */
++struct gsm_ldisc {
++	struct gsmmux_instance mux;
++	spinlock_t recv_lock;
++};
++
++#define tty2gsm_ldisc(tty)	((struct gsm_ldisc *) ((tty)->disc_data))
++#define gsm_ldisc2tty(gsm_ldisc)	((gsm_ldisc)->tty)
++
++static ssize_t gsm_ldisc_read(struct tty_struct *tty, struct file *file,
++			   __u8 __user *buf, size_t nr)
++{
++	/* reading by the userspace process on the 'master' tty is generally
++	 * not supported while this line discipline is in presence */
++	return -EAGAIN;
++}
++
++static ssize_t gsm_ldisc_write(struct tty_struct *tty, struct file *file,
++			    const unsigned char *buf, size_t nr)
++{
++	/* writing by the userspace process on the 'master' tty is generally
++	 * not supported while this line discipline is in presence */
++	return -EAGAIN;
++}
++
++static int gsm_ldisc_ioctl(struct tty_struct *tty, struct file *file,
++			    unsigned int cmd, unsigned long arg)
++{
++	struct gsm_ldisc *gsm_ldisc = tty2gsm_ldisc(tty);
++	int err;
++
++	if (gsm_ldisc == NULL)
++		return -ENXIO;
++	err = -EFAULT;
++
++	switch (cmd) {
++	default:
++		err = -ENOIOCTLCMD;
++	}
++
++	return err;
++}
++
++static unsigned int gsm_ldisc_poll(struct tty_struct *tty, struct file *filp,
++				    poll_table *wait)
++{
++	return 0;
++}
++
++static int gsm_ldisc_open(struct tty_struct *tty)
++{
++	struct gsm_ldisc *gsm_ldisc = tty2gsm_ldisc(tty);
++
++	/* Attach the line discipline ... */
++}
++
++static void gsm_ldisc_close(struct tty_struct *tty)
++{
++	struct gsm_ldisc *gsm_ldisc = tty2gsm_ldisc(tty);
++
++	/* Disconnect the line discipline */
++}
++
++/* this is called by the lower level (serial) driver when received data is
++ * available */
++
++static void gsm_ldisc_recv_buf(struct tty_struct *tty, const __u8 *data,
++			       char *flags, int count)
++{
++	struct gsm_ldisc *gsm_ldisc = tty2gsm_ldisc(tty);
++	unsigned long flags;
++
++	if (!gsm_ldisc)
++		return;
++
++	spin_lock_irqsave(&gsm_ldisc->recv_lock, flags);
++	gsmmux_input(gsm_ldisc->mux, buf, cflags, count);
++	spin_unlock_irqrestore(&gsm_ldisc->recv_lock, flags);
++
++}
++
++/* this gets called by the lower level (serial) driver when it wants
++ * us to send some [more] data */
++static void gsm_ldisc_write_wakeup(struct tty_struct *tty)
++{
++	struct gsm_ldisc *gsm_ldisc = tty2gsm_ldisc(tty);
++
++	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
++	if (!gsm_ldisc)
++		return;
++}
++
++static struct tty_ldisc gsmmux_ldisc = {
++	.owner		= THIS_MODULE,
++	.magic		= TTY_LDISC_MAGIC,
++	.name		= "gsmmux",
++	.open		= gsm_ldisc_open,
++	.close		= gsm_ldisc_close,
++	.read		= gsm_ldisc_read,
++	.write		= gsm_ldics_write,
++	.ioctl		= gsm_ldisc_ioctl,
++	.poll		= gsm_ldisc_poll,
++	.receive_buf	= gsm_ldisc_recv_buf,
++	.write_wakeup	= gsm_ldisc_write_wakeup,
++};
++
++static int __init gsm_ldisc_init(void)
++{
++	return tty_register_ldisc(N_GSMMUX, &gsmmux_ldisc);
++}
++
++static void __exit gsm_ldisc_exit(void)
++{
++	tty_unregister_ldisc(N_GSMMUX);
++}
++
++module_init(gsm_ldisc_init);
++module_exit(gsm_ldisc_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge at gnumonks.org>");
++MODULE_ALIAS_LDISC(N_GSMMUX);
+Index: linux-2.6.17.14-fic2/include/linux/serial_core.h
+===================================================================
+--- linux-2.6.17.14-fic2.orig/include/linux/serial_core.h	2006-12-03 18:21:27.000000000 +0100
++++ linux-2.6.17.14-fic2/include/linux/serial_core.h	2006-12-03 18:23:55.000000000 +0100
+@@ -130,6 +130,9 @@
+ /* SUN4V Hypervisor Console */
+ #define PORT_SUNHV	72
+ 
++/* GSM Multiplexer Virtual Port */
++#define PORT_GSMMUX	73
++
+ #ifdef __KERNEL__
+ 
+ #include <linux/config.h>

Added: trunk/src/target/kernel/patches/udc-nomodule-misccr.patch
===================================================================
--- trunk/src/target/kernel/patches/udc-nomodule-misccr.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/udc-nomodule-misccr.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,26 @@
+This patch fixes S3C2410 UDC support when built as module. It also ensures
+that the USB port is switched into device mode by configuering the MISCCR
+register accordingly.
+
+Index: linux-2.6.17.14-fic1/drivers/usb/gadget/s3c2410_udc.c
+===================================================================
+--- linux-2.6.17.14-fic1.orig/drivers/usb/gadget/s3c2410_udc.c	2006-11-03 01:22:14.000000000 +0100
++++ linux-2.6.17.14-fic1/drivers/usb/gadget/s3c2410_udc.c	2006-11-03 12:41:52.000000000 +0100
+@@ -1492,7 +1492,7 @@
+ 		return -ENODEV;
+ 	if (udc->driver)
+ 		return -EBUSY;
+-	if (!driver->bind || !driver->unbind || !driver->setup
++	if (!driver->bind || !driver->setup
+ 			|| driver->speed == USB_SPEED_UNKNOWN)
+ 		return -EINVAL;
+ 
+@@ -1700,6 +1700,8 @@
+ 	}
+ 	dprintk(DEBUG_VERBOSE, "%s: got irq %i\n", gadget_name, IRQ_USBD);
+ 
++	s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST|S3C2410_MISCCR_USBSUSPND0|S3C2410_MISCCR_USBSUSPND1, 0);
++
+ #ifdef ENABLE_SYSFS
+ 	/* create device files */
+ 	device_create_file(&pdev->dev, &dev_attr_regs);

Added: trunk/src/target/kernel/patches/udc.patch
===================================================================
--- trunk/src/target/kernel/patches/udc.patch	2007-01-22 19:28:23 UTC (rev 582)
+++ trunk/src/target/kernel/patches/udc.patch	2007-01-22 19:29:05 UTC (rev 583)
@@ -0,0 +1,2197 @@
+This is a driver for the S3C2410 usb device controller
+
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/devs.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/devs.c	2006-12-11 21:44:29.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/devs.c	2006-12-11 21:44:31.000000000 +0100
+@@ -36,6 +36,7 @@
+ #include <asm/irq.h>
+ 
+ #include <asm/arch/regs-serial.h>
++#include <asm/arch/udc.h>
+ 
+ #include "devs.h"
+ 
+@@ -161,6 +162,20 @@
+ 
+ EXPORT_SYMBOL(s3c_device_usbgadget);
+ 
++void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
++{
++	struct s3c2410_udc_mach_info *npd;
++
++	npd = kmalloc(sizeof(*npd), GFP_KERNEL);
++	if (npd) {
++		memcpy(npd, pd, sizeof(*npd));
++		s3c_device_usbgadget.dev.platform_data = npd;
++	} else {
++		printk(KERN_ERR "no memory for udc platform data\n");
++	}
++}
++
++
+ /* Watchdog */
+ 
+ static struct resource s3c_wdt_resource[] = {
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/s3c2410.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/s3c2410.c	2006-12-11 21:44:29.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/s3c2410.c	2006-12-11 21:44:31.000000000 +0100
+@@ -48,6 +48,7 @@
+ 
+ static struct map_desc s3c2410_iodesc[] __initdata = {
+ 	IODESC_ENT(USBHOST),
++	IODESC_ENT(USBDEV),
+ 	IODESC_ENT(CLKPWR),
+ 	IODESC_ENT(LCD),
+ 	IODESC_ENT(TIMER),
+Index: linux-2.6.17.14-fic4.test/drivers/usb/gadget/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/usb/gadget/Kconfig	2006-12-11 21:44:29.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/usb/gadget/Kconfig	2006-12-11 21:44:31.000000000 +0100
+@@ -187,6 +187,27 @@
+ 
+ 	   Select this only if your OMAP board has a Mini-AB connector.
+ 
++config USB_GADGET_S3C2410
++	boolean "S3C2410 USB Device Controller"
++	depends on ARCH_S3C2410
++	select USB_GADGET_SELECTED
++	help
++	  Samsung's S3C2410 is an ARM-4 processor with an integrated
++	  full speed USB 1.1 device controller.
++	  It has 4 configurable endpoints, as well as endpoint
++	  zero (for control transfers).
++
++config USB_S3C2410
++	tristate
++	depends on USB_GADGET_S3C2410
++	default USB_GADGET
++
++config USB_S3C2410_DEBUG
++	boolean "S3C2410 udc debug messages"
++	depends on USB_GADGET_S3C2410
++	help
++	  foo
++
+ config USB_GADGET_AT91
+ 	boolean "AT91 USB Device Port"
+ 	depends on ARCH_AT91RM9200
+Index: linux-2.6.17.14-fic4.test/drivers/usb/gadget/Makefile
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/usb/gadget/Makefile	2006-12-11 21:44:29.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/usb/gadget/Makefile	2006-12-11 21:44:31.000000000 +0100
+@@ -7,6 +7,7 @@
+ obj-$(CONFIG_USB_GOKU)		+= goku_udc.o
+ obj-$(CONFIG_USB_OMAP)		+= omap_udc.o
+ obj-$(CONFIG_USB_LH7A40X)	+= lh7a40x_udc.o
++obj-$(CONFIG_USB_S3C2410)	+= s3c2410_udc.o
+ obj-$(CONFIG_USB_AT91)		+= at91_udc.o
+ 
+ #
+Index: linux-2.6.17.14-fic4.test/drivers/usb/gadget/s3c2410_udc.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/drivers/usb/gadget/s3c2410_udc.c	2006-12-11 21:44:31.000000000 +0100
+@@ -0,0 +1,1800 @@
++/*
++ * linux/drivers/usb/gadget/s3c2410_udc.c
++ * Samsung on-chip full speed USB device controllers
++ *
++ * Copyright (C) 2004-2006 Herbert Pötzl - Arnaud Patard
++ *
++ * 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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/ioport.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/smp_lock.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/list.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/version.h>
++#include <linux/clk.h>
++
++#include <linux/usb.h>
++#include <linux/usb_gadget.h>
++
++#include <asm/byteorder.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++#include <asm/unaligned.h>
++#include <asm/arch/irqs.h>
++
++#include <asm/arch/hardware.h>
++#include <asm/arch/regs-clock.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/regs-udc.h>
++#include <asm/arch/udc.h>
++
++#include <asm/mach-types.h>
++
++#include "s3c2410_udc.h"
++
++#define ENABLE_SYSFS
++
++#define DRIVER_DESC     "S3C2410 USB Device Controller Gadget"
++#define DRIVER_VERSION  "30 Apr 2006"
++#define DRIVER_AUTHOR	"Herbert Pötzl <herbert at 13thfloor.at>, Arnaud Patard <arnaud.patard at rtp-net.org>"
++
++static const char       gadget_name [] = "s3c2410_udc";
++static const char	driver_desc [] = DRIVER_DESC;
++
++static struct s3c2410_udc	*the_controller;
++static struct clk      		*udc_clock;
++static struct clk      		*usb_bus_clock;
++static void __iomem 		*base_addr;
++static u64			rsrc_start;
++static u64			rsrc_len;
++
++static inline u32 udc_readl(u32 reg)
++{
++	return readl(base_addr+reg);
++}
++static inline void udc_writel(u32 value, u32 reg)
++{
++	writel(value,base_addr+reg);
++}
++
++static struct s3c2410_udc_mach_info *udc_info;
++
++/*************************** DEBUG FUNCTION ***************************/
++#define DEBUG_NORMAL	1
++#define DEBUG_VERBOSE	2
++
++#ifdef CONFIG_USB_S3C2410_DEBUG
++#define USB_S3C2410_DEBUG_LEVEL 1
++
++static uint32_t s3c2410_ticks=0;
++
++static int dprintk(int level, const char *fmt, ...)
++{
++	static char printk_buf[1024];
++	static long prevticks;
++	static int invocation;
++	va_list args;
++	int len;
++
++	if (level > USB_S3C2410_DEBUG_LEVEL)
++		return 0;
++
++	if (s3c2410_ticks != prevticks) {
++		prevticks = s3c2410_ticks;
++		invocation = 0;
++	}
++
++	len = scnprintf(printk_buf, \
++			sizeof(printk_buf), "%1lu.%02d USB: ", \
++			prevticks, invocation++);
++
++	va_start(args, fmt);
++	len = vscnprintf(printk_buf+len, \
++			sizeof(printk_buf)-len, fmt, args);
++	va_end(args);
++
++	return printk("%s", printk_buf);
++}
++#else
++static int dprintk(int level, const char *fmt, ...)  { return 0; }
++#endif
++#ifdef ENABLE_SYSFS
++static ssize_t s3c2410udc_regs_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
++	u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg;
++	u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;
++	u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2;
++	u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2;
++
++	addr_reg       = udc_readl(S3C2410_UDC_FUNC_ADDR_REG);
++	pwr_reg        = udc_readl(S3C2410_UDC_PWR_REG);
++	ep_int_reg     = udc_readl(S3C2410_UDC_EP_INT_REG);
++	usb_int_reg    = udc_readl(S3C2410_UDC_USB_INT_REG);
++	ep_int_en_reg  = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
++	usb_int_en_reg = udc_readl(S3C2410_UDC_USB_INT_EN_REG);
++	udc_writel(0, S3C2410_UDC_INDEX_REG);
++	ep0_csr        = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++	udc_writel(1, S3C2410_UDC_INDEX_REG);
++	ep1_i_csr1     = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++	ep1_i_csr2     = udc_readl(S3C2410_UDC_IN_CSR2_REG);
++	ep1_o_csr1     = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++	ep1_o_csr2     = udc_readl(S3C2410_UDC_IN_CSR2_REG);
++	udc_writel(2, S3C2410_UDC_INDEX_REG);
++	ep2_i_csr1     = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++	ep2_i_csr2     = udc_readl(S3C2410_UDC_IN_CSR2_REG);
++	ep2_o_csr1     = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++	ep2_o_csr2     = udc_readl(S3C2410_UDC_IN_CSR2_REG);
++
++
++	return snprintf(buf, PAGE_SIZE,        \
++		 "FUNC_ADDR_REG  : 0x%04X\n"   \
++		 "PWR_REG        : 0x%04X\n"   \
++		 "EP_INT_REG     : 0x%04X\n"   \
++		 "USB_INT_REG    : 0x%04X\n"   \
++		 "EP_INT_EN_REG  : 0x%04X\n"   \
++		 "USB_INT_EN_REG : 0x%04X\n"   \
++		 "EP0_CSR        : 0x%04X\n"   \
++		 "EP1_I_CSR1     : 0x%04X\n"   \
++		 "EP1_I_CSR2     : 0x%04X\n"   \
++		 "EP1_O_CSR1     : 0x%04X\n"   \
++		 "EP1_O_CSR2     : 0x%04X\n"   \
++		 "EP2_I_CSR1     : 0x%04X\n"   \
++		 "EP2_I_CSR2     : 0x%04X\n"   \
++		 "EP2_O_CSR1     : 0x%04X\n"   \
++		 "EP2_O_CSR2     : 0x%04X\n",  \
++		 addr_reg,pwr_reg,ep_int_reg,usb_int_reg,     \
++		 ep_int_en_reg, usb_int_en_reg, ep0_csr,      \
++		 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2, \
++		 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2  \
++		 );
++}
++
++static DEVICE_ATTR(regs, 0444,
++		   s3c2410udc_regs_show,
++		   NULL);
++#endif
++/*------------------------- I/O ----------------------------------*/
++static void done(struct s3c2410_ep *ep, struct s3c2410_request *req, int status);
++static void nuke (struct s3c2410_udc *udc, struct s3c2410_ep *ep, int status)
++{
++	/* Sanity check */
++	if (&ep->queue != NULL)
++		while (!list_empty (&ep->queue)) {
++			struct s3c2410_request  *req;
++			req = list_entry (ep->queue.next, struct s3c2410_request, queue);
++			done(ep,req,status);
++		}
++}
++
++/*
++ * 	done
++ */
++static void done(struct s3c2410_ep *ep, struct s3c2410_request *req, int status)
++{
++	unsigned halted = ep->halted;
++
++	list_del_init(&req->queue);
++
++	if (likely (req->req.status == -EINPROGRESS))
++		req->req.status = status;
++	else
++		status = req->req.status;
++
++	ep->halted = 1;
++	req->req.complete(&ep->ep, &req->req);
++	ep->halted = halted;
++}
++
++static inline void clear_ep_state (struct s3c2410_udc *dev)
++{
++	        unsigned i;
++
++		/* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
++		 * fifos, and pending transactions mustn't be continued in any case.
++		 */
++		 for (i = 1; i < S3C2410_ENDPOINTS; i++)
++			 nuke(dev, &dev->ep[i], -ECONNABORTED);
++}
++
++static inline int fifo_count_out(void)
++{
++	int tmp;
++
++	tmp = udc_readl(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8;
++	tmp |= udc_readl(S3C2410_UDC_OUT_FIFO_CNT1_REG);
++
++	return tmp & 0xffff;
++}
++
++/*
++ * 	write_packet
++ */
++static inline int
++write_packet(int fifo, struct s3c2410_request *req, unsigned max)
++{
++	unsigned	len;
++	u8		*buf;
++
++	buf = req->req.buf + req->req.actual;
++	prefetch(buf);
++
++	len = min(req->req.length - req->req.actual, max);
++	dprintk(DEBUG_VERBOSE, "write_packet %d %d %d ",req->req.actual,req->req.length,len);
++	req->req.actual += len;
++	dprintk(DEBUG_VERBOSE, "%d\n",req->req.actual);
++
++	writesb(base_addr+fifo, buf, len);
++	return len;
++}
++
++static void udc_reinit(struct s3c2410_udc *dev);
++
++/*
++ * 	write_fifo
++ *
++ * return:  0 = still running, 1 = completed, negative = errno
++ */
++static int write_fifo(struct s3c2410_ep *ep, struct s3c2410_request *req)
++{
++	unsigned	count;
++	int		is_last;
++	u32		idx;
++	int		fifo_reg;
++	u32		ep_csr;
++
++
++	switch(ep->bEndpointAddress&0x7F)
++	{
++		default:
++		case 0: idx = 0;
++			fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
++			break;
++		case 1:
++			idx = 1;
++			fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
++			break;
++		case 2:
++			idx = 2;
++			fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
++			break;
++
++		case 3:
++			idx = 3;
++			fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
++			break;
++
++		case 4:
++			idx = 4;
++			fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
++			break;
++	}
++
++	count = write_packet(fifo_reg, req, ep->ep.maxpacket);
++
++	/* last packet is often short (sometimes a zlp) */
++	if (count != ep->ep.maxpacket)
++		is_last = 1;
++	else  if (req->req.length != req->req.actual || req->req.zero)
++		is_last = 0;
++	else
++		is_last = 2;
++
++	/* Only ep0 debug messages are interesting */
++	if (!idx)
++		dprintk(DEBUG_NORMAL, "Written ep%d %d.%d of %d b [last %d,z %d]\n",idx,count,req->req.actual,req->req.length,is_last,req->req.zero);
++
++	if (is_last)
++	{
++		/* The order is important. It prevents to send 2 packet at the same time
++		 **/
++		if (!idx)
++		{
++			/* If we got a reset signal, no need to say 'data sent' */
++			if (! (udc_readl(S3C2410_UDC_USB_INT_REG) & S3C2410_UDC_USBINT_RESET))
++				set_ep0_de_in(base_addr);
++			ep->dev->ep0state=EP0_IDLE;
++		}
++		else
++		{
++			 udc_writel(idx, S3C2410_UDC_INDEX_REG);
++			 ep_csr=udc_readl(S3C2410_UDC_IN_CSR1_REG);
++			 udc_writel(idx, S3C2410_UDC_INDEX_REG);
++			 udc_writel(ep_csr|S3C2410_UDC_ICSR1_PKTRDY,S3C2410_UDC_IN_CSR1_REG);
++		}
++		done(ep, req, 0);
++		is_last=1;
++	}
++	else
++	{
++		if (!idx)
++		{
++			/* If we got a reset signal, no need to say 'data sent' */
++			if (! (udc_readl(S3C2410_UDC_USB_INT_REG) & S3C2410_UDC_USBINT_RESET))
++				set_ep0_ipr(base_addr);
++		}
++		else
++		{
++			udc_writel(idx, S3C2410_UDC_INDEX_REG);
++			ep_csr=udc_readl(S3C2410_UDC_IN_CSR1_REG);
++			udc_writel(idx, S3C2410_UDC_INDEX_REG);
++			udc_writel(ep_csr|S3C2410_UDC_ICSR1_PKTRDY,S3C2410_UDC_IN_CSR1_REG);
++		}
++	}
++
++
++	return is_last;
++}
++
++static inline int
++read_packet(int fifo, u8 *buf, struct s3c2410_request *req, unsigned avail)
++{
++	unsigned	len;
++
++	len = min(req->req.length - req->req.actual, avail);
++	req->req.actual += len;
++
++	readsb(fifo + base_addr, buf, len);
++	return len;
++}
++
++/*
++ * return:  0 = still running, 1 = queue empty, negative = errno
++ */
++static int read_fifo(struct s3c2410_ep *ep, struct s3c2410_request *req)
++{
++	u8		*buf;
++	u32		ep_csr;
++	unsigned	bufferspace;
++	int 		is_last=1;
++	unsigned 	avail;
++	int 		fifo_count = 0;
++	u32		idx;
++	int		fifo_reg;
++
++	
++	switch(ep->bEndpointAddress&0x7F)
++	{
++		default:
++		case 0: idx = 0;
++			fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
++			break;
++		case 1:
++			idx = 1;
++			fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
++			break;
++		case 2:
++			idx = 2;
++			fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
++			break;
++
++		case 3:
++			idx = 3;
++			fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
++			break;
++
++		case 4:
++			idx = 4;
++			fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
++			break;
++
++	}
++
++	if (!req->req.length) {
++		return 1;
++	}
++
++	buf = req->req.buf + req->req.actual;
++	bufferspace = req->req.length - req->req.actual;
++	if (!bufferspace)
++	{
++		dprintk(DEBUG_NORMAL, "read_fifo: Buffer full !!\n");
++		return -1;
++	}
++
++	udc_writel(idx, S3C2410_UDC_INDEX_REG);
++
++        fifo_count = fifo_count_out();
++	dprintk(DEBUG_NORMAL, "fifo_read fifo count : %d\n",fifo_count);
++
++	if (fifo_count > ep->ep.maxpacket)
++		avail = ep->ep.maxpacket;
++	else
++		avail = fifo_count;
++
++	fifo_count=read_packet(fifo_reg,buf,req,avail);
++
++	/* checking this with ep0 is not accurate as we already
++	 * read a control request 
++	 **/
++	if (idx && fifo_count < ep->ep.maxpacket) {
++		is_last = 1;
++		/* overflowed this request?  flush extra data */
++		if (fifo_count != avail) {
++			req->req.status = -EOVERFLOW;
++		}
++	} else {
++		if (req->req.length <= req->req.actual)
++			is_last = 1;
++		else
++			is_last = 0;
++	}
++
++	udc_writel(idx, S3C2410_UDC_INDEX_REG);
++	fifo_count = fifo_count_out();
++
++	/* Only ep0 debug messages are interesting */
++	if (!idx)
++		dprintk(DEBUG_VERBOSE, "fifo_read fifo count : %d [last %d]\n",fifo_count,is_last);
++
++
++	if (is_last) {
++		if (!idx)
++		{
++			set_ep0_de_out(base_addr);
++			ep->dev->ep0state=EP0_IDLE;
++		}
++		else
++		{
++			udc_writel(idx, S3C2410_UDC_INDEX_REG);
++			ep_csr=udc_readl(S3C2410_UDC_OUT_CSR1_REG);
++			udc_writel(idx, S3C2410_UDC_INDEX_REG);
++			udc_writel(ep_csr&~S3C2410_UDC_OCSR1_PKTRDY,S3C2410_UDC_OUT_CSR1_REG);
++		}
++		done(ep, req, 0);
++		if (!list_empty(&ep->queue))
++		{
++			is_last=0;
++			req = container_of(ep->queue.next,
++				struct s3c2410_request, queue);
++		}
++		else
++			is_last=1;
++
++	}
++	else
++	{
++		if (!idx)
++		{
++			clear_ep0_opr(base_addr);
++		}
++		else
++		{
++			udc_writel(idx, S3C2410_UDC_INDEX_REG);
++			ep_csr=udc_readl(S3C2410_UDC_OUT_CSR1_REG);
++			udc_writel(idx, S3C2410_UDC_INDEX_REG);
++			udc_writel(ep_csr&~S3C2410_UDC_OCSR1_PKTRDY,S3C2410_UDC_OUT_CSR1_REG);
++		}
++	}
++
++
++	return is_last;
++}
++
++
++static int
++read_fifo_crq(struct usb_ctrlrequest *crq)
++{
++	int bytes_read = 0;
++	int fifo_count = 0;
++	int i;
++
++
++	unsigned char *pOut = (unsigned char*)crq;
++
++	udc_writel(0, S3C2410_UDC_INDEX_REG);
++
++	fifo_count = fifo_count_out();
++
++	dprintk(DEBUG_NORMAL, "read_fifo_crq(): fifo_count=%d\n", fifo_count );
++
++	fifo_count = sizeof(struct usb_ctrlrequest);
++	while( fifo_count-- ) {
++		i = 0;
++
++		do {
++			*pOut = (unsigned char)udc_readl(S3C2410_UDC_EP0_FIFO_REG);
++			i++;
++		} while((fifo_count_out() != fifo_count) && (i < 10));
++
++		if ( i == 10 ) {
++			dprintk(DEBUG_NORMAL, "read_fifo(): read failure\n");
++		}
++
++		pOut++;
++		bytes_read++;
++	}
++
++	dprintk(DEBUG_VERBOSE, "read_fifo_crq: len=%d %02x:%02x {%x,%x,%x}\n",
++			bytes_read, crq->bRequest, crq->bRequestType,
++			crq->wValue, crq->wIndex, crq->wLength);
++
++	return bytes_read;
++}
++static int s3c2410_get_status(struct s3c2410_udc *dev, struct usb_ctrlrequest  *crq)
++{
++	u16 status = 0;
++	u8 ep_num = crq->wIndex & 0x7F;
++	u8 is_in = crq->wIndex & USB_DIR_IN;
++
++	switch(crq->bRequestType & USB_RECIP_MASK) {
++		case USB_RECIP_INTERFACE:
++			break;
++		case USB_RECIP_DEVICE:
++			status = dev->devstatus;
++			break;
++		case USB_RECIP_ENDPOINT:
++			if (ep_num>4 || crq->wLength > 2)
++				return 1;
++			if (!ep_num) {
++				udc_writel(0, S3C2410_UDC_INDEX_REG);
++				status = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++				status = ( (status & S3C2410_UDC_EP0_CSR_SENDSTL) == S3C2410_UDC_EP0_CSR_SENDSTL);
++			}
++			else {
++				udc_writel(ep_num, S3C2410_UDC_INDEX_REG);
++				if (is_in) {
++					status = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++					status = ( (status & S3C2410_UDC_ICSR1_SENTSTL) == S3C2410_UDC_ICSR1_SENTSTL);
++				}
++				else {
++					status = udc_readl(S3C2410_UDC_OUT_CSR1_REG);
++					status = ( (status & S3C2410_UDC_OCSR1_SENTSTL) == S3C2410_UDC_OCSR1_SENTSTL);
++				}
++			}
++
++			break;
++		default:
++			return 1;
++	}
++
++	/* Seems to be needed to get it working. ouch :( */
++	udelay(0x20);
++	udc_writel(status&0xFF,S3C2410_UDC_EP0_FIFO_REG);
++	udc_writel(status>>8,S3C2410_UDC_EP0_FIFO_REG);
++	set_ep0_de_in(base_addr);
++
++	return 0;
++}
++/*------------------------- usb state machine -------------------------------*/
++static void handle_ep0(struct s3c2410_udc *dev)
++{
++	u32			ep0csr;
++	struct s3c2410_ep	*ep = &dev->ep [0];
++	struct s3c2410_request	*req;
++	struct usb_ctrlrequest	crq;
++
++	if (list_empty(&ep->queue))
++    		req = NULL;
++	else
++		req = list_entry(ep->queue.next, struct s3c2410_request, queue);
++
++
++	udc_writel(0, S3C2410_UDC_INDEX_REG);
++	ep0csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++	dprintk(DEBUG_NORMAL,"ep0csr %x ep0state %s\n",ep0csr,ep0states[dev->ep0state]);
++
++	/* clear stall status */
++	if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
++		/* FIXME */
++		nuke(dev, ep, -EPIPE);
++	    	dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
++	    	clear_ep0_sst(base_addr);
++		dev->ep0state = EP0_IDLE;
++		return;
++	}
++
++	/* clear setup end */
++	if (ep0csr & S3C2410_UDC_EP0_CSR_SE
++	    	/* && dev->ep0state != EP0_IDLE */) {
++	    	dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
++		nuke(dev, ep, 0);
++	    	clear_ep0_se(base_addr);
++		dev->ep0state = EP0_IDLE;
++	}
++
++
++	switch (dev->ep0state) {
++	case EP0_IDLE:
++		/* start control request? */
++		if (ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) {
++			int len, ret, tmp;
++
++			nuke (dev, ep, -EPROTO);
++
++			len = read_fifo_crq(&crq);
++			if (len != sizeof(crq)) {
++			  	dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR"
++				    	" wanted %d bytes got %d. Stalling out...\n",
++					sizeof(crq), len);
++ 				set_ep0_ss(base_addr);
++				return;
++			}
++
++			dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n", crq.bRequest,crq.bRequestType,  crq.wLength);
++
++
++			/* cope with automagic for some standard requests. */
++			dev->req_std = (crq.bRequestType & USB_TYPE_MASK)
++						== USB_TYPE_STANDARD;
++			dev->req_config = 0;
++			dev->req_pending = 1;
++			switch (crq.bRequest) {
++				case USB_REQ_SET_CONFIGURATION:
++				    	dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n");
++					if (crq.bRequestType == USB_RECIP_DEVICE) {
++config_change:
++						dev->req_config = 1;
++/*						clear_ep_state(dev);*/
++						set_ep0_de_out(base_addr);
++					}
++					break;
++				case USB_REQ_SET_INTERFACE:
++				    	dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n");
++					if (crq.bRequestType == USB_RECIP_INTERFACE) {
++						goto config_change;
++					}
++					break;
++
++				case USB_REQ_SET_ADDRESS:
++				    	dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n");
++					if (crq.bRequestType == USB_RECIP_DEVICE) {
++						tmp = crq.wValue & 0x7F;
++						dev->address = tmp;
++ 						udc_writel((tmp | 0x80), S3C2410_UDC_FUNC_ADDR_REG);
++						set_ep0_de_out(base_addr);
++						return;
++					}
++					break;
++					
++				case USB_REQ_GET_STATUS:
++					dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n");
++					clear_ep0_opr(base_addr);
++					if (!s3c2410_get_status(dev, &crq)) {
++						return;
++					}
++					break;
++
++				default:
++					clear_ep0_opr(base_addr);
++					break;
++			}
++
++			if (crq.bRequestType & USB_DIR_IN)
++				dev->ep0state = EP0_IN_DATA_PHASE;
++			else
++				dev->ep0state = EP0_OUT_DATA_PHASE;
++			ret = dev->driver->setup(&dev->gadget, &crq);
++			if (ret < 0) {
++				if (dev->req_config) {
++					dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n",
++						crq.bRequest, ret);
++					return;
++				}
++				if (ret == -EOPNOTSUPP)
++					dprintk(DEBUG_NORMAL, "Operation not supported\n");
++				else
++					dprintk(DEBUG_NORMAL, "dev->driver->setup failed. (%d)\n",ret);
++
++				set_ep0_ss(base_addr);
++				set_ep0_de_out(base_addr);
++				dev->ep0state = EP0_IDLE;
++			/* deferred i/o == no response yet */
++			} else if (dev->req_pending) {
++			    	dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n");
++				dev->req_pending=0;
++			}
++			dprintk(DEBUG_VERBOSE, "ep0state %s\n",ep0states[dev->ep0state]);
++		}
++		break;
++	case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR etc */
++	    	dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
++		if (!(ep0csr & 2) && req)
++		{
++			write_fifo(ep, req);
++		}
++		break;
++	case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR etc */
++	    	dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
++		if ((ep0csr & 1) && req ) {
++			read_fifo(ep,req);
++		}
++		break;
++	case EP0_END_XFER:
++	    	dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
++		dev->ep0state=EP0_IDLE;
++		break;
++	case EP0_STALL:
++	    	dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
++	    	dev->ep0state=EP0_IDLE;
++		break;
++	}
++}
++/*
++ * 	handle_ep - Manage I/O endpoints
++ */
++static void handle_ep(struct s3c2410_ep *ep)
++{
++	struct s3c2410_request	*req;
++	int			is_in = ep->bEndpointAddress & USB_DIR_IN;
++	u32			ep_csr1;
++	u32			idx;
++
++	if (likely (!list_empty(&ep->queue)))
++		req = list_entry(ep->queue.next,
++				struct s3c2410_request, queue);
++	else
++		req = NULL;
++
++	idx = (u32)(ep->bEndpointAddress&0x7F);
++
++	if (is_in) {
++		udc_writel(idx, S3C2410_UDC_INDEX_REG);
++		ep_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++		dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n",idx,ep_csr1,req ? 1 : 0);
++
++		if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL)
++		{
++			dprintk(DEBUG_VERBOSE, "st\n");
++			udc_writel(idx, S3C2410_UDC_INDEX_REG);
++			udc_writel(0x00,S3C2410_UDC_IN_CSR1_REG);
++			return;
++		}
++
++		if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req)
++		{
++			write_fifo(ep,req);
++		}
++	}
++	else {
++		udc_writel(idx, S3C2410_UDC_INDEX_REG);
++		ep_csr1 = udc_readl(S3C2410_UDC_OUT_CSR1_REG);
++		dprintk(DEBUG_VERBOSE, "ep%01d read csr:%02x\n",idx,ep_csr1);
++
++		if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL)
++		{
++			udc_writel(idx, S3C2410_UDC_INDEX_REG);
++			udc_writel(0x00,S3C2410_UDC_OUT_CSR1_REG);
++			return;
++		}
++		if( (ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req)
++		{
++			read_fifo(ep,req);
++		}
++	}
++}
++
++#include <asm/arch/regs-irq.h>
++/*
++ *      s3c2410_udc_irq - interrupt handler
++ */
++static irqreturn_t
++s3c2410_udc_irq(int irq, void *_dev, struct pt_regs *r)
++{
++	struct s3c2410_udc      *dev = _dev;
++	int usb_status;
++	int usbd_status;
++	int pwr_reg;
++	int ep0csr;
++	int     i;
++	u32	idx;
++	unsigned long flags;
++
++	spin_lock_irqsave(&dev->lock,flags);
++
++	/* Driver connected ? */
++	if (!dev->driver) {
++		/* Clear interrupts */
++		udc_writel( \
++				udc_readl(S3C2410_UDC_USB_INT_REG), \
++				S3C2410_UDC_USB_INT_REG \
++			    );
++		udc_writel( \
++				udc_readl(S3C2410_UDC_EP_INT_REG), \
++				S3C2410_UDC_EP_INT_REG \
++			    );
++	}
++
++	/* Save index */
++	idx = udc_readl(S3C2410_UDC_INDEX_REG);
++
++	/* Read status registers */
++	usb_status = udc_readl(S3C2410_UDC_USB_INT_REG);
++	usbd_status = udc_readl(S3C2410_UDC_EP_INT_REG);
++	pwr_reg = udc_readl(S3C2410_UDC_PWR_REG);
++
++	S3C2410_UDC_SETIX(base_addr,EP0);
++	ep0csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++
++	// dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", usb_status, usbd_status, pwr_reg,ep0csr);
++
++	/*
++	 * Now, handle interrupts. There's two types :
++	 * - Reset, Resume, Suspend coming -> usb_int_reg
++	 * - EP -> ep_int_reg
++	 */
++
++	/* RESET */
++	if (usb_status & S3C2410_UDC_USBINT_RESET )
++	{
++		/* two kind of reset :
++		 * - reset start -> pwr reg = 8
++		 * - reset end   -> pwr reg = 0
++		 **/
++		dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",ep0csr,pwr_reg);
++		dev->gadget.speed = USB_SPEED_UNKNOWN;
++		udc_writel(0x00, S3C2410_UDC_INDEX_REG);
++		udc_writel((dev->ep[0].ep.maxpacket&0x7ff)>>3,S3C2410_UDC_MAXP_REG);
++		dev->address = 0;
++
++		dev->ep0state = EP0_IDLE;
++		dev->gadget.speed = USB_SPEED_FULL;
++
++		/* clear interrupt */
++		udc_writel(S3C2410_UDC_USBINT_RESET,
++			S3C2410_UDC_USB_INT_REG);
++
++		udc_writel(idx,S3C2410_UDC_INDEX_REG);
++		spin_unlock_irqrestore(&dev->lock,flags);
++		return IRQ_HANDLED;
++	}
++
++	/* RESUME */
++	if (usb_status & S3C2410_UDC_USBINT_RESUME)
++	{
++		dprintk(DEBUG_NORMAL, "USB resume\n");
++
++		/* clear interrupt */
++		udc_writel(S3C2410_UDC_USBINT_RESUME,
++			S3C2410_UDC_USB_INT_REG);
++		if (dev->gadget.speed != USB_SPEED_UNKNOWN
++			&& dev->driver
++			&& dev->driver->resume)
++			dev->driver->resume(&dev->gadget);
++	}
++
++	/* SUSPEND */
++	if (usb_status & S3C2410_UDC_USBINT_SUSPEND)
++	{
++		dprintk(DEBUG_NORMAL, "USB suspend\n");
++
++		/* clear interrupt */
++		udc_writel(S3C2410_UDC_USBINT_SUSPEND,
++			S3C2410_UDC_USB_INT_REG);
++
++		if (dev->gadget.speed != USB_SPEED_UNKNOWN
++				&& dev->driver
++				&& dev->driver->suspend)
++			dev->driver->suspend(&dev->gadget);
++
++		dev->ep0state = EP0_IDLE;
++	}
++
++	/* EP */
++	/* control traffic */
++	/* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
++	 * generate an interrupt
++	 */
++	if (usbd_status & S3C2410_UDC_INT_EP0)
++	{
++		dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
++		/* Clear the interrupt bit by setting it to 1 */
++		udc_writel(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
++		handle_ep0(dev);
++	}
++	/* endpoint data transfers */
++	for (i = 1; i < S3C2410_ENDPOINTS; i++) {
++		u32 tmp = 1 << i;
++		if (usbd_status & tmp) {
++			dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);
++
++			/* Clear the interrupt bit by setting it to 1 */
++			udc_writel(tmp, S3C2410_UDC_EP_INT_REG);
++			handle_ep(&dev->ep[i]);
++		}
++	}
++
++
++	dprintk(DEBUG_VERBOSE,"irq: %d done.\n", irq);
++
++	/* Restore old index */
++	udc_writel(idx,S3C2410_UDC_INDEX_REG);
++
++	spin_unlock_irqrestore(&dev->lock,flags);
++
++	return IRQ_HANDLED;
++}
++/*------------------------- s3c2410_ep_ops ----------------------------------*/
++
++/*
++ * 	s3c2410_ep_enable
++ */
++static int
++s3c2410_ep_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
++{
++	struct s3c2410_udc	*dev;
++	struct s3c2410_ep	*ep;
++	u32			max, tmp;
++	unsigned long		flags;
++	u32			csr1,csr2;
++	u32			int_en_reg;
++
++
++	ep = container_of (_ep, struct s3c2410_ep, ep);
++	if (!_ep || !desc || ep->desc || _ep->name == ep0name
++			|| desc->bDescriptorType != USB_DT_ENDPOINT)
++		return -EINVAL;
++	dev = ep->dev;
++	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
++		return -ESHUTDOWN;
++
++	max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff;
++
++	local_irq_save (flags);
++	_ep->maxpacket = max & 0x7ff;
++	ep->desc = desc;
++	ep->halted = 0;
++	ep->bEndpointAddress = desc->bEndpointAddress;
++
++	/* set max packet */
++	udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++	udc_writel(max>>3,S3C2410_UDC_MAXP_REG);
++
++
++	/* set type, direction, address; reset fifo counters */
++	if (desc->bEndpointAddress & USB_DIR_IN)
++	{
++		csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT;
++		csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN;
++
++		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++		udc_writel(csr1,S3C2410_UDC_IN_CSR1_REG);
++		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++		udc_writel(csr2,S3C2410_UDC_IN_CSR2_REG);
++	}
++	else
++	{
++		/* don't flush he in fifo or there will be an interrupt for that
++		 * endpoint */
++		csr1 = S3C2410_UDC_ICSR1_CLRDT;
++		csr2 = S3C2410_UDC_ICSR2_DMAIEN;
++
++		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++		udc_writel(csr1,S3C2410_UDC_IN_CSR1_REG);
++		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++		udc_writel(csr2,S3C2410_UDC_IN_CSR2_REG);
++
++		csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
++		csr2 = S3C2410_UDC_OCSR2_DMAIEN;
++
++		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++		udc_writel(csr1,S3C2410_UDC_OUT_CSR1_REG);
++		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++		udc_writel(csr2,S3C2410_UDC_OUT_CSR2_REG);
++	}
++
++
++	/* enable irqs */
++	int_en_reg = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
++	udc_writel(int_en_reg | (1<<ep->num),S3C2410_UDC_EP_INT_EN_REG);
++
++
++	/* print some debug message */
++	tmp = desc->bEndpointAddress;
++	dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
++		_ep->name,ep->num, tmp, desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);
++
++	local_irq_restore (flags);
++
++	return 0;
++}
++
++/*
++ * s3c2410_ep_disable
++ */
++static int s3c2410_ep_disable (struct usb_ep *_ep)
++{
++	struct s3c2410_ep	*ep = container_of(_ep, struct s3c2410_ep, ep);
++	unsigned long	flags;
++	u32			int_en_reg;
++
++
++	if (!_ep || !ep->desc) {
++		dprintk(DEBUG_NORMAL, "%s not enabled\n",
++			_ep ? ep->ep.name : NULL);
++		return -EINVAL;
++	}
++
++	local_irq_save(flags);
++
++	printk(KERN_ERR "ep_disable: %s\n",_ep->name);
++
++	ep->desc = NULL;
++	ep->halted = 1;
++
++	nuke (ep->dev, ep, -ESHUTDOWN);
++
++	/* disable irqs */
++	int_en_reg = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
++	udc_writel(int_en_reg & ~(1<<ep->num),S3C2410_UDC_EP_INT_EN_REG);
++
++	local_irq_restore(flags);
++
++	dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name);
++
++	return 0;
++}
++
++/*
++ * s3c2410_alloc_request
++ */
++static struct usb_request *
++s3c2410_alloc_request (struct usb_ep *_ep, gfp_t mem_flags)
++{
++	struct s3c2410_ep	*ep;
++	struct s3c2410_request	*req;
++
++    	dprintk(DEBUG_VERBOSE,"s3c2410_alloc_request(ep=%p,flags=%d)\n", _ep, mem_flags);
++
++	ep = container_of (_ep, struct s3c2410_ep, ep);
++	if (!_ep)
++		return NULL;
++
++	req = kzalloc (sizeof *req, mem_flags);
++	if (!req)
++		return NULL;
++	INIT_LIST_HEAD (&req->queue);
++	return &req->req;
++}
++
++/*
++ * s3c2410_free_request
++ */
++static void
++s3c2410_free_request (struct usb_ep *_ep, struct usb_request *_req)
++{
++	struct s3c2410_ep	*ep;
++	struct s3c2410_request	*req;
++
++    	dprintk(DEBUG_VERBOSE, "s3c2410_free_request(ep=%p,req=%p)\n", _ep, _req);
++
++	ep = container_of (_ep, struct s3c2410_ep, ep);
++	if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
++		return;
++
++	req = container_of (_req, struct s3c2410_request, req);
++	WARN_ON (!list_empty (&req->queue));
++	kfree (req);
++}
++
++/*
++ * 	s3c2410_alloc_buffer
++ */
++static void *
++s3c2410_alloc_buffer (
++	struct usb_ep *_ep,
++	unsigned bytes,
++	dma_addr_t *dma,
++	gfp_t mem_flags)
++{
++	char *retval;
++
++    	dprintk(DEBUG_VERBOSE,"s3c2410_alloc_buffer()\n");
++
++	if (!the_controller->driver)
++		return NULL;
++	retval = kmalloc (bytes, mem_flags);
++	*dma = (dma_addr_t) retval;
++	return retval;
++}
++
++/*
++ * s3c2410_free_buffer
++ */
++static void
++s3c2410_free_buffer (
++	struct usb_ep *_ep,
++	void *buf,
++	dma_addr_t dma,
++	unsigned bytes)
++{
++    	dprintk(DEBUG_VERBOSE, "s3c2410_free_buffer()\n");
++
++	if (bytes)
++		kfree (buf);
++}
++
++/*
++ * 	s3c2410_queue
++ */
++static int
++s3c2410_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
++{
++	struct s3c2410_request	*req;
++	struct s3c2410_ep	*ep;
++	struct s3c2410_udc	*dev;
++	u32			ep_csr=0;
++	int 			fifo_count=0;
++	unsigned long		flags;
++
++
++	ep = container_of(_ep, struct s3c2410_ep, ep);
++	if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
++		dprintk(DEBUG_NORMAL, "s3c2410_queue: inval 2\n");
++		return -EINVAL;
++	}
++
++	dev = ep->dev;
++	if (unlikely (!dev->driver
++			|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
++		return -ESHUTDOWN;
++	}
++
++	local_irq_save (flags);
++	
++	req = container_of(_req, struct s3c2410_request, req);
++	if (unlikely (!_req || !_req->complete || !_req->buf
++	|| !list_empty(&req->queue))) {
++		if (!_req)
++			dprintk(DEBUG_NORMAL, "s3c2410_queue: 1 X X X\n");
++		else
++		{
++			dprintk(DEBUG_NORMAL, "s3c2410_queue: 0 %01d %01d %01d\n",!_req->complete,!_req->buf, !list_empty(&req->queue));
++		}
++		local_irq_restore(flags);
++		return -EINVAL;
++	}
++
++	_req->status = -EINPROGRESS;
++	_req->actual = 0;
++
++	dprintk(DEBUG_VERBOSE,"s3c2410_queue: ep%x len %d\n",ep->bEndpointAddress,_req->length);
++	
++	if (ep->bEndpointAddress) {
++		udc_writel(ep->bEndpointAddress&0x7F,S3C2410_UDC_INDEX_REG);
++		ep_csr = udc_readl(ep->bEndpointAddress&USB_DIR_IN ? S3C2410_UDC_IN_CSR1_REG : S3C2410_UDC_OUT_CSR1_REG);
++		fifo_count=fifo_count_out();
++	}
++	else {
++		udc_writel(0,S3C2410_UDC_INDEX_REG);
++		ep_csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++	}
++	/* kickstart this i/o queue? */
++	if (list_empty(&ep->queue) && !ep->halted) {
++		if (ep->bEndpointAddress == 0 /* ep0 */) {
++			switch (dev->ep0state) {
++			case EP0_IN_DATA_PHASE:
++				if (write_fifo(ep, req)) {
++					dev->ep0state = EP0_IDLE;
++					req = NULL;
++				}
++				break;
++
++			case EP0_OUT_DATA_PHASE:
++				if ( (!_req->length) || ((ep_csr & 1) && read_fifo(ep,req))) {
++					dev->ep0state = EP0_IDLE;
++					req = NULL;
++				}
++				break;
++
++			default:
++				local_irq_restore(flags);
++				return -EL2HLT;
++			}
++		}
++		else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
++				&& (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) && write_fifo(ep, req)) {
++			req = NULL;
++		} else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) && fifo_count && read_fifo(ep, req)) {
++			req = NULL;
++		}
++
++	}
++
++	/* pio or dma irq handler advances the queue. */
++	if (likely (req != 0))
++		list_add_tail(&req->queue, &ep->queue);
++
++	local_irq_restore(flags);
++
++	dprintk(DEBUG_VERBOSE, "s3c2410_queue normal end\n");
++	return 0;
++}
++
++/*
++ * 	s3c2410_dequeue
++ */
++static int s3c2410_dequeue (struct usb_ep *_ep, struct usb_request *_req)
++{
++	struct s3c2410_ep	*ep;
++	struct s3c2410_udc	*udc;
++	int			retval = -EINVAL;
++	unsigned long		flags;
++	struct s3c2410_request	*req = NULL;
++
++    	dprintk(DEBUG_VERBOSE,"s3c2410_dequeue(ep=%p,req=%p)\n", _ep, _req);
++
++	if (!the_controller->driver)
++		return -ESHUTDOWN;
++
++	if (!_ep || !_req)
++		return retval;
++	ep = container_of (_ep, struct s3c2410_ep, ep);
++	udc = container_of (ep->gadget, struct s3c2410_udc, gadget);
++
++	local_irq_save (flags);
++
++	list_for_each_entry (req, &ep->queue, queue) {
++		if (&req->req == _req) {
++			list_del_init (&req->queue);
++			_req->status = -ECONNRESET;
++			retval = 0;
++			break;
++		}
++	}
++
++	if (retval == 0) {
++		dprintk(DEBUG_VERBOSE, "dequeued req %p from %s, len %d buf %p\n",
++				req, _ep->name, _req->length, _req->buf);
++
++		done(ep, req, -ECONNRESET);
++	}
++	local_irq_restore (flags);
++
++	return retval;
++}
++
++
++/*
++ * s3c2410_set_halt
++ */
++static int
++s3c2410_set_halt (struct usb_ep *_ep, int value)
++{
++	return 0;
++}
++
++
++static const struct usb_ep_ops s3c2410_ep_ops = {
++	.enable         = s3c2410_ep_enable,
++	.disable        = s3c2410_ep_disable,
++
++	.alloc_request  = s3c2410_alloc_request,
++	.free_request   = s3c2410_free_request,
++
++	.alloc_buffer   = s3c2410_alloc_buffer,
++	.free_buffer    = s3c2410_free_buffer,
++
++	.queue          = s3c2410_queue,
++	.dequeue        = s3c2410_dequeue,
++
++	.set_halt       = s3c2410_set_halt,
++};
++
++/*------------------------- usb_gadget_ops ----------------------------------*/
++
++/*
++ * 	s3c2410_g_get_frame
++ */
++static int s3c2410_g_get_frame (struct usb_gadget *_gadget)
++{
++	int tmp;
++
++	dprintk(DEBUG_VERBOSE,"s3c2410_g_get_frame()\n");
++
++	tmp = udc_readl(S3C2410_UDC_FRAME_NUM2_REG) << 8;
++	tmp |= udc_readl(S3C2410_UDC_FRAME_NUM1_REG);
++
++	return tmp & 0xffff;
++}
++
++/*
++ * 	s3c2410_wakeup
++ */
++static int s3c2410_wakeup (struct usb_gadget *_gadget)
++{
++
++	dprintk(DEBUG_NORMAL,"s3c2410_wakeup()\n");
++
++	return 0;
++}
++
++/*
++ * 	s3c2410_set_selfpowered
++ */
++static int s3c2410_set_selfpowered (struct usb_gadget *_gadget, int value)
++{
++	struct s3c2410_udc  *udc;
++
++	dprintk(DEBUG_NORMAL, "s3c2410_set_selfpowered()\n");
++
++	udc = container_of (_gadget, struct s3c2410_udc, gadget);
++
++	if (value)
++		udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
++	else
++		udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
++
++	return 0;
++}
++
++static void udc_disable(struct s3c2410_udc *dev);
++static void udc_enable(struct s3c2410_udc *dev);
++
++static int s3c2410_pullup (struct usb_gadget *_gadget, int is_on)
++{
++	struct s3c2410_udc  *udc;
++
++	dprintk(DEBUG_NORMAL, "s3c2410_pullup()\n");
++	
++	udc = container_of (_gadget, struct s3c2410_udc, gadget);
++
++	if (udc_info && udc_info->udc_command) {
++		if (is_on)
++			udc_enable(udc);
++		else {
++			if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
++				if (udc->driver && udc->driver->disconnect)
++					udc->driver->disconnect(&udc->gadget);
++			
++			}
++			udc_disable(udc);
++		}
++	}
++	else
++		return -EOPNOTSUPP;
++
++	return 0;
++}
++
++static const struct usb_gadget_ops s3c2410_ops = {
++	.get_frame          = s3c2410_g_get_frame,
++	.wakeup             = s3c2410_wakeup,
++	.set_selfpowered    = s3c2410_set_selfpowered,
++        .pullup             = s3c2410_pullup,
++};
++
++
++/*------------------------- gadget driver handling---------------------------*/
++/*
++ * udc_disable
++ */
++static void udc_disable(struct s3c2410_udc *dev)
++{
++	dprintk(DEBUG_NORMAL, "udc_disable called\n");
++
++	/* Disable all interrupts */
++	udc_writel(0x00, S3C2410_UDC_USB_INT_EN_REG);
++	udc_writel(0x00, S3C2410_UDC_EP_INT_EN_REG);
++
++	/* Clear the interrupt registers */
++	udc_writel(   S3C2410_UDC_USBINT_RESET  | \
++			S3C2410_UDC_USBINT_RESUME | \
++			S3C2410_UDC_USBINT_SUSPEND, \
++			S3C2410_UDC_USB_INT_REG);
++	udc_writel(   0x1F, S3C2410_UDC_EP_INT_REG);
++	
++
++	/* Good bye, cruel world */
++	if (udc_info && udc_info->udc_command)
++		udc_info->udc_command(S3C2410_UDC_P_DISABLE);
++
++	/* Set address to 0 */
++	/*udc_writel( 0x80, S3C2410_UDC_FUNC_ADDR_REG);*/
++
++	/* Set speed to unknown */
++	dev->gadget.speed = USB_SPEED_UNKNOWN;
++}
++/*
++ * udc_reinit
++ */
++static void udc_reinit(struct s3c2410_udc *dev)
++{
++	u32     i;
++
++	/* device/ep0 records init */
++	INIT_LIST_HEAD (&dev->gadget.ep_list);
++	INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
++	dev->ep0state = EP0_IDLE;
++
++
++	for (i = 0; i < S3C2410_ENDPOINTS; i++) {
++		struct s3c2410_ep *ep = &dev->ep[i];
++
++		if (i != 0)
++			list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
++
++		ep->dev = dev;
++		ep->desc = NULL;
++		ep->halted = 0;
++		INIT_LIST_HEAD (&ep->queue);
++	}
++}
++
++/*
++ * udc_enable
++ */
++static void udc_enable(struct s3c2410_udc *dev)
++{
++	int i;
++
++	dprintk(DEBUG_NORMAL, "udc_enable called\n");
++
++	/* dev->gadget.speed = USB_SPEED_UNKNOWN; */
++	dev->gadget.speed = USB_SPEED_FULL;
++
++	/* Set MAXP for all endpoints */
++	for (i = 0; i < S3C2410_ENDPOINTS; i++) {
++
++		udc_writel(i, S3C2410_UDC_INDEX_REG);
++		udc_writel((dev->ep[i].ep.maxpacket&0x7ff)>>3,S3C2410_UDC_MAXP_REG);
++	}
++
++	/* Set default power state */
++	udc_writel(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG);
++
++	/* Enable reset and suspend interrupt interrupts */
++	udc_writel(1<<2 | 1<<0 ,S3C2410_UDC_USB_INT_EN_REG);
++
++	/* Enable ep0 interrupt */
++	udc_writel(0x01,S3C2410_UDC_EP_INT_EN_REG);
++
++	/* time to say "hello, world" */
++	if (udc_info && udc_info->udc_command)
++		udc_info->udc_command(S3C2410_UDC_P_ENABLE);
++}
++
++
++/*
++ * 	nop_release
++ */
++static void nop_release (struct device *dev)
++{
++	        dprintk(DEBUG_NORMAL, "%s %s\n", __FUNCTION__, dev->bus_id);
++}
++/*
++ *	usb_gadget_register_driver
++ */
++int
++usb_gadget_register_driver (struct usb_gadget_driver *driver)
++{
++	struct s3c2410_udc *udc = the_controller;
++	int		retval;
++
++    	dprintk(DEBUG_NORMAL, "usb_gadget_register_driver() '%s'\n",
++		driver->driver.name);
++
++	/* Sanity checks */
++	if (!udc)
++		return -ENODEV;
++	if (udc->driver)
++		return -EBUSY;
++	if (!driver->bind || !driver->unbind || !driver->setup
++			|| driver->speed == USB_SPEED_UNKNOWN)
++		return -EINVAL;
++
++	/* Hook the driver */
++	udc->driver = driver;
++	udc->gadget.dev.driver = &driver->driver;
++
++	/*Bind the driver */
++	device_add(&udc->gadget.dev);
++	dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n", driver->driver.name);
++	if ((retval = driver->bind (&udc->gadget)) != 0) {
++		device_del(&udc->gadget.dev);
++		udc->driver = NULL;
++		udc->gadget.dev.driver = NULL;
++		return retval;
++	}
++
++        /* driver->driver.bus = 0; */
++
++	/* Enable udc */
++	udc_enable(udc);
++
++	return 0;
++}
++
++
++/*
++ * 	usb_gadget_unregister_driver
++ */
++int
++usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
++{
++	struct s3c2410_udc *udc = the_controller;
++
++	if (!udc)
++		return -ENODEV;
++	if (!driver || driver != udc->driver)
++		return -EINVAL;
++
++    	dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n",
++		driver->driver.name);
++
++	if (driver->disconnect)
++		driver->disconnect(&udc->gadget);
++
++	driver->unbind (&udc->gadget);
++	device_del(&udc->gadget.dev);
++	udc->driver = NULL;
++
++	device_release_driver (&udc->gadget.dev);
++
++	/* Disable udc */
++	udc_disable(udc);
++
++	return 0;
++}
++
++/*---------------------------------------------------------------------------*/
++static struct s3c2410_udc memory = {
++	.gadget = {
++		.ops		= &s3c2410_ops,
++		.ep0		= &memory.ep[0].ep,
++		.name		= gadget_name,
++		.dev = {
++			.bus_id		= "gadget",
++			.release	= nop_release,
++		},
++	},
++
++	/* control endpoint */
++	.ep[0] = {
++		.num		= 0,
++		.ep = {
++			.name		= ep0name,
++			.ops		= &s3c2410_ep_ops,
++			.maxpacket	= EP0_FIFO_SIZE,
++		},
++		.dev		= &memory,
++	},
++
++	/* first group of endpoints */
++	.ep[1] = {
++		.num		= 1,
++		.ep = {
++			.name		= "ep1-bulk",
++			.ops		= &s3c2410_ep_ops,
++			.maxpacket	= EP_FIFO_SIZE,
++		},
++		.dev		= &memory,
++		.fifo_size	= EP_FIFO_SIZE,
++		.bEndpointAddress = 1,
++		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
++	},
++	.ep[2] = {
++		.num		= 2,
++		.ep = {
++			.name		= "ep2-bulk",
++			.ops		= &s3c2410_ep_ops,
++			.maxpacket	= EP_FIFO_SIZE,
++		},
++		.dev		= &memory,
++		.fifo_size	= EP_FIFO_SIZE,
++		.bEndpointAddress = 2,
++		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
++	},
++	.ep[3] = {
++		.num		= 3,
++		.ep = {
++			.name		= "ep3-bulk",
++			.ops		= &s3c2410_ep_ops,
++			.maxpacket	= EP_FIFO_SIZE,
++		},
++		.dev		= &memory,
++		.fifo_size	= EP_FIFO_SIZE,
++		.bEndpointAddress = 3,
++		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
++	},
++	.ep[4] = {
++		.num		= 4,
++		.ep = {
++			.name		= "ep4-bulk",
++			.ops		= &s3c2410_ep_ops,
++			.maxpacket	= EP_FIFO_SIZE,
++		},
++		.dev		= &memory,
++		.fifo_size	= EP_FIFO_SIZE,
++		.bEndpointAddress = 4,
++		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
++	}
++
++};
++
++/*
++ *	probe - binds to the platform device
++ */
++static int s3c2410_udc_probe(struct platform_device *pdev)
++{
++	struct s3c2410_udc *udc = &memory;
++	int retval;
++
++	dprintk(DEBUG_NORMAL,"s3c2410_udc_probe\n");
++
++	usb_bus_clock = clk_get(NULL, "usb-bus");
++	if (IS_ERR(usb_bus_clock)) {
++		printk(KERN_INFO "failed to get usb bus clock source\n");
++		return PTR_ERR(usb_bus_clock);
++	}
++
++	clk_enable(usb_bus_clock);
++
++	udc_clock = clk_get(NULL, "usb-device");
++	if (IS_ERR(udc_clock)) {
++		printk(KERN_INFO "failed to get udc clock source\n");
++		return PTR_ERR(udc_clock);
++	}
++
++	clk_enable(udc_clock);
++
++	mdelay(10);
++
++	dprintk(DEBUG_VERBOSE, "got and enabled clocks\n");
++
++	if (strncmp(pdev->name, "s3c2440", 7) == 0) {
++		printk("Detected S3C2440 - increasing FIFO to 128 bytes\n");
++		memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE;
++		memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE;
++		memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE;
++		memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
++	}
++
++	spin_lock_init (&udc->lock);
++	udc_info = pdev->dev.platform_data;
++
++	rsrc_start = S3C2410_PA_USBDEV;
++	rsrc_len   = S3C24XX_SZ_USBDEV;
++
++	if (!request_mem_region(rsrc_start, rsrc_len, gadget_name))
++		return -EBUSY;
++
++	base_addr = ioremap(rsrc_start, rsrc_len);
++	if (!base_addr) {
++		retval = -ENOMEM;
++		goto err_mem;
++	}
++	
++	device_initialize(&udc->gadget.dev);
++	udc->gadget.dev.parent = &pdev->dev;
++	udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
++
++	the_controller = udc;
++	platform_set_drvdata(pdev, udc);
++
++	udc_disable(udc);
++	udc_reinit(udc);
++
++	/* irq setup after old hardware state is cleaned up */
++	retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
++		SA_INTERRUPT, gadget_name, udc);
++
++	if (retval != 0) {
++		printk(KERN_ERR "%s: can't get irq %i, err %d\n",
++			gadget_name, IRQ_USBD, retval);
++		retval = -EBUSY;
++		goto err_map;
++	}
++	dprintk(DEBUG_VERBOSE, "%s: got irq %i\n", gadget_name, IRQ_USBD);
++
++#ifdef ENABLE_SYSFS
++	/* create device files */
++	device_create_file(&pdev->dev, &dev_attr_regs);
++#endif
++	return 0;
++
++err_map:
++	iounmap(base_addr);
++err_mem:
++	release_mem_region(rsrc_start, rsrc_len);
++
++	return retval;
++}
++
++/*
++ * 	s3c2410_udc_remove
++ */
++static int s3c2410_udc_remove(struct platform_device *pdev)
++{
++	struct s3c2410_udc *udc = platform_get_drvdata(pdev);
++
++	dprintk(DEBUG_NORMAL, "s3c2410_udc_remove\n");
++	usb_gadget_unregister_driver(udc->driver);
++
++	if (udc->got_irq) {
++		free_irq(IRQ_USBD, udc);
++		udc->got_irq = 0;
++	}
++
++	iounmap(base_addr);
++	release_mem_region(rsrc_start, rsrc_len);
++	
++	platform_set_drvdata(pdev, NULL);
++	kfree(udc);
++
++	if (!IS_ERR(udc_clock) && udc_clock != NULL) {
++		clk_disable(udc_clock);
++		clk_put(udc_clock);
++		udc_clock = NULL;
++	}
++
++	if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
++		clk_disable(usb_bus_clock);
++		clk_put(usb_bus_clock);
++		usb_bus_clock = NULL;
++	}
++
++	return 0;
++}
++
++static struct platform_driver udc_driver_2410 = {
++	.driver		= {
++		.name 	= "s3c2410-usbgadget",
++		.owner	= THIS_MODULE,
++	},
++	.probe          = s3c2410_udc_probe,
++	.remove         = s3c2410_udc_remove,
++};
++
++static struct platform_driver udc_driver_2440 = {
++	.driver		= {
++		.name	= "s3c2440-usbgadget",
++		.owner	= THIS_MODULE,
++	},
++	.probe          = s3c2410_udc_probe,
++	.remove         = s3c2410_udc_remove,
++};
++
++static int __init udc_init(void)
++{
++	int retval;
++
++	dprintk(DEBUG_NORMAL, "%s: version %s\n", gadget_name, DRIVER_VERSION);
++
++	retval = platform_driver_register(&udc_driver_2410);
++	if (retval)
++		return retval;
++
++	return platform_driver_register(&udc_driver_2440);
++}
++
++static void __exit udc_exit(void)
++{
++	platform_driver_unregister(&udc_driver_2410);
++	platform_driver_unregister(&udc_driver_2440);
++}
++
++
++EXPORT_SYMBOL (usb_gadget_unregister_driver);
++EXPORT_SYMBOL (usb_gadget_register_driver);
++
++module_init(udc_init);
++module_exit(udc_exit);
++
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_VERSION(DRIVER_VERSION);
++MODULE_LICENSE("GPL");
+Index: linux-2.6.17.14-fic4.test/drivers/usb/gadget/s3c2410_udc.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/drivers/usb/gadget/s3c2410_udc.h	2006-12-11 21:44:31.000000000 +0100
+@@ -0,0 +1,187 @@
++#ifndef _S3C2410_UDC_H
++#define _S3C2410_UDC_H
++
++struct s3c2410_ep {
++	struct list_head		queue;
++	unsigned long			last_io;	/* jiffies timestamp */
++	struct usb_gadget		*gadget;
++	struct s3c2410_udc		*dev;
++	const struct usb_endpoint_descriptor *desc;
++	struct usb_ep			ep;
++	u8				num;
++
++	unsigned short			fifo_size;
++	u8				bEndpointAddress;
++	u8				bmAttributes;
++
++	unsigned			halted : 1;
++	unsigned			already_seen : 1;
++	unsigned			setup_stage : 1;
++};
++
++
++/* Warning : ep0 has a fifo of 16 bytes */
++/* Don't try to set 32 or 64            */
++#define EP0_FIFO_SIZE		16
++#define EP_FIFO_SIZE		64
++#define DEFAULT_POWER_STATE	0x00
++
++#define S3C2440_EP_FIFO_SIZE	128
++
++static const char ep0name [] = "ep0";
++
++static const char *const ep_name[] = {
++	ep0name,                                /* everyone has ep0 */
++	/* s3c2410 four bidirectional bulk endpoints */
++	"ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk",
++};
++
++#define S3C2410_ENDPOINTS       ARRAY_SIZE(ep_name)
++
++struct s3c2410_request {
++	struct list_head		queue;		/* ep's requests */
++	struct usb_request		req;
++};
++
++enum ep0_state {
++        EP0_IDLE,
++        EP0_IN_DATA_PHASE,
++        EP0_OUT_DATA_PHASE,
++        EP0_END_XFER,
++        EP0_STALL,
++};
++
++static const char *ep0states[]= {
++        "EP0_IDLE",
++        "EP0_IN_DATA_PHASE",
++        "EP0_OUT_DATA_PHASE",
++        "EP0_END_XFER",
++        "EP0_STALL",
++};
++
++struct s3c2410_udc {
++	spinlock_t			lock;
++
++	struct s3c2410_ep		ep[S3C2410_ENDPOINTS];
++	int				address;
++	struct usb_gadget		gadget;
++	struct usb_gadget_driver	*driver;
++	struct s3c2410_request		fifo_req;
++	u8				fifo_buf[EP_FIFO_SIZE];
++	u16				devstatus;
++
++	u32				port_status;
++    	int 	    	    	    	ep0state;
++
++	unsigned			got_irq : 1;
++
++	unsigned			req_std : 1;
++	unsigned			req_config : 1;
++	unsigned			req_pending : 1;
++};
++
++/****************** MACROS ******************/
++/* #define BIT_MASK	BIT_MASK*/
++#define BIT_MASK	0xFF
++
++#define maskb(base,v,m,a)      \
++	        writeb((readb(base+a) & ~(m))|((v)&(m)), (base+a))
++
++#define maskw(base,v,m,a)      \
++	        writew((readw(base+a) & ~(m))|((v)&(m)), (base+a))
++
++#define maskl(base,v,m,a)      \
++	        writel((readl(base+a) & ~(m))|((v)&(m)), (base+a))
++
++#define clear_ep0_sst(base) do {			\
++    	S3C2410_UDC_SETIX(base,EP0); 			\
++	writel(0x00, base+S3C2410_UDC_EP0_CSR_REG); 	\
++} while(0)
++
++#define clear_ep0_se(base) do {				\
++    	S3C2410_UDC_SETIX(base,EP0); 			\
++	maskl(base,S3C2410_UDC_EP0_CSR_SSE,		\
++	    	BIT_MASK, S3C2410_UDC_EP0_CSR_REG); 	\
++} while(0)
++
++#define clear_ep0_opr(base) do {			\
++   	S3C2410_UDC_SETIX(base,EP0);			\
++	maskl(base,S3C2410_UDC_EP0_CSR_SOPKTRDY,	\
++		BIT_MASK, S3C2410_UDC_EP0_CSR_REG); 	\
++} while(0)
++
++#define set_ep0_ipr(base) do {				\
++   	S3C2410_UDC_SETIX(base,EP0);			\
++	maskl(base,S3C2410_UDC_EP0_CSR_IPKRDY,		\
++		BIT_MASK, S3C2410_UDC_EP0_CSR_REG); 	\
++} while(0)
++
++#define set_ep0_de(base) do {				\
++   	S3C2410_UDC_SETIX(base,EP0);			\
++	maskl(base,S3C2410_UDC_EP0_CSR_DE,		\
++		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);	\
++} while(0)
++
++#if 0
++#define set_ep0_ss(base) do {				\
++   	S3C2410_UDC_SETIX(base,EP0);				\
++	maskl(base,S3C2410_UDC_EP0_CSR_SENDSTL|S3C2410_UDC_EP0_CSR_SOPKTRDY,	\
++		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);	\
++} while(0)
++#else
++#define set_ep0_ss(base) do {				\
++   	S3C2410_UDC_SETIX(base,EP0);				\
++	maskl(base,S3C2410_UDC_EP0_CSR_SENDSTL,	\
++		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);	\
++} while(0)
++#endif
++
++#define set_ep0_de_out(base) do {			\
++   	S3C2410_UDC_SETIX(base,EP0);			\
++	maskl(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY 	\
++		| S3C2410_UDC_EP0_CSR_DE),		\
++		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);	\
++} while(0)
++
++#define set_ep0_sse_out(base) do {			\
++   	S3C2410_UDC_SETIX(base,EP0);			\
++	maskl(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY 	\
++		| S3C2410_UDC_EP0_CSR_SSE),		\
++		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);	\
++} while(0)
++
++#define set_ep0_de_in(base) do {			\
++   	S3C2410_UDC_SETIX(base,EP0);			\
++	maskl(base,(S3C2410_UDC_EP0_CSR_IPKRDY		\
++		| S3C2410_UDC_EP0_CSR_DE),		\
++		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);		\
++} while(0)
++
++
++
++#define clear_stall_ep1_out(base) do {			\
++   	S3C2410_UDC_SETIX(base,EP1);			\
++	orl(0,base+S3C2410_UDC_OUT_CSR1_REG);		\
++} while(0)
++
++
++#define clear_stall_ep2_out(base) do {			\
++   	S3C2410_UDC_SETIX(base,EP2);			\
++	orl(0, base+S3C2410_UDC_OUT_CSR1_REG);		\
++} while(0)
++
++
++#define clear_stall_ep3_out(base) do {			\
++   	S3C2410_UDC_SETIX(base,EP3);			\
++	orl(0,base+S3C2410_UDC_OUT_CSR1_REG);		\
++} while(0)
++
++
++#define clear_stall_ep4_out(base) do {			\
++   	S3C2410_UDC_SETIX(base,EP4);			\
++	orl(0, base+S3C2410_UDC_OUT_CSR1_REG);		\
++} while(0)
++
++#endif
++
++
+Index: linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/regs-udc.h
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/include/asm-arm/arch-s3c2410/regs-udc.h	2006-12-11 21:44:29.000000000 +0100
++++ linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/regs-udc.h	2006-12-11 21:44:31.000000000 +0100
+@@ -17,8 +17,7 @@
+ #ifndef __ASM_ARCH_REGS_UDC_H
+ #define __ASM_ARCH_REGS_UDC_H
+ 
+-
+-#define S3C2410_USBDREG(x) ((x) + S3C24XX_VA_USBDEV)
++#define S3C2410_USBDREG(x) (x)
+ 
+ #define S3C2410_UDC_FUNC_ADDR_REG	S3C2410_USBDREG(0x0140)
+ #define S3C2410_UDC_PWR_REG		S3C2410_USBDREG(0x0144)
+@@ -142,8 +141,8 @@
+ #define S3C2410_UDC_OCSR2_ISO		(1<<6) // R/W
+ #define S3C2410_UDC_OCSR2_DMAIEN	(1<<5) // R/W
+ 
+-#define S3C2410_UDC_SETIX(x)	    \
+-	__raw_writel(S3C2410_UDC_INDEX_ ## x, S3C2410_UDC_INDEX_REG);
++#define S3C2410_UDC_SETIX(base,x)	    \
++	writel(S3C2410_UDC_INDEX_ ## x, base+S3C2410_UDC_INDEX_REG);
+ 
+ 
+ #define S3C2410_UDC_EP0_CSR_OPKRDY	(1<<0)
+Index: linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/udc.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic4.test/include/asm-arm/arch-s3c2410/udc.h	2006-12-11 21:44:31.000000000 +0100
+@@ -0,0 +1,32 @@
++/* linux/include/asm/arch-s3c2410/udc.h
++ *
++ * Copyright (c) 2005 Arnaud Patard <arnaud.patard at rtp-net.org>
++ *
++ *
++ * 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.
++ *
++ *
++ *  Changelog:
++ *	14-Mar-2005	RTP	Created file
++ *	02-Aug-2005	RTP	File rename
++ *	07-Sep-2005	BJD	Minor cleanups, changed cmd to enum
++*/
++
++#ifndef __ASM_ARM_ARCH_UDC_H
++#define __ASM_ARM_ARCH_UDC_H
++
++enum s3c2410_udc_cmd_e {
++	S3C2410_UDC_P_ENABLE	= 1,	/* Pull-up enable        */
++	S3C2410_UDC_P_DISABLE	= 2,	/* Pull-up disable       */
++	S3C2410_UDC_P_RESET	= 3,	/* UDC reset, in case of */
++};
++
++struct s3c2410_udc_mach_info {
++	void	(*udc_command)(enum s3c2410_udc_cmd_e);
++};
++
++extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *);
++
++#endif /* __ASM_ARM_ARCH_UDC_H */
+Index: linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-qt2410.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/arch/arm/mach-s3c2410/mach-qt2410.c	2006-12-11 21:44:30.000000000 +0100
++++ linux-2.6.17.14-fic4.test/arch/arm/mach-s3c2410/mach-qt2410.c	2006-12-11 21:44:34.000000000 +0100
+@@ -50,6 +50,7 @@
+ #include <asm/arch/regs-serial.h>
+ #include <asm/arch/fb.h>
+ #include <asm/arch/nand.h>
++#include <asm/arch/udc.h>
+ #include <asm/arch/spi.h>
+ #include <asm/arch/spi-gpio.h>
+ 
+@@ -308,6 +309,26 @@
+ 	},
+ };
+ 
++static void qt2410_udc_pullup(enum s3c2410_udc_cmd_e cmd)
++{
++	printk(KERN_DEBUG "udc: pullup(%d)\n", cmd);
++
++	switch (cmd) {
++	case S3C2410_UDC_P_ENABLE:
++		break;
++	case S3C2410_UDC_P_DISABLE:
++		break;
++	case S3C2410_UDC_P_RESET:
++		break;
++	default:
++		break;
++	}
++}
++
++static struct s3c2410_udc_mach_info qt2410_udc_cfg = {
++	.udc_command	= qt2410_udc_pullup,
++};
++
+ static void __init qt2410_map_io(void)
+ {
+ 	s3c24xx_init_io(qt2410_iodesc, ARRAY_SIZE(qt2410_iodesc));
+@@ -332,6 +353,7 @@
+ 
+ 	s3c_device_nand.dev.platform_data = &qt2410_nand_info;
+ 	s3c24xx_fb_set_platdata(&qt2410_lcd_cfg);
++	s3c24xx_udc_set_platdata(&qt2410_udc_cfg);
+ 
+ 	s3c2410_gpio_cfgpin(S3C2410_GPB5, S3C2410_GPIO_OUTPUT);
+ 	spi_register_board_info(qt2410_spi_board_info,





More information about the commitlog mailing list