arch/arm/Kconfig                        |   13 +-
 arch/arm/configs/em_x270_defconfig      |  367 +++--
 arch/arm/mach-pxa/Kconfig               |    8 +
 arch/arm/mach-pxa/Makefile              |    9 +-
 arch/arm/mach-pxa/cpu-pxa.c             |  442 ++++++
 arch/arm/mach-pxa/em-x270-devices.c     |  331 +++++
 arch/arm/mach-pxa/em-x270-lcd.c         |  223 +++
 arch/arm/mach-pxa/em-x270-pm.c          |  892 ++++++++++++
 arch/arm/mach-pxa/em-x270.c             |  127 ++-
 arch/arm/mach-pxa/pwr-i2c.c             |  539 +++++++
 arch/arm/mach-pxa/pxa27x.c              |    6 +-
 arch/arm/mach-pxa/spitz.c               |   27 +
 drivers/i2c/chips/Kconfig               |   13 +
 drivers/i2c/chips/Makefile              |    1 +
 drivers/i2c/chips/da9030.c              | 1213 ++++++++++++++++
 drivers/i2c/chips/da9030.h              |  282 ++++
 drivers/input/touchscreen/Kconfig       |   43 +
 drivers/input/touchscreen/Makefile      |   14 +
 drivers/input/touchscreen/wm9705.c      |  360 +++++
 drivers/input/touchscreen/wm9712.c      |  464 ++++++
 drivers/input/touchscreen/wm9713.c      |  461 ++++++
 drivers/input/touchscreen/wm97xx-core.c |  859 +++++++++++
 drivers/leds/Kconfig                    |    6 +
 drivers/leds/Makefile                   |    1 +
 drivers/leds/leds-em-x270.c             |   99 ++
 drivers/mtd/chips/jedec_probe.c         |   58 +-
 drivers/net/dm9000.c                    |    1 +
 drivers/power/Kconfig                   |    6 +
 drivers/power/Makefile                  |    1 +
 drivers/power/em_x270_battery.c         |  579 ++++++++
 drivers/usb/gadget/Kconfig              |   20 +
 drivers/usb/gadget/Makefile             |    1 +
 drivers/usb/gadget/epautoconf.c         |    9 +-
 drivers/usb/gadget/ether.c              |   63 +-
 drivers/usb/gadget/file_storage.c       |   11 +-
 drivers/usb/gadget/pxa27x_udc.c         | 2387 +++++++++++++++++++++++++++++++
 drivers/usb/gadget/pxa27x_udc.h         |  298 ++++
 drivers/usb/gadget/pxa2xx_udc.h         |    7 +-
 drivers/usb/gadget/serial.c             |   18 +-
 drivers/usb/gadget/zero.c               |   13 +-
 drivers/video/backlight/Kconfig         |    2 +-
 include/asm-arm/arch-pxa/pwr-i2c.h      |   61 +
 include/linux/da9030.h                  |  118 ++
 include/linux/usb_gadget.h              |   23 +-
 include/linux/wm97xx.h                  |  291 ++++
 sound/soc/pxa/Kconfig                   |    9 +
 sound/soc/pxa/Makefile                  |    2 +
 sound/soc/pxa/em-x270.c                 |  137 ++
 48 files changed, 10742 insertions(+), 173 deletions(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 691aae3..cf1dbc2 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -858,7 +858,7 @@ config KEXEC
 
 endmenu
 
-if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX )
+if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX || ARCH_PXA)
 
 menu "CPU Frequency scaling"
 
@@ -894,6 +894,15 @@ config CPU_FREQ_IMX
 
 	  If in doubt, say N.
 
+config CPU_FREQ_PXA
+	tristate "CPUfreq driver for PXA2xx CPUs"
+	depends on CPU_FREQ && ARCH_PXA
+	default y
+	help
+	  Thes enables the CPUfreq driver for PXA2xx CPUs.
+
+	  If in doubt, say Y.
+
 endmenu
 
 endif
@@ -1029,6 +1038,8 @@ source "drivers/spi/Kconfig"
 
 source "drivers/w1/Kconfig"
 
+source "drivers/power/Kconfig"
+
 source "drivers/hwmon/Kconfig"
 
 #source "drivers/l3/Kconfig"
diff --git a/arch/arm/configs/em_x270_defconfig b/arch/arm/configs/em_x270_defconfig
index 6bea090..3246136 100644
--- a/arch/arm/configs/em_x270_defconfig
+++ b/arch/arm/configs/em_x270_defconfig
@@ -1,13 +1,13 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.22
-# Mon Jul  9 15:18:20 2007
+# Linux kernel version: 2.6.23-rc9
+# Tue Oct  9 11:19:21 2007
 #
 CONFIG_ARM=y
 CONFIG_SYS_SUPPORTS_APM_EMULATION=y
 CONFIG_GENERIC_GPIO=y
 CONFIG_GENERIC_TIME=y
-# CONFIG_GENERIC_CLOCKEVENTS is not set
+CONFIG_GENERIC_CLOCKEVENTS=y
 CONFIG_MMU=y
 # CONFIG_NO_IOPORT is not set
 CONFIG_GENERIC_HARDIRQS=y
@@ -27,25 +27,20 @@ CONFIG_VECTORS_BASE=0xffff0000
 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
 
 #
-# Code maturity level options
+# General setup
 #
 CONFIG_EXPERIMENTAL=y
 CONFIG_BROKEN_ON_SMP=y
 CONFIG_INIT_ENV_ARG_LIMIT=32
-
-#
-# General setup
-#
 CONFIG_LOCALVERSION="-em-x270"
 # CONFIG_LOCALVERSION_AUTO is not set
 CONFIG_SWAP=y
 CONFIG_SYSVIPC=y
-# CONFIG_IPC_NS is not set
 CONFIG_SYSVIPC_SYSCTL=y
 # CONFIG_POSIX_MQUEUE is not set
 # CONFIG_BSD_PROCESS_ACCT is not set
 # CONFIG_TASKSTATS is not set
-# CONFIG_UTS_NS is not set
+# CONFIG_USER_NS is not set
 # CONFIG_AUDIT is not set
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
@@ -71,34 +66,27 @@ CONFIG_FUTEX=y
 CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
 CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
 CONFIG_EVENTFD=y
 CONFIG_SHMEM=y
 CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_SLAB=y
-# CONFIG_SLUB is not set
+CONFIG_SLUB_DEBUG=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
 # CONFIG_SLOB is not set
 CONFIG_RT_MUTEXES=y
 # CONFIG_TINY_SHMEM is not set
 CONFIG_BASE_SMALL=0
-
-#
-# Loadable module support
-#
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_MODULE_FORCE_UNLOAD=y
 # CONFIG_MODVERSIONS is not set
 # CONFIG_MODULE_SRCVERSION_ALL is not set
 CONFIG_KMOD=y
-
-#
-# Block layer
-#
 CONFIG_BLOCK=y
 # CONFIG_LBD is not set
 # CONFIG_BLK_DEV_IO_TRACE is not set
 # CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
 
 #
 # IO Schedulers
@@ -139,6 +127,7 @@ CONFIG_DEFAULT_IOSCHED="anticipatory"
 # CONFIG_ARCH_L7200 is not set
 # CONFIG_ARCH_KS8695 is not set
 # CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_MXC is not set
 # CONFIG_ARCH_PNX4008 is not set
 CONFIG_ARCH_PXA=y
 # CONFIG_ARCH_RPC is not set
@@ -160,6 +149,15 @@ CONFIG_ARCH_PXA=y
 # CONFIG_MACH_TRIZEPS4 is not set
 CONFIG_MACH_EM_X270=y
 CONFIG_PXA27x=y
+CONFIG_PXA_PWR_I2C=y
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
 
 #
 # Processor Type
@@ -185,6 +183,7 @@ CONFIG_XSCALE_PMU=y
 #
 # Bus support
 #
+# CONFIG_PCI_SYSCALL is not set
 # CONFIG_ARCH_SUPPORTS_MSI is not set
 
 #
@@ -196,8 +195,9 @@ CONFIG_XSCALE_PMU=y
 # Kernel Features
 #
 # CONFIG_TICK_ONESHOT is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
 # CONFIG_PREEMPT is not set
-# CONFIG_NO_IDLE_HZ is not set
 CONFIG_HZ=100
 CONFIG_AEABI=y
 CONFIG_OABI_COMPAT=y
@@ -212,6 +212,8 @@ CONFIG_FLAT_NODE_MEM_MAP=y
 CONFIG_SPLIT_PTLOCK_CPUS=4096
 # CONFIG_RESOURCES_64BIT is not set
 CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
 CONFIG_ALIGNMENT_TRAP=y
 
 #
@@ -219,11 +221,28 @@ CONFIG_ALIGNMENT_TRAP=y
 #
 CONFIG_ZBOOT_ROM_TEXT=0x0
 CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_CMDLINE=""
+CONFIG_CMDLINE="root=/dev/mtdblock2 rootfstype=jffs2 console=ttyS0,115200"
 # CONFIG_XIP_KERNEL is not set
 # CONFIG_KEXEC is not set
 
 #
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+# CONFIG_CPU_FREQ_DEBUG is not set
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+CONFIG_CPU_FREQ_PXA=y
+
+#
 # Floating point emulation
 #
 
@@ -238,8 +257,8 @@ CONFIG_FPE_NWFPE=y
 # Userspace binary formats
 #
 CONFIG_BINFMT_ELF=y
-# CONFIG_BINFMT_AOUT is not set
-# CONFIG_BINFMT_MISC is not set
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
 
 #
 # Power management options
@@ -247,8 +266,10 @@ CONFIG_BINFMT_ELF=y
 CONFIG_PM=y
 CONFIG_PM_LEGACY=y
 # CONFIG_PM_DEBUG is not set
-# CONFIG_PM_SYSFS_DEPRECATED is not set
-CONFIG_APM_EMULATION=m
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND_UP_POSSIBLE=y
+CONFIG_SUSPEND=y
+CONFIG_APM_EMULATION=y
 
 #
 # Networking
@@ -316,6 +337,7 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
 # QoS and/or fair queueing
 #
 # CONFIG_NET_SCHED is not set
+CONFIG_NET_SCH_FIFO=y
 
 #
 # Network testing
@@ -350,9 +372,12 @@ CONFIG_BT_HCIBFUSB=m
 #
 # Wireless
 #
-# CONFIG_CFG80211 is not set
-# CONFIG_WIRELESS_EXT is not set
-# CONFIG_MAC80211 is not set
+CONFIG_CFG80211=m
+CONFIG_WIRELESS_EXT=y
+CONFIG_MAC80211=m
+# CONFIG_MAC80211_LEDS is not set
+CONFIG_MAC80211_DEBUGFS=y
+# CONFIG_MAC80211_DEBUG is not set
 CONFIG_IEEE80211=m
 # CONFIG_IEEE80211_DEBUG is not set
 CONFIG_IEEE80211_CRYPT_WEP=m
@@ -360,6 +385,7 @@ CONFIG_IEEE80211_CRYPT_CCMP=m
 # CONFIG_IEEE80211_CRYPT_TKIP is not set
 # CONFIG_IEEE80211_SOFTMAC is not set
 # CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
 
 #
 # Device Drivers
@@ -374,10 +400,6 @@ CONFIG_FW_LOADER=y
 # CONFIG_DEBUG_DRIVER is not set
 # CONFIG_DEBUG_DEVRES is not set
 # CONFIG_SYS_HYPERVISOR is not set
-
-#
-# Connector - unified userspace <-> kernelspace linker
-#
 # CONFIG_CONNECTOR is not set
 CONFIG_MTD=y
 # CONFIG_MTD_DEBUG is not set
@@ -402,11 +424,10 @@ CONFIG_MTD_BLOCK=y
 #
 # RAM/ROM/Flash chip drivers
 #
-# CONFIG_MTD_CFI is not set
-# CONFIG_MTD_JEDECPROBE is not set
-# CONFIG_MTD_CFI_NOSWAP is not set
-# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
-# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
 CONFIG_MTD_MAP_BANK_WIDTH_1=y
 CONFIG_MTD_MAP_BANK_WIDTH_2=y
 CONFIG_MTD_MAP_BANK_WIDTH_4=y
@@ -417,14 +438,25 @@ CONFIG_MTD_CFI_I1=y
 CONFIG_MTD_CFI_I2=y
 # CONFIG_MTD_CFI_I4 is not set
 # CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_CFI_UTIL=y
 # CONFIG_MTD_RAM is not set
 # CONFIG_MTD_ROM is not set
 # CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_XIP is not set
 
 #
 # Mapping drivers for chip access
 #
 # CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_START=0x0
+CONFIG_MTD_PHYSMAP_LEN=0x100000
+CONFIG_MTD_PHYSMAP_BANKWIDTH=2
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+# CONFIG_MTD_IMPA7 is not set
 # CONFIG_MTD_SHARP_SL is not set
 # CONFIG_MTD_PLATRAM is not set
 
@@ -457,21 +489,17 @@ CONFIG_MTD_NAND_PLATFORM=y
 #
 # UBI - Unsorted block images
 #
-# CONFIG_MTD_UBI is not set
+CONFIG_MTD_UBI=m
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MTD_UBI_BEB_RESERVE=1
+CONFIG_MTD_UBI_GLUEBI=y
 
 #
-# Parallel port support
+# UBI debugging options
 #
+# CONFIG_MTD_UBI_DEBUG is not set
 # CONFIG_PARPORT is not set
-
-#
-# Plug and Play support
-#
-# CONFIG_PNPACPI is not set
-
-#
-# Block devices
-#
+CONFIG_BLK_DEV=y
 # CONFIG_BLK_DEV_COW_COMMON is not set
 CONFIG_BLK_DEV_LOOP=y
 # CONFIG_BLK_DEV_CRYPTOLOOP is not set
@@ -490,6 +518,7 @@ CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
 #
 # CONFIG_RAID_ATTRS is not set
 CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
 # CONFIG_SCSI_TGT is not set
 # CONFIG_SCSI_NETLINK is not set
 # CONFIG_SCSI_PROC_FS is not set
@@ -519,36 +548,23 @@ CONFIG_SCSI_WAIT_SCAN=m
 # CONFIG_SCSI_SPI_ATTRS is not set
 # CONFIG_SCSI_FC_ATTRS is not set
 # CONFIG_SCSI_ISCSI_ATTRS is not set
-# CONFIG_SCSI_SAS_ATTRS is not set
 # CONFIG_SCSI_SAS_LIBSAS is not set
-
-#
-# SCSI low-level drivers
-#
+CONFIG_SCSI_LOWLEVEL=y
 # CONFIG_ISCSI_TCP is not set
 # CONFIG_SCSI_DEBUG is not set
 # CONFIG_ATA is not set
-
-#
-# Multi-device support (RAID and LVM)
-#
 # CONFIG_MD is not set
-
-#
-# Network device support
-#
 CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
 # CONFIG_DUMMY is not set
 # CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
 # CONFIG_EQUALIZER is not set
 # CONFIG_TUN is not set
 # CONFIG_PHYLIB is not set
-
-#
-# Ethernet (10 or 100Mbit)
-#
 CONFIG_NET_ETHERNET=y
 CONFIG_MII=y
+# CONFIG_AX88796 is not set
 # CONFIG_SMC91X is not set
 CONFIG_DM9000=y
 # CONFIG_SMC911X is not set
@@ -571,16 +587,22 @@ CONFIG_DM9000=y
 # CONFIG_USB_USBNET_MII is not set
 # CONFIG_USB_USBNET is not set
 # CONFIG_WAN is not set
-# CONFIG_PPP is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_MPPE=m
+# CONFIG_PPPOE is not set
+CONFIG_PPPOL2TP=m
 # CONFIG_SLIP is not set
+CONFIG_SLHC=m
 # CONFIG_SHAPER is not set
 # CONFIG_NETCONSOLE is not set
 # CONFIG_NETPOLL is not set
 # CONFIG_NET_POLL_CONTROLLER is not set
-
-#
-# ISDN subsystem
-#
 # CONFIG_ISDN is not set
 
 #
@@ -612,16 +634,21 @@ CONFIG_INPUT_KEYBOARD=y
 # CONFIG_KEYBOARD_XTKBD is not set
 # CONFIG_KEYBOARD_NEWTON is not set
 # CONFIG_KEYBOARD_STOWAWAY is not set
-CONFIG_KEYBOARD_PXA27x=m
+CONFIG_KEYBOARD_PXA27x=y
 # CONFIG_KEYBOARD_GPIO is not set
 # CONFIG_INPUT_MOUSE is not set
 # CONFIG_INPUT_JOYSTICK is not set
 # CONFIG_INPUT_TABLET is not set
 CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
 # CONFIG_TOUCHSCREEN_GUNZE is not set
 # CONFIG_TOUCHSCREEN_ELO is not set
 # CONFIG_TOUCHSCREEN_MTOUCH is not set
 # CONFIG_TOUCHSCREEN_MK712 is not set
+CONFIG_TOUCHSCREEN_WM97XX=y
+# CONFIG_TOUCHSCREEN_WM9705 is not set
+CONFIG_TOUCHSCREEN_WM9712=y
+# CONFIG_TOUCHSCREEN_WM9713 is not set
 # CONFIG_TOUCHSCREEN_PENMOUNT is not set
 # CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
 # CONFIG_TOUCHSCREEN_TOUCHWIN is not set
@@ -660,58 +687,91 @@ CONFIG_SERIAL_PXA_CONSOLE=y
 CONFIG_SERIAL_CORE=y
 CONFIG_SERIAL_CORE_CONSOLE=y
 CONFIG_UNIX98_PTYS=y
-CONFIG_LEGACY_PTYS=y
-CONFIG_LEGACY_PTY_COUNT=256
-
-#
-# IPMI
-#
+# CONFIG_LEGACY_PTYS is not set
 # CONFIG_IPMI_HANDLER is not set
 # CONFIG_WATCHDOG is not set
 CONFIG_HW_RANDOM=m
 # CONFIG_NVRAM is not set
 # CONFIG_R3964 is not set
 # CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
 
 #
-# TPM devices
+# I2C Algorithms
 #
-# CONFIG_TCG_TPM is not set
-# CONFIG_I2C is not set
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA is not set
 
 #
-# SPI support
+# I2C Hardware Bus support
 #
-# CONFIG_SPI is not set
-# CONFIG_SPI_MASTER is not set
+# CONFIG_I2C_GPIO is not set
+CONFIG_I2C_PXA=y
+# CONFIG_I2C_PXA_SLAVE is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TINY_USB is not set
 
 #
-# Dallas's 1-wire bus
+# Miscellaneous I2C Chip support
 #
-# CONFIG_W1 is not set
-# CONFIG_HWMON is not set
+# CONFIG_SENSORS_DS1337 is not set
+# CONFIG_SENSORS_DS1374 is not set
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+CONFIG_DA9030=y
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
 
 #
-# Misc devices
+# SPI support
 #
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+# CONFIG_W1 is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+CONFIG_APM_POWER=y
+# CONFIG_BATTERY_DS2760 is not set
+# CONFIG_BATTERY_EM_X270 is not set
+# CONFIG_HWMON is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
 
 #
 # Multifunction device drivers
 #
 # CONFIG_MFD_SM501 is not set
-
-#
-# LED devices
-#
-# CONFIG_NEW_LEDS is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=m
 
 #
 # LED drivers
 #
+CONFIG_LEDS_GPIO=m
+CONFIG_LEDS_EM_X270=m
 
 #
 # LED Triggers
 #
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=m
+CONFIG_LEDS_TRIGGER_HEARTBEAT=m
 
 #
 # Multimedia devices
@@ -723,13 +783,17 @@ CONFIG_HW_RANDOM=m
 #
 # Graphics support
 #
-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CORGI=y
 
 #
 # Display device support
 #
 # CONFIG_DISPLAY_SUPPORT is not set
 # CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
 CONFIG_FB=y
 # CONFIG_FIRMWARE_EDID is not set
 # CONFIG_FB_DDC is not set
@@ -752,7 +816,7 @@ CONFIG_FB_DEFERRED_IO=y
 #
 # CONFIG_FB_S1D13XXX is not set
 CONFIG_FB_PXA=y
-# CONFIG_FB_PXA_PARAMETERS is not set
+CONFIG_FB_PXA_PARAMETERS=y
 # CONFIG_FB_MBX is not set
 # CONFIG_FB_VIRTUAL is not set
 
@@ -762,6 +826,7 @@ CONFIG_FB_PXA=y
 # CONFIG_VGA_CONSOLE is not set
 CONFIG_DUMMY_CONSOLE=y
 CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
 # CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
 # CONFIG_FONTS is not set
 CONFIG_FONT_8x8=y
@@ -774,18 +839,18 @@ CONFIG_LOGO_LINUX_CLUT224=y
 #
 # Sound
 #
-CONFIG_SOUND=m
+CONFIG_SOUND=y
 
 #
 # Advanced Linux Sound Architecture
 #
-CONFIG_SND=m
-CONFIG_SND_TIMER=m
-CONFIG_SND_PCM=m
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
 # CONFIG_SND_SEQUENCER is not set
 CONFIG_SND_OSSEMUL=y
-CONFIG_SND_MIXER_OSS=m
-CONFIG_SND_PCM_OSS=m
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
 CONFIG_SND_PCM_OSS_PLUGINS=y
 # CONFIG_SND_DYNAMIC_MINORS is not set
 CONFIG_SND_SUPPORT_OLD_API=y
@@ -817,17 +882,23 @@ CONFIG_SND_PXA2XX_AC97=m
 #
 # System on Chip audio support
 #
-# CONFIG_SND_SOC is not set
+CONFIG_SND_SOC_AC97_BUS=y
+CONFIG_SND_SOC=y
+CONFIG_SND_PXA2XX_SOC=y
+CONFIG_SND_PXA2XX_SOC_AC97=y
+CONFIG_SND_PXA2XX_SOC_EM_X270=y
 
 #
-# Open Sound System
+# SoC Audio support for SuperH
 #
-# CONFIG_SOUND_PRIME is not set
-CONFIG_AC97_BUS=m
+CONFIG_SND_SOC_WM9712=y
 
 #
-# HID Devices
+# Open Sound System
 #
+# CONFIG_SOUND_PRIME is not set
+CONFIG_AC97_BUS=y
+CONFIG_HID_SUPPORT=y
 CONFIG_HID=y
 # CONFIG_HID_DEBUG is not set
 
@@ -838,10 +909,7 @@ CONFIG_USB_HID=y
 # CONFIG_USB_HIDINPUT_POWERBOOK is not set
 # CONFIG_HID_FF is not set
 # CONFIG_USB_HIDDEV is not set
-
-#
-# USB support
-#
+CONFIG_USB_SUPPORT=y
 CONFIG_USB_ARCH_HAS_HCD=y
 CONFIG_USB_ARCH_HAS_OHCI=y
 # CONFIG_USB_ARCH_HAS_EHCI is not set
@@ -855,6 +923,7 @@ CONFIG_USB_DEVICEFS=y
 # CONFIG_USB_DEVICE_CLASS is not set
 # CONFIG_USB_DYNAMIC_MINORS is not set
 # CONFIG_USB_SUSPEND is not set
+# CONFIG_USB_PERSIST is not set
 # CONFIG_USB_OTG is not set
 
 #
@@ -866,12 +935,13 @@ CONFIG_USB_OHCI_HCD=y
 # CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
 CONFIG_USB_OHCI_LITTLE_ENDIAN=y
 # CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
 
 #
 # USB Device Class drivers
 #
-# CONFIG_USB_ACM is not set
-# CONFIG_USB_PRINTER is not set
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
 
 #
 # NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
@@ -896,8 +966,8 @@ CONFIG_USB_STORAGE=y
 #
 # USB Imaging devices
 #
-# CONFIG_USB_MDC800 is not set
-# CONFIG_USB_MICROTEK is not set
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
 # CONFIG_USB_MON is not set
 
 #
@@ -940,25 +1010,25 @@ CONFIG_USB_STORAGE=y
 # USB Gadget Support
 #
 # CONFIG_USB_GADGET is not set
-CONFIG_MMC=m
+CONFIG_MMC=y
 # CONFIG_MMC_DEBUG is not set
-# CONFIG_MMC_UNSAFE_RESUME is not set
+CONFIG_MMC_UNSAFE_RESUME=y
 
 #
 # MMC/SD Card Drivers
 #
-CONFIG_MMC_BLOCK=m
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
 
 #
 # MMC/SD Host Controller Drivers
 #
-CONFIG_MMC_PXA=m
-
-#
-# Real Time Clock
-#
+CONFIG_MMC_PXA=y
 CONFIG_RTC_LIB=y
-CONFIG_RTC_CLASS=m
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc1"
+# CONFIG_RTC_DEBUG is not set
 
 #
 # RTC interfaces
@@ -972,6 +1042,15 @@ CONFIG_RTC_INTF_DEV=y
 #
 # I2C RTC drivers
 #
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
 
 #
 # SPI RTC drivers
@@ -982,14 +1061,29 @@ CONFIG_RTC_INTF_DEV=y
 #
 # CONFIG_RTC_DRV_CMOS is not set
 # CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
 # CONFIG_RTC_DRV_DS1742 is not set
 # CONFIG_RTC_DRV_M48T86 is not set
-CONFIG_RTC_DRV_V3020=m
+# CONFIG_RTC_DRV_M48T59 is not set
+CONFIG_RTC_DRV_V3020=y
 
 #
 # on-CPU RTC drivers
 #
-CONFIG_RTC_DRV_SA1100=m
+CONFIG_RTC_DRV_SA1100=y
+
+#
+# DMA Engine support
+#
+# CONFIG_DMA_ENGINE is not set
+
+#
+# DMA Clients
+#
+
+#
+# DMA Devices
+#
 
 #
 # File systems
@@ -1098,7 +1192,6 @@ CONFIG_SMB_FS=y
 # CONFIG_NCP_FS is not set
 # CONFIG_CODA_FS is not set
 # CONFIG_AFS_FS is not set
-# CONFIG_9P_FS is not set
 
 #
 # Partition Types
@@ -1167,20 +1260,22 @@ CONFIG_NLS_UTF8=y
 CONFIG_ENABLE_MUST_CHECK=y
 CONFIG_MAGIC_SYSRQ=y
 # CONFIG_UNUSED_SYMBOLS is not set
-# CONFIG_DEBUG_FS is not set
+CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
 CONFIG_DEBUG_KERNEL=y
 # CONFIG_DEBUG_SHIRQ is not set
 # CONFIG_DETECT_SOFTLOCKUP is not set
+CONFIG_SCHED_DEBUG=y
 # CONFIG_SCHEDSTATS is not set
 # CONFIG_TIMER_STATS is not set
-# CONFIG_DEBUG_SLAB is not set
+# CONFIG_SLUB_DEBUG_ON is not set
 # CONFIG_DEBUG_RT_MUTEXES is not set
 # CONFIG_RT_MUTEX_TESTER is not set
 # CONFIG_DEBUG_SPINLOCK is not set
 # CONFIG_DEBUG_MUTEXES is not set
 # CONFIG_DEBUG_LOCK_ALLOC is not set
 # CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
 # CONFIG_DEBUG_SPINLOCK_SLEEP is not set
 # CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
 # CONFIG_DEBUG_KOBJECT is not set
@@ -1202,10 +1297,6 @@ CONFIG_DEBUG_LL=y
 #
 # CONFIG_KEYS is not set
 # CONFIG_SECURITY is not set
-
-#
-# Cryptographic options
-#
 CONFIG_CRYPTO=y
 CONFIG_CRYPTO_ALGAPI=m
 CONFIG_CRYPTO_BLKCIPHER=m
@@ -1215,7 +1306,7 @@ CONFIG_CRYPTO_MANAGER=m
 # CONFIG_CRYPTO_NULL is not set
 # CONFIG_CRYPTO_MD4 is not set
 # CONFIG_CRYPTO_MD5 is not set
-# CONFIG_CRYPTO_SHA1 is not set
+CONFIG_CRYPTO_SHA1=m
 # CONFIG_CRYPTO_SHA256 is not set
 # CONFIG_CRYPTO_SHA512 is not set
 # CONFIG_CRYPTO_WP512 is not set
@@ -1243,19 +1334,17 @@ CONFIG_CRYPTO_ARC4=m
 # CONFIG_CRYPTO_CRC32C is not set
 # CONFIG_CRYPTO_CAMELLIA is not set
 # CONFIG_CRYPTO_TEST is not set
-
-#
-# Hardware crypto devices
-#
+CONFIG_CRYPTO_HW=y
 
 #
 # Library routines
 #
 CONFIG_BITREVERSE=y
-# CONFIG_CRC_CCITT is not set
+CONFIG_CRC_CCITT=m
 # CONFIG_CRC16 is not set
 # CONFIG_CRC_ITU_T is not set
 CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
 # CONFIG_LIBCRC32C is not set
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=y
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index 5ebec6d..2957cb9 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -148,4 +148,12 @@ config PXA_SSP
 	tristate
 	help
 	  Enable support for PXA2xx SSP ports
+
+config PXA_PWR_I2C
+	bool "Simple Power I2C interface"
+	depends on PXA27x
+	help
+	  Enable support for PXA27x Power I2C interface. This driver
+	  enables very simple blocking access to Power I2C, which
+	  might be useful for early access to Power Management ICs.
 endif
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index 7d6ab5c..2d7a431 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -18,7 +18,7 @@ obj-$(CONFIG_PXA_SHARP_Cxx00)	+= spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o sp
 obj-$(CONFIG_MACH_AKITA)	+= akita-ioexp.o
 obj-$(CONFIG_MACH_POODLE)	+= poodle.o corgi_ssp.o
 obj-$(CONFIG_MACH_TOSA)         += tosa.o
-obj-$(CONFIG_MACH_EM_X270) += em-x270.o
+obj-$(CONFIG_MACH_EM_X270) += em-x270.o em-x270-pm.o em-x270-lcd.o em-x270-devices.o
 
 # Support for blinky lights
 led-y := leds.o
@@ -29,9 +29,16 @@ led-$(CONFIG_MACH_TRIZEPS4) += leds-trizeps4.o
 
 obj-$(CONFIG_LEDS) += $(led-y)
 
+# CPU Frequency scaling
+obj-$(CONFIG_CPU_FREQ_PXA) += cpu-pxa.o
+
 # Misc features
 obj-$(CONFIG_PM) += pm.o sleep.o
 obj-$(CONFIG_PXA_SSP) += ssp.o
+obj-$(CONFIG_PXA_PWR_I2C) += pwr-i2c.o
+
+#obj-m += da9030_asm.o
+#da9030_asm-y += da9030.o da9030_c.o
 
 ifeq ($(CONFIG_PXA27x),y)
 obj-$(CONFIG_PM) += standby.o
diff --git a/arch/arm/mach-pxa/cpu-pxa.c b/arch/arm/mach-pxa/cpu-pxa.c
new file mode 100644
index 0000000..7fa9703
--- /dev/null
+++ b/arch/arm/mach-pxa/cpu-pxa.c
@@ -0,0 +1,442 @@
+/*
+ *  linux/arch/arm/mach-pxa/cpu-pxa.c
+ *
+ *  Copyright (C) 2002,2003 Intrinsyc Software
+ *
+ * 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
+ *
+ * History:
+ *   31-Jul-2002 : Initial version [FB]
+ *   29-Jan-2003 : added PXA255 support [FB]
+ *   20-Apr-2003 : ported to v2.5 (Dustin McIntire, Sensoria Corp.)
+ *   11-Jan-2006 : v2.6, support for PXA27x processor up to 624MHz (Bill Reese, Hewlett Packard)
+ *
+ * Note:
+ *   This driver may change the memory bus clock rate, but will not do any
+ *   platform specific access timing changes... for example if you have flash
+ *   memory connected to CS0, you will need to register a platform specific
+ *   notifier which will adjust the memory access strobes to maintain a
+ *   minimum strobe width.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+
+#include <asm/hardware.h>
+
+#include <asm/arch/pxa-regs.h>
+
+/*
+ * This comes from generic.h in this directory.
+ */
+extern unsigned int get_clk_frequency_khz(int info);
+
+#define DEBUG  0
+
+#ifdef DEBUG
+  static unsigned int freq_debug = DEBUG;
+  module_param(freq_debug, int, 0644);
+  MODULE_PARM_DESC(freq_debug, "Set the debug messages to on=1/off=0");
+#else
+  #define freq_debug  0
+#endif
+
+typedef struct
+{
+  unsigned int khz;     /* CPU frequency                                   */
+  unsigned int membus;  /* memory bus frequency                            */
+  unsigned int cccr;    /* new CCLKCFG setting                             */
+  unsigned int div2;    /* alter memory controller settings to divide by 2 */
+  unsigned int cclkcfg; /* new CCLKCFG setting                             */
+} pxa_freqs_t;
+
+/* Define the refresh period in mSec for the SDRAM and the number of rows */
+#define SDRAM_TREF          64      /* standard 64ms SDRAM */
+#if defined(CONFIG_MACH_H4700) || defined(CONFIG_ARCH_H2200) || defined(CONFIG_MACH_MAGICIAN)
+#define SDRAM_ROWS          8192    /* hx4700 uses 2 64Mb DRAMs, 8912 rows */
+#else
+#define SDRAM_ROWS          4096    /* 64MB=8192 32MB=4096 */
+#endif
+#define MDREFR_DRI(x)       (((x*SDRAM_TREF/SDRAM_ROWS - 31)/32))
+
+#define CCLKCFG_TURBO       0x1
+#define CCLKCFG_FCS         0x2
+#define CCLKCFG_HALFTURBO   0x4
+#define CCLKCFG_FASTBUS     0x8
+#ifdef CONFIG_ARCH_H5400
+/* H5400's SAMCOP chip does not like when K2DB2 touched */
+#define MDREFR_DB2_MASK     (MDREFR_K1DB2)
+#else
+#define MDREFR_DB2_MASK     (MDREFR_K2DB2 | MDREFR_K1DB2)
+#endif
+#define MDREFR_DRI_MASK     0xFFF
+#define PXA25x_CCLKCFG      CCLKCFG_TURBO | CCLKCFG_FCS
+
+/*
+ * For the PXA27x:
+ * Control variables are A, L, 2N for CCCR; B, HT, T for CLKCFG.
+ *
+ * A = 0 => memory controller clock from table 3-7,
+ * A = 1 => memory controller clock = system bus clock
+ * Run mode frequency   = 13 MHz * L
+ * Turbo mode frequency = 13 MHz * L * N
+ * System bus frequency = 13 MHz * L / (B + 1)
+ * System initialized by bootldr to:
+ *
+ * In CCCR:
+ * A = 1
+ * L = 16         oscillator to run mode ratio
+ * 2N = 6         2 * (turbo mode to run mode ratio)
+ *
+ * In CCLKCFG:
+ * B = 1          Fast bus mode
+ * HT = 0         Half-Turbo mode
+ * T = 1          Turbo mode
+ *
+ * For now, just support some of the combinations in table 3-7 of
+ * PXA27x Processor Family Developer's Manual to simplify frequency
+ * change sequences.
+ *
+ * Specify 2N in the PXA27x_CCCR macro, not N!
+ */
+#define PXA27x_CCCR(A, L, N2) (A << 25 | N2 << 7 | L)
+#define PXA27x_CCLKCFG(B, HT, T) (B << 3 |  HT << 2 | CCLKCFG_FCS | T)
+
+#define PXA25x_CCCR(L, M, N) (L << 0 | M << 5 | N << 7)
+
+/*
+ * Valid frequency assignments
+ */
+static pxa_freqs_t pxa2xx_freqs[] =
+{
+    /* CPU   MEMBUS  CCCR                  DIV2 */
+#if defined(CONFIG_PXA25x)
+#if defined(CONFIG_PXA25x_ALTERNATE_FREQS)
+    { 99500, 99500,  PXA25x_CCCR(1, 1, 2), 1, PXA25x_CCLKCFG}, /* run=99,  turbo= 99, PXbus=50, SDRAM=50 */
+    {199100, 99500,  PXA25x_CCCR(1, 1, 4), 0, PXA25x_CCLKCFG}, /* run=99,  turbo=199, PXbus=50, SDRAM=99 */
+    {298500, 99500,  PXA25x_CCCR(1, 1, 6), 0, PXA25x_CCLKCFG}, /* run=99,  turbo=287, PXbus=50, SDRAM=99 */
+    {298600, 99500,  PXA25x_CCCR(1, 2, 3), 0, PXA25x_CCLKCFG}, /* run=199, turbo=287, PXbus=99, SDRAM=99 */
+    {398100, 99500,  PXA25x_CCCR(1, 2, 4), 0, PXA25x_CCLKCFG}  /* run=199, turbo=398, PXbus=99, SDRAM=99 */
+#else
+    { 99500,  99500, PXA25x_CCCR(1, 1, 2), 1, PXA25x_CCLKCFG}, /* run= 99, turbo= 99, PXbus=50,  SDRAM=50 */
+    {132700, 132700, PXA25x_CCCR(3, 1, 2), 1, PXA25x_CCLKCFG}, /* run=133, turbo=133, PXbus=66,  SDRAM=66 */
+    {199100,  99500, PXA25x_CCCR(1, 2, 2), 0, PXA25x_CCLKCFG}, /* run=199, turbo=199, PXbus=99,  SDRAM=99 */
+    {265400, 132700, PXA25x_CCCR(3, 2, 2), 1, PXA25x_CCLKCFG}, /* run=265, turbo=265, PXbus=133, SDRAM=66 */
+    {331800, 165900, PXA25x_CCCR(5, 2, 2), 1, PXA25x_CCLKCFG}, /* run=331, turbo=331, PXbus=166, SDRAM=83 */
+    {398100,  99500, PXA25x_CCCR(1, 3, 2), 0, PXA25x_CCLKCFG}  /* run=398, turbo=398, PXbus=196, SDRAM=99 */
+#endif
+#elif defined(CONFIG_PXA27x)
+    {104000, 104000, PXA27x_CCCR(1,  8, 2), 0, PXA27x_CCLKCFG(1, 0, 1)},
+    {156000, 104000, PXA27x_CCCR(1,  8, 6), 0, PXA27x_CCLKCFG(1, 1, 1)},
+    {208000, 208000, PXA27x_CCCR(0, 16, 2), 1, PXA27x_CCLKCFG(0, 0, 1)},
+    {312000, 208000, PXA27x_CCCR(1, 16, 3), 1, PXA27x_CCLKCFG(1, 0, 1)},
+    {416000, 208000, PXA27x_CCCR(1, 16, 4), 1, PXA27x_CCLKCFG(1, 0, 1)},
+    {520000, 208000, PXA27x_CCCR(1, 16, 5), 1, PXA27x_CCLKCFG(1, 0, 1)},
+/*     {624000, 208000, PXA27x_CCCR(1, 16, 6), 1, PXA27x_CCLKCFG(1, 0, 1)} */
+#endif
+};
+#define NUM_FREQS (sizeof(pxa2xx_freqs)/sizeof(pxa_freqs_t))
+
+static struct cpufreq_frequency_table pxa2xx_freq_table[NUM_FREQS+1];
+
+/* Return the memory clock rate for a given cpu frequency. */
+int pxa_cpufreq_memclk(int cpu_khz)
+{
+	int i;
+	int freq_mem = 0;
+
+	for (i = 0; i < NUM_FREQS; i++) {
+		if (pxa2xx_freqs[i].khz == cpu_khz) {
+			freq_mem = pxa2xx_freqs[i].membus;
+			break;
+		}
+	}
+
+	return freq_mem;
+}
+EXPORT_SYMBOL(pxa_cpufreq_memclk);
+
+
+/* find a valid frequency point */
+static int pxa_verify_policy(struct cpufreq_policy *policy)
+{
+    int ret;
+
+    ret=cpufreq_frequency_table_verify(policy, pxa2xx_freq_table);
+
+    if(freq_debug) {
+        printk("Verified CPU policy: %dKhz min to %dKhz max\n",
+            policy->min, policy->max);
+    }
+
+    return ret;
+}
+
+static int pxa_set_target(struct cpufreq_policy *policy,
+                 unsigned int target_freq,
+                 unsigned int relation)
+{
+    int idx;
+    cpumask_t cpus_allowed, allowedcpuset;
+    int cpu = policy->cpu;
+    struct cpufreq_freqs freqs;
+    unsigned long flags;
+    unsigned int unused;
+    unsigned int preset_mdrefr, postset_mdrefr, cclkcfg;
+
+    if(freq_debug) {
+      printk ("CPU PXA: target freq %d\n", target_freq);
+      printk ("CPU PXA: relation %d\n", relation);
+    }
+
+    /*
+     * Save this threads cpus_allowed mask.
+     */
+    cpus_allowed = current->cpus_allowed;
+
+    /*
+     * Bind to the specified CPU.  When this call returns,
+     * we should be running on the right CPU.
+     */
+    cpus_clear (allowedcpuset);
+    cpu_set (cpu, allowedcpuset);
+    set_cpus_allowed(current, allowedcpuset);
+    BUG_ON(cpu != smp_processor_id());
+
+    /* Lookup the next frequency */
+    if (cpufreq_frequency_table_target(policy, pxa2xx_freq_table,
+                                       target_freq, relation, &idx)) {
+      return -EINVAL;
+    }
+
+    freqs.old = policy->cur;
+    freqs.new = pxa2xx_freqs[idx].khz;
+    freqs.cpu = policy->cpu;
+    if(freq_debug) {
+        printk(KERN_INFO "Changing CPU frequency to %d Mhz, (SDRAM %d Mhz)\n",
+            freqs.new/1000, (pxa2xx_freqs[idx].div2) ?
+            (pxa2xx_freqs[idx].membus/2000) :
+            (pxa2xx_freqs[idx].membus/1000));
+    }
+
+    /*
+     * Tell everyone what we're about to do...
+     * you should add a notify client with any platform specific
+     * Vcc changing capability
+     */
+    cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+    /* Calculate the next MDREFR.  If we're slowing down the SDRAM clock
+     * we need to preset the smaller DRI before the change.  If we're speeding
+     * up we need to set the larger DRI value after the change.
+     */
+    preset_mdrefr = postset_mdrefr = MDREFR;
+    if((MDREFR & MDREFR_DRI_MASK) > MDREFR_DRI(pxa2xx_freqs[idx].membus)) {
+        preset_mdrefr = (preset_mdrefr & ~MDREFR_DRI_MASK) |
+                        MDREFR_DRI(pxa2xx_freqs[idx].membus);
+    }
+    postset_mdrefr = (postset_mdrefr & ~MDREFR_DRI_MASK) |
+                    MDREFR_DRI(pxa2xx_freqs[idx].membus);
+
+    /* If we're dividing the memory clock by two for the SDRAM clock, this
+     * must be set prior to the change.  Clearing the divide must be done
+     * after the change.
+     */
+    if(pxa2xx_freqs[idx].div2) {
+      /*
+       * Potentially speeding up memory clock, so slow down the memory
+       * before speeding up the clock.
+       */
+      preset_mdrefr  |= MDREFR_DB2_MASK | MDREFR_K0DB4;
+      preset_mdrefr  &= ~MDREFR_K0DB2;
+
+      postset_mdrefr |= MDREFR_DB2_MASK | MDREFR_K0DB4;
+      postset_mdrefr &= ~MDREFR_K0DB2;
+    } else {
+      /*
+       * Potentially slowing down memory clock.  Wait until after the change
+       * to speed up the memory.
+       */
+      postset_mdrefr &= ~MDREFR_DB2_MASK;
+      postset_mdrefr &= ~MDREFR_K0DB4;
+      postset_mdrefr |= MDREFR_K0DB2;
+    }
+
+    cclkcfg = pxa2xx_freqs[idx].cclkcfg;
+
+    if (freq_debug) {
+      printk (KERN_INFO "CPU PXA writing 0x%08x to CCCR\n",
+              pxa2xx_freqs[idx].cccr);
+      printk (KERN_INFO "CPU PXA writing 0x%08x to CCLKCFG\n",
+              pxa2xx_freqs[idx].cclkcfg);
+      printk (KERN_INFO "CPU PXA writing 0x%08x to MDREFR before change\n",
+              preset_mdrefr);
+      printk (KERN_INFO "CPU PXA writing 0x%08x to MDREFR after change\n",
+              postset_mdrefr);
+    }
+
+    local_irq_save(flags);
+
+    /* Set new the CCCR */
+    CCCR = pxa2xx_freqs[idx].cccr;
+
+    /*
+     * Should really set both of PMCR[xIDAE] while changing the core frequency
+     */
+
+    /*
+     * TODO: On the PXA27x: If we're setting half-turbo mode and changing the
+     * core frequency at the same time we must split it up into two operations.
+     * The current values in the pxa2xx_freqs table don't do this, so the code
+     * is unimplemented.
+     */
+
+    __asm__ __volatile__("                                  \
+        ldr r4, [%1] ;  /* load MDREFR */                   \
+        b   2f ;                                            \
+        .align  5 ;                                         \
+1:                                                          \
+        str %3, [%1] ;          /* preset the MDREFR */     \
+        mcr p14, 0, %2, c6, c0, 0 ; /* set CCLKCFG[FCS] */  \
+        str %4, [%1] ;          /* postset the MDREFR */    \
+                                                            \
+        b   3f       ;                                      \
+2:      b   1b       ;                                      \
+3:      nop          ;                                      \
+        "
+        : "=&r" (unused)
+        : "r" (&MDREFR), "r" (cclkcfg), \
+          "r" (preset_mdrefr), "r" (postset_mdrefr)
+        : "r4", "r5");
+    local_irq_restore(flags);
+
+    if (freq_debug) {
+      printk (KERN_INFO "CPU PXA Frequency change successful\n");
+      printk (KERN_INFO "CPU PXA new CCSR 0x%08x\n", CCSR);
+    }
+
+    /*
+     * Restore the CPUs allowed mask.
+     */
+    set_cpus_allowed(current, cpus_allowed);
+
+    /*
+     * Tell everyone what we've just done...
+     * you should add a notify client with any platform specific
+     * SDRAM refresh timer adjustments
+     */
+    cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+    return 0;
+}
+
+static int pxa_cpufreq_init(struct cpufreq_policy *policy)
+{
+    cpumask_t cpus_allowed, allowedcpuset;
+    unsigned int cpu = policy->cpu;
+    int i;
+    unsigned int cclkcfg;
+
+    cpus_allowed = current->cpus_allowed;
+
+    cpus_clear (allowedcpuset);
+    cpu_set (cpu, allowedcpuset);
+    set_cpus_allowed(current, allowedcpuset);
+    BUG_ON(cpu != smp_processor_id());
+
+    /* set default governor and cpuinfo */
+    policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+    policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */
+    policy->cur = get_clk_frequency_khz(0); /* current freq */
+
+    /* Generate the cpufreq_frequency_table struct */
+    for(i=0;i<NUM_FREQS;i++) {
+        pxa2xx_freq_table[i].frequency = pxa2xx_freqs[i].khz;
+        pxa2xx_freq_table[i].index = i;
+    }
+    pxa2xx_freq_table[i].frequency = CPUFREQ_TABLE_END;
+
+    /*
+     * Set the policy's minimum and maximum frequencies from the tables
+     * just constructed.  This sets cpuinfo.mxx_freq, min and max.
+     */
+    cpufreq_frequency_table_cpuinfo (policy, pxa2xx_freq_table);
+
+    set_cpus_allowed(current, cpus_allowed);
+    printk(KERN_INFO "PXA CPU frequency change support initialized\n");
+
+    if (freq_debug) {
+      printk (KERN_INFO "PXA CPU initial CCCR 0x%08x\n", CCCR);
+      asm
+	(
+	 "mrc p14, 0, %0, c6, c0, 0 ; /* read CCLKCFG from CP14 */ "
+	 : "=r" (cclkcfg) :
+	 );
+      printk ("PXA CPU initial CCLKCFG 0x%08x\n", cclkcfg);
+      printk ("PXA CPU initial MDREFR  0x%08x\n", MDREFR);
+    }
+
+    return 0;
+}
+
+static unsigned int pxa_cpufreq_get(unsigned int cpu)
+{
+    cpumask_t cpumask_saved;
+    unsigned int cur_freq;
+
+    cpumask_saved = current->cpus_allowed;
+    set_cpus_allowed(current, cpumask_of_cpu(cpu));
+    BUG_ON(cpu != smp_processor_id());
+
+    cur_freq = get_clk_frequency_khz(0);
+
+    set_cpus_allowed(current, cpumask_saved);
+
+    return cur_freq;
+}
+
+static struct cpufreq_driver pxa_cpufreq_driver = {
+    .verify     = pxa_verify_policy,
+    .target     = pxa_set_target,
+    .init       = pxa_cpufreq_init,
+    .get        = pxa_cpufreq_get,
+#if defined(CONFIG_PXA25x)
+    .name       = "PXA25x",
+#elif defined(CONFIG_PXA27x)
+    .name       = "PXA27x",
+#endif
+};
+
+static int __init pxa_cpu_init(void)
+{
+    return cpufreq_register_driver(&pxa_cpufreq_driver);
+}
+
+static void __exit pxa_cpu_exit(void)
+{
+    cpufreq_unregister_driver(&pxa_cpufreq_driver);
+}
+
+
+MODULE_AUTHOR ("Intrinsyc Software Inc.");
+MODULE_DESCRIPTION ("CPU frequency changing driver for the PXA architecture");
+MODULE_LICENSE("GPL");
+module_init(pxa_cpu_init);
+module_exit(pxa_cpu_exit);
+
diff --git a/arch/arm/mach-pxa/em-x270-devices.c b/arch/arm/mach-pxa/em-x270-devices.c
new file mode 100644
index 0000000..d142930
--- /dev/null
+++ b/arch/arm/mach-pxa/em-x270-devices.c
@@ -0,0 +1,331 @@
+/*
+ * Support for CompuLab EM-X270 platfrom specific device
+ * initialization, and per-device power management functions.
+ *
+ * Copyright (C) 2007 CompuLab, Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * 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/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/da9030.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include "../../../drivers/i2c/chips/da9030.h"
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+struct em_x270_dev_data {
+	int (*is_on)(struct device *dev);
+	int (*set_on)(struct device *dev, int on);
+	int (*get_opts)(struct device *dev,
+			struct device_attribute *attr,
+			char *buf);
+	int (*set_opts)(struct device *dev, int opts);
+};
+
+static void em_x270_dev_release(struct device * dev)
+{
+	
+}
+
+static ssize_t em_x270_dev_pm_show(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct em_x270_dev_data *pm_data = dev->platform_data;
+	int is_on = pm_data->is_on(dev);
+
+	return sprintf(buf, "%d\n", is_on);
+}
+
+static ssize_t em_x270_dev_pm_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	char *endp;
+	int on = simple_strtoul(buf, &endp, 0);
+	size_t size = endp - buf;
+	struct em_x270_dev_data *pm_data = dev->platform_data;
+
+	pr_info("%s: %s\n", __FUNCTION__, buf);
+
+	if (*endp && isspace(*endp))
+		size++;
+	if (size != count)
+		return -EINVAL;
+
+	pm_data->set_on(dev, on);
+
+	return count;
+}
+
+static DEVICE_ATTR(pwr_on, S_IWUSR | S_IRUGO,
+		   em_x270_dev_pm_show, em_x270_dev_pm_store);
+
+static int gprs_is_on(struct device *dev)
+{
+	return !!(pxa_gpio_get_value(20));
+}
+
+static int gprs_set_on(struct device *dev, int on)
+{
+	pr_info("%s: on = %d\n", __FUNCTION__, on);
+	if (on) {
+		pxa_gpio_mode(20 | GPIO_OUT | GPIO_DFLT_HIGH);
+		mdelay(500);
+		pxa_gpio_set_value(82, 0);
+		mdelay(500);
+		pxa_gpio_mode(82 | GPIO_OUT | GPIO_DFLT_HIGH);
+		mdelay(1000);
+		pxa_gpio_set_value(82, 0);
+	}
+	else {
+		pxa_gpio_set_value(20, 0);
+		pxa_gpio_mode(20 | GPIO_OUT);
+	}
+	
+	return 0;
+}
+
+static struct em_x270_dev_data em_x270_gprs_data = {
+	.is_on = gprs_is_on,
+	.set_on = gprs_set_on,
+};
+
+static struct platform_device em_x270_gprs = {
+	.name = "gprs",
+	.id = -1,
+	.dev = {
+		.platform_data = &em_x270_gprs_data,
+		.release = em_x270_dev_release,
+	},
+};
+
+static int is_wlan_on;
+
+static int wlan_is_on(struct device *dev)
+{
+	return is_wlan_on;
+}
+
+static int wlan_set_on(struct device *dev, int on)
+{
+	if (on) {
+		/* WLAN power-up sequence */
+		/* Mask LDO17 and LDO19 tolerance events */
+		da9030_set_reg(IRQ_MASK_C,
+			       IRQ_MASK_C_LDO19 | IRQ_MASK_C_LDO17);
+
+		/* Force I2C control over LDO17, LDO19 */
+		da9030_set_reg(SLEEP_CONTROL,
+			       APP_SLEEP_CTL_BYPASS_LDO19 |
+			       APP_SLEEP_CTL_BYPASS_LDO17);
+
+		/* Turn off LDO17 and LDO19 */
+		da9030_set_reg(REG_CONTROL_1_17,
+			       RC1_LDO16_EN | RC1_LDO15_EN |
+			       RC1_LDO11_EN | RC1_LDO10_EN | RC1_BUCK2_EN);
+		da9030_set_reg(REG_CONTROL_2_18, RC2_SIMCP_EN | RC2_LDO18_EN);
+
+		/* Set LDO17 voltage to 3V (VCC_WLAN_IO) */
+		da9030_set_reg(LDO_17_SIMCP0, LDO_17_SIMCP0_LDO17_3V);
+		mdelay(200);
+
+		/* Turn WLAN RF power on */
+		pxa_gpio_mode(115 | GPIO_OUT | GPIO_DFLT_HIGH);
+
+		/* Turn LDO17 on */
+		da9030_set_reg(REG_CONTROL_1_17,
+			       RC1_LDO17_EN| RC1_LDO16_EN | RC1_LDO15_EN |
+			       RC1_LDO11_EN | RC1_LDO10_EN | RC1_BUCK2_EN);
+		mdelay(200);
+	
+		/* Turn on LDO19 */
+		da9030_set_reg(REG_CONTROL_2_18,
+			       RC2_SIMCP_EN | RC2_LDO18_EN | RC2_LDO19_EN);
+		
+	}
+	else {
+		/* FIXME: implement BGW shutdown */
+	}
+	is_wlan_on = on;
+	
+	return 0;
+}
+
+static struct em_x270_dev_data em_x270_wlan_data = {
+	.is_on = wlan_is_on,
+	.set_on = wlan_set_on,
+};
+
+static struct platform_device em_x270_wlan = {
+	.name = "wlan",
+	.id = -1,
+	.dev = {
+		.platform_data = &em_x270_wlan_data,
+		.release = em_x270_dev_release,
+	},
+};
+
+static int gps_is_on(struct device *dev)
+{
+	int val = da9030_get_reg(REG_CONTROL_1_97);
+	return !!(val & RC3_LDO3_EN);
+}
+
+static int gps_set_on(struct device *dev, int on)
+{
+	int val = da9030_get_reg(REG_CONTROL_1_97);
+
+	if (on)
+		val |= RC3_LDO3_EN;
+	else
+		val &= ~RC3_LDO3_EN;
+	da9030_set_reg(REG_CONTROL_1_97, val);
+
+	return 0;
+}
+
+static struct em_x270_dev_data em_x270_gps_data = {
+	.is_on = gps_is_on,
+	.set_on = gps_set_on,
+};
+
+static struct platform_device em_x270_gps = {
+	.name = "gps",
+	.id = -1,
+	.dev = {
+		.platform_data = &em_x270_gps_data,
+		.release = em_x270_dev_release,
+	},
+};
+
+static int usb_mode;
+static int usb_host_is_on(struct device *dev)
+{
+	return usb_mode;
+}
+
+static int usb_host_set_on(struct device *dev, int on)
+{
+	if (on) {
+		da9030_set_reg(MISC_CONTROLB, MISCB_SESSION_VALID_ENABLE);
+		da9030_set_reg(USBPUMP,
+			       USB_PUMP_EN_USBVEP |
+			       USB_PUMP_EN_USBVE |
+			       USB_PUMP_USBVE);
+
+		/* enable port 2 transiever */
+		UP2OCR = UP2OCR_HXS | UP2OCR_HXOE;
+		usb_mode = 1;
+	}
+	else {
+		UP2OCR = UP2OCR_DPPUE | UP2OCR_DPPUBE | UP2OCR_HXOE;
+		da9030_set_reg(USBPUMP,
+			       USB_PUMP_EN_USBVEP | USB_PUMP_EN_USBVE);
+		usb_mode = 0;
+ 	}
+
+	return 0;
+}
+
+static struct em_x270_dev_data em_x270_usb_host_data = {
+	.is_on = usb_host_is_on,
+	.set_on = usb_host_set_on,
+};
+
+static struct platform_device em_x270_usb_host = {
+	.name = "usb_host",
+	.id = -1,
+	.dev = {
+		.platform_data = &em_x270_usb_host_data,
+		.release = em_x270_dev_release,
+	},
+};
+
+static struct platform_device *em_x270_devices[] = {
+	&em_x270_gprs,
+	&em_x270_wlan,
+	&em_x270_gps,
+	&em_x270_usb_host,
+};
+
+static struct work_struct usb_work;
+static void usb_worker(struct work_struct *work)
+{
+	usb_host_set_on(NULL, !pxa_gpio_get_value(21));
+}
+
+static irqreturn_t usb_irq_handler(int irq, void *regs)
+{
+	schedule_work(&usb_work);
+
+	pr_info("%s\n", __FUNCTION__);
+	return IRQ_HANDLED;
+}
+
+static int em_x270_devices_init(void)
+{
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(em_x270_devices); i++ ) {
+		ret = platform_device_register(em_x270_devices[i]);
+		if (ret) {
+			dev_dbg(&em_x270_devices[i]->dev,
+				 "Registration failed: %d\n", ret);
+			continue;
+		}
+
+		ret = device_create_file(&em_x270_devices[i]->dev,
+					 &dev_attr_pwr_on);
+		if (ret) {
+			dev_dbg(&em_x270_devices[i]->dev,
+				 "PWR_ON attribute failed: %d\n", ret);
+		}
+
+		dev_dbg(&em_x270_devices[i]->dev,
+			 "Registered PWR ON attribute\n");
+	}
+
+	/* setup USB detection irq */
+	INIT_WORK(&usb_work, usb_worker);
+	set_irq_type(IRQ_GPIO(21), IRQT_BOTHEDGE);
+	ret = request_irq(IRQ_GPIO(21), usb_irq_handler, IRQF_DISABLED,
+			  "usb detect", 0);
+	if (ret) {
+		pr_info("USB device detection disabled\n");
+	}
+	else {
+		schedule_work(&usb_work);
+	}
+
+	return 0;
+}
+
+static void em_x270_devices_exit(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(em_x270_devices); i++ ) {
+		device_remove_file(&em_x270_devices[i]->dev,
+					 &dev_attr_pwr_on);
+		platform_device_unregister(em_x270_devices[i]);
+	}
+}
+
+late_initcall(em_x270_devices_init);
+module_exit(em_x270_devices_exit);
+
+MODULE_AUTHOR("Mike Rapoport");
+MODULE_DESCRIPTION("Support for platfrom specific device attributes for EM-X270");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-pxa/em-x270-lcd.c b/arch/arm/mach-pxa/em-x270-lcd.c
new file mode 100644
index 0000000..437efe1
--- /dev/null
+++ b/arch/arm/mach-pxa/em-x270-lcd.c
@@ -0,0 +1,223 @@
+/*
+ * LCD initialization and backlight managemnet for EM-X270
+ *
+ * Copyright (C) 2007 CompuLab, Ltd.
+ * Author: Igor Vaisbein <igor@compulab.co.il>
+ *
+ * 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/platform_device.h>
+#include <linux/da9030.h>
+#include <linux/delay.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/sharpsl.h>
+#include <asm/arch/ssp.h>
+
+#include "devices.h"
+
+#define GPIO87_nPCE_2		87  /* Card Enable for Card Space (PXA27x) */
+#define GPIO87_nPCE_2_MD	(87 | GPIO_ALT_FN_1_IN)
+#define GPIO87_USB3_1_MD	(87 | GPIO_ALT_FN_3_IN)
+#define GPIO87_SSPTXD2_MD	(87 | GPIO_ALT_FN_1_OUT)
+#define GPIO87_SSFRM2_MD	(87 | GPIO_ALT_FN_3_OUT)
+
+#define LCCR4	__REG(0x44000010)
+
+#define TD035STEE1_LCD_ID    0x2008
+
+static void em_x270_set_bl_intensity(int intensity)
+{
+	da9030_set_wled((intensity != 0), intensity);
+}
+
+struct corgibl_machinfo em_x270_bl_machinfo = {
+	.max_intensity = 0x7,
+	.default_intensity = 0x3,
+	.limit_mask = 0x1,
+	.set_bl_intensity = em_x270_set_bl_intensity,
+};
+
+static void em_x270_bl_release(struct device * dev)
+{
+
+}
+
+static struct platform_device em_x270_bl = {
+	.name		= "corgi-bl",
+	.dev		= {
+		.release = em_x270_bl_release,
+ 		.parent = &pxa_device_fb.dev,
+		.platform_data	= &em_x270_bl_machinfo,
+	},
+	.id		= -1,
+};
+
+/*
+ * Helper functions to access LCD throuhg SPI interface
+ */
+static void set_ssp_9bit(void)
+{
+	int temp;
+	SSCR0 = 0xFF208;
+	SSCR0 = 0xFF288;
+	SSCR1 = 0x40000018;
+
+	while (SSSR & 0x8)
+		temp = SSDR;
+}
+
+static void set_ssp_18bit(void)
+{
+	int temp;
+	SSCR0 = 0x1FF201;
+	SSCR0 = 0x1FF281;
+	SSCR1 = 0x40000018;
+	while (SSSR & 0x8)
+		temp = SSDR;
+}
+
+static void set_ssp_rcv(void)
+{
+	int temp;
+	SSCR0 = 0x1FF20F;
+	SSCR0 = 0x1FF28F;
+	SSCR1 = 0x40000018;
+	while (SSSR & 0x8)
+		temp = SSDR;
+}
+
+static void send_ssp_9bit(unsigned int value)
+{
+	int temp;
+	SSDR = (value);
+
+	asm volatile ("mcr p15, 0, r0, c7, c10, 4":::"r0");
+
+	if (!(SSSR & 0x4))
+		while ((SSSR & 0xf00) == 0)
+			;
+
+	while ((SSSR & 0xf00) != 0)
+		;
+	while (SSSR & 0x10)
+		;
+	while (SSSR & 0x8)
+		temp = SSDR;
+}
+
+static void send_ssp_18bit(unsigned int value)
+{
+	int temp;
+	SSDR = (value);
+
+	asm volatile ("mcr p15, 0, r0, c7, c10, 4":::"r0");
+
+	if (!(SSSR & 0x4))
+		while ((SSSR & 0xf00) == 0)
+			;
+
+	while ((SSSR & 0xf00) != 0)
+		;
+	while (SSSR & 0x10)
+		;
+	while (SSSR & 0x8)
+		temp = SSDR;
+}
+
+static unsigned int rcv_ssp_18bit(void)
+{
+	SSDR = ((0x04) << 23);
+	asm volatile ("mcr p15, 0, r0, c7, c10, 4":::"r0");
+	if (!(SSSR & 0x4))
+		while ((SSSR & 0xf00) == 0)
+			;
+	while ((SSSR & 0xf00) != 0)
+		;
+	while (SSSR & 0x10)
+		;
+	return SSDR;
+}
+
+/* LCD init sequence */
+int em_x270_lcd_detect(void)
+{
+	unsigned int data;
+
+	/* Reset the LCD module */
+	pxa_gpio_mode(GPIO87_nPCE_2 | GPIO_OUT);
+	GPCR(GPIO87_nPCE_2) |= GPIO_bit(GPIO87_nPCE_2);
+	mdelay(75);
+	GPSR(GPIO87_nPCE_2) |= GPIO_bit(GPIO87_nPCE_2);
+	mdelay(70);
+
+	/* TD035STEE1 LCD_SSP initialization commands */
+	set_ssp_9bit();
+	send_ssp_9bit(0x000);
+	mdelay(5);
+
+	send_ssp_9bit(0x000);
+	mdelay(5);
+
+	send_ssp_9bit(0x000);
+	mdelay(5);
+
+	set_ssp_18bit();
+	send_ssp_18bit(0x17980);
+	mdelay(5);
+
+	send_ssp_18bit(0x17F10);
+	mdelay(5);
+
+	set_ssp_9bit();
+	send_ssp_9bit(0x011);
+	mdelay(50);
+
+	send_ssp_9bit(0x029);
+	mdelay(10);
+
+	set_ssp_rcv();
+
+	/* Check for LCD ID, to enable the back-light */
+	data = rcv_ssp_18bit();
+
+	if ((data & 0xFFFF) != TD035STEE1_LCD_ID)
+		return -ENODEV;
+
+	/* enable backlight */
+	da9030_set_wled(1, 2);
+	return 0;
+}
+
+static int em_x270_lcd_init(void)
+{
+	int ret;
+	pr_debug("%s\n", __FUNCTION__);
+	ret = em_x270_lcd_detect();
+	if (ret)
+		return ret;
+
+	/* make sure we keep LCCR4 with PCD=0 */
+	LCCR4 = 0x0;
+
+	return platform_device_register(&em_x270_bl);
+}
+
+static void em_x270_lcd_exit(void)
+{
+	pr_debug("%s\n", __FUNCTION__);
+	platform_device_unregister(&em_x270_bl);
+}
+
+late_initcall(em_x270_lcd_init);
+module_exit(em_x270_lcd_exit);
+
+MODULE_DESCRIPTION("EM-X270 backlight and LCD initialization driver");
+MODULE_AUTHOR("Mike Rapoport");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-pxa/em-x270-pm.c b/arch/arm/mach-pxa/em-x270-pm.c
new file mode 100644
index 0000000..55ba4cd
--- /dev/null
+++ b/arch/arm/mach-pxa/em-x270-pm.c
@@ -0,0 +1,892 @@
+/*
+ * Support for CompuLab EM-X270 platform power management
+ *
+ * Copyright (C) 2007 CompuLab, Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sysfs.h>
+#include <linux/pm.h>
+#include <linux/da9030.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+#include <linux/apm-emulation.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include "../../../drivers/i2c/chips/da9030.h"
+
+#include <asm/arch/pm.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/pwr-i2c.h>
+
+#define DA9030_ADDR	0x92
+
+#define EM_X270_BATCHK_TIME_SUSPEND (10*60) /* 10 min */
+
+#define VOLTAGE_MAX_DESIGN	4200000  /* 4.2V in uV */
+#define VOLTAGE_MIN_DESIGN	3000000  /* 3V in uV */
+
+#define REG2VOLT(x) ((((x) * 2650) >> 8) + 2650)
+#define VOLT2REG(x) ((((x) - 2650) << 8) / 2650)
+
+#define REG2CURR(x) ((((x) * 24000) >> 8) / 15)
+
+#define VCHARGE_MIN_THRESHOLD	VOLT2REG(3200)
+#define VCHARGE_MAX_THRESHOLD	VOLT2REG(5500)
+
+#define VBAT_LOW_THRESHOLD	VOLT2REG(3600)
+#define VBAT_CRIT_THRESHOLD	VOLT2REG(3400)
+
+#define VBAT_CHARGE_START	VOLT2REG(4100)
+#define VBAT_CHARGE_STOP	VOLT2REG(4200)
+#define VBAT_CHARGE_RESTART	VOLT2REG(4000)
+
+#define TBAT_LOW_THRESHOLD	197 /* 0oC */
+#define TBAT_HIGH_THRESHOLD	78 /* 45oC */
+#define TBAT_RESUME_THRESHOLD	100 /* 35oC */
+
+struct em_x270_charger;
+
+struct em_x270_charger_ops {
+	void (*get_status)(struct em_x270_charger *charger);
+	void (*set_charge)(struct em_x270_charger *charger, int on);
+
+	s32 (*da9030_get_reg)(u32 reg);
+	s32 (*da9030_set_reg)(u32 reg, u8 val);
+};
+
+struct em_x270_charger {
+	struct device *dev;
+
+	struct power_supply bat;
+	struct da9030_adc_res adc;
+	struct delayed_work work;
+
+	int interval;
+
+	int da9030_status;
+	int da9030_fault;
+	int mA;
+	int mV;
+	int is_on;
+
+	struct em_x270_charger_ops *ops;
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debug_file;
+#endif
+};
+
+static struct em_x270_charger *the_charger;
+
+static unsigned short tbat_readings[] = {
+	300,	244,	200,	178,	163,	152,	144,	137,	131,
+	126,	122,	118,	114,	111,	108,	105,	103,	101,
+	98,	96,	94,	93,	91,	89,	88,	86,	85,
+	83,	82,	81,	79,	78,	77,	76,	75,	74,
+	73,	72,	71,	70,	69,	68,	67,	67,	66,
+	65,	64,	63,	63,	62,	61,	60,	60,	59,
+	58,	58,	57,	57,	56,	55,	55,	54,	53,
+	53,	52,	52,	51,	51,	50,	50,	49,	49,
+	48,	48,	47,	47,	46,	46,	45,	45,	44,
+	44,	43,	43,	42,	42,	41,	41,	41,	40,
+	40,	39,	39,	38,	38,	38,	37,	37,	36,
+	36,	35,	35,	35,	34,	34,	34,	33,	33,
+	32,	32,	32,	31,	31,	30,	30,	30,	29,
+	29,	29,	28,	28,	28,	27,	27,	26,	26,
+	26,	25,	25,	25,	24,	24,	24,	23,	23,
+	23,	22,	22,	21,	21,	21,	20,	20,	20,
+	19,	19,	19,	18,	18,	18,	17,	17,	17,
+	16,	16,	16,	15,	15,	14,	14,	14,	13,
+	13,	13,	12,	12,	12,	11,	11,	11,	10,
+	10,	10,	9,	9,	8,	8,	8,	7,	7,
+	7,	6,	6,	5,	5,	5,	4,	4,	3,
+	3,	3,	2,	2,	1,	1,	1,	0,	0,
+	-1,	-1,	-1,	-2,	-2,	-3,	-3,	-4,	-4,
+	-5,	-5,	-6,	-6,	-6,	-7,	-7,	-8,	-9,
+	-9,	-10,	-10,	-11,	-11,	-12,	-12,	-13,	-14,
+	-14,	-15,	-16,	-16,	-17,	-18,	-18,	-19,	-20,
+	-21,	-21,	-22,	-23,	-24,	-25,	-26,	-27,	-28,
+	-30,	-31,	-32,	-34,	-35,	-37,	-39,	-41,	-44,
+	-47,	-50,	-56,	-64,	
+};	
+
+static inline int usb_host_on(void)
+{
+	return !pxa_gpio_get_value(21);
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static int debug_show(struct seq_file *s, void *data)
+{
+	struct em_x270_charger *charger = s->private;
+	
+	seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off");
+	if (charger->da9030_status & CHRG_CHARGER_ENABLE) {
+		seq_printf(s, "iset = %dmA, vset = %dmV\n",
+			   charger->mA, charger->mV);
+	}
+
+	seq_printf(s, "vbat_res = %d (%dmV)\n",
+		   charger->adc.vbat_res, REG2VOLT(charger->adc.vbat_res));
+	seq_printf(s, "vbatmin_res = %d (%dmV)\n",
+		   charger->adc.vbatmin_res,
+		   REG2VOLT(charger->adc.vbatmin_res));
+	seq_printf(s, "vbatmintxon = %d (%dmV)\n",
+		   charger->adc.vbatmintxon,
+		   REG2VOLT(charger->adc.vbatmintxon));
+	seq_printf(s, "ichmax_res = %d (%dmA)\n",
+		   charger->adc.ichmax_res,
+		   REG2CURR(charger->adc.ichmax_res));
+	seq_printf(s, "ichmin_res = %d (%dmA)\n",
+		   charger->adc.ichmin_res,
+		   REG2CURR(charger->adc.ichmin_res));
+	seq_printf(s, "ichaverage_res = %d (%dmA)\n",
+		   charger->adc.ichaverage_res,
+		   REG2CURR(charger->adc.ichaverage_res));
+	seq_printf(s, "vchmax_res = %d (%dmV)\n",
+		   charger->adc.vchmax_res,
+		   REG2VOLT(charger->adc.vchmax_res));
+	seq_printf(s, "vchmin_res = %d (%dmV)\n",
+		   charger->adc.vchmin_res,
+		   REG2VOLT(charger->adc.vchmin_res));
+	seq_printf(s, "tbat_res = %d (%doC)\n", charger->adc.tbat_res,
+		   tbat_readings[charger->adc.tbat_res]);
+	seq_printf(s, "adc_in4_res = %d\n", charger->adc.adc_in4_res);
+	seq_printf(s, "adc_in5_res = %d\n", charger->adc.adc_in5_res);
+
+	return 0;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, debug_show, inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+	.open		= debug_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static struct dentry* em_x270_create_debugfs(struct em_x270_charger *charger)
+{
+	charger->debug_file = debugfs_create_file("charger", 0666, 0, charger,
+						 &debug_fops);
+	return charger->debug_file;
+}
+
+static void em_x270_remove_debugfs(struct em_x270_charger *charger)
+{
+	debugfs_remove(charger->debug_file);
+}
+#else
+#define em_x270_create_debugfs(x)	NULL
+#define em_x270_remove_debugfs(x)	do {} while(0)
+#endif
+
+/*********************************************************************/
+/* DA9030 access functions for suspend/resume */
+#define DA_ADDR 0x49
+static inline s32 __da9030_get_reg(u32 reg)
+{
+	pr_info("%s: reg = %d\n", __FUNCTION__, reg);
+	return pxa_pwr_i2c_reg_read(DA_ADDR, reg);
+}
+
+static inline s32 __da9030_set_reg(u32 reg, u8 val)
+{
+	pr_info("%s: reg = %d, val = %d\n", __FUNCTION__, reg, val);
+	return pxa_pwr_i2c_reg_write(DA_ADDR, reg, val);
+}
+
+/*********************************************************************/
+/* helpers for charger state monnitor. Different version for stready
+ *  state and suspended system
+ */
+static void em_x270_get_charger_status(struct em_x270_charger *charger)
+{
+	da9030_get_charger(&charger->is_on, &charger->mA, &charger->mV);
+	da9030_read_adc(&charger->adc);
+	charger->da9030_status = da9030_get_status();
+	charger->da9030_fault = da9030_get_fault_log();
+}
+
+static void em_x270_set_charge(struct em_x270_charger *charger, int on)
+{
+	if (on) {
+		pr_debug("%s: enabling charger\n", __FUNCTION__);
+		da9030_set_thresholds(TBAT_HIGH_THRESHOLD,
+				      TBAT_RESUME_THRESHOLD,
+				      TBAT_LOW_THRESHOLD,
+				      VBAT_LOW_THRESHOLD);
+		da9030_set_reg(CCTR_CONTROL, CCTR_SET_8MIN);
+		da9030_set_charger(1, 1000, 4200);
+		da9030_set_led(3, 1, 0, 0, 0);
+		charger->is_on = 1;
+	}
+	else {
+		/* disable charger */
+		pr_debug("%s: disabling charger\n", __FUNCTION__);
+		da9030_set_charger(0, 0, 0);
+		da9030_set_led(3, 0, 0, 0, 0);
+		charger->is_on = 0;
+	}
+}
+
+static void em_x270_get_charger_status_suspend(struct em_x270_charger *charger)
+{
+	s32 val;
+
+	val = __da9030_get_reg(CHARGE_CONTROL);
+	charger->is_on = (val & CHRG_CHARGER_ENABLE) ? 1 : 0;
+	charger->mA = ((val >> 3) & 0xf) * 100;
+	charger->mV = (val & 0x7) * 50 + 4000;
+
+	charger->adc.vbat_res = __da9030_get_reg(VBAT_RES);
+	charger->adc.vchmax_res = __da9030_get_reg(VCHMAX_RES);
+	charger->adc.vchmin_res = __da9030_get_reg(VCHMIN_RES);
+	charger->adc.tbat_res = __da9030_get_reg(TBAT_RES);
+
+	charger->da9030_status = __da9030_get_reg(STATUS);
+	charger->da9030_fault = __da9030_get_reg(FAULT_LOG);
+}
+
+static void em_x270_set_charge_suspend(struct em_x270_charger *charger, int on)
+{
+	if (on) {
+		u8 val = 0;
+		int mA = 1000;
+		int mV = 4200;
+
+		pr_debug("%s: enabling charger\n", __FUNCTION__);
+		val = CHRG_CHARGER_ENABLE;
+		val |= (mA / 100) << 3;
+		val |= (mV - 4000) / 50;
+
+		__da9030_set_reg(CCTR_CONTROL, CCTR_SET_8MIN);
+		__da9030_set_reg(VBATMON, VBAT_LOW_THRESHOLD);
+		__da9030_set_reg(CHARGE_CONTROL, val);
+		charger->is_on = 1;
+	}
+	else {
+		/* disable charger */
+		pr_debug("%s: disabling charger\n", __FUNCTION__);
+		__da9030_set_reg(CHARGE_CONTROL, 0);
+		charger->is_on = 0;
+	}
+}
+
+static struct em_x270_charger_ops em_x270_charger_ops = {
+	.get_status = em_x270_get_charger_status,
+	.set_charge = em_x270_set_charge,
+	.da9030_get_reg = da9030_get_reg,
+	.da9030_set_reg = da9030_set_reg,
+};
+
+static struct em_x270_charger_ops em_x270_charger_ops_suspend = {
+	.get_status = em_x270_get_charger_status_suspend,
+	.set_charge = em_x270_set_charge_suspend,
+	.da9030_get_reg = __da9030_get_reg,
+	.da9030_set_reg = __da9030_set_reg,
+};
+
+/*********************************************************************/
+/* charging state machine */
+static void em_x270_check_charger_state(struct em_x270_charger *charger)
+{
+	charger->ops->get_status(charger);
+
+/* 	we wake or boot with external power on */
+	if (!charger->is_on) {
+		if ((charger->da9030_status & STATUS_CHDET) &&
+		    (!usb_host_on()) && 
+		    (charger->adc.vbat_res < VBAT_CHARGE_START)) {
+			pr_debug("%s: vbat_res <= 4100\n", __FUNCTION__);
+			charger->ops->set_charge(charger, 1);
+		}
+	}
+	else {
+		if (charger->adc.vbat_res >= VBAT_CHARGE_STOP) {
+			pr_debug("%s: vbat_res >= 4200\n", __FUNCTION__);
+			charger->ops->set_charge(charger, 0);
+			charger->ops->da9030_set_reg(VBATMON,
+						     VBAT_CHARGE_RESTART);
+		}
+		else if (charger->adc.vbat_res > VBAT_LOW_THRESHOLD) {
+			/* we are charging and passed LOW_THRESH,
+			   so upate DA9030 VBAT threshold
+			 */
+			pr_debug("%s: vbat_res >= %d\n", __FUNCTION__,
+				REG2VOLT(VBAT_LOW_THRESHOLD));
+			charger->ops->da9030_set_reg(VBATMON,
+						     VBAT_LOW_THRESHOLD);
+		}
+		if (charger->adc.vchmax_res > VCHARGE_MAX_THRESHOLD ||
+		    charger->adc.vchmin_res < VCHARGE_MIN_THRESHOLD ||
+		    /* Tempreture readings are negative */
+		    charger->adc.tbat_res < TBAT_HIGH_THRESHOLD ||
+		    charger->adc.tbat_res > TBAT_LOW_THRESHOLD ) {
+			/* disable charger */
+			pr_info("%s: thresholds fail\n", __FUNCTION__);
+			charger->ops->set_charge(charger, 0);
+		}
+	}
+}
+
+static void em_x270_charging_monitor(struct work_struct *work)
+{
+	struct em_x270_charger *charger;
+	
+	charger = container_of(work, struct em_x270_charger, work.work);
+	
+	em_x270_check_charger_state(charger);
+	
+	/* reschedule for the next time */
+	schedule_delayed_work(&charger->work, charger->interval);
+}
+
+void em_x270_battery_release(struct device * dev)
+{
+}
+
+struct em_x270_battery_thresh {
+	int voltage;
+	int percentage;
+};
+
+static struct em_x270_battery_thresh vbat_ranges[] = {
+	{ 150, 100},
+	{ 149,  99},
+	{ 148,  98},
+	{ 147,  98},
+	{ 146,  97},
+	{ 145,  96},
+	{ 144,  96},
+	{ 143,  95},
+	{ 142,  94},
+	{ 141,  93},
+	{ 140,  92},
+	{ 139,  91},
+	{ 138,  90},
+	{ 137,  90},
+	{ 136,  89},
+	{ 135,  88},
+	{ 134,  88},
+	{ 133,  87},
+	{ 132,  86},
+	{ 131,  85},
+	{ 130,  83},
+	{ 129,  82},
+	{ 128,  81},
+	{ 127,  81},
+	{ 126,  80},
+	{ 125,  75},
+	{ 124,  74},
+	{ 123,  73},
+	{ 122,  70},
+	{ 121,  66},
+	{ 120,  65},
+	{ 119,  64},
+	{ 118,  64},
+	{ 117,  63},
+	{ 116,  59},
+	{ 115,  58},
+	{ 114,  57},
+	{ 113,  57},
+	{ 112,  56},
+	{ 111,  50},
+	{ 110,  49},
+	{ 109,  49},
+	{ 108,  48},
+	{ 107,  48},
+	{ 106,  33},
+	{ 105,  32},
+	{ 104,  32},
+	{ 103,  32},
+	{ 102,  31},
+	{ 101,  16},
+	{ 100,  15},
+	{  99,  15},
+	{  98,  15},
+	{  97,  10},
+	{  96,   9},
+	{  95,   7},
+	{  94,   3},
+	{  93,   0},
+};
+
+static enum power_supply_property em_x270_bat_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_AVG,
+	POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+};
+
+static void em_x270_bat_check_status(struct em_x270_charger *charger,
+				     union power_supply_propval *val)
+{
+	/* FIXME: below code is very crude approximation of actual
+	   states, we need to take into account voltage and current
+	   measurements to determine actual charger state */
+	if (charger->da9030_status & STATUS_CHDET) {
+		if (!usb_host_on()) {
+			if (charger->is_on) {
+				val->intval = POWER_SUPPLY_STATUS_CHARGING;
+			}
+			else {
+				val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+			}
+		}
+	}
+	else {
+		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+	}
+}
+
+static void em_x270_bat_check_health(struct em_x270_charger *charger,
+				     union power_supply_propval *val)
+{
+	if (charger->da9030_fault & FAULT_LOG_OVER_TEMP) {
+		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+	}
+	else if (charger->da9030_fault & FAULT_LOG_VBAT_OVER) {
+		val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+	}
+	else {
+		val->intval = POWER_SUPPLY_HEALTH_GOOD;
+	}
+}
+
+static int vbat_interpolate(int reg)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(vbat_ranges); i++ )
+		if (vbat_ranges[i].voltage == reg) {
+			pr_debug("%s: voltage = %d, percentage = %d\n",
+				__FUNCTION__, vbat_ranges[i].voltage,
+				vbat_ranges[i].percentage);
+			return vbat_ranges[i].percentage;
+		}
+	
+	return 0;
+}
+
+static int em_x270_bat_get_property(struct power_supply *psy,
+				   enum power_supply_property psp,
+				   union power_supply_propval *val)
+{
+	u32 reg;
+	struct em_x270_charger *charger;
+	charger = container_of(psy, struct em_x270_charger, bat);
+
+	switch(psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		em_x270_bat_check_status(charger, val);
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		em_x270_bat_check_health(charger, val);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = VOLTAGE_MAX_DESIGN;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+		val->intval = VOLTAGE_MIN_DESIGN;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		reg = charger->adc.vbat_res;
+		/* V = (reg / 256) * 2.65 + 2.65 (V) */
+		val->intval = ((reg * 2650000) >> 8) + 2650000;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_AVG:
+		reg = charger->adc.ichaverage_res;
+		val->intval = reg; /* reg */
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		reg = charger->adc.vbat_res;
+		val->intval = vbat_interpolate(reg);
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		reg = charger->adc.tbat_res;
+		val->intval = tbat_readings[reg];
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = "MaxPower";
+		pr_debug("%s: MFG = %s\n", __FUNCTION__, val->strval);
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = "LP555597P6H-FPS";
+		pr_debug("%s: MODEL = %s\n", __FUNCTION__, val->strval);
+		break;
+	default: break;
+	}
+	
+	return 0;
+}
+
+static void em_x270_setup_battery(struct power_supply *bat)
+{
+	bat->name = "em-x270-battery";
+	bat->type = POWER_SUPPLY_TYPE_BATTERY;
+	bat->properties = em_x270_bat_props;
+	bat->num_properties = ARRAY_SIZE(em_x270_bat_props);
+	bat->get_property = em_x270_bat_get_property;
+	bat->use_for_apm = 1;
+};
+
+static void em_x270_chiover_callback(int event, void *_charger)
+{
+	/* disable charger */
+	struct em_x270_charger *charger = _charger;
+/* 	pr_info("%s\n", __FUNCTION__); */
+	em_x270_set_charge(charger, 0);
+}
+
+static void em_x270_tbat_callback(int event, void *_charger)
+{
+	/* disable charger */
+	struct em_x270_charger *charger = _charger;
+/* 	pr_info("%s\n", __FUNCTION__); */
+	em_x270_set_charge(charger, 0);
+}
+
+static void em_x270_vbat_callback(int event, void *_charger)
+{
+	struct em_x270_charger *charger = _charger;
+	da9030_read_adc(&charger->adc);
+	
+	if (!charger->is_on) {
+		if (charger->adc.vbat_res < VBAT_LOW_THRESHOLD) {
+			/* set VBAT threshold for critical */
+			da9030_set_reg(VBATMON, VBAT_CRIT_THRESHOLD);
+			da9030_set_reg(VBATMON_1, VBAT_CRIT_THRESHOLD);
+			apm_queue_event(APM_LOW_BATTERY);
+		}
+		else if (charger->adc.vbat_res < VBAT_CRIT_THRESHOLD) {
+			/* notify the system of battery critical */
+			apm_queue_event(APM_CRITICAL_SUSPEND);
+		}
+	}
+}
+
+static void em_x270_chdet_callback(int event, void *_charger)
+{
+	struct em_x270_charger *charger = _charger;
+	int status = da9030_get_status();
+	pr_info("%s\n", __FUNCTION__);
+
+/* 	em_x270_check_charger_state(charger); */
+
+	/* check if we have "real" charger or our own USB pump */
+	if (!usb_host_on())
+		em_x270_set_charge(charger, !!(status & CHRG_CHARGER_ENABLE));
+}
+
+static int em_x270_battery_probe(struct platform_device *pdev)
+{
+	struct em_x270_charger *charger;
+
+	pr_debug("%s\n", __FUNCTION__);
+	charger = kzalloc(sizeof(*charger), GFP_KERNEL);
+	if (charger == NULL) {
+		return -ENOMEM;
+	}
+	
+	the_charger = charger;
+
+	charger->dev = &pdev->dev;
+
+	charger->ops = &em_x270_charger_ops;
+
+	charger->interval = 10 * HZ; /* 10 seconds between monotor runs */
+	em_x270_setup_battery(&charger->bat);
+
+	platform_set_drvdata(pdev, charger);
+
+	da9030_enable_adc();
+
+	INIT_DELAYED_WORK(&charger->work, em_x270_charging_monitor);
+	schedule_delayed_work(&charger->work, charger->interval);
+
+	charger->debug_file = em_x270_create_debugfs(charger);
+
+	em_x270_setup_battery(&charger->bat);
+
+	da9030_register_callback(DA9030_IRQ_CHDET,
+				 em_x270_chdet_callback,
+				 charger);
+	da9030_register_callback(DA9030_IRQ_VBATMON,
+				 em_x270_vbat_callback,
+				 charger);
+
+	/* critical condition events */
+	da9030_register_callback(DA9030_IRQ_CHIOVER,
+				 em_x270_chiover_callback,
+				 charger);
+	da9030_register_callback(DA9030_IRQ_TBAT,
+				 em_x270_tbat_callback,
+				 charger);
+
+	da9030_set_thresholds(TBAT_HIGH_THRESHOLD,
+			      TBAT_RESUME_THRESHOLD,
+			      TBAT_LOW_THRESHOLD,
+			      VBAT_LOW_THRESHOLD);
+
+	power_supply_register(&pdev->dev, &charger->bat);
+
+	return 0;
+}
+
+static int em_x270_battery_remove(struct platform_device *dev)
+{
+	struct em_x270_charger *charger = platform_get_drvdata(dev);
+
+	pr_debug("%s\n", __FUNCTION__);
+	em_x270_remove_debugfs(charger);
+	cancel_delayed_work(&charger->work);
+	power_supply_unregister(&charger->bat);
+
+	kfree(charger);
+	the_charger = NULL;
+
+	return 0;
+}
+
+static int em_x270_battery_suspend(struct platform_device *pdev,
+				   pm_message_t state)
+{
+	pr_info("%s\n", __FUNCTION__);
+	da9030_set_reg(REG_CONTROL_1_97, RC3_BUCK_EN | RC3_LDO6_EN);
+	da9030_set_reg(REG_CONTROL_2_98, 0);
+	da9030_set_reg(REG_CONTROL_2_18,RC2_LDO18_EN);
+	da9030_set_reg(REG_CONTROL_1_17,
+		       RC1_LDO16_EN | RC1_LDO15_EN | RC1_BUCK2_EN);
+	da9030_set_reg(WLED_CONTROL, 0);
+
+	da9030_set_reg(LED_1_CONTROL, 0);
+	da9030_set_reg(LED_4_CONTROL, 0);
+
+	return 0;
+}
+
+extern int em_x270_lcd_detect(void);
+static int em_x270_battery_resume(struct platform_device *pdev)
+{
+	pr_info("%s\n", __FUNCTION__);
+
+	da9030_set_reg(LED_1_CONTROL, 0xff);
+	da9030_set_reg(LED_4_CONTROL, 0xff);
+
+	da9030_set_reg(REG_CONTROL_1_97,
+		       RC3_BUCK_EN | RC3_LDO1_EN | RC3_LDO2_EN |
+		       RC3_LDO3_EN | RC3_LDO6_EN | RC3_LDO7_EN);
+	da9030_set_reg(REG_CONTROL_2_98,
+		       RC4_SIMCP_ENABLE | RC4_LDO11_EN |
+		       RC4_LDO9_EN | RC4_LDO8_EN);
+	da9030_set_reg(REG_CONTROL_2_18,
+		       RC2_SIMCP_EN | RC2_LDO18_EN | RC2_LDO19_EN);
+	da9030_set_reg(REG_CONTROL_1_17,
+		       RC1_LDO17_EN| RC1_LDO16_EN | RC1_LDO15_EN |
+		       RC1_LDO11_EN | RC1_LDO10_EN | RC1_BUCK2_EN);
+
+	if (em_x270_lcd_detect() != 0)
+		pr_info("%s: LCD resume failed\n", __FUNCTION__);
+
+	return 0;
+}
+
+static struct platform_driver em_x270_battery_driver = {
+	.driver	= {
+		.name    = "em-x270-battery",
+		.owner	 = THIS_MODULE,
+	},
+	.probe = em_x270_battery_probe,
+	.remove = em_x270_battery_remove,
+
+	.suspend_late = em_x270_battery_suspend,
+	.resume_early = em_x270_battery_resume,
+};
+
+/**************************************************************************/
+/* global patform power management */
+/**************************************************************************/
+/* suspend/resume button */
+static irqreturn_t em_x270_suspend_irq(int irq, void *data)
+{
+	apm_queue_event(APM_USER_SUSPEND);
+	return IRQ_HANDLED;
+}
+
+static void em_x270_goto_sleep(suspend_state_t state,
+			       unsigned long alarm_time,
+			       unsigned int alarm_enable)
+{
+	pr_info("%s\n", __FUNCTION__);
+	
+	the_charger->ops = &em_x270_charger_ops_suspend;
+
+	RTSR &= RTSR_ALE;
+	RTAR = RCNR + EM_X270_BATCHK_TIME_SUSPEND;
+
+	pxa_pm_enter(state);
+}
+
+static int em_x270_enter_suspend(unsigned long alarm_time,
+				 unsigned int alarm_enable)
+{
+	s32 status = 0;
+	s32 event_a, event_b;
+	int ret;
+	int retry = 10;
+	pr_info("%s\n", __FUNCTION__);
+	
+	/* make sure  power I2C state is consistent */
+	PWRICR &= ~ICR_IUE;
+
+	if ((PEDR & PWER_GPIO1)) {
+		/* button pressed */
+		/* clear pending event to avoid interrupt*/
+		PEDR &= ~PWER_GPIO1;
+		GEDR0 &= ~GPIO_bit(1);
+
+		ret = 0;
+	}
+	else if ((PEDR & PWER_RTC)) {
+		PEDR &= ~PWER_RTC;
+		pr_debug("%s: timer alarm\n", __FUNCTION__);
+		em_x270_check_charger_state(the_charger);
+		ret = 1;
+	}
+	else if ((PEDR & PWER_GPIO0)) {
+		PEDR &= ~PWER_GPIO0;
+		GEDR0 &= ~GPIO_bit(0);
+
+		/* we woke up because of DA9030 event */
+		do {
+			status = the_charger->ops->da9030_get_reg(STATUS);
+			udelay(1000);
+		} while (status < 0 && retry--);
+
+		the_charger->da9030_status = status;
+		event_a = the_charger->ops->da9030_get_reg(EVENT_A);
+		event_b = the_charger->ops->da9030_get_reg(EVENT_B);
+
+		pr_info("%s: DA9030 wakeup: status: %x, ev_a: %x, ev_b: %x\n",
+			__FUNCTION__, the_charger->da9030_status,
+			event_a, event_b);
+
+/* 		if ((the_charger->da9030_status & STATUS_CHDET)) { */
+		if (event_a & EVENT_A_CHDET) {
+			pr_info("%s: EVENT_A_CHDET\n", __FUNCTION__);
+			em_x270_check_charger_state(the_charger);
+		}
+		else {
+			pr_info("%s: What the hell?\n", __FUNCTION__);
+		}
+
+		ret = 1;
+	}
+	else {
+		/* wake up is not DA9030, so continue and let battery
+		   driver check the charger state */
+		ret = 0;
+	}
+
+	/* get back to sleep */
+	if (ret)
+		em_x270_goto_sleep(PM_SUSPEND_MEM, alarm_time, alarm_enable);
+
+	return ret;
+}
+
+static int em_x270_pm_enter(suspend_state_t state)
+{
+	unsigned long alarm_time = RTAR;
+	unsigned int alarm_status = ((RTSR & RTSR_ALE) != 0);
+
+	/* pre-suspend */
+	pr_info("suspending emma\n");
+	pxa_gpio_mode(38 | GPIO_OUT | GPIO_DFLT_HIGH);
+
+	em_x270_goto_sleep(state, alarm_time, alarm_status);
+
+	/* check if we were resumed because of charger events */
+	while (em_x270_enter_suspend(alarm_time, alarm_status))
+		{}
+
+	/* make sure  power I2C state is consistent */
+	PWRICR &= ~ICR_IUE;
+
+	/* resume */
+	pr_info("emma is resumed\n");
+	the_charger->ops = &em_x270_charger_ops;
+
+	return 0;
+}
+
+static struct pm_ops em_x270_pm_ops = {
+	.enter		= em_x270_pm_enter,
+	.valid		= pm_valid_only_mem,
+};
+
+static int em_x270_pm_init(void)
+{
+	int ret;
+	pm_set_ops(&em_x270_pm_ops);
+
+	set_irq_type(IRQ_GPIO(1), IRQT_RISING);
+
+	ret = platform_driver_register(&em_x270_battery_driver);
+	if (ret)
+		return ret;
+
+	ret = request_irq(IRQ_GPIO(1), em_x270_suspend_irq, IRQF_DISABLED,
+			  "suspend button", 0);
+	if (ret) {
+		platform_driver_unregister(&em_x270_battery_driver);
+	}
+
+	return ret;
+}
+
+static void em_x270_pm_exit(void)
+{
+	free_irq(IRQ_GPIO(1), em_x270_suspend_irq);
+	platform_driver_unregister(&em_x270_battery_driver);
+}
+
+/* make sure I2C is already running */
+late_initcall(em_x270_pm_init);
+module_exit(em_x270_pm_exit);
+
+MODULE_DESCRIPTION("EM-X270 power manager");
+MODULE_AUTHOR("Mike Rapoport");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-pxa/em-x270.c b/arch/arm/mach-pxa/em-x270.c
index 3d0ad50..402d792 100644
--- a/arch/arm/mach-pxa/em-x270.c
+++ b/arch/arm/mach-pxa/em-x270.c
@@ -18,20 +18,26 @@
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 
-#include <asm/mach-types.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
 
+#include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 
 #include <asm/arch/pxa-regs.h>
 #include <asm/arch/pxafb.h>
 #include <asm/arch/ohci.h>
 #include <asm/arch/mmc.h>
+#include <asm/arch/pxa27x_keyboard.h>
 #include <asm/arch/bitfield.h>
 
+#include <asm/arch/udc.h>
+
+#include <asm/arch/sharpsl.h>
+
 #include "generic.h"
 
 /* GPIO IRQ usage */
-#define EM_X270_MMC_PD		(105)
 #define EM_X270_ETHIRQ		IRQ_GPIO(41)
 #define EM_X270_MMC_IRQ		IRQ_GPIO(13)
 
@@ -213,6 +219,68 @@ static struct platform_device em_x270_nand = {
 	}
 };
 
+/* DA9030 */
+static struct i2c_board_info em_x270_pmic_info = {
+	.driver_name = "da9030",
+	.type = "pmic",
+	.addr = 0x49,
+	.irq = IRQ_GPIO(0),
+};
+
+/* Keypad */
+/* The Demo KeyPad has the following mapping:
+ *	(0,0)		(1,2)		(2,1)
+ *		(0,2)	(1,1)	(2,0)
+ *	(0,1)		(1,0)		(2,2)
+ */
+static struct pxa27x_keyboard_platform_data em_x270_kbd = {
+	.nr_rows = 3,
+	.nr_cols = 3,
+	.keycodes = {
+		{    /* row 0 */
+			-1,
+			-1,
+			KEY_LEFT,
+		},
+		{    /* row 1 */
+			KEY_UP,
+			KEY_ENTER,
+			KEY_DOWN
+		},
+		{    /* row 2 */
+			KEY_RIGHT,
+			-1,
+			-1
+		},
+	},
+	.gpio_modes = {
+		(100 | GPIO_ALT_FN_1_IN),
+		(101 | GPIO_ALT_FN_1_IN),
+		(102 | GPIO_ALT_FN_1_IN),
+		(103 | GPIO_ALT_FN_2_OUT),
+		(104 | GPIO_ALT_FN_2_OUT),
+		(105 | GPIO_ALT_FN_2_OUT),
+	},
+};
+
+static struct platform_device em_x270_pxa_keypad = {
+        .name   = "pxa27x-keyboard",
+        .id     = -1,
+	.dev    =  {
+		.platform_data  = &em_x270_kbd,
+	},
+};
+
+static struct platform_device em_x270_battery_device = {
+	.name = "em-x270-battery",
+	.id = -1,
+};
+
+static struct platform_device em_x270_led_device = {
+	.name = "em-x270-led",
+	.id = -1,
+};
+
 /* platform devices */
 static struct platform_device *platform_devices[] __initdata = {
 	&em_x270_dm9k,
@@ -220,6 +288,9 @@ static struct platform_device *platform_devices[] __initdata = {
 	&em_x270_ts,
 	&em_x270_rtc,
 	&em_x270_nand,
+	&em_x270_pxa_keypad,
+	&em_x270_battery_device,
+	&em_x270_led_device,
 };
 
 
@@ -241,6 +312,33 @@ static struct pxaohci_platform_data em_x270_ohci_platform_data = {
 	.init		= em_x270_ohci_init,
 };
 
+/*
+ * USB Client (Gadget/UDC)
+ */
+static void em_x270_udc_command(int cmd)
+{
+	switch(cmd)	{
+	case PXA2XX_UDC_CMD_CONNECT:
+		UP2OCR = UP2OCR_HXOE | UP2OCR_DMPUE | UP2OCR_DMPUBE;
+		break;
+	case PXA2XX_UDC_CMD_DISCONNECT:
+/* 	UP2OCR = UP2OCR_HXS | UP2OCR_HXOE; */
+		//UP2OCR = UP2OCR_HXOE | UP2OCR_DMPUE | UP2OCR_DMPUBE;
+		break;
+	}
+}
+
+static int em_x270_udc_detect(void)
+{
+	return 1;
+}
+
+static struct pxa2xx_udc_mach_info em_x270_udc_info __initdata = {
+	.udc_is_connected	= em_x270_udc_detect,
+	.udc_command		= em_x270_udc_command,
+};
+
+
 
 static int em_x270_mci_init(struct device *dev,
 			    irq_handler_t em_x270_detect_int,
@@ -256,9 +354,6 @@ static int em_x270_mci_init(struct device *dev,
 	pxa_gpio_mode(GPIO110_MMCDAT2_MD);
 	pxa_gpio_mode(GPIO111_MMCDAT3_MD);
 
-	/* EM-X270 uses GPIO13 as SD power enable */
-	pxa_gpio_mode(EM_X270_MMC_PD | GPIO_OUT);
-
 	err = request_irq(EM_X270_MMC_IRQ, em_x270_detect_int,
 			  IRQF_DISABLED | IRQF_TRIGGER_FALLING,
 			  "MMC card detect", data);
@@ -313,12 +408,19 @@ static struct pxafb_mach_info em_x270_lcd = {
 	.num_modes	= 1,
 	.cmap_inverse	= 0,
 	.cmap_static	= 0,
-	.lccr0		= LCCR0_PAS,
-	.lccr3		= LCCR3_PixClkDiv(0x01) | LCCR3_Acb(0xff),
+
+	.lccr0		= LCCR0_Act,
+	.lccr3		= LCCR3_PixFlEdg,
 };
 
 static void __init em_x270_init(void)
 {
+	/* register PMIC */
+	i2c_register_board_info(1, &em_x270_pmic_info, 1);
+
+	/* setup DA9030 irq */
+	set_irq_type(IRQ_GPIO(0), IRQT_FALLING);
+
 	/* setup LCD */
 	set_pxa_fb_info(&em_x270_lcd);
 
@@ -329,6 +431,8 @@ static void __init em_x270_init(void)
 	pxa_set_mci_info(&em_x270_mci_platform_data);
 	pxa_set_ohci_info(&em_x270_ohci_platform_data);
 
+	pxa_set_udc_info(&em_x270_udc_info);
+
 	/* setup STUART GPIOs */
 	pxa_gpio_mode(GPIO46_STRXD_MD);
 	pxa_gpio_mode(GPIO47_STTXD_MD);
@@ -341,9 +445,16 @@ static void __init em_x270_init(void)
 
 	/* Setup interrupt for dm9000 */
 	set_irq_type(EM_X270_ETHIRQ, IRQT_RISING);
+
+	PCFR = 0x6;
+	PSLR = 0xff400000;
+        PMCR = 0x00000005;
+        PWER = 0x80000003;
+        PFER = 0x00000003;
+        PRER = 0x00000000;
 }
 
-MACHINE_START(EM_X270, "Compulab EM-x270")
+MACHINE_START(EM_X270, "Compulab EM-X270")
 	.boot_params	= 0xa0000100,
 	.phys_io	= 0x40000000,
 	.io_pg_offst	= (io_p2v(0x40000000) >> 18) & 0xfffc,
diff --git a/arch/arm/mach-pxa/pwr-i2c.c b/arch/arm/mach-pxa/pwr-i2c.c
new file mode 100644
index 0000000..8a501c4
--- /dev/null
+++ b/arch/arm/mach-pxa/pwr-i2c.c
@@ -0,0 +1,539 @@
+/*
+ * (C) Copyrihgt 2007 CompuLab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ * Adaptation of U-Boot I2C driver for PXA.
+ *
+ * (C) Copyright 2000
+ * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it
+ *
+ * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * (C) Copyright 2003 Pengutronix e.K.
+ * Robert Schwebel <r.schwebel@pengutronix.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * Back ported to the 8xx platform (from the 8260 platform) by
+ * Murray.Jensen@cmst.csiro.au, 27-Jan-01.
+ */
+
+/* #define DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/pwr-i2c.h>
+
+#define I2C_ICR_INIT    (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE)
+#define I2C_ISR_INIT	0x7FF
+
+/* Shall the current transfer have a start/stop condition? */
+#define I2C_COND_NORMAL		0
+#define I2C_COND_START		1
+#define I2C_COND_STOP		2
+
+/* Shall the current transfer be ack/nacked or being waited for it? */
+#define I2C_ACKNAK_WAITACK	1
+#define I2C_ACKNAK_SENDACK	2
+#define I2C_ACKNAK_SENDNAK	4
+
+/* Specify who shall transfer the data (master or slave) */
+#define I2C_READ		0
+#define I2C_WRITE		1
+
+/* All transfers are described by this data structure */
+struct i2c_msg {
+	u8 condition;
+	u8 acknack;
+	u8 direction;
+	u8 data;
+};
+
+/**
+ * pxa_pwr_i2c_transfer: - reset the host controller
+ *
+ */
+/* static void pxa_pwr_i2c_reset(void) */
+/* { */
+/* 	int i; */
+	
+/* /\* 	CKEN |= CKEN_PWRI2C; *\/ */
+/* /\* 	PWRICR |= PCFR_PI2C_EN; *\/ */
+
+/* /\* 	/\\* delay 250ms *\\/ *\/ */
+/* /\* 	for (i = 0; i < 250; i++) *\/ */
+/* /\* 		udelay(1000); *\/ */
+
+/* /\* 	PWRICR &= ~(ICR_MA | ICR_START | ICR_STOP); *\/ */
+/* /\* 	PWRICR |= ICR_UR; *\/ */
+/* /\* 	PWRISR = 0x7ff; *\/ */
+
+/* /\* 	PWRICR &= ~ICR_UR; *\/ */
+/* /\* 	PWRICR = ICR_GCD | ICR_SCLE; *\/ */
+/* /\* 	PWRICR |= ICR_IUE; *\/ */
+/* /\* 	PWRICR |= 0x8000; *\/ */
+
+/* /\* 	udelay(1000); *\/ */
+
+/* 	return; */
+
+/* 	PWRICR &= ~ICR_IUE;	/\* disable unit *\/ */
+/* 	PWRICR |= ICR_UR;	/\* reset the unit *\/ */
+/* 	udelay(100); */
+/* 	PWRICR &= ~ICR_IUE;	/\* disable unit *\/ */
+
+/* 	CKEN |= CKEN_PWRI2C; */
+/* 	PCFR |= PCFR_PI2C_EN; */
+
+/* 	PWRICR = I2C_ICR_INIT;	/\* set control register values *\/ */
+/* 	PWRISR = I2C_ISR_INIT;	/\* set clear interrupt bits *\/ */
+/* 	PWRICR |= ICR_IUE;	/\* enable unit *\/ */
+/* 	udelay(100); */
+/* } */
+
+static void pwr_i2c_abort()
+{
+	unsigned long timeout = 250000;
+	unsigned long time = 0;
+
+	while ((time < timeout) && (PWRIBMR & 0x1) == 0) {
+		unsigned long icr = PWRICR;
+
+		icr &= ~ICR_START;
+		icr |= ICR_ACKNAK | ICR_STOP | ICR_TB;
+
+		PWRICR = icr;
+
+		udelay(1000);
+		time += 1000;
+	}
+
+	PWRICR &= ~(ICR_MA | ICR_START | ICR_STOP);
+}
+
+static void pxa_pwr_i2c_reset(void)
+{
+	pr_debug("Resetting I2C Controller Unit\n");
+
+	/* abort any transfer currently under way */
+	pwr_i2c_abort();
+
+	/* reset according to 9.8 */
+	PWRICR = ICR_UR;
+	PWRISR = I2C_ISR_INIT;
+	PWRISR &= ~ICR_UR;
+
+	/* set control register values */
+	PWRICR = I2C_ICR_INIT;
+
+	/* enable unit */
+	PWRICR |= ICR_IUE;
+	udelay(100);
+
+/* 	CKEN |= CKEN_PWRI2C; */
+/* 	PCFR |= PCFR_PI2C_EN; */
+}
+
+/**
+ * i2c_isr_set_cleared: - wait until certain bits of the I2C status register
+ *	                  are set and cleared
+ *
+ * @return: 1 in case of success, 0 means timeout (no match within 10 ms).
+ */
+static int pxa_pwr_i2c_isr_set_cleared(unsigned long set_mask,
+				       unsigned long cleared_mask)
+{
+	int timeout = 10000;
+
+	while (((PWRISR & set_mask) != set_mask)
+	       || ((PWRISR & cleared_mask) != 0)) {
+		udelay(10);
+		if (timeout-- < 0)
+			return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * pxa_pwr_i2c_transfer: - Transfer one byte over the i2c bus
+ *
+ * This function can tranfer a byte over the i2c bus in both directions.
+ * It is used by the public API functions.
+ *
+ * @return:  0: transfer successful
+ *          -EINVAL: message is empty
+ *          -ETIMEDOUT: transmit timeout
+ *          -E: ACK missing
+ *          -ETIMEDOUT: receive timeout
+ *          -EINVAL: illegal parameters
+ *          -EBUSY: bus is busy and couldn't be aquired
+ */
+int pxa_pwr_i2c_transfer(struct i2c_msg *msg)
+{
+	int ret;
+
+	if (!msg)
+		goto transfer_error_msg_empty;
+
+	switch (msg->direction) {
+	case I2C_WRITE:
+		/* check if bus is not busy */
+/* 		if (!pxa_pwr_i2c_isr_set_cleared(0, (ISR_IBB | ISR_UB))) */
+/* 			goto transfer_error_bus_busy; */
+
+		/* start transmission */
+		PWRICR &= ~ICR_START;
+		PWRICR &= ~ICR_STOP;
+		PWRIDBR = msg->data;
+		if (msg->condition == I2C_COND_START)
+			PWRICR |= ICR_START;
+		if (msg->condition == I2C_COND_STOP)
+			PWRICR |= ICR_STOP;
+		if (msg->acknack == I2C_ACKNAK_SENDNAK)
+			PWRICR |= ICR_ACKNAK;
+		if (msg->acknack == I2C_ACKNAK_SENDACK)
+			PWRICR &= ~ICR_ACKNAK;
+		PWRICR &= ~ICR_ALDIE;
+		PWRICR |= ICR_TB;
+
+		/* transmit register empty? */
+		if (!pxa_pwr_i2c_isr_set_cleared(ISR_ITE, 0))
+			goto transfer_error_transmit_timeout;
+
+		/* clear 'transmit empty' state */
+		PWRISR |= ISR_ITE;
+
+		/* wait for ACK from slave */
+		if (msg->acknack == I2C_ACKNAK_WAITACK)
+			if (!pxa_pwr_i2c_isr_set_cleared(0, ISR_ACKNAK))
+				goto transfer_error_ack_missing;
+		break;
+	case I2C_READ:
+		/* check if bus is not busy */
+/* 		if (!pxa_pwr_i2c_isr_set_cleared(0, ISR_IBB)) */
+/* 			goto transfer_error_bus_busy; */
+
+		/* start receive */
+		PWRICR &= ~ICR_START;
+		PWRICR &= ~ICR_STOP;
+		if (msg->condition == I2C_COND_START)
+			PWRICR |= ICR_START;
+		if (msg->condition == I2C_COND_STOP)
+			PWRICR |= ICR_STOP;
+		if (msg->acknack == I2C_ACKNAK_SENDNAK)
+			PWRICR |= ICR_ACKNAK;
+		if (msg->acknack == I2C_ACKNAK_SENDACK)
+			PWRICR &= ~ICR_ACKNAK;
+		PWRICR &= ~ICR_ALDIE;
+		PWRICR |= ICR_TB;
+
+		/* receive register full? */
+		if (!pxa_pwr_i2c_isr_set_cleared(ISR_IRF, 0))
+			goto transfer_error_receive_timeout;
+
+		msg->data = PWRIDBR;
+
+		/* clear 'receive empty' state */
+		PWRISR |= ISR_IRF;
+
+		break;
+	default:
+		goto transfer_error_illegal_param;
+
+	}
+
+	return 0;
+
+transfer_error_msg_empty:
+	pr_debug("%s: error: 'msg' is empty\n", __FUNCTION__);
+	ret = -1;
+	goto i2c_transfer_finish;
+
+transfer_error_transmit_timeout:
+	pr_debug("%s: error:  transmit timeout\n", __FUNCTION__);
+	ret = -2;
+	goto i2c_transfer_finish;
+
+transfer_error_ack_missing:
+	pr_debug("%s: error:  ACK missing\n", __FUNCTION__);
+	ret = -3;
+	goto i2c_transfer_finish;
+
+transfer_error_receive_timeout:
+	pr_debug("%s: error:  receive timeout\n", __FUNCTION__);
+	ret = -4;
+	goto i2c_transfer_finish;
+
+transfer_error_illegal_param:
+	pr_debug("%s: error:  illegal parameters\n", __FUNCTION__);
+	ret = -5;
+	goto i2c_transfer_finish;
+
+transfer_error_bus_busy:
+	pr_debug("%s: error:  bus is busy\n", __FUNCTION__);
+	ret = -6;
+	goto i2c_transfer_finish;
+
+i2c_transfer_finish:
+	pr_debug("%s: ISR: 0x%04x\n", __FUNCTION__, ISR);
+	return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+/* API Functions                                                            */
+/* ------------------------------------------------------------------------ */
+/**
+ * i2c_probe: - Test if a chip answers for a given i2c address
+ *
+ * @chip:	address of the chip which is searched for
+ * @return: 	0 if a chip was found, -1 otherwhise
+ */
+int pxa_pwr_i2c_probe(u8 chip)
+{
+	struct i2c_msg msg;
+	int ret;
+
+	pxa_pwr_i2c_reset();
+
+	msg.condition = I2C_COND_START;
+	msg.acknack = I2C_ACKNAK_WAITACK;
+	msg.direction = I2C_WRITE;
+	msg.data = (chip << 1) + 1;
+	if ((ret = pxa_pwr_i2c_transfer(&msg)))
+		return ret;
+
+	msg.condition = I2C_COND_STOP;
+	msg.acknack = I2C_ACKNAK_SENDNAK;
+	msg.direction = I2C_READ;
+	msg.data = 0x00;
+	if ((ret = pxa_pwr_i2c_transfer(&msg)))
+		return ret;
+
+	return 0;
+}
+
+/**
+ * i2c_read: - Read multiple bytes from an i2c device
+ *
+ * The higher level routines take into account that this function is only
+ * called with len < page length of the device (see configuration file)
+ *
+ * @chip:	address of the chip which is to be read
+ * @addr:	i2c data address within the chip
+ * @alen:	length of the i2c data address (1..2 bytes)
+ * @buffer:	where to write the data
+ * @len:	how much byte do we want to read
+ * @return:	0 in case of success
+ */
+int pxa_pwr_i2c_read(u8 chip, uint addr, int alen, u8 * buffer, int len)
+{
+	struct i2c_msg msg;
+	u8 addr_bytes[3];	/* lowest...highest byte of data address */
+	int ret;
+
+	pr_debug("%s(chip=0x%02x, addr=0x%02x, alen=0x%02x, len=0x%02x)\n",
+		 __FUNCTION__, chip, addr, alen, len);
+
+	pxa_pwr_i2c_reset();
+
+	/* dummy chip address write */
+	pr_debug("%s: dummy chip address write\n", __FUNCTION__);
+	msg.condition = I2C_COND_START;
+	msg.acknack = I2C_ACKNAK_WAITACK;
+	msg.direction = I2C_WRITE;
+	msg.data = (chip << 1);
+	msg.data &= 0xFE;
+	if ((ret = pxa_pwr_i2c_transfer(&msg)))
+		return ret;
+
+	/*
+	 * send memory address bytes;
+	 * alen defines how much bytes we have to send.
+	 */
+	/*addr &= ((1 << CFG_EEPROM_PAGE_WRITE_BITS)-1); */
+	addr_bytes[0] = (u8) ((addr >> 0) & 0x000000FF);
+	addr_bytes[1] = (u8) ((addr >> 8) & 0x000000FF);
+	addr_bytes[2] = (u8) ((addr >> 16) & 0x000000FF);
+
+	while (--alen >= 0) {
+		pr_debug("%s: send memory word address byte %1d\n",
+			 __FUNCTION__, alen);
+		msg.condition = I2C_COND_NORMAL;
+		msg.acknack = I2C_ACKNAK_WAITACK;
+		msg.direction = I2C_WRITE;
+		msg.data = addr_bytes[alen];
+		if ((ret = pxa_pwr_i2c_transfer(&msg)))
+			return ret;
+	}
+
+	/* start read sequence */
+	pr_debug("%s: start read sequence\n", __FUNCTION__);
+	msg.condition = I2C_COND_START;
+	msg.acknack = I2C_ACKNAK_WAITACK;
+	msg.direction = I2C_WRITE;
+	msg.data = (chip << 1);
+	msg.data |= 0x01;
+	if ((ret = pxa_pwr_i2c_transfer(&msg)))
+		return ret;
+
+	/* read bytes; send NACK at last byte */
+	while (len--) {
+		if (len == 0) {
+			msg.condition = I2C_COND_STOP;
+			msg.acknack = I2C_ACKNAK_SENDNAK;
+		} else {
+			msg.condition = I2C_COND_NORMAL;
+			msg.acknack = I2C_ACKNAK_SENDACK;
+		}
+
+		msg.direction = I2C_READ;
+		msg.data = 0x00;
+		if ((ret = pxa_pwr_i2c_transfer(&msg)))
+			return ret;
+
+		*buffer = msg.data;
+		pr_debug("%s: reading byte (0x%08x)=0x%02x\n",
+			__FUNCTION__, (unsigned int)buffer, *buffer);
+		buffer++;
+	}
+
+	pxa_pwr_i2c_reset();
+	return 0;
+}
+
+/**
+ * i2c_write: -  Write multiple bytes to an i2c device
+ *
+ * The higher level routines take into account that this function is only
+ * called with len < page length of the device (see configuration file)
+ *
+ * @chip:	address of the chip which is to be written
+ * @addr:	i2c data address within the chip
+ * @alen:	length of the i2c data address (1..2 bytes)
+ * @buffer:	where to find the data to be written
+ * @len:	how much byte do we want to read
+ * @return:	0 in case of success
+ */
+int pxa_pwr_i2c_write(u8 chip, uint addr, int alen, u8 * buffer, int len)
+{
+	struct i2c_msg msg;
+	u8 addr_bytes[3];	/* lowest...highest byte of data address */
+	int ret;
+
+	pr_debug("%s(chip=0x%02x, addr=0x%02x, alen=0x%02x, len=0x%02x)\n",
+		  __FUNCTION__, chip, addr, alen, len);
+
+	pxa_pwr_i2c_reset();
+
+	/* chip address write */
+	pr_debug("%s: chip address write\n", __FUNCTION__);
+	msg.condition = I2C_COND_START;
+	msg.acknack = I2C_ACKNAK_WAITACK;
+	msg.direction = I2C_WRITE;
+	msg.data = (chip << 1);
+	msg.data &= 0xFE;
+	if ((ret = pxa_pwr_i2c_transfer(&msg)))
+		return ret;
+
+	/*
+	 * send memory address bytes;
+	 * alen defines how much bytes we have to send.
+	 */
+	addr_bytes[0] = (u8) ((addr >> 0) & 0x000000FF);
+	addr_bytes[1] = (u8) ((addr >> 8) & 0x000000FF);
+	addr_bytes[2] = (u8) ((addr >> 16) & 0x000000FF);
+
+	while (--alen >= 0) {
+		pr_debug("%s: send memory word address\n", __FUNCTION__);
+		msg.condition = I2C_COND_NORMAL;
+		msg.acknack = I2C_ACKNAK_WAITACK;
+		msg.direction = I2C_WRITE;
+		msg.data = addr_bytes[alen];
+		if ((ret = pxa_pwr_i2c_transfer(&msg)))
+			return ret;
+	}
+
+	/* write bytes; send NACK at last byte */
+	while (len--) {
+		pr_debug("%s: writing byte (0x%08x)=0x%02x\n",
+			__FUNCTION__, (unsigned int)buffer, *buffer);
+
+		if (len == 0)
+			msg.condition = I2C_COND_STOP;
+		else
+			msg.condition = I2C_COND_NORMAL;
+
+		msg.acknack = I2C_ACKNAK_WAITACK;
+		msg.direction = I2C_WRITE;
+		msg.data = *(buffer++);
+
+		if ((ret = pxa_pwr_i2c_transfer(&msg)))
+			return ret;
+	}
+
+	pxa_pwr_i2c_reset();
+	return 0;
+}
+
+/**
+ * pxa_pwr_i2c_reg_read: - Read single byte from an i2c device
+ *
+ * @chip:	address of the chip which is to be read
+ * @reg:	i2c data address within the chip
+ * @return:	data in case of success, negative error code otherwise
+ */
+s32 pxa_pwr_i2c_reg_read(u8 chip, u8 reg)
+{
+	char buf;
+	int ret;
+
+	pr_debug("%s(chip=0x%02x, reg=0x%02x)\n", __FUNCTION__, chip, reg);
+	ret = pxa_pwr_i2c_read(chip, reg, 1, &buf, 1);
+	if (ret == 0)
+		ret = buf;
+
+	return ret;
+}
+
+/**
+ * pxa_pwr_i2c_reg_write: -  Write multiple bytes to an i2c device
+ *
+ * The higher level routines take into account that this function is only
+ * called with len < page length of the device (see configuration file)
+ *
+ * @chip:	address of the chip which is to be written
+ * @reg:	i2c data address within the chip
+ * @return:	0 in case of success, negative error code otherwise
+ */
+s32 pxa_pwr_i2c_reg_write(u8 chip, u8 reg, u8 val)
+{
+	pr_debug("%s(chip=0x%02x, reg=0x%02x, val=0x%02x)\n",
+		  __FUNCTION__, chip, reg, val);
+	return pxa_pwr_i2c_write(chip, reg, 1, &val, 1);
+}
+
+MODULE_DESCRIPTION("PXA Power I2C");
+MODULE_AUTHOR("Mike Rapoport");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c
index 203371a..36b695f 100644
--- a/arch/arm/mach-pxa/pxa27x.c
+++ b/arch/arm/mach-pxa/pxa27x.c
@@ -243,7 +243,11 @@ void pxa27x_cpu_pm_enter(suspend_state_t state)
 	case PM_SUSPEND_MEM:
 		/* set resume return address */
 		PSPR = virt_to_phys(pxa_cpu_resume);
-		pxa27x_cpu_suspend(PWRMODE_SLEEP);
+#ifdef CONFIG_MACH_EM_X270
+ 		pxa27x_cpu_suspend(PWRMODE_DEEPSLEEP);
+#else
+  		pxa27x_cpu_suspend(PWRMODE_SLEEP);
+#endif
 		break;
 	}
 }
diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c
index bae47e1..a16e532 100644
--- a/arch/arm/mach-pxa/spitz.c
+++ b/arch/arm/mach-pxa/spitz.c
@@ -349,6 +349,32 @@ static struct pxamci_platform_data spitz_mci_platform_data = {
 
 
 /*
+ * USB Client (Gadget/UDC)
+ */
+static void spitz_udc_command(int cmd)
+{
+	switch(cmd)	{
+	case PXA2XX_UDC_CMD_CONNECT:
+		UP2OCR = UP2OCR_HXOE | UP2OCR_DMPUE | UP2OCR_DMPUBE;
+		break;
+	case PXA2XX_UDC_CMD_DISCONNECT:
+		//UP2OCR = UP2OCR_HXOE | UP2OCR_DMPUE | UP2OCR_DMPUBE;
+		break;
+	}
+}
+
+static int spitz_udc_detect(void)
+{
+	return 1;
+}
+
+static struct pxa2xx_udc_mach_info spitz_udc_info __initdata = {
+	.udc_is_connected	= spitz_udc_detect,
+	.udc_command		= spitz_udc_command,
+};
+
+
+/*
  * USB Host (OHCI)
  */
 static int spitz_ohci_init(struct device *dev)
@@ -499,6 +525,7 @@ static void __init common_init(void)
 	pxa_gpio_mode(SPITZ_GPIO_HSYNC | GPIO_IN);
 
 	platform_add_devices(devices, ARRAY_SIZE(devices));
+	pxa_set_udc_info(&spitz_udc_info);
 	pxa_set_mci_info(&spitz_mci_platform_data);
 	pxa_set_ohci_info(&spitz_ohci_platform_data);
 	pxa_set_ficp_info(&spitz_ficp_platform_data);
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index 2e1c24f..f80f2b1 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -163,4 +163,17 @@ config MENELAUS
 	  and other features that are often used in portable devices like
 	  cell phones and PDAs.
 
+config DA9030
+	tristate "Dialog Semiconductor DA9030 power management chip"
+	depends on EXPERIMENTAL && EMBEDDED
+	help
+	  If you say yes here you get support for the Dialog
+	  Semiconductor DA9030 power management chip. 
+	  This includes voltage regulators, lithium ion/polymer battery
+	  charging, and other features that are often used in portable
+	  devices like PDAs, cell phones and cameras.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called da9030.
+
 endmenu
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
index ca924e1..69f545c 100644
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
 obj-$(CONFIG_TPS65010)		+= tps65010.o
 obj-$(CONFIG_MENELAUS)		+= menelaus.o
 obj-$(CONFIG_SENSORS_TSL2550)	+= tsl2550.o
+obj-$(CONFIG_DA9030)		+= da9030.o
 
 ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/chips/da9030.c b/drivers/i2c/chips/da9030.c
new file mode 100644
index 0000000..9791272
--- /dev/null
+++ b/drivers/i2c/chips/da9030.c
@@ -0,0 +1,1213 @@
+/*
+ * Dialog Semiconductor DA9030 power management IC driver
+ *
+ * Copyright (C) 2007 Compulab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * Some parts based on menelaus.c:
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/da9030.h>
+#include <linux/kernel_stat.h>
+#include <linux/random.h>
+#include <linux/mutex.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include "da9030.h"
+
+#define DRIVER_NAME "da9030"
+
+static unsigned short normal_i2c[] = { 0x49, I2C_CLIENT_END };
+
+I2C_CLIENT_INSMOD;
+
+struct da9030 {
+	struct mutex lock;
+	struct i2c_client *client;
+
+	struct work_struct event_work;
+
+	/* there are 24 interrupts */
+	void (*callbacks[24])(int event, void *data);
+	void *callbacks_data[24];
+
+	struct dentry *debug_file;
+};
+
+/* I hardly believe there can be more than 1 such chip in the system,
+   so keeping its global instance won't really hurt */
+static struct da9030 *the_da9030;
+
+unsigned char defined_regs[] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
+	0x50, 0x51,
+	0x60, 0x61, 0x62, 0x63,
+	0x80, 0x81,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
+	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
+};
+
+static inline int is_reg_valid(u32 reg)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(defined_regs); i++)
+		if (reg == defined_regs[i])
+			return 1;
+
+	return 0;
+}
+
+s32 da9030_get_reg(u32 reg)
+{
+	if (!is_reg_valid(reg))
+		return -EINVAL;
+
+	return i2c_smbus_read_byte_data(the_da9030->client, reg);
+}
+EXPORT_SYMBOL(da9030_get_reg);
+
+s32 da9030_set_reg(u32 reg, u8 val)
+{
+	if (!is_reg_valid(reg))
+		return -EINVAL;
+
+	return i2c_smbus_write_byte_data(the_da9030->client, reg, val);
+}
+EXPORT_SYMBOL(da9030_set_reg);
+
+static s32 da9030_update_reg_bits(u32 reg, int bits, int shift, int val)
+{
+	int ret;
+	int new_val;
+
+	mutex_lock(&the_da9030->lock);
+	ret = da9030_get_reg(reg);
+	if (ret < 0)
+		goto out;
+
+	new_val = reg & ~(((1 << bits) - 1) << shift);
+	new_val |= (val << shift);
+
+	ret = da9030_set_reg(reg, new_val);
+	mutex_unlock(&the_da9030->lock);
+
+out:
+	return ret;
+}
+
+int da9030_get_status(void)
+{
+	s32 ret;
+
+	mutex_lock(&the_da9030->lock);
+	ret = da9030_get_reg(STATUS);
+	mutex_unlock(&the_da9030->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(da9030_get_status);
+
+int da9030_get_fault_log(void)
+{
+	s32 ret;
+
+	mutex_lock(&the_da9030->lock);
+	ret = da9030_get_reg(FAULT_LOG);
+	mutex_unlock(&the_da9030->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(da9030_get_fault_log);
+
+void da9030_read_adc(struct da9030_adc_res *adc)
+{
+ 	mutex_lock(&the_da9030->lock);
+
+	adc->vbat_res = da9030_get_reg(VBAT_RES);
+	adc->vbatmin_res = da9030_get_reg(VBATMIN_RES);
+	adc->vbatmintxon = da9030_get_reg(VBATMINTXON_RES);
+	adc->ichmax_res = da9030_get_reg(ICHMAX_RES);
+	adc->ichmin_res = da9030_get_reg(ICHMIN_RES);
+	adc->ichaverage_res = da9030_get_reg(ICHAVERAGE_RES);
+	adc->vchmax_res = da9030_get_reg(VCHMAX_RES);
+	adc->vchmin_res = da9030_get_reg(VCHMIN_RES);
+	adc->tbat_res = da9030_get_reg(TBAT_RES);
+	adc->adc_in4_res = da9030_get_reg(ADC_IN4_RES);
+	adc->adc_in5_res = da9030_get_reg(ADC_IN5_RES);
+
+	mutex_unlock(&the_da9030->lock);
+}
+EXPORT_SYMBOL(da9030_read_adc);
+
+void da9030_enable_adc(void)
+{
+	/* enable automatic A/D measurements */
+	mutex_lock(&the_da9030->lock);
+
+	da9030_set_reg(ADC_MAN_CONTROL,
+		       ADC_LDO_INT_ENABLE | ADC_TBATREF_ENABLE);
+	da9030_set_reg(ADC_MAN_CONTROL_1,
+		       ADC_LDO_INT_ENABLE | ADC_TBATREF_ENABLE);
+	da9030_set_reg(ADC_AUTO_CONTROL_1,
+		       ADC_TBAT_ENABLE | ADC_VBAT_IN_TXON | ADC_VCH_ENABLE |
+		       ADC_ICH_ENABLE | ADC_VBAT_ENABLE |
+		       ADC_AUTO_SLEEP_ENABLE);
+	da9030_set_reg(ADC_AUTO_CONTROL,
+		       ADC_TBAT_ENABLE | ADC_VBAT_IN_TXON | ADC_VCH_ENABLE |
+		       ADC_ICH_ENABLE | ADC_VBAT_ENABLE |
+		       ADC_AUTO_SLEEP_ENABLE);
+
+	mutex_unlock(&the_da9030->lock);
+}
+EXPORT_SYMBOL(da9030_enable_adc);
+
+void da9030_set_wled(int on, unsigned int brightness)
+{
+	u8 val;
+
+	mutex_lock(&the_da9030->lock);
+
+	if (on)
+		val = WLED_CP_ENABLE | (brightness & 0x7);
+	else
+		val = da9030_get_reg(WLED_CONTROL) & ~WLED_CP_ENABLE;
+
+	da9030_set_reg(WLED_CONTROL, val);
+
+	mutex_unlock(&the_da9030->lock);
+}
+EXPORT_SYMBOL(da9030_set_wled);
+
+int da9030_set_charger(int on, unsigned int mA, unsigned int mV)
+{
+	int ret;
+	u8 val = 0;
+	if (on) {
+		if (mA >= 1500 || mV < 4000 || mV > 4350)
+			return -EINVAL;
+
+		val = CHRG_CHARGER_ENABLE;
+		val |= (mA / 100) << 3;
+		val |= (mV - 4000) / 50;
+	}
+
+	mutex_lock(&the_da9030->lock);
+	ret = da9030_set_reg(CHARGE_CONTROL, val);
+	mutex_unlock(&the_da9030->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(da9030_set_charger);
+
+void da9030_get_charger(int *on, unsigned int *mA, unsigned int *mV)
+{
+	s32 val;
+
+	mutex_lock(&the_da9030->lock);
+
+	val = da9030_get_reg(CHARGE_CONTROL);
+
+	mutex_unlock(&the_da9030->lock);
+
+	if (on)
+		*on = (val & CHRG_CHARGER_ENABLE) ? 1 : 0;
+
+	if (mA)
+		*mA = ((val >> 3) & 0xf) * 100;
+
+	if (mV)
+		*mV = (val & 0x7) * 50 + 4000;
+}
+EXPORT_SYMBOL(da9030_get_charger);
+
+int da9030_set_led(int led, int on,
+		   enum da9030_led_rate rate,
+		   enum da9030_led_duty_cycle duty,
+		   enum da9030_led_pwm_chop pwm_chop)
+{
+	int reg;
+	int ret;
+
+	u8 val = 0;
+
+	if (led > 4)
+		return -EINVAL;
+
+	reg = LED_1_CONTROL + led;
+	if (on) {
+		val = LED_ENABLE;
+		val |= (rate & 0x3) << 5;
+		val |= (duty & 0x3) << 3;
+		val |= (pwm_chop & 0x7);
+	}
+
+	mutex_lock(&the_da9030->lock);
+	ret = da9030_set_reg(reg, val);
+	mutex_unlock(&the_da9030->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(da9030_set_led);
+
+void da9030_set_thresholds(unsigned int tbathighp, unsigned int tbathighn,
+			   unsigned int tbatlow, unsigned int vbatmon)
+{
+	mutex_lock(&the_da9030->lock);
+
+	da9030_set_reg(TBATHIGHP, tbathighp);
+	da9030_set_reg(TBATHIGHN, tbathighn);
+	da9030_set_reg(TBATLOW, tbatlow);
+	da9030_set_reg(VBATMONTXON, vbatmon);
+	da9030_set_reg(VBATMON, vbatmon);
+
+	da9030_set_reg(TBATHIGHP_1, tbathighp);
+	da9030_set_reg(TBATHIGHN_1, tbathighn);
+	da9030_set_reg(TBATLOW_1, tbatlow);
+	da9030_set_reg(VBATMONTXMON_1, vbatmon);
+	da9030_set_reg(VBATMON_1, vbatmon);
+
+	mutex_unlock(&the_da9030->lock);
+}
+EXPORT_SYMBOL(da9030_set_thresholds);
+
+struct da9030_vtg_value {
+	u16 vtg;
+	u16 val;
+};
+
+struct da9030_vtg_value da9030_vtg_1V8_3V2[] = {
+	{1800, 0x0},
+	{1900, 0x1},
+	{2000, 0x2},
+	{2100, 0x3},
+	{2200, 0x4},
+	{2300, 0x5},
+	{2400, 0x6},
+	{2500, 0x7},
+	{2600, 0x8},
+	{2700, 0x9},
+	{2800, 0xa},
+	{2900, 0xb},
+	{3000, 0xc},
+	{3100, 0xd},
+	{3200, 0xe},
+	{3200, 0xf},
+};
+
+struct da9030_vtg_value da9030_vtg_1V1_2V65[] = {
+	{1100, 0x0},
+	{1150, 0x1},
+	{1200, 0x2},
+	{1250, 0x3},
+	{1300, 0x4},
+	{1350, 0x5},
+	{1400, 0x6},
+	{1450, 0x7},
+	{1500, 0x8},
+	{1550, 0x9},
+	{1600, 0xa},
+	{1650, 0xb},
+	{1700, 0xc},
+	{1750, 0xd},
+	{1800, 0xe},
+	{1850, 0xf},
+	{1900, 0x10},
+	{1950, 0x11},
+	{2000, 0x12},
+	{2050, 0x13},
+	{2100, 0x14},
+	{2150, 0x15},
+	{2200, 0x16},
+	{2250, 0x17},
+	{2300, 0x18},
+	{2350, 0x19},
+	{2400, 0x1a},
+	{2450, 0x1b},
+	{2500, 0x1c},
+	{2550, 0x1d},
+	{2600, 0x1e},
+	{2650, 0x1f},
+};
+
+struct da9030_vtg_value da9030_vtg_2V76_2V94[] = {
+	{2760, 0x7},
+	{2790, 0x6},
+	{2820, 0x5},
+	{2850, 0x4},
+	{2850, 0x3},
+	{2880, 0x1},
+	{2910, 0x2},
+	{2940, 0x3},
+};
+
+struct da9030_vtg_value da9030_vtg_0V85_1V625[] = {
+	{850, 0x0},
+	{875, 0x1},
+	{900, 0x2},
+	{925, 0x3},
+	{950, 0x4},
+	{975, 0x5},
+	{1000, 0x6},
+	{1025, 0x7},
+	{1050, 0x8},
+	{1075, 0x9},
+	{1100, 0xa},
+	{1125, 0xb},
+	{1150, 0xc},
+	{1175, 0xd},
+	{1200, 0xe},
+	{1225, 0xf},
+	{1250, 0x10},
+	{1275, 0x11},
+	{1300, 0x12},
+	{1325, 0x13},
+	{1350, 0x14},
+	{1375, 0x15},
+	{1400, 0x16},
+	{1425, 0x17},
+	{1450, 0x18},
+	{1475, 0x19},
+	{1500, 0x1a},
+	{1525, 0x1b},
+	{1550, 0x1c},
+	{1575, 0x1d},
+	{1600, 0x1e},
+	{1625, 0x1f},
+};
+
+struct ldo_param {
+	u8 reg;
+	u8 shift;
+	u8 bits;
+};
+
+struct da9030_ldo {
+	const char *name;
+
+	struct ldo_param vtg;
+	struct ldo_param sleep;
+	struct ldo_param lock;
+
+	/* several LDOs have two enable/disable bits */
+	struct ldo_param enable[2];
+
+	struct da9030_vtg_value *values;
+	int values_count;
+};
+
+static struct da9030_ldo da9030_ldos[] = {
+	[0] = {
+		.name = "LDO1",
+		.vtg = {
+			.reg = LDO_1,
+			.shift = 0,
+			.bits = 5,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 1,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = LDO_1,
+			.shift = 5,
+			.bits = 2,
+		},
+		.lock = {
+			.reg = REG_SLEEP_CONTROL1,
+			.shift = 0,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[1] = {
+		.name = "LDO2",
+		.vtg = {
+			.reg = LDO_2_3,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 2,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL1,
+			.shift = 2,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[2] = {
+		.name = "LDO3",
+		.vtg = {
+			.reg = LDO_2_3,
+			.shift = 4,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 3,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL1,
+			.shift = 4,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[3] = {
+		.name = "LDO4",
+		.vtg = {
+			.reg = LDO_4_5,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 4,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL1,
+			.shift = 6,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[4] = {
+		.name = "LDO5",
+		.vtg = {
+			.reg = LDO_4_5,
+			.shift = 4,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 5,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL2,
+			.shift = 0,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[5] = {
+		.name = "LDO6",
+		.vtg = {
+			.reg = LDO_6_SIMCP,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 6,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[6] = {
+		.name = "LDO7",
+		.vtg = {
+			.reg = LDO_7_8,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 7,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL2,
+			.shift = 2,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[7] = {
+		.name = "LDO8",
+		.vtg = {
+			.reg = LDO_7_8,
+			.shift = 4,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_2_98,
+			.shift = 0,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL2,
+			.shift = 4,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[8] = {
+		.name = "LDO9",
+		.vtg = {
+			.reg = LDO_9_12,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_2_98,
+			.shift = 1,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL2,
+			.shift = 6,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[9] = {
+		.name = "LDO10",
+		.vtg = {
+			.reg = LDO_10_11,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17,
+			.shift = 1,
+			.bits = 1,
+		},
+		.enable[1] = {
+			.reg = REG_CONTROL_2_98,
+			.shift = 2,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[10] = {
+		.name = "LDO11",
+		.vtg = {
+			.reg = LDO_10_11,
+			.shift = 4,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17,
+			.shift = 2,
+			.bits = 1,
+		},
+		.enable[1] = {
+			.reg = REG_CONTROL_2_98,
+			.shift = 3,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[11] = {
+		.name = "LDO12",
+		.vtg = {
+			.reg = LDO_9_12,
+			.shift = 4,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_2_98,
+			.shift = 4,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL3,
+			.shift = 0,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[12] = {
+		.name = "LDO13",
+		.vtg = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17,
+			.shift = 3,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = NULL,
+		.values_count = 0,
+	},
+	[13] = {
+		.name = "LDO14",
+		.vtg = {
+			.reg = LDO_14_16,
+			.shift = 0,
+			.bits = 3,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17, /* FIXME: or 2_98? */
+			.shift = 4,
+			.bits = 1,
+		},
+		.enable[1] = {
+			.reg = REG_CONTROL_2_98, /* FIXME: or 2_98? */
+			.shift = 5,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_2V76_2V94,
+		.values_count = ARRAY_SIZE(da9030_vtg_2V76_2V94),
+	},
+	[14] = {
+		.name = "LDO15",
+		.vtg = {
+			.reg = LDO_15,
+			.shift = 0,
+			.bits = 5,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17,
+			.shift = 5,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.lock = {
+			.reg = LDO_15,
+			.shift = 5,
+			.bits = 3,
+		},
+		.values = da9030_vtg_1V1_2V65,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V1_2V65),
+	},
+	[15] = {
+		.name = "LDO16",
+		.vtg = {
+			.reg = LDO_14_16,
+			.shift = 3,
+			.bits = 5,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17,
+			.shift = 6,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V1_2V65,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V1_2V65),
+	},
+	[16] = {
+		.name = "LDO17",
+		.vtg = {
+			.reg = LDO_17_SIMCP0,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17,
+			.shift = 7,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[17] = {
+		.name = "LDO18",
+		.vtg = {
+			.reg = LDO_18_19,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_2_18,
+			.shift = 2,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[18] = {
+		.name = "LDO19",
+		.vtg = {
+			.reg = LDO_18_19,
+			.shift = 4,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_2_18,
+			.shift = 1,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+};
+
+static int da9030_get_vtg_value(int vtg, const struct da9030_vtg_value *tbl,
+				int n)
+{
+	int i;
+
+	for (i = 0; i < n; i++, tbl++)
+		if (tbl->vtg == vtg)
+			return tbl->val;
+	return -EINVAL;
+}
+
+static int da9030_set_ldo_volt(struct da9030_ldo *ldo, unsigned int mV)
+{
+	int val;
+
+	val = da9030_get_vtg_value(mV, ldo->values, ldo->values_count);
+	if (val < 0)
+		return val;
+
+	return da9030_update_reg_bits(ldo->vtg.reg, ldo->vtg.bits,
+				      ldo->vtg.shift, val);
+}
+
+static int da9030_enable_ldo(struct da9030_ldo *ldo, int enable)
+{
+	int ret;
+
+	ret = da9030_update_reg_bits(ldo->enable[0].reg, ldo->enable[1].bits,
+				     ldo->enable[0].shift, enable);
+
+	if (ret < 0)
+		return ret;
+
+	if (unlikely(ldo->enable[1].reg != 0))
+		ret = da9030_update_reg_bits(ldo->enable[1].reg,
+					     ldo->enable[1].bits,
+					     ldo->enable[1].shift, enable);
+
+	return ret;
+}
+
+static int da9030_set_ldo_sleep(struct da9030_ldo *ldo,
+				enum da9030_ldo_sleep_mode sleep_mode)
+{
+	return da9030_update_reg_bits(ldo->sleep.reg, ldo->sleep.bits,
+				      ldo->sleep.shift, sleep_mode);
+}
+
+int da9030_set_ldo(int ldo_num, int on, unsigned int mV,
+		   enum da9030_ldo_sleep_mode sleep_mode)
+{
+	struct da9030_ldo *ldo;
+	int ret;
+
+	if (ldo_num < 0 || ldo_num > ARRAY_SIZE(da9030_ldos))
+		return -EINVAL;
+
+	ldo = &da9030_ldos[ldo_num];
+
+	ret = da9030_set_ldo_volt(ldo, mV);
+	if (ret < 0)
+		return ret;
+
+	ret = da9030_enable_ldo(ldo, on);
+	if (ret < 0)
+		return ret;
+
+	ret = da9030_set_ldo_sleep(ldo, sleep_mode);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(da9030_set_ldo);
+
+int da9030_set_buck(int buck, int on, unsigned int mV, int flags)
+{
+	/* FIXME: implement */
+	return 0;
+}
+EXPORT_SYMBOL(da9030_set_buck);
+
+static void da9030_disable_irq(unsigned int irq)
+{
+	int reg, val;
+
+	if (irq < 8) {
+		reg = IRQ_MASK_A;
+		val = 1 << irq;
+	} else if (irq < 16) {
+		reg = IRQ_MASK_B;
+		val = 1 << (irq - 8);
+	} else {
+		reg = IRQ_MASK_C;
+		val = 1 << (irq - 16);
+	}
+
+	i2c_smbus_write_byte_data(
+		the_da9030->client, reg,
+		i2c_smbus_read_byte_data(the_da9030->client, reg) | val);
+}
+
+static void da9030_enable_irq(unsigned int irq)
+{
+	int reg, val;
+
+	if (irq < 8) {
+		reg = IRQ_MASK_A;
+		val = 1 << irq;
+	} else if (irq < 16) {
+		reg = IRQ_MASK_B;
+		val = 1 << (irq - 8);
+	} else {
+		reg = IRQ_MASK_C;
+		val = 1 << (irq - 16);
+	}
+
+	i2c_smbus_write_byte_data(
+		the_da9030->client, reg,
+		i2c_smbus_read_byte_data(the_da9030->client, reg) & ~val);
+}
+
+int da9030_register_callback(int event,
+			     void (*callback)(int event, void *data),
+			     void *data)
+{
+	int ret;
+
+	if (event < 0 || event > 23)
+		return -EINVAL;
+
+	mutex_lock(&the_da9030->lock);
+	if (the_da9030->callbacks[event])
+		ret = -EBUSY;
+	else {
+		the_da9030->callbacks[event] = callback;
+		the_da9030->callbacks_data[event] = data;
+		da9030_enable_irq(event);
+		ret = 0;
+	}
+	mutex_unlock(&the_da9030->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(da9030_register_callback);
+
+void da9030_unregister_callback(int event)
+{
+	mutex_lock(&the_da9030->lock);
+
+	da9030_disable_irq(event);
+	the_da9030->callbacks[event] = NULL;
+	the_da9030->callbacks_data[event] = 0;
+
+	mutex_unlock(&the_da9030->lock);
+}
+EXPORT_SYMBOL(da9030_unregister_callback);
+
+#ifdef CONFIG_DEBUG_FS
+#define MAX_BUF 256
+
+static int da9030_debug_show(struct seq_file *s, void *data)
+{
+	int i, res = 0;
+	struct da9030 *da9030 = s->private;
+	struct i2c_client *client = da9030->client;
+
+	seq_printf(s, "DA9030 state: da = %p, cl = %p, s->private = %p\n",
+		   da9030, client, s->private);
+
+	for (i = 0; i < ARRAY_SIZE(defined_regs); i++) {
+		res = i2c_smbus_read_byte_data(client, defined_regs[i]);
+		seq_printf(s, "%02x %x\n", defined_regs[i], res);
+	}
+	return 0;
+}
+
+static int da9030_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, da9030_debug_show, inode->i_private);
+}
+
+ssize_t da9030_debug_write(struct file *f, const char __user *buf, size_t len,
+			   loff_t *off)
+{
+	char buffer[MAX_BUF];
+	int reg, val;
+	char *endp;
+	struct da9030 *da9030 = ((struct seq_file *)f->private_data)->private;
+	struct i2c_client *client = da9030->client;
+
+	if (len > MAX_BUF) {
+		printk(KERN_INFO "%s: large buffer\n", __FUNCTION__);
+		len = MAX_BUF;
+	}
+
+	if (copy_from_user(buffer, buf, len)) {
+		printk(KERN_INFO "%s: copy_from_user failed\n", __FUNCTION__);
+		return -EFAULT;
+	}
+	buffer[len] = '\0';
+
+	reg = simple_strtoul(buffer, &endp, 0);
+	while (endp && isspace(*endp))
+		endp++;
+
+	val = simple_strtoul(endp, 0, 0);
+
+	i2c_smbus_write_byte_data(client, reg, val);
+
+	return len;
+}
+
+static const struct file_operations debug_fops = {
+	.open		= da9030_debug_open,
+	.read		= seq_read,
+	.write		= da9030_debug_write,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static struct dentry *da9030_create_debugfs(struct da9030 *da9030)
+{
+	da9030->debug_file = debugfs_create_file("da9030", 0666, 0, da9030,
+						 &debug_fops);
+	return da9030->debug_file;
+}
+
+static void da9030_remove_debugfs(struct da9030 *da9030)
+{
+	debugfs_remove(da9030->debug_file);
+}
+#else
+#define da9030_create_debugfs(x)	NULL
+#define da9030_remove_debugfs(x)	do {} while (0)
+#endif
+
+static irqreturn_t da9030_irq(int irq, void *_da9030)
+{
+	struct da9030 *da9030 = _da9030;
+
+	(void)schedule_work(&da9030->event_work);
+
+	return IRQ_HANDLED;
+}
+
+static void da9030_irq_worker(struct work_struct *work)
+{
+	struct da9030 *da9030 = container_of(work, struct da9030, event_work);
+	void (*callback)(int event, void *data);
+	u32 pending = 0;
+	u32 mask = 0;
+	int i;
+
+	while (1) {
+		pending = (i2c_smbus_read_byte_data(da9030->client,
+						    EVENT_A)) |
+			((i2c_smbus_read_byte_data(da9030->client,
+						   EVENT_B)) << 8) |
+			((i2c_smbus_read_byte_data(da9030->client,
+						   EVENT_C)) << 16);
+
+		mask = (i2c_smbus_read_byte_data(da9030->client,
+						 IRQ_MASK_A)) |
+			((i2c_smbus_read_byte_data(da9030->client,
+						   IRQ_MASK_B)) << 8) |
+			((i2c_smbus_read_byte_data(da9030->client,
+						   IRQ_MASK_C)) << 16);
+		pending &= ~mask;
+
+		if (!pending)
+			return;
+
+		while (pending) {
+			i = __ffs(pending);
+			callback = da9030->callbacks[i];
+			if (callback)
+				callback(i, da9030->callbacks_data[i]);
+			pending &= ~(1 << i);
+		}
+	}
+}
+
+static inline void da9030_disable_interrupts(struct da9030 *da9030)
+{
+	/* clear pending interruts */
+	(void)i2c_smbus_read_byte_data(da9030->client, EVENT_A);
+	(void)i2c_smbus_read_byte_data(da9030->client, EVENT_B);
+	(void)i2c_smbus_read_byte_data(da9030->client, EVENT_C);
+
+	/* disable interrupts */
+	i2c_smbus_write_byte_data(da9030->client, IRQ_MASK_A, 0xff);
+	i2c_smbus_write_byte_data(da9030->client, IRQ_MASK_B, 0xff);
+	i2c_smbus_write_byte_data(da9030->client, IRQ_MASK_C, 0xff);
+}
+
+static int da9030_probe(struct i2c_client *client)
+{
+	int ret;
+	struct da9030 *da9030;
+
+	if (the_da9030) {
+		dev_dbg(&client->dev, "only one %s for now\n",
+			DRIVER_NAME);
+		return -ENODEV;
+	}
+
+	ret = i2c_smbus_read_byte_data(client, CHIP_ID);
+
+	dev_info(&client->dev, "initialized chip revision %x\n", ret);
+
+	da9030 = kzalloc(sizeof(struct da9030), GFP_KERNEL);
+	if (da9030 == NULL) {
+		dev_err(&client->dev, "insufficient memory\n");
+		return -ENOMEM;
+	}
+	the_da9030 = da9030;
+	da9030->client = client;
+
+	mutex_init(&the_da9030->lock);
+
+	da9030_disable_interrupts(da9030);
+	INIT_WORK(&da9030->event_work, da9030_irq_worker);
+
+	ret = request_irq(client->irq, da9030_irq,
+			  IRQF_DISABLED, DRIVER_NAME, da9030);
+
+	if (ret) {
+		kfree(da9030);
+		dev_err(&client->dev,
+			"failed to allocate irq %d\n",
+			client->irq);
+		return ret;
+	}
+
+	i2c_set_clientdata(client, da9030);
+
+	da9030->debug_file = da9030_create_debugfs(da9030);
+
+	return 0;
+}
+
+/* static int da9030_detach_adapter(struct i2c_adapter *a) */
+static int da9030_remove(struct i2c_client *client)
+{
+	struct da9030 *da9030 = i2c_get_clientdata(client);
+
+	da9030_remove_debugfs(da9030);
+
+	free_irq(da9030->client->irq, da9030);
+	kfree(da9030);
+	i2c_set_clientdata(client, NULL);
+	the_da9030 = NULL;
+
+	return 0;
+}
+
+static struct i2c_driver da9030_driver = {
+	.driver = {
+		.name	= "da9030",
+		.owner	= THIS_MODULE,
+	},
+
+	.probe	= da9030_probe,
+	.remove	= da9030_remove,
+};
+
+static int da9030_init(void)
+{
+	i2c_add_driver(&da9030_driver);
+	return 0;
+}
+
+static void da9030_exit(void)
+{
+	i2c_del_driver(&da9030_driver);
+}
+
+/* NOTE:  this MUST be initialized before the other parts of the system
+ * that rely on it ... but after the i2c bus on which this relies.
+ * That is, much earlier than on PC-type systems, which don't often use
+ * I2C as a core system bus.
+ */
+subsys_initcall(da9030_init);
+module_exit(da9030_exit);
+
+MODULE_DESCRIPTION("DA9030 power manager driver");
+MODULE_AUTHOR("Mike Rapoport, Compulab");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/chips/da9030.h b/drivers/i2c/chips/da9030.h
new file mode 100644
index 0000000..4163156
--- /dev/null
+++ b/drivers/i2c/chips/da9030.h
@@ -0,0 +1,282 @@
+/* DA9030 Register definintions */
+
+#define CHIP_ID	0x00
+
+#define EVENT_A			0x01
+#define EVENT_A_CHIOVER		(1 << 7)
+#define EVENT_A_VBAT_MON_TXON	(1 << 6)
+#define EVENT_A_VBAT_MON	(1 << 5)
+#define EVENT_A_TBAT		(1 << 4)
+#define EVENT_A_CHDET		(1 << 3)
+#define EVENT_A_EXTON		(1 << 2)
+#define EVENT_A_PWREN1		(1 << 1)
+#define EVENT_A_ONKEY_N		(1 << 0)
+
+#define EVENT_B			0x02
+#define EVENT_B_WDOG_INT	(1 << 7)
+#define EVENT_B_SRP_DETECT	(1 << 6)
+#define EVENT_B_SESSION_VALID	(1 << 5)
+#define EVENT_B_VBUS_VALID_4_0	(1 << 4)
+#define EVENT_B_VBUS_VALID_4_4	(1 << 3)
+#define EVENT_B_ADC_READY	(1 << 2)
+#define EVENT_B_CCTO		(1 << 1)
+#define EVENT_B_TCTO		(1 << 0)
+
+#define EVENT_C		0x03
+#define EVENT_C_ADC_IN5	(1 << 7)
+#define EVENT_C_ADC_IN4	(1 << 6)
+#define EVENT_C_BUCK2	(1 << 5)
+#define EVENT_C_LDO19	(1 << 4)
+#define EVENT_C_LDO18	(1 << 3)
+#define EVENT_C_LDO17	(1 << 2)
+#define EVENT_C_LDO16	(1 << 1)
+#define EVENT_C_LDO15	(1 << 0)
+
+#define STATUS			0x04
+#define STATUS_MCLK_DETECT	(1 << 7)
+#define STATUS_VBAT_MON_TXON	(1 << 6)
+#define STATUS_VBAT_MON		(1 << 5)
+#define STATUS_TBAT		(1 << 4)
+#define STATUS_CHDET		(1 << 3)
+#define STATUS_EXTON		(1 << 2)
+#define STATUS_PWREN1		(1 << 1)
+#define STATUS_ONKEY_N		(1 << 0)
+
+#define IRQ_MASK_A			0x05
+#define IRQ_MASK_A_CHIOVER		(1 << 7)
+#define IRQ_MASK_A_VBAT_MON_TXON	(1 << 6)
+#define IRQ_MASK_A_VBAT_MON		(1 << 5)
+#define IRQ_MASK_A_TBAT			(1 << 4)
+#define IRQ_MASK_A_CHDET		(1 << 3)
+#define IRQ_MASK_A_EXTON		(1 << 2)
+#define IRQ_MASK_A_PWREN1		(1 << 1)
+#define IRQ_MASK_A_ONKEY_N		(1 << 0)
+
+#define IRQ_MASK_B			0x06
+#define IRQ_MASK_B_WDOG_INT		(1 << 7)
+#define IRQ_MASK_B_SRP_DETECT		(1 << 6)
+#define IRQ_MASK_B_SESSION_VALID	(1 << 5)
+#define IRQ_MASK_B_VBUS_VALID_4_0	(1 << 4)
+#define IRQ_MASK_B_VBUS_VALID_4_4	(1 << 3)
+#define IRQ_MASK_B_ADC_READY		(1 << 2)
+#define IRQ_MASK_B_CCTO			(1 << 1)
+#define IRQ_MASK_B_TCTO			(1 << 0)
+
+#define IRQ_MASK_C		0x07
+#define IRQ_MASK_C_ADC_IN5	(1 << 7)
+#define IRQ_MASK_C_ADC_IN4	(1 << 6)
+#define IRQ_MASK_C_BUCK2	(1 << 5)
+#define IRQ_MASK_C_LDO19	(1 << 4)
+#define IRQ_MASK_C_LDO18	(1 << 3)
+#define IRQ_MASK_C_LDO17	(1 << 2)
+#define IRQ_MASK_C_LDO16	(1 << 1)
+#define IRQ_MASK_C_LDO15	(1 << 0)
+
+#define SYS_CTRL_A				0x08
+#define SYS_CONTROL_A_SLEEP_N_PIN_ENABLE	0x1
+#define SYS_CONTROL_A_SHUT_DOWN			(1<<1)
+#define SYS_CONTROL_A_HWRES_ENABLE		(1<<2)
+#define SYS_CONTROL_A_WDOG_ACTION		(1<<3)
+#define SYS_CONTROL_A_WATCHDOG			(1<<7)
+
+#define SYS_CONTROL_B			0x09
+#define SYS_CLTR_B_SLEEP_13MHZ_EN	(1 << 4)
+#define SYS_CLTR_B_AUTO_CLK_SWITCH	(1 << 3)
+
+#define FAULT_LOG		0x0a
+#define FAULT_LOG_OVER_TEMP	(1 << 7)
+#define FAULT_LOG_VBAT_OVER	(1 << 4)
+
+#define LDO_10_11		0x10
+#define LDO_10_11_LDO10_3V	(0xc)
+#define LDO_10_11_LDO11_3V2	(0xe << 4)
+
+#define LDO_15			0x11
+#define LDO_14_16		0x12
+#define LDO_18_19		0x13
+#define LDO_18_19_LDO18_3V2	(0xe)
+
+#define LDO_17_SIMCP0		0x14
+#define LDO_17_SIMCP0_LDO17_3V	0x0F
+
+#define BUCK_2_DVC1	0x15
+#define BUCK_2_GO	(1 << 7)
+#define BUCK_2_SLEEP	(1 << 6)
+#define BUCK_2_TRIM_1V5	(0x1a)
+
+#define BUCK_2_DVC2	0x16
+
+#define REG_CONTROL_1_17	0x17
+#define RC1_LDO17_EN		(1 << 7)
+#define RC1_LDO16_EN		(1 << 6)
+#define RC1_LDO15_EN		(1 << 5)
+#define RC1_LDO14_EN		(1 << 4)
+#define RC1_LDO13_EN		(1 << 3)
+#define RC1_LDO11_EN		(1 << 2)
+#define RC1_LDO10_EN		(1 << 1)
+#define RC1_BUCK2_EN		(1 << 0)
+
+#define REG_CONTROL_2_18	0x18
+#define RC2_SIMCP_EN		(1 << 6)
+#define RC2_LDO19_EN		(1 << 1)
+#define RC2_LDO18_EN		(1 << 0)
+
+#define REG_CONTROL_3_
+
+#define USBPUMP			0x19
+#define USB_PUMP_EN_USBVEP	(1 << 7)
+#define USB_PUMP_EN_USBVE	(1 << 6)
+#define USB_PUMP_SPR_DETECT	(1 << 5)
+#define USB_PUMP_SESSION_VALID	(1 << 4)
+#define USB_PUMP_VBUS_VALID_4_0 (1 << 3)
+#define USB_PUMP_VBUS_VALID_4_4 (1 << 2)
+#define USB_PUMP_USBVEP		(1 << 1)
+#define USB_PUMP_USBVE		(1 << 0)
+
+#define SLEEP_CONTROL			0x1a
+#define APP_SLEEP_CTL_PWR_EN		(1 << 7)
+#define APP_SLEEP_CTL_SYS_EN		(1 << 6)
+#define APP_SLEEP_CTL_BYPASS_LDO19	(1 << 2)
+#define APP_SLEEP_CTL_BYPASS_LDO18	(1 << 1)
+#define APP_SLEEP_CTL_BYPASS_LDO17	(1 << 0)
+
+#define STARTUP_CONTROL			0x1b
+#define STARTUP_CTL_LDO11_PWR_SYS	(1 << 3)
+#define STARTUP_CTL_LDO10_PWR_SYS	(1 << 2)
+#define STARTUP_CTL_LDO11_START		(1 << 1)
+#define STARTUP_CTL_LDO10_START		(1 << 0)
+
+#define LED_1_CONTROL	0x20
+#define LED_2_CONTROL	0x21
+#define LED_3_CONTROL	0x22
+#define LED_4_CONTROL	0x23
+#define LEDPC_CONTROL	0x24
+#define LED_ENABLE	(1 << 7)
+
+#define WLED_CONTROL	0x25
+#define WLED_CP_ENABLE	(1 << 6)
+#define WLED_ISET_10	(3)
+
+#define MISC_CONTROLA		0x26
+#define MISC_CONTROLB			0x27
+#define MISCB_SESSION_VALID_ENABLE	(1 << 3)
+#define MISCB_USB_INT_RISING		(1 << 2)
+#define MISCB_I2C_ADDRESS		(1 << 1)
+#define MISCB_STARTUP_SEQUENCE		(1 << 0)
+
+#define CHARGE_CONTROL		0x28
+#define CHRG_CHARGER_ENABLE	(1 << 7)
+
+#define CCTR_CONTROL		0x29
+#define CCTR_SET_8MIN		0x01
+
+#define TCTR_CONTROL		0x2a
+#define CHARGE_PULSE		0x2b
+
+#define ADC_MAN_CONTROL		0x30
+
+#define ADC_AUTO_CONTROL	0x31
+#define ADC_IN5_EN		(1 << 7)
+#define ADC_IN4_EN		(1 << 6)
+#define ADC_TBAT_ENABLE		(1 << 5)
+#define ADC_VBAT_IN_TXON	(1 << 4)
+#define ADC_VCH_ENABLE		(1 << 3)
+#define ADC_ICH_ENABLE		(1 << 2)
+#define ADC_VBAT_ENABLE		(1 << 1)
+#define ADC_AUTO_SLEEP_ENABLE	(1 << 0)
+
+#define VBATMON		0x32
+#define VBATMONTXON	0x33
+#define TBATHIGHP	0x34
+#define TBATHIGHN	0x35
+#define TBATLOW		0x36
+#define ADC_IN4_MIN	0x37
+#define ADC_IN4_MAX	0x38
+#define ADC_IN5_MIN	0x39
+#define ADC_IN5_MAX	0x3a
+
+#define VBAT_RES	0x41
+#define VBATMIN_RES	0x42
+#define VBATMINTXON	0x43
+#define ICHMAX_RES	0x44
+#define ICHMIN_RES	0x45
+#define ICHAVERAGE_RES	0x46
+#define VCHMAX_RES	0x47
+#define VCHMIN_RES	0x48
+#define TBAT_RES	0x49
+#define ADC_IN4_RES	0x4a
+#define ADC_IN5_RES	0x4b
+
+#define LDO_1			0x90
+#define LDO_1_UNLOCK		(0x5 << 5)
+#define LDO_1_TRIM_3V		(0x12)
+
+#define LDO_2_3			0x91
+#define LDO_2_3_LDO2_3V2	(0xe << 4)
+#define LDO_2_3_LDO3_3V		(0xc)
+
+#define LDO_4_5			0x92
+#define LDO_6_SIMCP		0x93
+#define LDO_6_SIMCP_LDO6_3V2	(0xe)
+
+#define LDO_7_8			0x94
+#define LDO_9_12		0x95
+#define BUCK			0x96
+#define REG_CONTROL_1_97	0x97
+#define RC3_LDO7_EN    (1 << 7)
+#define RC3_LDO6_EN    (1 << 6)
+#define RC3_LDO5_EN    (1 << 5)
+#define RC3_LDO4_EN    (1 << 4)
+#define RC3_LDO3_EN    (1 << 3)
+#define RC3_LDO2_EN    (1 << 2)
+#define RC3_LDO1_EN    (1 << 1)
+#define RC3_BUCK_EN    (1 << 0)
+
+#define REG_CONTROL_2_98	0x98
+#define RC4_SLEEP              (1 << 7)
+#define RC4_SIMCP_ENABLE       (1 << 6)
+#define RC4_LDO14_EN   (1 << 5)
+#define RC4_LDO12_EN   (1 << 4)
+#define RC4_LDO11_EN   (1 << 3)
+#define RC4_LDO10_EN   (1 << 2)
+#define RC4_LDO9_EN    (1 << 1)
+#define RC4_LDO8_EN    (1 << 0)
+
+#define REG_SLEEP_CONTROL1	0x99
+#define REG_SLEEP_CONTROL2	0x9a
+#define REG_SLEEP_CONTROL3	0x9b
+
+#define ADC_MAN_CONTROL_1		0xa0
+#define ADC_DEBOUNCE_VBATMON_TXON	(1 << 7)
+#define ADC_DEBOUNCE_VBATMON		(1 << 6)
+#define ADC_TBATREF_ENABLE		(1 << 5)
+#define ADC_LDO_INT_ENABLE		(1 << 4)
+#define ADC_MAN_CONV			(1 << 3)
+#define ADC_MUX_VBAT			(0)
+
+#define ADC_AUTO_CONTROL_1	0xa1
+#define ADC_IN5_EN		(1 << 7)
+#define ADC_IN4_EN		(1 << 6)
+#define ADC_TBAT_ENABLE		(1 << 5)
+#define ADC_VBAT_IN_TXON	(1 << 4)
+#define ADC_VCH_ENABLE		(1 << 3)
+#define ADC_ICH_ENABLE		(1 << 2)
+#define ADC_VBAT_ENABLE		(1 << 1)
+#define ADC_AUTO_SLEEP_ENABLE	(1 << 0)
+
+#define VBATMON_1		0xa2
+#define VBATMONTXMON_1		0xa3
+#define TBATHIGHP_1		0xa4
+#define TBATHIGHN_1		0xa5
+#define TBATLOW_1		0xa6
+#define MAN_RES			0xb0
+#define VBAT_RES_1		0xb1
+#define VBATMIN_RES_1		0xb2
+#define VBATMINTXON_RES		0xb3
+#define ICHMAX_RES_1		0xb4
+#define ICHMIN_RES_1		0xb5
+#define ICHAVERAGE_RES_1	0xb6
+#define VCHMAX_RES_1		0xb7
+#define VCHMIN_RES_1		0xb8
+#define TBAT_RES_1		0xb9
+#define ADC_IN4_RES_1		0xba
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index f929fcd..174ea2e 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -126,6 +126,49 @@ config TOUCHSCREEN_HP600
 	  To compile this driver as a module, choose M here: the
 	  module will be called hp680_ts_input.
 
+config TOUCHSCREEN_WM97XX
+	tristate "Support for WM97xx AC97 touchscreen controllers"
+	depends AC97_BUS
+
+choice
+	prompt "WM97xx codec type"
+	depends TOUCHSCREEN_WM97XX
+
+config TOUCHSCREEN_WM9705
+	bool "WM9705 Touchscreen interface support"
+	depends on TOUCHSCREEN_WM97XX
+	help
+	  Say Y here if you have the wm9705 touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wm9705.
+
+config TOUCHSCREEN_WM9712
+	bool "WM9712 Touchscreen interface support"
+	depends on TOUCHSCREEN_WM97XX
+	help
+	  Say Y here if you have the wm9712 touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wm9712.
+
+config TOUCHSCREEN_WM9713
+	bool "WM9713 Touchscreen interface support"
+	depends on TOUCHSCREEN_WM97XX
+	help
+	  Say Y here if you have the wm9713 touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wm9713.
+
+endchoice
+
 config TOUCHSCREEN_PENMOUNT
 	tristate "Penmount serial touchscreen"
 	select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 5de8933..3a059b1 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -3,6 +3,7 @@
 #
 
 # Each configuration option enables a list of files.
+wm97xx-ts-objs := wm97xx-core.o
 
 obj-$(CONFIG_TOUCHSCREEN_ADS7846)	+= ads7846.o
 obj-$(CONFIG_TOUCHSCREEN_BITSY)		+= h3600_ts_input.o
@@ -18,3 +19,16 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)	+= touchwin.o
 obj-$(CONFIG_TOUCHSCREEN_UCB1400)	+= ucb1400_ts.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX)    += wm97xx-ts.o
+
+ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y)
+wm97xx-ts-objs += wm9713.o
+endif
+
+ifeq ($(CONFIG_TOUCHSCREEN_WM9712),y)
+wm97xx-ts-objs += wm9712.o
+endif
+
+ifeq ($(CONFIG_TOUCHSCREEN_WM9705),y)
+wm97xx-ts-objs += wm9705.o
+endif
diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c
new file mode 100644
index 0000000..1dae63d
--- /dev/null
+++ b/drivers/input/touchscreen/wm9705.c
@@ -0,0 +1,360 @@
+/*
+ * wm9705.c  --  Codec driver for Wolfson WM9705 AC97 Codec.
+ *
+ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *                   Russell King <rmk@arm.linux.org.uk>
+ *
+ *  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.
+ *
+ *  Revision history
+ *     6th Sep 2006  Mike Arthur <linux@wolfsonmicro.com>
+ *                   Added pre and post sample calls.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME			"wm97xx"
+#define WM9705_VERSION		"0.62"
+#define DEFAULT_PRESSURE	0xb0c0
+
+/*
+ * Debug
+ */
+#if 0
+#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ *     pil = 1 to use 200uA and
+ *     pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil = 0;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 4;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Pen detect comparator threshold.
+ *
+ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold
+ * i.e. 1 =  Vmid/15 threshold
+ *      15 =  Vmid/1 threshold
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down events.
+ */
+static int pdd = 8;
+module_param(pdd, int, 0);
+MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask = 0;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+	21,    // 1 AC97 Link frames
+	42,    // 2
+	84,    // 4
+	167,   // 8
+	333,   // 16
+	667,   // 32
+	1000,  // 48
+	1333,  // 64
+	2000,  // 96
+	2667,  // 128
+	3333,  // 160
+	4000,  // 192
+	4667,  // 224
+	5333,  // 256
+	6000,  // 288
+	0      // No delay, switch matrix always on
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+	udelay (3 * AC97_LINK_FRAME + delay_table [d]);
+}
+
+/*
+ * set up the physical settings of the WM9705
+ */
+static void init_wm9705_phy(struct wm97xx* wm)
+{
+	u16 dig1 = 0, dig2 = WM97XX_RPR;
+
+	/*
+	* mute VIDEO and AUX as they share X and Y touchscreen
+	* inputs on the WM9705
+	*/
+	wm97xx_reg_write(wm, AC97_AUX, 0x8000);
+	wm97xx_reg_write(wm, AC97_VIDEO, 0x8000);
+
+	/* touchpanel pressure current*/
+	if  (pil == 2) {
+		dig2 |= WM9705_PIL;
+		dbg("setting pressure measurement current to 400uA.");
+	} else if (pil)
+		dbg("setting pressure measurement current to 200uA.");
+	if(!pil)
+		pressure = 0;
+
+	/* polling mode sample settling delay */
+	if (delay!=4) {
+		if (delay < 0 || delay > 15) {
+		    dbg("supplied delay out of range.");
+		    delay = 4;
+		}
+	}
+	dig1 &= 0xff0f;
+	dig1 |= WM97XX_DELAY(delay);
+	dbg("setting adc sample delay to %d u Secs.", delay_table[delay]);
+
+	/* WM9705 pdd */
+	dig2 |= (pdd & 0x000f);
+	dbg("setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
+
+	/* mask */
+	dig2 |= ((mask & 0x3) << 4);
+
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+}
+
+static int wm9705_digitiser_ioctl(struct wm97xx* wm, int cmd)
+{
+	switch(cmd) {
+	case WM97XX_DIG_START:
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] | WM97XX_PRP_DET_DIG);
+		wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+		break;
+	case WM97XX_DIG_STOP:
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] & ~WM97XX_PRP_DET_DIG);
+		break;
+	case WM97XX_AUX_PREPARE:
+		memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
+		break;
+	case WM97XX_DIG_RESTORE:
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
+		break;
+	case WM97XX_PHY_INIT:
+		init_wm9705_phy(wm);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static inline int is_pden (struct wm97xx* wm)
+{
+	return wm->dig[2] & WM9705_PDEN;
+}
+
+/*
+ * Read a sample from the WM9705 adc in polling mode.
+ */
+static int wm9705_poll_sample (struct wm97xx* wm, int adcsel, int *sample)
+{
+	int timeout = 5 * delay;
+
+	if (!wm->pen_probably_down) {
+		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+		if (!(data & WM97XX_PEN_DOWN))
+			return RC_PENUP;
+		wm->pen_probably_down = 1;
+	}
+
+	/* set up digitiser */
+	if (adcsel & 0x8000)
+		adcsel = ((adcsel & 0x7fff) + 3) << 12;
+
+	if (wm->mach_ops && wm->mach_ops->pre_sample)
+		wm->mach_ops->pre_sample(adcsel);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
+
+	/* wait 3 AC97 time slots + delay for conversion */
+	poll_delay (delay);
+
+	/* wait for POLL to go low */
+	while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
+		udelay(AC97_LINK_FRAME);
+		timeout--;
+	}
+
+	if (timeout <= 0) {
+		/* If PDEN is set, we can get a timeout when pen goes up */
+		if (is_pden(wm))
+			wm->pen_probably_down = 0;
+		else
+			dbg ("adc sample timeout");
+		return RC_PENUP;
+	}
+
+	*sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	if (wm->mach_ops && wm->mach_ops->post_sample)
+		wm->mach_ops->post_sample(adcsel);
+
+	/* check we have correct sample */
+	if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
+		dbg ("adc wrong sample, read %x got %x", adcsel,
+		*sample & WM97XX_ADCSEL_MASK);
+		return RC_PENUP;
+	}
+
+	if (!(*sample & WM97XX_PEN_DOWN)) {
+		wm->pen_probably_down = 0;
+		return RC_PENUP;
+	}
+
+	return RC_VALID;
+}
+
+/*
+ * Sample the WM9705 touchscreen in polling mode
+ */
+static int wm9705_poll_touch(struct wm97xx* wm, struct wm97xx_data *data)
+{
+	int rc;
+
+	if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID)
+		return rc;
+	if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID)
+		return rc;
+	if (pil) {
+		if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID)
+			return rc;
+	} else
+		data->p = DEFAULT_PRESSURE;
+
+	return RC_VALID;
+}
+
+/*
+ * Enable WM9705 continuous mode, i.e. touch data is streamed across an AC97 slot
+ */
+static int wm9705_acc_enable (struct wm97xx* wm, int enable)
+{
+	u16 dig1, dig2;
+	int ret = 0;
+
+	dig1 = wm->dig[1];
+	dig2 = wm->dig[2];
+
+	if (enable) {
+		/* continous mode */
+		if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0)
+			return ret;
+		dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
+			WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
+		dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
+			WM97XX_DELAY (delay) |
+			WM97XX_SLT (wm->acc_slot) |
+			WM97XX_RATE (wm->acc_rate);
+		if (pil)
+			dig1 |= WM97XX_ADCSEL_PRES;
+		dig2 |= WM9705_PDEN;
+	} else {
+		dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
+		dig2 &= ~WM9705_PDEN;
+		if (wm->mach_ops->acc_shutdown)
+			wm->mach_ops->acc_shutdown(wm);
+	}
+
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+	return ret;
+}
+
+struct wm97xx_codec_drv wm97xx_codec = {
+	.id = 	WM9705_ID2,
+	.name = "wm9705",
+	.poll_sample = wm9705_poll_sample,
+	.poll_touch = wm9705_poll_touch,
+	.acc_enable = wm9705_acc_enable,
+	.digitiser_ioctl = wm9705_digitiser_ioctl,
+};
+
+EXPORT_SYMBOL_GPL(wm97xx_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c
new file mode 100644
index 0000000..99433e9
--- /dev/null
+++ b/drivers/input/touchscreen/wm9712.c
@@ -0,0 +1,464 @@
+/*
+ * wm9712.c  --  Codec driver for Wolfson WM9712 AC97 Codecs.
+ *
+ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *                   Russell King <rmk@arm.linux.org.uk>
+ *
+ *  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.
+ *
+ *  Revision history
+ *     4th Jul 2005  Initial version.
+ *     6th Sep 2006  Mike Arthur <linux@wolfsonmicro.com>
+ *                   Added pre and post sample calls.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME			"wm97xx"
+#define WM9712_VERSION		"0.61"
+#define DEFAULT_PRESSURE	0xb0c0
+
+/*
+ * Debug
+ */
+#if 0
+#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set internal pull up for pen detect.
+ *
+ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
+ * i.e. pull up resistance = 64k Ohms / rpu.
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down event.
+ */
+static int rpu = 8;
+module_param(rpu, int, 0);
+MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ *     pil = 1 to use 200uA and
+ *     pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil = 0;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 3;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Set five_wire = 1 to use a 5 wire touchscreen.
+ *
+ * NOTE: Five wire mode does not allow for readback of pressure.
+ */
+static int five_wire;
+module_param(five_wire, int, 0);
+MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen.");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask = 0;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * Coordinate Polling Enable.
+ *
+ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
+ * for every poll.
+ */
+static int coord = 0;
+module_param(coord, int, 0);
+MODULE_PARM_DESC(coord, "Polling coordinate mode");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+	21,    // 1 AC97 Link frames
+	42,    // 2
+	84,    // 4
+	167,   // 8
+	333,   // 16
+	667,   // 32
+	1000,  // 48
+	1333,  // 64
+	2000,  // 96
+	2667,  // 128
+	3333,  // 160
+	4000,  // 192
+	4667,  // 224
+	5333,  // 256
+	6000,  // 288
+	0      // No delay, switch matrix always on
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+	udelay (3 * AC97_LINK_FRAME + delay_table [d]);
+}
+
+/*
+ * set up the physical settings of the WM9712
+ */
+static void init_wm9712_phy(struct wm97xx* wm)
+{
+	u16 dig1 = 0;
+	u16 dig2 = WM97XX_RPR | WM9712_RPU(1);
+
+	/* WM9712 rpu */
+	if (rpu) {
+		dig2 &= 0xffc0;
+		dig2 |= WM9712_RPU(rpu);
+		dbg("setting pen detect pull-up to %d Ohms",64000 / rpu);
+	}
+
+	/* touchpanel pressure current*/
+	if (pil == 2) {
+		dig2 |= WM9712_PIL;
+		dbg("setting pressure measurement current to 400uA.");
+	} else if (pil)
+		dbg("setting pressure measurement current to 200uA.");
+	if(!pil)
+		pressure = 0;
+
+	/* WM9712 five wire */
+	if (five_wire) {
+		dig2 |= WM9712_45W;
+		dbg("setting 5-wire touchscreen mode.");
+	}
+
+	/* polling mode sample settling delay */
+	if (delay < 0 || delay > 15) {
+		dbg("supplied delay out of range.");
+		delay = 4;
+	}
+	dig1 &= 0xff0f;
+	dig1 |= WM97XX_DELAY(delay);
+	dbg("setting adc sample delay to %d u Secs.", delay_table[delay]);
+
+	/* mask */
+	dig2 |= ((mask & 0x3) << 6);
+	if (mask) {
+		u16 reg;
+		/* Set GPIO4 as Mask Pin*/
+		reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+		wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4);
+		reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+		wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4);
+	}
+
+	/* wait - coord mode */
+	if(coord)
+		dig2 |= WM9712_WAIT;
+
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+}
+
+static int wm9712_digitiser_ioctl(struct wm97xx* wm, int cmd)
+{
+	u16 dig2 = wm->dig[2];
+
+	switch(cmd) {
+	case WM97XX_DIG_START:
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 | WM97XX_PRP_DET_DIG);
+		wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+		break;
+	case WM97XX_DIG_STOP:
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 & ~WM97XX_PRP_DET_DIG);
+		break;
+	case WM97XX_AUX_PREPARE:
+		memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
+		break;
+	case WM97XX_DIG_RESTORE:
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
+		break;
+	case WM97XX_PHY_INIT:
+		init_wm9712_phy(wm);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static inline int is_pden (struct wm97xx* wm)
+{
+	return wm->dig[2] & WM9712_PDEN;
+}
+
+/*
+ * Read a sample from the WM9712 adc in polling mode.
+ */
+static int wm9712_poll_sample (struct wm97xx* wm, int adcsel, int *sample)
+{
+	int timeout = 5 * delay;
+
+	if (!wm->pen_probably_down) {
+		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+		if (!(data & WM97XX_PEN_DOWN))
+			return RC_PENUP;
+		wm->pen_probably_down = 1;
+	}
+
+	/* set up digitiser */
+	if (adcsel & 0x8000)
+		adcsel = ((adcsel & 0x7fff) + 3) << 12;
+
+	if (wm->mach_ops && wm->mach_ops->pre_sample)
+		wm->mach_ops->pre_sample(adcsel);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
+
+	/* wait 3 AC97 time slots + delay for conversion */
+	poll_delay (delay);
+
+	/* wait for POLL to go low */
+	while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
+		udelay(AC97_LINK_FRAME);
+		timeout--;
+	}
+
+	if (timeout <= 0) {
+		/* If PDEN is set, we can get a timeout when pen goes up */
+		if (is_pden(wm))
+			wm->pen_probably_down = 0;
+		else
+			dbg ("adc sample timeout");
+		return RC_PENUP;
+	}
+
+	*sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	if (wm->mach_ops && wm->mach_ops->post_sample)
+		wm->mach_ops->post_sample(adcsel);
+
+	/* check we have correct sample */
+	if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
+		dbg ("adc wrong sample, read %x got %x", adcsel,
+		*sample & WM97XX_ADCSEL_MASK);
+		return RC_PENUP;
+	}
+
+	if (!(*sample & WM97XX_PEN_DOWN)) {
+		wm->pen_probably_down = 0;
+		return RC_PENUP;
+	}
+
+	return RC_VALID;
+}
+
+/*
+ * Read a coord from the WM9712 adc in polling mode.
+ */
+static int wm9712_poll_coord (struct wm97xx* wm, struct wm97xx_data *data)
+{
+	int timeout = 5 * delay;
+
+	if (!wm->pen_probably_down) {
+		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+		if (!(data & WM97XX_PEN_DOWN))
+			return RC_PENUP;
+		wm->pen_probably_down = 1;
+	}
+
+	/* set up digitiser */
+	if (wm->mach_ops && wm->mach_ops->pre_sample)
+		wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
+		WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay));
+
+	/* wait 3 AC97 time slots + delay for conversion and read x */
+	poll_delay(delay);
+	data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	/* wait for POLL to go low */
+	while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
+		udelay(AC97_LINK_FRAME);
+		timeout--;
+	}
+
+	if (timeout <= 0) {
+		/* If PDEN is set, we can get a timeout when pen goes up */
+		if (is_pden(wm))
+			wm->pen_probably_down = 0;
+		else
+			dbg ("adc sample timeout");
+		return RC_PENUP;
+	}
+
+	/* read back y data */
+	data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	if (pil)
+		data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	else
+		data->p = DEFAULT_PRESSURE;
+
+	if (wm->mach_ops && wm->mach_ops->post_sample)
+		wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+	/* check we have correct sample */
+	if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
+		goto err;
+	if(pil && !(data->p & WM97XX_ADCSEL_PRES))
+		goto err;
+
+	if (!(data->x & WM97XX_PEN_DOWN)) {
+		wm->pen_probably_down = 0;
+		return RC_PENUP;
+	}
+	return RC_VALID;
+err:
+	return RC_PENUP;
+}
+
+/*
+ * Sample the WM9712 touchscreen in polling mode
+ */
+static int wm9712_poll_touch(struct wm97xx* wm, struct wm97xx_data *data)
+{
+	int rc;
+
+	if(coord) {
+		if((rc = wm9712_poll_coord(wm, data)) != RC_VALID)
+			return rc;
+	} else {
+		if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID)
+			return rc;
+
+		if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID)
+			return rc;
+
+		if (pil && !five_wire) {
+			if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID)
+				return rc;
+		} else
+			data->p = DEFAULT_PRESSURE;
+	}
+	return RC_VALID;
+}
+
+/*
+ * Enable WM9712 continuous mode, i.e. touch data is streamed across an AC97 slot
+ */
+static int wm9712_acc_enable (struct wm97xx* wm, int enable)
+{
+	u16 dig1, dig2;
+	int ret = 0;
+
+	dig1 = wm->dig[1];
+	dig2 = wm->dig[2];
+
+	if (enable) {
+		/* continous mode */
+		if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0)
+			return ret;
+		dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
+			WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
+		dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
+			WM97XX_DELAY (delay) |
+			WM97XX_SLT (wm->acc_slot) |
+			WM97XX_RATE (wm->acc_rate);
+		if (pil)
+			dig1 |= WM97XX_ADCSEL_PRES;
+		dig2 |= WM9712_PDEN;
+	} else {
+		dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
+		dig2 &= ~WM9712_PDEN;
+		if (wm->mach_ops->acc_shutdown)
+			wm->mach_ops->acc_shutdown(wm);
+	}
+
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+	return 0;
+}
+
+struct wm97xx_codec_drv wm97xx_codec = {
+	.id = 	WM9712_ID2,
+	.name = "wm9712",
+	.poll_sample = wm9712_poll_sample,
+	.poll_touch = wm9712_poll_touch,
+	.acc_enable = wm9712_acc_enable,
+	.digitiser_ioctl = wm9712_digitiser_ioctl,
+};
+
+EXPORT_SYMBOL_GPL(wm97xx_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("WM9712 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c
new file mode 100644
index 0000000..42f5f30
--- /dev/null
+++ b/drivers/input/touchscreen/wm9713.c
@@ -0,0 +1,461 @@
+/*
+ * wm9713.c  --  Codec touch driver for Wolfson WM9713 AC97 Codec.
+ *
+ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *                   Russell King <rmk@arm.linux.org.uk>
+ *
+ *  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.
+ *
+ *  Revision history
+ *     6th Sep 2006  Mike Arthur <linux@wolfsonmicro.com>
+ *                   Added pre and post sample calls.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME			"wm97xx"
+#define WM9713_VERSION		"0.53"
+#define DEFAULT_PRESSURE	0xb0c0
+
+/*
+ * Debug
+ */
+#if 0
+#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set internal pull up for pen detect.
+ *
+ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
+ * i.e. pull up resistance = 64k Ohms / rpu.
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down event.
+ */
+static int rpu = 8;
+module_param(rpu, int, 0);
+MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ *     pil = 1 to use 200uA and
+ *     pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil = 0;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 4;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask = 0;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * Coordinate Polling Enable.
+ *
+ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
+ * for every poll.
+ */
+static int coord = 0;
+module_param(coord, int, 0);
+MODULE_PARM_DESC(coord, "Polling coordinate mode");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+	21,    // 1 AC97 Link frames
+	42,    // 2
+	84,    // 4
+	167,   // 8
+	333,   // 16
+	667,   // 32
+	1000,  // 48
+	1333,  // 64
+	2000,  // 96
+	2667,  // 128
+	3333,  // 160
+	4000,  // 192
+	4667,  // 224
+	5333,  // 256
+	6000,  // 288
+	0      // No delay, switch matrix always on
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+	udelay (3 * AC97_LINK_FRAME + delay_table [d]);
+}
+
+/*
+ * set up the physical settings of the WM9713
+ */
+static void init_wm9713_phy(struct wm97xx* wm)
+{
+	u16 dig1 = 0, dig2, dig3;
+
+	/* default values */
+	dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5);
+	dig3= WM9712_RPU(1);
+
+	/* rpu */
+	if (rpu) {
+		dig3 &= 0xffc0;
+		dig3 |= WM9712_RPU(rpu);
+		info("setting pen detect pull-up to %d Ohms",64000 / rpu);
+	}
+
+	/* touchpanel pressure */
+	if (pil == 2) {
+		dig3 |= WM9712_PIL;
+		info("setting pressure measurement current to 400uA.");
+	} else if (pil)
+		info ("setting pressure measurement current to 200uA.");
+	if(!pil)
+		pressure = 0;
+
+	/* sample settling delay */
+	if (delay < 0 || delay > 15) {
+		info ("supplied delay out of range.");
+		delay = 4;
+		info("setting adc sample delay to %d u Secs.", delay_table[delay]);
+	}
+	dig2 &= 0xff0f;
+	dig2 |= WM97XX_DELAY(delay);
+
+	/* mask */
+	dig3 |= ((mask & 0x3) << 4);
+	if(coord)
+		dig3 |= WM9713_WAIT;
+
+	wm->misc = wm97xx_reg_read(wm, 0x5a);
+
+	wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
+	wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0);
+}
+
+static int wm9713_digitiser_ioctl(struct wm97xx* wm, int cmd)
+{
+	u16 val = 0;
+
+	switch(cmd){
+	case WM97XX_DIG_START:
+		val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
+		wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff);
+		wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] | WM97XX_PRP_DET_DIG);
+		wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+		break;
+	case WM97XX_DIG_STOP:
+		wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] & ~WM97XX_PRP_DET_DIG);
+		val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
+		wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000);
+		break;
+	case WM97XX_AUX_PREPARE:
+		memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+		wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0);
+		wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0);
+		wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG);
+		break;
+	case WM97XX_DIG_RESTORE:
+		wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]);
+		wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]);
+		wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]);
+		break;
+	case WM97XX_PHY_INIT:
+		init_wm9713_phy(wm);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static inline int is_pden (struct wm97xx* wm)
+{
+	return wm->dig[2] & WM9713_PDEN;
+}
+
+/*
+ * Read a sample from the WM9713 adc in polling mode.
+ */
+static int wm9713_poll_sample (struct wm97xx* wm, int adcsel, int *sample)
+{
+	u16 dig1;
+	int timeout = 5 * delay;
+
+	if (!wm->pen_probably_down) {
+		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+		if (!(data & WM97XX_PEN_DOWN))
+			return RC_PENUP;
+		wm->pen_probably_down = 1;
+	}
+
+	/* set up digitiser */
+	if (adcsel & 0x8000)
+		adcsel = 1 << ((adcsel & 0x7fff) + 3);
+
+	dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
+	dig1 &= ~WM9713_ADCSEL_MASK;
+
+	if (wm->mach_ops && wm->mach_ops->pre_sample)
+		wm->mach_ops->pre_sample(adcsel);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel |WM9713_POLL);
+
+	/* wait 3 AC97 time slots + delay for conversion */
+	poll_delay(delay);
+
+	/* wait for POLL to go low */
+	while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) {
+		udelay(AC97_LINK_FRAME);
+		timeout--;
+	}
+
+	if (timeout <= 0) {
+		/* If PDEN is set, we can get a timeout when pen goes up */
+		if (is_pden(wm))
+			wm->pen_probably_down = 0;
+		else
+			dbg ("adc sample timeout");
+		return RC_PENUP;
+	}
+
+	*sample =wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	if (wm->mach_ops && wm->mach_ops->post_sample)
+		wm->mach_ops->post_sample(adcsel);
+
+	/* check we have correct sample */
+	if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) {
+		dbg ("adc wrong sample, read %x got %x", adcsel,
+		     *sample & WM97XX_ADCSRC_MASK);
+		return RC_PENUP;
+	}
+
+	if (!(*sample & WM97XX_PEN_DOWN)) {
+		wm->pen_probably_down = 0;
+		return RC_PENUP;
+	}
+
+	return RC_VALID;
+}
+
+/*
+ * Read a coordinate from the WM9713 adc in polling mode.
+ */
+static int wm9713_poll_coord (struct wm97xx* wm, struct wm97xx_data *data)
+{
+	u16 dig1;
+	int timeout = 5 * delay;
+
+	if (!wm->pen_probably_down) {
+		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+		if (!(data & WM97XX_PEN_DOWN))
+			return RC_PENUP;
+		wm->pen_probably_down = 1;
+	}
+
+	/* set up digitiser */
+	dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
+	dig1 &= ~WM9713_ADCSEL_MASK;
+	if(pil)
+		dig1 |= WM97XX_ADCSEL_PRES;
+
+	if (wm->mach_ops && wm->mach_ops->pre_sample)
+		wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL | WM9713_COO);
+
+	/* wait 3 AC97 time slots + delay for conversion */
+	poll_delay(delay);
+	data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	/* wait for POLL to go low */
+	while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) {
+		udelay(AC97_LINK_FRAME);
+		timeout--;
+	}
+
+	if (timeout <= 0) {
+		/* If PDEN is set, we can get a timeout when pen goes up */
+		if (is_pden(wm))
+			wm->pen_probably_down = 0;
+		else
+			dbg ("adc sample timeout");
+		return RC_PENUP;
+	}
+
+	/* read back data */
+	data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	if (pil)
+		data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	else
+		data->p = DEFAULT_PRESSURE;
+
+	if (wm->mach_ops && wm->mach_ops->post_sample)
+		wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+	/* check we have correct sample */
+	if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
+		goto err;
+	if(pil && !(data->p & WM97XX_ADCSEL_PRES))
+		goto err;
+
+	if (!(data->x & WM97XX_PEN_DOWN)) {
+		wm->pen_probably_down = 0;
+		return RC_PENUP;
+	}
+	return RC_VALID;
+err:
+	return RC_PENUP;
+}
+
+/*
+ * Sample the WM9713 touchscreen in polling mode
+ */
+static int wm9713_poll_touch(struct wm97xx* wm, struct wm97xx_data *data)
+{
+	int rc;
+
+	if(coord) {
+		if((rc = wm9713_poll_coord(wm, data)) != RC_VALID)
+			return rc;
+	} else {
+		if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x)) != RC_VALID)
+			return rc;
+		if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y)) != RC_VALID)
+			return rc;
+		if (pil) {
+			if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, &data->p)) != RC_VALID)
+				return rc;
+		} else
+			data->p = DEFAULT_PRESSURE;
+	}
+	return RC_VALID;
+}
+
+/*
+ * Enable WM9713 continuous mode, i.e. touch data is streamed across an AC97 slot
+ */
+static int wm9713_acc_enable (struct wm97xx* wm, int enable)
+{
+	u16 dig1, dig2, dig3;
+	int ret = 0;
+
+	dig1 = wm->dig[0];
+	dig2 = wm->dig[1];
+	dig3 = wm->dig[2];
+
+	if (enable) {
+		/* continous mode */
+		if (wm->mach_ops->acc_startup &&
+			(ret = wm->mach_ops->acc_startup(wm)) < 0)
+			return ret;
+
+		dig1 &= ~WM9713_ADCSEL_MASK;
+		dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | WM9713_ADCSEL_Y;
+        if (pil)
+		dig1 |= WM9713_ADCSEL_PRES;
+		dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK  | WM97XX_CM_RATE_MASK);
+		dig2 |= WM97XX_SLEN | WM97XX_DELAY (delay) |
+		WM97XX_SLT (wm->acc_slot) | WM97XX_RATE (wm->acc_rate);
+		dig3 |= WM9713_PDEN;
+	} else {
+		dig1 &= ~(WM9713_CTC | WM9713_COO);
+		dig2 &= ~WM97XX_SLEN;
+		dig3 &= ~WM9713_PDEN;
+        if (wm->mach_ops->acc_shutdown)
+            wm->mach_ops->acc_shutdown(wm);
+	}
+
+	wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
+	return ret;
+}
+
+struct wm97xx_codec_drv wm97xx_codec = {
+	.id = 	WM9713_ID2,
+    .name = "wm9713",
+	.poll_sample = wm9713_poll_sample,
+	.poll_touch = wm9713_poll_touch,
+	.acc_enable = wm9713_acc_enable,
+	.digitiser_ioctl = wm9713_digitiser_ioctl,
+};
+
+EXPORT_SYMBOL_GPL(wm97xx_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("WM9713 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
new file mode 100644
index 0000000..87179d2
--- /dev/null
+++ b/drivers/input/touchscreen/wm97xx-core.c
@@ -0,0 +1,859 @@
+/*
+ * wm97xx-core.c  --  Touch screen driver core for Wolfson WM9705, WM9712
+ *                    and WM9713 AC97 Codecs.
+ *
+ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *                   Russell King <rmk@arm.linux.org.uk>
+ *
+ *  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.
+ *
+ * Notes:
+ *
+ *  Features:
+ *       - supports WM9705, WM9712, WM9713
+ *       - polling mode
+ *       - continuous mode (arch-dependent)
+ *       - adjustable rpu/dpp settings
+ *       - adjustable pressure current
+ *       - adjustable sample settle delay
+ *       - 4 and 5 wire touchscreens (5 wire is WM9712 only)
+ *       - pen down detection
+ *       - battery monitor
+ *       - sample AUX adc's
+ *       - power management
+ *       - codec GPIO
+ *       - codec event notification
+ * Todo
+ *       - Support for async sampling control for noisy LCD's.
+ *
+ *  Revision history
+ *    7th May 2003   Initial version.
+ *    6th June 2003  Added non module support and AC97 registration.
+ *   18th June 2003  Added AUX adc sampling.
+ *   23rd June 2003  Did some minimal reformatting, fixed a couple of
+ *                   codec_mutexing bugs and noted a race to fix.
+ *   24th June 2003  Added power management and fixed race condition.
+ *   10th July 2003  Changed to a misc device.
+ *   31st July 2003  Moved TS_EVENT and TS_CAL to wm97xx.h
+ *    8th Aug  2003  Added option for read() calling wm97xx_sample_touch()
+ *                   because some ac97_read/ac_97_write call schedule()
+ *    7th Nov  2003  Added Input touch event interface, stanley.cai@intel.com
+ *   13th Nov  2003  Removed h3600 touch interface, added interrupt based
+ *                   pen down notification and implemented continous mode
+ *                   on XScale arch.
+ *   16th Nov  2003  Ian Molton <spyro@f2s.com>
+ *                   Modified so that it suits the new 2.6 driver model.
+ *   25th Jan  2004  Andrew Zabolotny <zap@homelink.ru>
+ *                   Implemented IRQ-driven pen down detection, implemented
+ *                   the private API meant to be exposed to platform-specific
+ *                   drivers, reorganized the driver so that it supports
+ *                   an arbitrary number of devices.
+ *    1st Feb  2004  Moved continuous mode handling to a separate
+ *                   architecture-dependent file. For now only PXA
+ *                   built-in AC97 controller is supported (pxa-ac97-wm97xx.c).
+ *    11th Feb 2004  Reduced CPU usage by keeping a cached copy of both
+ *                   digitizer registers instead of reading them every time.
+ *                   A reorganization of the whole code for better
+ *                   error handling.
+ *    17th Apr 2004  Added BMON support.
+ *    17th Nov 2004  Added codec GPIO, codec event handling (real and virtual
+ *                   GPIOs) and 2.6 power management.
+ *    29th Nov 2004  Added WM9713 support.
+ *     4th Jul 2005  Moved codec specific code out to seperate files.
+ *     6th Sep 2006  Mike Arthur <linux@wolfsonmicro.com>
+ *                   Added bus interface.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/pm.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/freezer.h>
+#include <linux/wm97xx.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <asm/arch/irqs.h>
+
+#define TS_NAME			"wm97xx"
+#define WM_CORE_VERSION		"0.64"
+#define DEFAULT_PRESSURE	0xb0c0
+
+
+/*
+ * Touchscreen absolute values
+ *
+ * These parameters are used to help the input layer discard out of
+ * range readings and reduce jitter etc.
+ *
+ *   o min, max:- indicate the min and max values your touch screen returns
+ *   o fuzz:- use a higher number to reduce jitter
+ *
+ * The default values correspond to Mainstone II in QVGA mode
+ *
+ * Please read
+ * Documentation/input/input-programming.txt for more details.
+ */
+
+static int abs_x[3] = {350,3900,5};
+module_param_array(abs_x, int, NULL, 0);
+MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz");
+
+static int abs_y[3] = {320,3750,40};
+module_param_array(abs_y, int, NULL, 0);
+MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz");
+
+static int abs_p[3] = {0,150,4};
+module_param_array(abs_p, int, NULL, 0);
+MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz");
+
+/*
+ * Debug
+ */
+#if 0
+#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
+
+/*
+ * wm97xx IO access, all IO locking done by AC97 layer
+ */
+int wm97xx_reg_read(struct wm97xx *wm, u16 reg)
+{
+	if (wm->ac97)
+		return wm->ac97->bus->ops->read(wm->ac97, reg);
+	else
+		return -1;
+}
+EXPORT_SYMBOL_GPL(wm97xx_reg_read);
+
+void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val)
+{
+	/* cache digitiser registers */
+	if(reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3)
+		wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val;
+
+	/* cache gpio regs */
+	if(reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE)
+		wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val;
+
+	/* wm9713 irq reg */
+	if(reg == 0x5a)
+		wm->misc = val;
+
+	if (wm->ac97)
+		wm->ac97->bus->ops->write(wm->ac97, reg, val);
+}
+EXPORT_SYMBOL_GPL(wm97xx_reg_write);
+
+/**
+ *	wm97xx_read_aux_adc - Read the aux adc.
+ *	@wm: wm97xx device.
+ *  @adcsel: codec ADC to be read
+ *
+ *	Reads the selected AUX ADC.
+ */
+
+int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
+{
+	int power_adc = 0, auxval;
+	u16 power = 0;
+
+	/* get codec */
+	mutex_lock(&wm->codec_mutex);
+
+	/* When the touchscreen is not in use, we may have to power up the AUX ADC
+	 * before we can use sample the AUX inputs->
+	 */
+	if (wm->id == WM9713_ID2 &&
+	    (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) {
+		power_adc = 1;
+		wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff);
+	}
+
+	/* Prepare the codec for AUX reading */
+	wm->codec->digitiser_ioctl(wm, WM97XX_AUX_PREPARE);
+
+	/* Turn polling mode on to read AUX ADC */
+	wm->pen_probably_down = 1;
+	wm->codec->poll_sample(wm, adcsel, &auxval);
+
+	if (power_adc)
+		wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000);
+
+	wm->codec->digitiser_ioctl(wm, WM97XX_DIG_RESTORE);
+
+	wm->pen_probably_down = 0;
+
+	mutex_unlock(&wm->codec_mutex);
+	return auxval & 0xfff;
+}
+EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc);
+
+/**
+ *	wm97xx_get_gpio - Get the status of a codec GPIO.
+ *	@wm: wm97xx device.
+ *  @gpio: gpio
+ *
+ *	Get the status of a codec GPIO pin
+ */
+
+wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio)
+{
+	u16 status;
+	wm97xx_gpio_status_t ret;
+
+	mutex_lock(&wm->codec_mutex);
+	status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+
+	if (status & gpio)
+		ret = WM97XX_GPIO_HIGH;
+	else
+		ret = WM97XX_GPIO_LOW;
+
+	mutex_unlock(&wm->codec_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm97xx_get_gpio);
+
+/**
+ *	wm97xx_set_gpio - Set the status of a codec GPIO.
+ *	@wm: wm97xx device.
+ *  @gpio: gpio
+ *
+ *
+ *	Set the status of a codec GPIO pin
+ */
+
+void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
+				wm97xx_gpio_status_t status)
+{
+	u16 reg;
+
+	mutex_lock(&wm->codec_mutex);
+	reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+
+	if (status & WM97XX_GPIO_HIGH)
+		reg |= gpio;
+	else
+		reg &= ~gpio;
+
+	if (wm->id == WM9712_ID2)
+		wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1);
+	else
+		wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg);
+	mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_set_gpio);
+
+/*
+ * Codec GPIO pin configuration, this set's pin direction, polarity,
+ * stickyness and wake up.
+ */
+void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir,
+		   wm97xx_gpio_pol_t pol, wm97xx_gpio_sticky_t sticky,
+		   wm97xx_gpio_wake_t wake)
+{
+	u16 reg;
+
+	mutex_lock(&wm->codec_mutex);
+	reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+
+	if (pol == WM97XX_GPIO_POL_HIGH)
+		reg |= gpio;
+	else
+		reg &= ~gpio;
+
+	wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg);
+	reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
+
+	if (sticky == WM97XX_GPIO_STICKY)
+		reg |= gpio;
+	else
+		reg &= ~gpio;
+
+	wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg);
+	reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
+
+	if (wake == WM97XX_GPIO_WAKE)
+		reg |= gpio;
+	else
+		reg &= ~gpio;
+
+	wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg);
+	reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+
+	if (dir == WM97XX_GPIO_IN)
+		reg |= gpio;
+	else
+		reg &= ~gpio;
+
+	wm97xx_reg_write(wm, AC97_GPIO_CFG, reg);
+	mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_config_gpio);
+
+/*
+ * Handle a pen down interrupt.
+ */
+static void wm97xx_pen_irq_worker(struct work_struct *w)
+{
+/* 	struct wm97xx *wm = (struct wm97xx *) ptr; */
+	struct wm97xx *wm = container_of(w, struct wm97xx, pen_event_work);
+
+	/* do we need to enable the touch panel reader */
+	if (wm->id == WM9705_ID2) {
+		if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN)
+			wm->pen_is_down = 1;
+		else
+			wm->pen_is_down = 0;
+		wake_up_interruptible(&wm->pen_irq_wait);
+	} else {
+		u16 status, pol;
+		mutex_lock(&wm->codec_mutex);
+		status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+		pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+
+		if (WM97XX_GPIO_13 & pol & status) {
+			wm->pen_is_down = 1;
+			wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol & ~WM97XX_GPIO_13);
+		} else {
+			wm->pen_is_down = 0;
+		    wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol | WM97XX_GPIO_13);
+		}
+
+		if (wm->id == WM9712_ID2)
+			wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & ~WM97XX_GPIO_13) << 1);
+		else
+			wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & ~WM97XX_GPIO_13);
+		mutex_unlock(&wm->codec_mutex);
+		wake_up_interruptible(&wm->pen_irq_wait);
+	}
+
+	if (!wm->pen_is_down && wm->mach_ops && wm->mach_ops->acc_enabled)
+		wm->mach_ops->acc_pen_up(wm);
+	enable_irq(wm->pen_irq);
+}
+
+/*
+ * Codec PENDOWN irq handler
+ *
+ * We have to disable the codec interrupt in the handler because it can
+ * take upto 1ms to clear the interrupt source. The interrupt is then enabled
+ * again in the slow handler when the source has been cleared.
+ */
+static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id)
+{
+	struct wm97xx *wm = (struct wm97xx *) dev_id;
+	disable_irq(wm->pen_irq);
+	queue_work(wm->pen_irq_workq, &wm->pen_event_work);
+	return IRQ_HANDLED;
+}
+
+/*
+ * initialise pen IRQ handler and workqueue
+ */
+static int wm97xx_init_pen_irq(struct wm97xx *wm)
+{
+	u16 reg;
+
+	INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker/* , wm */);
+	if ((wm->pen_irq_workq =
+		create_singlethread_workqueue("kwm97pen")) == NULL) {
+		err("could not create pen irq work queue");
+		wm->pen_irq = 0;
+		return -EINVAL;
+	}
+
+
+	wm->pen_irq = IRQ_GPIO(90);
+	if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) {
+		err("could not register codec pen down interrupt, will poll for pen down");
+		destroy_workqueue(wm->pen_irq_workq);
+		wm->pen_irq = 0;
+		return -EINVAL;
+	}
+
+	/* enable PEN down on wm9712/13 */
+	if (wm->id != WM9705_ID2) {
+		reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+		wm97xx_reg_write(wm, AC97_MISC_AFE, reg & 0xfffb);
+		reg = wm97xx_reg_read(wm, 0x5a);
+		wm97xx_reg_write(wm, 0x5a, reg & ~0x0001);
+	}
+
+	return 0;
+}
+
+/* Private struct for communication between struct wm97xx_tshread
+ * and wm97xx_read_samples */
+struct ts_state {
+	int sleep_time;
+	int min_sleep_time;
+};
+
+static int wm97xx_read_samples(struct wm97xx *wm, struct ts_state *state)
+{
+	struct wm97xx_data data;
+	int rc;
+
+	mutex_lock(&wm->codec_mutex);
+
+    if (wm->mach_ops && wm->mach_ops->acc_enabled)
+	   rc = wm->mach_ops->acc_pen_down(wm);
+    else
+        rc = wm->codec->poll_touch(wm, &data);
+
+	if (rc & RC_PENUP) {
+		if (wm->pen_is_down) {
+			wm->pen_is_down = 0;
+			dbg("pen up");
+			input_report_abs(wm->input_dev, ABS_PRESSURE, 0);
+			input_sync(wm->input_dev);
+		} else if (!(rc & RC_AGAIN)) {
+			/* We need high frequency updates only while pen is down,
+			* the user never will be able to touch screen faster than
+			* a few times per second... On the other hand, when the
+			* user is actively working with the touchscreen we don't
+			* want to lose the quick response. So we will slowly
+			* increase sleep time after the pen is up and quicky
+			* restore it to ~one task switch when pen is down again.
+			*/
+			if (state->sleep_time < HZ / 10)
+				state->sleep_time++;
+		}
+
+	} else if (rc & RC_VALID) {
+		dbg("pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n",
+			data.x >> 12, data.x & 0xfff, data.y >> 12,
+			data.y & 0xfff, data.p >> 12, data.p & 0xfff);
+		input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);
+		input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);
+		input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff);
+		input_sync(wm->input_dev);
+		wm->pen_is_down = 1;
+		state->sleep_time = state->min_sleep_time;
+	} else if (rc & RC_PENDOWN) {
+		dbg("pen down");
+		wm->pen_is_down = 1;
+		state->sleep_time = state->min_sleep_time;
+	}
+
+	mutex_unlock(&wm->codec_mutex);
+	return rc;
+}
+
+/*
+* The touchscreen sample reader thread.
+*/
+static int wm97xx_ts_read(void *data)
+{
+	int rc;
+	struct ts_state state;
+	struct wm97xx *wm = (struct wm97xx *) data;
+
+	/* set up thread context */
+	wm->ts_task = current;
+	daemonize("kwm97xxts");
+
+	if (wm->codec == NULL) {
+		wm->ts_task = NULL;
+		printk(KERN_ERR "codec is NULL, bailing\n");
+	}
+
+	complete(&wm->ts_init);
+	wm->pen_is_down = 0;
+	state.min_sleep_time = HZ >= 100 ? HZ / 100 : 1;
+	if (state.min_sleep_time < 1)
+		state.min_sleep_time = 1;
+	state.sleep_time = state.min_sleep_time;
+
+	/* touch reader loop */
+	while (wm->ts_task) {
+		do {
+			try_to_freeze();
+			rc = wm97xx_read_samples(wm, &state);
+		} while (rc & RC_AGAIN);
+		if (!wm->pen_is_down && wm->pen_irq) {
+			/* Nice, we don't have to poll for pen down event */
+			wait_event_interruptible(wm->pen_irq_wait, wm->pen_is_down);
+		} else {
+			set_task_state(current, TASK_INTERRUPTIBLE);
+			schedule_timeout(state.sleep_time);
+		}
+	}
+	complete_and_exit(&wm->ts_exit, 0);
+}
+
+/**
+ *	wm97xx_ts_input_open - Open the touch screen input device.
+ *	@idev:	Input device to be opened.
+ *
+ *	Called by the input sub system to open a wm97xx touchscreen device.
+ *  Starts the touchscreen thread and touch digitiser.
+ */
+static int wm97xx_ts_input_open(struct input_dev *idev)
+{
+	int ret = 0;
+	struct wm97xx *wm = (struct wm97xx *) idev->private;
+
+	mutex_lock(&wm->codec_mutex);
+	/* first time opened ? */
+	if (wm->ts_use_count++ == 0) {
+		/* start touchscreen thread */
+		init_completion(&wm->ts_init);
+		init_completion(&wm->ts_exit);
+		ret = kernel_thread(wm97xx_ts_read, wm, CLONE_KERNEL);
+
+		if (ret >= 0) {
+			wait_for_completion(&wm->ts_init);
+			if (wm->ts_task == NULL)
+				ret = -EINVAL;
+		} else {
+			mutex_unlock(&wm->codec_mutex);
+			return ret;
+		}
+
+		/* start digitiser */
+        if (wm->mach_ops && wm->mach_ops->acc_enabled)
+            wm->codec->acc_enable(wm, 1);
+		wm->codec->digitiser_ioctl(wm, WM97XX_DIG_START);
+
+		/* init pen down/up irq handling */
+		if (wm->pen_irq) {
+			wm97xx_init_pen_irq(wm);
+
+			if (wm->pen_irq == 0) {
+				/* we failed to get an irq for pen down events,
+				 * so we resort to polling. kickstart the reader */
+				wm->pen_is_down = 1;
+				wake_up_interruptible(&wm->pen_irq_wait);
+			}
+		}
+	}
+
+	mutex_unlock(&wm->codec_mutex);
+	return 0;
+}
+
+/**
+ *	wm97xx_ts_input_close - Close the touch screen input device.
+ *	@idev:	Input device to be closed.
+ *
+ *	Called by the input sub system to close a wm97xx touchscreen device.
+ *  Kills the touchscreen thread and stops the touch digitiser.
+ */
+
+static void wm97xx_ts_input_close(struct input_dev *idev)
+{
+	struct wm97xx *wm = (struct wm97xx *) idev->private;
+
+	mutex_lock(&wm->codec_mutex);
+	if (--wm->ts_use_count == 0) {
+		/* destroy workqueues and free irqs */
+		if (wm->pen_irq) {
+			free_irq(wm->pen_irq, wm);
+			destroy_workqueue(wm->pen_irq_workq);
+		}
+
+		/* kill thread */
+		if (wm->ts_task) {
+			wm->ts_task = NULL;
+			wm->pen_is_down = 1;
+			mutex_unlock(&wm->codec_mutex);
+			wake_up_interruptible(&wm->pen_irq_wait);
+			wait_for_completion(&wm->ts_exit);
+			wm->pen_is_down = 0;
+			mutex_lock(&wm->codec_mutex);
+		}
+
+		/* stop digitiser */
+		wm->codec->digitiser_ioctl(wm, WM97XX_DIG_STOP);
+		if (wm->mach_ops && wm->mach_ops->acc_enabled)
+			wm->codec->acc_enable(wm, 0);
+	}
+	mutex_unlock(&wm->codec_mutex);
+}
+
+/*
+ * Bus interface to allow for client drivers codec access
+ * e.g. battery monitor
+ */
+static int wm97xx_bus_match(struct device *dev, struct device_driver *drv)
+{
+    return !(strcmp(dev->bus_id,drv->name));
+}
+
+/*
+ * The AC97 audio driver will do all the Codec suspend and resume
+ * tasks. This is just for anything machine specific or extra.
+ */
+static int wm97xx_bus_suspend(struct device *dev, pm_message_t state)
+{
+    int ret = 0;
+
+    if (dev->driver && dev->driver->suspend)
+        ret = dev->driver->suspend(dev, state);
+
+    return ret;
+}
+
+static int wm97xx_bus_resume(struct device *dev)
+{
+    int ret = 0;
+
+    if (dev->driver && dev->driver->resume)
+        ret = dev->driver->resume(dev);
+
+    return ret;
+}
+
+struct bus_type wm97xx_bus_type = {
+    .name       = "wm97xx",
+    .match      = wm97xx_bus_match,
+    .suspend    = wm97xx_bus_suspend,
+    .resume     = wm97xx_bus_resume,
+};
+EXPORT_SYMBOL_GPL(wm97xx_bus_type);
+
+static void  wm97xx_release(struct device *dev)
+{
+    kfree(dev);
+}
+
+static int wm97xx_probe(struct device *dev)
+{
+	struct wm97xx* wm;
+	int ret = 0, id = 0;
+
+	if (!(wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL)))
+		return -ENOMEM;
+    mutex_init(&wm->codec_mutex);
+
+	init_waitqueue_head(&wm->pen_irq_wait);
+	wm->dev = dev;
+	dev->driver_data = wm;
+	wm->ac97 = to_ac97_t(dev);
+
+	/* check that we have a supported codec */
+	if ((id = wm97xx_reg_read(wm, AC97_VENDOR_ID1)) != WM97XX_ID1) {
+		err("could not find a wm97xx, found a %x instead\n", id);
+		kfree(wm);
+		return -ENODEV;
+	}
+
+	wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2);
+	if(wm->id != wm97xx_codec.id) {
+		err("could not find a the selected codec, please build for wm97%2x", wm->id & 0xff);
+		kfree(wm);
+		return -ENODEV;
+	}
+
+	if((wm->input_dev = input_allocate_device()) == NULL) {
+        kfree(wm);
+		return -ENOMEM;
+    }
+
+	/* set up touch configuration */
+	info("detected a wm97%2x codec", wm->id & 0xff);
+	wm->input_dev->name = "wm97xx touchscreen";
+	wm->input_dev->open = wm97xx_ts_input_open;
+	wm->input_dev->close = wm97xx_ts_input_close;
+	set_bit(EV_ABS, wm->input_dev->evbit);
+	set_bit(ABS_X, wm->input_dev->absbit);
+	set_bit(ABS_Y, wm->input_dev->absbit);
+	set_bit(ABS_PRESSURE, wm->input_dev->absbit);
+	wm->input_dev->absmax[ABS_X] = abs_x[1];
+	wm->input_dev->absmax[ABS_Y] = abs_y[1];
+	wm->input_dev->absmax[ABS_PRESSURE] = abs_p[1];
+	wm->input_dev->absmin[ABS_X] = abs_x[0];
+	wm->input_dev->absmin[ABS_Y] = abs_y[0];
+	wm->input_dev->absmin[ABS_PRESSURE] = abs_p[0];
+	wm->input_dev->absfuzz[ABS_X] = abs_x[2];
+	wm->input_dev->absfuzz[ABS_Y] = abs_y[2];
+	wm->input_dev->absfuzz[ABS_PRESSURE] = abs_p[2];
+	wm->input_dev->private = wm;
+	wm->codec = &wm97xx_codec;
+	if((ret = input_register_device(wm->input_dev)) < 0) {
+		kfree(wm);
+		return -ENOMEM;
+    }
+
+	/* set up physical characteristics */
+	wm->codec->digitiser_ioctl(wm, WM97XX_PHY_INIT);
+
+	/* load gpio cache */
+	wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+	wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+	wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
+	wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
+	wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+	wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
+
+	/* register our battery device */
+    if (!(wm->battery_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) {
+    	ret = -ENOMEM;
+        goto batt_err;
+    }
+    wm->battery_dev->bus = &wm97xx_bus_type;
+    strcpy(wm->battery_dev->bus_id,"wm97xx-battery");
+    wm->battery_dev->driver_data = wm;
+    wm->battery_dev->parent = dev;
+    wm->battery_dev->release = wm97xx_release;
+    if((ret = device_register(wm->battery_dev)) < 0)
+    	goto batt_reg_err;
+
+	/* register our extended touch device (for machine specific extensions) */
+    if (!(wm->touch_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) {
+    	ret = -ENOMEM;
+        goto touch_err;
+    }
+    wm->touch_dev->bus = &wm97xx_bus_type;
+    strcpy(wm->touch_dev->bus_id,"wm97xx-touchscreen");
+    wm->touch_dev->driver_data = wm;
+    wm->touch_dev->parent = dev;
+    wm->touch_dev->release = wm97xx_release;
+    if((ret = device_register(wm->touch_dev)) < 0)
+    	goto touch_reg_err;
+
+    return ret;
+
+touch_reg_err:
+	kfree(wm->touch_dev);
+touch_err:
+    device_unregister(wm->battery_dev);
+batt_reg_err:
+	kfree(wm->battery_dev);
+batt_err:
+    input_unregister_device(wm->input_dev);
+    kfree(wm);
+    return ret;
+}
+
+static int wm97xx_remove(struct device *dev)
+{
+	struct wm97xx *wm = dev_get_drvdata(dev);
+
+	/* Stop touch reader thread */
+	if (wm->ts_task) {
+		wm->ts_task = NULL;
+		wm->pen_is_down = 1;
+		wake_up_interruptible(&wm->pen_irq_wait);
+		wait_for_completion(&wm->ts_exit);
+	}
+	device_unregister(wm->battery_dev);
+	device_unregister(wm->touch_dev);
+    input_unregister_device(wm->input_dev);
+
+	kfree(wm);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+int wm97xx_resume(struct device* dev)
+{
+	struct wm97xx *wm = dev_get_drvdata(dev);
+
+	/* restore digitiser and gpio's */
+	if(wm->id == WM9713_ID2) {
+		wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]);
+		wm97xx_reg_write(wm, 0x5a, wm->misc);
+		if(wm->ts_use_count) {
+			u16 reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff;
+			wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
+		}
+	}
+
+	wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]);
+
+	wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]);
+	wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]);
+	wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]);
+	wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]);
+	wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]);
+	wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]);
+
+	return 0;
+}
+
+#else
+#define wm97xx_resume		NULL
+#endif
+
+/*
+ * Machine specific operations
+ */
+int wm97xx_register_mach_ops(struct wm97xx *wm, struct wm97xx_mach_ops *mach_ops)
+{
+	mutex_lock(&wm->codec_mutex);
+    if(wm->mach_ops) {
+    	mutex_unlock(&wm->codec_mutex);
+    	return -EINVAL;
+    }
+    wm->mach_ops = mach_ops;
+    mutex_unlock(&wm->codec_mutex);
+    return 0;
+}
+EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops);
+
+void wm97xx_unregister_mach_ops(struct wm97xx *wm)
+{
+	mutex_lock(&wm->codec_mutex);
+    wm->mach_ops = NULL;
+    mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops);
+
+static struct device_driver wm97xx_driver = {
+	.name = 	"ac97",
+	.bus = 		&ac97_bus_type,
+	.owner = 	THIS_MODULE,
+	.probe = 	wm97xx_probe,
+	.remove = 	wm97xx_remove,
+	.resume =	wm97xx_resume,
+};
+
+static int __init wm97xx_init(void)
+{
+	int ret;
+
+	info("version %s liam.girdwood@wolfsonmicro.com", WM_CORE_VERSION);
+    if((ret = bus_register(&wm97xx_bus_type)) < 0)
+    	return ret;
+	return driver_register(&wm97xx_driver);
+}
+
+static void __exit wm97xx_exit(void)
+{
+	driver_unregister(&wm97xx_driver);
+    bus_unregister(&wm97xx_bus_type);
+}
+
+module_init(wm97xx_init);
+module_exit(wm97xx_exit);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 4468cb3..01b4828 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -101,6 +101,12 @@ config LEDS_GPIO
 	  outputs. To be useful the particular board must have LEDs
 	  and they must be connected to the GPIO lines.
 
+config LEDS_EM_X270
+	tristate "LED Support for the CompuLab EM-X270"
+	depends on LEDS_CLASS && MACH_EM_X270
+	help
+	  This option enables support for the LEDs on CompuLab EM-X270.
+
 comment "LED Triggers"
 
 config LEDS_TRIGGERS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index f8995c9..6d3941e 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_LEDS_WRAP)			+= leds-wrap.o
 obj-$(CONFIG_LEDS_H1940)		+= leds-h1940.o
 obj-$(CONFIG_LEDS_COBALT)		+= leds-cobalt.o
 obj-$(CONFIG_LEDS_GPIO)			+= leds-gpio.o
+obj-$(CONFIG_LEDS_EM_X270)		+= leds-em-x270.o
 
 # LED Triggers
 obj-$(CONFIG_LEDS_TRIGGER_TIMER)	+= ledtrig-timer.o
diff --git a/drivers/leds/leds-em-x270.c b/drivers/leds/leds-em-x270.c
new file mode 100644
index 0000000..485e7da
--- /dev/null
+++ b/drivers/leds/leds-em-x270.c
@@ -0,0 +1,99 @@
+/*
+ * LED Triggers Core
+ *
+ * Copyright 2007 CompuLab, Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on Corgi leds driver:
+ * Copyright 2005-2006 Openedhand Ltd.
+ * Author: Richard Purdie <rpurdie@openedhand.com>
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/da9030.h>
+
+static void em_x270_led_orange_set(struct led_classdev *led_cdev,
+				   enum led_brightness value)
+{
+	int on = 1;
+	int pwm_chop = 0;
+
+	pr_info("%s: value = %d\n", __FUNCTION__, value);
+	if (value == LED_OFF)
+		on = 0;
+	else if (value == LED_HALF)
+		pwm_chop = 4;
+	
+	da9030_set_led(3, on, 0, 0, pwm_chop);
+}
+
+static struct led_classdev em_x270_orange_led = {
+	.name			= "em_x270:orange",
+	.default_trigger	= "em-x270-battery.0-charging-or-full",
+	.brightness_set		= em_x270_led_orange_set,
+};
+
+#ifdef CONFIG_PM
+static int em_x270_led_suspend(struct platform_device *dev, pm_message_t state)
+{
+#ifdef CONFIG_LEDS_TRIGGERS
+	if (em_x270_orange_led.trigger && strcmp(em_x270_orange_led.trigger->name, "em-x270-battery-charging-or-full"))
+#endif
+		led_classdev_suspend(&em_x270_orange_led);
+	return 0;
+}
+
+static int em_x270_led_resume(struct platform_device *dev)
+{
+	led_classdev_resume(&em_x270_orange_led);
+	return 0;
+}
+#endif
+
+static int em_x270_led_probe(struct platform_device *pdev)
+{
+	return led_classdev_register(&pdev->dev, &em_x270_orange_led);
+}
+
+static int em_x270_led_remove(struct platform_device *pdev)
+{
+	led_classdev_unregister(&em_x270_orange_led);
+	return 0;
+}
+
+static struct platform_driver em_x270_led_driver = {
+	.probe		= em_x270_led_probe,
+	.remove		= em_x270_led_remove,
+#ifdef CONFIG_PM
+	.suspend	= em_x270_led_suspend,
+	.resume		= em_x270_led_resume,
+#endif
+	.driver		= {
+		.name		= "em-x270-led",
+	},
+};
+
+static int __init em_x270_led_init(void)
+{
+	return platform_driver_register(&em_x270_led_driver);
+}
+
+static void __exit em_x270_led_exit(void)
+{
+ 	platform_driver_unregister(&em_x270_led_driver);
+}
+
+module_init(em_x270_led_init);
+module_exit(em_x270_led_exit);
+
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("EX-X270 LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c
index 58e561e..7050e71 100644
--- a/drivers/mtd/chips/jedec_probe.c
+++ b/drivers/mtd/chips/jedec_probe.c
@@ -38,7 +38,7 @@
 #define MANUFACTURER_ST		0x0020
 #define MANUFACTURER_TOSHIBA	0x0098
 #define MANUFACTURER_WINBOND	0x00da
-
+#define CONTINUATION_CODE	0x007f
 
 /* AMD */
 #define AM29DL800BB	0x22C8
@@ -68,6 +68,10 @@
 #define AT49BV32X	0x00C8
 #define AT49BV32XT	0x00C9
 
+/* Eon */
+#define EN29SL800BB	0x226B
+#define EN29SL800BT	0x22EA
+
 /* Fujitsu */
 #define MBM29F040C	0x00A4
 #define MBM29LV650UE	0x22D7
@@ -632,6 +636,40 @@ static const struct amd_flash_info jedec_table[] = {
 			ERASEINFO(0x02000,8)
 		}
 	}, {
+		.mfr_id		= 0x1c,
+		.dev_id		= EN29SL800BT,
+		.name		= "Eon EN29SL800BT",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000, 15),
+			ERASEINFO(0x08000, 1),
+			ERASEINFO(0x02000, 2),
+			ERASEINFO(0x04000, 1),
+		}
+	}, {
+		.mfr_id		= 0x1c,
+		.dev_id		= EN29SL800BB,
+		.name		= "Eon EN29SL800BB",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000, 1),
+			ERASEINFO(0x02000, 2),
+			ERASEINFO(0x08000, 1),
+			ERASEINFO(0x10000, 15),
+		}
+	}, {
 		.mfr_id		= MANUFACTURER_FUJITSU,
 		.dev_id		= MBM29F040C,
 		.name		= "Fujitsu MBM29F040C",
@@ -1769,9 +1807,21 @@ static inline u32 jedec_read_mfr(struct map_info *map, __u32 base,
 {
 	map_word result;
 	unsigned long mask;
-	u32 ofs = cfi_build_cmd_addr(0, cfi_interleave(cfi), cfi->device_type);
-	mask = (1 << (cfi->device_type * 8)) -1;
-	result = map_read(map, base + ofs);
+	int bank = 0;
+
+	/* According to JEDEC "Standard Manufacturer's Identification Code"
+	 * (http://www.jedec.org/download/search/jep106W.pdf)
+	 * several first banks can contain 0x7f instead of actual ID
+	 */
+	do {
+		u32 ofs = cfi_build_cmd_addr(0 + (bank << 8),
+					     cfi_interleave(cfi),
+					     cfi->device_type);
+		mask = (1 << (cfi->device_type * 8)) -1;
+		result = map_read(map, base + ofs);
+		bank++;
+	} while ((result.x[0] & mask) == CONTINUATION_CODE);
+
 	return result.x[0] & mask;
 }
 
diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c
index 738aa59..88dbbdf 100644
--- a/drivers/net/dm9000.c
+++ b/drivers/net/dm9000.c
@@ -546,6 +546,7 @@ dm9000_probe(struct platform_device *pdev)
 
 	if (id_val != DM9000_ID) {
 		printk("%s: wrong id: 0x%08x\n", CARDNAME, id_val);
+		ret = -ENODEV;
 		goto release;
 	}
 
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 58c806e..3a16d9c 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -49,4 +49,10 @@ config BATTERY_OLPC
 	help
 	  Say Y to enable support for the battery on the OLPC laptop.
 
+config BATTERY_EM_X270
+	tristate "EM-X270 battery"
+	depends on DA9030 && MACH_EM_X270
+	help
+	  Say Y here to enable support for battery on EM-X270.
+
 endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 6413ded..dc9585e 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_APM_POWER)		+= apm_power.o
 obj-$(CONFIG_BATTERY_DS2760)	+= ds2760_battery.o
 obj-$(CONFIG_BATTERY_PMU)	+= pmu_battery.o
 obj-$(CONFIG_BATTERY_OLPC)	+= olpc_battery.o
+obj-$(CONFIG_BATTERY_EM_X270)	+= em_x270_battery.o
diff --git a/drivers/power/em_x270_battery.c b/drivers/power/em_x270_battery.c
new file mode 100644
index 0000000..2630c68
--- /dev/null
+++ b/drivers/power/em_x270_battery.c
@@ -0,0 +1,579 @@
+/*
+ * Power managemnet implementation for EM-X270
+ *
+ * Copyright (C) 2007 CompuLab, Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * 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.
+ *
+ */
+
+#define DEBUG
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pda_power.h>
+#include <linux/apm-emulation.h>
+#include <linux/da9030.h>
+#include <linux/power_supply.h>
+#include <linux/pm.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include "../i2c/chips/da9030.h"
+
+#define VOLTAGE_MAX_DESIGN	4200000  /* 4.2V in uV */
+#define VOLTAGE_MIN_DESIGN	3000000  /* 3V in uV */
+
+/* #define VCHARGE_MIN_THRESHOLD	72 /\* 4.5V *\/ */
+
+#define VCHARGE_MIN_THRESHOLD	60 /* ?.?V */
+#define VCHARGE_MAX_THRESHOLD	89 /* 5.5V */
+#define TBAT_LOW_THRESHOLD	197 /* 0oC */
+#define TBAT_HIGH_THRESHOLD	78 /* 45oC */
+#define TBAT_RESUME_THRESHOLD	100 /* 35oC */
+#define VBAT_LOW_THRESHOLD	82 /* 3.498V */
+#define VBAT_CRIT_THRESHOLD	62 /* 3.291V */
+
+struct da9030_charger {
+	struct device *dev;
+
+	struct power_supply bat;
+	struct da9030_adc_res adc;
+	struct delayed_work work;
+
+	int interval;
+	int status;
+	int mA;
+	int mV;
+		
+	int is_charging:1;
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debug_file;
+#endif
+};
+
+static unsigned short tbat_readings[] = {
+	300,	244,	200,	178,	163,	152,	144,	137,	131,
+	126,	122,	118,	114,	111,	108,	105,	103,	101,
+	98,	96,	94,	93,	91,	89,	88,	86,	85,
+	83,	82,	81,	79,	78,	77,	76,	75,	74,
+	73,	72,	71,	70,	69,	68,	67,	67,	66,
+	65,	64,	63,	63,	62,	61,	60,	60,	59,
+	58,	58,	57,	57,	56,	55,	55,	54,	53,
+	53,	52,	52,	51,	51,	50,	50,	49,	49,
+	48,	48,	47,	47,	46,	46,	45,	45,	44,
+	44,	43,	43,	42,	42,	41,	41,	41,	40,
+	40,	39,	39,	38,	38,	38,	37,	37,	36,
+	36,	35,	35,	35,	34,	34,	34,	33,	33,
+	32,	32,	32,	31,	31,	30,	30,	30,	29,
+	29,	29,	28,	28,	28,	27,	27,	26,	26,
+	26,	25,	25,	25,	24,	24,	24,	23,	23,
+	23,	22,	22,	21,	21,	21,	20,	20,	20,
+	19,	19,	19,	18,	18,	18,	17,	17,	17,
+	16,	16,	16,	15,	15,	14,	14,	14,	13,
+	13,	13,	12,	12,	12,	11,	11,	11,	10,
+	10,	10,	9,	9,	8,	8,	8,	7,	7,
+	7,	6,	6,	5,	5,	5,	4,	4,	3,
+	3,	3,	2,	2,	1,	1,	1,	0,	0,
+	-1,	-1,	-1,	-2,	-2,	-3,	-3,	-4,	-4,
+	-5,	-5,	-6,	-6,	-6,	-7,	-7,	-8,	-9,
+	-9,	-10,	-10,	-11,	-11,	-12,	-12,	-13,	-14,
+	-14,	-15,	-16,	-16,	-17,	-18,	-18,	-19,	-20,
+	-21,	-21,	-22,	-23,	-24,	-25,	-26,	-27,	-28,
+	-30,	-31,	-32,	-34,	-35,	-37,	-39,	-41,	-44,
+	-47,	-50,	-56,	-64,	
+};	
+
+#ifdef CONFIG_DEBUG_FS
+
+#define REG2VOLT(x) ((((x) * 2650) >> 8) + 2650)
+
+static int debug_show(struct seq_file *s, void *data)
+{
+	struct da9030_charger *charger = s->private;
+	
+	seq_printf(s, "charger is %s\n",
+		   charger->status & CHRG_CHARGER_ENABLE ? "on" : "off");
+	if (charger->status & CHRG_CHARGER_ENABLE) {
+		seq_printf(s, "iset = %dmA, vset = %dmV\n",
+			   charger->mA, charger->mV);
+	}
+
+	seq_printf(s, "vbat_res = %d (%dmV)\n",
+		   charger->adc.vbat_res, REG2VOLT(charger->adc.vbat_res));
+	seq_printf(s, "vbatmin_res = %d (%dmV)\n",
+		   charger->adc.vbatmin_res,
+		   REG2VOLT(charger->adc.vbatmin_res));
+	seq_printf(s, "vbatmintxon = %d (%dmV)\n",
+		   charger->adc.vbatmintxon,
+		   REG2VOLT(charger->adc.vbatmintxon));
+	seq_printf(s, "ichmax_res = %d\n", charger->adc.ichmax_res);
+	seq_printf(s, "ichmin_res = %d\n", charger->adc.ichmin_res);
+	seq_printf(s, "ichaverage_res = %d\n", charger->adc.ichaverage_res);
+	seq_printf(s, "vchmax_res = %d (%dmV)\n",
+		   charger->adc.vchmax_res,
+		   REG2VOLT(charger->adc.vchmax_res));
+	seq_printf(s, "vchmin_res = %d (%dmV)\n",
+		   charger->adc.vchmin_res,
+		   REG2VOLT(charger->adc.vchmin_res));
+	seq_printf(s, "tbat_res = %d (%doC\n", charger->adc.tbat_res,
+		   tbat_readings[charger->adc.tbat_res]);
+	seq_printf(s, "adc_in4_res = %d\n", charger->adc.adc_in4_res);
+	seq_printf(s, "adc_in5_res = %d\n", charger->adc.adc_in5_res);
+
+	return 0;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, debug_show, inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+	.open		= debug_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static struct dentry* da9030_create_debugfs(struct da9030_charger *charger)
+{
+	charger->debug_file = debugfs_create_file("charger", 0666, 0, charger,
+						 &debug_fops);
+	return charger->debug_file;
+}
+
+static void da9030_remove_debugfs(struct da9030_charger *charger)
+{
+	debugfs_remove(charger->debug_file);
+}
+#else
+#define da9030_create_debugfs(x)	NULL
+#define da9030_remove_debugfs(x)	do {} while(0)
+#endif
+
+static void da9030_set_charge(struct da9030_charger *charger, int on)
+{
+/* 	int status = da9030_get_status(); */
+	if (on) {
+		pr_debug("%s: enabling charger\n", __FUNCTION__);
+		da9030_set_thresholds(TBAT_HIGH_THRESHOLD,
+				      TBAT_RESUME_THRESHOLD,
+				      TBAT_LOW_THRESHOLD);
+		da9030_set_charger(1, 1000, 4200);
+		charger->is_charging = 1;
+	}
+	else {
+		/* disable charger */
+		pr_debug("%s: disabling charger\n", __FUNCTION__);
+		da9030_set_charger(0, 0, 0);
+		charger->is_charging = 0;
+	}
+}
+
+static void da9030_charging_monitor(struct work_struct *work)
+{
+	struct da9030_charger *charger;
+	int is_on;
+	unsigned int mA, mV;
+	
+	charger = container_of(work, struct da9030_charger, work.work);
+	
+	da9030_get_charger(&is_on, &mA, &mV);
+	da9030_read_adc(&charger->adc);
+
+	charger->status = da9030_get_status();
+	charger->mA = mA;
+	charger->mV = mV;
+
+	/* we wake or boot with external power on */
+	if (!is_on && (charger->status & STATUS_CHDET)) {
+		da9030_set_charge(charger, 1);
+		return;
+	}
+
+	if (is_on) {
+/* 		pr_info("%s: mA = %d, mV = %d\n", __FUNCTION__, mA, mV); */
+/* 		pr_info("%s: vchmin_res = %d, vchmax_res = %d\n", */
+/* 			__FUNCTION__, charger->adc.vchmin_res, */
+/* 			charger->adc.vchmax_res); */
+/* 		pr_info("%s: tbat_res = %d\n", */
+/* 			__FUNCTION__, charger->adc.tbat_res); */
+		if (charger->adc.vbat_res > VBAT_LOW_THRESHOLD) {
+			/* update VBAT threshlods ? */
+			da9030_set_reg(VBATMON, VBAT_LOW_THRESHOLD);
+		}
+		if (charger->adc.vchmax_res > VCHARGE_MAX_THRESHOLD ||
+		    charger->adc.vchmin_res < VCHARGE_MIN_THRESHOLD ||
+		    /* Tempreture readings are negative */
+		    charger->adc.tbat_res < TBAT_HIGH_THRESHOLD ||
+		    charger->adc.tbat_res > TBAT_LOW_THRESHOLD) {
+			/* disable charger */
+			da9030_set_charge(charger, 0);
+		}
+	}
+
+	/* reschedule for the next time */
+	schedule_delayed_work(&charger->work, charger->interval);
+}
+
+void da9030_battery_release(struct device * dev)
+{
+}
+
+static void da9030_external_power_changed(struct power_supply *psy)
+{
+/* 	struct da9030_charger *charger; */
+
+/* 	charger = container_of(psy, struct da9030_charger, bat); */
+/* 	pr_info("%s:\n", __FUNCTION__); */
+/* 	da9030_set_charge(charger); */
+}
+
+struct da9030_battery_thresh {
+	int voltage;
+	int percentage;
+};
+
+static struct da9030_battery_thresh vbat_ranges[] = {
+	{ 150, 100},
+	{ 149,  99},
+	{ 148,  98},
+	{ 147,  98},
+	{ 146,  97},
+	{ 145,  96},
+	{ 144,  96},
+	{ 143,  95},
+	{ 142,  94},
+	{ 141,  93},
+	{ 140,  92},
+	{ 139,  91},
+	{ 138,  90},
+	{ 137,  90},
+	{ 136,  89},
+	{ 135,  88},
+	{ 134,  88},
+	{ 133,  87},
+	{ 132,  86},
+	{ 131,  85},
+	{ 130,  83},
+	{ 129,  82},
+	{ 128,  81},
+	{ 127,  81},
+	{ 126,  80},
+	{ 125,  75},
+	{ 124,  74},
+	{ 123,  73},
+	{ 122,  70},
+	{ 121,  66},
+	{ 120,  65},
+	{ 119,  64},
+	{ 118,  64},
+	{ 117,  63},
+	{ 116,  59},
+	{ 115,  58},
+	{ 114,  57},
+	{ 113,  57},
+	{ 112,  56},
+	{ 111,  50},
+	{ 110,  49},
+	{ 109,  49},
+	{ 108,  48},
+	{ 107,  48},
+	{ 106,  33},
+	{ 105,  32},
+	{ 104,  32},
+	{ 103,  32},
+	{ 102,  31},
+	{ 101,  16},
+	{ 100,  15},
+	{  99,  15},
+	{  98,  15},
+	{  97,  10},
+	{  96,   9},
+	{  95,   7},
+	{  94,   3},
+	{  93,   0},
+};
+
+static enum power_supply_property da9030_bat_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_AVG,
+	POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+};
+
+static void da9030_bat_check_status(union power_supply_propval *val)
+{
+	int charger_on;
+	int status = da9030_get_status();
+
+	da9030_get_charger(&charger_on, 0, 0);
+
+	/* FIXME: below code is very crude approximation of actual
+	   states, we need to take into account voltage and current
+	   measurements to determine actual charger state */
+	if (status & STATUS_CHDET) {
+		if (charger_on) {
+			val->intval = POWER_SUPPLY_STATUS_CHARGING;
+		}
+		else {
+			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+		}
+	}
+	else {
+		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+	}
+}
+
+static void da9030_bat_check_health(union power_supply_propval *val)
+{
+	int fault_log = da9030_get_fault_log();
+
+	if (fault_log & FAULT_LOG_OVER_TEMP) {
+		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+	}
+	else if (fault_log & FAULT_LOG_VBAT_OVER) {
+		val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+	}
+	else {
+		val->intval = POWER_SUPPLY_HEALTH_GOOD;
+	}
+}
+
+static int vbat_interpolate(int reg)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(vbat_ranges); i++ )
+		if (vbat_ranges[i].voltage == reg) {
+			pr_debug("%s: voltage = %d, percentage = %d\n",
+				__FUNCTION__, vbat_ranges[i].voltage,
+				vbat_ranges[i].percentage);
+			return vbat_ranges[i].percentage;
+		}
+	
+	return 0;
+}
+
+static int da9030_bat_get_property(struct power_supply *psy,
+				   enum power_supply_property psp,
+				   union power_supply_propval *val)
+{
+	u32 reg;
+	struct da9030_charger *charger;
+	charger = container_of(psy, struct da9030_charger, bat);
+
+	switch(psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		da9030_bat_check_status(val);
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		da9030_bat_check_health(val);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = VOLTAGE_MAX_DESIGN;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+		val->intval = VOLTAGE_MIN_DESIGN;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		reg = charger->adc.vbat_res;
+		/* V = (reg / 256) * 2.65 + 2.65 (V) */
+		val->intval = ((reg * 2650000) >> 8) + 2650000;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_AVG:
+		reg = charger->adc.ichaverage_res;
+		val->intval = reg; /* reg */
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		reg = charger->adc.vbat_res;
+		val->intval = vbat_interpolate(reg);
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		reg = charger->adc.tbat_res;
+		val->intval = tbat_readings[reg];
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = "MaxPower";
+		pr_debug("%s: MFG = %s\n", __FUNCTION__, val->strval);
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = "LP555597P6H-FPS";
+		pr_debug("%s: MODEL = %s\n", __FUNCTION__, val->strval);
+		break;
+	default: break;
+	}
+	
+	return 0;
+}
+
+static void da9030_setup_battery(struct power_supply *bat)
+{
+	bat->name = "em-x270-battery";
+	bat->type = POWER_SUPPLY_TYPE_BATTERY;
+	bat->properties = da9030_bat_props;
+	bat->num_properties = ARRAY_SIZE(da9030_bat_props);
+	bat->get_property = da9030_bat_get_property;
+	bat->use_for_apm = 1;
+	bat->external_power_changed = da9030_external_power_changed;
+};
+
+static void da9030_chiover_callback(int event, void *_charger)
+{
+	/* disable charger */
+	struct da9030_charger *charger = _charger;
+	da9030_set_charge(charger, 0);
+}
+
+static void da9030_tbat_callback(int event, void *_charger)
+{
+	/* disable charger */
+	struct da9030_charger *charger = _charger;
+	da9030_set_charge(charger, 0);
+}
+
+static void da9030_vbat_callback(int event, void *_charger)
+{
+	struct da9030_charger *charger = _charger;
+	da9030_read_adc(&charger->adc);
+
+	if (charger->is_charging) {
+		if (charger->adc.vbat_res < VBAT_LOW_THRESHOLD) {
+			/* set VBAT threshold for critical */
+			da9030_set_reg(VBATMON, VBAT_CRIT_THRESHOLD);
+		}
+		else if (charger->adc.vbat_res < VBAT_CRIT_THRESHOLD) {
+			/* notify the system of battery critical */
+			apm_queue_event(APM_CRITICAL_SUSPEND);
+		}
+	}
+}
+
+static void da9030_ccto_callback(int event, void *_charger)
+{
+	/* x3 */
+}
+
+static void da9030_tcto_callback(int event, void *_charger)
+{
+	/* x3 */
+}
+
+static void da9030_chdet_callback(int event, void *_charger)
+{
+	struct da9030_charger *charger = _charger;
+	int status = da9030_get_status();
+	da9030_set_charge(charger, !!(status & CHRG_CHARGER_ENABLE));
+}
+
+static int da9030_battery_probe(struct platform_device *pdev)
+{
+	struct da9030_charger *charger;
+
+	pr_debug("%s\n", __FUNCTION__);
+	charger = kzalloc(sizeof(*charger), GFP_KERNEL);
+	if (charger == NULL) {
+		return -ENOMEM;
+	}
+
+	charger->dev = &pdev->dev;
+
+	charger->interval = 10 * HZ; /* 10 seconds between monotor runs */
+	da9030_setup_battery(&charger->bat);
+
+	platform_set_drvdata(pdev, charger);
+
+	da9030_enable_adc();
+
+	INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor);
+	schedule_delayed_work(&charger->work, charger->interval);
+
+	charger->debug_file = da9030_create_debugfs(charger);
+
+	da9030_setup_battery(&charger->bat);
+
+	da9030_register_callback(DA9030_IRQ_CHDET,
+				 da9030_chdet_callback,
+				 charger);
+	da9030_register_callback(DA9030_IRQ_VBATMON,
+				 da9030_vbat_callback,
+				 charger);
+
+	/* critical condition events */
+	da9030_register_callback(DA9030_IRQ_CHIOVER,
+				 da9030_chiover_callback,
+				 charger);
+	da9030_register_callback(DA9030_IRQ_TBAT,
+				 da9030_tbat_callback,
+				 charger);
+	
+	/* timer events */
+	da9030_register_callback(DA9030_IRQ_TCTO,
+				 da9030_tcto_callback,
+				 charger);
+	da9030_register_callback(DA9030_IRQ_CCTO,
+				 da9030_ccto_callback,
+				 charger);
+
+	power_supply_register(&pdev->dev, &charger->bat);
+
+	return 0;
+}
+
+static int da9030_battery_remove(struct platform_device *dev)
+{
+	struct da9030_charger *charger = platform_get_drvdata(dev);
+
+	pr_debug("%s\n", __FUNCTION__);
+	da9030_remove_debugfs(charger);
+	cancel_delayed_work(&charger->work);
+	power_supply_unregister(&charger->bat);
+	kfree(charger);
+	return 0;
+}
+
+static struct platform_driver da9030_battery_driver = {
+	.driver	= {
+		.name    = "da9030-battery",
+		.owner	 = THIS_MODULE,
+	},
+	.probe = da9030_battery_probe,
+	.remove = da9030_battery_remove,
+};
+
+static int da9030_battery_init(void)
+{
+	pr_debug("%s\n", __FUNCTION__);
+
+	return platform_driver_register(&da9030_battery_driver);
+}
+
+static void da9030_battery_exit(void)
+{
+	pr_debug("%s\n", __FUNCTION__);
+
+	platform_driver_unregister(&da9030_battery_driver);
+}
+
+module_init(da9030_battery_init);
+module_exit(da9030_battery_exit);
+
+MODULE_DESCRIPTION("DA9030 charger driver");
+MODULE_AUTHOR("Mike Rapoport");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 767aed5..4c44a7a 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -195,6 +195,26 @@ config USB_M66592
 	default USB_GADGET
 	select USB_GADGET_SELECTED
 
+config USB_GADGET_PXA27X
+	boolean "PXA 27x"
+	depends on ARCH_PXA && PXA27x
+	help
+	   Intel's PXA 27x series XScale ARM-5TE processors include
+	   an integrated full speed USB 1.1 device controller.
+
+	   It has 23 endpoints, as well as endpoint zero (for control
+	   transfers).
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "pxa27x_udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_PXA27X
+	tristate
+	depends on USB_GADGET_PXA27X
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_GOKU
 	boolean "Toshiba TC86C001 'Goku-S'"
 	depends on PCI
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 1bc0f03..b8743bf 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_DUMMY_HCD)	+= dummy_hcd.o
 obj-$(CONFIG_USB_NET2280)	+= net2280.o
 obj-$(CONFIG_USB_AMD5536UDC)	+= amd5536udc.o
 obj-$(CONFIG_USB_PXA2XX)	+= pxa2xx_udc.o
+obj-$(CONFIG_USB_PXA27X)	+= pxa27x_udc.o
 obj-$(CONFIG_USB_GOKU)		+= goku_udc.o
 obj-$(CONFIG_USB_OMAP)		+= omap_udc.o
 obj-$(CONFIG_USB_LH7A40X)	+= lh7a40x_udc.o
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 3aa46cf..d7d5550 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -228,14 +228,19 @@ find_ep (struct usb_gadget *gadget, const char *name)
  *
  * On failure, this returns a null endpoint descriptor.
  */
-struct usb_ep * __devinit usb_ep_autoconfig (
+struct usb_ep * usb_ep_autoconfig (
 	struct usb_gadget		*gadget,
-	struct usb_endpoint_descriptor	*desc
+	struct usb_endpoint_descriptor	*desc,
+	struct usb_endpoint_config *epconfig, int numconfigs
 )
 {
 	struct usb_ep	*ep;
 	u8		type;
 
+	/* Use device specific ep allocation code if provided */
+	if (gadget->ops->ep_alloc)
+		return gadget->ops->ep_alloc(gadget, desc, epconfig, numconfigs);
+
 	type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
 
 	/* First, apply chip-specific "best usage" knowledge.
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 593e235..87aa9fb 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -1350,6 +1350,10 @@ static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req)
 	/* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */
 }
 
+#ifdef CONFIG_USB_GADGET_PXA27X
+int write_ep0_zlp(void);
+#endif
+
 static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req)
 {
 	struct eth_dev          *dev = ep->driver_data;
@@ -1360,6 +1364,10 @@ static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req)
 	status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf);
 	if (status < 0)
 		ERROR(dev, "%s: rndis parse error %d\n", __FUNCTION__, status);
+
+#ifdef CONFIG_USB_GADGET_PXA27X
+	write_ep0_zlp();
+#endif
 	spin_unlock(&dev->lock);
 }
 
@@ -2287,7 +2295,8 @@ eth_bind (struct usb_gadget *gadget)
 	struct eth_dev		*dev;
 	struct net_device	*net;
 	u8			cdc = 1, zlp = 1, rndis = 1;
-	struct usb_ep		*in_ep, *out_ep, *status_ep = NULL;
+	struct usb_ep		*in_ep = NULL , *out_ep = NULL, *status_ep = NULL;
+	struct usb_endpoint_config ep_config[2];
 	int			status = -ENOMEM;
 	int			gcnum;
 
@@ -2386,7 +2395,30 @@ eth_bind (struct usb_gadget *gadget)
 
 	/* all we really need is bulk IN/OUT */
 	usb_ep_autoconfig_reset (gadget);
-	in_ep = usb_ep_autoconfig (gadget, &fs_source_desc);
+
+	ep_config[0].config = DEV_CONFIG_VALUE;
+#if 	defined(DEV_CONFIG_CDC)
+	ep_config[0].interface = data_intf.bInterfaceNumber;
+	ep_config[0].altinterface = data_intf.bAlternateSetting;
+#else 	/* DEV_CONFIG_SUBSET */
+	ep_config[0].interface = subset_data_intf.bInterfaceNumber;
+	ep_config[0].altinterface = subset_data_intf.bAlternateSetting;
+#endif
+
+#ifdef	CONFIG_USB_ETH_RNDIS
+	ep_config[1].config = DEV_RNDIS_CONFIG_VALUE;
+#ifdef CONFIG_USB_GADGET_PXA27X
+	ep_config[1].interface = 0;
+#else
+	ep_config[1].interface = rndis_data_intf.bInterfaceNumber;
+#endif
+	ep_config[1].altinterface = rndis_data_intf.bAlternateSetting;
+
+	in_ep = usb_ep_autoconfig(gadget, &fs_source_desc, &ep_config[0], 2);
+#else
+	in_ep = usb_ep_autoconfig(gadget, &fs_source_desc, &ep_config[0], 1);
+#endif
+
 	if (!in_ep) {
 autoconf_fail:
 		dev_err (&gadget->dev,
@@ -2396,7 +2428,12 @@ autoconf_fail:
 	}
 	in_ep->driver_data = in_ep;	/* claim */
 
-	out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
+#ifdef CONFIG_USB_ETH_RNDIS
+	out_ep = usb_ep_autoconfig(gadget, &fs_sink_desc, &ep_config[0], 2);
+#else
+	out_ep = usb_ep_autoconfig(gadget, &fs_sink_desc, &ep_config[0], 1);
+#endif
+
 	if (!out_ep)
 		goto autoconf_fail;
 	out_ep->driver_data = out_ep;	/* claim */
@@ -2406,7 +2443,25 @@ autoconf_fail:
 	 * Since some hosts expect one, try to allocate one anyway.
 	 */
 	if (cdc || rndis) {
-		status_ep = usb_ep_autoconfig (gadget, &fs_status_desc);
+#ifdef DEV_CONFIG_CDC
+		ep_config[0].config = DEV_CONFIG_VALUE;
+		ep_config[0].interface = control_intf.bInterfaceNumber;
+		ep_config[0].altinterface = control_intf.bAlternateSetting;
+#endif
+#ifdef CONFIG_USB_ETH_RNDIS
+		ep_config[1].config = DEV_RNDIS_CONFIG_VALUE;
+		ep_config[1].interface = rndis_control_intf.bInterfaceNumber;
+		ep_config[1].altinterface = rndis_control_intf.bAlternateSetting;
+#endif
+
+#if defined(DEV_CONFIG_CDC) && defined(CONFIG_USB_ETH_RNDIS)
+		status_ep = usb_ep_autoconfig(gadget, &fs_status_desc, &ep_config[0], 2);
+#elif defined(CONFIG_USB_ETH_RNDIS)
+		status_ep = usb_ep_autoconfig(gadget, &fs_status_desc, &ep_config[1], 1);
+#else
+		status_ep = usb_ep_autoconfig(gadget, &fs_status_desc, &ep_config[0], 1);
+#endif
+
 		if (status_ep) {
 			status_ep->driver_data = status_ep;	/* claim */
 		} else if (rndis) {
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index 965ad7b..b9cd8c9 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -3841,6 +3841,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
 	struct usb_ep		*ep;
 	struct usb_request	*req;
 	char			*pathbuf, *p;
+	struct usb_endpoint_config ep_config;
 
 	fsg->gadget = gadget;
 	set_gadget_data(gadget, fsg);
@@ -3911,21 +3912,25 @@ static int __init fsg_bind(struct usb_gadget *gadget)
 	}
 
 	/* Find all the endpoints we will use */
+	ep_config.config = CONFIG_VALUE;
+	ep_config.interface = intf_desc.bInterfaceNumber;
+	ep_config.altinterface = intf_desc.bAlternateSetting;
+
 	usb_ep_autoconfig_reset(gadget);
-	ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc);
+	ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc, &ep_config, 1);
 	if (!ep)
 		goto autoconf_fail;
 	ep->driver_data = fsg;		// claim the endpoint
 	fsg->bulk_in = ep;
 
-	ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc);
+	ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc, &ep_config, 1);
 	if (!ep)
 		goto autoconf_fail;
 	ep->driver_data = fsg;		// claim the endpoint
 	fsg->bulk_out = ep;
 
 	if (transport_is_cbi()) {
-		ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc);
+		ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc, &ep_config, 1);
 		if (!ep)
 			goto autoconf_fail;
 		ep->driver_data = fsg;		// claim the endpoint
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
new file mode 100644
index 0000000..d4270d4
--- /dev/null
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -0,0 +1,2387 @@
+/*
+ * Handles the Intel 27x USB Device Controller (UDC)
+ *
+ * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker)
+ * Copyright (C) 2003 Robert Schwebel, Pengutronix
+ * Copyright (C) 2003 Benedikt Spranger, Pengutronix
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2003 Joshua Wise
+ * Copyright (C) 2004 Intel Corporation
+ * Copyright (C) 2005 SDG Systems, LLC  (Aric Blumer)
+ * Copyright (C) 2005-2006 Openedhand Ltd. (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; 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
+ *
+ */
+
+#undef	DEBUG
+//#define DEBUG 1
+//#define	VERBOSE	DBG_VERBOSE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <asm/unaligned.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb_gadget.h>
+
+#include <asm/arch/udc.h>
+
+/*
+ * This driver handles the USB Device Controller (UDC) in Intel's PXA 27x
+ * series processors.
+ *
+ * Such controller drivers work with a gadget driver.  The gadget driver
+ * returns descriptors, implements configuration and data protocols used
+ * by the host to interact with this device, and allocates endpoints to
+ * the different protocol interfaces.  The controller driver virtualizes
+ * usb hardware so that the gadget drivers will be more portable.
+ *
+ * This UDC hardware wants to implement a bit too much USB protocol. The
+ * biggest issue is that the endpoints have to be setup before the controller
+ * can be enabled and each endpoint can only have one configuration, interface
+ * and alternative interface number. Once enabled, these cannot be changed
+ * without a controller reset.
+ *
+ * Intel Errata #22 mentions issues when changing alternate interface.
+ * The exact meaning of this remains uncertain as gadget drivers using alternate
+ * interfaces such as CDC-Ethernet appear to work...
+ */
+
+#define	DRIVER_VERSION	"01-01-2006"
+#define	DRIVER_DESC	"PXA 27x USB Device Controller driver"
+
+static const char driver_name [] = "pxa27x_udc";
+
+static const char ep0name [] = "ep0";
+
+
+#define	USE_DMA
+//#undef USE_DMA
+
+#ifdef CONFIG_PROC_FS
+#define	UDC_PROC_FILE
+#endif
+
+#include "pxa27x_udc.h"
+
+#ifdef	USE_DMA
+static int use_dma = 1;
+module_param(use_dma, bool, 0);
+MODULE_PARM_DESC(use_dma, "true to use dma");
+
+static void dma_nodesc_handler(int dmach, void *_ep);
+static void kick_dma(struct pxa27x_ep *ep, struct pxa27x_request *req);
+
+#define	DMASTR " (dma support)"
+
+#else	/* !USE_DMA */
+#define	DMASTR " (pio only)"
+#endif
+
+#define UDCISR0_IR0	 0x3
+#define UDCISR_INT_MASK	 (UDC_INT_FIFOERROR | UDC_INT_PACKETCMP)
+#define UDCICR_INT_MASK	 UDCISR_INT_MASK
+
+#define UDCCSR_MASK	(UDCCSR_FST | UDCCSR_DME)
+
+static void pxa27x_ep_fifo_flush(struct usb_ep *ep);
+static void nuke(struct pxa27x_ep *, int status);
+static void udc_init_ep(struct pxa27x_udc *dev);
+
+
+/*
+ * Endpoint Functions
+ */
+static void pio_irq_enable(int ep_num)
+{
+        if (ep_num < 16)
+                UDCICR0 |= 3 << (ep_num * 2);
+        else {
+                ep_num -= 16;
+                UDCICR1 |= 3 << (ep_num * 2);
+	}
+}
+
+static void pio_irq_disable(int ep_num)
+{
+        ep_num &= 0xf;
+        if (ep_num < 16)
+                UDCICR0 &= ~(3 << (ep_num * 2));
+        else {
+                ep_num -= 16;
+                UDCICR1 &= ~(3 << (ep_num * 2));
+        }
+}
+
+/* The UDCCR reg contains mask and interrupt status bits,
+ * so using '|=' isn't safe as it may ack an interrupt.
+ */
+#define UDCCR_MASK_BITS (UDCCR_OEN | UDCCR_UDE)
+
+static inline void udc_set_mask_UDCCR(int mask)
+{
+	UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS);
+}
+
+static inline void udc_clear_mask_UDCCR(int mask)
+{
+	UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS);
+}
+
+static inline void udc_ack_int_UDCCR(int mask)
+{
+	/* udccr contains the bits we dont want to change */
+	__u32 udccr = UDCCR & UDCCR_MASK_BITS;
+
+	UDCCR = udccr | (mask & ~UDCCR_MASK_BITS);
+}
+
+/*
+ * Endpoint enable/disable
+ *
+ * Not much to do here as the ep_alloc function sets up most things. Once
+ * enabled, not much of the pxa27x configuration can be changed.
+ *
+ */
+static int pxa27x_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct pxa27x_virt_ep *virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep);
+	struct pxa27x_ep *ep = virt_ep->pxa_ep;
+	struct pxa27x_udc       *dev;
+
+	if (!_ep || !desc || _ep->name == ep0name
+			|| desc->bDescriptorType != USB_DT_ENDPOINT
+			|| ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) {
+		dev_err(ep->dev->dev, "%s, bad ep or descriptor\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	/* xfer types must match, except that interrupt ~= bulk */
+	if( ep->ep_type != USB_ENDPOINT_XFER_BULK
+			&& desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
+		dev_err(ep->dev->dev, "%s, %s type mismatch\n", __FUNCTION__, _ep->name);
+		return -EINVAL;
+	}
+
+	/* hardware _could_ do smaller, but driver doesn't */
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+				&& le16_to_cpu (desc->wMaxPacketSize)
+						!= BULK_FIFO_SIZE)
+			|| !desc->wMaxPacketSize) {
+		dev_err(ep->dev->dev, "%s, bad %s maxpacket\n", __FUNCTION__, _ep->name);
+		return -ERANGE;
+	}
+
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		dev_err(ep->dev->dev, "%s, bogus device state\n", __FUNCTION__);
+		return -ESHUTDOWN;
+	}
+
+	ep->desc = desc;
+	ep->dma = -1;
+	ep->stopped = 0;
+	ep->pio_irqs = ep->dma_irqs = 0;
+	ep->usb_ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	/* flush fifo (mostly for OUT buffers) */
+	pxa27x_ep_fifo_flush(_ep);
+
+	/* ... reset halt state too, if we could ... */
+
+#ifdef USE_DMA
+	/* for (some) bulk and ISO endpoints, try to get a DMA channel and
+	 * bind it to the endpoint.  otherwise use PIO.
+	 */
+	dev_dbg(ep->dev->dev, "%s: called attributes=%d\n", __FUNCTION__, ep->ep_type);
+	switch (ep->ep_type) {
+	case USB_ENDPOINT_XFER_ISOC:
+		if (le16_to_cpu(desc->wMaxPacketSize) % 32)
+			break;
+		// fall through
+	case USB_ENDPOINT_XFER_BULK:
+		if (!use_dma || !ep->reg_drcmr)
+			break;
+		ep->dma = pxa_request_dma((char *)_ep->name, (le16_to_cpu(desc->wMaxPacketSize) > 64)
+				? DMA_PRIO_MEDIUM : DMA_PRIO_LOW, dma_nodesc_handler, ep);
+		if (ep->dma >= 0) {
+			*ep->reg_drcmr = DRCMR_MAPVLD | ep->dma;
+			dev_dbg(ep->dev->dev, "%s using dma%d\n", _ep->name, ep->dma);
+		}
+	default:
+		break;
+	}
+#endif
+	DBG(DBG_VERBOSE, "enabled %s\n", _ep->name);
+	return 0;
+}
+
+static int pxa27x_ep_disable(struct usb_ep *_ep)
+{
+	struct pxa27x_virt_ep *virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep);
+	struct pxa27x_ep *ep = virt_ep->pxa_ep;
+	unsigned long flags;
+
+	if (!_ep || !ep->desc) {
+		dev_err(ep->dev->dev, "%s, %s not enabled\n", __FUNCTION__,
+			_ep ? _ep->name : NULL);
+		return -EINVAL;
+	}
+	local_irq_save(flags);
+	nuke(ep, -ESHUTDOWN);
+
+#ifdef	USE_DMA
+	if (ep->dma >= 0) {
+		*ep->reg_drcmr = 0;
+		pxa_free_dma(ep->dma);
+		ep->dma = -1;
+	}
+#endif
+
+	/* flush fifo (mostly for IN buffers) */
+	pxa27x_ep_fifo_flush(_ep);
+
+	ep->desc = 0;
+	ep->stopped = 1;
+
+	local_irq_restore(flags);
+	DBG(DBG_VERBOSE, "%s disabled\n", _ep->name);
+	return 0;
+}
+
+
+
+/* for the pxa27x, these can just wrap kmalloc/kfree.  gadget drivers
+ * must still pass correctly initialized endpoints, since other controller
+ * drivers may care about how it's currently set up (dma issues etc).
+ */
+
+/*
+ * 	pxa27x_ep_alloc_request - allocate a request data structure
+ */
+static struct usb_request *
+pxa27x_ep_alloc_request(struct usb_ep *_ep, unsigned gfp_flags)
+{
+	struct pxa27x_request *req;
+
+	req = kzalloc(sizeof *req, gfp_flags);
+	if (!req)
+		return 0;
+
+	INIT_LIST_HEAD(&req->queue);
+	return &req->req;
+}
+
+
+/*
+ * 	pxa27x_ep_free_request - deallocate a request data structure
+ */
+static void
+pxa27x_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct pxa27x_request *req;
+
+	req = container_of(_req, struct pxa27x_request, req);
+	WARN_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *	done - retire a request; caller blocked irqs
+ */
+static void done(struct pxa27x_ep *ep, struct pxa27x_request *req, int status)
+{
+	list_del_init(&req->queue);
+	if (likely (req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (status && status != -ESHUTDOWN)
+		DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n",
+			ep->usb_ep->name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	/* don't modify queue heads during completion callback */
+	req->req.complete(ep->usb_ep, &req->req);
+}
+
+
+static inline void ep0_idle(struct pxa27x_udc *dev)
+{
+	dev->ep0state = EP0_IDLE;
+}
+
+static int write_packet(volatile u32 *uddr, struct pxa27x_request *req, unsigned max)
+{
+	u32		*buf;
+	int	length, count, remain;
+
+	buf = (u32*)(req->req.buf + req->req.actual);
+	prefetch(buf);
+
+	/* how big will this packet be? */
+	length = min(req->req.length - req->req.actual, max);
+	req->req.actual += length;
+
+	remain = length & 0x3;
+	count = length & ~(0x3);
+
+	//dev_dbg(ep->dev->dev, "Length %d, Remain %d, Count %d\n",length, remain, count);
+
+	while (likely(count)) {
+		//dev_dbg(ep->dev->dev, "Sending:0x%x\n", *buf);
+		*uddr = *buf++;
+		count -= 4;
+	}
+
+	if (remain) {
+		volatile u8* reg=(u8*)uddr;
+		char *rd =(u8*)buf;
+
+		while (remain--) {
+			*reg=*rd++;
+		}
+	}
+
+	return length;
+}
+
+/*
+ * write to an IN endpoint fifo, as many packets as possible.
+ * irqs will use this to write the rest later.
+ * caller guarantees at least one packet buffer is ready (or a zlp).
+ */
+static int
+write_fifo(struct pxa27x_ep *ep, struct pxa27x_request *req)
+{
+	unsigned max;
+
+	max = le16_to_cpu(ep->desc->wMaxPacketSize);
+	do {
+		int count, is_last, is_short;
+
+		//dev_dbg(ep->dev->dev, "write_fifo7 %x\n", *ep->reg_udccsr);
+
+		if (*ep->reg_udccsr & UDCCSR_PC)	{
+			//dev_dbg(ep->dev->dev, "Transmit Complete\n");
+			*ep->reg_udccsr = UDCCSR_PC | (*ep->reg_udccsr & UDCCSR_MASK);
+		}
+
+		if (*ep->reg_udccsr & UDCCSR_TRN)	{
+			//dev_dbg(ep->dev->dev, "Clearing Underrun\n");
+			*ep->reg_udccsr = UDCCSR_TRN | (*ep->reg_udccsr & UDCCSR_MASK);
+		}
+		//dev_dbg(ep->dev->dev, "write_fifo8 %x\n", *ep->reg_udccsr);
+
+		count = write_packet(ep->reg_udcdr, req, max);
+
+		/* last packet is usually short (or a zlp) */
+		if (unlikely (count != max))
+			is_last = is_short = 1;
+		else {
+			if (likely(req->req.length != req->req.actual)
+					|| req->req.zero)
+				is_last = 0;
+			else
+				is_last = 1;
+			/* interrupt/iso maxpacket may not fill the fifo */
+			is_short = unlikely (max < ep->fifo_size);
+		}
+
+		//dev_dbg(ep->dev->dev, "write_fifo0 %x\n", *ep->reg_udccsr);
+
+		dev_dbg(ep->dev->dev, "wrote %s count:%d bytes%s%s %d left %p\n",
+			ep->usb_ep->name, count,
+			is_last ? "/L" : "", is_short ? "/S" : "",
+			req->req.length - req->req.actual, &req->req);
+
+		/* let loose that packet. maybe try writing another one,
+		 * double buffering might work.
+		 */
+
+		if (is_short)
+			*ep->reg_udccsr = UDCCSR_SP | (*ep->reg_udccsr & UDCCSR_MASK);
+
+		dev_dbg(ep->dev->dev, "write_fifo0.5 %x\n", *ep->reg_udccsr);
+
+		/* requests complete when all IN data is in the FIFO */
+		if (is_last) {
+			done(ep, req, 0);
+			if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) {
+				pio_irq_disable(ep->pxa_ep_num);
+				//dev_dbg(ep->dev->dev, "write_fifo1 %x\n", *ep->reg_udccsr);
+#ifdef USE_DMA
+				/* unaligned data and zlps couldn't use dma */
+				if (unlikely(!list_empty(&ep->queue))) {
+					req = list_entry(ep->queue.next,
+						struct pxa27x_request, queue);
+					kick_dma(ep,req);
+					return 0;
+				}
+#endif
+			}
+			//dev_dbg(ep->dev->dev, "write_fifo2 %x\n", *ep->reg_udccsr);
+			return 1;
+		}
+
+		// TODO experiment: how robust can fifo mode tweaking be?
+		// double buffering is off in the default fifo mode, which
+		// prevents TFS from being set here.
+
+	} while (*ep->reg_udccsr & UDCCSR_FS);
+	//dev_dbg(ep->dev->dev, "write_fifo2 %x\n", *ep->reg_udccsr);
+	return 0;
+}
+
+/* caller asserts req->pending (ep0 irq status nyet cleared); starts
+ * ep0 data stage.  these chips want very simple state transitions.
+ */
+static inline
+void ep0start(struct pxa27x_udc *dev, u32 flags, const char *tag)
+{
+	UDCCSR0 = flags|UDCCSR0_SA|UDCCSR0_OPC;
+	UDCISR0 = UDCICR_INT(0, UDC_INT_FIFOERROR | UDC_INT_PACKETCMP);
+	dev->req_pending = 0;
+	DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n",
+		__FUNCTION__, tag, UDCCSR0, flags);
+}
+
+static int
+write_ep0_fifo(struct pxa27x_ep *ep, struct pxa27x_request *req)
+{
+	unsigned	count;
+	int		is_short;
+
+	count = write_packet(&UDCDR0, req, EP0_FIFO_SIZE);
+	ep->dev->stats.write.bytes += count;
+
+	/* last packet "must be" short (or a zlp) */
+	is_short = (count != EP0_FIFO_SIZE);
+
+	DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count,
+		req->req.length - req->req.actual, &req->req);
+
+	if (unlikely (is_short)) {
+		if (ep->dev->req_pending)
+			ep0start(ep->dev, UDCCSR0_IPR, "short IN");
+		else
+			UDCCSR0 = UDCCSR0_IPR;
+
+		count = req->req.length;
+		done(ep, req, 0);
+		ep0_idle(ep->dev);
+#if 0
+		/* This seems to get rid of lost status irqs in some cases:
+		 * host responds quickly, or next request involves config
+		 * change automagic, or should have been hidden, or ...
+		 *
+		 * FIXME get rid of all udelays possible...
+		 */
+		if (count >= EP0_FIFO_SIZE) {
+			count = 100;
+			do {
+				if ((UDCCSR0 & UDCCSR0_OPC) != 0) {
+					/* clear OPC, generate ack */
+					UDCCSR0 = UDCCSR0_OPC;
+					break;
+				}
+				count--;
+				udelay(1);
+			} while (count);
+		}
+#endif
+	} else if (ep->dev->req_pending)
+		ep0start(ep->dev, 0, "IN");
+	return is_short;
+}
+
+
+/*
+ * read_fifo -  unload packet(s) from the fifo we use for usb OUT
+ * transfers and put them into the request.  caller should have made
+ * sure there's at least one packet ready.
+ *
+ * returns true if the request completed because of short packet or the
+ * request buffer having filled (and maybe overran till end-of-packet).
+ */
+static int read_fifo(struct pxa27x_ep *ep, struct pxa27x_request *req)
+{
+	for (;;) {
+		u32		*buf;
+		int	bufferspace, count, is_short;
+
+		/* make sure there's a packet in the FIFO.*/
+		if (unlikely ((*ep->reg_udccsr & UDCCSR_PC) == 0))
+			break;
+		buf =(u32*) (req->req.buf + req->req.actual);
+		prefetchw(buf);
+		bufferspace = req->req.length - req->req.actual;
+
+		/* read all bytes from this packet */
+		if (likely (*ep->reg_udccsr & UDCCSR_BNE)) {
+			count = 0x3ff & *ep->reg_udcbcr;
+			req->req.actual += min(count, bufferspace);
+		} else /* zlp */
+			count = 0;
+
+		is_short = (count < ep->usb_ep->maxpacket);
+		dev_dbg(ep->dev->dev, "read %s udccsr:%02x, count:%d bytes%s req %p %d/%d\n",
+			ep->usb_ep->name, *ep->reg_udccsr, count,
+			is_short ? "/S" : "",
+			&req->req, req->req.actual, req->req.length);
+
+		count = min(count, bufferspace);
+		while (likely (count > 0)) {
+			*buf++ = *ep->reg_udcdr;
+			count -= 4;
+		}
+		dev_dbg(ep->dev->dev, "Buf:0x%p\n", req->req.buf);
+
+		*ep->reg_udccsr =  UDCCSR_PC;
+		/* RPC/RSP/RNE could now reflect the other packet buffer */
+
+		/* completion */
+		if (is_short || req->req.actual == req->req.length) {
+			done(ep, req, 0);
+			if (list_empty(&ep->queue))
+				pio_irq_disable(ep->pxa_ep_num);
+			return 1;
+		}
+
+		/* finished that packet.  the next one may be waiting... */
+	}
+	return 0;
+}
+
+/*
+ * special ep0 version of the above.  no UBCR0 or double buffering; status
+ * handshaking is magic.  most device protocols don't need control-OUT.
+ * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other
+ * protocols do use them.
+ */
+static int read_ep0_fifo(struct pxa27x_ep *ep, struct pxa27x_request *req)
+{
+	u32		*buf, word;
+	unsigned	bufferspace;
+
+	buf = (u32*) (req->req.buf + req->req.actual);
+	bufferspace = req->req.length - req->req.actual;
+
+	while (UDCCSR0 & UDCCSR0_RNE) {
+		word = UDCDR0;
+
+		if (unlikely (bufferspace == 0)) {
+			/* this happens when the driver's buffer
+			 * is smaller than what the host sent.
+			 * discard the extra data.
+			 */
+			if (req->req.status != -EOVERFLOW)
+				dev_info(ep->dev->dev, "%s overflow\n", ep->usb_ep->name);
+			req->req.status = -EOVERFLOW;
+		} else {
+			*buf++ = word;
+			req->req.actual += 4;
+			bufferspace -= 4;
+		}
+	}
+
+	UDCCSR0 = UDCCSR0_OPC ;
+
+	/* completion */
+	if (req->req.actual >= req->req.length)
+		return 1;
+
+	/* finished that packet.  the next one may be waiting... */
+	return 0;
+}
+
+#ifdef	USE_DMA
+
+#define	MAX_IN_DMA	((DCMD_LENGTH + 1) - BULK_FIFO_SIZE)
+static void kick_dma(struct pxa27x_ep *ep, struct pxa27x_request *req)
+{
+	u32	dcmd = 0;
+	u32	len = req->req.length;
+	u32	buf = req->req.dma;
+	u32	fifo = io_v2p((u32)ep->reg_udcdr);
+
+	buf += req->req.actual;
+	len -= req->req.actual;
+	ep->dma_con = 0;
+
+	DMSG("%s: req:0x%p length:%d, actual:%d dma:%d\n",
+			__FUNCTION__, &req->req, req->req.length,
+			req->req.actual,ep->dma);
+
+	/* no-descriptor mode can be simple for bulk-in, iso-in, iso-out */
+	DCSR(ep->dma) = DCSR_NODESC;
+	if (buf & 0x3)
+		DALGN |= 1 << ep->dma;
+	else
+		DALGN &= ~(1 << ep->dma);
+
+	if (ep->dir_in) {
+		DSADR(ep->dma) = buf;
+		DTADR(ep->dma) = fifo;
+		if (len > MAX_IN_DMA) {
+			len= MAX_IN_DMA;
+			ep->dma_con =1 ;
+		} else if (len >= ep->usb_ep->maxpacket) {
+			if ((ep->dma_con = (len % ep->usb_ep->maxpacket) != 0))
+				len = ep->usb_ep->maxpacket;
+		}
+		 dcmd = len | DCMD_BURST32 | DCMD_WIDTH4 | DCMD_ENDIRQEN
+			| DCMD_FLOWTRG | DCMD_INCSRCADDR;
+	} else {
+		DSADR(ep->dma) = fifo;
+		DTADR(ep->dma) = buf;
+		dcmd = len | DCMD_BURST32 | DCMD_WIDTH4 | DCMD_ENDIRQEN
+			| DCMD_FLOWSRC | DCMD_INCTRGADDR;
+	}
+	*ep->reg_udccsr = UDCCSR_DME;
+	DCMD(ep->dma) = dcmd;
+	DCSR(ep->dma) =  DCSR_NODESC | DCSR_EORIRQEN \
+				| ((ep->dir_in) ? DCSR_STOPIRQEN : 0);
+	*ep->reg_drcmr = ep->dma | DRCMR_MAPVLD;
+	DCSR(ep->dma) |= DCSR_RUN;
+}
+
+static void cancel_dma(struct pxa27x_ep *ep)
+{
+	struct pxa27x_request	*req;
+	u32			tmp;
+
+	if (DCSR(ep->dma) == 0 || list_empty(&ep->queue))
+		return;
+
+	DMSG("hehe dma:%d,dcsr:0x%x\n", ep->dma, DCSR(ep->dma));
+	DCSR(ep->dma) = 0;
+	while ((DCSR(ep->dma) & DCSR_STOPSTATE) == 0)
+		cpu_relax();
+
+	req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+	tmp = DCMD(ep->dma) & DCMD_LENGTH;
+	req->req.actual = req->req.length - tmp;
+
+	/* the last tx packet may be incomplete, so flush the fifo.
+	 * FIXME correct req.actual if we can
+	 */
+	*ep->reg_udccsr = UDCCSR_FEF;
+}
+
+static void dma_nodesc_handler(int dmach, void *_ep)
+{
+	struct pxa27x_ep	*ep = _ep;
+	struct pxa27x_request	*req, *req_next;
+	u32			dcsr, tmp, completed;
+
+	local_irq_disable();
+
+	req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+
+	DMSG("%s, buf:0x%p\n",__FUNCTION__, req->req.buf);
+
+	ep->dma_irqs++;
+	ep->dev->stats.irqs++;
+
+	completed = 0;
+
+	dcsr = DCSR(dmach);
+	DCSR(ep->dma) &= ~DCSR_RUN;
+
+	if (dcsr & DCSR_BUSERR) {
+		DCSR(dmach) = DCSR_BUSERR;
+		dev_err(ep->dev->dev, "DMA Bus Error\n");
+		req->req.status = -EIO;
+		completed = 1;
+	} else if (dcsr & DCSR_ENDINTR) {
+		DCSR(dmach) = DCSR_ENDINTR;
+		if (ep->dir_in) {
+			tmp = req->req.length - req->req.actual;
+			/* Last packet is a short one*/
+			if (tmp < ep->usb_ep->maxpacket) {
+				int count = 0;
+
+				*ep->reg_udccsr = UDCCSR_SP | \
+					(*ep->reg_udccsr & UDCCSR_MASK);
+				/*Wait for packet out */
+				while( (count++ < 10000) && \
+					!(*ep->reg_udccsr & UDCCSR_FS));
+				if (count >= 10000)
+					DMSG("Failed to send packet\n");
+				else
+					DMSG("%s: short packet sent len:%d,"
+					"length:%d,actual:%d\n", __FUNCTION__,
+					tmp, req->req.length, req->req.actual);
+				req->req.actual = req->req.length;
+				completed = 1;
+			/* There are still packets to transfer */
+			} else if ( ep->dma_con) {
+				DMSG("%s: more packets,length:%d,actual:%d\n",
+					 __FUNCTION__,req->req.length,
+					 req->req.actual);
+				req->req.actual += ep->usb_ep->maxpacket;
+				completed = 0;
+			} else {
+				DMSG("%s: no more packets,length:%d,"
+					"actual:%d\n", __FUNCTION__,
+					req->req.length, req->req.actual);
+				req->req.actual = req->req.length;
+				completed = 1;
+			}
+		} else {
+			req->req.actual = req->req.length;
+			completed = 1;
+		}
+	} else if (dcsr & DCSR_EORINTR) { //Only happened in OUT DMA
+		int remain,udccsr ;
+
+		DCSR(dmach) = DCSR_EORINTR;
+		remain = DCMD(dmach) & DCMD_LENGTH;
+		req->req.actual = req->req.length - remain;
+
+		udccsr = *ep->reg_udccsr;
+		if (udccsr & UDCCSR_SP) {
+			*ep->reg_udccsr = UDCCSR_PC | (udccsr & UDCCSR_MASK);
+			completed = 1;
+		}
+		DMSG("%s: length:%d actual:%d\n",
+				__FUNCTION__, req->req.length, req->req.actual);
+	} else
+		DMSG("%s: Others dma:%d DCSR:0x%x DCMD:0x%x\n",
+				__FUNCTION__, dmach, DCSR(dmach), DCMD(dmach));
+
+	if (likely(completed)) {
+		if (req->queue.next != &ep->queue) {
+			req_next = list_entry(req->queue.next,
+					struct pxa27x_request, queue);
+			kick_dma(ep, req_next);
+		}
+		done(ep, req, 0);
+	} else {
+		kick_dma(ep, req);
+	}
+
+	local_irq_enable();
+}
+
+#endif
+/*-------------------------------------------------------------------------*/
+
+static int
+pxa27x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, unsigned gfp_flags)
+{
+	struct pxa27x_virt_ep *virt_ep;
+	struct pxa27x_ep *ep;
+	struct pxa27x_request	*req;
+	struct pxa27x_udc	*dev;
+	unsigned long		flags;
+
+	virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep);
+	ep = virt_ep->pxa_ep;
+
+	req = container_of(_req, struct pxa27x_request, req);
+	if (unlikely (!_req || !_req->complete || !_req->buf||
+			!list_empty(&req->queue))) {
+		DMSG("%s, bad params\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	if (unlikely (!_ep || (!ep->desc && _ep->name != ep0name))) {
+		DMSG("%s, bad ep\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	DMSG("%s, ep point %d is queue\n", __FUNCTION__, ep->ep_num);
+
+	dev = ep->dev;
+	if (unlikely (!dev->driver
+			|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+		DMSG("%s, bogus device state\n", __FUNCTION__);
+		return -ESHUTDOWN;
+	}
+
+	/* iso is always one packet per request, that's the only way
+	 * we can report per-packet status.  that also helps with dma.
+	 */
+	if (unlikely (ep->ep_type == USB_ENDPOINT_XFER_ISOC
+			&& req->req.length > le16_to_cpu
+						(ep->desc->wMaxPacketSize)))
+		return -EMSGSIZE;
+
+#ifdef	USE_DMA
+	// FIXME caller may already have done the dma mapping
+	if (ep->dma >= 0) {
+		_req->dma = dma_map_single(dev->dev, _req->buf, _req->length,
+			(ep->dir_in) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+	}
+#endif
+
+	DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n",
+	     _ep->name, _req, _req->length, _req->buf);
+
+	local_irq_save(flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	/* kickstart this i/o queue? */
+	if (list_empty(&ep->queue) && !ep->stopped) {
+		if (ep->desc == 0 /* ep0 */) {
+			unsigned	length = _req->length;
+
+			switch (dev->ep0state) {
+			case EP0_IN_DATA_PHASE:
+				dev->stats.write.ops++;
+				if (write_ep0_fifo(ep, req))
+					req = 0;
+				break;
+
+			case EP0_OUT_DATA_PHASE:
+				dev->stats.read.ops++;
+				if (dev->req_pending)
+					ep0start(dev, UDCCSR0_IPR, "OUT");
+				if (length == 0 || ((UDCCSR0 & UDCCSR0_RNE) != 0
+						&& read_ep0_fifo(ep, req))) {
+					ep0_idle(dev);
+					done(ep, req, 0);
+					req = 0;
+				}
+				break;
+			case EP0_NO_ACTION:
+				ep0_idle(dev);
+				req=0;
+				break;
+			default:
+				DMSG("ep0 i/o, odd state %d\n", dev->ep0state);
+				local_irq_restore (flags);
+				return -EL2HLT;
+			}
+#ifdef USE_DMA
+		/* either start dma or prime pio pump */
+		} else if (ep->dma >= 0) {
+			kick_dma(ep, req);
+#endif
+		/* can the FIFO can satisfy the request immediately? */
+		} else if (ep->dir_in && (*ep->reg_udccsr & UDCCSR_FS) != 0
+				&& write_fifo(ep, req)) {
+			req = 0;
+		} else if ((*ep->reg_udccsr & UDCCSR_FS) != 0
+				&& read_fifo(ep, req)) {
+			req = 0;
+		}
+		DMSG("req:%p,ep->desc:%p,ep->dma:%d\n", req, ep->desc, ep->dma);
+		if (likely (req && ep->desc) && ep->dma < 0)
+			pio_irq_enable(ep->pxa_ep_num);
+	}
+
+	/* pio or dma irq handler advances the queue. */
+	if (likely (req != 0))
+		list_add_tail(&req->queue, &ep->queue);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+
+/*
+ * 	nuke - dequeue ALL requests
+ */
+static void nuke(struct pxa27x_ep *ep, int status)
+{
+	struct pxa27x_request *req;
+
+	/* called with irqs blocked */
+#ifdef	USE_DMA
+	if (ep->dma >= 0 && !ep->stopped)
+		cancel_dma(ep);
+#endif
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+		done(ep, req, status);
+	}
+	if (ep->desc)
+		pio_irq_disable(ep->pxa_ep_num);
+}
+
+
+/* dequeue JUST ONE request */
+static int pxa27x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct pxa27x_virt_ep *virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep);
+	struct pxa27x_ep *ep = virt_ep->pxa_ep;
+	struct pxa27x_request	*req;
+	unsigned long		flags;
+
+	if (!_ep || _ep->name == ep0name)
+		return -EINVAL;
+
+	local_irq_save(flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		local_irq_restore(flags);
+		return -EINVAL;
+	}
+
+#ifdef	USE_DMA
+	if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) {
+		cancel_dma(ep);
+		done(ep, req, -ECONNRESET);
+		/* restart i/o */
+		if (!list_empty(&ep->queue)) {
+			req = list_entry(ep->queue.next,
+					struct pxa27x_request, queue);
+			kick_dma(ep, req);
+		}
+	} else
+#endif
+		done(ep, req, -ECONNRESET);
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pxa27x_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct pxa27x_virt_ep *virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep);
+	struct pxa27x_ep *ep = virt_ep->pxa_ep;
+	unsigned long		flags;
+
+	DMSG("%s is called\n", __FUNCTION__);
+	if (unlikely (!_ep || (!ep->desc && _ep->name != ep0name))
+			|| ep->ep_type == USB_ENDPOINT_XFER_ISOC) {
+		DMSG("%s, bad ep\n", __FUNCTION__);
+		return -EINVAL;
+	}
+	if (value == 0) {
+		/* this path (reset toggle+halt) is needed to implement
+		 * SET_INTERFACE on normal hardware.  but it can't be
+		 * done from software on the PXA UDC, and the hardware
+		 * forgets to do it as part of SET_INTERFACE automagic.
+		 */
+		DMSG("only host can clear %s halt\n", _ep->name);
+		return -EROFS;
+	}
+
+	local_irq_save(flags);
+
+	if (ep->dir_in	&& ((*ep->reg_udccsr & UDCCSR_FS) == 0
+			   || !list_empty(&ep->queue))) {
+		local_irq_restore(flags);
+		return -EAGAIN;
+	}
+
+	/* FST bit is the same for control, bulk in, bulk out, interrupt in */
+	*ep->reg_udccsr = UDCCSR_FST|UDCCSR_FEF;
+
+	/* ep0 needs special care */
+	if (!ep->desc) {
+		start_watchdog(ep->dev);
+		ep->dev->req_pending = 0;
+		ep->dev->ep0state = EP0_STALL;
+
+ 	/* and bulk/intr endpoints like dropping stalls too */
+ 	} else {
+ 		unsigned i;
+ 		for (i = 0; i < 1000; i += 20) {
+ 			if (*ep->reg_udccsr & UDCCSR_SST)
+ 				break;
+ 			udelay(20);
+ 		}
+  	}
+ 	local_irq_restore(flags);
+
+	DBG(DBG_VERBOSE, "%s halt\n", _ep->name);
+	return 0;
+}
+
+static int pxa27x_ep_fifo_status(struct usb_ep *_ep)
+{
+	struct pxa27x_virt_ep *virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep);
+	struct pxa27x_ep *ep = virt_ep->pxa_ep;
+
+	if (!_ep) {
+		DMSG("%s, bad ep\n", __FUNCTION__);
+		return -ENODEV;
+	}
+	/* pxa can't report unclaimed bytes from IN fifos */
+	if (ep->dir_in)
+		return -EOPNOTSUPP;
+	if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN
+			|| (*ep->reg_udccsr & UDCCSR_FS) == 0)
+		return 0;
+	else
+		return (*ep->reg_udcbcr & 0xfff) + 1;
+}
+
+static void pxa27x_ep_fifo_flush(struct usb_ep *_ep)
+{
+	struct pxa27x_virt_ep *virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep);
+	struct pxa27x_ep *ep = virt_ep->pxa_ep;
+
+	DMSG("pxa27x_ep_fifo_flush\n");
+
+	if (!_ep || _ep->name == ep0name || !list_empty(&ep->queue)) {
+		DMSG("%s, bad ep\n", __FUNCTION__);
+		return;
+	}
+
+	/* toggle and halt bits stay unchanged */
+
+	/* for OUT, just read and discard the FIFO contents. */
+	if (!ep->dir_in) {
+		while (((*ep->reg_udccsr) & UDCCSR_BNE) != 0)
+			(void) *ep->reg_udcdr;
+		return;
+	}
+
+	/* most IN status is the same, but ISO can't stall */
+	*ep->reg_udccsr = UDCCSR_PC|UDCCSR_FST|UDCCSR_TRN
+		| (ep->ep_type == USB_ENDPOINT_XFER_ISOC)
+			? 0 : UDCCSR_SST;
+}
+
+
+static struct usb_ep_ops pxa27x_ep_ops = {
+	.enable		= pxa27x_ep_enable,
+	.disable	= pxa27x_ep_disable,
+
+	.alloc_request	= pxa27x_ep_alloc_request,
+	.free_request	= pxa27x_ep_free_request,
+
+	.queue		= pxa27x_ep_queue,
+	.dequeue	= pxa27x_ep_dequeue,
+
+	.set_halt	= pxa27x_ep_set_halt,
+	.fifo_status	= pxa27x_ep_fifo_status,
+	.fifo_flush	= pxa27x_ep_fifo_flush,
+};
+
+
+/* ---------------------------------------------------------------------------
+ * 	device-scoped parts of the api to the usb controller hardware
+ * ---------------------------------------------------------------------------
+ */
+
+static inline unsigned int validate_fifo_size(u8 bmAttributes)
+{
+	switch (bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		return EP0_FIFO_SIZE;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		return ISO_FIFO_SIZE;
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		return BULK_FIFO_SIZE;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		return INT_FIFO_SIZE;
+		break;
+	default:
+		break;
+	}
+}
+
+static void pxa27x_ep_free(struct usb_gadget *gadget, struct usb_ep *_ep)
+{
+	struct pxa27x_udc *dev = the_controller;
+	struct pxa27x_virt_ep *virt_ep;
+	int i;
+
+	virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep);
+
+	for (i = 1; i < UDC_EP_NUM; i++) {
+		if (dev->ep[i].usb_ep == &virt_ep->usb_ep) {
+			if (dev->ep[i].desc) {
+				virt_ep->pxa_ep = &dev->ep[i];
+				pxa27x_ep_disable(&virt_ep->usb_ep);
+			}
+			dev->ep[i].usb_ep = NULL;
+		}
+	}
+
+	if (!list_empty(&virt_ep->usb_ep.ep_list))
+		list_del_init(&virt_ep->usb_ep.ep_list);
+
+	kfree(virt_ep->usb_ep.name);
+	kfree(virt_ep);
+}
+
+static void pxa27x_ep_freeall(struct usb_gadget *gadget)
+{
+	struct pxa27x_udc *dev = the_controller;
+	int i;
+
+	for (i = 1; i < UDC_EP_NUM; i++) {
+		if(dev->ep[i].usb_ep)
+			pxa27x_ep_free(gadget, dev->ep[i].usb_ep);
+	}
+}
+
+#define NAME_SIZE 18
+
+static int pxa27x_find_free_ep(struct pxa27x_udc *dev)
+{
+	int i;
+	for (i = 1; i < UDC_EP_NUM; i++) {
+		if(!dev->ep[i].assigned)
+			return i;
+	}
+	return -1;
+}
+
+/*
+ * Endpoint Allocation/Configuration
+ *
+ * pxa27x endpoint configuration is fixed when the device is enabled. Any pxa
+ * endpoint is only active in one configuration, interface and alternate
+ * interface combination so to support gadget drivers, we map one usb_ep to
+ * one of several pxa ep's. One pxa endpoint is assigned per configuration
+ * combination.
+ */
+static struct usb_ep* pxa27x_ep_alloc(struct usb_gadget *gadget, struct usb_endpoint_descriptor *desc,
+	struct usb_endpoint_config *epconfig, int configs)
+{
+	struct pxa27x_udc *dev = the_controller;
+	struct pxa27x_virt_ep *virt_ep;
+	unsigned int i, fifo_size;
+	char *name;
+
+	if (unlikely(configs < 1)) {
+		dev_err(dev->dev, "%s: Error in config data\n", __FUNCTION__);
+		return NULL;
+	}
+
+	virt_ep = kmalloc(sizeof(struct pxa27x_virt_ep), GFP_KERNEL);
+	name = kmalloc(NAME_SIZE, GFP_KERNEL);
+	if (!virt_ep || !name) {
+		dev_err(dev->dev, "%s: -ENOMEM\n", __FUNCTION__);
+		kfree(name);
+		kfree(virt_ep);
+		return NULL;
+	}
+
+	if (!(desc->wMaxPacketSize)) {
+		fifo_size = validate_fifo_size(desc->bmAttributes);
+		desc->wMaxPacketSize = fifo_size;
+	} else {
+		fifo_size = desc->wMaxPacketSize;
+	}
+
+	DMSG("pxa27x_ep_alloc:  bLength: %d, bDescriptorType: %x, bEndpointAddress: %x,\n"
+	     	"  bmAttributes: %x, wMaxPacketSize: %d\n", desc->bLength,
+		desc->bDescriptorType, desc->bEndpointAddress, desc->bmAttributes,
+		desc->wMaxPacketSize);
+
+	if (!(desc->bEndpointAddress & 0xF))
+		desc->bEndpointAddress |= dev->ep_num;
+
+	for (i = 0; i < configs; i++)
+	{
+		struct pxa27x_ep *pxa_ep;
+		int j;
+
+		DMSG("pxa27x_ep_alloc:  config: %d, interface: %d, altinterface: %x,\n",
+			epconfig->config, epconfig->interface, epconfig->altinterface);
+
+		j = pxa27x_find_free_ep(dev);
+
+		if (unlikely(j < 0)) {
+			dev_err(dev->dev, "pxa27x_ep_alloc: Failed to find a spare endpoint\n");
+			pxa27x_ep_free(gadget, &virt_ep->usb_ep);
+			return NULL;
+		}
+
+		pxa_ep = &dev->ep[j];
+
+		if (i == 0)
+			virt_ep->pxa_ep = pxa_ep;
+
+		pxa_ep->assigned = 1;
+		pxa_ep->ep_num = dev->ep_num;
+		pxa_ep->pxa_ep_num = j;
+		pxa_ep->usb_ep = &virt_ep->usb_ep;
+		pxa_ep->dev = dev;
+		pxa_ep->desc = desc;
+		pxa_ep->pio_irqs = pxa_ep->dma_irqs = 0;
+		pxa_ep->dma = -1;
+
+		pxa_ep->fifo_size = fifo_size;
+		pxa_ep->dir_in = (desc->bEndpointAddress & USB_DIR_IN) ? 1 : 0;
+		pxa_ep->ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+		pxa_ep->stopped = 1;
+		pxa_ep->dma_con = 0;
+		pxa_ep->config = epconfig->config;
+		pxa_ep->interface = epconfig->interface;
+		pxa_ep->aisn = epconfig->altinterface;
+
+		pxa_ep->reg_udccsr = &UDCCSR0 + j;
+		pxa_ep->reg_udcbcr = &UDCBCR0 + j;
+		pxa_ep->reg_udcdr = &UDCDR0 + j ;
+		pxa_ep->reg_udccr = &UDCCRA - 1 + j;
+#ifdef USE_DMA
+		pxa_ep->reg_drcmr = &DRCMR24 + j;
+#endif
+
+		/* Configure UDCCR */
+		*pxa_ep->reg_udccr = ((pxa_ep->config << UDCCONR_CN_S) & UDCCONR_CN)
+			| ((pxa_ep->interface << UDCCONR_IN_S) & UDCCONR_IN)
+			| ((pxa_ep->aisn << UDCCONR_AISN_S) & UDCCONR_AISN)
+			| ((dev->ep_num << UDCCONR_EN_S) & UDCCONR_EN)
+			| ((pxa_ep->ep_type << UDCCONR_ET_S) & UDCCONR_ET)
+			| ((pxa_ep->dir_in) ? UDCCONR_ED : 0)
+			| ((min(pxa_ep->fifo_size, (unsigned)desc->wMaxPacketSize) << UDCCONR_MPS_S ) & UDCCONR_MPS)
+			| UDCCONR_EE;
+//			| UDCCONR_DE | UDCCONR_EE;
+
+
+
+#ifdef USE_DMA
+		/* Only BULK use DMA */
+		if ((pxa_ep->ep_type & USB_ENDPOINT_XFERTYPE_MASK)\
+				== USB_ENDPOINT_XFER_BULK)
+			*pxa_ep->reg_udccsr = UDCCSR_DME;
+#endif
+
+		DMSG("UDCCR: 0x%p is 0x%x\n", pxa_ep->reg_udccr,*pxa_ep->reg_udccr);
+
+		epconfig++;
+	}
+
+	/* Fill ep name*/
+	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+		case USB_ENDPOINT_XFER_BULK:
+			sprintf(name, "ep%d%s-bulk", dev->ep_num,
+				((desc->bEndpointAddress & USB_DIR_IN) ? "in":"out"));
+			break;
+		case USB_ENDPOINT_XFER_INT:
+			sprintf(name, "ep%d%s-intr", dev->ep_num,
+				((desc->bEndpointAddress & USB_DIR_IN) ? "in":"out"));
+			break;
+		default:
+			sprintf(name, "ep%d%s", dev->ep_num,
+				((desc->bEndpointAddress & USB_DIR_IN) ? "in":"out"));
+			break;
+	}
+
+	virt_ep->desc = desc;
+	virt_ep->usb_ep.name = name;
+	virt_ep->usb_ep.ops = &pxa27x_ep_ops;
+	virt_ep->usb_ep.maxpacket = min((ushort)fifo_size, desc->wMaxPacketSize);
+
+	list_add_tail(&virt_ep->usb_ep.ep_list, &gadget->ep_list);
+
+	dev->ep_num++;
+	return &virt_ep->usb_ep;
+}
+
+static int pxa27x_udc_get_frame(struct usb_gadget *_gadget)
+{
+	return (UDCFNR & 0x7FF);
+}
+
+static int pxa27x_udc_wakeup(struct usb_gadget *_gadget)
+{
+	/* host may not have enabled remote wakeup */
+	if ((UDCCR & UDCCR_DWRE) == 0)
+		return -EHOSTUNREACH;
+	udc_set_mask_UDCCR(UDCCR_UDR);
+	return 0;
+}
+
+static const struct usb_gadget_ops pxa27x_udc_ops = {
+	.ep_alloc	= pxa27x_ep_alloc,
+	.get_frame	= pxa27x_udc_get_frame,
+	.wakeup		= pxa27x_udc_wakeup,
+	// current versions must always be self-powered
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef UDC_PROC_FILE
+
+static const char proc_node_name [] = "driver/udc";
+
+static int
+udc_proc_read(char *page, char **start, off_t off, int count,
+		int *eof, void *_dev)
+{
+	char			*buf = page;
+	struct pxa27x_udc	*dev = _dev;
+	char			*next = buf;
+	unsigned		size = count;
+	unsigned long		flags;
+	int			i, t;
+	u32			tmp;
+
+	if (off != 0)
+		return 0;
+
+	local_irq_save(flags);
+
+	/* basic device status */
+	t = scnprintf(next, size, DRIVER_DESC "\n"
+		"%s version: %s\nGadget driver: %s\n",
+		driver_name, DRIVER_VERSION DMASTR,
+		dev->driver ? dev->driver->driver.name : "(none)");
+	size -= t;
+	next += t;
+
+	/* registers for device and ep0 */
+	t = scnprintf(next, size,
+		"uicr %02X.%02X, usir %02X.%02x, ufnr %02X\n",
+		UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR);
+	size -= t;
+	next += t;
+
+	tmp = UDCCR;
+	t = scnprintf(next, size,"udccr %02X =%s%s%s%s%s%s%s%s%s%s, con=%d,inter=%d,altinter=%d\n", tmp,
+		(tmp & UDCCR_OEN) ? " oen":"",
+		(tmp & UDCCR_AALTHNP) ? " aalthnp":"",
+		(tmp & UDCCR_AHNP) ? " rem" : "",
+		(tmp & UDCCR_BHNP) ? " rstir" : "",
+		(tmp & UDCCR_DWRE) ? " dwre" : "",
+		(tmp & UDCCR_SMAC) ? " smac" : "",
+		(tmp & UDCCR_EMCE) ? " emce" : "",
+		(tmp & UDCCR_UDR) ? " udr" : "",
+		(tmp & UDCCR_UDA) ? " uda" : "",
+		(tmp & UDCCR_UDE) ? " ude" : "",
+		(tmp & UDCCR_ACN) >> UDCCR_ACN_S,
+		(tmp & UDCCR_AIN) >> UDCCR_AIN_S,
+		(tmp & UDCCR_AAISN)>> UDCCR_AAISN_S );
+
+	size -= t;
+	next += t;
+
+	tmp = UDCCSR0;
+	t = scnprintf(next, size,
+		"udccsr0 %02X =%s%s%s%s%s%s%s\n", tmp,
+		(tmp & UDCCSR0_SA) ? " sa" : "",
+		(tmp & UDCCSR0_RNE) ? " rne" : "",
+		(tmp & UDCCSR0_FST) ? " fst" : "",
+		(tmp & UDCCSR0_SST) ? " sst" : "",
+		(tmp & UDCCSR0_DME) ? " dme" : "",
+		(tmp & UDCCSR0_IPR) ? " ipr" : "",
+		(tmp & UDCCSR0_OPC) ? " opc" : "");
+	size -= t;
+	next += t;
+
+	if (!dev->driver)
+		goto done;
+
+	t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
+		dev->stats.write.bytes, dev->stats.write.ops,
+		dev->stats.read.bytes, dev->stats.read.ops,
+		dev->stats.irqs);
+	size -= t;
+	next += t;
+
+	/* dump endpoint queues */
+	for (i = 0; i < UDC_EP_NUM; i++) {
+		struct pxa27x_ep	*ep = &dev->ep [i];
+		struct pxa27x_request	*req;
+		int			t;
+
+		if (i != 0) {
+			const struct usb_endpoint_descriptor	*d;
+
+			d = ep->desc;
+			if (!d)
+				continue;
+			tmp = *dev->ep [i].reg_udccsr;
+			t = scnprintf(next, size,
+				"%d max %d %s udccs %02x udccr:0x%x\n",
+				i, le16_to_cpu (d->wMaxPacketSize),
+				(ep->dma >= 0) ? "dma" : "pio", tmp,
+				*dev->ep[i].reg_udccr);
+			/* TODO translate all five groups of udccs bits! */
+
+		} else /* ep0 should only have one transfer queued */
+			t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n",
+				ep->pio_irqs);
+		if (t <= 0 || t > size)
+			goto done;
+		size -= t;
+		next += t;
+
+		if (list_empty(&ep->queue)) {
+			t = scnprintf(next, size, "\t(nothing queued)\n");
+			if (t <= 0 || t > size)
+				goto done;
+			size -= t;
+			next += t;
+			continue;
+		}
+		list_for_each_entry(req, &ep->queue, queue) {
+#ifdef	USE_DMA
+			if (ep->dma >= 0 && req->queue.prev == &ep->queue)
+				t = scnprintf(next, size, "\treq %p len %d/%d "
+					"buf %p (dma%d dcmd %08x)\n",
+					&req->req, req->req.actual,
+					req->req.length, req->req.buf,
+					ep->dma, DCMD(ep->dma)
+					/* low 13 bits == bytes-to-go */);
+			else
+#endif
+				t = scnprintf(next, size,
+					"\treq %p len %d/%d buf %p\n",
+					&req->req, req->req.actual,
+					req->req.length, req->req.buf);
+			if (t <= 0 || t > size)
+				goto done;
+			size -= t;
+			next += t;
+		}
+	}
+
+done:
+	local_irq_restore(flags);
+	*eof = 1;
+	return count - size;
+}
+
+#define create_proc_files() \
+	create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
+#define remove_proc_files() \
+	remove_proc_entry(proc_node_name, NULL)
+
+#else	/* !UDC_PROC_FILE */
+#define create_proc_files() do {} while (0)
+#define remove_proc_files() do {} while (0)
+
+#endif	/* UDC_PROC_FILE */
+
+/* "function" sysfs attribute */
+static ssize_t show_function(struct device *_dev, struct device_attribute *attr, char *buf)
+{
+	struct pxa27x_udc *dev = dev_get_drvdata(_dev);
+
+	if (!dev->driver || !dev->driver->function
+			|| strlen(dev->driver->function) > PAGE_SIZE)
+		return 0;
+	return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+static DEVICE_ATTR(function, S_IRUGO, show_function, NULL);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * 	udc_disable - disable USB device controller
+ */
+static void udc_disable(struct pxa27x_udc *dev)
+{
+	UDCICR0 = UDCICR1 = 0x00000000;
+
+	udc_clear_mask_UDCCR(UDCCR_UDE);
+
+        /* Disable clock for USB device */
+	pxa_set_cken(CKEN_USB, 0);
+
+	ep0_idle(dev);
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	dev->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+}
+
+
+/*
+ * 	udc_reinit - initialize software state
+ */
+static void udc_reinit(struct pxa27x_udc *dev)
+{
+	u32	i;
+
+	dev->ep0state = EP0_IDLE;
+
+	/* basic endpoint records init */
+	for (i = 0; i < UDC_EP_NUM; i++) {
+		struct pxa27x_ep *ep = &dev->ep[i];
+
+		ep->stopped = 0;
+		ep->pio_irqs = ep->dma_irqs = 0;
+	}
+	dev->configuration = 0;
+	dev->interface = 0;
+	dev->alternate = 0;
+	/* the rest was statically initialized, and is read-only */
+}
+
+/* until it's enabled, this UDC should be completely invisible
+ * to any USB host.
+ */
+static void udc_enable(struct pxa27x_udc *dev)
+{
+	udc_clear_mask_UDCCR(UDCCR_UDE);
+
+        /* Enable clock for USB device */
+	pxa_set_cken(CKEN_USB, 1);
+
+	UDCICR0 = UDCICR1 = 0;
+
+	ep0_idle(dev);
+	dev->gadget.speed = USB_SPEED_FULL;
+	dev->stats.irqs = 0;
+
+	udc_set_mask_UDCCR(UDCCR_UDE);
+	udelay(2);
+	if (UDCCR & UDCCR_EMCE)
+		dev_err(dev->dev, "There are error in configuration, udc disabled\n");
+
+	/* caller must be able to sleep in order to cope
+	 * with startup transients.
+	 */
+	msleep(100);
+
+	/* enable suspend/resume and reset irqs */
+	UDCICR1 = UDCICR1_IECC | UDCICR1_IERU | UDCICR1_IESU | UDCICR1_IERS;
+
+	/* enable ep0 irqs */
+	UDCICR0 = UDCICR_INT(0,UDCICR_INT_MASK);
+
+	DMSG("Connecting\n");
+	/* RPFIXME */
+	UP2OCR = UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE;
+	//dev->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+}
+
+
+/* when a driver is successfully registered, it will receive
+ * control requests including set_configuration(), which enables
+ * non-control requests.  then usb traffic follows until a
+ * disconnect is reported.  then a host may connect again, or
+ * the driver might get unbound.
+ */
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct pxa27x_udc *dev = the_controller;
+	int retval;
+
+	if (!driver || driver->speed != USB_SPEED_FULL || !driver->bind
+			|| !driver->unbind || !driver->disconnect || !driver->setup)
+		return -EINVAL;
+	if (!dev)
+		return -ENODEV;
+	if (dev->driver)
+		return -EBUSY;
+
+	udc_disable(dev);
+	udc_init_ep(dev);
+	udc_reinit(dev);
+
+	/* first hook up the driver ... */
+	dev->driver = driver;
+	dev->gadget.dev.driver = &driver->driver;
+	dev->ep_num = 1;
+
+	retval = device_add(&dev->gadget.dev);
+	if (retval) {
+		DMSG("device_add error %d\n", retval);
+		goto add_fail;
+	}
+	retval = driver->bind(&dev->gadget);
+	if (retval) {
+		DMSG("bind to driver %s --> error %d\n",
+				driver->driver.name, retval);
+		goto bind_fail;
+	}
+	retval = device_create_file(dev->dev, &dev_attr_function);
+	if (retval) {
+		DMSG("device_create_file failed: %d\n", retval);
+		goto create_file_fail;
+	}
+
+	/* ... then enable host detection and ep0; and we're ready
+	 * for set_configuration as well as eventual disconnect.
+	 * NOTE:  this shouldn't power up until later.
+	 */
+	DMSG("registered gadget driver '%s'\n", driver->driver.name);
+	udc_enable(dev);
+	dump_state(dev);
+	return 0;
+
+create_file_fail:
+	driver->unbind(&dev->gadget);
+bind_fail:
+	device_del(&dev->gadget.dev);
+add_fail:
+	dev->driver = 0;
+	dev->gadget.dev.driver = 0;
+	return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+static void
+stop_activity(struct pxa27x_udc *dev, struct usb_gadget_driver *driver)
+{
+	int i;
+
+	DMSG("Trace path 1\n");
+	/* don't disconnect drivers more than once */
+	if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = 0;
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* prevent new request submissions, kill any outstanding requests  */
+	for (i = 0; i < UDC_EP_NUM; i++) {
+		struct pxa27x_ep *ep = &dev->ep[i];
+
+		ep->stopped = 1;
+		nuke(ep, -ESHUTDOWN);
+	}
+	del_timer_sync(&dev->timer);
+
+	/* report disconnect; the driver is already quiesced */
+	if (driver)
+		driver->disconnect(&dev->gadget);
+
+	/* re-init driver-visible data structures */
+	udc_reinit(dev);
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct pxa27x_udc	*dev = the_controller;
+
+	if (!dev)
+		return -ENODEV;
+	if (!driver || driver != dev->driver)
+		return -EINVAL;
+
+	local_irq_disable();
+	udc_disable(dev);
+	stop_activity(dev, driver);
+	local_irq_enable();
+
+	driver->unbind(&dev->gadget);
+	pxa27x_ep_freeall(&dev->gadget);
+	dev->driver = 0;
+
+	device_del(&dev->gadget.dev);
+	device_remove_file(dev->dev, &dev_attr_function);
+
+	DMSG("unregistered gadget driver '%s'\n", driver->driver.name);
+	dump_state(dev);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+
+/*-------------------------------------------------------------------------*/
+
+static inline void clear_ep_state(struct pxa27x_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 < UDC_EP_NUM; i++)
+		nuke(&dev->ep[i], -ECONNABORTED);
+}
+
+static void udc_watchdog(unsigned long _dev)
+{
+	struct pxa27x_udc	*dev = (void *)_dev;
+
+	local_irq_disable();
+	if (dev->ep0state == EP0_STALL
+			&& (UDCCSR0 & UDCCSR0_FST) == 0
+			&& (UDCCSR0 & UDCCSR0_SST) == 0) {
+		UDCCSR0 = UDCCSR0_FST|UDCCSR0_FTF;
+		DBG(DBG_VERBOSE, "ep0 re-stall\n");
+		start_watchdog(dev);
+	}
+	local_irq_enable();
+}
+
+static void handle_ep0(struct pxa27x_udc *dev)
+{
+	u32			udccsr0 = UDCCSR0;
+	struct pxa27x_ep	*ep = &dev->ep[0];
+	struct pxa27x_request	*req;
+	union {
+		struct usb_ctrlrequest	r;
+		u8			raw[8];
+		u32			word[2];
+	} u;
+
+	if (list_empty(&ep->queue))
+		req = 0;
+	else
+		req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+
+	/* clear stall status */
+	if (udccsr0 & UDCCSR0_SST) {
+		nuke(ep, -EPIPE);
+		UDCCSR0 = UDCCSR0_SST;
+		del_timer(&dev->timer);
+		ep0_idle(dev);
+	}
+
+	/* previous request unfinished?  non-error iff back-to-back ... */
+	if ((udccsr0 & UDCCSR0_SA) != 0 && dev->ep0state != EP0_IDLE) {
+		nuke(ep, 0);
+		del_timer(&dev->timer);
+		ep0_idle(dev);
+	}
+
+	switch (dev->ep0state) {
+	case EP0_NO_ACTION:
+		dev_info(dev->dev, "%s: Busy\n", __FUNCTION__);
+		/*Fall through */
+	case EP0_IDLE:
+		/* late-breaking status? */
+		udccsr0 = UDCCSR0;
+
+		/* start control request? */
+		if (likely((udccsr0 & (UDCCSR0_OPC|UDCCSR0_SA|UDCCSR0_RNE))
+				== (UDCCSR0_OPC|UDCCSR0_SA|UDCCSR0_RNE))) {
+			int i;
+
+			nuke(ep, -EPROTO);
+			/* read SETUP packet */
+			for (i = 0; i < 2; i++) {
+				if (unlikely(!(UDCCSR0 & UDCCSR0_RNE))) {
+bad_setup:
+					DMSG("SETUP %d!\n", i);
+					goto stall;
+				}
+				u.word [i] =  UDCDR0;
+			}
+			if (unlikely((UDCCSR0 & UDCCSR0_RNE) != 0))
+				goto bad_setup;
+
+			le16_to_cpus(&u.r.wValue);
+			le16_to_cpus(&u.r.wIndex);
+			le16_to_cpus(&u.r.wLength);
+
+			DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+				u.r.bRequestType, u.r.bRequest,
+				u.r.wValue, u.r.wIndex, u.r.wLength);
+			/* cope with automagic for some standard requests. */
+			dev->req_std = (u.r.bRequestType & USB_TYPE_MASK)
+						== USB_TYPE_STANDARD;
+			dev->req_config = 0;
+			dev->req_pending = 1;
+#if 0
+			switch (u.r.bRequest) {
+			/* hardware was supposed to hide this */
+			case USB_REQ_SET_CONFIGURATION:
+			case USB_REQ_SET_INTERFACE:
+			case USB_REQ_SET_ADDRESS:
+				dev_err(dev->dev, "Should not come here\n");
+				break;
+			}
+
+#endif
+			if (u.r.bRequestType & USB_DIR_IN)
+				dev->ep0state = EP0_IN_DATA_PHASE;
+			else
+				dev->ep0state = EP0_OUT_DATA_PHASE;
+			i = dev->driver->setup(&dev->gadget, &u.r);
+
+			if (i < 0) {
+				/* hardware automagic preventing STALL... */
+				if (dev->req_config) {
+					/* hardware sometimes neglects to tell
+					 * tell us about config change events,
+					 * so later ones may fail...
+					 */
+					WARN("config change %02x fail %d?\n",
+						u.r.bRequest, i);
+					return;
+					/* TODO experiment:  if has_cfr,
+					 * hardware didn't ACK; maybe we
+					 * could actually STALL!
+					 */
+				}
+				DBG(DBG_VERBOSE, "protocol STALL, "
+					"%02x err %d\n", UDCCSR0, i);
+stall:
+				/* the watchdog timer helps deal with cases
+				 * where udc seems to clear FST wrongly, and
+				 * then NAKs instead of STALLing.
+				 */
+				ep0start(dev, UDCCSR0_FST|UDCCSR0_FTF, "stall");
+				start_watchdog(dev);
+				dev->ep0state = EP0_STALL;
+
+			/* deferred i/o == no response yet */
+			} else if (dev->req_pending) {
+				if (likely(dev->ep0state == EP0_IN_DATA_PHASE
+						|| dev->req_std || u.r.wLength))
+					ep0start(dev, 0, "defer");
+				else
+					ep0start(dev, UDCCSR0_IPR, "defer/IPR");
+			}
+
+			/* expect at least one data or status stage irq */
+			return;
+
+		} else {
+			/* some random early IRQ:
+			 * - we acked FST
+			 * - IPR cleared
+			 * - OPC got set, without SA (likely status stage)
+			 */
+			UDCCSR0 = udccsr0 & (UDCCSR0_SA|UDCCSR0_OPC);
+		}
+		break;
+	case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR etc */
+		if (udccsr0 & UDCCSR0_OPC) {
+			UDCCSR0 = UDCCSR0_OPC|UDCCSR0_FTF;
+			DBG(DBG_VERBOSE, "ep0in premature status\n");
+			if (req)
+				done(ep, req, 0);
+			ep0_idle(dev);
+		} else /* irq was IPR clearing */ {
+			if (req) {
+				/* this IN packet might finish the request */
+				(void) write_ep0_fifo(ep, req);
+			} /* else IN token before response was written */
+		}
+		break;
+	case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR etc */
+		if (udccsr0 & UDCCSR0_OPC) {
+			if (req) {
+				/* this OUT packet might finish the request */
+				if (read_ep0_fifo(ep, req))
+					done(ep, req, 0);
+				/* else more OUT packets expected */
+			} /* else OUT token before read was issued */
+		} else /* irq was IPR clearing */ {
+			DBG(DBG_VERBOSE, "ep0out premature status\n");
+			if (req)
+				done(ep, req, 0);
+			ep0_idle(dev);
+		}
+		break;
+	case EP0_STALL:
+		UDCCSR0 = UDCCSR0_FST;
+		break;
+		}
+	UDCISR0 = UDCISR_INT(0, UDCISR_INT_MASK);
+}
+
+
+static void handle_ep(struct pxa27x_ep *ep)
+{
+	struct pxa27x_request	*req;
+	int			completed;
+	u32			udccsr=0;
+
+	DMSG("%s is called\n", __FUNCTION__);
+	do {
+		completed = 0;
+		if (likely (!list_empty(&ep->queue))) {
+			req = list_entry(ep->queue.next,
+					struct pxa27x_request, queue);
+		} else
+			req = 0;
+
+//		udccsr = *ep->reg_udccsr;
+		DMSG("%s: req:%p, udcisr0:0x%x udccsr %p:0x%x\n", __FUNCTION__,
+				req, UDCISR0, ep->reg_udccsr, *ep->reg_udccsr);
+		if (unlikely(ep->dir_in)) {
+			udccsr = (UDCCSR_SST | UDCCSR_TRN) & *ep->reg_udccsr;
+			if (unlikely (udccsr))
+				*ep->reg_udccsr = udccsr;
+
+			if (req && likely ((*ep->reg_udccsr & UDCCSR_FS) != 0))
+				completed = write_fifo(ep, req);
+
+		} else {
+			udccsr = (UDCCSR_SST | UDCCSR_TRN) & *ep->reg_udccsr;
+			if (unlikely(udccsr))
+				*ep->reg_udccsr = udccsr;
+
+			/* fifos can hold packets, ready for reading... */
+			if (likely(req)) {
+				completed = read_fifo(ep, req);
+			} else {
+				pio_irq_disable (ep->pxa_ep_num);
+				//*ep->reg_udccsr = UDCCSR_FEF;
+				DMSG("%s: no req for out data\n",
+						__FUNCTION__);
+			}
+		}
+		ep->pio_irqs++;
+	} while (completed);
+}
+
+static void pxa27x_update_eps(struct pxa27x_udc *dev)
+{
+	struct pxa27x_virt_ep *virt_ep;
+	int i;
+
+	for (i = 1; i < UDC_EP_NUM; i++) {
+		if(!dev->ep[i].assigned || !dev->ep[i].usb_ep)
+			continue;
+		virt_ep = container_of(dev->ep[i].usb_ep, struct pxa27x_virt_ep, usb_ep);
+
+		DMSG("%s, Updating eps %d:%d, %d:%d, %d:%d, %p,%p\n", __FUNCTION__, dev->ep[i].config, dev->configuration
+			,dev->ep[i].interface, dev->interface, dev->ep[i].aisn, dev->alternate, virt_ep->pxa_ep, &dev->ep[i]);
+
+		if(dev->ep[i].config == dev->configuration && virt_ep->pxa_ep != &dev->ep[i]) {
+			if ((dev->ep[i].interface == dev->interface &&
+				dev->ep[i].aisn == dev->alternate) || virt_ep->pxa_ep->config != dev->configuration) {
+
+			if (virt_ep->pxa_ep->desc) {
+				DMSG("%s, Changing end point to %d (en/dis)\n", __FUNCTION__, i);
+				pxa27x_ep_disable(&virt_ep->usb_ep);
+				virt_ep->pxa_ep = &dev->ep[i];
+				pxa27x_ep_enable(&virt_ep->usb_ep, virt_ep->desc);
+			} else {
+				DMSG("%s, Changing end point to %d (no en/dis)\n", __FUNCTION__, i);
+				virt_ep->pxa_ep = &dev->ep[i];
+			}
+			}
+		}
+	}
+}
+
+static void pxa27x_change_configuration(struct pxa27x_udc *dev)
+{
+	struct usb_ctrlrequest req ;
+
+	pxa27x_update_eps(dev);
+
+	req.bRequestType = 0;
+	req.bRequest = USB_REQ_SET_CONFIGURATION;
+	req.wValue = dev->configuration;
+	req.wIndex = 0;
+	req.wLength = 0;
+
+	dev->ep0state = EP0_NO_ACTION;
+	dev->driver->setup(&dev->gadget, &req);
+}
+
+static void pxa27x_change_interface(struct pxa27x_udc *dev)
+{
+	struct usb_ctrlrequest  req;
+
+	pxa27x_update_eps(dev);
+
+	req.bRequestType = USB_RECIP_INTERFACE;
+	req.bRequest = USB_REQ_SET_INTERFACE;
+	req.wValue = dev->alternate;
+	req.wIndex = dev->interface;
+	req.wLength = 0;
+
+	dev->ep0state = EP0_NO_ACTION;
+	dev->driver->setup(&dev->gadget, &req);
+}
+
+/*
+ *	pxa27x_udc_irq - interrupt handler
+ *
+ * avoid delays in ep0 processing. the control handshaking isn't always
+ * under software control (pxa250c0 and the pxa255 are better), and delays
+ * could cause usb protocol errors.
+ */
+static irqreturn_t pxa27x_udc_irq(int irq, void *_dev)
+{
+	struct pxa27x_udc	*dev = _dev;
+	int			handled;
+
+	dev->stats.irqs++;
+
+	DBG(DBG_VERBOSE, "Interrupt, UDCISR0:0x%08x, UDCISR1:0x%08x, "
+			"UDCCR:0x%08x\n", UDCISR0, UDCISR1, UDCCR);
+	do {
+		u32 udcir = UDCISR1 & 0xF8000000;
+
+		handled = 0;
+
+		/* SUSpend Interrupt Request */
+		if (unlikely(udcir & UDCISR1_IRSU)) {
+			UDCISR1 = UDCISR1_IRSU;
+			handled = 1;
+			DBG(DBG_VERBOSE, "USB suspend\n");
+			if (dev->gadget.speed != USB_SPEED_UNKNOWN
+					&& dev->driver
+					&& dev->driver->suspend)
+				dev->driver->suspend(&dev->gadget);
+			ep0_idle(dev);
+		}
+
+		/* RESume Interrupt Request */
+		if (unlikely(udcir & UDCISR1_IRRU)) {
+			UDCISR1 = UDCISR1_IRRU;
+			handled = 1;
+			DBG(DBG_VERBOSE, "USB resume\n");
+
+			if (dev->gadget.speed != USB_SPEED_UNKNOWN
+					&& dev->driver
+					&& dev->driver->resume)
+				dev->driver->resume(&dev->gadget);
+		}
+
+		if (unlikely(udcir & UDCISR1_IRCC)) {
+			unsigned config, interface, alternate;
+
+			handled = 1;
+			DBG(DBG_VERBOSE, "USB SET_CONFIGURATION or "
+				"SET_INTERFACE command received\n");
+
+			config = (UDCCR & UDCCR_ACN) >> UDCCR_ACN_S;
+
+			if (dev->configuration != config) {
+				dev->configuration = config;
+				pxa27x_change_configuration(dev) ;
+			}
+
+			interface = (UDCCR & UDCCR_AIN) >> UDCCR_AIN_S;
+			alternate = (UDCCR & UDCCR_AAISN) >> UDCCR_AAISN_S;
+
+			if ((dev->interface != interface) || (dev->alternate != alternate)) {
+				dev->interface = interface;
+				dev->alternate = alternate;
+				pxa27x_change_interface(dev);
+			}
+
+			UDCCR |= UDCCR_SMAC;
+
+			UDCISR1 = UDCISR1_IRCC;
+			DMSG("%s: con:%d,inter:%d,alt:%d\n",
+				__FUNCTION__, config,interface, alternate);
+		}
+
+		/* ReSeT Interrupt Request - USB reset */
+		if (unlikely(udcir & UDCISR1_IRRS)) {
+			UDCISR1 = UDCISR1_IRRS;
+			handled = 1;
+
+			if ((UDCCR & UDCCR_UDA) == 0) {
+				DBG(DBG_VERBOSE, "USB reset start\n");
+
+				/* reset driver and endpoints,
+				 * in case that's not yet done
+				 */
+				stop_activity(dev, dev->driver);
+			}
+			INFO("USB reset\n");
+			dev->gadget.speed = USB_SPEED_FULL;
+			memset(&dev->stats, 0, sizeof dev->stats);
+
+		} else {
+			u32	udcisr0 = UDCISR0 ;
+			u32	udcisr1 = UDCISR1 & 0xFFFF;
+			int	i;
+
+			if (unlikely (!udcisr0 && !udcisr1))
+				continue;
+
+			DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", udcisr1,udcisr0);
+
+			/* control traffic */
+			if (udcisr0 & UDCISR0_IR0) {
+				dev->ep[0].pio_irqs++;
+				handle_ep0(dev);
+				handled = 1;
+			}
+
+			udcisr0 >>= 2;
+			/* endpoint data transfers */
+			for (i = 1; udcisr0!=0 && i < 16; udcisr0>>=2,i++) {
+				UDCISR0 = UDCISR_INT(i, UDCISR_INT_MASK);
+
+				if (udcisr0 & UDC_INT_FIFOERROR)
+					dev_err(dev->dev, " Endpoint %d Fifo error\n", i);
+				if (udcisr0 & UDC_INT_PACKETCMP) {
+					handle_ep(&dev->ep[i]);
+					handled = 1;
+				}
+
+			}
+
+			for (i = 0; udcisr1!=0 && i < 8; udcisr1 >>= 2, i++) {
+				UDCISR1 = UDCISR_INT(i, UDCISR_INT_MASK);
+
+				if (udcisr1 & UDC_INT_FIFOERROR) {
+					dev_err(dev->dev, "Endpoint %d fifo error\n", (i+16));
+				}
+
+				if (udcisr1 & UDC_INT_PACKETCMP) {
+					handle_ep(&dev->ep[i+16]);
+					handled = 1;
+				}
+			}
+		}
+
+		/* we could also ask for 1 msec SOF (SIR) interrupts */
+
+	} while (handled);
+	return IRQ_HANDLED;
+}
+
+int write_ep0_zlp(void)
+{
+	UDCCSR0 = UDCCSR0_IPR;
+	return 0;
+}
+EXPORT_SYMBOL(write_ep0_zlp);
+
+static void udc_init_ep(struct pxa27x_udc *dev)
+{
+	int i;
+
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+	for (i = 0; i < UDC_EP_NUM; i++) {
+		struct pxa27x_ep *ep = &dev->ep[i];
+
+		ep->dma = -1;
+		if (i != 0) {
+			memset(ep, 0, sizeof(*ep));
+		}
+		INIT_LIST_HEAD(&ep->queue);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void nop_release(struct device *dev)
+{
+	DMSG("%s %s\n", __FUNCTION__, dev->bus_id);
+}
+
+/* this uses load-time allocation and initialization (instead of
+ * doing it at run-time) to save code, eliminate fault paths, and
+ * be more obviously correct.
+ */
+
+static struct pxa27x_udc memory = {
+	.gadget = {
+		.ops		= &pxa27x_udc_ops,
+		.ep0		= &memory.virt_ep0.usb_ep,
+		.name		= driver_name,
+		.dev = {
+			.bus_id		= "gadget",
+			.release	= nop_release,
+		},
+	},
+
+	/* control endpoint */
+	.virt_ep0 = {
+		.pxa_ep = &memory.ep[0],
+		.usb_ep = {
+			.name		= ep0name,
+			.ops		= &pxa27x_ep_ops,
+			.maxpacket	= EP0_FIFO_SIZE,
+		},
+	},
+
+	.ep[0] = {
+		.usb_ep 	= &memory.virt_ep0.usb_ep,
+		.dev		= &memory,
+		.reg_udccsr	= &UDCCSR0,
+		.reg_udcdr	= &UDCDR0,
+	},
+};
+
+static int __init pxa27x_udc_probe(struct platform_device *_dev)
+{
+	struct pxa27x_udc *dev = &memory;
+	int retval;
+
+	/* other non-static parts of init */
+	dev->dev = &_dev->dev;
+	dev->mach = _dev->dev.platform_data;
+
+	/* RPFIXME */
+	UP2OCR = UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE;
+
+	init_timer(&dev->timer);
+	dev->timer.function = udc_watchdog;
+	dev->timer.data = (unsigned long) dev;
+
+	device_initialize(&dev->gadget.dev);
+	dev->gadget.dev.parent = &_dev->dev;
+	dev->gadget.dev.dma_mask = _dev->dev.dma_mask;
+
+	the_controller = dev;
+	platform_set_drvdata(_dev, dev);
+
+	udc_disable(dev);
+	udc_init_ep(dev);
+	udc_reinit(dev);
+
+	/* irq setup after old hardware state is cleaned up */
+	retval = request_irq(IRQ_USB, pxa27x_udc_irq,
+			SA_INTERRUPT, driver_name, dev);
+	if (retval != 0) {
+		dev_err(dev->dev, "%s: can't get irq %i, err %d\n",
+			driver_name, IRQ_USB, retval);
+		return -EBUSY;
+	}
+	dev->got_irq = 1;
+
+	create_proc_files();
+
+	return 0;
+}
+
+static int pxa27x_udc_remove(struct platform_device *_dev)
+{
+	struct pxa27x_udc *dev = platform_get_drvdata(_dev);
+
+	udc_disable(dev);
+	remove_proc_files();
+	usb_gadget_unregister_driver(dev->driver);
+
+	pxa27x_ep_freeall(&dev->gadget);
+
+	if (dev->got_irq) {
+		free_irq(IRQ_USB, dev);
+		dev->got_irq = 0;
+	}
+	platform_set_drvdata(_dev, 0);
+	the_controller = 0;
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static void pxa27x_udc_shutdown(struct platform_device *_dev)
+{
+	struct pxa27x_udc *dev = platform_get_drvdata(_dev);
+
+        udc_disable(dev);
+}
+
+static int pxa27x_udc_suspend(struct platform_device *_dev, pm_message_t state)
+{
+	int i;
+	struct pxa27x_udc *dev = platform_get_drvdata(_dev);
+
+	DMSG("%s is called\n", __FUNCTION__);
+
+	dev->udccsr0 = UDCCSR0;
+	for(i=1; (i<UDC_EP_NUM); i++) {
+		if (dev->ep[i].assigned) {
+			struct pxa27x_ep *ep = &dev->ep[i];
+				ep->udccsr_value = *ep->reg_udccsr;
+			ep->udccr_value = *ep->reg_udccr;
+			DMSG("EP%d, udccsr:0x%x, udccr:0x%x\n",
+				i, *ep->reg_udccsr, *ep->reg_udccr);
+		}
+	}
+
+	udc_clear_mask_UDCCR(UDCCR_UDE);
+	pxa_set_cken(CKEN_USB, 0);
+
+	return 0;
+}
+
+static int pxa27x_udc_resume(struct platform_device *_dev)
+{
+	int i;
+	struct pxa27x_udc *dev = platform_get_drvdata(_dev);
+
+	DMSG("%s is called\n", __FUNCTION__);
+	UDCCSR0 = dev->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME);
+	for (i=1; i < UDC_EP_NUM; i++) {
+		if (dev->ep[i].assigned) {
+			struct pxa27x_ep *ep = &dev->ep[i];
+				*ep->reg_udccsr = ep->udccsr_value;
+			*ep->reg_udccr = ep->udccr_value;
+			DMSG("EP%d, udccsr:0x%x, udccr:0x%x\n",
+					i, *ep->reg_udccsr, *ep->reg_udccr);
+		}
+	}
+
+	udc_enable(dev);
+
+	/* OTGPH bit is set when sleep mode is entered.
+	 * it indicates that OTG pad is retaining its state.
+	 * Upon exit from sleep mode and before clearing OTGPH,
+	 * Software must configure the USB OTG pad, UDC, and UHC
+	 * to the state they were in before entering sleep mode.*/
+	PSSR  |= PSSR_OTGPH;
+
+	return 0;
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static struct platform_driver udc_driver = {
+	.driver	   = {
+		.name = "pxa2xx-udc",
+	},
+	.probe     = pxa27x_udc_probe,
+	.remove    = pxa27x_udc_remove,
+#ifdef CONFIG_PM
+	.shutdown  = pxa27x_udc_shutdown,
+	.suspend   = pxa27x_udc_suspend,
+	.resume    = pxa27x_udc_resume
+#endif
+};
+
+static int __init udc_init(void)
+{
+	printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
+	return platform_driver_register(&udc_driver);
+}
+module_init(udc_init);
+
+static void __exit udc_exit(void)
+{
+	platform_driver_unregister(&udc_driver);
+}
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h
new file mode 100644
index 0000000..d4377cf
--- /dev/null
+++ b/drivers/usb/gadget/pxa27x_udc.h
@@ -0,0 +1,298 @@
+/*
+ * linux/drivers/usb/gadget/pxa27x_udc.h
+ * Intel PXA27x on-chip full speed USB device controller
+ *
+ * Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2004 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __LINUX_USB_GADGET_PXA27X_H
+#define __LINUX_USB_GADGET_PXA27X_H
+
+#include <linux/types.h>
+
+struct pxa27x_udc;
+
+struct pxa27x_ep {
+	struct pxa27x_udc			*dev;
+	struct usb_ep				*usb_ep;
+	const struct usb_endpoint_descriptor	*desc;
+
+	struct list_head			queue;
+	unsigned long				pio_irqs;
+	unsigned long				dma_irqs;
+
+	unsigned				pxa_ep_num;
+	int					dma;
+	unsigned				fifo_size;
+	unsigned				ep_type;
+
+	unsigned				stopped : 1;
+	unsigned				dma_con : 1;
+	unsigned				dir_in : 1;
+	unsigned				assigned : 1;
+
+	unsigned				ep_num;
+	unsigned				config;
+	unsigned				interface;
+	unsigned				aisn;
+	/* UDCCSR = UDC Control/Status Register for this EP
+	 * UBCR = UDC Byte Count Remaining (contents of OUT fifo)
+	 * UDCDR = UDC Endpoint Data Register (the fifo)
+	 * UDCCR = UDC Endpoint Configuration Registers
+	 * DRCM = DMA Request Channel Map
+	 */
+	volatile u32				*reg_udccsr;
+	volatile u32				*reg_udcbcr;
+	volatile u32				*reg_udcdr;
+	volatile u32				*reg_udccr;
+#ifdef USE_DMA
+	volatile u32				*reg_drcmr;
+#define	drcmr(n)  .reg_drcmr = & DRCMR ## n ,
+#else
+#define	drcmr(n)
+#endif
+
+#ifdef CONFIG_PM
+	unsigned				udccsr_value;
+	unsigned				udccr_value;
+#endif
+};
+
+struct pxa27x_virt_ep {
+	struct usb_ep				usb_ep;
+	const struct usb_endpoint_descriptor	*desc;
+	struct pxa27x_ep			*pxa_ep;
+};
+
+struct pxa27x_request {
+	struct usb_request			req;
+	struct list_head			queue;
+};
+
+enum ep0_state {
+	EP0_IDLE,
+	EP0_IN_DATA_PHASE,
+	EP0_OUT_DATA_PHASE,
+//	EP0_END_XFER,
+	EP0_STALL,
+	EP0_NO_ACTION
+};
+
+#define EP0_FIFO_SIZE	((unsigned)16)
+#define BULK_FIFO_SIZE	((unsigned)64)
+#define ISO_FIFO_SIZE	((unsigned)256)
+#define INT_FIFO_SIZE	((unsigned)8)
+
+struct udc_stats {
+	struct ep0stats {
+		unsigned long		ops;
+		unsigned long		bytes;
+	} read, write;
+	unsigned long			irqs;
+};
+
+#define	UDC_EP_NUM	24
+
+
+struct pxa27x_udc {
+	struct usb_gadget			gadget;
+	struct usb_gadget_driver		*driver;
+
+	enum ep0_state				ep0state;
+	struct udc_stats			stats;
+	unsigned				got_irq : 1,
+						has_cfr : 1,
+						req_pending : 1,
+						req_std : 1,
+						req_config : 1;
+
+#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200))
+	struct timer_list			timer;
+
+	struct device				*dev;
+	struct pxa2xx_udc_mach_info		*mach;
+	u64					dma_mask;
+	struct pxa27x_virt_ep			virt_ep0;
+	struct pxa27x_ep			ep[UDC_EP_NUM];
+	unsigned int 				ep_num;
+
+	unsigned				configuration,
+						interface,
+						alternate;
+#ifdef CONFIG_PM
+	unsigned				udccsr0;
+#endif
+};
+
+static struct pxa27x_udc *the_controller;
+
+#if 0
+/*-------------------------------------------------------------------------*/
+
+
+/* one GPIO should be used to detect host disconnect */
+static inline int is_usb_connected(void)
+{
+	if (!the_controller->mach->udc_is_connected)
+		return 1;
+	return the_controller->mach->udc_is_connected();
+}
+
+/* one GPIO should force the host to see this device (or not) */
+static inline void make_usb_disappear(void)
+{
+	if (!the_controller->mach->udc_command)
+		return;
+	the_controller->mach->udc_command(PXA27X_UDC_CMD_DISCONNECT);
+}
+
+static inline void let_usb_appear(void)
+{
+	if (!the_controller->mach->udc_command)
+		return;
+	the_controller->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Debugging support vanishes in non-debug builds.  DBG_NORMAL should be
+ * mostly silent during normal use/testing, with no timing side-effects.
+ */
+#define DBG_NORMAL	1	/* error paths, device state transitions */
+#define DBG_VERBOSE	2	/* add some success path trace info */
+#define DBG_NOISY	3	/* ... even more: request level */
+#define DBG_VERY_NOISY	4	/* ... even more: packet level */
+
+#ifdef DEBUG
+static const char *state_name[] = {
+	"EP0_IDLE",
+	"EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
+	"EP0_END_XFER", "EP0_STALL"
+};
+
+#define DMSG(stuff...) printk(KERN_ERR "udc: " stuff)
+
+#ifdef VERBOSE
+#    define UDC_DEBUG DBG_VERBOSE
+#else
+#    define UDC_DEBUG DBG_NORMAL
+#endif
+
+static void __attribute__ ((__unused__))
+dump_udccr(const char *label)
+{
+	u32	udccr = UDCCR;
+	DMSG("%s 0x%08x =%s%s%s%s%s%s%s%s%s%s, con=%d,inter=%d,altinter=%d\n",
+		label, udccr,
+		(udccr & UDCCR_OEN) ? " oen":"",
+		(udccr & UDCCR_AALTHNP) ? " aalthnp":"",
+		(udccr & UDCCR_AHNP) ? " rem" : "",
+		(udccr & UDCCR_BHNP) ? " rstir" : "",
+		(udccr & UDCCR_DWRE) ? " dwre" : "",
+		(udccr & UDCCR_SMAC) ? " smac" : "",
+		(udccr & UDCCR_EMCE) ? " emce" : "",
+		(udccr & UDCCR_UDR) ? " udr" : "",
+		(udccr & UDCCR_UDA) ? " uda" : "",
+		(udccr & UDCCR_UDE) ? " ude" : "",
+		(udccr & UDCCR_ACN) >> UDCCR_ACN_S,
+		(udccr & UDCCR_AIN) >> UDCCR_AIN_S,
+		(udccr & UDCCR_AAISN)>> UDCCR_AAISN_S );
+}
+
+static void __attribute__ ((__unused__))
+dump_udccsr0(const char *label)
+{
+	u32		udccsr0 = UDCCSR0;
+
+	DMSG("%s %s 0x%08x =%s%s%s%s%s%s%s\n",
+		label, state_name[the_controller->ep0state], udccsr0,
+		(udccsr0 & UDCCSR0_SA) ? " sa" : "",
+		(udccsr0 & UDCCSR0_RNE) ? " rne" : "",
+		(udccsr0 & UDCCSR0_FST) ? " fst" : "",
+		(udccsr0 & UDCCSR0_SST) ? " sst" : "",
+		(udccsr0 & UDCCSR0_DME) ? " dme" : "",
+		(udccsr0 & UDCCSR0_IPR) ? " ipr" : "",
+		(udccsr0 & UDCCSR0_OPC) ? " opr" : "");
+}
+
+static void __attribute__ ((__unused__))
+dump_state(struct pxa27x_udc *dev)
+{
+	unsigned	i;
+
+	DMSG("%s, udcicr %02X.%02X, udcsir %02X.%02x, udcfnr %02X\n",
+		state_name[dev->ep0state],
+		UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR);
+	dump_udccr("udccr");
+
+	if (!dev->driver) {
+		DMSG("no gadget driver bound\n");
+		return;
+	} else
+		DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
+
+
+	dump_udccsr0 ("udccsr0");
+	DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n",
+		dev->stats.write.bytes, dev->stats.write.ops,
+		dev->stats.read.bytes, dev->stats.read.ops);
+
+	for (i = 1; i < UDC_EP_NUM; i++) {
+		if (dev->ep[i].assigned)
+			DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccsr);
+	}
+}
+
+#if 0
+static void dump_regs(u8 ep)
+{
+	DMSG("EP:%d UDCCSR:0x%08x UDCBCR:0x%08x\n UDCCR:0x%08x\n",
+		ep,UDCCSN(ep), UDCBCN(ep), UDCCN(ep));
+}
+static void dump_req (struct pxa27x_request *req)
+{
+	struct usb_request *r = &req->req;
+
+	DMSG("%s: buf:0x%08x length:%d dma:0x%08x actual:%d\n",
+			__FUNCTION__, (unsigned)r->buf, r->length,
+			r->dma,	r->actual);
+}
+#endif
+
+#else
+
+#define DMSG(stuff...)		do{}while(0)
+
+#define	dump_udccr(x)	do{}while(0)
+#define	dump_udccsr0(x)	do{}while(0)
+#define	dump_state(x)	do{}while(0)
+
+#define UDC_DEBUG ((unsigned)4)
+
+#endif
+
+#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0)
+
+#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
+#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
+
+
+#endif /* __LINUX_USB_GADGET_PXA27X_H */
diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h
index 0e5d0e6..9483a49 100644
--- a/drivers/usb/gadget/pxa2xx_udc.h
+++ b/drivers/usb/gadget/pxa2xx_udc.h
@@ -206,7 +206,8 @@ dump_state(struct pxa2xx_udc *dev)
 	unsigned	i;
 
 	DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
-		is_usb_connected() ? "host " : "disconnected",
+		//is_usb_connected() ? "host " : "disconnected",
+		"host ",
 		state_name[dev->ep0state],
 		UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
 	dump_udccr("udccr");
@@ -223,8 +224,8 @@ dump_state(struct pxa2xx_udc *dev)
 	} else
 		DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
 
-	if (!is_usb_connected())
-		return;
+	//if (!is_usb_connected())
+	//	return;
 
 	dump_udccs0 ("udccs0");
 	DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n",
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index ce4d2e0..5dac23f 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -1356,6 +1356,7 @@ static int __init gs_bind(struct usb_gadget *gadget)
 	struct usb_ep *ep;
 	struct gs_dev *dev;
 	int gcnum;
+	struct usb_endpoint_config ep_config[2];
 
 	/* Some controllers can't support CDC ACM:
 	 * - sh doesn't support multiple interfaces or configs;
@@ -1376,22 +1377,33 @@ static int __init gs_bind(struct usb_gadget *gadget)
 			__constant_cpu_to_le16(GS_VERSION_NUM|0x0099);
 	}
 
+	ep_config[0].config = GS_BULK_CONFIG_ID;
+	ep_config[0].interface = gs_bulk_interface_desc.bInterfaceNumber;
+	ep_config[0].altinterface = gs_bulk_interface_desc.bAlternateSetting;
+	ep_config[1].config = GS_ACM_CONFIG_ID;
+	ep_config[1].interface = gs_data_interface_desc.bInterfaceNumber;
+	ep_config[1].altinterface = gs_data_interface_desc.bAlternateSetting;
+
 	usb_ep_autoconfig_reset(gadget);
 
-	ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc);
+	ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc, &ep_config[0], 2);
 	if (!ep)
 		goto autoconf_fail;
 	EP_IN_NAME = ep->name;
 	ep->driver_data = ep;	/* claim the endpoint */
 
-	ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc);
+	ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc, &ep_config[0], 2);
 	if (!ep)
 		goto autoconf_fail;
 	EP_OUT_NAME = ep->name;
 	ep->driver_data = ep;	/* claim the endpoint */
 
 	if (use_acm) {
-		ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc);
+		ep_config[0].config = GS_ACM_CONFIG_ID;
+		ep_config[0].interface = gs_control_interface_desc.bInterfaceNumber;
+		ep_config[0].altinterface = gs_control_interface_desc.bAlternateSetting;
+
+		ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc, &ep_config[0], 1);
 		if (!ep) {
 			printk(KERN_ERR "gs_bind: cannot run ACM on %s\n", gadget->name);
 			goto autoconf_fail;
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index fcfe869..1e40d46 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -1142,6 +1142,7 @@ zero_bind (struct usb_gadget *gadget)
 	struct zero_dev		*dev;
 	struct usb_ep		*ep;
 	int			gcnum;
+	struct usb_endpoint_config ep_config[2];
 
 	/* FIXME this can't yet work right with SH ... it has only
 	 * one configuration, numbered one.
@@ -1154,7 +1155,15 @@ zero_bind (struct usb_gadget *gadget)
 	 * but there may also be important quirks to address.
 	 */
 	usb_ep_autoconfig_reset (gadget);
-	ep = usb_ep_autoconfig (gadget, &fs_source_desc);
+
+	ep_config[0].config = CONFIG_SOURCE_SINK;
+	ep_config[0].interface = source_sink_intf.bInterfaceNumber;
+	ep_config[0].altinterface = source_sink_intf.bAlternateSetting;
+	ep_config[1].config = CONFIG_LOOPBACK;
+	ep_config[1].interface = loopback_intf.bInterfaceNumber;
+	ep_config[1].altinterface = loopback_intf.bAlternateSetting;
+
+	ep = usb_ep_autoconfig(gadget, &fs_source_desc, &ep_config[0], 2);
 	if (!ep) {
 autoconf_fail:
 		printk (KERN_ERR "%s: can't autoconfigure on %s\n",
@@ -1164,7 +1173,7 @@ autoconf_fail:
 	EP_IN_NAME = ep->name;
 	ep->driver_data = ep;	/* claim */
 	
-	ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
+	ep = usb_ep_autoconfig(gadget, &fs_sink_desc, &ep_config[0], 2);
 	if (!ep)
 		goto autoconf_fail;
 	EP_OUT_NAME = ep->name;
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 2580f5f..12e4b91 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -40,7 +40,7 @@ config BACKLIGHT_CLASS_DEVICE
 
 config BACKLIGHT_CORGI
 	tristate "Sharp Corgi Backlight Driver (SL Series)"
-	depends on BACKLIGHT_CLASS_DEVICE && PXA_SHARPSL
+	depends on BACKLIGHT_CLASS_DEVICE && (PXA_SHARPSL || MACH_EM_X270)
 	default y
 	help
 	  If you have a Sharp Zaurus SL-C7xx, SL-Cxx00 or SL-6000x say y to enable the
diff --git a/include/asm-arm/arch-pxa/pwr-i2c.h b/include/asm-arm/arch-pxa/pwr-i2c.h
new file mode 100644
index 0000000..6bad486
--- /dev/null
+++ b/include/asm-arm/arch-pxa/pwr-i2c.h
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 2007 CompuLab, Ltd
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * Simple Power I2C interface for PXA processors.
+ * Based on U-Boot PXA I2C driver.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#ifndef __PXA_PWR_I2C_H__
+#define __PXA_PWR_I2C_H__
+
+/*
+ * Configuration items.
+ */
+#define I2C_RXTX_LEN	128	/* maximum tx/rx buffer length */
+
+/*
+ * Probe the given I2C chip address.  Returns 0 if a chip responded,
+ * not 0 on failure.
+ */
+extern int pxa_pwr_i2c_probe(u8 chip);
+
+/*
+ * Read/Write interface:
+ *   chip:    I2C chip address, range 0..127
+ *   addr:    Memory (register) address within the chip
+ *   alen:    Number of bytes to use for addr (typically 1, 2 for larger
+ *              memories, 0 for register type devices with only one
+ *              register)
+ *   buffer:  Where to read/write the data
+ *   len:     How many bytes to read/write
+ *
+ *   Returns: 0 on success, not 0 on failure
+ */
+extern int pxa_pwr_i2c_read(u8 chip, uint addr, int alen, u8 *buffer, int len);
+extern int pxa_pwr_i2c_write(u8 chip, uint addr, int alen,
+			     u8 *buffer, int len);
+
+/*
+ * Utility routines to read/write registers.
+ */
+extern s32 pxa_pwr_i2c_reg_read (u8 chip, u8 reg);
+extern s32  pxa_pwr_i2c_reg_write(u8 chip, u8 reg, u8 val);
+
+#endif	/* __PXA_PWR_I2C_H___ */
diff --git a/include/linux/da9030.h b/include/linux/da9030.h
new file mode 100644
index 0000000..6eb89e2
--- /dev/null
+++ b/include/linux/da9030.h
@@ -0,0 +1,118 @@
+#ifndef _DA9030_H
+#define _DA9030_H
+
+/* DA9030 has 24 possible interrupts */
+#define DA9030_IRQ_COUNT 24
+
+/* EVENT A */
+#define DA9030_IRQ_ONKEY_EN	0
+#define DA9030_IRQ_PWREN1	1
+#define DA9030_IRQ_EXTON	2
+#define DA9030_IRQ_CHDET	3
+#define DA9030_IRQ_TBAT		4
+#define DA9030_IRQ_VBATMON	5
+#define DA9030_IRQ_VBATMONTXON	6
+#define DA9030_IRQ_CHIOVER	7
+
+/* EVENT B */
+#define DA9030_IRQ_TCTO		8
+#define DA9030_IRQ_CCTO		9
+#define DA9030_IRQ_ADC_READY	10
+#define DA9030_IRQ_VBUS_4_4	11
+#define DA9030_IRQ_VBUS_4_0	12
+#define DA9030_IRQ_SESSION_VALID 13
+#define DA9030_IRQ_SRP_DETECT	14
+#define DA9030_IRQ_WDOG_INTR	15
+
+/* EVENT C */
+#define DA9030_IRQ_LDO15	16
+#define DA9030_IRQ_LDO16	17
+#define DA9030_IRQ_LDO17	18
+#define DA9030_IRQ_LDO18	19
+#define DA9030_IRQ_LDO19	20
+#define DA9030_IRQ_BUCK2	21
+#define DA9030_IRQ_ADC_IN4	22
+#define DA9030_IRQ_ADC_IN5	23
+
+enum da9030_ldo_sleep_mode {
+	DA9030_LDO_SLEEP_KEEP		= 0x0,
+	DA9030_LDO_SLEEP_SHUT_DOWN	= 0x1,
+	DA9030_LDO_SLEEP_POWER_UP	= 0x2,
+	DA9030_LDO_SLEEP_HIGH_Z		= 0x3,
+};
+
+enum da9030_led_rate {
+	DA9030_LED_RATE_ON	= 0x0,
+	DA9030_LED_RATE_0_52	= 0x1,
+	DA9030_LED_RATE_1_05	= 0x2,
+	DA9030_LED_RATE_2_1	= 0x3,
+};
+
+enum da9030_led_duty_cycle {
+	DA9030_LED_DUTY_1_16	= 0x0,
+	DA9030_LED_DUTY_1_8	= 0x1,
+	DA9030_LED_DUTY_1_4	= 0x2,
+	DA9030_LED_DUTY_1_2	= 0x3,
+};
+
+enum da9030_led_pwm_chop {
+	DA9030_LED_PWM_8_8	= 0x0,
+	DA9030_LED_PWM_7_8	= 0x1,
+	DA9030_LED_PWM_6_8	= 0x2,
+	DA9030_LED_PWM_5_8	= 0x3,
+	DA9030_LED_PWM_4_8	= 0x4,
+	DA9030_LED_PWM_3_8	= 0x5,
+	DA9030_LED_PWM_2_8	= 0x6,
+	DA9030_LED_PWM_1_8	= 0x7,
+};
+
+struct da9030_adc_res {
+	int vbat_res;
+	int vbatmin_res;
+	int vbatmintxon;
+	int ichmax_res;
+	int ichmin_res;
+	int ichaverage_res;
+	int vchmax_res;
+	int vchmin_res;
+	int tbat_res;
+	int adc_in4_res;
+	int adc_in5_res;
+};
+
+extern int da9030_get_status(void);
+extern int da9030_get_fault_log(void);
+
+extern void da9030_enable_adc(void);
+extern void da9030_read_adc(struct da9030_adc_res *adc);
+
+extern void da9030_set_wled(int on, unsigned int brightness);
+
+extern int da9030_set_led(int led, int on,
+			  enum da9030_led_rate rate,
+			  enum da9030_led_duty_cycle duty,
+			  enum da9030_led_pwm_chop pwm_chop);
+
+extern int da9030_set_charger(int on, unsigned int mA, unsigned int mV);
+extern void da9030_get_charger(int *on, unsigned int *mA, unsigned int *mV);
+
+extern int da9030_set_ldo(int ldo, int on, unsigned int mV,
+			  enum da9030_ldo_sleep_mode sleep_mode);
+extern int da9030_set_buck(int buck, int on, unsigned int mV, int flags);
+
+extern void da9030_set_thresholds(unsigned int tbathighp,
+				  unsigned int tbathighn,
+				  unsigned int tbatlow,
+				  unsigned int vbatmon);
+
+extern int da9030_register_callback(int event,
+				    void (*callback)(int event, void *data),
+				    void *data);
+extern void da9030_unregister_callback(int event);
+
+/* Keep these for now, although they are unsafe. When I'll see what
+   other methods are needed from DA9030, I'll remove these */
+extern s32 da9030_get_reg(u32 reg);
+extern s32 da9030_set_reg(u32 reg, u8 val);
+
+#endif
diff --git a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h
index 4f59b2a..792c568 100644
--- a/include/linux/usb_gadget.h
+++ b/include/linux/usb_gadget.h
@@ -397,10 +397,28 @@ usb_ep_fifo_flush (struct usb_ep *ep)
 
 struct usb_gadget;
 
+/**
+ * struct usb_endpoint_config - possible configurations of a given endpoint
+ * @config: the configuration number
+ * @interface: the interface number
+ * @altinterface: the altinterface number
+ *
+ * Used as an array to pass information about the possible configurations
+ * of a given endpoint to the bus controller.
+ */
+struct usb_endpoint_config {
+	u8	config;
+	u8	interface;
+	u8	altinterface;
+};
+
 /* the rest of the api to the controller hardware: device operations,
  * which don't involve endpoints (or i/o).
  */
 struct usb_gadget_ops {
+	struct usb_ep* (*ep_alloc)(struct usb_gadget *,
+				struct usb_endpoint_descriptor *,
+				struct usb_endpoint_config *, int);
 	int	(*get_frame)(struct usb_gadget *);
 	int	(*wakeup)(struct usb_gadget *);
 	int	(*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
@@ -824,7 +842,10 @@ int usb_gadget_config_buf(const struct usb_config_descriptor *config,
 /* utility wrapping a simple endpoint selection policy */
 
 extern struct usb_ep *usb_ep_autoconfig (struct usb_gadget *,
-			struct usb_endpoint_descriptor *) __devinit;
+			struct usb_endpoint_descriptor *,
+			struct usb_endpoint_config *,
+			int numconfigs
+);
 
 extern void usb_ep_autoconfig_reset (struct usb_gadget *) __devinit;
 
diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h
new file mode 100644
index 0000000..354e533
--- /dev/null
+++ b/include/linux/wm97xx.h
@@ -0,0 +1,291 @@
+
+/*
+ * Register bits and API for Wolfson WM97xx series of codecs
+ */
+
+#ifndef _LINUX_WM97XX_H
+#define _LINUX_WM97XX_H
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/input.h>	/* Input device layer */
+
+/*
+ * WM97xx AC97 Touchscreen registers
+ */
+#define AC97_WM97XX_DIGITISER1		0x76
+#define AC97_WM97XX_DIGITISER2		0x78
+#define AC97_WM97XX_DIGITISER_RD 	0x7a
+#define AC97_WM9713_DIG1			0x74
+#define AC97_WM9713_DIG2			AC97_WM97XX_DIGITISER1
+#define AC97_WM9713_DIG3			AC97_WM97XX_DIGITISER2
+
+/*
+ * WM97xx register bits
+ */
+#define WM97XX_POLL			0x8000	/* initiate a polling measurement */
+#define WM97XX_ADCSEL_X		0x1000	/* x coord measurement */
+#define WM97XX_ADCSEL_Y		0x2000	/* y coord measurement */
+#define WM97XX_ADCSEL_PRES	0x3000	/* pressure measurement */
+#define WM97XX_ADCSEL_MASK	0x7000
+#define WM97XX_COO			0x0800	/* enable coordinate mode */
+#define WM97XX_CTC			0x0400	/* enable continuous mode */
+#define WM97XX_CM_RATE_93	0x0000	/* 93.75Hz continuous rate */
+#define WM97XX_CM_RATE_187	0x0100	/* 187.5Hz continuous rate */
+#define WM97XX_CM_RATE_375	0x0200	/* 375Hz continuous rate */
+#define WM97XX_CM_RATE_750	0x0300	/* 750Hz continuous rate */
+#define WM97XX_CM_RATE_8K	0x00f0	/* 8kHz continuous rate */
+#define WM97XX_CM_RATE_12K	0x01f0	/* 12kHz continuous rate */
+#define WM97XX_CM_RATE_24K	0x02f0	/* 24kHz continuous rate */
+#define WM97XX_CM_RATE_48K	0x03f0	/* 48kHz continuous rate */
+#define WM97XX_CM_RATE_MASK	0x03f0
+#define WM97XX_RATE(i)		(((i & 3) << 8) | ((i & 4) ? 0xf0 : 0))
+#define WM97XX_DELAY(i)		((i << 4) & 0x00f0)	/* sample delay times */
+#define WM97XX_DELAY_MASK	0x00f0
+#define WM97XX_SLEN			0x0008	/* slot read back enable */
+#define WM97XX_SLT(i)		((i - 5) & 0x7)	/* touchpanel slot selection (5-11) */
+#define WM97XX_SLT_MASK		0x0007
+#define WM97XX_PRP_DETW		0x4000	/* pen detect on, digitiser off, wake up */
+#define WM97XX_PRP_DET		0x8000	/* pen detect on, digitiser off, no wake up */
+#define WM97XX_PRP_DET_DIG	0xc000	/* pen detect on, digitiser on */
+#define WM97XX_RPR			0x2000	/* wake up on pen down */
+#define WM97XX_PEN_DOWN		0x8000	/* pen is down */
+#define WM97XX_ADCSRC_MASK	0x7000	/* ADC source mask */
+
+#define WM97XX_AUX_ID1		0x8001
+#define WM97XX_AUX_ID2		0x8002
+#define WM97XX_AUX_ID3		0x8003
+#define WM97XX_AUX_ID4		0x8004
+
+
+/* WM9712 Bits */
+#define WM9712_45W			0x1000	/* set for 5-wire touchscreen */
+#define WM9712_PDEN			0x0800	/* measure only when pen down */
+#define WM9712_WAIT			0x0200	/* wait until adc is read before next sample */
+#define WM9712_PIL			0x0100	/* current used for pressure measurement. set 400uA else 200uA */
+#define WM9712_MASK_HI		0x0040	/* hi on mask pin (47) stops conversions */
+#define WM9712_MASK_EDGE	0x0080	/* rising/falling edge on pin delays sample */
+#define	WM9712_MASK_SYNC	0x00c0	/* rising/falling edge on mask initiates sample */
+#define WM9712_RPU(i)		(i&0x3f)	/* internal pull up on pen detect (64k / rpu) */
+#define WM9712_PD(i)		(0x1 << i)	/* power management */
+
+/* WM9712 Registers */
+#define AC97_WM9712_POWER	0x24
+#define AC97_WM9712_REV		0x58
+
+/* WM9705 Bits */
+#define WM9705_PDEN			0x1000	/* measure only when pen is down */
+#define WM9705_PINV			0x0800	/* inverts sense of pen down output */
+#define WM9705_BSEN			0x0400	/* BUSY flag enable, pin47 is 1 when busy */
+#define WM9705_BINV			0x0200	/* invert BUSY (pin47) output */
+#define WM9705_WAIT			0x0100	/* wait until adc is read before next sample */
+#define WM9705_PIL			0x0080	/* current used for pressure measurement. set 400uA else 200uA */
+#define WM9705_PHIZ			0x0040	/* set PHONE and PCBEEP inputs to high impedance */
+#define WM9705_MASK_HI		0x0010	/* hi on mask stops conversions */
+#define WM9705_MASK_EDGE	0x0020	/* rising/falling edge on pin delays sample */
+#define	WM9705_MASK_SYNC	0x0030	/* rising/falling edge on mask initiates sample */
+#define WM9705_PDD(i)		(i & 0x000f)	/* pen detect comparator threshold */
+
+
+/* WM9713 Bits */
+#define WM9713_PDPOL		0x0400	/* Pen down polarity */
+#define WM9713_POLL			0x0200	/* initiate a polling measurement */
+#define WM9713_CTC			0x0100	/* enable continuous mode */
+#define WM9713_ADCSEL_X		0x0002	/* X measurement */
+#define WM9713_ADCSEL_Y		0x0004	/* Y measurement */
+#define WM9713_ADCSEL_PRES	0x0008	/* Pressure measurement */
+#define WM9713_COO			0x0001	/* enable coordinate mode */
+#define WM9713_PDEN			0x0800	/* measure only when pen down */
+#define WM9713_ADCSEL_MASK	0x00fe	/* ADC selection mask */
+#define WM9713_WAIT			0x0200	/* coordinate wait */
+
+/* AUX ADC ID's */
+#define TS_COMP1			0x0
+#define TS_COMP2			0x1
+#define TS_BMON				0x2
+#define TS_WIPER			0x3
+
+/* ID numbers */
+#define WM97XX_ID1			0x574d
+#define WM9712_ID2			0x4c12
+#define WM9705_ID2			0x4c05
+#define WM9713_ID2			0x4c13
+
+/* Codec GPIO's */
+#define WM97XX_MAX_GPIO		16
+#define WM97XX_GPIO_1		(1 << 1)
+#define WM97XX_GPIO_2		(1 << 2)
+#define WM97XX_GPIO_3		(1 << 3)
+#define WM97XX_GPIO_4		(1 << 4)
+#define WM97XX_GPIO_5		(1 << 5)
+#define WM97XX_GPIO_6		(1 << 6)
+#define WM97XX_GPIO_7		(1 << 7)
+#define WM97XX_GPIO_8		(1 << 8)
+#define WM97XX_GPIO_9		(1 << 9)
+#define WM97XX_GPIO_10		(1 << 10)
+#define WM97XX_GPIO_11		(1 << 11)
+#define WM97XX_GPIO_12		(1 << 12)
+#define WM97XX_GPIO_13		(1 << 13)
+#define WM97XX_GPIO_14		(1 << 14)
+#define WM97XX_GPIO_15		(1 << 15)
+
+
+#define AC97_LINK_FRAME		21	/* time in uS for AC97 link frame */
+
+
+/*---------------- Return codes from sample reading functions ---------------*/
+
+/* More data is available; call the sample gathering function again */
+#define RC_AGAIN			0x00000001
+/* The returned sample is valid */
+#define RC_VALID			0x00000002
+/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */
+#define RC_PENUP			0x00000004
+/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful
+   to tell the handler that the pen is down but we don't know yet his coords,
+   so the handler should not sleep or wait for pendown irq) */
+#define RC_PENDOWN			0x00000008
+
+/* The wm97xx driver provides a private API for writing platform-specific
+ * drivers.
+ */
+
+/* The structure used to return arch specific sampled data into */
+struct wm97xx_data {
+    int x;
+    int y;
+    int p;
+};
+
+/* Codec GPIO status
+ */
+typedef enum {
+    WM97XX_GPIO_HIGH,
+    WM97XX_GPIO_LOW
+} wm97xx_gpio_status_t;
+
+/* Codec GPIO direction
+ */
+typedef enum {
+    WM97XX_GPIO_IN,
+    WM97XX_GPIO_OUT
+} wm97xx_gpio_dir_t;
+
+/* Codec GPIO polarity
+ */
+typedef enum {
+    WM97XX_GPIO_POL_HIGH,
+    WM97XX_GPIO_POL_LOW
+} wm97xx_gpio_pol_t;
+
+/* Codec GPIO sticky
+ */
+typedef enum {
+    WM97XX_GPIO_STICKY,
+    WM97XX_GPIO_NOTSTICKY
+} wm97xx_gpio_sticky_t;
+
+/* Codec GPIO wake
+ */
+typedef enum {
+    WM97XX_GPIO_WAKE,
+    WM97XX_GPIO_NOWAKE
+} wm97xx_gpio_wake_t;
+
+
+/*
+ * Digitiser ioctl commands
+ */
+#define WM97XX_DIG_START	0x1
+#define WM97XX_DIG_STOP		0x2
+#define WM97XX_PHY_INIT		0x3
+#define WM97XX_AUX_PREPARE	0x4
+#define WM97XX_DIG_RESTORE	0x5
+
+struct wm97xx;
+extern struct wm97xx_codec_drv wm97xx_codec;
+
+/*
+ * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs
+ */
+struct wm97xx_codec_drv {
+	u16 id;
+    char *name;
+	int (*poll_sample) (struct wm97xx *, int adcsel, int *sample);	/* read 1 sample */
+	int (*poll_touch) (struct wm97xx *, struct wm97xx_data *);	/* read X,Y,[P] in poll */
+	int (*digitiser_ioctl) (struct wm97xx *, int cmd);
+	int (*acc_enable) (struct wm97xx *, int enable);
+};
+
+
+/* Machine specific and accelerated touch operations */
+struct wm97xx_mach_ops {
+
+	/* accelerated touch readback - coords are transmited on AC97 link */
+	int acc_enabled;
+    void (*acc_pen_up) (struct wm97xx *);
+    int (*acc_pen_down) (struct wm97xx *);
+    int (*acc_startup) (struct wm97xx *);
+    void (*acc_shutdown) (struct wm97xx *);
+
+    /* pre and post sample - can be used to minimise any analog noise */
+    void (*pre_sample) (int);  /* function to run before sampling */
+    void (*post_sample) (int);  /* function to run after sampling */
+};
+
+struct wm97xx {
+	u16 dig[3], id, gpio[6], misc;	/* Cached codec registers */
+	u16 dig_save[3];		/* saved during aux reading */
+	struct wm97xx_codec_drv *codec;	/* attached codec driver*/
+	struct input_dev* input_dev;	/* touchscreen input device */
+	struct snd_ac97 *ac97;		/* ALSA codec access */
+	struct device *dev;		/* ALSA device */
+    struct device *battery_dev;
+    struct device *touch_dev;
+    struct wm97xx_mach_ops *mach_ops;
+    struct mutex codec_mutex;
+	struct completion ts_init;
+	struct completion ts_exit;
+	struct task_struct *ts_task;
+	unsigned int pen_irq;	/* Pen IRQ number in use */
+	wait_queue_head_t pen_irq_wait;	/* Pen IRQ wait queue */
+	struct workqueue_struct *pen_irq_workq;
+	struct work_struct pen_event_work;
+	u16 acc_slot; /* AC97 slot used for acc touch data */
+	u16 acc_rate; /* acc touch data rate */
+	unsigned int ts_use_count;
+	unsigned pen_is_down:1;	/* Pen is down */
+	unsigned aux_waiting:1;	/* aux measurement waiting */
+	unsigned pen_probably_down:1;	/* used in polling mode */
+};
+
+/* Codec GPIO access (not supported on WM9705)
+ * This can be used to set/get codec GPIO and Virtual GPIO status.
+ */
+wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio);
+void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
+			  wm97xx_gpio_status_t status);
+void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio,
+				     wm97xx_gpio_dir_t dir,
+				     wm97xx_gpio_pol_t pol,
+				     wm97xx_gpio_sticky_t sticky,
+				     wm97xx_gpio_wake_t wake);
+
+/* codec AC97 IO access */
+int wm97xx_reg_read(struct wm97xx *wm, u16 reg);
+void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val);
+
+/* aux adc readback */
+int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel);
+
+/* machine ops */
+int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *);
+void wm97xx_unregister_mach_ops(struct wm97xx *);
+
+extern struct bus_type wm97xx_bus_type;
+#endif
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index a83e229..89b5730 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -53,3 +53,12 @@ config SND_PXA2XX_SOC_TOSA
 	help
 	  Say Y if you want to add support for SoC audio on Sharp
 	  Zaurus SL-C6000x models (Tosa).
+
+config SND_PXA2XX_SOC_EM_X270
+	tristate "SoC Audio support for CompuLab EM-x270"
+	depends on SND_PXA2XX_SOC && MACH_EM_X270
+	select SND_PXA2XX_SOC_AC97
+	select SND_SOC_WM9712
+	help
+	  Say Y if you want to add support for SoC audio on 
+	  CompuLab EM-x270.
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 78e0d6b..32375ac 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -12,9 +12,11 @@ snd-soc-corgi-objs := corgi.o
 snd-soc-poodle-objs := poodle.o
 snd-soc-tosa-objs := tosa.o
 snd-soc-spitz-objs := spitz.o
+snd-soc-em-x270-objs := em-x270.o
 
 obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
 obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
 obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
 obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
+obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
 
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c
new file mode 100644
index 0000000..9e891d0
--- /dev/null
+++ b/sound/soc/pxa/em-x270.c
@@ -0,0 +1,137 @@
+/*
+ * em-x270.c  --  SoC audio for EM-X270
+ *
+ * Copyright 2007 CompuLab, Ltd.
+ *
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copied from tosa.c:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ *          Richard Purdie <richard@openedhand.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/audio.h>
+
+#include "../codecs/wm9712.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+static struct snd_soc_machine em_x270;
+
+#define EM_X270_HP        0
+#define EM_X270_MIC_INT   1
+#define EM_X270_HEADSET   2
+#define EM_X270_HP_OFF    3
+#define EM_X270_SPK_ON    0
+#define EM_X270_SPK_OFF   1
+
+
+static struct snd_soc_ops em_x270_ops = {
+};
+
+static const struct snd_kcontrol_new em_x270_controls[] = {
+};
+
+static int em_x270_ac97_init(struct snd_soc_codec *codec)
+{
+	int i, err;
+
+	/* add em_x270 specific controls */
+	for (i = 0; i < ARRAY_SIZE(em_x270_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				snd_soc_cnew(&em_x270_controls[i],codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	snd_soc_dapm_sync_endpoints(codec);
+	return 0;
+}
+
+static struct snd_soc_dai_link em_x270_dai[] = {
+	{
+		.name = "AC97",
+		.stream_name = "AC97 HiFi",
+		.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+		.codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+		.init = em_x270_ac97_init,
+		.ops = &em_x270_ops,
+	},
+	{
+		.name = "AC97 Aux",
+		.stream_name = "AC97 Aux",
+		.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+		.codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+		.ops = &em_x270_ops,
+	},
+};
+
+static struct snd_soc_machine em_x270 = {
+	.name = "EM-X270",
+	.dai_link = em_x270_dai,
+	.num_links = ARRAY_SIZE(em_x270_dai),
+};
+
+static struct snd_soc_device em_x270_snd_devdata = {
+	.machine = &em_x270,
+	.platform = &pxa2xx_soc_platform,
+	.codec_dev = &soc_codec_dev_wm9712,
+};
+
+static struct platform_device *em_x270_snd_device;
+
+static int __init em_x270_init(void)
+{
+	int ret;
+
+	if (!machine_is_em_x270())
+		return -ENODEV;
+
+	em_x270_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!em_x270_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(em_x270_snd_device, &em_x270_snd_devdata);
+	em_x270_snd_devdata.dev = &em_x270_snd_device->dev;
+	ret = platform_device_add(em_x270_snd_device);
+
+	if (ret)
+		platform_device_put(em_x270_snd_device);
+
+	return ret;
+}
+
+static void __exit em_x270_exit(void)
+{
+	platform_device_unregister(em_x270_snd_device);
+}
+
+module_init(em_x270_init);
+module_exit(em_x270_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mike Rapoport");
+MODULE_DESCRIPTION("ALSA SoC EM-X270");
+MODULE_LICENSE("GPL");