From 85d6ef746cd7d1e2e77f963c73ecc75bd7b57ae9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Fabian=20Silva=20Delgado?=
 <emulatorman@parabola.nu>
Date: Thu, 12 Mar 2015 03:43:53 -0300
Subject: linux-libre-lts-knock: update loongson-community patch

---
 .../3.14.14-a410a5e2b7-loongson-community.patch    | 10869 ------------------
 .../3.14.26-8475f027b4-loongson-community.patch    | 10872 +++++++++++++++++++
 kernels/linux-libre-lts-knock/PKGBUILD             |     6 +-
 3 files changed, 10875 insertions(+), 10872 deletions(-)
 delete mode 100644 kernels/linux-libre-lts-knock/3.14.14-a410a5e2b7-loongson-community.patch
 create mode 100644 kernels/linux-libre-lts-knock/3.14.26-8475f027b4-loongson-community.patch

(limited to 'kernels')

diff --git a/kernels/linux-libre-lts-knock/3.14.14-a410a5e2b7-loongson-community.patch b/kernels/linux-libre-lts-knock/3.14.14-a410a5e2b7-loongson-community.patch
deleted file mode 100644
index c7a63c2e5..000000000
--- a/kernels/linux-libre-lts-knock/3.14.14-a410a5e2b7-loongson-community.patch
+++ /dev/null
@@ -1,10869 +0,0 @@
-diff --git a/Makefile b/Makefile
-index 230c7f6..69dfbc4 100644
---- a/Makefile
-+++ b/Makefile
-@@ -244,8 +244,8 @@ CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
- 
- HOSTCC       = gcc
- HOSTCXX      = g++
--HOSTCFLAGS   = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer
--HOSTCXXFLAGS = -O2
-+HOSTCFLAGS   = -Wall -Wmissing-prototypes -Wstrict-prototypes -O3 -fomit-frame-pointer
-+HOSTCXXFLAGS = -O3
- 
- # Decide whether to build built-in, modular, or both.
- # Normally, just do built-in.
-@@ -582,7 +582,7 @@ all: vmlinux
- ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
- KBUILD_CFLAGS	+= -Os $(call cc-disable-warning,maybe-uninitialized,)
- else
--KBUILD_CFLAGS	+= -O2
-+KBUILD_CFLAGS	+= -O3
- endif
- 
- include $(srctree)/arch/$(SRCARCH)/Makefile
-diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
-index 95fa1f1..82dc7e8 100644
---- a/arch/mips/Kconfig
-+++ b/arch/mips/Kconfig
-@@ -278,7 +278,7 @@ config LASAT
- 
- config MACH_LOONGSON
- 	bool "Loongson family of machines"
--	select SYS_SUPPORTS_ZBOOT
-+	select SYS_SUPPORTS_ZBOOT_UART16550
- 	help
- 	  This enables the support of Loongson family of machines.
- 
-@@ -885,6 +885,60 @@ config CSRC_IOASIC
- config CSRC_R4K
- 	bool
- 
-+config MIPS_USER_RDTSC
-+	bool "Emulate rdtsc instruction for MIPS"
-+	depends on CSRC_R4K && MIPS32_O32
-+	default n
-+	help
-+	  This optoin enables the Emulated rdtsc support for MIPS, which allows
-+	  the user-space applications read the R4k count directly. Currently,
-+	  this only support the CONFIG_MIPS32_O32 and R4K, but future, we may
-+	  add support for scall64-{n32,64}.S and scall32-32.S and for the count
-+	  registers provided by the other MIPS variants.
-+
-+	  This emulation based on the syscall instruction, by default, the
-+	  syscall is encoded as 0x0000000c, except the 0xc, the other parts can
-+	  be encoded as specific meaning. when a syscall instruction is issued,
-+	  through checking the encoding of the instruction, when the encoding
-+	  is the generic 0x000000c, we do the generic syscall work, if
-+	  something other is encoded in, we can do relevant things, except for
-+	  the light-weight things, such as read a register. herein, we read the
-+	  count register whenever there is something encoded in the syscall
-+	  instruction. In the future, we may be possible to abstract more
-+	  light-weight & frequently-used operations and add a
-+	  sys_call_table-like table to store the entries of some light-weight
-+	  operations and encode 1,2,3... into the syscall instruction and jump
-+	  to respective entry for diffrent numbers, as a result, we get
-+	  fast-syscall and which may speed up the user-space applications and
-+	  even be possibly improve the determinism.
-+
-+	  *Example*
-+
-+	  #include <stdio.h>
-+	  #include <stdint.h>
-+
-+	  /*
-+	   * Currently, our return value is only 32bit, In the long run,
-+	   * this should be uint64_t, just like clock_gettime(), but it
-+	   * should has high precision/low overhead than clock_gettime()
-+	   */
-+	  uint32_t rdtsc(void)
-+	  {
-+		  /*
-+		   * Linux will store the value of the count register into
-+		   * the v0 register, which is just the return value of this
-+		   * function, so, please ignore the compiling warning.
-+		   */
-+		  __asm__ __volatile__ (
-+			  "syscall 1\n"
-+		  :::"$2");
-+	  }
-+
-+	  int main(int argc, char *argv[])
-+	  {
-+		  return printf("cycles: %u\n", rdtsc());
-+	  }
-+
- config CSRC_GIC
- 	bool
- 
-@@ -1492,6 +1546,15 @@ config CPU_LOONGSON2
- 	bool
- 	select CPU_SUPPORTS_32BIT_KERNEL
- 	select CPU_SUPPORTS_64BIT_KERNEL
-+	select CPU_SUPPORTS_HIGHMEM if ! EMBEDDED
-+	select ARCH_WANT_OPTIONAL_GPIOLIB
-+
-+config CPU_LOONGSON1
-+	bool
-+	select CPU_MIPS32
-+	select CPU_MIPSR2
-+	select CPU_HAS_PREFETCH
-+	select CPU_SUPPORTS_32BIT_KERNEL
- 	select CPU_SUPPORTS_HIGHMEM
- 	select CPU_SUPPORTS_HUGEPAGES
- 
-@@ -2110,7 +2173,7 @@ config SYS_SUPPORTS_MICROMIPS
- 
- config ARCH_FLATMEM_ENABLE
- 	def_bool y
--	depends on !NUMA && !CPU_LOONGSON2
-+	depends on !NUMA && !(CPU_LOONGSON2 && HIBERNATION)
- 
- config ARCH_DISCONTIGMEM_ENABLE
- 	bool
-diff --git a/arch/mips/Kconfig.debug b/arch/mips/Kconfig.debug
-index b147e70..f32dfb0 100644
---- a/arch/mips/Kconfig.debug
-+++ b/arch/mips/Kconfig.debug
-@@ -7,9 +7,9 @@ config TRACE_IRQFLAGS_SUPPORT
- source "lib/Kconfig.debug"
- 
- config EARLY_PRINTK
--	bool "Early printk" if EXPERT
-+	bool "Early printk"
- 	depends on SYS_HAS_EARLY_PRINTK
--	default y
-+	default n 
- 	help
- 	  This option enables special console drivers which allow the kernel
- 	  to print messages very early in the bootup process.
-diff --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile
-index 61af6b6..8598044 100644
---- a/arch/mips/boot/compressed/Makefile
-+++ b/arch/mips/boot/compressed/Makefile
-@@ -30,9 +30,10 @@ KBUILD_AFLAGS := $(LINUXINCLUDE) $(KBUILD_AFLAGS) -D__ASSEMBLY__ \
- targets := head.o decompress.o string.o dbg.o uart-16550.o uart-alchemy.o
- 
- # decompressor objects (linked with vmlinuz)
--vmlinuzobjs-y := $(obj)/head.o $(obj)/decompress.o $(obj)/string.o $(obj)/dbg.o
-+vmlinuzobjs-y := $(obj)/head.o $(obj)/decompress.o $(obj)/string.o
- 
- ifdef CONFIG_DEBUG_ZBOOT
-+vmlinuzobjs-y += $(obj)/dbg.o
- vmlinuzobjs-$(CONFIG_SYS_SUPPORTS_ZBOOT_UART16550) += $(obj)/uart-16550.o
- vmlinuzobjs-$(CONFIG_MIPS_ALCHEMY)		   += $(obj)/uart-alchemy.o
- endif
-@@ -79,9 +80,18 @@ quiet_cmd_zld = LD	$@
-       cmd_zld = $(LD) $(LDFLAGS) -Ttext $(VMLINUZ_LOAD_ADDRESS) -T $< $(vmlinuzobjs-y) -o $@
- quiet_cmd_strip = STRIP	  $@
-       cmd_strip = $(STRIP) -s $@
-+ifdef CONFIG_EMBEDDED
-+quiet_cmd_sstrip = SSTRIP  $@
-+      cmd_sstrip = $(srctree)/scripts/sstrip.sh $@
-+endif
- vmlinuz: $(src)/ld.script $(vmlinuzobjs-y) $(obj)/calc_vmlinuz_load_addr
- 	$(call cmd,zld)
- 	$(call cmd,strip)
-+	$(call cmd,sstrip)
-+
-+vmlinuz.unsstrip: $(src)/ld.script $(vmlinuzobjs-y) $(obj)/calc_vmlinuz_load_addr
-+	$(call cmd,zld)
-+	$(call cmd,strip)
- 
- #
- # Some DECstations need all possible sections of an ECOFF executable
-@@ -94,14 +104,14 @@ endif
- hostprogs-y += ../elf2ecoff
- 
- ifdef CONFIG_32BIT
--	VMLINUZ = vmlinuz
-+	VMLINUZ = vmlinuz.unsstrip
- else
- 	VMLINUZ = vmlinuz.32
- endif
- 
- quiet_cmd_32 = OBJCOPY $@
-       cmd_32 = $(OBJCOPY) -O $(32bit-bfd) $(OBJCOPYFLAGS) $< $@
--vmlinuz.32: vmlinuz
-+vmlinuz.32: vmlinuz.unsstrip
- 	$(call cmd,32)
- 
- quiet_cmd_ecoff = ECOFF	  $@
-@@ -110,11 +120,11 @@ vmlinuz.ecoff: $(obj)/../elf2ecoff $(VMLINUZ)
- 	$(call cmd,ecoff)
- 
- OBJCOPYFLAGS_vmlinuz.bin := $(OBJCOPYFLAGS) -O binary
--vmlinuz.bin: vmlinuz
-+vmlinuz.bin: vmlinuz.unsstrip
- 	$(call cmd,objcopy)
- 
- OBJCOPYFLAGS_vmlinuz.srec := $(OBJCOPYFLAGS) -S -O srec
--vmlinuz.srec: vmlinuz
-+vmlinuz.srec: vmlinuz.unsstrip
- 	$(call cmd,objcopy)
- 
--clean-files := $(objtree)/vmlinuz $(objtree)/vmlinuz.{32,ecoff,bin,srec}
-+clean-files := $(objtree)/vmlinuz $(objtree)/vmlinuz.{32,ecoff,bin,srec,unsstrip}
-diff --git a/arch/mips/boot/compressed/decompress.c b/arch/mips/boot/compressed/decompress.c
-index c00c4dd..f4a656d 100644
---- a/arch/mips/boot/compressed/decompress.c
-+++ b/arch/mips/boot/compressed/decompress.c
-@@ -27,8 +27,13 @@ unsigned long free_mem_end_ptr;
- extern unsigned char __image_begin, __image_end;
- 
- /* debug interfaces  */
-+#ifdef CONFIG_DEBUG_ZBOOT
- extern void puts(const char *s);
- extern void puthex(unsigned long long val);
-+#else
-+#define puts(s)
-+#define puthex(val)
-+#endif
- 
- void error(char *x)
- {
-diff --git a/arch/mips/boot/compressed/ld.script b/arch/mips/boot/compressed/ld.script
-index 5a33409..de04ac9 100644
---- a/arch/mips/boot/compressed/ld.script
-+++ b/arch/mips/boot/compressed/ld.script
-@@ -49,5 +49,6 @@ SECTIONS
- 		*(.reginfo)
- 		*(.comment)
- 		*(.note)
-+		*(.gnu.attributes)
- 	}
- }
-diff --git a/arch/mips/include/asm/dma-mapping.h b/arch/mips/include/asm/dma-mapping.h
-index 84238c5..28e0b03 100644
---- a/arch/mips/include/asm/dma-mapping.h
-+++ b/arch/mips/include/asm/dma-mapping.h
-@@ -6,9 +6,7 @@
- #include <asm/cache.h>
- #include <asm-generic/dma-coherent.h>
- 
--#ifndef CONFIG_SGI_IP27 /* Kludge to fix 2.6.39 build for IP27 */
- #include <dma-coherence.h>
--#endif
- 
- extern struct dma_map_ops *mips_dma_map_ops;
- 
-diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
-index a0ee0cb..c6df1c4 100644
---- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
-+++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
-@@ -299,7 +299,42 @@ extern void _wrmsr(u32 msr, u32 hi, u32 lo);
- 
- /****************** NATIVE ***************************/
- /* GPIO : I/O SPACE; REG : 32BITS */
--#define GPIOL_OUT_VAL		0x00
--#define GPIOL_OUT_EN		0x04
-+#define	GPIOL_OUT_VAL		0x00
-+#define	GPIOL_OUT_EN		0x04
-+#define	GPIOL_OUT_AUX1_SEL	0x10
-+/* SMB : I/O SPACE, REG : 8BITS WIDTH */
-+#define	SMB_SDA			0x00
-+#define	SMB_STS			0x01
-+#define	SMB_STS_SLVSTP		(1 << 7)
-+#define	SMB_STS_SDAST		(1 << 6)
-+#define	SMB_STS_BER		(1 << 5)
-+#define	SMB_STS_NEGACK		(1 << 4)
-+#define	SMB_STS_STASTR		(1 << 3)
-+#define	SMB_STS_NMATCH		(1 << 2)
-+#define	SMB_STS_MASTER		(1 << 1)
-+#define	SMB_STS_XMIT		(1 << 0)
-+#define	SMB_CTRL_STS		0x02
-+#define	SMB_CSTS_TGSTL		(1 << 5)
-+#define	SMB_CSTS_TSDA		(1 << 4)
-+#define	SMB_CSTS_GCMTCH		(1 << 3)
-+#define	SMB_CSTS_MATCH		(1 << 2)
-+#define	SMB_CSTS_BB		(1 << 1)
-+#define	SMB_CSTS_BUSY		(1 << 0)
-+#define	SMB_CTRL1		0x03
-+#define	SMB_CTRL1_STASTRE	(1 << 7)
-+#define	SMB_CTRL1_NMINTE	(1 << 6)
-+#define	SMB_CTRL1_GCMEN		(1 << 5)
-+#define	SMB_CTRL1_ACK		(1 << 4)
-+#define	SMB_CTRL1_RSVD		(1 << 3)
-+#define	SMB_CTRL1_INTEN		(1 << 2)
-+#define	SMB_CTRL1_STOP		(1 << 1)
-+#define	SMB_CTRL1_START		(1 << 0)
-+#define	SMB_ADDR		0x04
-+#define	SMB_ADDR_SAEN		(1 << 7)
-+#define	SMB_CONTROLLER_ADDR	(0xef << 0)
-+#define	SMB_CTRL2		0x05
-+#define	SMB_FREQ		(0x20 << 1)
-+#define	SMB_ENABLE		(0x01 << 0)
-+#define	SMB_CTRL3		0x06
- 
- #endif				/* _CS5536_H */
-diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
-index 021d017..d058e46 100644
---- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
-+++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
-@@ -10,26 +10,45 @@
- 
- #ifdef CONFIG_CS5536_MFGPT
- extern void setup_mfgpt0_timer(void);
--extern void disable_mfgpt0_counter(void);
--extern void enable_mfgpt0_counter(void);
-+extern void disable_mfgpt_counter(void);
-+extern void enable_mfgpt_counter(void);
- #else
- static inline void __maybe_unused setup_mfgpt0_timer(void)
- {
- }
--static inline void __maybe_unused disable_mfgpt0_counter(void)
-+static inline void __maybe_unused disable_mfgpt_counter(void)
- {
- }
--static inline void __maybe_unused enable_mfgpt0_counter(void)
-+static inline void __maybe_unused enable_mfgpt_counter(void)
- {
- }
- #endif
- 
--#define MFGPT_TICK_RATE 14318000
--#define COMPARE	 ((MFGPT_TICK_RATE + HZ/2) / HZ)
-+#define MFGPT_CLK_RATE(c)		((14318000UL-32768)*c + 32768)
-+#define MFGPT_TICK_RATE(c, scale)	(MFGPT_CLK_RATE(c) / (1 << scale))
-+#define MFGPT_COMPARE(c, scale)		((MFGPT_TICK_RATE(c, scale)+HZ/2)/HZ)
- 
--#define MFGPT_BASE	mfgpt_base
--#define MFGPT0_CMP2	(MFGPT_BASE + 2)
--#define MFGPT0_CNT	(MFGPT_BASE + 4)
--#define MFGPT0_SETUP	(MFGPT_BASE + 6)
-+#define MFGPT_SETUP_ENABLE		(1 << 15)
-+#define MFGPT_SETUP_ACK			(3 << 13)
-+#define MFGPT_SETUP_SETUP		(1 << 12)
-+#define MFGPT_SETUP_CMP2EVT		(3 <<  8)
-+#define MFGPT_SETUP_CMP1EVT		(3 <<  6)
-+#define MFGPT_SETUP_CLOCK(c)		(c <<  4)
-+#define MFGPT_SETUP_SCALE(scale)	scale
-+
-+#define MFGPT0_CMP1	mfgpt_base
-+#define MFGPT0_CMP2	(mfgpt_base + 0x02)
-+#define MFGPT0_CNT	(mfgpt_base + 0x04)
-+#define MFGPT0_SETUP	(mfgpt_base + 0x06)
-+
-+#define MFGPT1_CMP1	(mfgpt_base + 0x08)
-+#define MFGPT1_CMP2	(mfgpt_base + 0x0A)
-+#define MFGPT1_CNT	(mfgpt_base + 0x0C)
-+#define MFGPT1_SETUP	(mfgpt_base + 0x0E)
-+
-+#define MFGPT2_CMP1	(mfgpt_base + 0x10)
-+#define MFGPT2_CMP2	(mfgpt_base + 0x12)
-+#define MFGPT2_CNT	(mfgpt_base + 0x14)
-+#define MFGPT2_SETUP	(mfgpt_base + 0x16)
- 
- #endif /*!_CS5536_MFGPT_H */
-diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_pci.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_pci.h
-index 8a7ecb4..ac01334 100644
---- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_pci.h
-+++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_pci.h
-@@ -13,6 +13,7 @@
- 
- #include <linux/types.h>
- #include <linux/pci_regs.h>
-+#include <linux/pci_ids.h>
- 
- extern void cs5536_pci_conf_write4(int function, int reg, u32 value);
- extern u32 cs5536_pci_conf_read4(int function, int reg);
-diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_vsm.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_vsm.h
-index 1f17c18..9bc368f0d 100644
---- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_vsm.h
-+++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_vsm.h
-@@ -17,15 +17,43 @@ typedef u32 (*cs5536_pci_vsm_read)(int reg);
- extern void pci_##name##_write_reg(int reg, u32 value); \
- extern u32 pci_##name##_read_reg(int reg);
- 
-+#define DEFINE_CS5536_MODULE(name) \
-+static void pci_##name##_write_reg(int reg, u32 value) {} \
-+static u32 pci_##name##_read_reg(int reg) { return 0; }
-+
-+/* isa module */
-+#ifdef CONFIG_CS5536_ISA
-+DECLARE_CS5536_MODULE(isa)
-+#else
-+DEFINE_CS5536_MODULE(isa)
-+#endif
-+
- /* ide module */
-+#ifdef CONFIG_CS5536_IDE
- DECLARE_CS5536_MODULE(ide)
-+#else
-+DEFINE_CS5536_MODULE(ide)
-+#endif
-+
- /* acc module */
-+#ifdef CONFIG_CS5536_AUDIO
- DECLARE_CS5536_MODULE(acc)
-+#else
-+DEFINE_CS5536_MODULE(acc)
-+#endif
-+
- /* ohci module */
-+#ifdef CONFIG_CS5536_OHCI
- DECLARE_CS5536_MODULE(ohci)
--/* isa module */
--DECLARE_CS5536_MODULE(isa)
-+#else
-+DEFINE_CS5536_MODULE(ohci)
-+#endif
-+
- /* ehci module */
-+#ifdef CONFIG_CS5536_EHCI
- DECLARE_CS5536_MODULE(ehci)
-+#else
-+DEFINE_CS5536_MODULE(ehci)
-+#endif
- 
- #endif				/* _CS5536_VSM_H */
-diff --git a/arch/mips/include/asm/mach-loongson/gpio.h b/arch/mips/include/asm/mach-loongson/gpio.h
-index 211a7b7..f15db3c 100644
---- a/arch/mips/include/asm/mach-loongson/gpio.h
-+++ b/arch/mips/include/asm/mach-loongson/gpio.h
-@@ -13,12 +13,16 @@
- #ifndef __STLS2F_GPIO_H
- #define __STLS2F_GPIO_H
- 
-+#ifdef CONFIG_GPIOLIB
-+#define ARCH_NR_GPIOS 4
- #include <asm-generic/gpio.h>
- 
- extern void gpio_set_value(unsigned gpio, int value);
- extern int gpio_get_value(unsigned gpio);
- extern int gpio_cansleep(unsigned gpio);
- 
-+#endif
-+
- /* The chip can do interrupt
-  * but it has not been tested and doc not clear
-  */
-diff --git a/arch/mips/include/asm/mach-loongson/loongson.h b/arch/mips/include/asm/mach-loongson/loongson.h
-index b286534..222d179 100644
---- a/arch/mips/include/asm/mach-loongson/loongson.h
-+++ b/arch/mips/include/asm/mach-loongson/loongson.h
-@@ -32,17 +32,13 @@ extern void __init prom_init_memory(void);
- extern void __init prom_init_cmdline(void);
- extern void __init prom_init_machtype(void);
- extern void __init prom_init_env(void);
--#ifdef CONFIG_LOONGSON_UART_BASE
--extern unsigned long _loongson_uart_base, loongson_uart_base;
--extern void prom_init_loongson_uart_base(void);
--#endif
-+extern void __init prom_init_uart_base(void);
- 
--static inline void prom_init_uart_base(void)
--{
--#ifdef CONFIG_LOONGSON_UART_BASE
--	prom_init_loongson_uart_base();
--#endif
--}
-+/*
-+ * Copy kernel command line from arcs_cmdline
-+ */
-+#include <asm/setup.h>
-+extern char loongson_cmdline[COMMAND_LINE_SIZE];
- 
- /* irq operation functions */
- extern void bonito_irqdispatch(void);
-@@ -249,6 +245,12 @@ extern struct cpufreq_frequency_table loongson2_clockmod_table[];
- 
- /* Chip Config */
- #define LOONGSON_CHIPCFG0		LOONGSON_REG(LOONGSON_REGBASE + 0x80)
-+#define LOONGSON_GET_CPUFREQ()		(LOONGSON_CHIPCFG0 & 7)
-+
-+#define LOONGSON_SET_CPUFREQ(level)	do { \
-+	LOONGSON_CHIPCFG0 = (LOONGSON_CHIPCFG0 & (~7)) | (level); \
-+} while (0)
-+
- #endif
- 
- /*
-diff --git a/arch/mips/include/asm/mach-loongson/machine.h b/arch/mips/include/asm/mach-loongson/machine.h
-index 3810d5c..d219499 100644
---- a/arch/mips/include/asm/mach-loongson/machine.h
-+++ b/arch/mips/include/asm/mach-loongson/machine.h
-@@ -24,4 +24,10 @@
- 
- #endif
- 
-+#ifdef CONFIG_DEXXON_GDIUM
-+
-+#define LOONGSON_MACHTYPE MACH_DEXXON_GDIUM2F10
-+
-+#endif
-+
- #endif /* __ASM_MACH_LOONGSON_MACHINE_H */
-diff --git a/arch/mips/include/asm/mach-loongson1/clock.h b/arch/mips/include/asm/mach-loongson1/clock.h
-new file mode 100644
-index 0000000..dd1afdb
---- /dev/null
-+++ b/arch/mips/include/asm/mach-loongson1/clock.h
-@@ -0,0 +1,53 @@
-+#ifndef __ASM_MACH_LOONGSON1_CLOCK_H
-+#define __ASM_MACH_LOONGSON1_CLOCK_H
-+
-+#include <linux/kref.h>
-+#include <linux/list.h>
-+#include <linux/seq_file.h>
-+#include <linux/clk.h>
-+
-+extern void (*cpu_wait) (void);
-+
-+struct clk;
-+
-+struct clk_ops {
-+	void (*init) (struct clk *clk);
-+	void (*enable) (struct clk *clk);
-+	void (*disable) (struct clk *clk);
-+	void (*recalc) (struct clk *clk);
-+	int (*set_rate) (struct clk *clk, unsigned long rate, int algo_id);
-+	long (*round_rate) (struct clk *clk, unsigned long rate);
-+};
-+
-+struct clk {
-+	struct list_head node;
-+	const char *name;
-+	int id;
-+	struct module *owner;
-+
-+	struct clk *parent;
-+	struct clk_ops *ops;
-+
-+	struct kref kref;
-+
-+	unsigned long rate;
-+	unsigned long flags;
-+};
-+
-+#define CLK_ALWAYS_ENABLED	(1 << 0)
-+#define CLK_RATE_PROPAGATES	(1 << 1)
-+
-+/* Should be defined by processor-specific code */
-+void arch_init_clk_ops(struct clk_ops **, int type);
-+
-+int clk_init(void);
-+
-+int __clk_enable(struct clk *);
-+void __clk_disable(struct clk *);
-+
-+void clk_recalc_rate(struct clk *);
-+
-+int clk_register(struct clk *);
-+void clk_unregister(struct clk *);
-+
-+#endif				/* __ASM_MIPS_CLOCK_H */
-diff --git a/arch/mips/include/asm/mach-loongson1/regs-intc.h b/arch/mips/include/asm/mach-loongson1/regs-intc.h
-new file mode 100644
-index 0000000..6d5db23
---- /dev/null
-+++ b/arch/mips/include/asm/mach-loongson1/regs-intc.h
-@@ -0,0 +1,25 @@
-+/*
-+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
-+ *
-+ * Loongson1 Interrupt register definitions.
-+ *
-+ * 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.
-+ */
-+
-+#ifndef __ASM_MACH_LOONGSON1_REGS_INTC_H
-+#define __ASM_MACH_LOONGSON1_REGS_INTC_H
-+
-+#define LS1X_INTC_REG(n, x) \
-+		(ioremap(LS1X_INTC_BASE + (n * 0x18) + (x), 4))
-+
-+#define LS1X_INTC_INTISR(n)		LS1X_INTC_REG(n, 0x0)
-+#define LS1X_INTC_INTIEN(n)		LS1X_INTC_REG(n, 0x4)
-+#define LS1X_INTC_INTSET(n)		LS1X_INTC_REG(n, 0x8)
-+#define LS1X_INTC_INTCLR(n)		LS1X_INTC_REG(n, 0xc)
-+#define LS1X_INTC_INTPOL(n)		LS1X_INTC_REG(n, 0x10)
-+#define LS1X_INTC_INTEDGE(n)		LS1X_INTC_REG(n, 0x14)
-+
-+#endif /* __ASM_MACH_LOONGSON1_REGS_INTC_H */
-diff --git a/arch/mips/include/asm/module.h b/arch/mips/include/asm/module.h
-index 44b705d..c28a782 100644
---- a/arch/mips/include/asm/module.h
-+++ b/arch/mips/include/asm/module.h
-@@ -126,6 +126,8 @@ search_module_dbetables(unsigned long addr)
- #define MODULE_PROC_FAMILY "LOONGSON1 "
- #elif defined CONFIG_CPU_LOONGSON2
- #define MODULE_PROC_FAMILY "LOONGSON2 "
-+#elif defined CONFIG_CPU_LOONGSON1
-+#define MODULE_PROC_FAMILY "LOONGSON1 "
- #elif defined CONFIG_CPU_CAVIUM_OCTEON
- #define MODULE_PROC_FAMILY "OCTEON "
- #elif defined CONFIG_CPU_XLR
-diff --git a/arch/mips/include/asm/timex.h b/arch/mips/include/asm/timex.h
-index c542475..74fef34 100644
---- a/arch/mips/include/asm/timex.h
-+++ b/arch/mips/include/asm/timex.h
-@@ -10,6 +10,10 @@
- 
- #ifdef __KERNEL__
- 
-+#ifdef CONFIG_CSRC_R4K
-+#define ARCH_HAS_PREPARED_LPJ
-+#endif
-+
- #include <asm/cpu-features.h>
- #include <asm/mipsregs.h>
- #include <asm/cpu-type.h>
-diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h
-index f25181b..d243152 100644
---- a/arch/mips/include/uapi/asm/inst.h
-+++ b/arch/mips/include/uapi/asm/inst.h
-@@ -62,6 +62,8 @@ enum spec_op {
- enum spec2_op {
- 	madd_op, maddu_op, mul_op, spec2_3_unused_op,
- 	msub_op, msubu_op, /* more unused ops */
-+	loongson_madd_op = 0x18, loongson_msub_op,
-+	loongson_nmadd_op, loongson_nmsub_op,
- 	clz_op = 0x20, clo_op,
- 	dclz_op = 0x24, dclo_op,
- 	sdbpp_op = 0x3f
-@@ -135,7 +137,7 @@ enum cop0_com_func {
-  */
- enum cop1_fmt {
- 	s_fmt, d_fmt, e_fmt, q_fmt,
--	w_fmt, l_fmt
-+	w_fmt, l_fmt, ps_fmt
- };
- 
- /*
-@@ -164,7 +166,8 @@ enum cop1_sdw_func {
- enum cop1x_func {
- 	lwxc1_op     =	0x00, ldxc1_op	   =  0x01,
- 	swxc1_op     =  0x08, sdxc1_op	   =  0x09,
--	pfetch_op    =	0x0f, madd_s_op	   =  0x20,
-+	pfetch_op    =	0x0f,
-+	prefx_op     =  0x17, madd_s_op    =  0x20,
- 	madd_d_op    =	0x21, madd_e_op	   =  0x22,
- 	msub_s_op    =	0x28, msub_d_op	   =  0x29,
- 	msub_e_op    =	0x2a, nmadd_s_op   =  0x30,
-diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
-index 6788727..0f81805 100644
---- a/arch/mips/kernel/scall64-o32.S
-+++ b/arch/mips/kernel/scall64-o32.S
-@@ -26,6 +26,18 @@
- 
- 	.align	5
- NESTED(handle_sys, PT_SIZE, sp)
-+#ifdef CONFIG_MIPS_USER_RDTSC
-+	MFC0	k0, CP0_EPC
-+	lw	k1, 0(k0)
-+	sltiu	k1, k1, 0x1c
-+	bne	k1, zero, 1f		# Normal syscall code: 0x0c < 0x1c
-+	 nop
-+	mfc0	v0, CP0_COUNT 		# Get TSC
-+	PTR_ADDIU	k0, 4		# ret from syscall
-+	MTC0	k0, CP0_EPC
-+	eret
-+1:
-+#endif /* CONFIG_MIPS_USER_RDTSC */
- 	.set	noat
- 	SAVE_SOME
- 	TRACE_IRQS_ON_RELOAD
-diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c
-index dcb8e5d..45177bc 100644
---- a/arch/mips/kernel/time.c
-+++ b/arch/mips/kernel/time.c
-@@ -120,6 +120,11 @@ static __init int cpu_has_mfc0_count_bug(void)
- 
- void __init time_init(void)
- {
-+#ifdef CONFIG_HR_SCHED_CLOCK
-+	if (!mips_clockevent_init() || !cpu_has_mfc0_count_bug())
-+		write_c0_count(0);
-+#endif
-+
- 	plat_time_init();
- 
- 	/*
-diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
-index eeddc58..d7bec00 100644
---- a/arch/mips/lib/Makefile
-+++ b/arch/mips/lib/Makefile
-@@ -2,10 +2,14 @@
- # Makefile for MIPS-specific library files..
- #
- 
--lib-y	+= bitops.o csum_partial.o delay.o memcpy.o memset.o \
-+lib-y	+= bitops.o csum_partial.o memcpy.o memset.o \
- 	   mips-atomic.o strlen_user.o strncpy_user.o \
- 	   strnlen_user.o uncached.o
- 
-+ifndef CONFIG_CSRC_R4K
-+lib-y	+= delay.o
-+endif
-+
- obj-y			+= iomap.o
- obj-$(CONFIG_PCI)	+= iomap-pci.o
- 
-diff --git a/arch/mips/loongson/Kconfig b/arch/mips/loongson/Kconfig
-index 263beb9..d56d594 100644
---- a/arch/mips/loongson/Kconfig
-+++ b/arch/mips/loongson/Kconfig
-@@ -32,12 +32,12 @@ config LEMOTE_FULOONG2E
- 
- config LEMOTE_MACH2F
- 	bool "Lemote Loongson 2F family machines"
--	select ARCH_SPARSEMEM_ENABLE
-+	select ARCH_SPARSEMEM_ENABLE if HIBERNATION
- 	select BOARD_SCACHE
- 	select BOOT_ELF32
- 	select CEVT_R4K if ! MIPS_EXTERNAL_TIMER
- 	select CPU_HAS_WB
--	select CS5536
-+	select CS5536 if PCI
- 	select CSRC_R4K if ! MIPS_EXTERNAL_TIMER
- 	select DMA_NONCOHERENT
- 	select GENERIC_ISA_DMA_SUPPORT_BROKEN
-@@ -45,23 +45,68 @@ config LEMOTE_MACH2F
- 	select HW_HAS_PCI
- 	select I8259
- 	select IRQ_CPU
--	select ISA
- 	select SYS_HAS_CPU_LOONGSON2F
- 	select SYS_HAS_EARLY_PRINTK
- 	select SYS_SUPPORTS_32BIT_KERNEL
- 	select SYS_SUPPORTS_64BIT_KERNEL
--	select SYS_SUPPORTS_HIGHMEM
-+	select SYS_SUPPORTS_HIGHMEM if ! EMBEDDED
- 	select SYS_SUPPORTS_LITTLE_ENDIAN
--	select LOONGSON_MC146818
-+	select LOONGSON_MC146818 if RTC_DRV_CMOS
- 	help
- 	  Lemote Loongson 2F family machines utilize the 2F revision of
- 	  Loongson processor and the AMD CS5536 south bridge.
- 
- 	  These family machines include fuloong2f mini PC, yeeloong2f notebook,
- 	  LingLoong allinone PC and so forth.
-+
-+config DEXXON_GDIUM
-+	bool "Dexxon Gdium Netbook"
-+	select ARCH_SPARSEMEM_ENABLE
-+	select BOARD_SCACHE
-+	select BOOT_ELF32
-+	select CEVT_R4K if ! MIPS_EXTERNAL_TIMER
-+	select CPU_HAS_WB
-+	select CSRC_R4K if ! MIPS_EXTERNAL_TIMER
-+	select DMA_NONCOHERENT
-+	select GENERIC_ISA_DMA_SUPPORT_BROKEN
-+	select HW_HAS_PCI
-+	select I8259
-+	select IRQ_CPU
-+	select ISA
-+	select SYS_HAS_CPU_LOONGSON2F
-+	select SYS_HAS_EARLY_PRINTK
-+	select SYS_SUPPORTS_32BIT_KERNEL
-+	select SYS_SUPPORTS_64BIT_KERNEL
-+	select SYS_SUPPORTS_HIGHMEM
-+	select SYS_SUPPORTS_LITTLE_ENDIAN
-+	select ARCH_REQUIRE_GPIOLIB
-+	select HAVE_PWM if MFD_SM501
-+	help
-+	  Dexxon gdium netbook based on Loongson 2F and SM502.
- endchoice
- 
- config CS5536
-+	select CS5536_IDE if (PATA_AMD || BLK_DEV_AMD74XX || PATA_CS5536)
-+	select CS5536_OHCI if USB_OHCI_HCD
-+	select CS5536_EHCI if USB_EHCI_HCD
-+	select CS5536_AUDIO if SND_CS5535AUDIO
-+	select CS5536_ISA
-+	bool
-+
-+config CS5536_ISA
-+	select ISA
-+	bool
-+
-+config CS5536_IDE
-+	bool
-+
-+config CS5536_OHCI
-+	bool
-+
-+config CS5536_EHCI
-+	bool
-+
-+config CS5536_AUDIO
- 	bool
- 
- config CS5536_MFGPT
-@@ -81,13 +126,25 @@ config LOONGSON_SUSPEND
- 	default y
- 	depends on CPU_SUPPORTS_CPUFREQ && SUSPEND
- 
--config LOONGSON_UART_BASE
--	bool
--	default y
--	depends on EARLY_PRINTK || SERIAL_8250
--
- config LOONGSON_MC146818
- 	bool
- 	default n
- 
-+config GDIUM_PWM_CLOCK
-+	tristate "Gdium PWM Timer"
-+	default n
-+	depends on HAVE_PWM && EXPERIMENTAL && BROKEN
-+	select MIPS_EXTERNAL_TIMER
-+	help
-+	  This options enables the experimental sm501-pwm based clock. With it,
-+	  you may be possible to use the loongson2f cpufreq driver.
-+
-+config GDIUM_VERSION
-+	int "Configure Gdium Version"
-+	depends on DEXXON_GDIUM
-+	default "3"
-+	help
-+	  I have no information about how to determine which version your board
-+	  is, If the default config doesn't work for it, please change it to
-+	  smaller ones.
- endif # MACH_LOONGSON
-diff --git a/arch/mips/loongson/Makefile b/arch/mips/loongson/Makefile
-index 0dc0055..4b69866 100644
---- a/arch/mips/loongson/Makefile
-+++ b/arch/mips/loongson/Makefile
-@@ -15,3 +15,9 @@ obj-$(CONFIG_LEMOTE_FULOONG2E)	+= fuloong-2e/
- #
- 
- obj-$(CONFIG_LEMOTE_MACH2F)  += lemote-2f/
-+
-+#
-+# Dexxon gdium netbook, based on loongson 2F and SM502
-+#
-+
-+obj-$(CONFIG_DEXXON_GDIUM)  += gdium/
-diff --git a/arch/mips/loongson/Platform b/arch/mips/loongson/Platform
-index 29692e5..6be5dff 100644
---- a/arch/mips/loongson/Platform
-+++ b/arch/mips/loongson/Platform
-@@ -30,3 +30,4 @@ platform-$(CONFIG_MACH_LOONGSON) += loongson/
- cflags-$(CONFIG_MACH_LOONGSON) += -I$(srctree)/arch/mips/include/asm/mach-loongson -mno-branch-likely
- load-$(CONFIG_LEMOTE_FULOONG2E) += 0xffffffff80100000
- load-$(CONFIG_LEMOTE_MACH2F) += 0xffffffff80200000
-+load-$(CONFIG_DEXXON_GDIUM) += 0xffffffff80200000
-diff --git a/arch/mips/loongson/common/Makefile b/arch/mips/loongson/common/Makefile
-index 9e4484c..73f1f9f 100644
---- a/arch/mips/loongson/common/Makefile
-+++ b/arch/mips/loongson/common/Makefile
-@@ -12,7 +12,6 @@ obj-$(CONFIG_PCI) += pci.o
- #
- obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
- obj-$(CONFIG_SERIAL_8250) += serial.o
--obj-$(CONFIG_LOONGSON_UART_BASE) += uart_base.o
- obj-$(CONFIG_LOONGSON_MC146818) += rtc.o
- 
- #
-diff --git a/arch/mips/loongson/common/cmdline.c b/arch/mips/loongson/common/cmdline.c
-index 72fed00..96d5919 100644
---- a/arch/mips/loongson/common/cmdline.c
-+++ b/arch/mips/loongson/common/cmdline.c
-@@ -17,10 +17,15 @@
-  * Free Software Foundation;  either version 2 of the  License, or (at your
-  * option) any later version.
-  */
-+#include <linux/module.h>
- #include <asm/bootinfo.h>
- 
- #include <loongson.h>
- 
-+/* the kernel command line copied from arcs_cmdline */
-+char loongson_cmdline[COMMAND_LINE_SIZE];
-+EXPORT_SYMBOL(loongson_cmdline);
-+
- void __init prom_init_cmdline(void)
- {
- 	int prom_argc;
-@@ -45,4 +50,31 @@ void __init prom_init_cmdline(void)
- 	}
- 
- 	prom_init_machtype();
-+
-+	/* append machine specific command line */
-+	switch (mips_machtype) {
-+	case MACH_LEMOTE_LL2F:
-+		if ((strstr(arcs_cmdline, "video=")) == NULL)
-+			strcat(arcs_cmdline, " video=sisfb:1360x768-16@60");
-+		break;
-+	case MACH_LEMOTE_FL2F:
-+		if ((strstr(arcs_cmdline, "ide_core.ignore_cable=")) == NULL)
-+			strcat(arcs_cmdline, " ide_core.ignore_cable=0");
-+		break;
-+	case MACH_LEMOTE_ML2F7:
-+		/* Mengloong-2F has a 800x480 screen */
-+		if ((strstr(arcs_cmdline, "vga=")) == NULL)
-+			strcat(arcs_cmdline, " vga=0x313");
-+		break;
-+	case MACH_DEXXON_GDIUM2F10:
-+		/* gdium has a 1024x600 screen */
-+		if ((strstr(arcs_cmdline, "video=")) == NULL)
-+			strcat(arcs_cmdline, " video=sm501fb:1024x600@60");
-+		break;
-+	default:
-+		break;
-+	}
-+
-+	/* copy arcs_cmdline into loongson_cmdline */
-+	strncpy(loongson_cmdline, arcs_cmdline, COMMAND_LINE_SIZE);
- }
-diff --git a/arch/mips/loongson/common/cs5536/Makefile b/arch/mips/loongson/common/cs5536/Makefile
-index f12e640..70f6057 100644
---- a/arch/mips/loongson/common/cs5536/Makefile
-+++ b/arch/mips/loongson/common/cs5536/Makefile
-@@ -2,8 +2,13 @@
- # Makefile for CS5536 support.
- #
- 
--obj-$(CONFIG_CS5536) += cs5536_pci.o cs5536_ide.o cs5536_acc.o cs5536_ohci.o \
--			cs5536_isa.o cs5536_ehci.o
-+obj-$(CONFIG_CS5536)	+= cs5536_pci.o
-+
-+obj-$(CONFIG_ISA)		+= cs5536_isa.o
-+obj-$(CONFIG_CS5536_IDE)	+= cs5536_ide.o
-+obj-$(CONFIG_CS5536_AUDIO)	+= cs5536_acc.o
-+obj-$(CONFIG_CS5536_OHCI)	+= cs5536_ohci.o
-+obj-$(CONFIG_CS5536_EHCI)	+= cs5536_ehci.o
- 
- #
- # Enable cs5536 mfgpt Timer
-diff --git a/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c b/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c
-index c639b9d..a7078ae 100644
---- a/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c
-+++ b/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c
-@@ -7,6 +7,9 @@
-  * Copyright (C) 2009 Lemote Inc.
-  * Author: Wu zhangjin, wuzhangjin@gmail.com
-  *
-+ * Copyright (C) 2010 Lemote Inc.
-+ * Author: Gang Liang, randomizedthinking@gmail.com
-+ *
-  * Reference: AMD Geode(TM) CS5536 Companion Device Data Book
-  *
-  *  This program is free software; you can redistribute	 it and/or modify it
-@@ -15,11 +18,24 @@
-  *  option) any later version.
-  */
- 
-+/*
-+ * The MFGPT base address is variable, i.e., it could change over time. In
-+ * reality, it only changes once when setting up the PCI memory mapping (occurs
-+ * about 0.2 second from boot).  But because of this, we have to read in the
-+ * mfgpt base address repeatly in the beginning of various routines, most
-+ * noticeably, mfgpt1_read_cycle (for sched_clock), and mfgpt1_interrupt.
-+ *
-+ * The source of problem is that PMON and the current cs5536 set up pci
-+ * register window differently (to be further confirmed). Can we set
-+ * them the same so as to save the trouble here?
-+ *
-+ * Now an ugly hack is used to save a few CPU cycles... likely an
-+ * over-optimization. Feel free to remove it.
-+ */
-+
- #include <linux/io.h>
- #include <linux/init.h>
- #include <linux/module.h>
--#include <linux/jiffies.h>
--#include <linux/spinlock.h>
- #include <linux/interrupt.h>
- #include <linux/clockchips.h>
- 
-@@ -27,108 +43,143 @@
- 
- #include <cs5536/cs5536_mfgpt.h>
- 
--DEFINE_SPINLOCK(mfgpt_lock);
--EXPORT_SYMBOL(mfgpt_lock);
-+static void mfgpt0_set_mode(enum clock_event_mode, struct clock_event_device*);
-+static int mfgpt0_next_event(unsigned long, struct clock_event_device*);
-+static irqreturn_t mfgpt0_interrupt(int irq, void *dev_id);
-+static void mfgpt0_start_timer(u16 delta);
- 
-+static cycle_t mfgpt1_read_cycle(struct clocksource *cs);
-+
-+static enum clock_event_mode mfgpt0_mode = CLOCK_EVT_MODE_SHUTDOWN;
- static u32 mfgpt_base;
- 
--/*
-- * Initialize the MFGPT timer.
-- *
-- * This is also called after resume to bring the MFGPT into operation again.
-- */
-+static struct clock_event_device mfgpt0_clockevent = {
-+	.name = "mfgpt0",
-+	.features = CLOCK_EVT_MODE_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
-+	.set_mode = mfgpt0_set_mode,
-+	.set_next_event = mfgpt0_next_event,
-+	.rating = 220,
-+	.irq = CS5536_MFGPT_INTR,
-+};
-+
-+static struct irqaction irq5 = {
-+	.handler = mfgpt0_interrupt,
-+	.flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER,
-+	.name = "mfgpt0-timer"
-+};
-+
-+static struct clocksource mfgpt1_clocksource = {
-+	.name = "mfgpt1",
-+	.rating = 210,
-+	.read = mfgpt1_read_cycle,
-+	.mask = CLOCKSOURCE_MASK(16),
-+	.flags = CLOCK_SOURCE_IS_CONTINUOUS
-+};
- 
--/* disable counter */
--void disable_mfgpt0_counter(void)
-+static inline void enable_mfgpt0_counter(void)
- {
--	outw(inw(MFGPT0_SETUP) & 0x7fff, MFGPT0_SETUP);
-+	u32 basehi;
-+	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
-+
-+	/* clockevent: 14M, divisor = 8 (scale=3), CMP2 event mode */
-+	outw(MFGPT_SETUP_ACK | MFGPT_SETUP_CMP2EVT |
-+	     MFGPT_SETUP_CLOCK(1) | MFGPT_SETUP_SCALE(3), MFGPT0_SETUP);
-+	outw(0, MFGPT0_CNT);
-+	outw(MFGPT_COMPARE(1, 3), MFGPT0_CMP2);
-+	outw(0xFFFF, MFGPT0_SETUP);
- }
--EXPORT_SYMBOL(disable_mfgpt0_counter);
- 
--/* enable counter, comparator2 to event mode, 14.318MHz clock */
--void enable_mfgpt0_counter(void)
-+static inline void enable_mfgpt1_counter(void)
- {
--	outw(0xe310, MFGPT0_SETUP);
-+	u32 basehi;
-+	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
-+
-+	/* clocksource: 32K w/ divisor = 2 (scale=1) */
-+	outw(MFGPT_SETUP_ACK | MFGPT_SETUP_CLOCK(0) |
-+		MFGPT_SETUP_SCALE(1), MFGPT1_SETUP);
-+
-+	outw(0, MFGPT1_CNT);
-+	outw(0xFFFF, MFGPT1_CMP2);  /* CNT won't tick with no CMP set */
-+	outw(0xFFFF, MFGPT1_SETUP);
- }
--EXPORT_SYMBOL(enable_mfgpt0_counter);
- 
--static void init_mfgpt_timer(enum clock_event_mode mode,
--			     struct clock_event_device *evt)
-+void enable_mfgpt_counter(void)
- {
--	spin_lock(&mfgpt_lock);
--
--	switch (mode) {
--	case CLOCK_EVT_MODE_PERIODIC:
--		outw(COMPARE, MFGPT0_CMP2);	/* set comparator2 */
--		outw(0, MFGPT0_CNT);	/* set counter to 0 */
--		enable_mfgpt0_counter();
--		break;
--
--	case CLOCK_EVT_MODE_SHUTDOWN:
--	case CLOCK_EVT_MODE_UNUSED:
--		if (evt->mode == CLOCK_EVT_MODE_PERIODIC ||
--		    evt->mode == CLOCK_EVT_MODE_ONESHOT)
--			disable_mfgpt0_counter();
--		break;
--
--	case CLOCK_EVT_MODE_ONESHOT:
--		/* The oneshot mode have very high deviation, Not use it! */
--		break;
--
--	case CLOCK_EVT_MODE_RESUME:
--		/* Nothing to do here */
--		break;
--	}
--	spin_unlock(&mfgpt_lock);
-+	/* TODO: add a mfgpt system hard reset here
-+	 * timers might not reset correctly when OS crashes
-+	 */
-+
-+	enable_mfgpt0_counter();
-+	enable_mfgpt1_counter();
- }
-+EXPORT_SYMBOL(enable_mfgpt_counter);
- 
--static struct clock_event_device mfgpt_clockevent = {
--	.name = "mfgpt",
--	.features = CLOCK_EVT_FEAT_PERIODIC,
--	.set_mode = init_mfgpt_timer,
--	.irq = CS5536_MFGPT_INTR,
--};
-+void disable_mfgpt_counter(void)
-+{
-+	outw(0x7FFF, MFGPT0_SETUP);
-+	outw(0x7FFF, MFGPT1_SETUP);
-+}
-+EXPORT_SYMBOL(disable_mfgpt_counter);
- 
--static irqreturn_t timer_interrupt(int irq, void *dev_id)
-+static void mfgpt0_start_timer(u16 delta)
- {
--	u32 basehi;
-+	outw(0x7FFF, MFGPT0_SETUP);
-+	outw(0,      MFGPT0_CNT);
-+	outw(delta,  MFGPT0_CMP2);
-+	outw(0xFFFF, MFGPT0_SETUP);
-+}
- 
--	/*
--	 * get MFGPT base address
--	 *
--	 * NOTE: do not remove me, it's need for the value of mfgpt_base is
--	 * variable
--	 */
-+static void mfgpt0_set_mode(enum clock_event_mode mode,
-+		struct clock_event_device *evt)
-+{
-+	outw(0x7FFF, MFGPT0_SETUP);
-+	if (mode == CLOCK_EVT_MODE_PERIODIC)
-+		mfgpt0_start_timer(MFGPT_COMPARE(1, 3));
-+
-+	mfgpt0_mode = mode;
-+}
-+
-+static int mfgpt0_next_event(unsigned long delta,
-+		struct clock_event_device *evt)
-+{
-+	mfgpt0_start_timer(delta);
-+	return 0;
-+}
-+
-+static irqreturn_t mfgpt0_interrupt(int irq, void *dev_id)
-+{
-+	u32 basehi;
- 	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
- 
--	/* ack */
--	outw(inw(MFGPT0_SETUP) | 0x4000, MFGPT0_SETUP);
-+	/* stop the timer and ack the interrupt */
-+	outw(0x7FFF, MFGPT0_SETUP);
- 
--	mfgpt_clockevent.event_handler(&mfgpt_clockevent);
-+	if (mfgpt0_mode == CLOCK_EVT_MODE_SHUTDOWN)
-+		return IRQ_HANDLED;
- 
-+	/* restart timer for periodic mode */
-+	if (mfgpt0_mode == CLOCK_EVT_MODE_PERIODIC)
-+		outw(0xFFFF, MFGPT0_SETUP);
-+
-+	mfgpt0_clockevent.event_handler(&mfgpt0_clockevent);
- 	return IRQ_HANDLED;
- }
- 
--static struct irqaction irq5 = {
--	.handler = timer_interrupt,
--	.flags = IRQF_NOBALANCING | IRQF_TIMER,
--	.name = "timer"
--};
--
- /*
-  * Initialize the conversion factor and the min/max deltas of the clock event
-  * structure and register the clock event source with the framework.
-  */
- void __init setup_mfgpt0_timer(void)
- {
--	u32 basehi;
--	struct clock_event_device *cd = &mfgpt_clockevent;
-+	struct clock_event_device *cd = &mfgpt0_clockevent;
- 	unsigned int cpu = smp_processor_id();
--
- 	cd->cpumask = cpumask_of(cpu);
--	clockevent_set_clock(cd, MFGPT_TICK_RATE);
--	cd->max_delta_ns = clockevent_delta2ns(0xffff, cd);
--	cd->min_delta_ns = clockevent_delta2ns(0xf, cd);
-+
-+	cd->shift = 22;
-+	cd->mult  = div_sc(MFGPT_TICK_RATE(1, 3), NSEC_PER_SEC, cd->shift);
-+
-+	cd->min_delta_ns = clockevent_delta2ns(0xF, cd);
-+	cd->max_delta_ns = clockevent_delta2ns(0xFFFF, cd);
- 
- 	/* Enable MFGPT0 Comparator 2 Output to the Interrupt Mapper */
- 	_wrmsr(DIVIL_MSR_REG(MFGPT_IRQ), 0, 0x100);
-@@ -136,79 +187,24 @@ void __init setup_mfgpt0_timer(void)
- 	/* Enable Interrupt Gate 5 */
- 	_wrmsr(DIVIL_MSR_REG(PIC_ZSEL_LOW), 0, 0x50000);
- 
--	/* get MFGPT base address */
--	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
--
-+	enable_mfgpt0_counter();
- 	clockevents_register_device(cd);
--
- 	setup_irq(CS5536_MFGPT_INTR, &irq5);
- }
- 
--/*
-- * Since the MFGPT overflows every tick, its not very useful
-- * to just read by itself. So use jiffies to emulate a free
-- * running counter:
-- */
--static cycle_t mfgpt_read(struct clocksource *cs)
-+static cycle_t mfgpt1_read_cycle(struct clocksource *cs)
- {
--	unsigned long flags;
--	int count;
--	u32 jifs;
--	static int old_count;
--	static u32 old_jifs;
--
--	spin_lock_irqsave(&mfgpt_lock, flags);
--	/*
--	 * Although our caller may have the read side of xtime_lock,
--	 * this is now a seqlock, and we are cheating in this routine
--	 * by having side effects on state that we cannot undo if
--	 * there is a collision on the seqlock and our caller has to
--	 * retry.  (Namely, old_jifs and old_count.)  So we must treat
--	 * jiffies as volatile despite the lock.  We read jiffies
--	 * before latching the timer count to guarantee that although
--	 * the jiffies value might be older than the count (that is,
--	 * the counter may underflow between the last point where
--	 * jiffies was incremented and the point where we latch the
--	 * count), it cannot be newer.
--	 */
--	jifs = jiffies;
--	/* read the count */
--	count = inw(MFGPT0_CNT);
--
--	/*
--	 * It's possible for count to appear to go the wrong way for this
--	 * reason:
--	 *
--	 *  The timer counter underflows, but we haven't handled the resulting
--	 *  interrupt and incremented jiffies yet.
--	 *
--	 * Previous attempts to handle these cases intelligently were buggy, so
--	 * we just do the simple thing now.
--	 */
--	if (count < old_count && jifs == old_jifs)
--		count = old_count;
--
--	old_count = count;
--	old_jifs = jifs;
--
--	spin_unlock_irqrestore(&mfgpt_lock, flags);
--
--	return (cycle_t) (jifs * COMPARE) + count;
-+	return inw(MFGPT1_CNT);
- }
- 
--static struct clocksource clocksource_mfgpt = {
--	.name = "mfgpt",
--	.rating = 120, /* Functional for real use, but not desired */
--	.read = mfgpt_read,
--	.mask = CLOCKSOURCE_MASK(32),
--};
--
--int __init init_mfgpt_clocksource(void)
-+int __init init_mfgpt1_clocksource(void)
- {
- 	if (num_possible_cpus() > 1)	/* MFGPT does not scale! */
- 		return 0;
- 
--	return clocksource_register_hz(&clocksource_mfgpt, MFGPT_TICK_RATE);
-+	enable_mfgpt1_counter();
-+
-+	return clocksource_register_hz(&mfgpt1_clocksource, MFGPT_TICK_RATE(0, 1));
- }
- 
--arch_initcall(init_mfgpt_clocksource);
-+arch_initcall(init_mfgpt1_clocksource);
-diff --git a/arch/mips/loongson/common/early_printk.c b/arch/mips/loongson/common/early_printk.c
-index ced461b..89aecbf 100644
---- a/arch/mips/loongson/common/early_printk.c
-+++ b/arch/mips/loongson/common/early_printk.c
-@@ -10,9 +10,13 @@
-  *  option) any later version.
-  */
- #include <linux/serial_reg.h>
-+#include <linux/module.h>
-+#include <asm/bootinfo.h>
- 
- #include <loongson.h>
- 
-+unsigned long _loongson_uart_base;
-+
- #define PORT(base, offset) (u8 *)(base + offset)
- 
- static inline unsigned int serial_in(unsigned char *base, int offset)
-@@ -39,3 +43,29 @@ void prom_putchar(char c)
- 
- 	serial_out(uart_base, UART_TX, c);
- }
-+
-+void __init prom_init_uart_base(void)
-+{
-+	unsigned long loongson_uart_base;
-+
-+	switch (mips_machtype) {
-+	case MACH_LEMOTE_FL2E:
-+		loongson_uart_base = LOONGSON_PCIIO_BASE + 0x3f8;
-+		break;
-+	case MACH_LEMOTE_FL2F:
-+	case MACH_LEMOTE_LL2F:
-+		loongson_uart_base = LOONGSON_PCIIO_BASE + 0x2f8;
-+		break;
-+	case MACH_LEMOTE_ML2F7:
-+	case MACH_LEMOTE_YL2F89:
-+	case MACH_DEXXON_GDIUM2F10:
-+	case MACH_LEMOTE_NAS:
-+	default:
-+		/* The CPU provided serial port */
-+		loongson_uart_base = LOONGSON_LIO1_BASE + 0x3f8;
-+		break;
-+	}
-+
-+	_loongson_uart_base =
-+		(unsigned long)ioremap_nocache(loongson_uart_base, 8);
-+}
-diff --git a/arch/mips/loongson/common/init.c b/arch/mips/loongson/common/init.c
-index ae7af1f..3083978 100644
---- a/arch/mips/loongson/common/init.c
-+++ b/arch/mips/loongson/common/init.c
-@@ -30,8 +30,10 @@ void __init prom_init(void)
- 	prom_init_env();
- 	prom_init_memory();
- 
-+#ifdef CONFIG_EARLY_PRINTK
- 	/*init the uart base address */
- 	prom_init_uart_base();
-+#endif
- }
- 
- void __init prom_free_prom_memory(void)
-diff --git a/arch/mips/loongson/common/irq.c b/arch/mips/loongson/common/irq.c
-index 687003b..d62fa77 100644
---- a/arch/mips/loongson/common/irq.c
-+++ b/arch/mips/loongson/common/irq.c
-@@ -10,6 +10,10 @@
- #include <linux/delay.h>
- #include <linux/interrupt.h>
- 
-+#include <asm/irq_cpu.h>
-+#include <asm/i8259.h>
-+#include <asm/mipsregs.h>
-+
- #include <loongson.h>
- /*
-  * the first level int-handler will jump here if it is a bonito irq
-@@ -48,20 +52,32 @@ asmlinkage void plat_irq_dispatch(void)
- void __init arch_init_irq(void)
- {
- 	/*
--	 * Clear all of the interrupts while we change the able around a bit.
--	 * int-handler is not on bootstrap
-+	 * The vector addresses of the generic exceptions are in the cached
-+	 * address space.
- 	 */
--	clear_c0_status(ST0_IM | ST0_BEV);
-+	clear_c0_status(ST0_BEV);
- 
--	/* no steer */
-+	/* No steer */
- 	LOONGSON_INTSTEER = 0;
- 
- 	/*
--	 * Mask out all interrupt by writing "1" to all bit position in
--	 * the interrupt reset reg.
-+	 * Clear all interrupts
- 	 */
- 	LOONGSON_INTENCLR = ~0;
- 
-+	/*
-+	 * Sets the first-level interrupt dispatcher:
-+	 *
-+	 * 0-15: i8259 interrupt (If CONFIG_I8259 selected)
-+	 * 16-23: mips cpu interrupt
-+	 * 32-63: bonito irq
-+	 */
-+	mips_cpu_irq_init();
-+	bonito_irq_init();
-+#ifdef CONFIG_I8259
-+	init_i8259_irqs();
-+#endif
-+
- 	/* machine specific irq init */
- 	mach_init_irq();
- }
-diff --git a/arch/mips/loongson/common/mem.c b/arch/mips/loongson/common/mem.c
-index 8626a42..7aea259 100644
---- a/arch/mips/loongson/common/mem.c
-+++ b/arch/mips/loongson/common/mem.c
-@@ -14,39 +14,24 @@
- #include <mem.h>
- #include <pci.h>
- 
-+#define MB(x) ((x) << 20)
-+
- void __init prom_init_memory(void)
- {
--	add_memory_region(0x0, (memsize << 20), BOOT_MEM_RAM);
--
--	add_memory_region(memsize << 20, LOONGSON_PCI_MEM_START - (memsize <<
--				20), BOOT_MEM_RESERVED);
--
-+	add_memory_region(0x0, MB(memsize), BOOT_MEM_RAM);
-+	add_memory_region(MB(memsize), LOONGSON_PCI_MEM_START - MB(memsize), BOOT_MEM_RESERVED);
- #ifdef CONFIG_CPU_SUPPORTS_ADDRWINCFG
--	{
--		int bit;
--
--		bit = fls(memsize + highmemsize);
--		if (bit != ffs(memsize + highmemsize))
--			bit += 20;
--		else
--			bit = bit + 20 - 1;
--
--		/* set cpu window3 to map CPU to DDR: 2G -> 2G */
--		LOONGSON_ADDRWIN_CPUTODDR(ADDRWIN_WIN3, 0x80000000ul,
--					  0x80000000ul, (1 << bit));
--		mmiowb();
--	}
--#endif /* !CONFIG_CPU_SUPPORTS_ADDRWINCFG */
-+	/* set cpu window3 to map CPU to DDR: 2G -> 0G */
-+	LOONGSON_ADDRWIN_CPUTODDR(ADDRWIN_WIN3, 0x80000000ul, 0, MB(memsize + highmemsize));
-+	mmiowb();
-+#endif
- 
- #ifdef CONFIG_64BIT
- 	if (highmemsize > 0)
--		add_memory_region(LOONGSON_HIGHMEM_START,
--				  highmemsize << 20, BOOT_MEM_RAM);
--
-+		add_memory_region(LOONGSON_HIGHMEM_START, MB(highmemsize), BOOT_MEM_RAM);
- 	add_memory_region(LOONGSON_PCI_MEM_END + 1, LOONGSON_HIGHMEM_START -
--			  LOONGSON_PCI_MEM_END - 1, BOOT_MEM_RESERVED);
--
--#endif /* !CONFIG_64BIT */
-+			LOONGSON_PCI_MEM_END - 1, BOOT_MEM_RESERVED);
-+#endif
- }
- 
- /* override of arch/mips/mm/cache.c: __uncached_access */
-diff --git a/arch/mips/loongson/common/mtd.c b/arch/mips/loongson/common/mtd.c
-new file mode 100644
-index 0000000..49a57a7
---- /dev/null
-+++ b/arch/mips/loongson/common/mtd.c
-@@ -0,0 +1,91 @@
-+/*
-+ *  Driver for flushing/dumping ROM of PMON on loongson family machines
-+ *
-+ *  Copyright (C) 2008-2009 Lemote Inc.
-+ *  Author: Yan Hua <yanh@lemote.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/types.h>
-+#include <linux/kernel.h>
-+#include <linux/init.h>
-+#include <linux/mtd/mtd.h>
-+#include <linux/mtd/map.h>
-+#include <linux/mtd/partitions.h>
-+
-+#include <asm/io.h>
-+
-+#include <loongson.h>
-+
-+#define FLASH_PHYS_ADDR LOONGSON_BOOT_BASE
-+#define FLASH_SIZE 0x080000
-+
-+#define FLASH_PARTITION0_ADDR 0x00000000
-+#define FLASH_PARTITION0_SIZE 0x00080000
-+
-+struct map_info flash_map = {
-+	.name = "flash device",
-+	.size = FLASH_SIZE,
-+	.bankwidth = 1,
-+};
-+
-+struct mtd_partition flash_parts[] = {
-+	{
-+	 .name = "Bootloader",
-+	 .offset = FLASH_PARTITION0_ADDR,
-+	 .size = FLASH_PARTITION0_SIZE},
-+};
-+
-+#define PARTITION_COUNT ARRAY_SIZE(flash_parts)
-+
-+static struct mtd_info *mymtd;
-+
-+int __init init_flash(void)
-+{
-+	printk(KERN_NOTICE "flash device: %x at %x\n",
-+	       FLASH_SIZE, FLASH_PHYS_ADDR);
-+
-+	flash_map.phys = FLASH_PHYS_ADDR;
-+	flash_map.virt = ioremap(FLASH_PHYS_ADDR, FLASH_SIZE);
-+
-+	if (!flash_map.virt) {
-+		printk(KERN_NOTICE "Failed to ioremap\n");
-+		return -EIO;
-+	}
-+
-+	simple_map_init(&flash_map);
-+
-+	mymtd = do_map_probe("cfi_probe", &flash_map);
-+	if (mymtd) {
-+		add_mtd_partitions(mymtd, flash_parts, PARTITION_COUNT);
-+		printk(KERN_NOTICE "pmon flash device initialized\n");
-+		return 0;
-+	}
-+
-+	iounmap((void *)flash_map.virt);
-+	return -ENXIO;
-+}
-+
-+static void __exit cleanup_flash(void)
-+{
-+	if (mymtd) {
-+		del_mtd_partitions(mymtd);
-+		map_destroy(mymtd);
-+	}
-+	if (flash_map.virt) {
-+		iounmap((void *)flash_map.virt);
-+		flash_map.virt = 0;
-+	}
-+}
-+
-+module_init(init_flash);
-+module_exit(cleanup_flash);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Yanhua <yanh@lemote.com>");
-+MODULE_DESCRIPTION("MTD driver for pmon flushing/dumping");
-diff --git a/arch/mips/loongson/common/pci.c b/arch/mips/loongson/common/pci.c
-index fa77844..a992931 100644
---- a/arch/mips/loongson/common/pci.c
-+++ b/arch/mips/loongson/common/pci.c
-@@ -50,11 +50,11 @@ static void __init setup_pcimap(void)
- 		LOONGSON_PCIMAP_WIN(0, 0);
- 
- 	/*
--	 * PCI-DMA to local mapping: [2G,2G+256M] -> [0M,256M]
-+	 * PCI-DMA to local mapping: [2G,4G] -> [0M,2G]
- 	 */
- 	LOONGSON_PCIBASE0 = 0x80000000ul;   /* base: 2G -> mmap: 0M */
--	/* size: 256M, burst transmission, pre-fetch enable, 64bit */
--	LOONGSON_PCI_HIT0_SEL_L = 0xc000000cul;
-+	/* size: 2G, burst transmission, pre-fetch enable, 64bit */
-+	LOONGSON_PCI_HIT0_SEL_L = 0x8000000cul;
- 	LOONGSON_PCI_HIT0_SEL_H = 0xfffffffful;
- 	LOONGSON_PCI_HIT1_SEL_L = 0x00000006ul; /* set this BAR as invalid */
- 	LOONGSON_PCI_HIT1_SEL_H = 0x00000000ul;
-diff --git a/arch/mips/loongson/common/serial.c b/arch/mips/loongson/common/serial.c
-index 5f2b78a..c828600 100644
---- a/arch/mips/loongson/common/serial.c
-+++ b/arch/mips/loongson/common/serial.c
-@@ -10,6 +10,7 @@
-  * Author: Wu Zhangjin (wuzhangjin@gmail.com)
-  */
- 
-+#include <linux/module.h>
- #include <linux/io.h>
- #include <linux/init.h>
- #include <linux/serial_8250.h>
-@@ -19,58 +20,44 @@
- #include <loongson.h>
- #include <machine.h>
- 
--#define PORT(int)			\
-+#define PORT(int, base_baud, io_type, port)			\
- {								\
- 	.irq		= int,					\
--	.uartclk	= 1843200,				\
--	.iotype		= UPIO_PORT,				\
-+	.uartclk	= base_baud,				\
-+	.iotype		= io_type,				\
- 	.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,	\
- 	.regshift	= 0,					\
-+	.iobase	= port,						\
- }
- 
--#define PORT_M(int)				\
--{								\
--	.irq		= MIPS_CPU_IRQ_BASE + (int),		\
--	.uartclk	= 3686400,				\
--	.iotype		= UPIO_MEM,				\
--	.membase	= (void __iomem *)NULL,			\
--	.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,	\
--	.regshift	= 0,					\
--}
--
--static struct plat_serial8250_port uart8250_data[][2] = {
--	[MACH_LOONGSON_UNKNOWN]		{},
--	[MACH_LEMOTE_FL2E]		{PORT(4), {} },
--	[MACH_LEMOTE_FL2F]		{PORT(3), {} },
--	[MACH_LEMOTE_ML2F7]		{PORT_M(3), {} },
--	[MACH_LEMOTE_YL2F89]		{PORT_M(3), {} },
--	[MACH_DEXXON_GDIUM2F10]		{PORT_M(3), {} },
--	[MACH_LEMOTE_NAS]		{PORT_M(3), {} },
--	[MACH_LEMOTE_LL2F]		{PORT(3), {} },
--	[MACH_LOONGSON_END]		{},
-+static struct plat_serial8250_port uart8250_data[] = {
-+	/* ttyS0: cpu_uart0 Yeeloong, Gdium, UNAS, ...  */
-+	PORT((MIPS_CPU_IRQ_BASE + 3), 3686400, UPIO_MEM, 0x3f8),
-+	/* ttyS1: sb_uart1 2E */
-+	PORT(4, 1843200, UPIO_PORT, 0x3f8),
-+	/* ttyS2: sb_uart2 fuloong2f */
-+	PORT(3, 1843200, UPIO_PORT, 0x2f8),
-+	{},
- };
- 
- static struct platform_device uart8250_device = {
- 	.name = "serial8250",
- 	.id = PLAT8250_DEV_PLATFORM,
-+	.dev = {
-+		.platform_data = uart8250_data,
-+	},
- };
- 
- static int __init serial_init(void)
- {
--	unsigned char iotype;
--
--	iotype = uart8250_data[mips_machtype][0].iotype;
--
--	if (UPIO_MEM == iotype)
--		uart8250_data[mips_machtype][0].membase =
--			(void __iomem *)_loongson_uart_base;
--	else if (UPIO_PORT == iotype)
--		uart8250_data[mips_machtype][0].iobase =
--		    loongson_uart_base - LOONGSON_PCIIO_BASE;
--
--	uart8250_device.dev.platform_data = uart8250_data[mips_machtype];
-+	uart8250_data[0].membase = (void __iomem *)ioremap_nocache(
-+			LOONGSON_LIO1_BASE + uart8250_data[0].iobase, 8);
- 
--	return platform_device_register(&uart8250_device);
-+	platform_device_register(&uart8250_device);
-+	return 0;
- }
- 
- device_initcall(serial_init);
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("liu shiwei <liushiwei@anheng.com.cn>");
-+MODULE_DESCRIPTION("loongson serial");
-diff --git a/arch/mips/loongson/common/time.c b/arch/mips/loongson/common/time.c
-index 262a1f6..eebbeef 100644
---- a/arch/mips/loongson/common/time.c
-+++ b/arch/mips/loongson/common/time.c
-@@ -10,6 +10,8 @@
-  *  Free Software Foundation;  either version 2 of the	License, or (at your
-  *  option) any later version.
-  */
-+#include <linux/rtc.h>
-+
- #include <asm/mc146818-time.h>
- #include <asm/time.h>
- 
-@@ -24,8 +26,81 @@ void __init plat_time_init(void)
- 	setup_mfgpt0_timer();
- }
- 
-+#ifdef CONFIG_LOONGSON_MC146818
- void read_persistent_clock(struct timespec *ts)
- {
- 	ts->tv_sec = mc146818_get_cmos_time();
- 	ts->tv_nsec = 0;
- }
-+#else
-+
-+/* If no CMOS RTC, use the one below */
-+
-+/*
-+ * Cloned from drivers/rtc/hctosys.c
-+ *
-+ * If CONFIG_RTC_HCTOSYS=y is enabled, the system time can be set from the
-+ * hardware clock(when boot and resuming from suspend), this may be also done
-+ * (duplicately) by the timekeeper, which may need to be avoided(TODO).
-+ *
-+ * read_persistent_clock() may be useful in some places, e.g. there is not
-+ * peristent clock in the system, we can use this to recover the system time.
-+ *
-+ * Note: The device indicated by CONFIG_RTC_HCTOSYS_DEVICE must be the one
-+ * created by the RTC driver. Use Gdium as an example, We must disable the
-+ * rt_cmos driver If we want to use the rtc_m41t80 driver for
-+ * CONFIG_RTC_HCTOSYS_DEVICE is configured as /dev/rtc0, if rtc_cmos is
-+ * enabled, rtc_cmos driver will be used, but it is not supported by Gdium.
-+ * So, for Gdium, please ensure "# CONFIG_RTC_DRV_CMOS is not set"
-+ */
-+
-+#ifdef CONFIG_RTC_HCTOSYS
-+void read_persistent_clock(struct timespec *ts)
-+{
-+	int err = -ENODEV;
-+	struct rtc_time tm;
-+	struct rtc_device *rtc;
-+
-+	/* We can not access the RTC device before it is initialized ... */
-+	if (rtc_hctosys_ret != 0) {
-+		ts->tv_sec = 0;
-+		ts->tv_nsec = 0;
-+		return;
-+	}
-+
-+	rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
-+
-+	if (rtc == NULL) {
-+		pr_err("%s: unable to open rtc device (%s)\n",
-+			__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
-+		goto err_open;
-+	}
-+
-+	err = rtc_read_time(rtc, &tm);
-+	if (err) {
-+		dev_err(rtc->dev.parent,
-+			"hctosys: unable to read the hardware clock\n");
-+		goto err_read;
-+
-+	}
-+
-+	err = rtc_valid_tm(&tm);
-+	if (err) {
-+		dev_err(rtc->dev.parent,
-+			"hctosys: invalid date/time\n");
-+		goto err_invalid;
-+	}
-+
-+	ts->tv_nsec = NSEC_PER_SEC >> 1,
-+	rtc_tm_to_time(&tm, &ts->tv_sec);
-+
-+err_invalid:
-+err_read:
-+	rtc_class_close(rtc);
-+
-+err_open:
-+	rtc_hctosys_ret = err;
-+}
-+#endif /* CONFIG_RTC_HCTOSYS */
-+
-+#endif /* !CONFIG_LOONGSON_MC146818 */
-diff --git a/arch/mips/loongson/common/uart_base.c b/arch/mips/loongson/common/uart_base.c
-deleted file mode 100644
-index e192ad0..0000000
---- a/arch/mips/loongson/common/uart_base.c
-+++ /dev/null
-@@ -1,45 +0,0 @@
--/*
-- * Copyright (C) 2009 Lemote Inc.
-- * Author: Wu Zhangjin, wuzhangjin@gmail.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 <asm/bootinfo.h>
--
--#include <loongson.h>
--
--/* ioremapped */
--unsigned long _loongson_uart_base;
--EXPORT_SYMBOL(_loongson_uart_base);
--/* raw */
--unsigned long loongson_uart_base;
--EXPORT_SYMBOL(loongson_uart_base);
--
--void prom_init_loongson_uart_base(void)
--{
--	switch (mips_machtype) {
--	case MACH_LEMOTE_FL2E:
--		loongson_uart_base = LOONGSON_PCIIO_BASE + 0x3f8;
--		break;
--	case MACH_LEMOTE_FL2F:
--	case MACH_LEMOTE_LL2F:
--		loongson_uart_base = LOONGSON_PCIIO_BASE + 0x2f8;
--		break;
--	case MACH_LEMOTE_ML2F7:
--	case MACH_LEMOTE_YL2F89:
--	case MACH_DEXXON_GDIUM2F10:
--	case MACH_LEMOTE_NAS:
--	default:
--		/* The CPU provided serial port */
--		loongson_uart_base = LOONGSON_LIO1_BASE + 0x3f8;
--		break;
--	}
--
--	_loongson_uart_base =
--		(unsigned long)ioremap_nocache(loongson_uart_base, 8);
--}
-diff --git a/arch/mips/loongson/fuloong-2e/irq.c b/arch/mips/loongson/fuloong-2e/irq.c
-index ef5ec8f..232930e 100644
---- a/arch/mips/loongson/fuloong-2e/irq.c
-+++ b/arch/mips/loongson/fuloong-2e/irq.c
-@@ -9,7 +9,6 @@
-  */
- #include <linux/interrupt.h>
- 
--#include <asm/irq_cpu.h>
- #include <asm/i8259.h>
- 
- #include <loongson.h>
-@@ -57,11 +56,6 @@ void __init mach_init_irq(void)
- 	LOONGSON_INTEDGE = LOONGSON_ICU_SYSTEMERR | LOONGSON_ICU_MASTERERR |
- 	    LOONGSON_ICU_RETRYERR | LOONGSON_ICU_MBOXES;
- 
--	/* Sets the first-level interrupt dispatcher. */
--	mips_cpu_irq_init();
--	init_i8259_irqs();
--	bonito_irq_init();
--
- 	/* bonito irq at IP2 */
- 	setup_irq(MIPS_CPU_IRQ_BASE + 2, &cascade_irqaction);
- 	/* 8259 irq at IP5 */
-diff --git a/arch/mips/loongson/gdium/Makefile b/arch/mips/loongson/gdium/Makefile
-new file mode 100644
-index 0000000..f3f4f51
---- /dev/null
-+++ b/arch/mips/loongson/gdium/Makefile
-@@ -0,0 +1,6 @@
-+# Makefile for gdium
-+
-+obj-y += irq.o reset.o platform.o
-+
-+obj-$(CONFIG_MFD_SM501) += sm501-pwm.o
-+obj-$(CONFIG_GDIUM_PWM_CLOCK) += gdium-clock.o
-diff --git a/arch/mips/loongson/gdium/gdium-clock.c b/arch/mips/loongson/gdium/gdium-clock.c
-new file mode 100644
-index 0000000..fdbf42a
---- /dev/null
-+++ b/arch/mips/loongson/gdium/gdium-clock.c
-@@ -0,0 +1,234 @@
-+/*
-+ * Doesn't work really well. When used, the clocksource is producing
-+ * bad timings and the clockevent can't be used (don't have one shot feature
-+ * thus can't switch on the fly and the pwm is initialised too late to be able
-+ * to use it at boot time).
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/platform_device.h>
-+#include <linux/interrupt.h>
-+#include <linux/delay.h>
-+#include <linux/pwm.h>
-+#include <linux/clocksource.h>
-+#include <linux/debugfs.h>
-+#include <asm/irq_cpu.h>
-+#include <asm/mipsregs.h>
-+#include <asm/mips-boards/bonito64.h>
-+#include <asm/time.h>
-+
-+#include <loongson.h>
-+
-+#define CLOCK_PWM		1
-+#define CLOCK_PWM_FREQ		1500000				/* Freq in Hz */
-+#define CLOCK_LATCH		((CLOCK_PWM_FREQ + HZ/2) / HZ)
-+#define CLOCK_PWM_PERIOD	(1000000000/CLOCK_PWM_FREQ)	/* period ns  */
-+#define CLOCK_PWM_DUTY		50
-+#define CLOCK_PWM_IRQ		(MIPS_CPU_IRQ_BASE + 4)
-+
-+static const char drv_name[] = "gdium-clock";
-+
-+static struct pwm_device *clock_pwm;
-+
-+static DEFINE_SPINLOCK(clock_pwm_lock);
-+static uint64_t clock_tick;
-+
-+static irqreturn_t gdium_pwm_clock_interrupt(int irq, void *dev_id)
-+{
-+	struct clock_event_device *cd = dev_id;
-+	unsigned long flag;
-+
-+	spin_lock_irqsave(&clock_pwm_lock, flag);
-+	clock_tick++;
-+	/* wait intn2 to finish */
-+	do {
-+		LOONGSON_INTENCLR = (1 << 13);
-+	} while (LOONGSON_INTISR & (1 << 13));
-+	spin_unlock_irqrestore(&clock_pwm_lock, flag);
-+
-+	if (cd && cd->event_handler)
-+		cd->event_handler(cd);
-+
-+	return IRQ_HANDLED;
-+}
-+
-+static cycle_t gdium_pwm_clock_read(struct clocksource *cs)
-+{
-+	unsigned long flag;
-+	uint32_t jifs;
-+	uint64_t ticks;
-+
-+	spin_lock_irqsave(&clock_pwm_lock, flag);
-+	jifs = jiffies;
-+	ticks = clock_tick;
-+	spin_unlock_irqrestore(&clock_pwm_lock, flag);
-+	/* return (cycle_t)ticks; */
-+	return (cycle_t)(CLOCK_LATCH * jifs);
-+}
-+
-+static struct clocksource gdium_pwm_clock_clocksource = {
-+	.name   = "gdium_csrc",
-+	.read   = gdium_pwm_clock_read,
-+	.mask   = CLOCKSOURCE_MASK(64),
-+	.flags	= CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_MUST_VERIFY,
-+	.shift	= 20,
-+};
-+
-+/* Debug fs */
-+static int gdium_pwm_clock_show(struct seq_file *s, void *p)
-+{
-+	unsigned long flag;
-+	uint64_t ticks;
-+
-+	spin_lock_irqsave(&clock_pwm_lock, flag);
-+	ticks = clock_tick;
-+	spin_unlock_irqrestore(&clock_pwm_lock, flag);
-+	seq_printf(s, "%lld\n", ticks);
-+	return 0;
-+}
-+
-+static int gdium_pwm_clock_open(struct inode *inode, struct file *file)
-+{
-+	return single_open(file, gdium_pwm_clock_show, inode->i_private);
-+}
-+
-+static const struct file_operations gdium_pwm_clock_fops = {
-+	.open		= gdium_pwm_clock_open,
-+	.read		= seq_read,
-+	.llseek		= seq_lseek,
-+	.release	= single_release,
-+	.owner		= THIS_MODULE,
-+};
-+static struct dentry   *debugfs_file;
-+
-+static void gdium_pwm_clock_set_mode(enum clock_event_mode mode,
-+		struct clock_event_device *evt)
-+{
-+	/* Nothing to do ...  */
-+}
-+
-+static struct clock_event_device gdium_pwm_clock_cevt = {
-+	.name           = "gdium_cevt",
-+	.features       = CLOCK_EVT_FEAT_PERIODIC,
-+	/* .mult, .shift, .max_delta_ns and .min_delta_ns left uninitialized */
-+	.rating         = 299,
-+	.irq            = CLOCK_PWM_IRQ,
-+	.set_mode       = gdium_pwm_clock_set_mode,
-+};
-+
-+static struct platform_device_id platform_device_ids[] = {
-+	{
-+		.name = "gdium-pwmclk",
-+	},
-+	{}
-+};
-+MODULE_DEVICE_TABLE(platform, platform_device_ids);
-+
-+static struct platform_driver gdium_pwm_clock_driver = {
-+	.driver		= {
-+		.name   = drv_name,
-+		.owner  = THIS_MODULE,
-+	},
-+	.id_table = platform_device_ids,
-+};
-+
-+static int gdium_pwm_clock_drvinit(void)
-+{
-+	int ret;
-+	struct clocksource *cs = &gdium_pwm_clock_clocksource;
-+	struct clock_event_device *cd = &gdium_pwm_clock_cevt;
-+	unsigned int cpu = smp_processor_id();
-+
-+	clock_tick = 0;
-+
-+	clock_pwm = pwm_request(CLOCK_PWM, drv_name);
-+	if (clock_pwm == NULL) {
-+		pr_err("unable to request PWM for Gdium clock\n");
-+		return -EBUSY;
-+	}
-+	ret = pwm_config(clock_pwm, CLOCK_PWM_DUTY, CLOCK_PWM_PERIOD);
-+	if (ret) {
-+		pr_err("unable to configure PWM for Gdium clock\n");
-+		goto err_pwm_request;
-+	}
-+	ret = pwm_enable(clock_pwm);
-+	if (ret) {
-+		pr_err("unable to enable PWM for Gdium clock\n");
-+		goto err_pwm_request;
-+	}
-+
-+	cd->cpumask = cpumask_of(cpu);
-+
-+	cd->shift = 22;
-+	cd->mult  = div_sc(CLOCK_PWM_FREQ, NSEC_PER_SEC, cd->shift);
-+	cd->max_delta_ns = clockevent_delta2ns(0x7FFF, cd);
-+	cd->min_delta_ns = clockevent_delta2ns(0xF, cd);
-+	clockevents_register_device(&gdium_pwm_clock_cevt);
-+
-+	/* SM501 PWM1 connected to intn2 <->ip4 */
-+	LOONGSON_INTPOL = (1 << 13);
-+	LOONGSON_INTEDGE &= ~(1 << 13);
-+	ret = request_irq(CLOCK_PWM_IRQ, gdium_pwm_clock_interrupt, IRQF_DISABLED, drv_name, &gdium_pwm_clock_cevt);
-+	if (ret) {
-+		pr_err("Can't claim irq\n");
-+		goto err_pwm_disable;
-+	}
-+
-+	cs->rating = 200;
-+	cs->mult = clocksource_hz2mult(CLOCK_PWM_FREQ, cs->shift);
-+	ret = clocksource_register(&gdium_pwm_clock_clocksource);
-+	if (ret) {
-+		pr_err("Can't register clocksource\n");
-+		goto err_irq;
-+	}
-+	pr_info("Clocksource registered with shift %d and mult %d\n",
-+			cs->shift, cs->mult);
-+
-+	debugfs_file = debugfs_create_file(drv_name, S_IFREG | S_IRUGO,
-+			NULL, NULL, &gdium_pwm_clock_fops);
-+
-+	return 0;
-+
-+err_irq:
-+	free_irq(CLOCK_PWM_IRQ, &gdium_pwm_clock_cevt);
-+err_pwm_disable:
-+	pwm_disable(clock_pwm);
-+err_pwm_request:
-+	pwm_free(clock_pwm);
-+	return ret;
-+}
-+
-+static void gdium_pwm_clock_drvexit(void)
-+{
-+	free_irq(CLOCK_PWM_IRQ, &gdium_pwm_clock_cevt);
-+	pwm_disable(clock_pwm);
-+	pwm_free(clock_pwm);
-+}
-+
-+
-+static int __devinit gdium_pwm_clock_init(void)
-+{
-+	int ret = gdium_pwm_clock_drvinit();
-+
-+	if (ret) {
-+		pr_err("Fail to register gdium clock driver\n");
-+		return ret;
-+	}
-+
-+	return platform_driver_register(&gdium_pwm_clock_driver);
-+}
-+
-+static void __exit gdium_pwm_clock_cleanup(void)
-+{
-+	gdium_pwm_clock_drvexit();
-+	platform_driver_unregister(&gdium_pwm_clock_driver);
-+}
-+
-+module_init(gdium_pwm_clock_init);
-+module_exit(gdium_pwm_clock_cleanup);
-+
-+MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
-+MODULE_DESCRIPTION("Gdium PWM clock driver");
-+MODULE_LICENSE("GPL");
-+MODULE_ALIAS("platform:gdium-pwmclk");
-diff --git a/arch/mips/loongson/gdium/irq.c b/arch/mips/loongson/gdium/irq.c
-new file mode 100644
-index 0000000..2415d20
---- /dev/null
-+++ b/arch/mips/loongson/gdium/irq.c
-@@ -0,0 +1,55 @@
-+/*
-+ * Copyright (C) 2007 Lemote Inc.
-+ * Author: Fuxin Zhang, zhangfx@lemote.com
-+ *
-+ * Copyright (c) 2010 yajin <yajin@vm-kernel.org>
-+ *
-+ *  This program is free software; you can redistribute  it and/or modify it
-+ *  under  the terms of  the GNU General  Public License as published by the
-+ *  Free Software Foundation;  either version 2 of the  License, or (at your
-+ *  option) any later version.
-+ */
-+
-+#include <linux/interrupt.h>
-+#include <linux/module.h>
-+
-+#include <loongson.h>
-+#include <machine.h>
-+
-+#define LOONGSON_TIMER_IRQ      (MIPS_CPU_IRQ_BASE + 7) /* cpu timer */
-+#define LOONGSON_NORTH_BRIDGE_IRQ       (MIPS_CPU_IRQ_BASE + 6) /* bonito */
-+#define LOONGSON_UART_IRQ       (MIPS_CPU_IRQ_BASE + 3) /* cpu serial port */
-+
-+void mach_irq_dispatch(unsigned int pending)
-+{
-+	if (pending & CAUSEF_IP7)
-+		do_IRQ(LOONGSON_TIMER_IRQ);
-+	else if (pending & CAUSEF_IP6) {        /* North Bridge, Perf counter */
-+		do_perfcnt_IRQ();
-+		bonito_irqdispatch();
-+	} else if (pending & CAUSEF_IP3)        /* CPU UART */
-+		do_IRQ(LOONGSON_UART_IRQ);
-+#if defined(CONFIG_GDIUM_PWM_CLOCK) || defined(CONFIG_GDIUM_PWM_CLOCK_MODULE)
-+	else if (pending & CAUSEF_IP4)		/* SM501 PWM clock */
-+		do_IRQ(MIPS_CPU_IRQ_BASE + 4);
-+#endif
-+	else
-+		spurious_interrupt();
-+}
-+
-+static irqreturn_t ip6_action(int cpl, void *dev_id)
-+{
-+	return IRQ_HANDLED;
-+}
-+
-+struct irqaction ip6_irqaction = {
-+	.handler = ip6_action,
-+	.name = "cascade",
-+	.flags = IRQF_SHARED,
-+};
-+
-+void __init mach_init_irq(void)
-+{
-+	/* setup north bridge irq (bonito) */
-+	setup_irq(LOONGSON_NORTH_BRIDGE_IRQ, &ip6_irqaction);
-+}
-diff --git a/arch/mips/loongson/gdium/platform.c b/arch/mips/loongson/gdium/platform.c
-new file mode 100644
-index 0000000..ffafba4
---- /dev/null
-+++ b/arch/mips/loongson/gdium/platform.c
-@@ -0,0 +1,135 @@
-+/*
-+ * Copyright (c) 2009 Philippe Vachon <philippe@cowpig.ca>
-+ *
-+ * 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/init.h>
-+#include <linux/kernel.h>
-+#include <linux/platform_device.h>
-+#include <linux/pwm_backlight.h>
-+#include <linux/i2c.h>
-+#include <linux/i2c-gpio.h>
-+
-+#define GDIUM_GPIO_BASE 224
-+
-+static struct i2c_board_info __initdata sm502dev_i2c_devices[] = {
-+	{
-+		I2C_BOARD_INFO("lm75", 0x48),
-+	},
-+	{
-+		I2C_BOARD_INFO("m41t83", 0x68),
-+	},
-+	{
-+		I2C_BOARD_INFO("gdium-laptop", 0x40),
-+	},
-+};
-+
-+static int sm502dev_backlight_init(struct device *dev)
-+{
-+	/* Add gpio request stuff here */
-+	return 0;
-+}
-+
-+static void sm502dev_backlight_exit(struct device *dev)
-+{
-+	/* Add gpio free stuff here */
-+}
-+
-+static struct platform_pwm_backlight_data backlight_data = {
-+	.pwm_id		= 0,
-+	.max_brightness	= 15,
-+	.dft_brightness	= 8,
-+	.pwm_period_ns	= 50000, /* 20 kHz */
-+	.init		= sm502dev_backlight_init,
-+	.exit		= sm502dev_backlight_exit,
-+};
-+
-+static struct platform_device backlight = {
-+	.name = "pwm-backlight",
-+	.dev  = {
-+		.platform_data = &backlight_data,
-+	},
-+	.id   = -1,
-+};
-+
-+/*
-+ * Warning this stunt is very dangerous
-+ * as the sm501 gpio have dynamic numbers...
-+ */
-+/* bus 0 is the one for the ST7, DS75 etc... */
-+static struct i2c_gpio_platform_data i2c_gpio0_data = {
-+#if CONFIG_GDIUM_VERSION > 2
-+	.sda_pin	= GDIUM_GPIO_BASE + 13,
-+	.scl_pin	= GDIUM_GPIO_BASE + 6,
-+#else
-+	.sda_pin        = 192+15,
-+	.scl_pin        = 192+14,
-+#endif
-+	.udelay		= 5,
-+	.timeout	= HZ / 10,
-+	.sda_is_open_drain = 0,
-+	.scl_is_open_drain = 0,
-+};
-+
-+static struct platform_device i2c_gpio0_device = {
-+	.name	= "i2c-gpio",
-+	.id	= 0,
-+	.dev	= { .platform_data  = &i2c_gpio0_data, },
-+};
-+
-+/* bus 1 is for the CRT/VGA external screen */
-+static struct i2c_gpio_platform_data i2c_gpio1_data = {
-+	.sda_pin	= GDIUM_GPIO_BASE + 10,
-+	.scl_pin	= GDIUM_GPIO_BASE + 9,
-+	.udelay		= 5,
-+	.timeout	= HZ / 10,
-+	.sda_is_open_drain = 0,
-+	.scl_is_open_drain = 0,
-+};
-+
-+static struct platform_device i2c_gpio1_device = {
-+	.name	= "i2c-gpio",
-+	.id	= 1,
-+	.dev	= { .platform_data  = &i2c_gpio1_data, },
-+};
-+
-+static struct platform_device gdium_clock = {
-+	.name		= "gdium-pwmclk",
-+	.id		= -1,
-+};
-+
-+static struct platform_device *devices[] __initdata = {
-+	&i2c_gpio0_device,
-+	&i2c_gpio1_device,
-+	&backlight,
-+	&gdium_clock,
-+};
-+
-+static int __init gdium_platform_devices_setup(void)
-+{
-+	int ret;
-+
-+	pr_info("Registering gdium platform devices\n");
-+
-+	ret = i2c_register_board_info(0, sm502dev_i2c_devices,
-+		ARRAY_SIZE(sm502dev_i2c_devices));
-+
-+	if (ret != 0) {
-+		pr_info("Error while registering platform devices: %d\n", ret);
-+		return ret;
-+	}
-+
-+	platform_add_devices(devices, ARRAY_SIZE(devices));
-+
-+	return 0;
-+}
-+
-+/*
-+ * some devices are on the pwm stuff which is behind the mfd which is
-+ * behind the pci bus so arch_initcall can't work because too early
-+ */
-+late_initcall(gdium_platform_devices_setup);
-diff --git a/arch/mips/loongson/gdium/reset.c b/arch/mips/loongson/gdium/reset.c
-new file mode 100644
-index 0000000..8289f95
---- /dev/null
-+++ b/arch/mips/loongson/gdium/reset.c
-@@ -0,0 +1,22 @@
-+/* Board-specific reboot/shutdown routines
-+ *
-+ * Copyright (C) 2010 yajin <yajin@vm-kernel.org>
-+ *
-+ * This program is free software; you can redistribute  it and/or modify it
-+ * under  the terms of  the GNU General  Public License as published by the
-+ * Free Software Foundation;  either version 2 of the  License, or (at your
-+ * option) any later version.
-+ */
-+#include <loongson.h>
-+
-+void mach_prepare_shutdown(void)
-+{
-+	LOONGSON_GPIOIE &= ~(1<<1);
-+	LOONGSON_GPIODATA |= (1<<1);
-+}
-+
-+void mach_prepare_reboot(void)
-+{
-+	LOONGSON_GPIOIE &= ~(1<<2);
-+	LOONGSON_GPIODATA &= ~(1<<2);
-+}
-diff --git a/arch/mips/loongson/gdium/sm501-pwm.c b/arch/mips/loongson/gdium/sm501-pwm.c
-new file mode 100644
-index 0000000..5af3b23
---- /dev/null
-+++ b/arch/mips/loongson/gdium/sm501-pwm.c
-@@ -0,0 +1,465 @@
-+/*
-+ * SM501 PWM clock
-+ * Copyright (C) 2009-2010 Arnaud Patard <apatard@mandriva.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/kernel.h>
-+#include <linux/errno.h>
-+#include <linux/init.h>
-+#include <linux/interrupt.h>
-+#include <linux/platform_device.h>
-+#include <linux/slab.h>
-+#include <linux/pwm.h>
-+#include <linux/sm501.h>
-+#include <linux/sm501-regs.h>
-+#include <linux/debugfs.h>
-+#include <linux/seq_file.h>
-+
-+static const char drv_name[] = "sm501-pwm";
-+
-+#define INPUT_CLOCK		96 /* MHz */
-+#define PWM_COUNT		3
-+
-+#define SM501PWM_HIGH_COUNTER	(1<<20)
-+#define SM501PWM_LOW_COUNTER	(1<<8)
-+#define SM501PWM_CLOCK_DIVIDE	(1>>4)
-+#define SM501PWM_IP		(1<<3)
-+#define SM501PWM_I		(1<<2)
-+#define SM501PWM_E		(1<<0)
-+
-+struct pwm_device {
-+	struct list_head	node;
-+	struct device		*dev;
-+	void __iomem		*regs;
-+	int			duty_ns;
-+	int			period_ns;
-+	char			enabled;
-+	void			(*handler)(struct pwm_device *pwm);
-+
-+	const char		*label;
-+	unsigned int		use_count;
-+	unsigned int		pwm_id;
-+};
-+
-+struct sm501pwm_info {
-+	void __iomem	*regs;
-+	int		irq;
-+	struct resource *res;
-+	struct device	*dev;
-+	struct dentry	*debugfs;
-+
-+	struct pwm_device pwm[3];
-+};
-+
-+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
-+{
-+	unsigned int high, low, divider;
-+	int divider1, divider2;
-+	unsigned long long delay;
-+
-+	if (!pwm || !pwm->regs || period_ns == 0 || duty_ns > period_ns)
-+		return -EINVAL;
-+
-+	/* Get delay
-+	 * We're loosing some precision but multiplying then dividing
-+	 * will overflow
-+	 */
-+	if (period_ns > 1000) {
-+		delay = period_ns / 1000;
-+		delay *= INPUT_CLOCK;
-+	} else {
-+		delay = period_ns * 96;
-+		delay /= 1000;
-+	}
-+
-+	/* Get the number of clock low and high */
-+	high  = delay * duty_ns / period_ns;
-+	low = delay - high;
-+
-+	/* Get divider to make 'low' and 'high' fit into 12 bits */
-+	/* No need to say that the divider must be >= 0 */
-+	divider1 = fls(low)-12;
-+	divider2 = fls(high)-12;
-+
-+	if (divider1 < 0)
-+		divider1 = 0;
-+	if (divider2 < 0)
-+		divider2 = 0;
-+
-+	divider = max(divider1, divider2);
-+
-+	low >>= divider;
-+	high >>= divider;
-+
-+	pwm->duty_ns = duty_ns;
-+	pwm->period_ns = period_ns;
-+
-+	writel((high<<20)|(low<<8)|(divider<<4), pwm->regs);
-+	return 0;
-+}
-+EXPORT_SYMBOL(pwm_config);
-+
-+int pwm_enable(struct pwm_device *pwm)
-+{
-+	u32 reg;
-+
-+	if (!pwm)
-+		return -EINVAL;
-+
-+	switch (pwm->pwm_id) {
-+	case 0:
-+		sm501_configure_gpio(pwm->dev->parent, 29, 1);
-+		break;
-+	case 1:
-+		sm501_configure_gpio(pwm->dev->parent, 30, 1);
-+		break;
-+	case 2:
-+		sm501_configure_gpio(pwm->dev->parent, 31, 1);
-+		break;
-+	default:
-+		return -EINVAL;
-+	}
-+
-+	reg = readl(pwm->regs);
-+	reg |= (SM501PWM_IP | SM501PWM_E);
-+	writel(reg, pwm->regs);
-+	pwm->enabled = 1;
-+
-+	return 0;
-+}
-+EXPORT_SYMBOL(pwm_enable);
-+
-+void pwm_disable(struct pwm_device *pwm)
-+{
-+	u32 reg;
-+
-+	if (!pwm)
-+		return;
-+
-+	reg = readl(pwm->regs);
-+	reg &= ~(SM501PWM_IP | SM501PWM_E);
-+	writel(reg, pwm->regs);
-+
-+	switch (pwm->pwm_id) {
-+	case 0:
-+		sm501_configure_gpio(pwm->dev->parent, 29, 0);
-+		break;
-+	case 1:
-+		sm501_configure_gpio(pwm->dev->parent, 30, 0);
-+		break;
-+	case 2:
-+		sm501_configure_gpio(pwm->dev->parent, 31, 0);
-+		break;
-+	default:
-+		break;
-+	}
-+	pwm->enabled = 0;
-+}
-+EXPORT_SYMBOL(pwm_disable);
-+
-+static DEFINE_MUTEX(pwm_lock);
-+static LIST_HEAD(pwm_list);
-+
-+struct pwm_device *pwm_request(int pwm_id, const char *label)
-+{
-+	struct pwm_device *pwm;
-+	int found = 0;
-+
-+	mutex_lock(&pwm_lock);
-+
-+	list_for_each_entry(pwm, &pwm_list, node) {
-+		if (pwm->pwm_id == pwm_id && pwm->use_count == 0) {
-+			pwm->use_count++;
-+			pwm->label = label;
-+			found = 1;
-+			break;
-+		}
-+	}
-+
-+	mutex_unlock(&pwm_lock);
-+
-+	return (found) ? pwm : NULL;
-+}
-+EXPORT_SYMBOL(pwm_request);
-+
-+void pwm_free(struct pwm_device *pwm)
-+{
-+	mutex_lock(&pwm_lock);
-+
-+	if (pwm->use_count) {
-+		pwm->use_count--;
-+		pwm->label = NULL;
-+	} else
-+		dev_warn(pwm->dev, "PWM device already freed\n");
-+
-+	mutex_unlock(&pwm_lock);
-+}
-+EXPORT_SYMBOL(pwm_free);
-+
-+int pwm_int_enable(struct pwm_device *pwm)
-+{
-+	unsigned long conf;
-+
-+	if (!pwm || !pwm->regs || !pwm->handler)
-+		return -EINVAL;
-+
-+	conf = readl(pwm->regs);
-+	conf |= SM501PWM_I;
-+	writel(conf, pwm->regs);
-+	return 0;
-+}
-+EXPORT_SYMBOL(pwm_int_enable);
-+
-+int pwm_int_disable(struct pwm_device *pwm)
-+{
-+	unsigned long conf;
-+
-+	if (!pwm || !pwm->regs || !pwm->handler)
-+		return -EINVAL;
-+
-+	conf = readl(pwm->regs);
-+	conf &= ~SM501PWM_I;
-+	writel(conf, pwm->regs);
-+	return 0;
-+}
-+EXPORT_SYMBOL(pwm_int_disable);
-+
-+int pwm_set_handler(struct pwm_device *pwm,
-+		    void (*handler)(struct pwm_device *pwm))
-+{
-+	if (!pwm || !handler)
-+		return -EINVAL;
-+	pwm->handler = handler;
-+	return 0;
-+}
-+EXPORT_SYMBOL(pwm_set_handler);
-+
-+static irqreturn_t sm501pwm_irq(int irq, void *dev_id)
-+{
-+	unsigned long value;
-+	struct sm501pwm_info *info = (struct sm501pwm_info *)dev_id;
-+	struct pwm_device *pwm;
-+	int i;
-+
-+	value = sm501_modify_reg(info->dev->parent, SM501_IRQ_STATUS, 0, 0);
-+
-+	/* Check is the interrupt is for us */
-+	if (value & (1<<22)) {
-+		for (i = 0 ; i < PWM_COUNT ; i++) {
-+			/*
-+			 * Find which pwm triggered the interrupt
-+			 * and ack
-+			 */
-+			value = readl(info->regs + i*4);
-+			if (value & SM501PWM_IP)
-+				writel(value | SM501PWM_IP, info->regs + i*4);
-+
-+			pwm = &info->pwm[i];
-+			if (pwm->handler)
-+				pwm->handler(pwm);
-+		}
-+		return IRQ_HANDLED;
-+	}
-+
-+	return IRQ_NONE;
-+}
-+
-+static void add_pwm(int id, struct sm501pwm_info *info)
-+{
-+	struct pwm_device *pwm = &info->pwm[id];
-+
-+	pwm->use_count	= 0;
-+	pwm->pwm_id	= id;
-+	pwm->dev	= info->dev;
-+	pwm->regs	= info->regs + id * 4;
-+
-+	mutex_lock(&pwm_lock);
-+	list_add_tail(&pwm->node, &pwm_list);
-+	mutex_unlock(&pwm_lock);
-+}
-+
-+static void del_pwm(int id, struct sm501pwm_info *info)
-+{
-+	struct pwm_device *pwm = &info->pwm[id];
-+
-+	pwm->use_count  = 0;
-+	pwm->pwm_id     = -1;
-+	mutex_lock(&pwm_lock);
-+	list_del(&pwm->node);
-+	mutex_unlock(&pwm_lock);
-+}
-+
-+/* Debug fs */
-+static int sm501pwm_show(struct seq_file *s, void *p)
-+{
-+	struct pwm_device *pwm;
-+
-+	mutex_lock(&pwm_lock);
-+	list_for_each_entry(pwm, &pwm_list, node) {
-+		if (pwm->use_count) {
-+			seq_printf(s, "pwm-%d (%12s) %d %d %s\n",
-+					pwm->pwm_id, pwm->label,
-+					pwm->duty_ns, pwm->period_ns,
-+					pwm->enabled ? "on" : "off");
-+			seq_printf(s, "       %08x\n", readl(pwm->regs));
-+		}
-+	}
-+	mutex_unlock(&pwm_lock);
-+
-+	return 0;
-+}
-+
-+static int sm501pwm_open(struct inode *inode, struct file *file)
-+{
-+	return single_open(file, sm501pwm_show, inode->i_private);
-+}
-+
-+static const struct file_operations sm501pwm_fops = {
-+	.open		= sm501pwm_open,
-+	.read		= seq_read,
-+	.llseek		= seq_lseek,
-+	.release	= single_release,
-+	.owner		= THIS_MODULE,
-+};
-+
-+static int __init sm501pwm_probe(struct platform_device *pdev)
-+{
-+	struct sm501pwm_info *info;
-+	struct device   *dev = &pdev->dev;
-+	struct resource *res;
-+	int ret = 0;
-+	int res_len;
-+	int i;
-+
-+	info = kzalloc(sizeof(struct sm501pwm_info), GFP_KERNEL);
-+	if (!info) {
-+		dev_err(dev, "Allocation failure\n");
-+		ret = -ENOMEM;
-+		goto err;
-+	}
-+	info->dev = dev;
-+	platform_set_drvdata(pdev, info);
-+
-+	/* Get irq number */
-+	info->irq = platform_get_irq(pdev, 0);
-+	if (!info->irq) {
-+		dev_err(dev, "no irq found\n");
-+		ret = -ENODEV;
-+		goto err_alloc;
-+	}
-+
-+	/* Get regs address */
-+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+	if (res == NULL) {
-+		dev_err(dev, "No memory resource found\n");
-+		ret = -ENODEV;
-+		goto err_alloc;
-+	}
-+	info->res = res;
-+	res_len = (res->end - res->start)+1;
-+
-+	if (!request_mem_region(res->start, res_len, drv_name)) {
-+		dev_err(dev, "Can't request iomem resource\n");
-+		ret = -EBUSY;
-+		goto err_alloc;
-+	}
-+
-+	info->regs = ioremap(res->start, res_len);
-+	if (!info->regs) {
-+		dev_err(dev, "ioremap failed\n");
-+		ret = -ENOMEM;
-+		goto err_mem;
-+	}
-+
-+	ret = request_irq(info->irq, sm501pwm_irq, IRQF_SHARED, drv_name, info);
-+	if (ret != 0) {
-+		dev_err(dev, "can't get irq\n");
-+		goto err_map;
-+	}
-+
-+
-+	sm501_unit_power(info->dev->parent, SM501_GATE_GPIO, 1);
-+
-+	for (i = 0; i < 3; i++)
-+		add_pwm(i, info);
-+
-+	dev_info(dev, "SM501 PWM Found at %lx irq %d\n",
-+		 (unsigned long)info->res->start, info->irq);
-+
-+	info->debugfs = debugfs_create_file("pwm", S_IFREG | S_IRUGO,
-+				NULL, info, &sm501pwm_fops);
-+
-+
-+	return 0;
-+
-+err_map:
-+	iounmap(info->regs);
-+
-+err_mem:
-+	release_mem_region(res->start, res_len);
-+
-+err_alloc:
-+	kfree(info);
-+	platform_set_drvdata(pdev, NULL);
-+err:
-+	return ret;
-+}
-+
-+static int sm501pwm_remove(struct platform_device *pdev)
-+{
-+	struct sm501pwm_info *info = platform_get_drvdata(pdev);
-+	int i;
-+
-+	if (info->debugfs)
-+		debugfs_remove(info->debugfs);
-+
-+	for (i = 0; i < 3; i++) {
-+		pwm_disable(&info->pwm[i]);
-+		del_pwm(i, info);
-+	}
-+
-+	sm501_unit_power(info->dev->parent, SM501_GATE_GPIO, 0);
-+	sm501_modify_reg(info->dev->parent, SM501_IRQ_STATUS, 0, 1<<22);
-+
-+	free_irq(info->irq, info);
-+	iounmap(info->regs);
-+	release_mem_region(info->res->start,
-+			   (info->res->end - info->res->start)+1);
-+	kfree(info);
-+	platform_set_drvdata(pdev, NULL);
-+
-+	return 0;
-+}
-+
-+static struct platform_driver sm501pwm_driver = {
-+	.probe		= sm501pwm_probe,
-+	.remove		= sm501pwm_remove,
-+	.driver		= {
-+		.name	= drv_name,
-+		.owner	= THIS_MODULE,
-+	},
-+};
-+
-+static int __devinit sm501pwm_init(void)
-+{
-+	return platform_driver_register(&sm501pwm_driver);
-+}
-+
-+static void __exit sm501pwm_cleanup(void)
-+{
-+	platform_driver_unregister(&sm501pwm_driver);
-+}
-+
-+module_init(sm501pwm_init);
-+module_exit(sm501pwm_cleanup);
-+
-+MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
-+MODULE_DESCRIPTION("SM501 PWM driver");
-+MODULE_LICENSE("GPL");
-+MODULE_ALIAS("platform:sm501-pwm");
-diff --git a/arch/mips/loongson/lemote-2f/Makefile b/arch/mips/loongson/lemote-2f/Makefile
-index 4f9eaa3..f945bd7a 100644
---- a/arch/mips/loongson/lemote-2f/Makefile
-+++ b/arch/mips/loongson/lemote-2f/Makefile
-@@ -2,7 +2,7 @@
- # Makefile for lemote loongson2f family machines
- #
- 
--obj-y += clock.o machtype.o irq.o reset.o ec_kb3310b.o
-+obj-y += clock.o machtype.o irq.o reset.o ec_kb3310b.o platform.o
- 
- #
- # Suspend Support
-diff --git a/arch/mips/loongson/lemote-2f/irq.c b/arch/mips/loongson/lemote-2f/irq.c
-index 6f8682e..2d54037 100644
---- a/arch/mips/loongson/lemote-2f/irq.c
-+++ b/arch/mips/loongson/lemote-2f/irq.c
-@@ -11,9 +11,7 @@
- #include <linux/interrupt.h>
- #include <linux/module.h>
- 
--#include <asm/irq_cpu.h>
- #include <asm/i8259.h>
--#include <asm/mipsregs.h>
- 
- #include <loongson.h>
- #include <machine.h>
-@@ -117,11 +115,6 @@ void __init mach_init_irq(void)
- 	LOONGSON_INTPOL = LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1;
- 	LOONGSON_INTEDGE &= ~(LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1);
- 
--	/* Sets the first-level interrupt dispatcher. */
--	mips_cpu_irq_init();
--	init_i8259_irqs();
--	bonito_irq_init();
--
- 	/* setup north bridge irq (bonito) */
- 	setup_irq(LOONGSON_NORTH_BRIDGE_IRQ, &ip6_irqaction);
- 	/* setup source bridge irq (i8259) */
-diff --git a/arch/mips/loongson/lemote-2f/platform.c b/arch/mips/loongson/lemote-2f/platform.c
-new file mode 100644
-index 0000000..5316360
---- /dev/null
-+++ b/arch/mips/loongson/lemote-2f/platform.c
-@@ -0,0 +1,48 @@
-+/*
-+ * Copyright (C) 2009 Lemote Inc.
-+ * Author: Wu Zhangjin, wuzhangjin@gmail.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/err.h>
-+#include <linux/platform_device.h>
-+
-+#include <asm/bootinfo.h>
-+
-+static struct platform_device yeeloong_pdev = {
-+	.name = "yeeloong_laptop",
-+	.id = -1,
-+};
-+
-+static struct platform_device lynloong_pdev = {
-+	.name = "lynloong_pc",
-+	.id = -1,
-+};
-+
-+static int __init lemote2f_platform_init(void)
-+{
-+	struct platform_device *pdev = NULL;
-+
-+	switch (mips_machtype) {
-+	case MACH_LEMOTE_YL2F89:
-+		pdev = &yeeloong_pdev;
-+		break;
-+	case MACH_LEMOTE_LL2F:
-+		pdev = &lynloong_pdev;
-+		break;
-+	default:
-+		break;
-+
-+	}
-+
-+	if (pdev != NULL)
-+		return platform_device_register(pdev);
-+
-+	return -ENODEV;
-+}
-+
-+arch_initcall(lemote2f_platform_init);
-diff --git a/arch/mips/loongson/lemote-2f/pm.c b/arch/mips/loongson/lemote-2f/pm.c
-index cac4d38..2b6e0ef 100644
---- a/arch/mips/loongson/lemote-2f/pm.c
-+++ b/arch/mips/loongson/lemote-2f/pm.c
-@@ -140,10 +140,10 @@ int wakeup_loongson(void)
- 
- void __weak mach_suspend(void)
- {
--	disable_mfgpt0_counter();
-+	disable_mfgpt_counter();
- }
- 
- void __weak mach_resume(void)
- {
--	enable_mfgpt0_counter();
-+	enable_mfgpt_counter();
- }
-diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
-index 0b4e2e3..60fad2c 100644
---- a/arch/mips/math-emu/cp1emu.c
-+++ b/arch/mips/math-emu/cp1emu.c
-@@ -7,6 +7,9 @@
-  * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
-  * Copyright (C) 2000  MIPS Technologies, Inc.
-  *
-+ * Loongson instruction support
-+ * Copyright (C) 2011  Mark H Weaver <mhw@netris.org>
-+ *
-  *  This program is free software; you can distribute it and/or modify it
-  *  under the terms of the GNU General Public License (Version 2) as
-  *  published by the Free Software Foundation.
-@@ -58,6 +61,14 @@
- #endif
- #define __mips 4
- 
-+#ifdef __loongson_fp
-+#undef __loongson_fp
-+#endif
-+#if __mips >= 4 && __mips != 32
-+/* Include support for Loongson floating point instructions */
-+#define __loongson_fp 1
-+#endif
-+
- /* Function which emulates a floating point instruction. */
- 
- static int fpu_emu(struct pt_regs *, struct mips_fpu_struct *,
-@@ -67,6 +78,10 @@ static int fpu_emu(struct pt_regs *, struct mips_fpu_struct *,
- static int fpux_emu(struct pt_regs *,
- 	struct mips_fpu_struct *, mips_instruction, void *__user *);
- #endif
-+#ifdef __loongson_fp
-+static int loongson_spec2_emu(struct pt_regs *,
-+	struct mips_fpu_struct *, mips_instruction, void *__user *);
-+#endif
- 
- /* Further private data for which no space exists in mips_fpu_struct */
- 
-@@ -896,6 +911,14 @@ static inline int cop1_64bit(struct pt_regs *xcp)
- #define DPFROMREG(dp, x)	DIFROMREG((dp).bits, x)
- #define DPTOREG(dp, x)	DITOREG((dp).bits, x)
- 
-+/* Support for Loongson paired single floating-point format */
-+#define PSIFROMREG(si1, si2, x) ({ u64 di; DIFROMREG(di, x);		\
-+			(si1) = (u32)di; (si2) = (u32)(di >> 32); })
-+#define PSITOREG(si1, si2, x) DITOREG((si1) | ((u64)(si2) << 32), x)
-+
-+#define PSPFROMREG(sp1, sp2, x) PSIFROMREG((sp1).bits, (sp2).bits, x)
-+#define PSPTOREG(sp1, sp2, x)	PSITOREG((sp1).bits, (sp2).bits, x)
-+
- /*
-  * Emulate the single floating point instruction pointed at by EPC.
-  * Two instructions if the instruction is in a branch delay slot.
-@@ -1291,6 +1314,15 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
- 		break;
- #endif
- 
-+#ifdef __loongson_fp
-+	case spec2_op:{
-+		int sig = loongson_spec2_emu(xcp, ctx, ir, fault_addr);
-+		if (sig)
-+			return sig;
-+		break;
-+	}
-+#endif
-+
- 	default:
- sigill:
- 		return SIGILL;
-@@ -1370,6 +1402,172 @@ DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub, );
- DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg);
- DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg);
- 
-+#ifdef __loongson_fp
-+static int loongson_spec2_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
-+	mips_instruction ir, void *__user *fault_addr)
-+{
-+	int rfmt;		/* resulting format */
-+	unsigned rcsr = 0;	/* resulting csr */
-+	union {
-+		ieee754dp d;
-+		struct {
-+			ieee754sp s;
-+			ieee754sp s2;
-+		};
-+	} rv;			/* resulting value */
-+
-+	/* XXX maybe add a counter for loongson spec2 fp instructions? */
-+	/* MIPS_FPU_EMU_INC_STATS(cp1xops); */
-+
-+	switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) {
-+	case s_fmt:{
-+		ieee754sp(*handler) (ieee754sp, ieee754sp, ieee754sp);
-+		ieee754sp fd, fs, ft;
-+
-+		switch (MIPSInst_FUNC(ir)) {
-+		case loongson_madd_op:
-+			handler = fpemu_sp_madd;
-+			goto scoptop;
-+		case loongson_msub_op:
-+			handler = fpemu_sp_msub;
-+			goto scoptop;
-+		case loongson_nmadd_op:
-+			handler = fpemu_sp_nmadd;
-+			goto scoptop;
-+		case loongson_nmsub_op:
-+			handler = fpemu_sp_nmsub;
-+			goto scoptop;
-+
-+		      scoptop:
-+			SPFROMREG(fd, MIPSInst_FD(ir));
-+			SPFROMREG(fs, MIPSInst_FS(ir));
-+			SPFROMREG(ft, MIPSInst_FT(ir));
-+			rv.s = (*handler) (fd, fs, ft);
-+
-+		      copcsr:
-+			if (ieee754_cxtest(IEEE754_INEXACT))
-+				rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S;
-+			if (ieee754_cxtest(IEEE754_UNDERFLOW))
-+				rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S;
-+			if (ieee754_cxtest(IEEE754_OVERFLOW))
-+				rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S;
-+			if (ieee754_cxtest(IEEE754_INVALID_OPERATION))
-+				rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S;
-+
-+			break;
-+
-+		default:
-+			return SIGILL;
-+		}
-+		break;
-+	}
-+
-+	case d_fmt:{
-+		ieee754dp(*handler) (ieee754dp, ieee754dp, ieee754dp);
-+		ieee754dp fd, fs, ft;
-+
-+		switch (MIPSInst_FUNC(ir)) {
-+		case loongson_madd_op:
-+			handler = fpemu_dp_madd;
-+			goto dcoptop;
-+		case loongson_msub_op:
-+			handler = fpemu_dp_msub;
-+			goto dcoptop;
-+		case loongson_nmadd_op:
-+			handler = fpemu_dp_nmadd;
-+			goto dcoptop;
-+		case loongson_nmsub_op:
-+			handler = fpemu_dp_nmsub;
-+			goto dcoptop;
-+
-+		      dcoptop:
-+			DPFROMREG(fd, MIPSInst_FD(ir));
-+			DPFROMREG(fs, MIPSInst_FS(ir));
-+			DPFROMREG(ft, MIPSInst_FT(ir));
-+			rv.d = (*handler) (fd, fs, ft);
-+			goto copcsr;
-+
-+		default:
-+			return SIGILL;
-+		}
-+		break;
-+	}
-+
-+	case ps_fmt:{
-+		ieee754sp(*handler) (ieee754sp, ieee754sp, ieee754sp);
-+		struct _ieee754_csr ieee754_csr_save;
-+		ieee754sp fd1, fs1, ft1;
-+		ieee754sp fd2, fs2, ft2;
-+
-+		switch (MIPSInst_FUNC(ir)) {
-+		case loongson_madd_op:
-+			handler = fpemu_sp_madd;
-+			goto pscoptop;
-+		case loongson_msub_op:
-+			handler = fpemu_sp_msub;
-+			goto pscoptop;
-+		case loongson_nmadd_op:
-+			handler = fpemu_sp_nmadd;
-+			goto pscoptop;
-+		case loongson_nmsub_op:
-+			handler = fpemu_sp_nmsub;
-+			goto pscoptop;
-+
-+		      pscoptop:
-+			PSPFROMREG(fd1, fd2, MIPSInst_FD(ir));
-+			PSPFROMREG(fs1, fs2, MIPSInst_FS(ir));
-+			PSPFROMREG(ft1, ft2, MIPSInst_FT(ir));
-+			rv.s = (*handler) (fd1, fs1, ft1);
-+			ieee754_csr_save = ieee754_csr;
-+			rv.s2 = (*handler) (fd2, fs2, ft2);
-+			ieee754_csr.cx |= ieee754_csr_save.cx;
-+			ieee754_csr.sx |= ieee754_csr_save.sx;
-+			goto copcsr;
-+
-+		default:
-+			return SIGILL;
-+		}
-+		break;
-+	}
-+
-+	default:
-+		return SIGILL;
-+	}
-+
-+	/*
-+	 * Update the fpu CSR register for this operation.
-+	 * If an exception is required, generate a tidy SIGFPE exception,
-+	 * without updating the result register.
-+	 * Note: cause exception bits do not accumulate, they are rewritten
-+	 * for each op; only the flag/sticky bits accumulate.
-+	 */
-+	ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr;
-+	if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
-+		/*printk ("SIGFPE: fpu csr = %08x\n",ctx->fcr31); */
-+		return SIGFPE;
-+	}
-+
-+	/*
-+	 * Now we can safely write the result back to the register file.
-+	 */
-+	switch (rfmt) {
-+	case d_fmt:
-+		DPTOREG(rv.d, MIPSInst_FD(ir));
-+		break;
-+	case s_fmt:
-+		SPTOREG(rv.s, MIPSInst_FD(ir));
-+		break;
-+	case ps_fmt:
-+		PSPTOREG(rv.s, rv.s2, MIPSInst_FD(ir));
-+		break;
-+	default:
-+		return SIGILL;
-+	}
-+
-+	return 0;
-+}
-+#endif
-+
- static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
- 	mips_instruction ir, void *__user *fault_addr)
- {
-@@ -1463,7 +1661,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
- 			break;
- 
- 		default:
--			return SIGILL;
-+			goto SIGILL_unless_prefx_op;
- 		}
- 		break;
- 	}
-@@ -1533,7 +1731,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
- 			goto copcsr;
- 
- 		default:
--			return SIGILL;
-+			goto SIGILL_unless_prefx_op;
- 		}
- 		break;
- 	}
-@@ -1546,6 +1744,11 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
- 		break;
- 
- 	default:
-+	      SIGILL_unless_prefx_op:
-+		if (MIPSInst_FUNC(ir) == prefx_op) {
-+			/* ignore prefx operation */
-+			break;
-+		}
- 		return SIGILL;
- 	}
- 
-@@ -1566,7 +1769,12 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
- 	unsigned cond;
- 	union {
- 		ieee754dp d;
--		ieee754sp s;
-+		struct {
-+			ieee754sp s;
-+#ifdef __loongson_fp
-+			ieee754sp s2; /* for Loongson paired singles */
-+#endif
-+		};
- 		int w;
- #ifdef __mips64
- 		s64 l;
-@@ -1638,7 +1846,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
- 		case fmov_op:
- 			/* an easy one */
- 			SPFROMREG(rv.s, MIPSInst_FS(ir));
--			goto copcsr;
-+			break;
- 
- 			/* binary op on handler */
- 		      scopbop:
-@@ -1825,7 +2033,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
- 		case fmov_op:
- 			/* an easy one */
- 			DPFROMREG(rv.d, MIPSInst_FS(ir));
--			goto copcsr;
-+			break;
- 
- 			/* binary op on handler */
- 		      dcopbop:{
-@@ -1936,6 +2144,83 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
- 		break;
- 	}
- 
-+#ifdef __loongson_fp
-+	case ps_fmt:{		/* 6 */
-+		/* Support for Loongson paired single fp instructions */
-+		union {
-+			ieee754sp(*b) (ieee754sp, ieee754sp);
-+			ieee754sp(*u) (ieee754sp);
-+		} handler;
-+
-+		switch (MIPSInst_FUNC(ir)) {
-+			/* binary ops */
-+		case fadd_op:
-+			handler.b = ieee754sp_add;
-+			goto pscopbop;
-+		case fsub_op:
-+			handler.b = ieee754sp_sub;
-+			goto pscopbop;
-+		case fmul_op:
-+			handler.b = ieee754sp_mul;
-+			goto pscopbop;
-+
-+			/* unary  ops */
-+		case fabs_op:
-+			handler.u = ieee754sp_abs;
-+			goto pscopuop;
-+		case fneg_op:
-+			handler.u = ieee754sp_neg;
-+			goto pscopuop;
-+		case fmov_op:
-+			/* an easy one */
-+			PSPFROMREG(rv.s, rv.s2, MIPSInst_FS(ir));
-+			break;
-+
-+		      pscopbop: /* paired binary op handler */
-+			{
-+				struct _ieee754_csr ieee754_csr_save;
-+				ieee754sp fs1, ft1;
-+				ieee754sp fs2, ft2;
-+
-+				PSPFROMREG(fs1, fs2, MIPSInst_FS(ir));
-+				PSPFROMREG(ft1, ft2, MIPSInst_FT(ir));
-+				rv.s  = (*handler.b) (fs1, ft1);
-+				ieee754_csr_save = ieee754_csr;
-+				rv.s2 = (*handler.b) (fs2, ft2);
-+				ieee754_csr.cx |= ieee754_csr_save.cx;
-+				ieee754_csr.sx |= ieee754_csr_save.sx;
-+				goto copcsr;
-+			}
-+		      pscopuop: /* paired unary op handler */
-+			{
-+				struct _ieee754_csr ieee754_csr_save;
-+				ieee754sp fs1;
-+				ieee754sp fs2;
-+
-+				PSPFROMREG(fs1, fs2, MIPSInst_FS(ir));
-+				rv.s  = (*handler.u) (fs1);
-+				ieee754_csr_save = ieee754_csr;
-+				rv.s2 = (*handler.u) (fs2);
-+				ieee754_csr.cx |= ieee754_csr_save.cx;
-+				ieee754_csr.sx |= ieee754_csr_save.sx;
-+				goto copcsr;
-+			}
-+			break;
-+
-+		default:
-+			if (MIPSInst_FUNC(ir) >= fcmp_op) {
-+				/* Loongson fp hardware handles all
-+				   cases of fp compare insns, so we
-+				   shouldn't have to */
-+				printk ("Loongson paired-single fp compare"
-+					" unimplemented in cp1emu.c\n");
-+			}
-+			return SIGILL;
-+		}
-+		break;
-+	}
-+#endif
-+
- 	case w_fmt:{
- 		ieee754sp fs;
- 
-@@ -2025,6 +2310,11 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
- 		DITOREG(rv.l, MIPSInst_FD(ir));
- 		break;
- #endif
-+#ifdef __loongson_fp
-+	case ps_fmt:
-+		PSPTOREG(rv.s, rv.s2, MIPSInst_FD(ir));
-+		break;
-+#endif
- 	default:
- 		return SIGILL;
- 	}
-diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c
-index 44b6dff..e782fae 100644
---- a/arch/mips/mm/dma-default.c
-+++ b/arch/mips/mm/dma-default.c
-@@ -336,7 +336,7 @@ int mips_dma_supported(struct device *dev, u64 mask)
- 	return plat_dma_supported(dev, mask);
- }
- 
--void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
-+void mips_dma_cache_sync(struct device *dev, void *vaddr, size_t size,
- 			 enum dma_data_direction direction)
- {
- 	BUG_ON(direction == DMA_NONE);
-@@ -345,8 +345,6 @@ void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
- 		__dma_sync_virtual(vaddr, size, direction);
- }
- 
--EXPORT_SYMBOL(dma_cache_sync);
--
- static struct dma_map_ops mips_default_dma_map_ops = {
- 	.alloc = mips_dma_alloc_coherent,
- 	.free = mips_dma_free_coherent,
-diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile
-index 137f2a6..b9845dc 100644
---- a/arch/mips/pci/Makefile
-+++ b/arch/mips/pci/Makefile
-@@ -29,6 +29,7 @@ obj-$(CONFIG_LASAT)		+= pci-lasat.o
- obj-$(CONFIG_MIPS_COBALT)	+= fixup-cobalt.o
- obj-$(CONFIG_LEMOTE_FULOONG2E)	+= fixup-fuloong2e.o ops-loongson2.o
- obj-$(CONFIG_LEMOTE_MACH2F)	+= fixup-lemote2f.o ops-loongson2.o
-+obj-$(CONFIG_DEXXON_GDIUM)      += fixup-gdium.o ops-loongson2.o
- obj-$(CONFIG_MIPS_MALTA)	+= fixup-malta.o pci-malta.o
- obj-$(CONFIG_PMC_MSP7120_GW)	+= fixup-pmcmsp.o ops-pmcmsp.o
- obj-$(CONFIG_PMC_MSP7120_EVAL)	+= fixup-pmcmsp.o ops-pmcmsp.o
-diff --git a/arch/mips/pci/fixup-gdium.c b/arch/mips/pci/fixup-gdium.c
-new file mode 100644
-index 0000000..b296220
---- /dev/null
-+++ b/arch/mips/pci/fixup-gdium.c
-@@ -0,0 +1,90 @@
-+/*
-+ * Copyright (C) 2010 yajin <yajin@vm-kernel.org>
-+ *
-+ *  This program is free software; you can redistribute  it and/or modify it
-+ *  under  the terms of  the GNU General  Public License as published by the
-+ *  Free Software Foundation;  either version 2 of the  License, or (at your
-+ *  option) any later version.
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/pci.h>
-+
-+#include <loongson.h>
-+/*
-+ * http://www.pcidatabase.com
-+ * GDIUM has different PCI mapping
-+ *  slot 13 (0x1814/0x0301) -> RaLink rt2561 Wireless-G PCI
-+ *  slog 14 (0x126f/0x0501) -> sm501
-+ *  slot 15 (0x1033/0x0035) -> NEC Dual OHCI controllers
-+ *                             plus Single EHCI controller
-+ *  slot 16 (0x10ec/0x8139) -> Realtek 8139c
-+ *  slot 17 (0x1033/0x00e0) -> NEC USB 2.0 Host Controller
-+ */
-+
-+#undef INT_IRQA
-+#undef INT_IRQB
-+#undef INT_IRQC
-+#undef INT_IRQD
-+#define INT_IRQA 36
-+#define INT_IRQB 37
-+#define INT_IRQC 38
-+#define INT_IRQD 39
-+
-+int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
-+{
-+	int irq = 0;
-+
-+	switch (slot) {
-+	case 13:
-+		irq = INT_IRQC + ((pin - 1) & 3);
-+		break;
-+	case 14:
-+		irq = INT_IRQA;
-+		break;
-+	case 15:
-+#if CONFIG_GDIUM_VERSION > 2
-+		irq = INT_IRQB;
-+#else
-+		irq = INT_IRQA + ((pin - 1) & 3);
-+#endif
-+		break;
-+	case 16:
-+		irq = INT_IRQD;
-+		break;
-+#if CONFIG_GDIUM_VERSION > 2
-+	case 17:
-+		irq = INT_IRQC;
-+		break;
-+#endif
-+	default:
-+		pr_info(" strange pci slot number %d on gdium.\n", slot);
-+		break;
-+	}
-+	return irq;
-+}
-+
-+/* Do platform specific device initialization at pci_enable_device() time */
-+int pcibios_plat_dev_init(struct pci_dev *dev)
-+{
-+	return 0;
-+}
-+
-+/* Fixups for the USB host controllers */
-+static void __init gdium_usb_host_fixup(struct pci_dev *dev)
-+{
-+	unsigned int val;
-+	pci_read_config_dword(dev, 0xe0, &val);
-+#if CONFIG_GDIUM_VERSION > 2
-+	pci_write_config_dword(dev, 0xe0, (val & ~3) | 0x3);
-+#else
-+	pci_write_config_dword(dev, 0xe0, (val & ~7) | 0x5);
-+	pci_write_config_dword(dev, 0xe4, 1<<5);
-+#endif
-+}
-+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB,
-+				gdium_usb_host_fixup);
-+#if CONFIG_GDIUM_VERSION > 2
-+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_CT_65550,
-+				gdium_usb_host_fixup);
-+#endif
-diff --git a/arch/mips/power/hibernate.S b/arch/mips/power/hibernate.S
-index 32a7c82..7e0277a 100644
---- a/arch/mips/power/hibernate.S
-+++ b/arch/mips/power/hibernate.S
-@@ -43,7 +43,6 @@ LEAF(swsusp_arch_resume)
- 	bne t1, t3, 1b
- 	PTR_L t0, PBE_NEXT(t0)
- 	bnez t0, 0b
--	jal local_flush_tlb_all /* Avoid TLB mismatch after kernel resume */
- 	PTR_LA t0, saved_regs
- 	PTR_L ra, PT_R31(t0)
- 	PTR_L sp, PT_R29(t0)
-diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c
-index 0448860..fa7cfab 100644
---- a/drivers/ata/pata_cs5536.c
-+++ b/drivers/ata/pata_cs5536.c
-@@ -46,8 +46,6 @@ static int use_msr;
- module_param_named(msr, use_msr, int, 0644);
- MODULE_PARM_DESC(msr, "Force using MSR to configure IDE function (Default: 0)");
- #else
--#undef rdmsr	/* avoid accidental MSR usage on, e.g. x86-64 */
--#undef wrmsr
- #define rdmsr(x, y, z) do { } while (0)
- #define wrmsr(x, y, z) do { } while (0)
- #define use_msr 0
-diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
-index f722001..5af02de 100644
---- a/drivers/hid/Kconfig
-+++ b/drivers/hid/Kconfig
-@@ -786,6 +786,13 @@ config HID_ZYDACRON
- 	---help---
- 	Support for Zydacron remote control.
- 
-+config HID_GDIUM
-+	bool "Gdium Fn keys support" if EMBEDDED
-+	depends on USB_HID && DEXXON_GDIUM
-+	default !EMBEDDED
-+	---help---
-+	Support for Functions keys available on Gdiums.
-+
- config HID_SENSOR_HUB
- 	tristate "HID Sensors framework support"
- 	depends on HID
-diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
-index 30e4431..e41ca68 100644
---- a/drivers/hid/Makefile
-+++ b/drivers/hid/Makefile
-@@ -115,6 +115,7 @@ obj-$(CONFIG_HID_ZEROPLUS)	+= hid-zpff.o
- obj-$(CONFIG_HID_ZYDACRON)	+= hid-zydacron.o
- obj-$(CONFIG_HID_WACOM)		+= hid-wacom.o
- obj-$(CONFIG_HID_WALTOP)	+= hid-waltop.o
-+obj-$(CONFIG_HID_GDIUM)		+= hid-gdium.o
- obj-$(CONFIG_HID_WIIMOTE)	+= hid-wiimote.o
- obj-$(CONFIG_HID_SENSOR_HUB)	+= hid-sensor-hub.o
- 
-diff --git a/drivers/hid/hid-gdium.c b/drivers/hid/hid-gdium.c
-new file mode 100644
-index 0000000..67cc095
---- /dev/null
-+++ b/drivers/hid/hid-gdium.c
-@@ -0,0 +1,210 @@
-+/*
-+ * hid-gdium  --  Gdium laptop function keys
-+ *
-+ * Arnaud Patard <apatard@mandriva.com>
-+ *
-+ * Based on hid-apple.c
-+ *
-+ *  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/device.h>
-+#include <linux/hid.h>
-+#include <linux/module.h>
-+#include <linux/usb.h>
-+
-+#include "hid-ids.h"
-+
-+#define GDIUM_FN_ON	1
-+
-+static int fnmode = GDIUM_FN_ON;
-+module_param(fnmode, int, 0644);
-+MODULE_PARM_DESC(fnmode, "Mode of fn key on Gdium (0 = disabled, 1 = Enabled)");
-+
-+struct gdium_data {
-+	unsigned int fn_on;
-+};
-+
-+
-+struct gdium_key_translation {
-+	u16 from;
-+	u16 to;
-+};
-+
-+static struct gdium_key_translation gdium_fn_keys[] = {
-+	{ KEY_F1,	KEY_CAMERA },
-+	{ KEY_F2,	KEY_CONNECT },
-+	{ KEY_F3,	KEY_MUTE },
-+	{ KEY_F4,	KEY_VOLUMEUP},
-+	{ KEY_F5,	KEY_VOLUMEDOWN },
-+	{ KEY_F6,	KEY_SWITCHVIDEOMODE },
-+	{ KEY_F7,	KEY_F19 }, /* F7+12. Have to use existant keycodes */
-+	{ KEY_F8,	KEY_BRIGHTNESSUP },
-+	{ KEY_F9,	KEY_BRIGHTNESSDOWN },
-+	{ KEY_F10,	KEY_SLEEP },
-+	{ KEY_F11,	KEY_PROG1 },
-+	{ KEY_F12,	KEY_PROG2 },
-+	{ KEY_UP,	KEY_PAGEUP },
-+	{ KEY_DOWN,	KEY_PAGEDOWN },
-+	{ KEY_INSERT,	KEY_NUMLOCK },
-+	{ KEY_DELETE,	KEY_SCROLLLOCK },
-+	{ KEY_T,	KEY_STOPCD },
-+	{ KEY_F,	KEY_PREVIOUSSONG },
-+	{ KEY_H,	KEY_NEXTSONG },
-+	{ KEY_G,        KEY_PLAYPAUSE },
-+	{ }
-+};
-+
-+static struct gdium_key_translation *gdium_find_translation(
-+		struct gdium_key_translation *table, u16 from)
-+{
-+	struct gdium_key_translation *trans;
-+
-+	/* Look for the translation */
-+	for (trans = table; trans->from; trans++)
-+		if (trans->from == from)
-+			return trans;
-+	return NULL;
-+}
-+
-+static int hidinput_gdium_event(struct hid_device *hid, struct input_dev *input,
-+		struct hid_usage *usage, __s32 value)
-+{
-+	struct gdium_data *data = hid_get_drvdata(hid);
-+	struct gdium_key_translation *trans;
-+	int do_translate;
-+
-+	if (usage->type != EV_KEY)
-+		return 0;
-+
-+	if ((usage->code == KEY_FN)) {
-+		data->fn_on = !!value;
-+		input_event(input, usage->type, usage->code, value);
-+		return 1;
-+	}
-+
-+	if (fnmode) {
-+		trans = gdium_find_translation(gdium_fn_keys, usage->code);
-+		if (trans) {
-+			do_translate = data->fn_on;
-+			if (do_translate) {
-+				input_event(input, usage->type, trans->to, value);
-+				return 1;
-+			}
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+static int gdium_input_event(struct hid_device *hdev, struct hid_field *field,
-+			struct hid_usage *usage, __s32 value)
-+{
-+	if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || !usage->type)
-+		return 0;
-+
-+	if (hidinput_gdium_event(hdev, field->hidinput->input, usage, value))
-+		return 1;
-+
-+	return 0;
-+}
-+
-+
-+static void gdium_input_setup(struct input_dev *input)
-+{
-+	struct gdium_key_translation *trans;
-+
-+	set_bit(KEY_NUMLOCK, input->keybit);
-+
-+	/* Enable all needed keys */
-+	for (trans = gdium_fn_keys; trans->from; trans++)
-+		set_bit(trans->to, input->keybit);
-+}
-+
-+static int gdium_input_mapping(struct hid_device *hdev, struct hid_input *hi,
-+		struct hid_field *field, struct hid_usage *usage,
-+		unsigned long **bit, int *max)
-+{
-+	if (((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD)
-+			&& ((usage->hid & HID_USAGE) == 0x82)) {
-+		hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_FN);
-+		gdium_input_setup(hi->input);
-+		return 1;
-+	}
-+	return 0;
-+}
-+
-+static int gdium_input_probe(struct hid_device *hdev, const struct hid_device_id *id)
-+{
-+	struct gdium_data *data;
-+	int ret;
-+
-+	data = kzalloc(sizeof(*data), GFP_KERNEL);
-+	if (!data) {
-+		dev_err(&hdev->dev, "can't alloc gdium keyboard data\n");
-+		return -ENOMEM;
-+	}
-+
-+	hid_set_drvdata(hdev, data);
-+
-+	ret = hid_parse(hdev);
-+	if (ret) {
-+		dev_err(&hdev->dev, "parse failed\n");
-+		goto err_free;
-+	}
-+
-+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
-+	if (ret) {
-+		dev_err(&hdev->dev, "hw start failed\n");
-+		goto err_free;
-+	}
-+
-+	return 0;
-+err_free:
-+	kfree(data);
-+	return ret;
-+}
-+static void gdium_input_remove(struct hid_device *hdev)
-+{
-+	hid_hw_stop(hdev);
-+	kfree(hid_get_drvdata(hdev));
-+}
-+
-+static const struct hid_device_id gdium_input_devices[] = {
-+	{ HID_USB_DEVICE(USB_VENDOR_ID_GDIUM, USB_DEVICE_ID_GDIUM) },
-+	{}
-+};
-+MODULE_DEVICE_TABLE(hid, gdium_input_devices);
-+
-+static struct hid_driver gdium_input_driver = {
-+	.name = "gdium-fnkeys",
-+	.id_table = gdium_input_devices,
-+	.probe = gdium_input_probe,
-+	.remove = gdium_input_remove,
-+	.event = gdium_input_event,
-+	.input_mapping = gdium_input_mapping,
-+};
-+
-+static int gdium_input_init(void)
-+{
-+	int ret;
-+
-+	ret = hid_register_driver(&gdium_input_driver);
-+	if (ret)
-+		 pr_err("can't register gdium keyboard driver\n");
-+
-+	return ret;
-+}
-+static void gdium_input_exit(void)
-+{
-+	hid_unregister_driver(&gdium_input_driver);
-+}
-+
-+module_init(gdium_input_init);
-+module_exit(gdium_input_exit);
-+MODULE_LICENSE("GPL");
-+
-diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
-index 6e12cd0..1ce18ed 100644
---- a/drivers/hid/hid-ids.h
-+++ b/drivers/hid/hid-ids.h
-@@ -956,6 +956,9 @@
- #define USB_VENDOR_ID_ZYTRONIC		0x14c8
- #define USB_DEVICE_ID_ZYTRONIC_ZXY100	0x0005
- 
-+#define USB_VENDOR_ID_GDIUM		0x04B4
-+#define USB_DEVICE_ID_GDIUM		0xe001
-+
- #define USB_VENDOR_ID_PRIMAX	0x0461
- #define USB_DEVICE_ID_PRIMAX_KEYBOARD	0x4e05
- 
-diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
-index c5eec02..9e4eb1d 100644
---- a/drivers/i2c/busses/Kconfig
-+++ b/drivers/i2c/busses/Kconfig
-@@ -1002,7 +1002,7 @@ config SCx200_I2C_SDA
- 
- config SCx200_ACB
- 	tristate "Geode ACCESS.bus support"
--	depends on X86_32 && PCI
-+	depends on PCI
- 	help
- 	  Enable the use of the ACCESS.bus controllers on the Geode SCx200 and
- 	  SC1100 processors and the CS5535 and CS5536 Geode companion devices.
-diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
-index 376f2dc..b576801 100644
---- a/drivers/ide/ide-iops.c
-+++ b/drivers/ide/ide-iops.c
-@@ -27,6 +27,10 @@
- #include <asm/uaccess.h>
- #include <asm/io.h>
- 
-+#ifdef CONFIG_LEMOTE_MACH2F
-+#include <asm/bootinfo.h>
-+#endif
-+
- void SELECT_MASK(ide_drive_t *drive, int mask)
- {
- 	const struct ide_port_ops *port_ops = drive->hwif->port_ops;
-@@ -300,6 +304,11 @@ void ide_check_nien_quirk_list(ide_drive_t *drive)
- {
- 	const char **list, *m = (char *)&drive->id[ATA_ID_PROD];
- 
-+#ifdef CONFIG_LEMOTE_MACH2F
-+	if (mips_machtype != MACH_LEMOTE_YL2F89)
-+		return;
-+#endif
-+
- 	for (list = nien_quirk_list; *list != NULL; list++)
- 		if (strstr(m, *list) != NULL) {
- 			drive->dev_flags |= IDE_DFLAG_NIEN_QUIRK;
-diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
-index e7dc441..124e8c3 100644
---- a/drivers/mfd/sm501.c
-+++ b/drivers/mfd/sm501.c
-@@ -58,7 +58,7 @@ struct sm501_gpio {
- struct sm501_gpio {
- 	/* no gpio support, empty definition for sm501_devdata. */
- };
--#endif
-+#endif	/* CONFIG_MFD_SM501_GPIO */
- 
- struct sm501_devdata {
- 	spinlock_t			 reg_lock;
-@@ -1135,6 +1135,22 @@ static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
- {
- 	return sm->gpio.registered;
- }
-+
-+void sm501_configure_gpio(struct device *dev, unsigned int gpio, unsigned
-+		char mode)
-+{
-+	unsigned long set, reg, offset = gpio;
-+
-+	if (offset >= 32) {
-+		reg = SM501_GPIO63_32_CONTROL;
-+		offset = gpio - 32;
-+	} else
-+		reg = SM501_GPIO31_0_CONTROL;
-+
-+	set = mode ? 1 << offset : 0;
-+
-+	sm501_modify_reg(dev, reg, set, 0);
-+}
- #else
- static inline int sm501_register_gpio(struct sm501_devdata *sm)
- {
-@@ -1154,7 +1170,13 @@ static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
- {
- 	return 0;
- }
--#endif
-+
-+void sm501_configure_gpio(struct device *dev, unsigned int gpio,
-+			 unsigned char mode)
-+{
-+}
-+#endif	/* CONFIG_MFD_SM501_GPIO */
-+EXPORT_SYMBOL_GPL(sm501_configure_gpio);
- 
- static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm,
- 					    struct sm501_platdata_gpio_i2c *iic)
-@@ -1209,6 +1231,20 @@ static int sm501_register_gpio_i2c(struct sm501_devdata *sm,
- 	return 0;
- }
- 
-+/* register sm501 PWM device */
-+static int sm501_register_pwm(struct sm501_devdata *sm)
-+{
-+	struct platform_device *pdev;
-+
-+	pdev = sm501_create_subdev(sm, "sm501-pwm", 2, 0);
-+	if (!pdev)
-+		return -ENOMEM;
-+	sm501_create_subio(sm, &pdev->resource[0], 0x10020, 0xC);
-+	sm501_create_irq(sm, &pdev->resource[1]);
-+
-+	return sm501_register_device(sm, pdev);
-+}
-+
- /* sm501_dbg_regs
-  *
-  * Debug attribute to attach to parent device to show core registers
-@@ -1367,6 +1403,8 @@ static int sm501_init_dev(struct sm501_devdata *sm)
- 			sm501_register_uart(sm, idata->devices);
- 		if (idata->devices & SM501_USE_GPIO)
- 			sm501_register_gpio(sm);
-+		if (idata->devices & SM501_USE_PWM)
-+			sm501_register_pwm(sm);
- 	}
- 
- 	if (pdata && pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) {
-@@ -1553,10 +1591,15 @@ static struct sm501_initdata sm501_pci_initdata = {
- 	.devices	= SM501_USE_ALL,
- 
- 	/* Errata AB-3 says that 72MHz is the fastest available
--	 * for 33MHZ PCI with proper bus-mastering operation */
--
-+	 * for 33MHZ PCI with proper bus-mastering operation
-+	 * For gdium, it works under 84&112M clock freq.*/
-+#ifdef CONFIG_DEXXON_GDIUM
-+	.mclk		= 84 * MHZ,
-+	.m1xclk		= 112 * MHZ,
-+#else
- 	.mclk		= 72 * MHZ,
- 	.m1xclk		= 144 * MHZ,
-+#endif
- };
- 
- static struct sm501_platdata_fbsub sm501_pdata_fbsub = {
-diff --git a/drivers/net/titan_ge.c b/drivers/net/titan_ge.c
-new file mode 100644
-index 0000000..dc137bf8
---- /dev/null
-+++ b/drivers/net/titan_ge.c
-@@ -0,0 +1,2069 @@
-+/*
-+ * drivers/net/titan_ge.c - Driver for Titan ethernet ports
-+ *
-+ * Copyright (C) 2003 PMC-Sierra Inc.
-+ * Author : Manish Lachwani (lachwani@pmc-sierra.com)
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2
-+ * of the License, or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-+ */
-+
-+/*
-+ * The MAC unit of the Titan consists of the following:
-+ *
-+ * -> XDMA Engine to move data to from the memory to the MAC packet FIFO
-+ * -> FIFO is where the incoming and outgoing data is placed
-+ * -> TRTG is the unit that pulls the data from the FIFO for Tx and pushes
-+ *    the data into the FIFO for Rx
-+ * -> TMAC is the outgoing MAC interface and RMAC is the incoming.
-+ * -> AFX is the address filtering block
-+ * -> GMII block to communicate with the PHY
-+ *
-+ * Rx will look like the following:
-+ * GMII --> RMAC --> AFX --> TRTG --> Rx FIFO --> XDMA --> CPU memory
-+ *
-+ * Tx will look like the following:
-+ * CPU memory --> XDMA --> Tx FIFO --> TRTG --> TMAC --> GMII
-+ *
-+ * The Titan driver has support for the following performance features:
-+ * -> Rx side checksumming
-+ * -> Jumbo Frames
-+ * -> Interrupt Coalscing
-+ * -> Rx NAPI
-+ * -> SKB Recycling
-+ * -> Transmit/Receive descriptors in SRAM
-+ * -> Fast routing for IP forwarding
-+ */
-+
-+#include <linux/dma-mapping.h>
-+#include <linux/module.h>
-+#include <linux/kernel.h>
-+#include <linux/sched.h>
-+#include <linux/ioport.h>
-+#include <linux/interrupt.h>
-+#include <linux/slab.h>
-+#include <linux/string.h>
-+#include <linux/errno.h>
-+#include <linux/ip.h>
-+#include <linux/init.h>
-+#include <linux/in.h>
-+#include <linux/platform_device.h>
-+#include <linux/netdevice.h>
-+#include <linux/etherdevice.h>
-+#include <linux/skbuff.h>
-+#include <linux/mii.h>
-+#include <linux/delay.h>
-+#include <linux/skbuff.h>
-+#include <linux/prefetch.h>
-+
-+/* For MII specifc registers, titan_mdio.h should be included */
-+#include <net/ip.h>
-+
-+#include <asm/bitops.h>
-+#include <asm/io.h>
-+#include <asm/types.h>
-+#include <asm/pgtable.h>
-+#include <asm/system.h>
-+#include <asm/titan_dep.h>
-+
-+#include "titan_ge.h"
-+#include "titan_mdio.h"
-+
-+/* Static Function Declarations	 */
-+static int titan_ge_eth_open(struct net_device *);
-+static void titan_ge_eth_stop(struct net_device *);
-+static struct net_device_stats *titan_ge_get_stats(struct net_device *);
-+static int titan_ge_init_rx_desc_ring(titan_ge_port_info *, int, int,
-+				      unsigned long, unsigned long,
-+				      unsigned long);
-+static int titan_ge_init_tx_desc_ring(titan_ge_port_info *, int,
-+				      unsigned long, unsigned long);
-+
-+static int titan_ge_open(struct net_device *);
-+static int titan_ge_start_xmit(struct sk_buff *, struct net_device *);
-+static int titan_ge_stop(struct net_device *);
-+
-+static unsigned long titan_ge_tx_coal(unsigned long, int);
-+
-+static void titan_ge_port_reset(unsigned int);
-+static int titan_ge_free_tx_queue(titan_ge_port_info *);
-+static int titan_ge_rx_task(struct net_device *, titan_ge_port_info *);
-+static int titan_ge_port_start(struct net_device *, titan_ge_port_info *);
-+
-+static int titan_ge_return_tx_desc(titan_ge_port_info *, int);
-+
-+/*
-+ * Some configuration for the FIFO and the XDMA channel needs
-+ * to be done only once for all the ports. This flag controls
-+ * that
-+ */
-+static unsigned long config_done;
-+
-+/*
-+ * One time out of memory flag
-+ */
-+static unsigned int oom_flag;
-+
-+static int titan_ge_poll(struct net_device *netdev, int *budget);
-+
-+static int titan_ge_receive_queue(struct net_device *, unsigned int);
-+
-+static struct platform_device *titan_ge_device[3];
-+
-+/* MAC Address */
-+extern unsigned char titan_ge_mac_addr_base[6];
-+
-+unsigned long titan_ge_base;
-+static unsigned long titan_ge_sram;
-+
-+static char titan_string[] = "titan";
-+
-+/*
-+ * The Titan GE has two alignment requirements:
-+ * -> skb->data to be cacheline aligned (32 byte)
-+ * -> IP header alignment to 16 bytes
-+ *
-+ * The latter is not implemented. So, that results in an extra copy on
-+ * the Rx. This is a big performance hog. For the former case, the
-+ * dev_alloc_skb() has been replaced with titan_ge_alloc_skb(). The size
-+ * requested is calculated:
-+ *
-+ * Ethernet Frame Size : 1518
-+ * Ethernet Header     : 14
-+ * Future Titan change for IP header alignment : 2
-+ *
-+ * Hence, we allocate (1518 + 14 + 2+ 64) = 1580 bytes.  For IP header
-+ * alignment, we use skb_reserve().
-+ */
-+
-+#define ALIGNED_RX_SKB_ADDR(addr) \
-+	((((unsigned long)(addr) + (64UL - 1UL)) \
-+	& ~(64UL - 1UL)) - (unsigned long)(addr))
-+
-+#define titan_ge_alloc_skb(__length, __gfp_flags) \
-+({      struct sk_buff *__skb; \
-+	__skb = alloc_skb((__length) + 64, (__gfp_flags)); \
-+	if(__skb) { \
-+		int __offset = (int) ALIGNED_RX_SKB_ADDR(__skb->data); \
-+		if(__offset) \
-+			skb_reserve(__skb, __offset); \
-+	} \
-+	__skb; \
-+})
-+
-+/*
-+ * Configure the GMII block of the Titan based on what the PHY tells us
-+ */
-+static void titan_ge_gmii_config(int port_num)
-+{
-+	unsigned int reg_data = 0, phy_reg;
-+	int err;
-+
-+	err = titan_ge_mdio_read(port_num, TITAN_GE_MDIO_PHY_STATUS, &phy_reg);
-+
-+	if (err == TITAN_GE_MDIO_ERROR) {
-+		printk(KERN_ERR
-+		       "Could not read PHY control register 0x11 \n");
-+		printk(KERN_ERR
-+			"Setting speed to 1000 Mbps and Duplex to Full \n");
-+
-+		return;
-+	}
-+
-+	err = titan_ge_mdio_write(port_num, TITAN_GE_MDIO_PHY_IE, 0);
-+
-+	if (phy_reg & 0x8000) {
-+		if (phy_reg & 0x2000) {
-+			/* Full Duplex and 1000 Mbps */
-+			TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_MODE +
-+					(port_num << 12)), 0x201);
-+		}  else {
-+			/* Half Duplex and 1000 Mbps */
-+			TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_MODE +
-+					(port_num << 12)), 0x2201);
-+			}
-+	}
-+	if (phy_reg & 0x4000) {
-+		if (phy_reg & 0x2000) {
-+			/* Full Duplex and 100 Mbps */
-+			TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_MODE +
-+					(port_num << 12)), 0x100);
-+		} else {
-+			/* Half Duplex and 100 Mbps */
-+			TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_MODE +
-+					(port_num << 12)), 0x2100);
-+		}
-+	}
-+	reg_data = TITAN_GE_READ(TITAN_GE_GMII_CONFIG_GENERAL +
-+				(port_num << 12));
-+	reg_data |= 0x3;
-+	TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_GENERAL +
-+			(port_num << 12)), reg_data);
-+}
-+
-+/*
-+ * Enable the TMAC if it is not
-+ */
-+static void titan_ge_enable_tx(unsigned int port_num)
-+{
-+	unsigned long reg_data;
-+
-+	reg_data = TITAN_GE_READ(TITAN_GE_TMAC_CONFIG_1 + (port_num << 12));
-+	if (!(reg_data & 0x8000)) {
-+		printk("TMAC disabled for port %d!! \n", port_num);
-+
-+		reg_data |= 0x0001;	/* Enable TMAC */
-+		reg_data |= 0x4000;	/* CRC Check Enable */
-+		reg_data |= 0x2000;	/* Padding enable */
-+		reg_data |= 0x0800;	/* CRC Add enable */
-+		reg_data |= 0x0080;	/* PAUSE frame */
-+
-+		TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_1 +
-+				(port_num << 12)), reg_data);
-+	}
-+}
-+
-+/*
-+ * Tx Timeout function
-+ */
-+static void titan_ge_tx_timeout(struct net_device *netdev)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+
-+	printk(KERN_INFO "%s: TX timeout  ", netdev->name);
-+	printk(KERN_INFO "Resetting card \n");
-+
-+	/* Do the reset outside of interrupt context */
-+	schedule_work(&titan_ge_eth->tx_timeout_task);
-+}
-+
-+/*
-+ * Update the AFX tables for UC and MC for slice 0 only
-+ */
-+static void titan_ge_update_afx(titan_ge_port_info * titan_ge_eth)
-+{
-+	int port = titan_ge_eth->port_num;
-+	unsigned int i;
-+	volatile unsigned long reg_data = 0;
-+	u8 p_addr[6];
-+
-+	memcpy(p_addr, titan_ge_eth->port_mac_addr, 6);
-+
-+	/* Set the MAC address here for TMAC and RMAC */
-+	TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_HI + (port << 12)),
-+		       ((p_addr[5] << 8) | p_addr[4]));
-+	TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_MID + (port << 12)),
-+		       ((p_addr[3] << 8) | p_addr[2]));
-+	TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_LOW + (port << 12)),
-+		       ((p_addr[1] << 8) | p_addr[0]));
-+
-+	TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_HI + (port << 12)),
-+		       ((p_addr[5] << 8) | p_addr[4]));
-+	TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_MID + (port << 12)),
-+		       ((p_addr[3] << 8) | p_addr[2]));
-+	TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_LOW + (port << 12)),
-+		       ((p_addr[1] << 8) | p_addr[0]));
-+
-+	TITAN_GE_WRITE((0x112c | (port << 12)), 0x1);
-+	/* Configure the eight address filters */
-+	for (i = 0; i < 8; i++) {
-+		/* Select each of the eight filters */
-+		TITAN_GE_WRITE((TITAN_GE_AFX_ADDRS_FILTER_CTRL_2 +
-+				(port << 12)), i);
-+
-+		/* Configure the match */
-+		reg_data = 0x9;	/* Forward Enable Bit */
-+		TITAN_GE_WRITE((TITAN_GE_AFX_ADDRS_FILTER_CTRL_0 +
-+				(port << 12)), reg_data);
-+
-+		/* Finally, AFX Exact Match Address Registers */
-+		TITAN_GE_WRITE((TITAN_GE_AFX_EXACT_MATCH_LOW + (port << 12)),
-+			       ((p_addr[1] << 8) | p_addr[0]));
-+		TITAN_GE_WRITE((TITAN_GE_AFX_EXACT_MATCH_MID + (port << 12)),
-+			       ((p_addr[3] << 8) | p_addr[2]));
-+		TITAN_GE_WRITE((TITAN_GE_AFX_EXACT_MATCH_HIGH + (port << 12)),
-+			       ((p_addr[5] << 8) | p_addr[4]));
-+
-+		/* VLAN id set to 0 */
-+		TITAN_GE_WRITE((TITAN_GE_AFX_EXACT_MATCH_VID +
-+				(port << 12)), 0);
-+	}
-+}
-+
-+/*
-+ * Actual Routine to reset the adapter when the timeout occurred
-+ */
-+static void titan_ge_tx_timeout_task(struct net_device *netdev)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+	int port = titan_ge_eth->port_num;
-+
-+	printk("Titan GE: Transmit timed out. Resetting ... \n");
-+
-+	/* Dump debug info */
-+	printk(KERN_ERR "TRTG cause : %x \n",
-+			TITAN_GE_READ(0x100c + (port << 12)));
-+
-+	/* Fix this for the other ports */
-+	printk(KERN_ERR "FIFO cause : %x \n", TITAN_GE_READ(0x482c));
-+	printk(KERN_ERR "IE cause : %x \n", TITAN_GE_READ(0x0040));
-+	printk(KERN_ERR "XDMA GDI ERROR : %x \n",
-+			TITAN_GE_READ(0x5008 + (port << 8)));
-+	printk(KERN_ERR "CHANNEL ERROR: %x \n",
-+			TITAN_GE_READ(TITAN_GE_CHANNEL0_INTERRUPT
-+						+ (port << 8)));
-+
-+	netif_device_detach(netdev);
-+	titan_ge_port_reset(titan_ge_eth->port_num);
-+	titan_ge_port_start(netdev, titan_ge_eth);
-+	netif_device_attach(netdev);
-+}
-+
-+/*
-+ * Change the MTU of the Ethernet Device
-+ */
-+static int titan_ge_change_mtu(struct net_device *netdev, int new_mtu)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+	unsigned long flags;
-+
-+	if ((new_mtu > 9500) || (new_mtu < 64))
-+		return -EINVAL;
-+
-+	spin_lock_irqsave(&titan_ge_eth->lock, flags);
-+
-+	netdev->mtu = new_mtu;
-+
-+	/* Now we have to reopen the interface so that SKBs with the new
-+	 * size will be allocated */
-+
-+	if (netif_running(netdev)) {
-+		titan_ge_eth_stop(netdev);
-+
-+		if (titan_ge_eth_open(netdev) != TITAN_OK) {
-+			printk(KERN_ERR
-+			       "%s: Fatal error on opening device\n",
-+			       netdev->name);
-+			spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
-+			return -1;
-+		}
-+	}
-+
-+	spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
-+	return 0;
-+}
-+
-+/*
-+ * Titan Gbe Interrupt Handler. All the three ports send interrupt to one line
-+ * only. Once an interrupt is triggered, figure out the port and then check
-+ * the channel.
-+ */
-+static irqreturn_t titan_ge_int_handler(int irq, void *dev_id)
-+{
-+	struct net_device *netdev = (struct net_device *) dev_id;
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+	unsigned int port_num = titan_ge_eth->port_num;
-+	unsigned int reg_data;
-+	unsigned int eth_int_cause_error = 0, is;
-+	unsigned long eth_int_cause1;
-+	int err = 0;
-+#ifdef CONFIG_SMP
-+	unsigned long eth_int_cause2;
-+#endif
-+
-+	/* Ack the CPU interrupt */
-+	switch (port_num) {
-+	case 0:
-+		is = OCD_READ(RM9000x2_OCD_INTP0STATUS1);
-+		OCD_WRITE(RM9000x2_OCD_INTP0CLEAR1, is);
-+
-+#ifdef CONFIG_SMP
-+		is = OCD_READ(RM9000x2_OCD_INTP1STATUS1);
-+		OCD_WRITE(RM9000x2_OCD_INTP1CLEAR1, is);
-+#endif
-+		break;
-+
-+	case 1:
-+		is = OCD_READ(RM9000x2_OCD_INTP0STATUS0);
-+		OCD_WRITE(RM9000x2_OCD_INTP0CLEAR0, is);
-+
-+#ifdef CONFIG_SMP
-+		is = OCD_READ(RM9000x2_OCD_INTP1STATUS0);
-+		OCD_WRITE(RM9000x2_OCD_INTP1CLEAR0, is);
-+#endif
-+		break;
-+
-+	case 2:
-+		is = OCD_READ(RM9000x2_OCD_INTP0STATUS4);
-+		OCD_WRITE(RM9000x2_OCD_INTP0CLEAR4, is);
-+
-+#ifdef CONFIG_SMP
-+		is = OCD_READ(RM9000x2_OCD_INTP1STATUS4);
-+		OCD_WRITE(RM9000x2_OCD_INTP1CLEAR4, is);
-+#endif
-+	}
-+
-+	eth_int_cause1 = TITAN_GE_READ(TITAN_GE_INTR_XDMA_CORE_A);
-+#ifdef CONFIG_SMP
-+	eth_int_cause2 = TITAN_GE_READ(TITAN_GE_INTR_XDMA_CORE_B);
-+#endif
-+
-+	/* Spurious interrupt */
-+#ifdef CONFIG_SMP
-+	if ( (eth_int_cause1 == 0) && (eth_int_cause2 == 0)) {
-+#else
-+	if (eth_int_cause1 == 0) {
-+#endif
-+		eth_int_cause_error = TITAN_GE_READ(TITAN_GE_CHANNEL0_INTERRUPT +
-+					(port_num << 8));
-+
-+		if (eth_int_cause_error == 0)
-+			return IRQ_NONE;
-+	}
-+
-+	/* Handle Tx first. No need to ack interrupts */
-+#ifdef CONFIG_SMP
-+	if ( (eth_int_cause1 & 0x20202) ||
-+		(eth_int_cause2 & 0x20202) )
-+#else
-+	if (eth_int_cause1 & 0x20202)
-+#endif
-+		titan_ge_free_tx_queue(titan_ge_eth);
-+
-+	/* Handle the Rx next */
-+#ifdef CONFIG_SMP
-+	if ( (eth_int_cause1 & 0x10101) ||
-+		(eth_int_cause2 & 0x10101)) {
-+#else
-+	if (eth_int_cause1 & 0x10101) {
-+#endif
-+		if (netif_rx_schedule_prep(netdev)) {
-+			unsigned int ack;
-+
-+			ack = TITAN_GE_READ(TITAN_GE_INTR_XDMA_IE);
-+			/* Disable Tx and Rx both */
-+			if (port_num == 0)
-+				ack &= ~(0x3);
-+			if (port_num == 1)
-+				ack &= ~(0x300);
-+
-+			if (port_num == 2)
-+				ack &= ~(0x30000);
-+
-+			/* Interrupts have been disabled */
-+			TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_IE, ack);
-+
-+			__netif_rx_schedule(netdev);
-+		}
-+	}
-+
-+	/* Handle error interrupts */
-+	if (eth_int_cause_error && (eth_int_cause_error != 0x2)) {
-+		printk(KERN_ERR
-+			"XDMA Channel Error : %x  on port %d\n",
-+			eth_int_cause_error, port_num);
-+
-+		printk(KERN_ERR
-+			"XDMA GDI Hardware error : %x  on port %d\n",
-+			TITAN_GE_READ(0x5008 + (port_num << 8)), port_num);
-+
-+		printk(KERN_ERR
-+			"XDMA currently has %d Rx descriptors \n",
-+			TITAN_GE_READ(0x5048 + (port_num << 8)));
-+
-+		printk(KERN_ERR
-+			"XDMA currently has prefetcted %d Rx descriptors \n",
-+			TITAN_GE_READ(0x505c + (port_num << 8)));
-+
-+		TITAN_GE_WRITE((TITAN_GE_CHANNEL0_INTERRUPT +
-+			       (port_num << 8)), eth_int_cause_error);
-+	}
-+
-+	/*
-+	 * PHY interrupt to inform abt the changes. Reading the
-+	 * PHY Status register will clear the interrupt
-+	 */
-+	if ((!(eth_int_cause1 & 0x30303)) &&
-+		(eth_int_cause_error == 0)) {
-+		err =
-+		    titan_ge_mdio_read(port_num,
-+			       TITAN_GE_MDIO_PHY_IS, &reg_data);
-+
-+		if (reg_data & 0x0400) {
-+			/* Link status change */
-+			titan_ge_mdio_read(port_num,
-+				   TITAN_GE_MDIO_PHY_STATUS, &reg_data);
-+			if (!(reg_data & 0x0400)) {
-+				/* Link is down */
-+				netif_carrier_off(netdev);
-+				netif_stop_queue(netdev);
-+			} else {
-+				/* Link is up */
-+				netif_carrier_on(netdev);
-+				netif_wake_queue(netdev);
-+
-+				/* Enable the queue */
-+				titan_ge_enable_tx(port_num);
-+			}
-+		}
-+	}
-+
-+	return IRQ_HANDLED;
-+}
-+
-+/*
-+ * Multicast and Promiscuous mode set. The
-+ * set_multi entry point is called whenever the
-+ * multicast address list or the network interface
-+ * flags are updated.
-+ */
-+static void titan_ge_set_multi(struct net_device *netdev)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+	unsigned int port_num = titan_ge_eth->port_num;
-+	unsigned long reg_data;
-+
-+	reg_data = TITAN_GE_READ(TITAN_GE_AFX_ADDRS_FILTER_CTRL_1 +
-+				(port_num << 12));
-+
-+	if (netdev->flags & IFF_PROMISC) {
-+		reg_data |= 0x2;
-+	}
-+	else if (netdev->flags & IFF_ALLMULTI) {
-+		reg_data |= 0x01;
-+		reg_data |= 0x400; /* Use the 64-bit Multicast Hash bin */
-+	}
-+	else {
-+		reg_data = 0x2;
-+	}
-+
-+	TITAN_GE_WRITE((TITAN_GE_AFX_ADDRS_FILTER_CTRL_1 +
-+			(port_num << 12)), reg_data);
-+	if (reg_data & 0x01) {
-+		TITAN_GE_WRITE((TITAN_GE_AFX_MULTICAST_HASH_LOW +
-+				(port_num << 12)), 0xffff);
-+		TITAN_GE_WRITE((TITAN_GE_AFX_MULTICAST_HASH_MIDLOW +
-+				(port_num << 12)), 0xffff);
-+		TITAN_GE_WRITE((TITAN_GE_AFX_MULTICAST_HASH_MIDHI +
-+				(port_num << 12)), 0xffff);
-+		TITAN_GE_WRITE((TITAN_GE_AFX_MULTICAST_HASH_HI +
-+				(port_num << 12)), 0xffff);
-+	}
-+}
-+
-+/*
-+ * Open the network device
-+ */
-+static int titan_ge_open(struct net_device *netdev)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+	unsigned int port_num = titan_ge_eth->port_num;
-+	unsigned int irq = TITAN_ETH_PORT_IRQ - port_num;
-+	int retval;
-+
-+	retval = request_irq(irq, titan_ge_int_handler,
-+		     SA_INTERRUPT | SA_SAMPLE_RANDOM , netdev->name, netdev);
-+
-+	if (retval != 0) {
-+		printk(KERN_ERR "Cannot assign IRQ number to TITAN GE \n");
-+		return -1;
-+	}
-+
-+	netdev->irq = irq;
-+	printk(KERN_INFO "Assigned IRQ %d to port %d\n", irq, port_num);
-+
-+	spin_lock_irq(&(titan_ge_eth->lock));
-+
-+	if (titan_ge_eth_open(netdev) != TITAN_OK) {
-+		spin_unlock_irq(&(titan_ge_eth->lock));
-+		printk("%s: Error opening interface \n", netdev->name);
-+		free_irq(netdev->irq, netdev);
-+		return -EBUSY;
-+	}
-+
-+	spin_unlock_irq(&(titan_ge_eth->lock));
-+
-+	return 0;
-+}
-+
-+/*
-+ * Allocate the SKBs for the Rx ring. Also used
-+ * for refilling the queue
-+ */
-+static int titan_ge_rx_task(struct net_device *netdev,
-+				titan_ge_port_info *titan_ge_port)
-+{
-+	struct device *device = &titan_ge_device[titan_ge_port->port_num]->dev;
-+	volatile titan_ge_rx_desc *rx_desc;
-+	struct sk_buff *skb;
-+	int rx_used_desc;
-+	int count = 0;
-+
-+	while (titan_ge_port->rx_ring_skbs < titan_ge_port->rx_ring_size) {
-+
-+	/* First try to get the skb from the recycler */
-+#ifdef TITAN_GE_JUMBO_FRAMES
-+		skb = titan_ge_alloc_skb(TITAN_GE_JUMBO_BUFSIZE, GFP_ATOMIC);
-+#else
-+		skb = titan_ge_alloc_skb(TITAN_GE_STD_BUFSIZE, GFP_ATOMIC);
-+#endif
-+		if (unlikely(!skb)) {
-+			/* OOM, set the flag */
-+			printk("OOM \n");
-+			oom_flag = 1;
-+			break;
-+		}
-+		count++;
-+		skb->dev = netdev;
-+
-+		titan_ge_port->rx_ring_skbs++;
-+
-+		rx_used_desc = titan_ge_port->rx_used_desc_q;
-+		rx_desc = &(titan_ge_port->rx_desc_area[rx_used_desc]);
-+
-+#ifdef TITAN_GE_JUMBO_FRAMES
-+		rx_desc->buffer_addr = dma_map_single(device, skb->data,
-+				TITAN_GE_JUMBO_BUFSIZE - 2, DMA_FROM_DEVICE);
-+#else
-+		rx_desc->buffer_addr = dma_map_single(device, skb->data,
-+				TITAN_GE_STD_BUFSIZE - 2, DMA_FROM_DEVICE);
-+#endif
-+
-+		titan_ge_port->rx_skb[rx_used_desc] = skb;
-+		rx_desc->cmd_sts = TITAN_GE_RX_BUFFER_OWNED;
-+
-+		titan_ge_port->rx_used_desc_q =
-+			(rx_used_desc + 1) % TITAN_GE_RX_QUEUE;
-+	}
-+
-+	return count;
-+}
-+
-+/*
-+ * Actual init of the Tital GE port. There is one register for
-+ * the channel configuration
-+ */
-+static void titan_port_init(struct net_device *netdev,
-+			    titan_ge_port_info * titan_ge_eth)
-+{
-+	unsigned long reg_data;
-+
-+	titan_ge_port_reset(titan_ge_eth->port_num);
-+
-+	/* First reset the TMAC */
-+	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG);
-+	reg_data |= 0x80000000;
-+	TITAN_GE_WRITE(TITAN_GE_CHANNEL0_CONFIG, reg_data);
-+
-+	udelay(30);
-+
-+	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG);
-+	reg_data &= ~(0xc0000000);
-+	TITAN_GE_WRITE(TITAN_GE_CHANNEL0_CONFIG, reg_data);
-+
-+	/* Now reset the RMAC */
-+	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG);
-+	reg_data |= 0x00080000;
-+	TITAN_GE_WRITE(TITAN_GE_CHANNEL0_CONFIG, reg_data);
-+
-+	udelay(30);
-+
-+	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG);
-+	reg_data &= ~(0x000c0000);
-+	TITAN_GE_WRITE(TITAN_GE_CHANNEL0_CONFIG, reg_data);
-+}
-+
-+/*
-+ * Start the port. All the hardware specific configuration
-+ * for the XDMA, Tx FIFO, Rx FIFO, TMAC, RMAC, TRTG and AFX
-+ * go here
-+ */
-+static int titan_ge_port_start(struct net_device *netdev,
-+				titan_ge_port_info * titan_port)
-+{
-+	volatile unsigned long reg_data, reg_data1;
-+	int port_num = titan_port->port_num;
-+	int count = 0;
-+	unsigned long reg_data_1;
-+
-+	if (config_done == 0) {
-+		reg_data = TITAN_GE_READ(0x0004);
-+		reg_data |= 0x100;
-+		TITAN_GE_WRITE(0x0004, reg_data);
-+
-+		reg_data &= ~(0x100);
-+		TITAN_GE_WRITE(0x0004, reg_data);
-+
-+		/* Turn on GMII/MII mode and turn off TBI mode */
-+		reg_data = TITAN_GE_READ(TITAN_GE_TSB_CTRL_1);
-+		reg_data |= 0x00000700;
-+		reg_data &= ~(0x00800000); /* Fencing */
-+
-+		TITAN_GE_WRITE(0x000c, 0x00001100);
-+
-+		TITAN_GE_WRITE(TITAN_GE_TSB_CTRL_1, reg_data);
-+
-+		/* Set the CPU Resource Limit register */
-+		TITAN_GE_WRITE(0x00f8, 0x8);
-+
-+		/* Be conservative when using the BIU buffers */
-+		TITAN_GE_WRITE(0x0068, 0x4);
-+	}
-+
-+	titan_port->tx_threshold = 0;
-+	titan_port->rx_threshold = 0;
-+
-+	/* We need to write the descriptors for Tx and Rx */
-+	TITAN_GE_WRITE((TITAN_GE_CHANNEL0_TX_DESC + (port_num << 8)),
-+		       (unsigned long) titan_port->tx_dma);
-+	TITAN_GE_WRITE((TITAN_GE_CHANNEL0_RX_DESC + (port_num << 8)),
-+		       (unsigned long) titan_port->rx_dma);
-+
-+	if (config_done == 0) {
-+		/* Step 1:  XDMA config	*/
-+		reg_data = TITAN_GE_READ(TITAN_GE_XDMA_CONFIG);
-+		reg_data &= ~(0x80000000);      /* clear reset */
-+		reg_data |= 0x1 << 29;	/* sparse tx descriptor spacing */
-+		reg_data |= 0x1 << 28;	/* sparse rx descriptor spacing */
-+		reg_data |= (0x1 << 23) | (0x1 << 24);  /* Descriptor Coherency */
-+		reg_data |= (0x1 << 21) | (0x1 << 22);  /* Data Coherency */
-+		TITAN_GE_WRITE(TITAN_GE_XDMA_CONFIG, reg_data);
-+	}
-+
-+	/* IR register for the XDMA */
-+	reg_data = TITAN_GE_READ(TITAN_GE_GDI_INTERRUPT_ENABLE + (port_num << 8));
-+	reg_data |= 0x80068000; /* No Rx_OOD */
-+	TITAN_GE_WRITE((TITAN_GE_GDI_INTERRUPT_ENABLE + (port_num << 8)), reg_data);
-+
-+	/* Start the Tx and Rx XDMA controller */
-+	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG + (port_num << 8));
-+	reg_data &= 0x4fffffff;     /* Clear tx reset */
-+	reg_data &= 0xfff4ffff;     /* Clear rx reset */
-+
-+#ifdef TITAN_GE_JUMBO_FRAMES
-+	reg_data |= 0xa0 | 0x30030000;
-+#else
-+	reg_data |= 0x40 | 0x20030000;
-+#endif
-+
-+#ifndef CONFIG_SMP
-+	reg_data &= ~(0x10);
-+	reg_data |= 0x0f; /* All of the packet */
-+#endif
-+
-+	TITAN_GE_WRITE((TITAN_GE_CHANNEL0_CONFIG + (port_num << 8)), reg_data);
-+
-+	/* Rx desc count */
-+	count = titan_ge_rx_task(netdev, titan_port);
-+	TITAN_GE_WRITE((0x5048 + (port_num << 8)), count);
-+	count = TITAN_GE_READ(0x5048 + (port_num << 8));
-+
-+	udelay(30);
-+
-+	/*
-+	 * Step 2:  Configure the SDQPF, i.e. FIFO
-+	 */
-+	if (config_done == 0) {
-+		reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_RXFIFO_CTL);
-+		reg_data = 0x1;
-+		TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_CTL, reg_data);
-+		reg_data &= ~(0x1);
-+		TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_CTL, reg_data);
-+		reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_RXFIFO_CTL);
-+		TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_CTL, reg_data);
-+
-+		reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_TXFIFO_CTL);
-+		reg_data = 0x1;
-+		TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_CTL, reg_data);
-+		reg_data &= ~(0x1);
-+		TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_CTL, reg_data);
-+		reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_TXFIFO_CTL);
-+		TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_CTL, reg_data);
-+	}
-+	/*
-+	 * Enable RX FIFO 0, 4 and 8
-+	 */
-+	if (port_num == 0) {
-+		reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_RXFIFO_0);
-+
-+		reg_data |= 0x100000;
-+		reg_data |= (0xff << 10);
-+
-+		TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_0, reg_data);
-+		/*
-+		 * BAV2,BAV and DAV settings for the Rx FIFO
-+		 */
-+		reg_data1 = TITAN_GE_READ(0x4844);
-+		reg_data1 |= ( (0x10 << 20) | (0x10 << 10) | 0x1);
-+		TITAN_GE_WRITE(0x4844, reg_data1);
-+
-+		reg_data &= ~(0x00100000);
-+		reg_data |= 0x200000;
-+
-+		TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_0, reg_data);
-+
-+		reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_TXFIFO_0);
-+		reg_data |= 0x100000;
-+
-+		TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_0, reg_data);
-+
-+		reg_data |= (0xff << 10);
-+
-+		TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_0, reg_data);
-+
-+		/*
-+		 * BAV2, BAV and DAV settings for the Tx FIFO
-+		 */
-+		reg_data1 = TITAN_GE_READ(0x4944);
-+		reg_data1 = ( (0x1 << 20) | (0x1 << 10) | 0x10);
-+
-+		TITAN_GE_WRITE(0x4944, reg_data1);
-+
-+		reg_data &= ~(0x00100000);
-+		reg_data |= 0x200000;
-+
-+		TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_0, reg_data);
-+
-+	}
-+
-+	if (port_num == 1) {
-+		reg_data = TITAN_GE_READ(0x4870);
-+
-+		reg_data |= 0x100000;
-+		reg_data |= (0xff << 10) | (0xff + 1);
-+
-+		TITAN_GE_WRITE(0x4870, reg_data);
-+		/*
-+		 * BAV2,BAV and DAV settings for the Rx FIFO
-+		 */
-+		reg_data1 = TITAN_GE_READ(0x4874);
-+		reg_data1 |= ( (0x10 << 20) | (0x10 << 10) | 0x1);
-+		TITAN_GE_WRITE(0x4874, reg_data1);
-+
-+		reg_data &= ~(0x00100000);
-+		reg_data |= 0x200000;
-+
-+		TITAN_GE_WRITE(0x4870, reg_data);
-+
-+		reg_data = TITAN_GE_READ(0x494c);
-+		reg_data |= 0x100000;
-+
-+		TITAN_GE_WRITE(0x494c, reg_data);
-+		reg_data |= (0xff << 10) | (0xff + 1);
-+		TITAN_GE_WRITE(0x494c, reg_data);
-+
-+		/*
-+		 * BAV2, BAV and DAV settings for the Tx FIFO
-+		 */
-+		reg_data1 = TITAN_GE_READ(0x4950);
-+		reg_data1 = ( (0x1 << 20) | (0x1 << 10) | 0x10);
-+
-+		TITAN_GE_WRITE(0x4950, reg_data1);
-+
-+		reg_data &= ~(0x00100000);
-+		reg_data |= 0x200000;
-+
-+		TITAN_GE_WRITE(0x494c, reg_data);
-+	}
-+
-+	/*
-+	 * Titan 1.2 revision does support port #2
-+	 */
-+	if (port_num == 2) {
-+		/*
-+		 * Put the descriptors in the SRAM
-+		 */
-+		reg_data = TITAN_GE_READ(0x48a0);
-+
-+		reg_data |= 0x100000;
-+		reg_data |= (0xff << 10) | (2*(0xff + 1));
-+
-+		TITAN_GE_WRITE(0x48a0, reg_data);
-+		/*
-+		 * BAV2,BAV and DAV settings for the Rx FIFO
-+		 */
-+		reg_data1 = TITAN_GE_READ(0x48a4);
-+		reg_data1 |= ( (0x10 << 20) | (0x10 << 10) | 0x1);
-+		TITAN_GE_WRITE(0x48a4, reg_data1);
-+
-+		reg_data &= ~(0x00100000);
-+		reg_data |= 0x200000;
-+
-+		TITAN_GE_WRITE(0x48a0, reg_data);
-+
-+		reg_data = TITAN_GE_READ(0x4958);
-+		reg_data |= 0x100000;
-+
-+		TITAN_GE_WRITE(0x4958, reg_data);
-+		reg_data |= (0xff << 10) | (2*(0xff + 1));
-+		TITAN_GE_WRITE(0x4958, reg_data);
-+
-+		/*
-+		 * BAV2, BAV and DAV settings for the Tx FIFO
-+		 */
-+		reg_data1 = TITAN_GE_READ(0x495c);
-+		reg_data1 = ( (0x1 << 20) | (0x1 << 10) | 0x10);
-+
-+		TITAN_GE_WRITE(0x495c, reg_data1);
-+
-+		reg_data &= ~(0x00100000);
-+		reg_data |= 0x200000;
-+
-+		TITAN_GE_WRITE(0x4958, reg_data);
-+	}
-+
-+	if (port_num == 2) {
-+		reg_data = TITAN_GE_READ(0x48a0);
-+
-+		reg_data |= 0x100000;
-+		reg_data |= (0xff << 10) | (2*(0xff + 1));
-+
-+		TITAN_GE_WRITE(0x48a0, reg_data);
-+		/*
-+		 * BAV2,BAV and DAV settings for the Rx FIFO
-+		 */
-+		reg_data1 = TITAN_GE_READ(0x48a4);
-+		reg_data1 |= ( (0x10 << 20) | (0x10 << 10) | 0x1);
-+		TITAN_GE_WRITE(0x48a4, reg_data1);
-+
-+		reg_data &= ~(0x00100000);
-+		reg_data |= 0x200000;
-+
-+		TITAN_GE_WRITE(0x48a0, reg_data);
-+
-+		reg_data = TITAN_GE_READ(0x4958);
-+		reg_data |= 0x100000;
-+
-+		TITAN_GE_WRITE(0x4958, reg_data);
-+		reg_data |= (0xff << 10) | (2*(0xff + 1));
-+		TITAN_GE_WRITE(0x4958, reg_data);
-+
-+		/*
-+		 * BAV2, BAV and DAV settings for the Tx FIFO
-+		 */
-+		reg_data1 = TITAN_GE_READ(0x495c);
-+		reg_data1 = ( (0x1 << 20) | (0x1 << 10) | 0x10);
-+
-+		TITAN_GE_WRITE(0x495c, reg_data1);
-+
-+		reg_data &= ~(0x00100000);
-+		reg_data |= 0x200000;
-+
-+		TITAN_GE_WRITE(0x4958, reg_data);
-+	}
-+
-+	/*
-+	 * Step 3:  TRTG block enable
-+	 */
-+	reg_data = TITAN_GE_READ(TITAN_GE_TRTG_CONFIG + (port_num << 12));
-+
-+	/*
-+	 * This is the 1.2 revision of the chip. It has fix for the
-+	 * IP header alignment. Now, the IP header begins at an
-+	 * aligned address and this wont need an extra copy in the
-+	 * driver. This performance drawback existed in the previous
-+	 * versions of the silicon
-+	 */
-+	reg_data_1 = TITAN_GE_READ(0x103c + (port_num << 12));
-+	reg_data_1 |= 0x40000000;
-+	TITAN_GE_WRITE((0x103c + (port_num << 12)), reg_data_1);
-+
-+	reg_data_1 |= 0x04000000;
-+	TITAN_GE_WRITE((0x103c + (port_num << 12)), reg_data_1);
-+
-+	mdelay(5);
-+
-+	reg_data_1 &= ~(0x04000000);
-+	TITAN_GE_WRITE((0x103c + (port_num << 12)), reg_data_1);
-+
-+	mdelay(5);
-+
-+	reg_data |= 0x0001;
-+	TITAN_GE_WRITE((TITAN_GE_TRTG_CONFIG + (port_num << 12)), reg_data);
-+
-+	/*
-+	 * Step 4:  Start the Tx activity
-+	 */
-+	TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_2 + (port_num << 12)), 0xe197);
-+#ifdef TITAN_GE_JUMBO_FRAMES
-+	TITAN_GE_WRITE((0x1258 + (port_num << 12)), 0x4000);
-+#endif
-+	reg_data = TITAN_GE_READ(TITAN_GE_TMAC_CONFIG_1 + (port_num << 12));
-+	reg_data |= 0x0001;	/* Enable TMAC */
-+	reg_data |= 0x6c70;	/* PAUSE also set */
-+
-+	TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_1 + (port_num << 12)), reg_data);
-+
-+	udelay(30);
-+
-+	/* Destination Address drop bit */
-+	reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_2 + (port_num << 12));
-+	reg_data |= 0x218;        /* DA_DROP bit and pause */
-+	TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_2 + (port_num << 12)), reg_data);
-+
-+	TITAN_GE_WRITE((0x1218 + (port_num << 12)), 0x3);
-+
-+#ifdef TITAN_GE_JUMBO_FRAMES
-+	TITAN_GE_WRITE((0x1208 + (port_num << 12)), 0x4000);
-+#endif
-+	/* Start the Rx activity */
-+	reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_1 + (port_num << 12));
-+	reg_data |= 0x0001;	/* RMAC Enable */
-+	reg_data |= 0x0010;	/* CRC Check enable */
-+	reg_data |= 0x0040;	/* Min Frame check enable */
-+	reg_data |= 0x4400;	/* Max Frame check enable */
-+
-+	TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_1 + (port_num << 12)), reg_data);
-+
-+	udelay(30);
-+
-+	/*
-+	 * Enable the Interrupts for Tx and Rx
-+	 */
-+	reg_data1 = TITAN_GE_READ(TITAN_GE_INTR_XDMA_IE);
-+
-+	if (port_num == 0) {
-+		reg_data1 |= 0x3;
-+#ifdef CONFIG_SMP
-+		TITAN_GE_WRITE(0x0038, 0x003);
-+#else
-+		TITAN_GE_WRITE(0x0038, 0x303);
-+#endif
-+	}
-+
-+	if (port_num == 1) {
-+		reg_data1 |= 0x300;
-+	}
-+
-+	if (port_num == 2)
-+		reg_data1 |= 0x30000;
-+
-+	TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_IE, reg_data1);
-+	TITAN_GE_WRITE(0x003c, 0x300);
-+
-+	if (config_done == 0) {
-+		TITAN_GE_WRITE(0x0024, 0x04000024);	/* IRQ vector */
-+		TITAN_GE_WRITE(0x0020, 0x000fb000);	/* INTMSG base */
-+	}
-+
-+	/* Priority */
-+	reg_data = TITAN_GE_READ(0x1038 + (port_num << 12));
-+	reg_data &= ~(0x00f00000);
-+	TITAN_GE_WRITE((0x1038 + (port_num << 12)), reg_data);
-+
-+	/* Step 5:  GMII config */
-+	titan_ge_gmii_config(port_num);
-+
-+	if (config_done == 0) {
-+		TITAN_GE_WRITE(0x1a80, 0);
-+		config_done = 1;
-+	}
-+
-+	return TITAN_OK;
-+}
-+
-+/*
-+ * Function to queue the packet for the Ethernet device
-+ */
-+static void titan_ge_tx_queue(titan_ge_port_info * titan_ge_eth,
-+				struct sk_buff * skb)
-+{
-+	struct device *device = &titan_ge_device[titan_ge_eth->port_num]->dev;
-+	unsigned int curr_desc = titan_ge_eth->tx_curr_desc_q;
-+	volatile titan_ge_tx_desc *tx_curr;
-+	int port_num = titan_ge_eth->port_num;
-+
-+	tx_curr = &(titan_ge_eth->tx_desc_area[curr_desc]);
-+	tx_curr->buffer_addr =
-+		dma_map_single(device, skb->data, skb_headlen(skb),
-+			       DMA_TO_DEVICE);
-+
-+	titan_ge_eth->tx_skb[curr_desc] = (struct sk_buff *) skb;
-+	tx_curr->buffer_len = skb_headlen(skb);
-+
-+	/* Last descriptor enables interrupt and changes ownership */
-+	tx_curr->cmd_sts = 0x1 | (1 << 15) | (1 << 5);
-+
-+	/* Kick the XDMA to start the transfer from memory to the FIFO */
-+	TITAN_GE_WRITE((0x5044 + (port_num << 8)), 0x1);
-+
-+	/* Current descriptor updated */
-+	titan_ge_eth->tx_curr_desc_q = (curr_desc + 1) % TITAN_GE_TX_QUEUE;
-+
-+	/* Prefetch the next descriptor */
-+	prefetch((const void *)
-+		 &titan_ge_eth->tx_desc_area[titan_ge_eth->tx_curr_desc_q]);
-+}
-+
-+/*
-+ * Actually does the open of the Ethernet device
-+ */
-+static int titan_ge_eth_open(struct net_device *netdev)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+	unsigned int port_num = titan_ge_eth->port_num;
-+	struct device *device = &titan_ge_device[port_num]->dev;
-+	unsigned long reg_data;
-+	unsigned int phy_reg;
-+	int err = 0;
-+
-+	/* Stop the Rx activity */
-+	reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_1 + (port_num << 12));
-+	reg_data &= ~(0x00000001);
-+	TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_1 + (port_num << 12)), reg_data);
-+
-+	/* Clear the port interrupts */
-+	TITAN_GE_WRITE((TITAN_GE_CHANNEL0_INTERRUPT + (port_num << 8)), 0x0);
-+
-+	if (config_done == 0) {
-+		TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_A, 0);
-+		TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_B, 0);
-+	}
-+
-+	/* Set the MAC Address */
-+	memcpy(titan_ge_eth->port_mac_addr, netdev->dev_addr, 6);
-+
-+	if (config_done == 0)
-+		titan_port_init(netdev, titan_ge_eth);
-+
-+	titan_ge_update_afx(titan_ge_eth);
-+
-+	/* Allocate the Tx ring now */
-+	titan_ge_eth->tx_ring_skbs = 0;
-+	titan_ge_eth->tx_ring_size = TITAN_GE_TX_QUEUE;
-+
-+	/* Allocate space in the SRAM for the descriptors */
-+	titan_ge_eth->tx_desc_area = (titan_ge_tx_desc *)
-+		(titan_ge_sram + TITAN_TX_RING_BYTES * port_num);
-+	titan_ge_eth->tx_dma = TITAN_SRAM_BASE + TITAN_TX_RING_BYTES * port_num;
-+
-+	if (!titan_ge_eth->tx_desc_area) {
-+		printk(KERN_ERR
-+		       "%s: Cannot allocate Tx Ring (size %d bytes) for port %d\n",
-+		       netdev->name, TITAN_TX_RING_BYTES, port_num);
-+		return -ENOMEM;
-+	}
-+
-+	memset(titan_ge_eth->tx_desc_area, 0, titan_ge_eth->tx_desc_area_size);
-+
-+	/* Now initialize the Tx descriptor ring */
-+	titan_ge_init_tx_desc_ring(titan_ge_eth,
-+				   titan_ge_eth->tx_ring_size,
-+				   (unsigned long) titan_ge_eth->tx_desc_area,
-+				   (unsigned long) titan_ge_eth->tx_dma);
-+
-+	/* Allocate the Rx ring now */
-+	titan_ge_eth->rx_ring_size = TITAN_GE_RX_QUEUE;
-+	titan_ge_eth->rx_ring_skbs = 0;
-+
-+	titan_ge_eth->rx_desc_area =
-+		(titan_ge_rx_desc *)(titan_ge_sram + 0x1000 + TITAN_RX_RING_BYTES * port_num);
-+
-+	titan_ge_eth->rx_dma = TITAN_SRAM_BASE + 0x1000 + TITAN_RX_RING_BYTES * port_num;
-+
-+	if (!titan_ge_eth->rx_desc_area) {
-+		printk(KERN_ERR "%s: Cannot allocate Rx Ring (size %d bytes)\n",
-+		       netdev->name, TITAN_RX_RING_BYTES);
-+
-+		printk(KERN_ERR "%s: Freeing previously allocated TX queues...",
-+		       netdev->name);
-+
-+		dma_free_coherent(device, titan_ge_eth->tx_desc_area_size,
-+				    (void *) titan_ge_eth->tx_desc_area,
-+				    titan_ge_eth->tx_dma);
-+
-+		return -ENOMEM;
-+	}
-+
-+	memset(titan_ge_eth->rx_desc_area, 0, titan_ge_eth->rx_desc_area_size);
-+
-+	/* Now initialize the Rx ring */
-+#ifdef TITAN_GE_JUMBO_FRAMES
-+	if ((titan_ge_init_rx_desc_ring
-+	    (titan_ge_eth, titan_ge_eth->rx_ring_size, TITAN_GE_JUMBO_BUFSIZE,
-+	     (unsigned long) titan_ge_eth->rx_desc_area, 0,
-+	      (unsigned long) titan_ge_eth->rx_dma)) == 0)
-+#else
-+	if ((titan_ge_init_rx_desc_ring
-+	     (titan_ge_eth, titan_ge_eth->rx_ring_size, TITAN_GE_STD_BUFSIZE,
-+	      (unsigned long) titan_ge_eth->rx_desc_area, 0,
-+	      (unsigned long) titan_ge_eth->rx_dma)) == 0)
-+#endif
-+		panic("%s: Error initializing RX Ring\n", netdev->name);
-+
-+	/* Fill the Rx ring with the SKBs */
-+	titan_ge_port_start(netdev, titan_ge_eth);
-+
-+	/*
-+	 * Check if Interrupt Coalscing needs to be turned on. The
-+	 * values specified in the register is multiplied by
-+	 * (8 x 64 nanoseconds) to determine when an interrupt should
-+	 * be sent to the CPU.
-+	 */
-+
-+	if (TITAN_GE_TX_COAL) {
-+		titan_ge_eth->tx_int_coal =
-+		    titan_ge_tx_coal(TITAN_GE_TX_COAL, port_num);
-+	}
-+
-+	err = titan_ge_mdio_read(port_num, TITAN_GE_MDIO_PHY_STATUS, &phy_reg);
-+	if (err == TITAN_GE_MDIO_ERROR) {
-+		printk(KERN_ERR
-+		       "Could not read PHY control register 0x11 \n");
-+		return TITAN_ERROR;
-+	}
-+	if (!(phy_reg & 0x0400)) {
-+		netif_carrier_off(netdev);
-+		netif_stop_queue(netdev);
-+		return TITAN_ERROR;
-+	} else {
-+		netif_carrier_on(netdev);
-+		netif_start_queue(netdev);
-+	}
-+
-+	return TITAN_OK;
-+}
-+
-+/*
-+ * Queue the packet for Tx. Currently no support for zero copy,
-+ * checksum offload and Scatter Gather. The chip does support
-+ * Scatter Gather only. But, that wont help here since zero copy
-+ * requires support for Tx checksumming also.
-+ */
-+int titan_ge_start_xmit(struct sk_buff *skb, struct net_device *netdev)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+	unsigned long flags;
-+	struct net_device_stats *stats;
-+//printk("titan_ge_start_xmit\n");
-+
-+	stats = &titan_ge_eth->stats;
-+	spin_lock_irqsave(&titan_ge_eth->lock, flags);
-+
-+	if ((TITAN_GE_TX_QUEUE - titan_ge_eth->tx_ring_skbs) <=
-+	    (skb_shinfo(skb)->nr_frags + 1)) {
-+		netif_stop_queue(netdev);
-+		spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
-+		printk(KERN_ERR "Tx OOD \n");
-+		return 1;
-+	}
-+
-+	titan_ge_tx_queue(titan_ge_eth, skb);
-+	titan_ge_eth->tx_ring_skbs++;
-+
-+	if (TITAN_GE_TX_QUEUE <= (titan_ge_eth->tx_ring_skbs + 4)) {
-+		spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
-+		titan_ge_free_tx_queue(titan_ge_eth);
-+		spin_lock_irqsave(&titan_ge_eth->lock, flags);
-+	}
-+
-+	stats->tx_bytes += skb->len;
-+	stats->tx_packets++;
-+
-+	spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
-+
-+	netdev->trans_start = jiffies;
-+
-+	return 0;
-+}
-+
-+/*
-+ * Actually does the Rx. Rx side checksumming supported.
-+ */
-+static int titan_ge_rx(struct net_device *netdev, int port_num,
-+			titan_ge_port_info * titan_ge_port,
-+		       titan_ge_packet * packet)
-+{
-+	int rx_curr_desc, rx_used_desc;
-+	volatile titan_ge_rx_desc *rx_desc;
-+
-+	rx_curr_desc = titan_ge_port->rx_curr_desc_q;
-+	rx_used_desc = titan_ge_port->rx_used_desc_q;
-+
-+	if (((rx_curr_desc + 1) % TITAN_GE_RX_QUEUE) == rx_used_desc)
-+		return TITAN_ERROR;
-+
-+	rx_desc = &(titan_ge_port->rx_desc_area[rx_curr_desc]);
-+
-+	if (rx_desc->cmd_sts & TITAN_GE_RX_BUFFER_OWNED)
-+		return TITAN_ERROR;
-+
-+	packet->skb = titan_ge_port->rx_skb[rx_curr_desc];
-+	packet->len = (rx_desc->cmd_sts & 0x7fff);
-+
-+	/*
-+	 * At this point, we dont know if the checksumming
-+	 * actually helps relieve CPU. So, keep it for
-+	 * port 0 only
-+	 */
-+	packet->checksum = ntohs((rx_desc->buffer & 0xffff0000) >> 16);
-+	packet->cmd_sts = rx_desc->cmd_sts;
-+
-+	titan_ge_port->rx_curr_desc_q = (rx_curr_desc + 1) % TITAN_GE_RX_QUEUE;
-+
-+	/* Prefetch the next descriptor */
-+	prefetch((const void *)
-+	       &titan_ge_port->rx_desc_area[titan_ge_port->rx_curr_desc_q + 1]);
-+
-+	return TITAN_OK;
-+}
-+
-+/*
-+ * Free the Tx queue of the used SKBs
-+ */
-+static int titan_ge_free_tx_queue(titan_ge_port_info *titan_ge_eth)
-+{
-+	unsigned long flags;
-+
-+	/* Take the lock */
-+	spin_lock_irqsave(&(titan_ge_eth->lock), flags);
-+
-+	while (titan_ge_return_tx_desc(titan_ge_eth, titan_ge_eth->port_num) == 0)
-+		if (titan_ge_eth->tx_ring_skbs != 1)
-+			titan_ge_eth->tx_ring_skbs--;
-+
-+	spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
-+
-+	return TITAN_OK;
-+}
-+
-+/*
-+ * Threshold beyond which we do the cleaning of
-+ * Tx queue and new allocation for the Rx
-+ * queue
-+ */
-+#define	TX_THRESHOLD	4
-+#define	RX_THRESHOLD	10
-+
-+/*
-+ * Receive the packets and send it to the kernel.
-+ */
-+static int titan_ge_receive_queue(struct net_device *netdev, unsigned int max)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+	unsigned int port_num = titan_ge_eth->port_num;
-+	titan_ge_packet packet;
-+	struct net_device_stats *stats;
-+	struct sk_buff *skb;
-+	unsigned long received_packets = 0;
-+	unsigned int ack;
-+
-+	stats = &titan_ge_eth->stats;
-+
-+	while ((--max)
-+	       && (titan_ge_rx(netdev, port_num, titan_ge_eth, &packet) == TITAN_OK)) {
-+		skb = (struct sk_buff *) packet.skb;
-+
-+		titan_ge_eth->rx_ring_skbs--;
-+
-+		if (--titan_ge_eth->rx_work_limit < 0)
-+			break;
-+		received_packets++;
-+
-+		stats->rx_packets++;
-+		stats->rx_bytes += packet.len;
-+
-+		if ((packet.cmd_sts & TITAN_GE_RX_PERR) ||
-+			(packet.cmd_sts & TITAN_GE_RX_OVERFLOW_ERROR) ||
-+			(packet.cmd_sts & TITAN_GE_RX_TRUNC) ||
-+			(packet.cmd_sts & TITAN_GE_RX_CRC_ERROR)) {
-+				stats->rx_dropped++;
-+				dev_kfree_skb_any(skb);
-+
-+				continue;
-+		}
-+		/*
-+		 * Either support fast path or slow path. Decision
-+		 * making can really slow down the performance. The
-+		 * idea is to cut down the number of checks and improve
-+		 * the fastpath.
-+		 */
-+
-+		skb_put(skb, packet.len - 2);
-+
-+		/*
-+		 * Increment data pointer by two since thats where
-+		 * the MAC starts
-+		 */
-+		skb_reserve(skb, 2);
-+		skb->protocol = eth_type_trans(skb, netdev);
-+		netif_receive_skb(skb);
-+
-+		if (titan_ge_eth->rx_threshold > RX_THRESHOLD) {
-+			ack = titan_ge_rx_task(netdev, titan_ge_eth);
-+			TITAN_GE_WRITE((0x5048 + (port_num << 8)), ack);
-+			titan_ge_eth->rx_threshold = 0;
-+		} else
-+			titan_ge_eth->rx_threshold++;
-+
-+		if (titan_ge_eth->tx_threshold > TX_THRESHOLD) {
-+			titan_ge_eth->tx_threshold = 0;
-+			titan_ge_free_tx_queue(titan_ge_eth);
-+		}
-+		else
-+			titan_ge_eth->tx_threshold++;
-+
-+	}
-+	return received_packets;
-+}
-+
-+
-+/*
-+ * Enable the Rx side interrupts
-+ */
-+static void titan_ge_enable_int(unsigned int port_num,
-+			titan_ge_port_info *titan_ge_eth,
-+			struct net_device *netdev)
-+{
-+	unsigned long reg_data = TITAN_GE_READ(TITAN_GE_INTR_XDMA_IE);
-+
-+	if (port_num == 0)
-+		reg_data |= 0x3;
-+	if (port_num == 1)
-+		reg_data |= 0x300;
-+	if (port_num == 2)
-+		reg_data |= 0x30000;
-+
-+	/* Re-enable interrupts */
-+	TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_IE, reg_data);
-+}
-+
-+/*
-+ * Main function to handle the polling for Rx side NAPI.
-+ * Receive interrupts have been disabled at this point.
-+ * The poll schedules the transmit followed by receive.
-+ */
-+static int titan_ge_poll(struct net_device *netdev, int *budget)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+	int port_num = titan_ge_eth->port_num;
-+	int work_done = 0;
-+	unsigned long flags, status;
-+
-+	titan_ge_eth->rx_work_limit = *budget;
-+	if (titan_ge_eth->rx_work_limit > netdev->quota)
-+		titan_ge_eth->rx_work_limit = netdev->quota;
-+
-+	do {
-+		/* Do the transmit cleaning work here */
-+		titan_ge_free_tx_queue(titan_ge_eth);
-+
-+		/* Ack the Rx interrupts */
-+		if (port_num == 0)
-+			TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_A, 0x3);
-+		if (port_num == 1)
-+			TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_A, 0x300);
-+		if (port_num == 2)
-+			TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_A, 0x30000);
-+
-+		work_done += titan_ge_receive_queue(netdev, 0);
-+
-+		/* Out of quota and there is work to be done */
-+		if (titan_ge_eth->rx_work_limit < 0)
-+			goto not_done;
-+
-+		/* Receive alloc_skb could lead to OOM */
-+		if (oom_flag == 1) {
-+			oom_flag = 0;
-+			goto oom;
-+		}
-+
-+		status = TITAN_GE_READ(TITAN_GE_INTR_XDMA_CORE_A);
-+	} while (status & 0x30300);
-+
-+	/* If we are here, then no more interrupts to process */
-+	goto done;
-+
-+not_done:
-+	*budget -= work_done;
-+	netdev->quota -= work_done;
-+	return 1;
-+
-+oom:
-+	printk(KERN_ERR "OOM \n");
-+	netif_rx_complete(netdev);
-+	return 0;
-+
-+done:
-+	/*
-+	 * No more packets on the poll list. Turn the interrupts
-+	 * back on and we should be able to catch the new
-+	 * packets in the interrupt handler
-+	 */
-+	if (!work_done)
-+		work_done = 1;
-+
-+	*budget -= work_done;
-+	netdev->quota -= work_done;
-+
-+	spin_lock_irqsave(&titan_ge_eth->lock, flags);
-+
-+	/* Remove us from the poll list */
-+	netif_rx_complete(netdev);
-+
-+	/* Re-enable interrupts */
-+	titan_ge_enable_int(port_num, titan_ge_eth, netdev);
-+
-+	spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
-+
-+	return 0;
-+}
-+
-+/*
-+ * Close the network device
-+ */
-+int titan_ge_stop(struct net_device *netdev)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+
-+	spin_lock_irq(&(titan_ge_eth->lock));
-+	titan_ge_eth_stop(netdev);
-+	free_irq(netdev->irq, netdev);
-+	spin_unlock_irq(&titan_ge_eth->lock);
-+
-+	return TITAN_OK;
-+}
-+
-+/*
-+ * Free the Tx ring
-+ */
-+static void titan_ge_free_tx_rings(struct net_device *netdev)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+	unsigned int port_num = titan_ge_eth->port_num;
-+	unsigned int curr;
-+	unsigned long reg_data;
-+
-+	/* Stop the Tx DMA */
-+	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG +
-+				(port_num << 8));
-+	reg_data |= 0xc0000000;
-+	TITAN_GE_WRITE((TITAN_GE_CHANNEL0_CONFIG +
-+			(port_num << 8)), reg_data);
-+
-+	/* Disable the TMAC */
-+	reg_data = TITAN_GE_READ(TITAN_GE_TMAC_CONFIG_1 +
-+				(port_num << 12));
-+	reg_data &= ~(0x00000001);
-+	TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_1 +
-+			(port_num << 12)), reg_data);
-+
-+	for (curr = 0;
-+	     (titan_ge_eth->tx_ring_skbs) && (curr < TITAN_GE_TX_QUEUE);
-+	     curr++) {
-+		if (titan_ge_eth->tx_skb[curr]) {
-+			dev_kfree_skb(titan_ge_eth->tx_skb[curr]);
-+			titan_ge_eth->tx_ring_skbs--;
-+		}
-+	}
-+
-+	if (titan_ge_eth->tx_ring_skbs != 0)
-+		printk
-+		    ("%s: Error on Tx descriptor free - could not free %d"
-+		     " descriptors\n", netdev->name,
-+		     titan_ge_eth->tx_ring_skbs);
-+
-+#ifndef TITAN_RX_RING_IN_SRAM
-+	dma_free_coherent(&titan_ge_device[port_num]->dev,
-+			  titan_ge_eth->tx_desc_area_size,
-+			  (void *) titan_ge_eth->tx_desc_area,
-+			  titan_ge_eth->tx_dma);
-+#endif
-+}
-+
-+/*
-+ * Free the Rx ring
-+ */
-+static void titan_ge_free_rx_rings(struct net_device *netdev)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+	unsigned int port_num = titan_ge_eth->port_num;
-+	unsigned int curr;
-+	unsigned long reg_data;
-+
-+	/* Stop the Rx DMA */
-+	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG +
-+				(port_num << 8));
-+	reg_data |= 0x000c0000;
-+	TITAN_GE_WRITE((TITAN_GE_CHANNEL0_CONFIG +
-+			(port_num << 8)), reg_data);
-+
-+	/* Disable the RMAC */
-+	reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_1 +
-+				(port_num << 12));
-+	reg_data &= ~(0x00000001);
-+	TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_1 +
-+			(port_num << 12)), reg_data);
-+
-+	for (curr = 0;
-+	     titan_ge_eth->rx_ring_skbs && (curr < TITAN_GE_RX_QUEUE);
-+	     curr++) {
-+		if (titan_ge_eth->rx_skb[curr]) {
-+			dev_kfree_skb(titan_ge_eth->rx_skb[curr]);
-+			titan_ge_eth->rx_ring_skbs--;
-+		}
-+	}
-+
-+	if (titan_ge_eth->rx_ring_skbs != 0)
-+		printk(KERN_ERR
-+		       "%s: Error in freeing Rx Ring. %d skb's still"
-+		       " stuck in RX Ring - ignoring them\n", netdev->name,
-+		       titan_ge_eth->rx_ring_skbs);
-+
-+#ifndef TITAN_RX_RING_IN_SRAM
-+	dma_free_coherent(&titan_ge_device[port_num]->dev,
-+			  titan_ge_eth->rx_desc_area_size,
-+			  (void *) titan_ge_eth->rx_desc_area,
-+			  titan_ge_eth->rx_dma);
-+#endif
-+}
-+
-+/*
-+ * Actually does the stop of the Ethernet device
-+ */
-+static void titan_ge_eth_stop(struct net_device *netdev)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+
-+	netif_stop_queue(netdev);
-+
-+	titan_ge_port_reset(titan_ge_eth->port_num);
-+
-+	titan_ge_free_tx_rings(netdev);
-+	titan_ge_free_rx_rings(netdev);
-+
-+	/* Disable the Tx and Rx Interrupts for all channels */
-+	TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_IE, 0x0);
-+}
-+
-+/*
-+ * Update the MAC address. Note that we have to write the
-+ * address in three station registers, 16 bits each. And this
-+ * has to be done for TMAC and RMAC
-+ */
-+static void titan_ge_update_mac_address(struct net_device *netdev)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+	unsigned int port_num = titan_ge_eth->port_num;
-+	u8 p_addr[6];
-+
-+	memcpy(titan_ge_eth->port_mac_addr, netdev->dev_addr, 6);
-+	memcpy(p_addr, netdev->dev_addr, 6);
-+
-+	/* Update the Address Filtering Match tables */
-+	titan_ge_update_afx(titan_ge_eth);
-+
-+	printk("Station MAC : %d %d %d %d %d %d  \n",
-+		p_addr[5], p_addr[4], p_addr[3],
-+		p_addr[2], p_addr[1], p_addr[0]);
-+
-+	/* Set the MAC address here for TMAC and RMAC */
-+	TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_HI + (port_num << 12)),
-+		       ((p_addr[5] << 8) | p_addr[4]));
-+	TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_MID + (port_num << 12)),
-+		       ((p_addr[3] << 8) | p_addr[2]));
-+	TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_LOW + (port_num << 12)),
-+		       ((p_addr[1] << 8) | p_addr[0]));
-+
-+	TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_HI + (port_num << 12)),
-+		       ((p_addr[5] << 8) | p_addr[4]));
-+	TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_MID + (port_num << 12)),
-+		       ((p_addr[3] << 8) | p_addr[2]));
-+	TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_LOW + (port_num << 12)),
-+		       ((p_addr[1] << 8) | p_addr[0]));
-+}
-+
-+/*
-+ * Set the MAC address of the Ethernet device
-+ */
-+static int titan_ge_set_mac_address(struct net_device *dev, void *addr)
-+{
-+	titan_ge_port_info *tp = netdev_priv(dev);
-+	struct sockaddr *sa = addr;
-+
-+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
-+
-+	spin_lock_irq(&tp->lock);
-+	titan_ge_update_mac_address(dev);
-+	spin_unlock_irq(&tp->lock);
-+
-+	return 0;
-+}
-+
-+/*
-+ * Get the Ethernet device stats
-+ */
-+static struct net_device_stats *titan_ge_get_stats(struct net_device *netdev)
-+{
-+	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
-+
-+	return &titan_ge_eth->stats;
-+}
-+
-+/*
-+ * Initialize the Rx descriptor ring for the Titan Ge
-+ */
-+static int titan_ge_init_rx_desc_ring(titan_ge_port_info * titan_eth_port,
-+				      int rx_desc_num,
-+				      int rx_buff_size,
-+				      unsigned long rx_desc_base_addr,
-+				      unsigned long rx_buff_base_addr,
-+				      unsigned long rx_dma)
-+{
-+	volatile titan_ge_rx_desc *rx_desc;
-+	unsigned long buffer_addr;
-+	int index;
-+	unsigned long titan_ge_rx_desc_bus = rx_dma;
-+
-+	buffer_addr = rx_buff_base_addr;
-+	rx_desc = (titan_ge_rx_desc *) rx_desc_base_addr;
-+
-+	/* Check alignment */
-+	if (rx_buff_base_addr & 0xF)
-+		return 0;
-+
-+	/* Check Rx buffer size */
-+	if ((rx_buff_size < 8) || (rx_buff_size > TITAN_GE_MAX_RX_BUFFER))
-+		return 0;
-+
-+	/* 64-bit alignment
-+	if ((rx_buff_base_addr + rx_buff_size) & 0x7)
-+		return 0; */
-+
-+	/* Initialize the Rx desc ring */
-+	for (index = 0; index < rx_desc_num; index++) {
-+		titan_ge_rx_desc_bus += sizeof(titan_ge_rx_desc);
-+		rx_desc[index].cmd_sts = 0;
-+		rx_desc[index].buffer_addr = buffer_addr;
-+		titan_eth_port->rx_skb[index] = NULL;
-+		buffer_addr += rx_buff_size;
-+	}
-+
-+	titan_eth_port->rx_curr_desc_q = 0;
-+	titan_eth_port->rx_used_desc_q = 0;
-+
-+	titan_eth_port->rx_desc_area = (titan_ge_rx_desc *) rx_desc_base_addr;
-+	titan_eth_port->rx_desc_area_size =
-+	    rx_desc_num * sizeof(titan_ge_rx_desc);
-+
-+	titan_eth_port->rx_dma = rx_dma;
-+
-+	return TITAN_OK;
-+}
-+
-+/*
-+ * Initialize the Tx descriptor ring. Descriptors in the SRAM
-+ */
-+static int titan_ge_init_tx_desc_ring(titan_ge_port_info * titan_ge_port,
-+				      int tx_desc_num,
-+				      unsigned long tx_desc_base_addr,
-+				      unsigned long tx_dma)
-+{
-+	titan_ge_tx_desc *tx_desc;
-+	int index;
-+	unsigned long titan_ge_tx_desc_bus = tx_dma;
-+
-+	if (tx_desc_base_addr & 0xF)
-+		return 0;
-+
-+	tx_desc = (titan_ge_tx_desc *) tx_desc_base_addr;
-+
-+	for (index = 0; index < tx_desc_num; index++) {
-+		titan_ge_port->tx_dma_array[index] =
-+		    (dma_addr_t) titan_ge_tx_desc_bus;
-+		titan_ge_tx_desc_bus += sizeof(titan_ge_tx_desc);
-+		tx_desc[index].cmd_sts = 0x0000;
-+		tx_desc[index].buffer_len = 0;
-+		tx_desc[index].buffer_addr = 0x00000000;
-+		titan_ge_port->tx_skb[index] = NULL;
-+	}
-+
-+	titan_ge_port->tx_curr_desc_q = 0;
-+	titan_ge_port->tx_used_desc_q = 0;
-+
-+	titan_ge_port->tx_desc_area = (titan_ge_tx_desc *) tx_desc_base_addr;
-+	titan_ge_port->tx_desc_area_size =
-+	    tx_desc_num * sizeof(titan_ge_tx_desc);
-+
-+	titan_ge_port->tx_dma = tx_dma;
-+	return TITAN_OK;
-+}
-+
-+/*
-+ * Initialize the device as an Ethernet device
-+ */
-+static int __init titan_ge_probe(struct device *device)
-+{
-+	titan_ge_port_info *titan_ge_eth;
-+	struct net_device *netdev;
-+	int port = to_platform_device(device)->id;
-+	int err;
-+
-+	netdev = alloc_etherdev(sizeof(titan_ge_port_info));
-+	if (!netdev) {
-+		err = -ENODEV;
-+		goto out;
-+	}
-+
-+	netdev->open = titan_ge_open;
-+	netdev->stop = titan_ge_stop;
-+	netdev->hard_start_xmit = titan_ge_start_xmit;
-+	netdev->get_stats = titan_ge_get_stats;
-+	netdev->set_multicast_list = titan_ge_set_multi;
-+	netdev->set_mac_address = titan_ge_set_mac_address;
-+
-+	/* Tx timeout */
-+	netdev->tx_timeout = titan_ge_tx_timeout;
-+	netdev->watchdog_timeo = 2 * HZ;
-+
-+	/* Set these to very high values */
-+	netdev->poll = titan_ge_poll;
-+	netdev->weight = 64;
-+
-+	netdev->tx_queue_len = TITAN_GE_TX_QUEUE;
-+	netif_carrier_off(netdev);
-+	netdev->base_addr = 0;
-+
-+	netdev->change_mtu = titan_ge_change_mtu;
-+
-+	titan_ge_eth = netdev_priv(netdev);
-+	/* Allocation of memory for the driver structures */
-+
-+	titan_ge_eth->port_num = port;
-+
-+	/* Configure the Tx timeout handler */
-+	INIT_WORK(&titan_ge_eth->tx_timeout_task,
-+		  (void (*)(void *)) titan_ge_tx_timeout_task, netdev);
-+
-+	spin_lock_init(&titan_ge_eth->lock);
-+
-+	/* set MAC addresses */
-+	memcpy(netdev->dev_addr, titan_ge_mac_addr_base, 6);
-+	netdev->dev_addr[5] += port;
-+
-+	err = register_netdev(netdev);
-+
-+	if (err)
-+		goto out_free_netdev;
-+
-+	printk(KERN_NOTICE
-+	       "%s: port %d with MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
-+	       netdev->name, port, netdev->dev_addr[0],
-+	       netdev->dev_addr[1], netdev->dev_addr[2],
-+	       netdev->dev_addr[3], netdev->dev_addr[4],
-+	       netdev->dev_addr[5]);
-+
-+	printk(KERN_NOTICE "Rx NAPI supported, Tx Coalescing ON \n");
-+
-+	return 0;
-+
-+out_free_netdev:
-+	kfree(netdev);
-+
-+out:
-+	return err;
-+}
-+
-+static void __devexit titan_device_remove(struct device *device)
-+{
-+}
-+
-+/*
-+ * Reset the Ethernet port
-+ */
-+static void titan_ge_port_reset(unsigned int port_num)
-+{
-+	unsigned int reg_data;
-+
-+	/* Stop the Tx port activity */
-+	reg_data = TITAN_GE_READ(TITAN_GE_TMAC_CONFIG_1 +
-+				(port_num << 12));
-+	reg_data &= ~(0x0001);
-+	TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_1 +
-+			(port_num << 12)), reg_data);
-+
-+	/* Stop the Rx port activity */
-+	reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_1 +
-+				(port_num << 12));
-+	reg_data &= ~(0x0001);
-+	TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_1 +
-+			(port_num << 12)), reg_data);
-+
-+	return;
-+}
-+
-+/*
-+ * Return the Tx desc after use by the XDMA
-+ */
-+static int titan_ge_return_tx_desc(titan_ge_port_info * titan_ge_eth, int port)
-+{
-+	int tx_desc_used;
-+	struct sk_buff *skb;
-+
-+	tx_desc_used = titan_ge_eth->tx_used_desc_q;
-+
-+	/* return right away */
-+	if (tx_desc_used == titan_ge_eth->tx_curr_desc_q)
-+		return TITAN_ERROR;
-+
-+	/* Now the critical stuff */
-+	skb = titan_ge_eth->tx_skb[tx_desc_used];
-+
-+	dev_kfree_skb_any(skb);
-+
-+	titan_ge_eth->tx_skb[tx_desc_used] = NULL;
-+	titan_ge_eth->tx_used_desc_q =
-+	    (tx_desc_used + 1) % TITAN_GE_TX_QUEUE;
-+
-+	return 0;
-+}
-+
-+/*
-+ * Coalescing for the Tx path
-+ */
-+static unsigned long titan_ge_tx_coal(unsigned long delay, int port)
-+{
-+	unsigned long rx_delay;
-+
-+	rx_delay = TITAN_GE_READ(TITAN_GE_INT_COALESCING);
-+	delay = (delay << 16) | rx_delay;
-+
-+	TITAN_GE_WRITE(TITAN_GE_INT_COALESCING, delay);
-+	TITAN_GE_WRITE(0x5038, delay);
-+
-+	return delay;
-+}
-+
-+static struct device_driver titan_soc_driver = {
-+	.name   = titan_string,
-+	.bus    = &platform_bus_type,
-+	.probe  = titan_ge_probe,
-+	.remove = __devexit_p(titan_device_remove),
-+};
-+
-+static void titan_platform_release (struct device *device)
-+{
-+	struct platform_device *pldev;
-+
-+	/* free device */
-+	pldev = to_platform_device (device);
-+	kfree (pldev);
-+}
-+
-+/*
-+ * Register the Titan GE with the kernel
-+ */
-+static int __init titan_ge_init_module(void)
-+{
-+	struct platform_device *pldev;
-+	unsigned int version, device;
-+	int i;
-+
-+	printk(KERN_NOTICE
-+	       "PMC-Sierra TITAN 10/100/1000 Ethernet Driver \n");
-+
-+	titan_ge_base = (unsigned long) ioremap(TITAN_GE_BASE, TITAN_GE_SIZE);
-+	if (!titan_ge_base) {
-+		printk("Mapping Titan GE failed\n");
-+		goto out;
-+	}
-+
-+	device = TITAN_GE_READ(TITAN_GE_DEVICE_ID);
-+	version = (device & 0x000f0000) >> 16;
-+	device &= 0x0000ffff;
-+
-+	printk(KERN_NOTICE "Device Id : %x,  Version : %x \n", device, version);
-+
-+#ifdef TITAN_RX_RING_IN_SRAM
-+	titan_ge_sram = (unsigned long) ioremap(TITAN_SRAM_BASE,
-+						TITAN_SRAM_SIZE);
-+	if (!titan_ge_sram) {
-+		printk("Mapping Titan SRAM failed\n");
-+		goto out_unmap_ge;
-+	}
-+#endif
-+
-+	if (driver_register(&titan_soc_driver)) {
-+		printk(KERN_ERR "Driver registration failed\n");
-+		goto out_unmap_sram;
-+	}
-+
-+	for (i = 0; i < 3; i++) {
-+		titan_ge_device[i] = NULL;
-+
-+		if (!(pldev = kmalloc (sizeof (*pldev), GFP_KERNEL)))
-+			continue;
-+
-+		memset (pldev, 0, sizeof (*pldev));
-+		pldev->name		= titan_string;
-+		pldev->id		= i;
-+		pldev->dev.release	= titan_platform_release;
-+		titan_ge_device[i]	= pldev;
-+
-+		if (platform_device_register (pldev)) {
-+			kfree (pldev);
-+			titan_ge_device[i] = NULL;
-+			continue;
-+		}
-+
-+		if (!pldev->dev.driver) {
-+			/*
-+			 * The driver was not bound to this device, there was
-+			 * no hardware at this address. Unregister it, as the
-+			 * release fuction will take care of freeing the
-+			 * allocated structure
-+			 */
-+			titan_ge_device[i] = NULL;
-+			platform_device_unregister (pldev);
-+		}
-+	}
-+
-+	return 0;
-+
-+out_unmap_sram:
-+	iounmap((void *)titan_ge_sram);
-+
-+out_unmap_ge:
-+	iounmap((void *)titan_ge_base);
-+
-+out:
-+	return -ENOMEM;
-+}
-+
-+/*
-+ * Unregister the Titan GE from the kernel
-+ */
-+static void __exit titan_ge_cleanup_module(void)
-+{
-+	int i;
-+
-+	driver_unregister(&titan_soc_driver);
-+
-+	for (i = 0; i < 3; i++) {
-+		if (titan_ge_device[i]) {
-+			platform_device_unregister (titan_ge_device[i]);
-+			titan_ge_device[i] = NULL;
-+		}
-+	}
-+
-+	iounmap((void *)titan_ge_sram);
-+	iounmap((void *)titan_ge_base);
-+}
-+
-+MODULE_AUTHOR("Manish Lachwani <lachwani@pmc-sierra.com>");
-+MODULE_DESCRIPTION("Titan GE Ethernet driver");
-+MODULE_LICENSE("GPL");
-+
-+module_init(titan_ge_init_module);
-+module_exit(titan_ge_cleanup_module);
-diff --git a/drivers/net/titan_ge.h b/drivers/net/titan_ge.h
-new file mode 100644
-index 0000000..3719f78
---- /dev/null
-+++ b/drivers/net/titan_ge.h
-@@ -0,0 +1,415 @@
-+#ifndef _TITAN_GE_H_
-+#define _TITAN_GE_H_
-+
-+#include <linux/module.h>
-+#include <linux/kernel.h>
-+#include <linux/spinlock.h>
-+#include <asm/byteorder.h>
-+
-+/*
-+ * These functions should be later moved to a more generic location since there
-+ * will be others accessing it also
-+ */
-+
-+/*
-+ * This is the way it works: LKB5 Base is at 0x0128. TITAN_BASE is defined in
-+ * include/asm/titan_dep.h. TITAN_GE_BASE is the value in the TITAN_GE_LKB5
-+ * register.
-+ */
-+
-+#define	TITAN_GE_BASE	0xfe000000UL
-+#define	TITAN_GE_SIZE	0x10000UL
-+
-+extern unsigned long titan_ge_base;
-+
-+#define	TITAN_GE_WRITE(offset, data) \
-+		*(volatile u32 *)(titan_ge_base + (offset)) = (data)
-+
-+#define TITAN_GE_READ(offset) *(volatile u32 *)(titan_ge_base + (offset))
-+
-+#ifndef msec_delay
-+#define msec_delay(x)   do { if(in_interrupt()) { \
-+				/* Don't mdelay in interrupt context! */ \
-+				BUG(); \
-+			} else { \
-+				set_current_state(TASK_UNINTERRUPTIBLE); \
-+				schedule_timeout((x * HZ)/1000); \
-+			} } while(0)
-+#endif
-+
-+#define TITAN_GE_PORT_0
-+
-+#define	TITAN_SRAM_BASE		((OCD_READ(RM9000x2_OCD_LKB13) & ~1) << 4)
-+#define	TITAN_SRAM_SIZE		0x2000UL
-+
-+/*
-+ * We may need these constants
-+ */
-+#define TITAN_BIT0    0x00000001
-+#define TITAN_BIT1    0x00000002
-+#define TITAN_BIT2    0x00000004
-+#define TITAN_BIT3    0x00000008
-+#define TITAN_BIT4    0x00000010
-+#define TITAN_BIT5    0x00000020
-+#define TITAN_BIT6    0x00000040
-+#define TITAN_BIT7    0x00000080
-+#define TITAN_BIT8    0x00000100
-+#define TITAN_BIT9    0x00000200
-+#define TITAN_BIT10   0x00000400
-+#define TITAN_BIT11   0x00000800
-+#define TITAN_BIT12   0x00001000
-+#define TITAN_BIT13   0x00002000
-+#define TITAN_BIT14   0x00004000
-+#define TITAN_BIT15   0x00008000
-+#define TITAN_BIT16   0x00010000
-+#define TITAN_BIT17   0x00020000
-+#define TITAN_BIT18   0x00040000
-+#define TITAN_BIT19   0x00080000
-+#define TITAN_BIT20   0x00100000
-+#define TITAN_BIT21   0x00200000
-+#define TITAN_BIT22   0x00400000
-+#define TITAN_BIT23   0x00800000
-+#define TITAN_BIT24   0x01000000
-+#define TITAN_BIT25   0x02000000
-+#define TITAN_BIT26   0x04000000
-+#define TITAN_BIT27   0x08000000
-+#define TITAN_BIT28   0x10000000
-+#define TITAN_BIT29   0x20000000
-+#define TITAN_BIT30   0x40000000
-+#define TITAN_BIT31   0x80000000
-+
-+/* Flow Control */
-+#define	TITAN_GE_FC_NONE	0x0
-+#define	TITAN_GE_FC_FULL	0x1
-+#define	TITAN_GE_FC_TX_PAUSE	0x2
-+#define	TITAN_GE_FC_RX_PAUSE	0x3
-+
-+/* Duplex Settings */
-+#define	TITAN_GE_FULL_DUPLEX	0x1
-+#define	TITAN_GE_HALF_DUPLEX	0x2
-+
-+/* Speed settings */
-+#define	TITAN_GE_SPEED_1000	0x1
-+#define	TITAN_GE_SPEED_100	0x2
-+#define	TITAN_GE_SPEED_10	0x3
-+
-+/* Debugging info only */
-+#undef TITAN_DEBUG
-+
-+/* Keep the rings in the Titan's SSRAM */
-+#define TITAN_RX_RING_IN_SRAM
-+
-+#ifdef CONFIG_64BIT
-+#define	TITAN_GE_IE_MASK	0xfffffffffb001b64
-+#define	TITAN_GE_IE_STATUS	0xfffffffffb001b60
-+#else
-+#define	TITAN_GE_IE_MASK	0xfb001b64
-+#define	TITAN_GE_IE_STATUS	0xfb001b60
-+#endif
-+
-+/* Support for Jumbo Frames */
-+#undef TITAN_GE_JUMBO_FRAMES
-+
-+/* Rx buffer size */
-+#ifdef TITAN_GE_JUMBO_FRAMES
-+#define	TITAN_GE_JUMBO_BUFSIZE	9080
-+#else
-+#define	TITAN_GE_STD_BUFSIZE	1580
-+#endif
-+
-+/*
-+ * Tx and Rx Interrupt Coalescing parameter. These values are
-+ * for 1 Ghz processor. Rx coalescing can be taken care of
-+ * by NAPI. NAPI is adaptive and hence useful. Tx coalescing
-+ * is not adaptive. Hence, these values need to be adjusted
-+ * based on load, CPU speed etc.
-+ */
-+#define	TITAN_GE_RX_COAL	150
-+#define	TITAN_GE_TX_COAL	300
-+
-+#if defined(__BIG_ENDIAN)
-+
-+/* Define the Rx descriptor */
-+typedef struct eth_rx_desc {
-+	u32     reserved;	/* Unused 		*/
-+	u32     buffer_addr;	/* CPU buffer address 	*/
-+	u32	cmd_sts;	/* Command and Status	*/
-+	u32	buffer;		/* XDMA buffer address	*/
-+} titan_ge_rx_desc;
-+
-+/* Define the Tx descriptor */
-+typedef struct eth_tx_desc {
-+	u16     cmd_sts;	/* Command, Status and Buffer count */
-+	u16	buffer_len;	/* Length of the buffer	*/
-+	u32     buffer_addr;	/* Physical address of the buffer */
-+} titan_ge_tx_desc;
-+
-+#elif defined(__LITTLE_ENDIAN)
-+
-+/* Define the Rx descriptor */
-+typedef struct eth_rx_desc {
-+	u32	buffer_addr;	/* CPU buffer address   */
-+	u32	reserved;	/* Unused               */
-+	u32	buffer;		/* XDMA buffer address  */
-+	u32	cmd_sts;	/* Command and Status   */
-+} titan_ge_rx_desc;
-+
-+/* Define the Tx descriptor */
-+typedef struct eth_tx_desc {
-+	u32     buffer_addr;	/* Physical address of the buffer */
-+	u16     buffer_len;     /* Length of the buffer */
-+	u16     cmd_sts;        /* Command, Status and Buffer count */
-+} titan_ge_tx_desc;
-+#endif
-+
-+/* Default Tx Queue Size */
-+#define	TITAN_GE_TX_QUEUE	128
-+#define TITAN_TX_RING_BYTES	(TITAN_GE_TX_QUEUE * sizeof(struct eth_tx_desc))
-+
-+/* Default Rx Queue Size */
-+#define	TITAN_GE_RX_QUEUE	64
-+#define TITAN_RX_RING_BYTES	(TITAN_GE_RX_QUEUE * sizeof(struct eth_rx_desc))
-+
-+/* Packet Structure */
-+typedef struct _pkt_info {
-+	unsigned int           len;
-+	unsigned int            cmd_sts;
-+	unsigned int            buffer;
-+	struct sk_buff          *skb;
-+	unsigned int		checksum;
-+} titan_ge_packet;
-+
-+
-+#define	PHYS_CNT	3
-+
-+/* Titan Port specific data structure */
-+typedef struct _eth_port_ctrl {
-+	unsigned int		port_num;
-+	u8			port_mac_addr[6];
-+
-+	/* Rx descriptor pointers */
-+	int 			rx_curr_desc_q, rx_used_desc_q;
-+
-+	/* Tx descriptor pointers */
-+	int 			tx_curr_desc_q, tx_used_desc_q;
-+
-+	/* Rx descriptor area */
-+	volatile titan_ge_rx_desc	*rx_desc_area;
-+	unsigned int			rx_desc_area_size;
-+	struct sk_buff*			rx_skb[TITAN_GE_RX_QUEUE];
-+
-+	/* Tx Descriptor area */
-+	volatile titan_ge_tx_desc	*tx_desc_area;
-+	unsigned int                    tx_desc_area_size;
-+	struct sk_buff*                 tx_skb[TITAN_GE_TX_QUEUE];
-+
-+	/* Timeout task */
-+	struct work_struct		tx_timeout_task;
-+
-+	/* DMA structures and handles */
-+	dma_addr_t			tx_dma;
-+	dma_addr_t			rx_dma;
-+	dma_addr_t			tx_dma_array[TITAN_GE_TX_QUEUE];
-+
-+	/* Device lock */
-+	spinlock_t			lock;
-+
-+	unsigned int			tx_ring_skbs;
-+	unsigned int			rx_ring_size;
-+	unsigned int			tx_ring_size;
-+	unsigned int			rx_ring_skbs;
-+
-+	struct net_device_stats		stats;
-+
-+	/* Tx and Rx coalescing */
-+	unsigned long			rx_int_coal;
-+	unsigned long			tx_int_coal;
-+
-+	/* Threshold for replenishing the Rx and Tx rings */
-+	unsigned int			tx_threshold;
-+	unsigned int			rx_threshold;
-+
-+	/* NAPI work limit */
-+	unsigned int			rx_work_limit;
-+} titan_ge_port_info;
-+
-+/* Titan specific constants */
-+#define	TITAN_ETH_PORT_IRQ		3
-+
-+/* Max Rx buffer */
-+#define	TITAN_GE_MAX_RX_BUFFER		65536
-+
-+/* Tx and Rx Error */
-+#define	TITAN_GE_ERROR
-+
-+/* Rx Descriptor Command and Status */
-+
-+#define	TITAN_GE_RX_CRC_ERROR		TITAN_BIT27	/* crc error */
-+#define	TITAN_GE_RX_OVERFLOW_ERROR	TITAN_BIT15	/* overflow */
-+#define TITAN_GE_RX_BUFFER_OWNED	TITAN_BIT21	/* buffer ownership */
-+#define	TITAN_GE_RX_STP			TITAN_BIT31	/* start of packet */
-+#define	TITAN_GE_RX_BAM			TITAN_BIT30	/* broadcast address match */
-+#define TITAN_GE_RX_PAM			TITAN_BIT28	/* physical address match */
-+#define TITAN_GE_RX_LAFM		TITAN_BIT29	/* logical address filter match */
-+#define TITAN_GE_RX_VLAN		TITAN_BIT26	/* virtual lans */
-+#define TITAN_GE_RX_PERR		TITAN_BIT19	/* packet error */
-+#define TITAN_GE_RX_TRUNC		TITAN_BIT20	/* packet size greater than 32 buffers */
-+
-+/* Tx Descriptor Command */
-+#define	TITAN_GE_TX_BUFFER_OWNED	TITAN_BIT5	/* buffer ownership */
-+#define	TITAN_GE_TX_ENABLE_INTERRUPT	TITAN_BIT15	/* Interrupt Enable */
-+
-+/* Return Status */
-+#define	TITAN_OK	0x1	/* Good Status */
-+#define	TITAN_ERROR	0x2	/* Error Status */
-+
-+/* MIB specific register offset */
-+#define TITAN_GE_MSTATX_STATS_BASE_LOW       0x0800  /* MSTATX COUNTL[15:0] */
-+#define TITAN_GE_MSTATX_STATS_BASE_MID       0x0804  /* MSTATX COUNTM[15:0] */
-+#define TITAN_GE_MSTATX_STATS_BASE_HI        0x0808  /* MSTATX COUNTH[7:0] */
-+#define TITAN_GE_MSTATX_CONTROL              0x0828  /* MSTATX Control */
-+#define TITAN_GE_MSTATX_VARIABLE_SELECT      0x082C  /* MSTATX Variable Select */
-+
-+/* MIB counter offsets, add to the TITAN_GE_MSTATX_STATS_BASE_XXX */
-+#define TITAN_GE_MSTATX_RXFRAMESOK                   0x0040
-+#define TITAN_GE_MSTATX_RXOCTETSOK                   0x0050
-+#define TITAN_GE_MSTATX_RXFRAMES                     0x0060
-+#define TITAN_GE_MSTATX_RXOCTETS                     0x0070
-+#define TITAN_GE_MSTATX_RXUNICASTFRAMESOK            0x0080
-+#define TITAN_GE_MSTATX_RXBROADCASTFRAMESOK          0x0090
-+#define TITAN_GE_MSTATX_RXMULTICASTFRAMESOK          0x00A0
-+#define TITAN_GE_MSTATX_RXTAGGEDFRAMESOK             0x00B0
-+#define TITAN_GE_MSTATX_RXMACPAUSECONTROLFRAMESOK    0x00C0
-+#define TITAN_GE_MSTATX_RXMACCONTROLFRAMESOK         0x00D0
-+#define TITAN_GE_MSTATX_RXFCSERROR                   0x00E0
-+#define TITAN_GE_MSTATX_RXALIGNMENTERROR             0x00F0
-+#define TITAN_GE_MSTATX_RXSYMBOLERROR                0x0100
-+#define TITAN_GE_MSTATX_RXLAYER1ERROR                0x0110
-+#define TITAN_GE_MSTATX_RXINRANGELENGTHERROR         0x0120
-+#define TITAN_GE_MSTATX_RXLONGLENGTHERROR            0x0130
-+#define TITAN_GE_MSTATX_RXLONGLENGTHCRCERROR         0x0140
-+#define TITAN_GE_MSTATX_RXSHORTLENGTHERROR           0x0150
-+#define TITAN_GE_MSTATX_RXSHORTLLENGTHCRCERROR       0x0160
-+#define TITAN_GE_MSTATX_RXFRAMES64OCTETS             0x0170
-+#define TITAN_GE_MSTATX_RXFRAMES65TO127OCTETS        0x0180
-+#define TITAN_GE_MSTATX_RXFRAMES128TO255OCTETS       0x0190
-+#define TITAN_GE_MSTATX_RXFRAMES256TO511OCTETS       0x01A0
-+#define TITAN_GE_MSTATX_RXFRAMES512TO1023OCTETS      0x01B0
-+#define TITAN_GE_MSTATX_RXFRAMES1024TO1518OCTETS     0x01C0
-+#define TITAN_GE_MSTATX_RXFRAMES1519TOMAXSIZE        0x01D0
-+#define TITAN_GE_MSTATX_RXSTATIONADDRESSFILTERED     0x01E0
-+#define TITAN_GE_MSTATX_RXVARIABLE                   0x01F0
-+#define TITAN_GE_MSTATX_GENERICADDRESSFILTERED       0x0200
-+#define TITAN_GE_MSTATX_UNICASTFILTERED              0x0210
-+#define TITAN_GE_MSTATX_MULTICASTFILTERED            0x0220
-+#define TITAN_GE_MSTATX_BROADCASTFILTERED            0x0230
-+#define TITAN_GE_MSTATX_HASHFILTERED                 0x0240
-+#define TITAN_GE_MSTATX_TXFRAMESOK                   0x0250
-+#define TITAN_GE_MSTATX_TXOCTETSOK                   0x0260
-+#define TITAN_GE_MSTATX_TXOCTETS                     0x0270
-+#define TITAN_GE_MSTATX_TXTAGGEDFRAMESOK             0x0280
-+#define TITAN_GE_MSTATX_TXMACPAUSECONTROLFRAMESOK    0x0290
-+#define TITAN_GE_MSTATX_TXFCSERROR                   0x02A0
-+#define TITAN_GE_MSTATX_TXSHORTLENGTHERROR           0x02B0
-+#define TITAN_GE_MSTATX_TXLONGLENGTHERROR            0x02C0
-+#define TITAN_GE_MSTATX_TXSYSTEMERROR                0x02D0
-+#define TITAN_GE_MSTATX_TXMACERROR                   0x02E0
-+#define TITAN_GE_MSTATX_TXCARRIERSENSEERROR          0x02F0
-+#define TITAN_GE_MSTATX_TXSQETESTERROR               0x0300
-+#define TITAN_GE_MSTATX_TXUNICASTFRAMESOK            0x0310
-+#define TITAN_GE_MSTATX_TXBROADCASTFRAMESOK          0x0320
-+#define TITAN_GE_MSTATX_TXMULTICASTFRAMESOK          0x0330
-+#define TITAN_GE_MSTATX_TXUNICASTFRAMESATTEMPTED     0x0340
-+#define TITAN_GE_MSTATX_TXBROADCASTFRAMESATTEMPTED   0x0350
-+#define TITAN_GE_MSTATX_TXMULTICASTFRAMESATTEMPTED   0x0360
-+#define TITAN_GE_MSTATX_TXFRAMES64OCTETS             0x0370
-+#define TITAN_GE_MSTATX_TXFRAMES65TO127OCTETS        0x0380
-+#define TITAN_GE_MSTATX_TXFRAMES128TO255OCTETS       0x0390
-+#define TITAN_GE_MSTATX_TXFRAMES256TO511OCTETS       0x03A0
-+#define TITAN_GE_MSTATX_TXFRAMES512TO1023OCTETS      0x03B0
-+#define TITAN_GE_MSTATX_TXFRAMES1024TO1518OCTETS     0x03C0
-+#define TITAN_GE_MSTATX_TXFRAMES1519TOMAXSIZE        0x03D0
-+#define TITAN_GE_MSTATX_TXVARIABLE                   0x03E0
-+#define TITAN_GE_MSTATX_RXSYSTEMERROR                0x03F0
-+#define TITAN_GE_MSTATX_SINGLECOLLISION              0x0400
-+#define TITAN_GE_MSTATX_MULTIPLECOLLISION            0x0410
-+#define TITAN_GE_MSTATX_DEFERREDXMISSIONS            0x0420
-+#define TITAN_GE_MSTATX_LATECOLLISIONS               0x0430
-+#define TITAN_GE_MSTATX_ABORTEDDUETOXSCOLLS          0x0440
-+
-+/* Interrupt specific defines */
-+#define TITAN_GE_DEVICE_ID         0x0000  /* Device ID */
-+#define TITAN_GE_RESET             0x0004  /* Reset reg */
-+#define TITAN_GE_TSB_CTRL_0        0x000C  /* TSB Control reg 0 */
-+#define TITAN_GE_TSB_CTRL_1        0x0010  /* TSB Control reg 1 */
-+#define TITAN_GE_INTR_GRP0_STATUS  0x0040  /* General Interrupt Group 0 Status */
-+#define TITAN_GE_INTR_XDMA_CORE_A  0x0048  /* XDMA Channel Interrupt Status, Core A*/
-+#define TITAN_GE_INTR_XDMA_CORE_B  0x004C  /* XDMA Channel Interrupt Status, Core B*/
-+#define	TITAN_GE_INTR_XDMA_IE	   0x0058  /* XDMA Channel Interrupt Enable */
-+#define TITAN_GE_SDQPF_ECC_INTR    0x480C  /* SDQPF ECC Interrupt Status */
-+#define TITAN_GE_SDQPF_RXFIFO_CTL  0x4828  /* SDQPF RxFifo Control and Interrupt Enb*/
-+#define TITAN_GE_SDQPF_RXFIFO_INTR 0x482C  /* SDQPF RxFifo Interrupt Status */
-+#define TITAN_GE_SDQPF_TXFIFO_CTL  0x4928  /* SDQPF TxFifo Control and Interrupt Enb*/
-+#define TITAN_GE_SDQPF_TXFIFO_INTR 0x492C  /* SDQPF TxFifo Interrupt Status */
-+#define	TITAN_GE_SDQPF_RXFIFO_0	   0x4840  /* SDQPF RxFIFO Enable */
-+#define	TITAN_GE_SDQPF_TXFIFO_0	   0x4940  /* SDQPF TxFIFO Enable */
-+#define TITAN_GE_XDMA_CONFIG       0x5000  /* XDMA Global Configuration */
-+#define TITAN_GE_XDMA_INTR_SUMMARY 0x5010  /* XDMA Interrupt Summary */
-+#define TITAN_GE_XDMA_BUFADDRPRE   0x5018  /* XDMA Buffer Address Prefix */
-+#define TITAN_GE_XDMA_DESCADDRPRE  0x501C  /* XDMA Descriptor Address Prefix */
-+#define TITAN_GE_XDMA_PORTWEIGHT   0x502C  /* XDMA Port Weight Configuration */
-+
-+/* Rx MAC defines */
-+#define TITAN_GE_RMAC_CONFIG_1               0x1200  /* RMAC Configuration 1 */
-+#define TITAN_GE_RMAC_CONFIG_2               0x1204  /* RMAC Configuration 2 */
-+#define TITAN_GE_RMAC_MAX_FRAME_LEN          0x1208  /* RMAC Max Frame Length */
-+#define TITAN_GE_RMAC_STATION_HI             0x120C  /* Rx Station Address High */
-+#define TITAN_GE_RMAC_STATION_MID            0x1210  /* Rx Station Address Middle */
-+#define TITAN_GE_RMAC_STATION_LOW            0x1214  /* Rx Station Address Low */
-+#define TITAN_GE_RMAC_LINK_CONFIG            0x1218  /* RMAC Link Configuration */
-+
-+/* Tx MAC defines */
-+#define TITAN_GE_TMAC_CONFIG_1               0x1240  /* TMAC Configuration 1 */
-+#define TITAN_GE_TMAC_CONFIG_2               0x1244  /* TMAC Configuration 2 */
-+#define TITAN_GE_TMAC_IPG                    0x1248  /* TMAC Inter-Packet Gap */
-+#define TITAN_GE_TMAC_STATION_HI             0x124C  /* Tx Station Address High */
-+#define TITAN_GE_TMAC_STATION_MID            0x1250  /* Tx Station Address Middle */
-+#define TITAN_GE_TMAC_STATION_LOW            0x1254  /* Tx Station Address Low */
-+#define TITAN_GE_TMAC_MAX_FRAME_LEN          0x1258  /* TMAC Max Frame Length */
-+#define TITAN_GE_TMAC_MIN_FRAME_LEN          0x125C  /* TMAC Min Frame Length */
-+#define TITAN_GE_TMAC_PAUSE_FRAME_TIME       0x1260  /* TMAC Pause Frame Time */
-+#define TITAN_GE_TMAC_PAUSE_FRAME_INTERVAL   0x1264  /* TMAC Pause Frame Interval */
-+
-+/* GMII register */
-+#define TITAN_GE_GMII_INTERRUPT_STATUS       0x1348  /* GMII Interrupt Status */
-+#define TITAN_GE_GMII_CONFIG_GENERAL         0x134C  /* GMII Configuration General */
-+#define TITAN_GE_GMII_CONFIG_MODE            0x1350  /* GMII Configuration Mode */
-+
-+/* Tx and Rx XDMA defines */
-+#define	TITAN_GE_INT_COALESCING		     0x5030 /* Interrupt Coalescing */
-+#define	TITAN_GE_CHANNEL0_CONFIG	     0x5040 /* Channel 0 XDMA config */
-+#define	TITAN_GE_CHANNEL0_INTERRUPT	     0x504c /* Channel 0 Interrupt Status */
-+#define	TITAN_GE_GDI_INTERRUPT_ENABLE        0x5050 /* IE for the GDI Errors */
-+#define	TITAN_GE_CHANNEL0_PACKET	     0x5060 /* Channel 0 Packet count */
-+#define	TITAN_GE_CHANNEL0_BYTE		     0x5064 /* Channel 0 Byte count */
-+#define	TITAN_GE_CHANNEL0_TX_DESC	     0x5054 /* Channel 0 Tx first desc */
-+#define	TITAN_GE_CHANNEL0_RX_DESC	     0x5058 /* Channel 0 Rx first desc */
-+
-+/* AFX (Address Filter Exact) register offsets for Slice 0 */
-+#define TITAN_GE_AFX_EXACT_MATCH_LOW         0x1100  /* AFX Exact Match Address Low*/
-+#define TITAN_GE_AFX_EXACT_MATCH_MID         0x1104  /* AFX Exact Match Address Mid*/
-+#define TITAN_GE_AFX_EXACT_MATCH_HIGH        0x1108  /* AFX Exact Match Address Hi */
-+#define TITAN_GE_AFX_EXACT_MATCH_VID         0x110C  /* AFX Exact Match VID */
-+#define TITAN_GE_AFX_MULTICAST_HASH_LOW      0x1110  /* AFX Multicast HASH Low */
-+#define TITAN_GE_AFX_MULTICAST_HASH_MIDLOW   0x1114  /* AFX Multicast HASH MidLow */
-+#define TITAN_GE_AFX_MULTICAST_HASH_MIDHI    0x1118  /* AFX Multicast HASH MidHi */
-+#define TITAN_GE_AFX_MULTICAST_HASH_HI       0x111C  /* AFX Multicast HASH Hi */
-+#define TITAN_GE_AFX_ADDRS_FILTER_CTRL_0     0x1120  /* AFX Address Filter Ctrl 0 */
-+#define TITAN_GE_AFX_ADDRS_FILTER_CTRL_1     0x1124  /* AFX Address Filter Ctrl 1 */
-+#define TITAN_GE_AFX_ADDRS_FILTER_CTRL_2     0x1128  /* AFX Address Filter Ctrl 2 */
-+
-+/* Traffic Groomer block */
-+#define        TITAN_GE_TRTG_CONFIG	     0x1000  /* TRTG Config */
-+
-+#endif 				/* _TITAN_GE_H_ */
-+
-diff --git a/drivers/net/titan_mdio.c b/drivers/net/titan_mdio.c
-new file mode 100644
-index 0000000..8a8785b
---- /dev/null
-+++ b/drivers/net/titan_mdio.c
-@@ -0,0 +1,217 @@
-+/*
-+ * drivers/net/titan_mdio.c - Driver for Titan ethernet ports
-+ *
-+ * Copyright (C) 2003 PMC-Sierra Inc.
-+ * Author : Manish Lachwani (lachwani@pmc-sierra.com)
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2
-+ * of the License, or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-+ *
-+ * Management Data IO (MDIO) driver for the Titan GMII. Interacts with the Marvel PHY
-+ * on the Titan. No support for the TBI as yet.
-+ *
-+ */
-+
-+#include	"titan_mdio.h"
-+
-+#define MDIO_DEBUG
-+
-+/*
-+ * Local constants
-+ */
-+#define MAX_CLKA            1023
-+#define MAX_PHY_DEV         31
-+#define MAX_PHY_REG         31
-+#define WRITEADDRS_OPCODE   0x0
-+#define	READ_OPCODE	    0x2
-+#define WRITE_OPCODE        0x1
-+#define MAX_MDIO_POLL       100
-+
-+/*
-+ * Titan MDIO and SCMB registers
-+ */
-+#define TITAN_GE_SCMB_CONTROL                0x01c0  /* SCMB Control */
-+#define TITAN_GE_SCMB_CLKA	             0x01c4  /* SCMB Clock A */
-+#define TITAN_GE_MDIO_COMMAND                0x01d0  /* MDIO Command */
-+#define TITAN_GE_MDIO_DEVICE_PORT_ADDRESS    0x01d4  /* MDIO Device and Port addrs */
-+#define TITAN_GE_MDIO_DATA                   0x01d8  /* MDIO Data */
-+#define TITAN_GE_MDIO_INTERRUPTS             0x01dC  /* MDIO Interrupts */
-+
-+/*
-+ * Function to poll the MDIO
-+ */
-+static int titan_ge_mdio_poll(void)
-+{
-+	int	i, val;
-+
-+	for (i = 0; i < MAX_MDIO_POLL; i++) {
-+		val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND);
-+
-+		if (!(val & 0x8000))
-+			return TITAN_GE_MDIO_GOOD;
-+	}
-+
-+	return TITAN_GE_MDIO_ERROR;
-+}
-+
-+
-+/*
-+ * Initialize and configure the MDIO
-+ */
-+int titan_ge_mdio_setup(titan_ge_mdio_config *titan_mdio)
-+{
-+	unsigned long	val;
-+
-+	/* Reset the SCMB and program into MDIO mode*/
-+	TITAN_GE_MDIO_WRITE(TITAN_GE_SCMB_CONTROL, 0x9000);
-+	TITAN_GE_MDIO_WRITE(TITAN_GE_SCMB_CONTROL, 0x1000);
-+
-+	/* CLK A */
-+	val = TITAN_GE_MDIO_READ(TITAN_GE_SCMB_CLKA);
-+	val = ( (val & ~(0x03ff)) | (titan_mdio->clka & 0x03ff));
-+	TITAN_GE_MDIO_WRITE(TITAN_GE_SCMB_CLKA, val);
-+
-+	/* Preamble Suppresion */
-+	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND);
-+	val = ( (val & ~(0x0001)) | (titan_mdio->mdio_spre & 0x0001));
-+	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val);
-+
-+	/* MDIO mode */
-+	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS);
-+	val = ( (val & ~(0x4000)) | (titan_mdio->mdio_mode & 0x4000));
-+	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val);
-+
-+	return TITAN_GE_MDIO_GOOD;
-+}
-+
-+/*
-+ * Set the PHY address in indirect mode
-+ */
-+int titan_ge_mdio_inaddrs(int dev_addr, int reg_addr)
-+{
-+	volatile unsigned long	val;
-+
-+	/* Setup the PHY device */
-+	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS);
-+	val = ( (val & ~(0x1f00)) | ( (dev_addr << 8) & 0x1f00));
-+	val = ( (val & ~(0x001f)) | ( reg_addr & 0x001f));
-+	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val);
-+
-+	/* Write the new address */
-+	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND);
-+	val = ( (val & ~(0x0300)) | ( (WRITEADDRS_OPCODE << 8) & 0x0300));
-+	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val);
-+
-+	return TITAN_GE_MDIO_GOOD;
-+}
-+
-+/*
-+ * Read the MDIO register. This is what the individual parametes mean:
-+ *
-+ * dev_addr : PHY ID
-+ * reg_addr : register offset
-+ *
-+ * See the spec for the Titan MAC. We operate in the Direct Mode.
-+ */
-+
-+#define MAX_RETRIES	2
-+
-+int titan_ge_mdio_read(int dev_addr, int reg_addr, unsigned int *pdata)
-+{
-+	volatile unsigned long	val;
-+	int retries = 0;
-+
-+	/* Setup the PHY device */
-+
-+again:
-+	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS);
-+	val = ( (val & ~(0x1f00)) | ( (dev_addr << 8) & 0x1f00));
-+	val = ( (val & ~(0x001f)) | ( reg_addr & 0x001f));
-+	val |= 0x4000;
-+	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val);
-+
-+	udelay(30);
-+
-+	/* Issue the read command */
-+	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND);
-+	val = ( (val & ~(0x0300)) | ( (READ_OPCODE << 8) & 0x0300));
-+	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val);
-+
-+	udelay(30);
-+
-+	if (titan_ge_mdio_poll() != TITAN_GE_MDIO_GOOD)
-+		return TITAN_GE_MDIO_ERROR;
-+
-+	*pdata = (unsigned int)TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DATA);
-+	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_INTERRUPTS);
-+
-+	udelay(30);
-+
-+	if (val & 0x2) {
-+		if (retries == MAX_RETRIES)
-+			return TITAN_GE_MDIO_ERROR;
-+		else {
-+			retries++;
-+			goto again;
-+		}
-+	}
-+
-+	return TITAN_GE_MDIO_GOOD;
-+}
-+
-+/*
-+ * Write to the MDIO register
-+ *
-+ * dev_addr : PHY ID
-+ * reg_addr : register that needs to be written to
-+ *
-+ */
-+int titan_ge_mdio_write(int dev_addr, int reg_addr, unsigned int data)
-+{
-+	volatile unsigned long	val;
-+
-+	if (titan_ge_mdio_poll() != TITAN_GE_MDIO_GOOD)
-+		return TITAN_GE_MDIO_ERROR;
-+
-+	/* Setup the PHY device */
-+	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS);
-+	val = ( (val & ~(0x1f00)) | ( (dev_addr << 8) & 0x1f00));
-+	val = ( (val & ~(0x001f)) | ( reg_addr & 0x001f));
-+	val |= 0x4000;
-+	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val);
-+
-+	udelay(30);
-+
-+	/* Setup the data to write */
-+	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DATA, data);
-+
-+	udelay(30);
-+
-+	/* Issue the write command */
-+	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND);
-+	val = ( (val & ~(0x0300)) | ( (WRITE_OPCODE << 8) & 0x0300));
-+	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val);
-+
-+	udelay(30);
-+
-+	if (titan_ge_mdio_poll() != TITAN_GE_MDIO_GOOD)
-+		return TITAN_GE_MDIO_ERROR;
-+
-+	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_INTERRUPTS);
-+	if (val & 0x2)
-+		return TITAN_GE_MDIO_ERROR;
-+
-+	return TITAN_GE_MDIO_GOOD;
-+}
-+
-diff --git a/drivers/net/titan_mdio.h b/drivers/net/titan_mdio.h
-new file mode 100644
-index 0000000..5d23344
---- /dev/null
-+++ b/drivers/net/titan_mdio.h
-@@ -0,0 +1,56 @@
-+/*
-+ * MDIO used to interact with the PHY when using GMII/MII
-+ */
-+#ifndef _TITAN_MDIO_H
-+#define _TITAN_MDIO_H
-+
-+#include <linux/netdevice.h>
-+#include <linux/workqueue.h>
-+#include <linux/delay.h>
-+#include "titan_ge.h"
-+
-+
-+#define	TITAN_GE_MDIO_ERROR	(-9000)
-+#define	TITAN_GE_MDIO_GOOD	0
-+
-+#define	TITAN_GE_MDIO_BASE		titan_ge_base
-+
-+#define	TITAN_GE_MDIO_READ(offset)	\
-+	*(volatile u32 *)(titan_ge_base + (offset))
-+
-+#define	TITAN_GE_MDIO_WRITE(offset, data)	\
-+	*(volatile u32 *)(titan_ge_base + (offset)) = (data)
-+
-+
-+/* GMII specific registers */
-+#define	TITAN_GE_MARVEL_PHY_ID		0x00
-+#define	TITAN_PHY_AUTONEG_ADV		0x04
-+#define	TITAN_PHY_LP_ABILITY		0x05
-+#define	TITAN_GE_MDIO_MII_CTRL		0x09
-+#define	TITAN_GE_MDIO_MII_EXTENDED	0x0f
-+#define	TITAN_GE_MDIO_PHY_CTRL		0x10
-+#define	TITAN_GE_MDIO_PHY_STATUS	0x11
-+#define	TITAN_GE_MDIO_PHY_IE		0x12
-+#define	TITAN_GE_MDIO_PHY_IS		0x13
-+#define	TITAN_GE_MDIO_PHY_LED		0x18
-+#define	TITAN_GE_MDIO_PHY_LED_OVER	0x19
-+#define	PHY_ANEG_TIME_WAIT		45	/* 45 seconds wait time */
-+
-+/*
-+ * MDIO Config Structure
-+ */
-+typedef struct {
-+	unsigned int		clka;
-+	int			mdio_spre;
-+	int			mdio_mode;
-+} titan_ge_mdio_config;
-+
-+/*
-+ * Function Prototypes
-+ */
-+int titan_ge_mdio_setup(titan_ge_mdio_config *);
-+int titan_ge_mdio_inaddrs(int, int);
-+int titan_ge_mdio_read(int, int, unsigned int *);
-+int titan_ge_mdio_write(int, int, unsigned int);
-+
-+#endif /* _TITAN_MDIO_H */
-diff --git a/drivers/net/wireless/rtl818x/rtl8187/rfkill.c b/drivers/net/wireless/rtl818x/rtl8187/rfkill.c
-index 3411671..4d252c1 100644
---- a/drivers/net/wireless/rtl818x/rtl8187/rfkill.c
-+++ b/drivers/net/wireless/rtl818x/rtl8187/rfkill.c
-@@ -22,6 +22,10 @@
- 
- static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv)
- {
-+#ifdef CONFIG_LEMOTE_MACH2F
-+	/* Allow users to activate rfkill through only the /sys interface */
-+	return 1;
-+#else
- 	u8 gpio;
- 
- 	gpio = rtl818x_ioread8(priv, &priv->map->GPIO0);
-@@ -29,6 +33,7 @@ static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv)
- 	gpio = rtl818x_ioread8(priv, &priv->map->GPIO1);
- 
- 	return gpio & priv->rfkill_mask;
-+#endif
- }
- 
- void rtl8187_rfkill_init(struct ieee80211_hw *hw)
-diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
-index 09fde58..eacabd1 100644
---- a/drivers/platform/Kconfig
-+++ b/drivers/platform/Kconfig
-@@ -4,5 +4,8 @@ endif
- if GOLDFISH
- source "drivers/platform/goldfish/Kconfig"
- endif
-+if MIPS
-+source "drivers/platform/mips/Kconfig"
-+endif
- 
- source "drivers/platform/chrome/Kconfig"
-diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
-index 3656b7b..ca26925 100644
---- a/drivers/platform/Makefile
-+++ b/drivers/platform/Makefile
-@@ -3,6 +3,7 @@
- #
- 
- obj-$(CONFIG_X86)		+= x86/
-+obj-$(CONFIG_MIPS)		+= mips/
- obj-$(CONFIG_OLPC)		+= olpc/
- obj-$(CONFIG_GOLDFISH)		+= goldfish/
- obj-$(CONFIG_CHROME_PLATFORMS)	+= chrome/
-diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
-new file mode 100644
-index 0000000..722d690
---- /dev/null
-+++ b/drivers/platform/mips/Kconfig
-@@ -0,0 +1,60 @@
-+#
-+# MIPS Platform Specific Drivers
-+#
-+
-+menuconfig MIPS_PLATFORM_DEVICES
-+	bool "MIPS Platform Specific Device Drivers"
-+	default y
-+	help
-+	  Say Y here to get to see options for device drivers of various
-+	  MIPS platforms, including vendor-specific netbook/laptop/pc extension
-+	  drivers.  This option alone does not add any kernel code.
-+
-+	  If you say N, all options in this submenu will be skipped and disabled.
-+
-+if MIPS_PLATFORM_DEVICES
-+
-+config LEMOTE_YEELOONG2F
-+	tristate "Lemote YeeLoong Laptop"
-+	depends on LEMOTE_MACH2F
-+	select BACKLIGHT_LCD_SUPPORT
-+	select LCD_CLASS_DEVICE
-+	select BACKLIGHT_CLASS_DEVICE
-+	select POWER_SUPPLY
-+	select HWMON
-+	select VIDEO_OUTPUT_CONTROL
-+	select INPUT_SPARSEKMAP
-+	select INPUT_EVDEV
-+	depends on INPUT
-+	default m
-+	help
-+	  YeeLoong netbook is a mini laptop made by Lemote, which is basically
-+	  compatible to FuLoong2F mini PC, but it has an extra Embedded
-+	  Controller(kb3310b) for battery, hotkey, backlight, temperature and
-+	  fan management.
-+
-+config LEMOTE_LYNLOONG2F
-+	tristate "Lemote LynLoong PC"
-+	depends on LEMOTE_MACH2F
-+	select BACKLIGHT_LCD_SUPPORT
-+	select BACKLIGHT_CLASS_DEVICE
-+	select VIDEO_OUTPUT_CONTROL
-+	default m
-+	help
-+	  LynLoong PC is an AllINONE machine made by Lemote, which is basically
-+	  compatible to FuLoong2F Mini PC, the only difference is that it has a
-+	  size-fixed screen: 1360x768 with sisfb video driver. and also, it has
-+	  its own specific suspend support.
-+
-+config GDIUM_LAPTOP
-+	tristate "GDIUM laptop extras"
-+	depends on DEXXON_GDIUM
-+	select POWER_SUPPLY
-+	select I2C
-+	select INPUT_POLLDEV
-+	default m
-+	help
-+	  This mini-driver drives the ST7 chipset present in the Gdium laptops.
-+	  This gives battery support, wlan rfkill.
-+
-+endif # MIPS_PLATFORM_DEVICES
-diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile
-new file mode 100644
-index 0000000..f013e78
---- /dev/null
-+++ b/drivers/platform/mips/Makefile
-@@ -0,0 +1,9 @@
-+#
-+# Makefile for MIPS Platform-Specific Drivers
-+#
-+
-+obj-$(CONFIG_LEMOTE_YEELOONG2F)	+= yeeloong_laptop.o # yeeloong_ecrom.o
-+CFLAGS_yeeloong_laptop.o = -I$(srctree)/arch/mips/loongson/lemote-2f
-+
-+obj-$(CONFIG_LEMOTE_LYNLOONG2F)	+= lynloong_pc.o
-+obj-$(CONFIG_GDIUM_LAPTOP)	+= gdium_laptop.o
-diff --git a/drivers/platform/mips/gdium_laptop.c b/drivers/platform/mips/gdium_laptop.c
-new file mode 100644
-index 0000000..41a65ad
---- /dev/null
-+++ b/drivers/platform/mips/gdium_laptop.c
-@@ -0,0 +1,927 @@
-+/*
-+ * gdium_laptop  --  Gdium laptop extras
-+ *
-+ * Arnaud Patard <apatard@mandriva.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/init.h>
-+#include <linux/platform_device.h>
-+#include <linux/input-polldev.h>
-+#include <linux/debugfs.h>
-+#include <linux/seq_file.h>
-+#include <linux/i2c.h>
-+#include <linux/mutex.h>
-+#include <linux/power_supply.h>
-+#include <linux/workqueue.h>
-+#include <linux/delay.h>
-+#include <linux/slab.h>
-+#include <asm/gpio.h>
-+
-+/* For input device */
-+#define SCAN_INTERVAL		150
-+
-+/* For battery status */
-+#define BAT_SCAN_INTERVAL	500
-+
-+#define EC_FIRM_VERSION		0
-+
-+#if CONFIG_GDIUM_VERSION > 2
-+#define EC_REG_BASE		1
-+#else
-+#define EC_REG_BASE		0
-+#endif
-+
-+#define EC_STATUS		(EC_REG_BASE+0)
-+#define EC_STATUS_LID		(1<<0)
-+#define EC_STATUS_PWRBUT	(1<<1)
-+#define EC_STATUS_BATID		(1<<2)		/* this bit has no real meaning on v2.         */
-+						/* Same as EC_STATUS_ADAPT                     */
-+						/* but on v3 it's BATID which mean bat present */
-+#define EC_STATUS_SYS_POWER	(1<<3)
-+#define EC_STATUS_WLAN		(1<<4)
-+#define EC_STATUS_ADAPT		(1<<5)
-+
-+#define EC_CTRL			(EC_REG_BASE+1)
-+#define EC_CTRL_DDR_CLK		(1<<0)
-+#define EC_CTRL_CHARGE_LED	(1<<1)
-+#define EC_CTRL_BEEP		(1<<2)
-+#define EC_CTRL_SUSB		(1<<3)	/* memory power */
-+#define EC_CTRL_TRICKLE		(1<<4)
-+#define EC_CTRL_WLAN_EN		(1<<5)
-+#define EC_CTRL_SUSC		(1<<6) /* main power */
-+#define EC_CTRL_CHARGE_EN	(1<<7)
-+
-+#define EC_BAT_LOW		(EC_REG_BASE+2)
-+#define EC_BAT_HIGH		(EC_REG_BASE+3)
-+
-+#define EC_SIGN			(EC_REG_BASE+4)
-+#define EC_SIGN_OS		0xAE /* write 0xae to control pm stuff */
-+#define EC_SIGN_EC		0x00 /* write 0x00 to let the st7 manage pm stuff */
-+
-+#if 0
-+#define EC_TEST			(EC_REG_BASE+5) /* Depending on firmware version this register */
-+						/* may be the programmation register so don't play */
-+						/* with it */
-+#endif
-+
-+#define BAT_VOLT_PRESENT	500000	/* Min voltage to consider battery present uV */
-+#define BAT_MIN			7000000	/* Min battery voltage in uV */
-+#define BAT_MIN_MV		7000	/* Min battery voltage in mV */
-+#define BAT_TRICKLE_EN		8000000	/* Charging at 1.4A before  8.0V and then charging at 0.25A */
-+#define BAT_MAX			7950000	/* Max battery voltage ~8V in V */
-+#define BAT_MAX_MV		7950	/* Max battery voltage ~8V in V */
-+#define BAT_READ_ERROR		300000	/* battery read error of 0.3V */
-+#define BAT_READ_ERROR_MV	300	/* battery read error of 0.3V */
-+
-+#define SM502_WLAN_ON		(224+16)/* SM502 GPIO16 may be used on gdium v2 (v3?) as wlan_on */
-+					/* when R422 is connected */
-+
-+static unsigned char verbose;
-+static unsigned char gpio16;
-+static unsigned char ec;
-+module_param(verbose, byte, S_IRUGO | S_IWUSR);
-+MODULE_PARM_DESC(verbose, "Add some debugging messages");
-+module_param(gpio16, byte, S_IRUGO);
-+MODULE_PARM_DESC(gpio16, "Enable wlan_on signal on SM502");
-+module_param(ec, byte, S_IRUGO);
-+MODULE_PARM_DESC(ec, "Let the ST7 handle the battery (default OS)");
-+
-+struct gdium_laptop_data {
-+	struct i2c_client		*client;
-+	struct input_polled_dev		*input_polldev;
-+	struct dentry			*debugfs;
-+	struct mutex			mutex;
-+	struct platform_device		*bat_pdev;
-+	struct power_supply		gdium_ac;
-+	struct power_supply		gdium_battery;
-+	struct workqueue_struct		*workqueue;
-+	struct delayed_work		work;
-+	char				charge_cmd;
-+	/* important registers value */
-+	char				status;
-+	char				ctrl;
-+	/* mV */
-+	int				battery_level;
-+	char				version;
-+};
-+
-+/**********************************************************************/
-+/* Low level I2C functions                                            */
-+/* All are supposed to be called with mutex held                      */
-+/**********************************************************************/
-+/*
-+ * Return battery voltage in mV
-+ * >= 0 battery voltage
-+ * < 0 error
-+ */
-+static s32 ec_read_battery(struct i2c_client *client)
-+{
-+	unsigned char bat_low, bat_high;
-+	s32 data;
-+	unsigned int ret;
-+
-+	/*
-+	 * a = battery high
-+	 * b = battery low
-+	 * bat = a << 2 | b & 0x03;
-+	 * battery voltage = (bat / 1024) * 5 * 2
-+	 */
-+	data = i2c_smbus_read_byte_data(client, EC_BAT_LOW);
-+	if (data < 0) {
-+		dev_err(&client->dev, "ec_read_bat: read bat_low failed\n");
-+		return data;
-+	}
-+	bat_low = data & 0xff;
-+	if (verbose)
-+		dev_info(&client->dev, "bat_low %x\n", bat_low);
-+
-+	data = i2c_smbus_read_byte_data(client, EC_BAT_HIGH);
-+	if (data < 0) {
-+		dev_err(&client->dev, "ec_read_bat: read bat_high failed\n");
-+		return data;
-+	}
-+	bat_high = data & 0xff;
-+	if (verbose)
-+		dev_info(&client->dev, "bat_high %x\n", bat_high);
-+
-+	ret = (bat_high << 2) | (bat_low & 3);
-+	/*
-+	 * mV
-+	 */
-+	ret = (ret * 5 * 2) * 1000 / 1024;
-+
-+	return ret;
-+}
-+
-+static s32 ec_read_version(struct i2c_client *client)
-+{
-+#if CONFIG_GDIUM_VERSION > 2
-+	return i2c_smbus_read_byte_data(client, EC_FIRM_VERSION);
-+#else
-+	return 0;
-+#endif
-+}
-+
-+static s32 ec_read_status(struct i2c_client *client)
-+{
-+	return i2c_smbus_read_byte_data(client, EC_STATUS);
-+}
-+
-+static s32 ec_read_ctrl(struct i2c_client *client)
-+{
-+	return i2c_smbus_read_byte_data(client, EC_CTRL);
-+}
-+
-+static s32 ec_write_ctrl(struct i2c_client *client, unsigned char newvalue)
-+{
-+	return i2c_smbus_write_byte_data(client, EC_CTRL, newvalue);
-+}
-+
-+static s32 ec_read_sign(struct i2c_client *client)
-+{
-+	return i2c_smbus_read_byte_data(client, EC_SIGN);
-+}
-+
-+static s32 ec_write_sign(struct i2c_client *client, unsigned char sign)
-+{
-+	unsigned char value;
-+	s32 ret;
-+
-+	ret = i2c_smbus_write_byte_data(client, EC_SIGN, sign);
-+	if (ret < 0) {
-+		dev_err(&client->dev, "ec_set_control: write failed\n");
-+		return ret;
-+	}
-+
-+	value = ec_read_sign(client);
-+	if (value != sign) {
-+		dev_err(&client->dev, "Failed to set control to %s\n",
-+				sign == EC_SIGN_OS ? "OS" : "EC");
-+		return -EIO;
-+	}
-+
-+	return 0;
-+}
-+
-+#if 0
-+static int ec_power_off(struct i2c_client *client)
-+{
-+	char value;
-+	int ret;
-+
-+	value = ec_read_ctrl(client);
-+	if (value < 0) {
-+		dev_err(&client->dev, "ec_power_off: read failed\n");
-+		return value;
-+	}
-+	value &= ~(EC_CTRL_SUSB | EC_CTRL_SUSC);
-+	ret = ec_write_ctrl(client, value);
-+	if (ret < 0) {
-+		dev_err(&client->dev, "ec_power_off: write failed\n");
-+		return ret;
-+	}
-+
-+	return 0;
-+}
-+#endif
-+
-+static s32 ec_wlan_status(struct i2c_client *client)
-+{
-+	s32 value;
-+
-+	value = ec_read_ctrl(client);
-+	if (value < 0)
-+		return value;
-+
-+	return (value & EC_CTRL_WLAN_EN) ? 1 : 0;
-+}
-+
-+static s32 ec_wlan_en(struct i2c_client *client, int on)
-+{
-+	s32 value;
-+
-+	value = ec_read_ctrl(client);
-+	if (value < 0)
-+		return value;
-+
-+	value &= ~EC_CTRL_WLAN_EN;
-+	if (on)
-+		value |= EC_CTRL_WLAN_EN;
-+
-+	return ec_write_ctrl(client, value&0xff);
-+}
-+
-+#if 0
-+static s32 ec_led_status(struct i2c_client *client)
-+{
-+	s32 value;
-+
-+	value = ec_read_ctrl(client);
-+	if (value < 0)
-+		return value;
-+
-+	return (value & EC_CTRL_CHARGE_LED) ? 1 : 0;
-+}
-+#endif
-+
-+/* Changing the charging led status has never worked */
-+static s32 ec_led_en(struct i2c_client *client, int on)
-+{
-+#if 0
-+	s32 value;
-+
-+	value = ec_read_ctrl(client);
-+	if (value < 0)
-+		return value;
-+
-+	value &= ~EC_CTRL_CHARGE_LED;
-+	if (on)
-+		value |= EC_CTRL_CHARGE_LED;
-+	return ec_write_ctrl(client, value&0xff);
-+#else
-+	return 0;
-+#endif
-+}
-+
-+static s32 ec_charge_en(struct i2c_client *client, int on, int trickle)
-+{
-+	s32 value;
-+	s32 set = 0;
-+
-+	value = ec_read_ctrl(client);
-+	if (value < 0)
-+		return value;
-+
-+	if (on)
-+		set |= EC_CTRL_CHARGE_EN;
-+	if (trickle)
-+		set |= EC_CTRL_TRICKLE;
-+
-+	/* Be clever : don't change values if you don't need to */
-+	if ((value & (EC_CTRL_CHARGE_EN | EC_CTRL_TRICKLE)) == set)
-+		return 0;
-+
-+	value &= ~(EC_CTRL_CHARGE_EN | EC_CTRL_TRICKLE);
-+	value |= set;
-+	ec_led_en(client, on);
-+	return ec_write_ctrl(client, (unsigned char)(value&0xff));
-+
-+}
-+
-+/**********************************************************************/
-+/* Input functions                                                    */
-+/**********************************************************************/
-+struct gdium_keys {
-+	int last_state;
-+	int key_code;
-+	int mask;
-+	int type;
-+};
-+
-+static struct gdium_keys gkeys[] = {
-+	{
-+		.key_code	= KEY_WLAN,
-+		.mask		= EC_STATUS_WLAN,
-+		.type		= EV_KEY,
-+	},
-+	{
-+		.key_code	= KEY_POWER,
-+		.mask		= EC_STATUS_PWRBUT,
-+		.type		= EV_KEY, /*EV_PWR,*/
-+	},
-+	{
-+		.key_code	= SW_LID,
-+		.mask		= EC_STATUS_LID,
-+		.type		= EV_SW,
-+	},
-+};
-+
-+static void gdium_laptop_keys_poll(struct input_polled_dev *dev)
-+{
-+	int state, i;
-+	struct gdium_laptop_data *data = dev->private;
-+	struct i2c_client *client = data->client;
-+	struct input_dev *input = dev->input;
-+	s32 status;
-+
-+	mutex_lock(&data->mutex);
-+	status = ec_read_status(client);
-+	mutex_unlock(&data->mutex);
-+
-+	if (status < 0) {
-+		/*
-+		 * Don't know exactly  which version of the firmware
-+		 * has this bug but when the power button is pressed
-+		 * there are i2c read errors :(
-+		 */
-+		if ((data->version >= 0x13) && !gkeys[1].last_state) {
-+			input_event(input, EV_KEY, KEY_POWER, 1);
-+			input_sync(input);
-+			gkeys[1].last_state = 1;
-+		}
-+		return;
-+	}
-+
-+	for (i = 0; i < ARRAY_SIZE(gkeys); i++) {
-+		state = status & gkeys[i].mask;
-+		if (state != gkeys[i].last_state) {
-+			gkeys[i].last_state = state;
-+			/* for power key, we want power & key press/release event */
-+			if (gkeys[i].type == EV_PWR) {
-+				input_event(input, EV_KEY, gkeys[i].key_code, !!state);
-+				input_sync(input);
-+			}
-+			/* Disable wifi on key press but not key release */
-+			/*
-+			 * On firmware >= 0x13 the EC_STATUS_WLAN has it's
-+			 * original meaning of Wifi status and no more the
-+			 * wifi button status so we have to ignore the event
-+			 * on theses versions
-+			 */
-+			if (state && (gkeys[i].key_code == KEY_WLAN) && (data->version < 0x13)) {
-+				mutex_lock(&data->mutex);
-+				ec_wlan_en(client, !ec_wlan_status(client));
-+				if (gpio16)
-+					gpio_set_value(SM502_WLAN_ON, !ec_wlan_status(client));
-+				mutex_unlock(&data->mutex);
-+			}
-+
-+			input_event(input, gkeys[i].type, gkeys[i].key_code, !!state);
-+			input_sync(input);
-+		}
-+	}
-+}
-+
-+static int gdium_laptop_input_init(struct gdium_laptop_data *data)
-+{
-+	struct i2c_client *client = data->client;
-+	struct input_dev *input;
-+	int ret, i;
-+
-+	data->input_polldev = input_allocate_polled_device();
-+	if (!data->input_polldev) {
-+		ret = -ENOMEM;
-+		goto err;
-+	}
-+
-+	input = data->input_polldev->input;
-+	input->evbit[0] = BIT(EV_KEY) | BIT_MASK(EV_PWR) | BIT_MASK(EV_SW);
-+	data->input_polldev->poll = gdium_laptop_keys_poll;
-+	data->input_polldev->poll_interval = SCAN_INTERVAL;
-+	data->input_polldev->private = data;
-+	input->name = "gdium-keys";
-+	input->dev.parent = &client->dev;
-+
-+	input->id.bustype = BUS_HOST;
-+	input->id.vendor = 0x0001;
-+	input->id.product = 0x0001;
-+	input->id.version = 0x0100;
-+
-+	for (i = 0; i < ARRAY_SIZE(gkeys); i++)
-+		input_set_capability(input, gkeys[i].type, gkeys[i].key_code);
-+
-+	ret = input_register_polled_device(data->input_polldev);
-+	if (ret) {
-+		dev_err(&client->dev, "Unable to register button device\n");
-+		goto err_poll_dev;
-+	}
-+
-+	return 0;
-+
-+err_poll_dev:
-+	input_free_polled_device(data->input_polldev);
-+err:
-+	return ret;
-+}
-+
-+static void gdium_laptop_input_exit(struct gdium_laptop_data *data)
-+{
-+	input_unregister_polled_device(data->input_polldev);
-+	input_free_polled_device(data->input_polldev);
-+}
-+
-+/**********************************************************************/
-+/* Battery management                                                 */
-+/**********************************************************************/
-+static int gdium_ac_get_props(struct power_supply *psy,
-+		enum power_supply_property psp,
-+		union power_supply_propval *val)
-+{
-+	char status;
-+	struct gdium_laptop_data *data = container_of(psy, struct gdium_laptop_data, gdium_ac);
-+	int ret = 0;
-+
-+	if (!data) {
-+		pr_err("gdium-ac: gdium_laptop_data not found\n");
-+		return -EINVAL;
-+	}
-+
-+	status = data->status;
-+	switch (psp) {
-+	case POWER_SUPPLY_PROP_ONLINE:
-+		val->intval = !!(status & EC_STATUS_ADAPT);
-+		break;
-+	default:
-+		ret = -EINVAL;
-+		break;
-+	}
-+
-+	return ret;
-+}
-+
-+#undef RET
-+#define RET (val->intval)
-+
-+static int gdium_battery_get_props(struct power_supply *psy,
-+		enum power_supply_property psp,
-+		union power_supply_propval *val)
-+{
-+	char status, ctrl;
-+	struct gdium_laptop_data *data = container_of(psy, struct gdium_laptop_data, gdium_battery);
-+	int percentage_capacity = 0, charge_now = 0, time_to_empty = 0;
-+	int ret = 0, tmp;
-+
-+	if (!data) {
-+		pr_err("gdium-battery: gdium_laptop_data not found\n");
-+		return -EINVAL;
-+	}
-+
-+	status = data->status;
-+	ctrl   = data->ctrl;
-+	switch (psp) {
-+	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-+		/* uAh */
-+		RET = 5000000;
-+		break;
-+	case POWER_SUPPLY_PROP_CURRENT_NOW:
-+	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
-+		/* This formula is gotten by gnuplot with the statistic data */
-+		time_to_empty = (data->battery_level - BAT_MIN_MV + BAT_READ_ERROR_MV) * 113 - 29870;
-+		if (psp == POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW) {
-+			/* seconds */
-+			RET = time_to_empty / 10;
-+			break;
-+		}
-+		/* fall through */
-+	case POWER_SUPPLY_PROP_CHARGE_NOW:
-+	case POWER_SUPPLY_PROP_CAPACITY: {
-+		tmp = data->battery_level * 1000;
-+		/* > BAT_MIN to avoid negative values */
-+		percentage_capacity = 0;
-+		if ((status & EC_STATUS_BATID) && (tmp > BAT_MIN))
-+			percentage_capacity = (tmp-BAT_MIN)*100/(BAT_MAX-BAT_MIN);
-+
-+		if (percentage_capacity > 100)
-+			percentage_capacity = 100;
-+
-+		if (psp == POWER_SUPPLY_PROP_CAPACITY) {
-+			RET = percentage_capacity;
-+			break;
-+		}
-+		charge_now = 50000 * percentage_capacity;
-+		if (psp == POWER_SUPPLY_PROP_CHARGE_NOW) {
-+			/* uAh */
-+			RET = charge_now;
-+			break;
-+		}
-+	}	/* fall through */
-+	case POWER_SUPPLY_PROP_STATUS: {
-+		if (status & EC_STATUS_ADAPT)
-+			if (ctrl & EC_CTRL_CHARGE_EN)
-+				RET = POWER_SUPPLY_STATUS_CHARGING;
-+			else
-+				RET = POWER_SUPPLY_STATUS_NOT_CHARGING;
-+		else
-+			RET = POWER_SUPPLY_STATUS_DISCHARGING;
-+
-+		if (psp == POWER_SUPPLY_PROP_STATUS)
-+			break;
-+		/* mAh -> µA */
-+		switch (RET) {
-+		case POWER_SUPPLY_STATUS_CHARGING:
-+			RET = -(data->charge_cmd == 2) ? 1400000 : 250000;
-+			break;
-+		case POWER_SUPPLY_STATUS_DISCHARGING:
-+			RET = charge_now / time_to_empty * 36000;
-+			break;
-+		case POWER_SUPPLY_STATUS_NOT_CHARGING:
-+		default:
-+			RET = 0;
-+			break;
-+		}
-+	} break;
-+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-+		RET = BAT_MAX+BAT_READ_ERROR;
-+		break;
-+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-+		RET = BAT_MIN-BAT_READ_ERROR;
-+		break;
-+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-+		/* mV -> uV */
-+		RET = data->battery_level * 1000;
-+		break;
-+	case POWER_SUPPLY_PROP_PRESENT:
-+#if CONFIG_GDIUM_VERSION > 2
-+		RET = !!(status & EC_STATUS_BATID);
-+#else
-+		RET = !!(data->battery_level > BAT_VOLT_PRESENT);
-+#endif
-+		break;
-+	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
-+		tmp = data->battery_level * 1000;
-+		RET = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
-+		if (status & EC_STATUS_BATID) {
-+			if (tmp >= BAT_MAX) {
-+				RET = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
-+				if (tmp >= BAT_MAX+BAT_READ_ERROR)
-+					RET = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
-+			} else if (tmp <= BAT_MIN+BAT_READ_ERROR) {
-+				RET = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
-+				if (tmp <= BAT_MIN)
-+					RET = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
-+			} else
-+				RET = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
-+		}
-+		break;
-+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
-+		if (ctrl & EC_CTRL_TRICKLE)
-+			RET = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
-+		else if (ctrl & EC_CTRL_CHARGE_EN)
-+			RET = POWER_SUPPLY_CHARGE_TYPE_FAST;
-+		else
-+			RET = POWER_SUPPLY_CHARGE_TYPE_NONE;
-+		break;
-+	case POWER_SUPPLY_PROP_CURRENT_MAX:
-+		/* 1.4A ? */
-+		RET = 1400000;
-+		break;
-+	default:
-+		break;
-+	}
-+
-+	return ret;
-+}
-+#undef RET
-+
-+static enum power_supply_property gdium_ac_props[] = {
-+	POWER_SUPPLY_PROP_ONLINE,
-+};
-+
-+static enum power_supply_property gdium_battery_props[] = {
-+	POWER_SUPPLY_PROP_STATUS,
-+	POWER_SUPPLY_PROP_PRESENT,
-+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-+	POWER_SUPPLY_PROP_CHARGE_NOW,
-+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
-+	POWER_SUPPLY_PROP_CURRENT_MAX,
-+	POWER_SUPPLY_PROP_CURRENT_NOW,
-+	POWER_SUPPLY_PROP_CAPACITY,
-+	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-+	POWER_SUPPLY_PROP_CHARGE_TYPE,
-+};
-+
-+static void gdium_laptop_battery_work(struct work_struct *work)
-+{
-+	struct gdium_laptop_data *data = container_of(work, struct gdium_laptop_data, work.work);
-+	struct i2c_client *client;
-+	int ret;
-+	char old_status, old_charge_cmd;
-+	char present;
-+	s32 status;
-+
-+	mutex_lock(&data->mutex);
-+	client	= data->client;
-+	status	= ec_read_status(client);
-+	ret	= ec_read_battery(client);
-+
-+	if ((status < 0) || (ret < 0))
-+		goto i2c_read_error;
-+
-+	old_status = data->status;
-+	old_charge_cmd = data->charge_cmd;
-+	data->status = status;
-+
-+	/*
-+	 * Charge only if :
-+	 * - battery present
-+	 * - ac adapter plugged in
-+	 * - battery not fully charged
-+	 */
-+#if CONFIG_GDIUM_VERSION > 2
-+	present = !!(data->status & EC_STATUS_BATID);
-+#else
-+	present = !!(ret > BAT_VOLT_PRESENT);
-+#endif
-+	data->battery_level = 0;
-+	if (present) {
-+		data->battery_level = (unsigned int)ret;
-+		if (data->status & EC_STATUS_ADAPT)
-+			data->battery_level -= BAT_READ_ERROR_MV;
-+	}
-+
-+	data->charge_cmd = 0;
-+	if ((data->status & EC_STATUS_ADAPT) && present && (data->battery_level <= BAT_MAX_MV))
-+		data->charge_cmd = (ret < BAT_TRICKLE_EN) ? 2 : 3;
-+
-+	ec_charge_en(client, (data->charge_cmd >> 1) & 1, data->charge_cmd & 1);
-+
-+	/*
-+	 * data->ctrl must be set _after_ calling ec_charge_en as this will change the
-+	 * control register content
-+	 */
-+	data->ctrl = ec_read_ctrl(client);
-+
-+	if ((data->status & EC_STATUS_ADAPT) != (old_status & EC_STATUS_ADAPT)) {
-+		power_supply_changed(&data->gdium_ac);
-+		/* Send charging/discharging state change */
-+		power_supply_changed(&data->gdium_battery);
-+	} else if ((data->status & EC_STATUS_ADAPT) &&
-+			((old_charge_cmd&2) != (data->charge_cmd&2)))
-+		power_supply_changed(&data->gdium_battery);
-+
-+i2c_read_error:
-+	mutex_unlock(&data->mutex);
-+	queue_delayed_work(data->workqueue, &data->work, msecs_to_jiffies(BAT_SCAN_INTERVAL));
-+}
-+
-+static int gdium_laptop_battery_init(struct gdium_laptop_data *data)
-+{
-+	int ret;
-+
-+	data->bat_pdev = platform_device_register_simple("gdium-battery", 0, NULL, 0);
-+	if (IS_ERR(data->bat_pdev))
-+		return PTR_ERR(data->bat_pdev);
-+
-+	data->gdium_battery.name		= data->bat_pdev->name;
-+	data->gdium_battery.properties		= gdium_battery_props;
-+	data->gdium_battery.num_properties	= ARRAY_SIZE(gdium_battery_props);
-+	data->gdium_battery.get_property	= gdium_battery_get_props;
-+	data->gdium_battery.use_for_apm		= 1;
-+
-+	ret = power_supply_register(&data->bat_pdev->dev, &data->gdium_battery);
-+	if (ret)
-+		goto err_platform;
-+
-+	data->gdium_ac.name			= "gdium-ac";
-+	data->gdium_ac.type			= POWER_SUPPLY_TYPE_MAINS;
-+	data->gdium_ac.properties		= gdium_ac_props;
-+	data->gdium_ac.num_properties		= ARRAY_SIZE(gdium_ac_props);
-+	data->gdium_ac.get_property		= gdium_ac_get_props;
-+/*	data->gdium_ac.use_for_apm_ac		= 1,	*/
-+
-+	ret = power_supply_register(&data->bat_pdev->dev, &data->gdium_ac);
-+	if (ret)
-+		goto err_battery;
-+
-+	if (!ec) {
-+		INIT_DELAYED_WORK(&data->work, gdium_laptop_battery_work);
-+		data->workqueue = create_singlethread_workqueue("gdium-battery-work");
-+		if (!data->workqueue) {
-+			ret = -ESRCH;
-+			goto err_work;
-+		}
-+		queue_delayed_work(data->workqueue, &data->work, msecs_to_jiffies(BAT_SCAN_INTERVAL));
-+	}
-+
-+	return 0;
-+
-+err_work:
-+err_battery:
-+	power_supply_unregister(&data->gdium_battery);
-+err_platform:
-+	platform_device_unregister(data->bat_pdev);
-+
-+	return ret;
-+}
-+static void gdium_laptop_battery_exit(struct gdium_laptop_data *data)
-+{
-+	if (!ec) {
-+		cancel_rearming_delayed_workqueue(data->workqueue, &data->work);
-+		destroy_workqueue(data->workqueue);
-+	}
-+	power_supply_unregister(&data->gdium_battery);
-+	power_supply_unregister(&data->gdium_ac);
-+	platform_device_unregister(data->bat_pdev);
-+}
-+
-+/* Debug fs */
-+static int gdium_laptop_regs_show(struct seq_file *s, void *p)
-+{
-+	struct gdium_laptop_data *data = s->private;
-+	struct i2c_client *client = data->client;
-+
-+	mutex_lock(&data->mutex);
-+	seq_printf(s, "Version    : 0x%02x\n", (unsigned char)ec_read_version(client));
-+	seq_printf(s, "Status     : 0x%02x\n", (unsigned char)ec_read_status(client));
-+	seq_printf(s, "Ctrl       : 0x%02x\n", (unsigned char)ec_read_ctrl(client));
-+	seq_printf(s, "Sign       : 0x%02x\n", (unsigned char)ec_read_sign(client));
-+	seq_printf(s, "Bat Lo     : 0x%02x\n", (unsigned char)i2c_smbus_read_byte_data(client, EC_BAT_LOW));
-+	seq_printf(s, "Bat Hi     : 0x%02x\n", (unsigned char)i2c_smbus_read_byte_data(client, EC_BAT_HIGH));
-+	seq_printf(s, "Battery    : %d uV\n",  (unsigned int)ec_read_battery(client) * 1000);
-+	seq_printf(s, "Charge cmd : %s %s\n", data->charge_cmd & 2 ? "C" : " ", data->charge_cmd & 1 ? "T" : " ");
-+
-+	mutex_unlock(&data->mutex);
-+	return 0;
-+}
-+
-+static int gdium_laptop_regs_open(struct inode *inode,
-+					 struct file *file)
-+{
-+	return single_open(file, gdium_laptop_regs_show, inode->i_private);
-+}
-+
-+static const struct file_operations gdium_laptop_regs_fops = {
-+	.open		= gdium_laptop_regs_open,
-+	.read		= seq_read,
-+	.llseek		= seq_lseek,
-+	.release	= single_release,
-+	.owner		= THIS_MODULE,
-+};
-+
-+
-+static int gdium_laptop_probe(struct i2c_client *client, const struct i2c_device_id *id)
-+{
-+	struct gdium_laptop_data *data;
-+	int ret;
-+
-+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
-+		dev_err(&client->dev,
-+				"%s: no smbus_byte support !\n", __func__);
-+		return -ENODEV;
-+	}
-+
-+	data = kzalloc(sizeof(struct gdium_laptop_data), GFP_KERNEL);
-+	if (!data)
-+		return -ENOMEM;
-+
-+	i2c_set_clientdata(client, data);
-+	data->client = client;
-+	mutex_init(&data->mutex);
-+
-+	ret = ec_read_version(client);
-+	if (ret < 0)
-+		goto err_alloc;
-+
-+	data->version = (unsigned char)ret;
-+
-+	ret = gdium_laptop_input_init(data);
-+	if (ret)
-+		goto err_alloc;
-+
-+	ret = gdium_laptop_battery_init(data);
-+	if (ret)
-+		goto err_input;
-+
-+
-+	if (!ec) {
-+		ret = ec_write_sign(client, EC_SIGN_OS);
-+		if (ret)
-+			goto err_sign;
-+	}
-+
-+	if (gpio16) {
-+		ret = gpio_request(SM502_WLAN_ON, "wlan-on");
-+		if (ret < 0)
-+			goto err_sign;
-+		gpio_set_value(SM502_WLAN_ON, ec_wlan_status(client));
-+		gpio_direction_output(SM502_WLAN_ON, 1);
-+	}
-+
-+	dev_info(&client->dev, "Found firmware 0x%02x\n", data->version);
-+	data->debugfs = debugfs_create_file("gdium_laptop", S_IFREG | S_IRUGO,
-+				NULL, data, &gdium_laptop_regs_fops);
-+
-+	return 0;
-+
-+err_sign:
-+	gdium_laptop_battery_exit(data);
-+err_input:
-+	gdium_laptop_input_exit(data);
-+err_alloc:
-+	kfree(data);
-+	return ret;
-+}
-+
-+static int gdium_laptop_remove(struct i2c_client *client)
-+{
-+	struct gdium_laptop_data *data = i2c_get_clientdata(client);
-+
-+	if (gpio16)
-+		gpio_free(SM502_WLAN_ON);
-+	ec_write_sign(client, EC_SIGN_EC);
-+	if (data->debugfs)
-+		debugfs_remove(data->debugfs);
-+
-+	gdium_laptop_battery_exit(data);
-+	gdium_laptop_input_exit(data);
-+
-+	kfree(data);
-+	return 0;
-+}
-+
-+#ifdef CONFIG_PM
-+static int gdium_laptop_suspend(struct i2c_client *client, pm_message_t msg)
-+{
-+	struct gdium_laptop_data *data = i2c_get_clientdata(client);
-+
-+	if (!ec)
-+		cancel_rearming_delayed_workqueue(data->workqueue, &data->work);
-+	return 0;
-+}
-+
-+static int gdium_laptop_resume(struct i2c_client *client)
-+{
-+	struct gdium_laptop_data *data = i2c_get_clientdata(client);
-+
-+	if (!ec)
-+		queue_delayed_work(data->workqueue, &data->work, msecs_to_jiffies(BAT_SCAN_INTERVAL));
-+	return 0;
-+}
-+#else
-+#define gdium_laptop_suspend NULL
-+#define gdium_laptop_resume NULL
-+#endif
-+static const struct i2c_device_id gdium_id[] = {
-+	{ "gdium-laptop" },
-+	{},
-+};
-+MODULE_DEVICE_TABLE(i2c, gdium_id);
-+
-+static struct i2c_driver gdium_laptop_driver = {
-+	.driver = {
-+		.name = "gdium-laptop",
-+		.owner = THIS_MODULE,
-+	},
-+	.probe = gdium_laptop_probe,
-+	.remove = gdium_laptop_remove,
-+	.shutdown = gdium_laptop_remove,
-+	.suspend = gdium_laptop_suspend,
-+	.resume = gdium_laptop_resume,
-+	.id_table = gdium_id,
-+};
-+
-+static int __init gdium_laptop_init(void)
-+{
-+	return i2c_add_driver(&gdium_laptop_driver);
-+}
-+
-+static void __exit gdium_laptop_exit(void)
-+{
-+	i2c_del_driver(&gdium_laptop_driver);
-+}
-+
-+module_init(gdium_laptop_init);
-+module_exit(gdium_laptop_exit);
-+
-+MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
-+MODULE_DESCRIPTION("Gdium laptop extras");
-+MODULE_LICENSE("GPL");
-diff --git a/drivers/platform/mips/lynloong_pc.c b/drivers/platform/mips/lynloong_pc.c
-new file mode 100644
-index 0000000..68f29e4
---- /dev/null
-+++ b/drivers/platform/mips/lynloong_pc.c
-@@ -0,0 +1,515 @@
-+/*
-+ * Driver for LynLoong PC extras
-+ *
-+ *  Copyright (C) 2009 Lemote Inc.
-+ *  Author: Wu Zhangjin <wuzhangjin@gmail.com>, Xiang Yu <xiangy@lemote.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/err.h>
-+#include <linux/module.h>
-+#include <linux/platform_device.h>
-+#include <linux/backlight.h>	/* for backlight subdriver */
-+#include <linux/fb.h>
-+#include <linux/video_output.h>	/* for video output subdriver */
-+#include <linux/delay.h>	/* for suspend support */
-+
-+#include <cs5536/cs5536.h>
-+#include <cs5536/cs5536_mfgpt.h>
-+
-+#include <loongson.h>
-+
-+static u32 gpio_base, mfgpt_base;
-+
-+static void set_gpio_reg_high(int gpio, int reg)
-+{
-+	u32 val;
-+
-+	val = inl(gpio_base + reg);
-+	val |= (1 << gpio);
-+	val &= ~(1 << (16 + gpio));
-+	outl(val, gpio_base + reg);
-+	mmiowb();
-+}
-+
-+static void set_gpio_reg_low(int gpio, int reg)
-+{
-+	u32 val;
-+
-+	val = inl(gpio_base + reg);
-+	val |= (1 << (16 + gpio));
-+	val &= ~(1 << gpio);
-+	outl(val, gpio_base + reg);
-+	mmiowb();
-+}
-+
-+static void set_gpio_output_low(int gpio)
-+{
-+	set_gpio_reg_high(gpio, GPIOL_OUT_EN);
-+	set_gpio_reg_low(gpio, GPIOL_OUT_VAL);
-+}
-+
-+static void set_gpio_output_high(int gpio)
-+{
-+	set_gpio_reg_high(gpio, GPIOL_OUT_EN);
-+	set_gpio_reg_high(gpio, GPIOL_OUT_VAL);
-+}
-+
-+/* backlight subdriver */
-+
-+#define MAX_BRIGHTNESS 100
-+#define DEFAULT_BRIGHTNESS 50
-+#define MIN_BRIGHTNESS 0
-+static unsigned int level;
-+
-+DEFINE_SPINLOCK(backlight_lock);
-+/* Tune the brightness */
-+static void setup_mfgpt2(void)
-+{
-+	unsigned long flags;
-+
-+	spin_lock_irqsave(&backlight_lock, flags);
-+
-+	/* Set MFGPT2 comparator 1,2 */
-+	outw(MAX_BRIGHTNESS-level, MFGPT2_CMP1);
-+	outw(MAX_BRIGHTNESS, MFGPT2_CMP2);
-+	/* Clear MFGPT2 UP COUNTER */
-+	outw(0, MFGPT2_CNT);
-+	/* Enable counter, compare mode, 32k */
-+	outw(0x8280, MFGPT2_SETUP);
-+
-+	spin_unlock_irqrestore(&backlight_lock, flags);
-+}
-+
-+static int lynloong_set_brightness(struct backlight_device *bd)
-+{
-+	level = (bd->props.fb_blank == FB_BLANK_UNBLANK &&
-+		 bd->props.power == FB_BLANK_UNBLANK) ?
-+	    bd->props.brightness : 0;
-+
-+	if (level > MAX_BRIGHTNESS)
-+		level = MAX_BRIGHTNESS;
-+	else if (level < MIN_BRIGHTNESS)
-+		level = MIN_BRIGHTNESS;
-+
-+	setup_mfgpt2();
-+
-+	return 0;
-+}
-+
-+static int lynloong_get_brightness(struct backlight_device *bd)
-+{
-+	return level;
-+}
-+
-+static struct backlight_ops backlight_ops = {
-+	.get_brightness = lynloong_get_brightness,
-+	.update_status = lynloong_set_brightness,
-+};
-+
-+static struct backlight_device *lynloong_backlight_dev;
-+
-+static int lynloong_backlight_init(void)
-+{
-+	int ret;
-+	u32 hi;
-+	struct backlight_properties props;
-+
-+	/* Get gpio_base */
-+	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &gpio_base);
-+	/* Get mfgpt_base */
-+	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &hi, &mfgpt_base);
-+	/* Get gpio_base */
-+	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &gpio_base);
-+
-+	/* Select for mfgpt */
-+	set_gpio_reg_high(7, GPIOL_OUT_AUX1_SEL);
-+	/* Enable brightness controlling */
-+	set_gpio_output_high(7);
-+
-+	memset(&props, 0, sizeof(struct backlight_properties));
-+	props.max_brightness = MAX_BRIGHTNESS;
-+	props.type = BACKLIGHT_PLATFORM;
-+	lynloong_backlight_dev = backlight_device_register("backlight0", NULL,
-+			NULL, &backlight_ops, &props);
-+
-+	if (IS_ERR(lynloong_backlight_dev)) {
-+		ret = PTR_ERR(lynloong_backlight_dev);
-+		return ret;
-+	}
-+
-+	lynloong_backlight_dev->props.brightness = DEFAULT_BRIGHTNESS;
-+	backlight_update_status(lynloong_backlight_dev);
-+
-+	return 0;
-+}
-+
-+static void lynloong_backlight_exit(void)
-+{
-+	if (lynloong_backlight_dev) {
-+		backlight_device_unregister(lynloong_backlight_dev);
-+		lynloong_backlight_dev = NULL;
-+	}
-+	/* Disable brightness controlling */
-+	set_gpio_output_low(7);
-+}
-+
-+/* video output driver */
-+static int vo_status = 1;
-+
-+static int lcd_video_output_get(struct output_device *od)
-+{
-+	return vo_status;
-+}
-+
-+static int lcd_video_output_set(struct output_device *od)
-+{
-+	int i;
-+	unsigned long status;
-+
-+	status = !!od->request_state;
-+
-+	if (status == 0) {
-+		/* Set the current status as off */
-+		vo_status = 0;
-+		/* Turn off the backlight */
-+		set_gpio_output_low(11);
-+		for (i = 0; i < 0x500; i++)
-+			delay();
-+		/* Turn off the LCD */
-+		set_gpio_output_high(8);
-+	} else {
-+		/* Turn on the LCD */
-+		set_gpio_output_low(8);
-+		for (i = 0; i < 0x500; i++)
-+			delay();
-+		/* Turn on the backlight */
-+		set_gpio_output_high(11);
-+		/* Set the current status as on */
-+		vo_status = 1;
-+	}
-+
-+	return 0;
-+}
-+
-+static struct output_properties lcd_output_properties = {
-+	.set_state = lcd_video_output_set,
-+	.get_status = lcd_video_output_get,
-+};
-+
-+static struct output_device *lcd_output_dev;
-+
-+static void lynloong_lcd_vo_set(int status)
-+{
-+	lcd_output_dev->request_state = status;
-+	lcd_video_output_set(lcd_output_dev);
-+}
-+
-+static int lynloong_vo_init(void)
-+{
-+	int ret;
-+
-+	/* Register video output device: lcd */
-+	lcd_output_dev = video_output_register("LCD", NULL, NULL,
-+			&lcd_output_properties);
-+
-+	if (IS_ERR(lcd_output_dev)) {
-+		ret = PTR_ERR(lcd_output_dev);
-+		lcd_output_dev = NULL;
-+		return ret;
-+	}
-+	/* Ensure LCD is on by default */
-+	lynloong_lcd_vo_set(1);
-+
-+	return 0;
-+}
-+
-+static void lynloong_vo_exit(void)
-+{
-+	if (lcd_output_dev) {
-+		video_output_unregister(lcd_output_dev);
-+		lcd_output_dev = NULL;
-+	}
-+}
-+
-+/* suspend support */
-+
-+#ifdef CONFIG_PM
-+
-+static u32 smb_base;
-+
-+/* I2C operations */
-+
-+static int i2c_wait(void)
-+{
-+	char c;
-+	int i;
-+
-+	udelay(1000);
-+	for (i = 0; i < 20; i++) {
-+		c = inb(smb_base | SMB_STS);
-+		if (c & (SMB_STS_BER | SMB_STS_NEGACK))
-+			return -1;
-+		if (c & SMB_STS_SDAST)
-+			return 0;
-+		udelay(100);
-+	}
-+	return -2;
-+}
-+
-+static void i2c_read_single(int addr, int regNo, char *value)
-+{
-+	unsigned char c;
-+
-+	/* Start condition */
-+	c = inb(smb_base | SMB_CTRL1);
-+	outb(c | SMB_CTRL1_START, smb_base | SMB_CTRL1);
-+	i2c_wait();
-+
-+	/* Send slave address */
-+	outb(addr & 0xfe, smb_base | SMB_SDA);
-+	i2c_wait();
-+
-+	/* Acknowledge smbus */
-+	c = inb(smb_base | SMB_CTRL1);
-+	outb(c | SMB_CTRL1_ACK, smb_base | SMB_CTRL1);
-+
-+	/* Send register index */
-+	outb(regNo, smb_base | SMB_SDA);
-+	i2c_wait();
-+
-+	/* Acknowledge smbus */
-+	c = inb(smb_base | SMB_CTRL1);
-+	outb(c | SMB_CTRL1_ACK, smb_base | SMB_CTRL1);
-+
-+	/* Start condition again */
-+	c = inb(smb_base | SMB_CTRL1);
-+	outb(c | SMB_CTRL1_START, smb_base | SMB_CTRL1);
-+	i2c_wait();
-+
-+	/* Send salve address again */
-+	outb(1 | addr, smb_base | SMB_SDA);
-+	i2c_wait();
-+
-+	/* Acknowledge smbus */
-+	c = inb(smb_base | SMB_CTRL1);
-+	outb(c | SMB_CTRL1_ACK, smb_base | SMB_CTRL1);
-+
-+	/* Read data */
-+	*value = inb(smb_base | SMB_SDA);
-+
-+	/* Stop condition */
-+	outb(SMB_CTRL1_STOP, smb_base | SMB_CTRL1);
-+	i2c_wait();
-+}
-+
-+static void i2c_write_single(int addr, int regNo, char value)
-+{
-+	unsigned char c;
-+
-+	/* Start condition */
-+	c = inb(smb_base | SMB_CTRL1);
-+	outb(c | SMB_CTRL1_START, smb_base | SMB_CTRL1);
-+	i2c_wait();
-+	/* Send slave address */
-+	outb(addr & 0xfe, smb_base | SMB_SDA);
-+	i2c_wait();;
-+
-+	/* Send register index */
-+	outb(regNo, smb_base | SMB_SDA);
-+	i2c_wait();
-+
-+	/* Write data */
-+	outb(value, smb_base | SMB_SDA);
-+	i2c_wait();
-+	/* Stop condition */
-+	outb(SMB_CTRL1_STOP, smb_base | SMB_CTRL1);
-+	i2c_wait();
-+}
-+
-+static void stop_clock(int clk_reg, int clk_sel)
-+{
-+	u8 value;
-+
-+	i2c_read_single(0xd3, clk_reg, &value);
-+	value &= ~(1 << clk_sel);
-+	i2c_write_single(0xd2, clk_reg, value);
-+}
-+
-+static void enable_clock(int clk_reg, int clk_sel)
-+{
-+	u8 value;
-+
-+	i2c_read_single(0xd3, clk_reg, &value);
-+	value |= (1 << clk_sel);
-+	i2c_write_single(0xd2, clk_reg, value);
-+}
-+
-+static char cached_clk_freq;
-+static char cached_pci_fixed_freq;
-+
-+static void decrease_clk_freq(void)
-+{
-+	char value;
-+
-+	i2c_read_single(0xd3, 1, &value);
-+	cached_clk_freq = value;
-+
-+	/* Select frequency by software */
-+	value |= (1 << 1);
-+	/* CPU, 3V66, PCI : 100, 66, 33(1) */
-+	value |= (1 << 2);
-+	i2c_write_single(0xd2, 1, value);
-+
-+	/* Cache the pci frequency */
-+	i2c_read_single(0xd3, 14, &value);
-+	cached_pci_fixed_freq = value;
-+
-+	/* Enable PCI fix mode */
-+	value |= (1 << 5);
-+	/* 3V66, PCI : 64MHz, 32MHz */
-+	value |= (1 << 3);
-+	i2c_write_single(0xd2, 14, value);
-+
-+}
-+
-+static void resume_clk_freq(void)
-+{
-+	i2c_write_single(0xd2, 1, cached_clk_freq);
-+	i2c_write_single(0xd2, 14, cached_pci_fixed_freq);
-+}
-+
-+static void stop_clocks(void)
-+{
-+	/* CPU Clock Register */
-+	stop_clock(2, 5);	/* not used */
-+	stop_clock(2, 6);	/* not used */
-+	stop_clock(2, 7);	/* not used */
-+
-+	/* PCI Clock Register */
-+	stop_clock(3, 1);	/* 8100 */
-+	stop_clock(3, 5);	/* SIS */
-+	stop_clock(3, 0);	/* not used */
-+	stop_clock(3, 6);	/* not used */
-+
-+	/* PCI 48M Clock Register */
-+	stop_clock(4, 6);	/* USB grounding */
-+	stop_clock(4, 5);	/* REF(5536_14M) */
-+
-+	/* 3V66 Control Register */
-+	stop_clock(5, 0);	/* VCH_CLK..., grounding */
-+}
-+
-+static void enable_clocks(void)
-+{
-+	enable_clock(3, 1);	/* 8100 */
-+	enable_clock(3, 5);	/* SIS */
-+
-+	enable_clock(4, 6);
-+	enable_clock(4, 5);	/* REF(5536_14M) */
-+
-+	enable_clock(5, 0);	/* VCH_CLOCK, grounding */
-+}
-+
-+static int lynloong_suspend(struct device *dev)
-+{
-+	/* Disable AMP */
-+	set_gpio_output_high(6);
-+	/* Turn off LCD */
-+	lynloong_lcd_vo_set(0);
-+
-+	/* Stop the clocks of some devices */
-+	stop_clocks();
-+
-+	/* Decrease the external clock frequency */
-+	decrease_clk_freq();
-+
-+	return 0;
-+}
-+
-+static int lynloong_resume(struct device *dev)
-+{
-+	/* Turn on the LCD */
-+	lynloong_lcd_vo_set(1);
-+
-+	/* Resume clock frequency, enable the relative clocks */
-+	resume_clk_freq();
-+	enable_clocks();
-+
-+	/* Enable AMP */
-+	set_gpio_output_low(6);
-+
-+	return 0;
-+}
-+
-+static const SIMPLE_DEV_PM_OPS(lynloong_pm_ops, lynloong_suspend,
-+	lynloong_resume);
-+#endif	/* !CONFIG_PM */
-+
-+static struct platform_device_id platform_device_ids[] = {
-+	{
-+		.name = "lynloong_pc",
-+	},
-+	{}
-+};
-+
-+MODULE_DEVICE_TABLE(platform, platform_device_ids);
-+
-+static struct platform_driver platform_driver = {
-+	.driver = {
-+		.name = "lynloong_pc",
-+		.owner = THIS_MODULE,
-+#ifdef CONFIG_PM
-+		.pm = &lynloong_pm_ops,
-+#endif
-+	},
-+	.id_table = platform_device_ids,
-+};
-+
-+static int __init lynloong_init(void)
-+{
-+	int ret;
-+
-+	pr_info("LynLoong platform specific driver loaded.\n");
-+
-+	/* Register platform stuff */
-+	ret = platform_driver_register(&platform_driver);
-+	if (ret) {
-+		pr_err("Failed to register LynLoong platform driver.\n");
-+		return ret;
-+	}
-+
-+	ret = lynloong_backlight_init();
-+	if (ret) {
-+		pr_err("Failed to register LynLoong backlight driver.\n");
-+		return ret;
-+	}
-+
-+	ret = lynloong_vo_init();
-+	if (ret) {
-+		pr_err("Failed to register LynLoong backlight driver.\n");
-+		lynloong_vo_exit();
-+		return ret;
-+	}
-+
-+	return 0;
-+}
-+
-+static void __exit lynloong_exit(void)
-+{
-+	lynloong_vo_exit();
-+	lynloong_backlight_exit();
-+	platform_driver_unregister(&platform_driver);
-+
-+	pr_info("LynLoong platform specific driver unloaded.\n");
-+}
-+
-+module_init(lynloong_init);
-+module_exit(lynloong_exit);
-+
-+MODULE_AUTHOR("Wu Zhangjin <wuzhangjin@gmail.com>; Xiang Yu <xiangy@lemote.com>");
-+MODULE_DESCRIPTION("LynLoong PC driver");
-+MODULE_LICENSE("GPL");
-diff --git a/drivers/platform/mips/yeeloong_ecrom.c b/drivers/platform/mips/yeeloong_ecrom.c
-new file mode 100644
-index 0000000..1bfe4cf
---- /dev/null
-+++ b/drivers/platform/mips/yeeloong_ecrom.c
-@@ -0,0 +1,944 @@
-+/*
-+ * Driver for flushing/dumping ROM of EC on YeeLoong laptop
-+ *
-+ * Copyright (C) 2009 Lemote Inc.
-+ * Author: liujl <liujl@lemote.com>
-+ *
-+ * NOTE :
-+ * 	The EC resources accessing and programming are supported.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/proc_fs.h>
-+#include <linux/miscdevice.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+
-+#include <ec_kb3310b.h>
-+
-+#define	EC_MISC_DEV		"ec_misc"
-+#define EC_IOC_MAGIC		'E'
-+
-+/* ec registers range */
-+#define	EC_MAX_REGADDR	0xFFFF
-+#define	EC_MIN_REGADDR	0xF000
-+#define	EC_RAM_ADDR	0xF800
-+
-+/* version burned address */
-+#define	VER_ADDR	0xf7a1
-+#define	VER_MAX_SIZE	7
-+#define	EC_ROM_MAX_SIZE	0x10000
-+
-+/* ec internal register */
-+#define	REG_POWER_MODE		0xF710
-+#define	FLAG_NORMAL_MODE	0x00
-+#define	FLAG_IDLE_MODE		0x01
-+#define	FLAG_RESET_MODE		0x02
-+
-+/* ec update program flag */
-+#define	PROGRAM_FLAG_NONE	0x00
-+#define	PROGRAM_FLAG_IE		0x01
-+#define	PROGRAM_FLAG_ROM	0x02
-+
-+/* XBI relative registers */
-+#define REG_XBISEG0     0xFEA0
-+#define REG_XBISEG1     0xFEA1
-+#define REG_XBIRSV2     0xFEA2
-+#define REG_XBIRSV3     0xFEA3
-+#define REG_XBIRSV4     0xFEA4
-+#define REG_XBICFG      0xFEA5
-+#define REG_XBICS       0xFEA6
-+#define REG_XBIWE       0xFEA7
-+#define REG_XBISPIA0    0xFEA8
-+#define REG_XBISPIA1    0xFEA9
-+#define REG_XBISPIA2    0xFEAA
-+#define REG_XBISPIDAT   0xFEAB
-+#define REG_XBISPICMD   0xFEAC
-+#define REG_XBISPICFG   0xFEAD
-+#define REG_XBISPIDATR  0xFEAE
-+#define REG_XBISPICFG2  0xFEAF
-+
-+/* commands definition for REG_XBISPICMD */
-+#define	SPICMD_WRITE_STATUS		0x01
-+#define	SPICMD_BYTE_PROGRAM		0x02
-+#define	SPICMD_READ_BYTE		0x03
-+#define	SPICMD_WRITE_DISABLE	0x04
-+#define	SPICMD_READ_STATUS		0x05
-+#define	SPICMD_WRITE_ENABLE		0x06
-+#define	SPICMD_HIGH_SPEED_READ	0x0B
-+#define	SPICMD_POWER_DOWN		0xB9
-+#define	SPICMD_SST_EWSR			0x50
-+#define	SPICMD_SST_SEC_ERASE	0x20
-+#define	SPICMD_SST_BLK_ERASE	0x52
-+#define	SPICMD_SST_CHIP_ERASE	0x60
-+#define	SPICMD_FRDO				0x3B
-+#define	SPICMD_SEC_ERASE		0xD7
-+#define	SPICMD_BLK_ERASE		0xD8
-+#define SPICMD_CHIP_ERASE		0xC7
-+
-+/* bits definition for REG_XBISPICFG */
-+#define	SPICFG_AUTO_CHECK		0x01
-+#define	SPICFG_SPI_BUSY			0x02
-+#define	SPICFG_DUMMY_READ		0x04
-+#define	SPICFG_EN_SPICMD		0x08
-+#define	SPICFG_LOW_SPICS		0x10
-+#define	SPICFG_EN_SHORT_READ	0x20
-+#define	SPICFG_EN_OFFSET_READ	0x40
-+#define	SPICFG_EN_FAST_READ		0x80
-+
-+/* watchdog timer registers */
-+#define	REG_WDTCFG				0xfe80
-+#define	REG_WDTPF				0xfe81
-+#define REG_WDT					0xfe82
-+
-+/* lpc configure register */
-+#define	REG_LPCCFG				0xfe95
-+
-+/* 8051 reg */
-+#define	REG_PXCFG				0xff14
-+
-+/* Fan register in KB3310 */
-+#define	REG_ECFAN_SPEED_LEVEL	0xf4e4
-+#define	REG_ECFAN_SWITCH		0xf4d2
-+
-+/* the ec flash rom id number */
-+#define	EC_ROM_PRODUCT_ID_SPANSION	0x01
-+#define	EC_ROM_PRODUCT_ID_MXIC		0xC2
-+#define	EC_ROM_PRODUCT_ID_AMIC		0x37
-+#define	EC_ROM_PRODUCT_ID_EONIC		0x1C
-+
-+/* misc ioctl operations */
-+#define	IOCTL_RDREG		_IOR(EC_IOC_MAGIC, 1, int)
-+#define	IOCTL_WRREG		_IOW(EC_IOC_MAGIC, 2, int)
-+#define	IOCTL_READ_EC		_IOR(EC_IOC_MAGIC, 3, int)
-+#define	IOCTL_PROGRAM_IE	_IOW(EC_IOC_MAGIC, 4, int)
-+#define	IOCTL_PROGRAM_EC	_IOW(EC_IOC_MAGIC, 5, int)
-+
-+/* start address for programming of EC content or IE */
-+/*  ec running code start address */
-+#define	EC_START_ADDR	0x00000000
-+/*  ec information element storing address */
-+#define	IE_START_ADDR	0x00020000
-+
-+/* EC state */
-+#define	EC_STATE_IDLE	0x00	/*  ec in idle state */
-+#define	EC_STATE_BUSY	0x01	/*  ec in busy state */
-+
-+/* timeout value for programming */
-+#define	EC_FLASH_TIMEOUT	0x1000	/*  ec program timeout */
-+/* command checkout timeout including cmd to port or state flag check */
-+#define	EC_CMD_TIMEOUT		0x1000
-+#define	EC_SPICMD_STANDARD_TIMEOUT	(4 * 1000)	/*  unit : us */
-+#define	EC_MAX_DELAY_UNIT	(10)	/*  every time for polling */
-+#define	SPI_FINISH_WAIT_TIME	10
-+/* EC content max size */
-+#define	EC_CONTENT_MAX_SIZE	(64 * 1024)
-+#define	IE_CONTENT_MAX_SIZE	(0x100000 - IE_START_ADDR)
-+
-+/* the register operation access struct */
-+struct ec_reg {
-+	u32 addr;		/* the address of kb3310 registers */
-+	u8 val;			/* the register value */
-+};
-+
-+struct ec_info {
-+	u32 start_addr;
-+	u32 size;
-+	u8 *buf;
-+};
-+
-+/* open for using rom protection action */
-+#define	EC_ROM_PROTECTION
-+
-+/* enable the chip reset mode */
-+static int ec_init_reset_mode(void)
-+{
-+	int timeout;
-+	unsigned char status = 0;
-+	int ret = 0;
-+
-+	/* make chip goto reset mode */
-+	ret = ec_query_seq(CMD_INIT_RESET_MODE);
-+	if (ret < 0) {
-+		printk(KERN_ERR "ec init reset mode failed.\n");
-+		goto out;
-+	}
-+
-+	/* make the action take active */
-+	timeout = EC_CMD_TIMEOUT;
-+	status = ec_read(REG_POWER_MODE) & FLAG_RESET_MODE;
-+	while (timeout--) {
-+		if (status) {
-+			udelay(EC_REG_DELAY);
-+			break;
-+		}
-+		status = ec_read(REG_POWER_MODE) & FLAG_RESET_MODE;
-+		udelay(EC_REG_DELAY);
-+	}
-+	if (timeout <= 0) {
-+		printk(KERN_ERR "ec rom fixup : can't check reset status.\n");
-+		ret = -EINVAL;
-+	} else
-+		printk(KERN_INFO "(%d/%d)reset 0xf710 :  0x%x\n", timeout,
-+			   EC_CMD_TIMEOUT - timeout, status);
-+
-+	/* set MCU to reset mode */
-+	udelay(EC_REG_DELAY);
-+	status = ec_read(REG_PXCFG);
-+	status |= (1 << 0);
-+	ec_write(REG_PXCFG, status);
-+	udelay(EC_REG_DELAY);
-+
-+	/* disable FWH/LPC */
-+	udelay(EC_REG_DELAY);
-+	status = ec_read(REG_LPCCFG);
-+	status &= ~(1 << 7);
-+	ec_write(REG_LPCCFG, status);
-+	udelay(EC_REG_DELAY);
-+
-+	printk(KERN_INFO "entering reset mode ok..............\n");
-+
-+ out:
-+	return ret;
-+}
-+
-+/* make ec exit from reset mode */
-+static void ec_exit_reset_mode(void)
-+{
-+	unsigned char regval;
-+
-+	udelay(EC_REG_DELAY);
-+	regval = ec_read(REG_LPCCFG);
-+	regval |= (1 << 7);
-+	ec_write(REG_LPCCFG, regval);
-+	regval = ec_read(REG_PXCFG);
-+	regval &= ~(1 << 0);
-+	ec_write(REG_PXCFG, regval);
-+	printk(KERN_INFO "exit reset mode ok..................\n");
-+
-+	return;
-+}
-+
-+/* make ec disable WDD */
-+static void ec_disable_WDD(void)
-+{
-+	unsigned char status;
-+
-+	udelay(EC_REG_DELAY);
-+	status = ec_read(REG_WDTCFG);
-+	ec_write(REG_WDTPF, 0x03);
-+	ec_write(REG_WDTCFG, (status & 0x80) | 0x48);
-+	printk(KERN_INFO "Disable WDD ok..................\n");
-+
-+	return;
-+}
-+
-+/* make ec enable WDD */
-+static void ec_enable_WDD(void)
-+{
-+	unsigned char status;
-+
-+	udelay(EC_REG_DELAY);
-+	status = ec_read(REG_WDTCFG);
-+	ec_write(REG_WDT, 0x28);	/* set WDT 5sec(0x28) */
-+	ec_write(REG_WDTCFG, (status & 0x80) | 0x03);
-+	printk(KERN_INFO "Enable WDD ok..................\n");
-+
-+	return;
-+}
-+
-+/* make ec goto idle mode */
-+static int ec_init_idle_mode(void)
-+{
-+	int timeout;
-+	unsigned char status = 0;
-+	int ret = 0;
-+
-+	ec_query_seq(CMD_INIT_IDLE_MODE);
-+
-+	/* make the action take active */
-+	timeout = EC_CMD_TIMEOUT;
-+	status = ec_read(REG_POWER_MODE) & FLAG_IDLE_MODE;
-+	while (timeout--) {
-+		if (status) {
-+			udelay(EC_REG_DELAY);
-+			break;
-+		}
-+		status = ec_read(REG_POWER_MODE) & FLAG_IDLE_MODE;
-+		udelay(EC_REG_DELAY);
-+	}
-+	if (timeout <= 0) {
-+		printk(KERN_ERR "ec rom fixup : can't check out the status.\n");
-+		ret = -EINVAL;
-+	} else
-+		printk(KERN_INFO "(%d/%d)0xf710 :  0x%x\n", timeout,
-+			   EC_CMD_TIMEOUT - timeout, ec_read(REG_POWER_MODE));
-+
-+	printk(KERN_INFO "entering idle mode ok...................\n");
-+
-+	return ret;
-+}
-+
-+/* make ec exit from idle mode */
-+static int ec_exit_idle_mode(void)
-+{
-+
-+	ec_query_seq(CMD_EXIT_IDLE_MODE);
-+
-+	printk(KERN_INFO "exit idle mode ok...................\n");
-+
-+	return 0;
-+}
-+
-+static int ec_instruction_cycle(void)
-+{
-+	unsigned long timeout;
-+	int ret = 0;
-+
-+	timeout = EC_FLASH_TIMEOUT;
-+	while (timeout-- >= 0) {
-+		if (!(ec_read(REG_XBISPICFG) & SPICFG_SPI_BUSY))
-+			break;
-+	}
-+	if (timeout <= 0) {
-+		printk(KERN_ERR
-+		       "EC_INSTRUCTION_CYCLE : timeout for check flag.\n");
-+		ret = -EINVAL;
-+		goto out;
-+	}
-+
-+ out:
-+	return ret;
-+}
-+
-+/* To see if the ec is in busy state or not. */
-+static inline int ec_flash_busy(unsigned long timeout)
-+{
-+	/* assurance the first command be going to rom */
-+	if (ec_instruction_cycle() < 0)
-+		return EC_STATE_BUSY;
-+#if 1
-+	timeout = timeout / EC_MAX_DELAY_UNIT;
-+	while (timeout-- > 0) {
-+		/* check the rom's status of busy flag */
-+		ec_write(REG_XBISPICMD, SPICMD_READ_STATUS);
-+		if (ec_instruction_cycle() < 0)
-+			return EC_STATE_BUSY;
-+		if ((ec_read(REG_XBISPIDAT) & 0x01) == 0x00)
-+			return EC_STATE_IDLE;
-+		udelay(EC_MAX_DELAY_UNIT);
-+	}
-+	if (timeout <= 0) {
-+		printk(KERN_ERR
-+		       "EC_FLASH_BUSY : timeout for check rom flag.\n");
-+		return EC_STATE_BUSY;
-+	}
-+#else
-+	/* check the rom's status of busy flag */
-+	ec_write(REG_XBISPICMD, SPICMD_READ_STATUS);
-+	if (ec_instruction_cycle() < 0)
-+		return EC_STATE_BUSY;
-+
-+	timeout = timeout / EC_MAX_DELAY_UNIT;
-+	while (timeout-- > 0) {
-+		if ((ec_read(REG_XBISPIDAT) & 0x01) == 0x00)
-+			return EC_STATE_IDLE;
-+		udelay(EC_MAX_DELAY_UNIT);
-+	}
-+	if (timeout <= 0) {
-+		printk(KERN_ERR
-+		       "EC_FLASH_BUSY : timeout for check rom flag.\n");
-+		return EC_STATE_BUSY;
-+	}
-+#endif
-+
-+	return EC_STATE_IDLE;
-+}
-+
-+static int rom_instruction_cycle(unsigned char cmd)
-+{
-+	unsigned long timeout = 0;
-+
-+	switch (cmd) {
-+	case SPICMD_READ_STATUS:
-+	case SPICMD_WRITE_ENABLE:
-+	case SPICMD_WRITE_DISABLE:
-+	case SPICMD_READ_BYTE:
-+	case SPICMD_HIGH_SPEED_READ:
-+		timeout = 0;
-+		break;
-+	case SPICMD_WRITE_STATUS:
-+		timeout = 300 * 1000;
-+		break;
-+	case SPICMD_BYTE_PROGRAM:
-+		timeout = 5 * 1000;
-+		break;
-+	case SPICMD_SST_SEC_ERASE:
-+	case SPICMD_SEC_ERASE:
-+		timeout = 1000 * 1000;
-+		break;
-+	case SPICMD_SST_BLK_ERASE:
-+	case SPICMD_BLK_ERASE:
-+		timeout = 3 * 1000 * 1000;
-+		break;
-+	case SPICMD_SST_CHIP_ERASE:
-+	case SPICMD_CHIP_ERASE:
-+		timeout = 20 * 1000 * 1000;
-+		break;
-+	default:
-+		timeout = EC_SPICMD_STANDARD_TIMEOUT;
-+	}
-+	if (timeout == 0)
-+		return ec_instruction_cycle();
-+	if (timeout < EC_SPICMD_STANDARD_TIMEOUT)
-+		timeout = EC_SPICMD_STANDARD_TIMEOUT;
-+
-+	return ec_flash_busy(timeout);
-+}
-+
-+/* delay for start/stop action */
-+static void delay_spi(int n)
-+{
-+	while (n--)
-+		inb(EC_IO_PORT_HIGH);
-+}
-+
-+/* start the action to spi rom function */
-+static void ec_start_spi(void)
-+{
-+	unsigned char val;
-+
-+	delay_spi(SPI_FINISH_WAIT_TIME);
-+	val = ec_read(REG_XBISPICFG) | SPICFG_EN_SPICMD | SPICFG_AUTO_CHECK;
-+	ec_write(REG_XBISPICFG, val);
-+	delay_spi(SPI_FINISH_WAIT_TIME);
-+}
-+
-+/* stop the action to spi rom function */
-+static void ec_stop_spi(void)
-+{
-+	unsigned char val;
-+
-+	delay_spi(SPI_FINISH_WAIT_TIME);
-+	val =
-+	    ec_read(REG_XBISPICFG) & (~(SPICFG_EN_SPICMD | SPICFG_AUTO_CHECK));
-+	ec_write(REG_XBISPICFG, val);
-+	delay_spi(SPI_FINISH_WAIT_TIME);
-+}
-+
-+/* read one byte from xbi interface */
-+static int ec_read_byte(unsigned int addr, unsigned char *byte)
-+{
-+	int ret = 0;
-+
-+	/* enable spicmd writing. */
-+	ec_start_spi();
-+
-+	/* enable write spi flash */
-+	ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE);
-+	if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) {
-+		printk(KERN_ERR "EC_READ_BYTE : SPICMD_WRITE_ENABLE failed.\n");
-+		ret = -EINVAL;
-+		goto out;
-+	}
-+
-+	/* write the address */
-+	ec_write(REG_XBISPIA2, (addr & 0xff0000) >> 16);
-+	ec_write(REG_XBISPIA1, (addr & 0x00ff00) >> 8);
-+	ec_write(REG_XBISPIA0, (addr & 0x0000ff) >> 0);
-+	/* start action */
-+	ec_write(REG_XBISPICMD, SPICMD_HIGH_SPEED_READ);
-+	if (rom_instruction_cycle(SPICMD_HIGH_SPEED_READ) == EC_STATE_BUSY) {
-+		printk(KERN_ERR
-+		       "EC_READ_BYTE : SPICMD_HIGH_SPEED_READ failed.\n");
-+		ret = -EINVAL;
-+		goto out;
-+	}
-+
-+	*byte = ec_read(REG_XBISPIDAT);
-+
-+ out:
-+	/* disable spicmd writing. */
-+	ec_stop_spi();
-+
-+	return ret;
-+}
-+
-+/* write one byte to ec rom */
-+static int ec_write_byte(unsigned int addr, unsigned char byte)
-+{
-+	int ret = 0;
-+
-+	/* enable spicmd writing. */
-+	ec_start_spi();
-+
-+	/* enable write spi flash */
-+	ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE);
-+	if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) {
-+		printk(KERN_ERR
-+		       "EC_WRITE_BYTE : SPICMD_WRITE_ENABLE failed.\n");
-+		ret = -EINVAL;
-+		goto out;
-+	}
-+
-+	/* write the address */
-+	ec_write(REG_XBISPIA2, (addr & 0xff0000) >> 16);
-+	ec_write(REG_XBISPIA1, (addr & 0x00ff00) >> 8);
-+	ec_write(REG_XBISPIA0, (addr & 0x0000ff) >> 0);
-+	ec_write(REG_XBISPIDAT, byte);
-+	/* start action */
-+	ec_write(REG_XBISPICMD, SPICMD_BYTE_PROGRAM);
-+	if (rom_instruction_cycle(SPICMD_BYTE_PROGRAM) == EC_STATE_BUSY) {
-+		printk(KERN_ERR
-+		       "EC_WRITE_BYTE : SPICMD_BYTE_PROGRAM failed.\n");
-+		ret = -EINVAL;
-+		goto out;
-+	}
-+
-+ out:
-+	/* disable spicmd writing. */
-+	ec_stop_spi();
-+
-+	return ret;
-+}
-+
-+/* unprotect SPI ROM */
-+/* EC_ROM_unprotect function code */
-+static int EC_ROM_unprotect(void)
-+{
-+	unsigned char status;
-+
-+	/* enable write spi flash */
-+	ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE);
-+	if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) {
-+		printk(KERN_ERR
-+		       "EC_UNIT_ERASE : SPICMD_WRITE_ENABLE failed.\n");
-+		return 1;
-+	}
-+
-+	/* unprotect the status register of rom */
-+	ec_write(REG_XBISPICMD, SPICMD_READ_STATUS);
-+	if (rom_instruction_cycle(SPICMD_READ_STATUS) == EC_STATE_BUSY) {
-+		printk(KERN_ERR "EC_UNIT_ERASE : SPICMD_READ_STATUS failed.\n");
-+		return 1;
-+	}
-+	status = ec_read(REG_XBISPIDAT);
-+	ec_write(REG_XBISPIDAT, status & 0x02);
-+	if (ec_instruction_cycle() < 0) {
-+		printk(KERN_ERR "EC_UNIT_ERASE : write status value failed.\n");
-+		return 1;
-+	}
-+
-+	ec_write(REG_XBISPICMD, SPICMD_WRITE_STATUS);
-+	if (rom_instruction_cycle(SPICMD_WRITE_STATUS) == EC_STATE_BUSY) {
-+		printk(KERN_ERR
-+		       "EC_UNIT_ERASE : SPICMD_WRITE_STATUS failed.\n");
-+		return 1;
-+	}
-+
-+	/* enable write spi flash */
-+	ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE);
-+	if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) {
-+		printk(KERN_ERR
-+		       "EC_UNIT_ERASE : SPICMD_WRITE_ENABLE failed.\n");
-+		return 1;
-+	}
-+
-+	return 0;
-+}
-+
-+/* erase one block or chip or sector as needed */
-+static int ec_unit_erase(unsigned char erase_cmd, unsigned int addr)
-+{
-+	unsigned char status;
-+	int ret = 0, i = 0;
-+	int unprotect_count = 3;
-+	int check_flag = 0;
-+
-+	/* enable spicmd writing. */
-+	ec_start_spi();
-+
-+#ifdef EC_ROM_PROTECTION
-+	/* added for re-check SPICMD_READ_STATUS */
-+	while (unprotect_count-- > 0) {
-+		if (EC_ROM_unprotect()) {
-+			ret = -EINVAL;
-+			goto out;
-+		}
-+
-+		/* first time:500ms --> 5.5sec -->10.5sec */
-+		for (i = 0; i < ((2 - unprotect_count) * 100 + 10); i++)
-+			udelay(50000);
-+		ec_write(REG_XBISPICMD, SPICMD_READ_STATUS);
-+		if (rom_instruction_cycle(SPICMD_READ_STATUS)
-+				== EC_STATE_BUSY) {
-+			printk(KERN_ERR
-+			       "EC_PROGRAM_ROM : SPICMD_READ_STATUS failed.\n");
-+		} else {
-+			status = ec_read(REG_XBISPIDAT);
-+			printk(KERN_INFO "Read unprotect status : 0x%x\n",
-+				   status);
-+			if ((status & 0x1C) == 0x00) {
-+				printk(KERN_INFO
-+					   "Read unprotect status OK1 : 0x%x\n",
-+					   status & 0x1C);
-+				check_flag = 1;
-+				break;
-+			}
-+		}
-+	}
-+
-+	if (!check_flag) {
-+		printk(KERN_INFO "SPI ROM unprotect fail.\n");
-+		return 1;
-+	}
-+#endif
-+
-+	/* block address fill */
-+	if (erase_cmd == SPICMD_BLK_ERASE) {
-+		ec_write(REG_XBISPIA2, (addr & 0x00ff0000) >> 16);
-+		ec_write(REG_XBISPIA1, (addr & 0x0000ff00) >> 8);
-+		ec_write(REG_XBISPIA0, (addr & 0x000000ff) >> 0);
-+	}
-+
-+	/* erase the whole chip first */
-+	ec_write(REG_XBISPICMD, erase_cmd);
-+	if (rom_instruction_cycle(erase_cmd) == EC_STATE_BUSY) {
-+		printk(KERN_ERR "EC_UNIT_ERASE : erase failed.\n");
-+		ret = -EINVAL;
-+		goto out;
-+	}
-+
-+ out:
-+	/* disable spicmd writing. */
-+	ec_stop_spi();
-+
-+	return ret;
-+}
-+
-+/* update the whole rom content with H/W mode
-+ * PLEASE USING ec_unit_erase() FIRSTLY
-+ */
-+static int ec_program_rom(struct ec_info *info, int flag)
-+{
-+	unsigned int addr = 0;
-+	unsigned long size = 0;
-+	unsigned char *ptr = NULL;
-+	unsigned char data;
-+	unsigned char val = 0;
-+	int ret = 0;
-+	int i, j;
-+	unsigned char status;
-+
-+	/* modify for program serial No.
-+	 * set IE_START_ADDR & use idle mode,
-+	 * disable WDD
-+	 */
-+	if (flag == PROGRAM_FLAG_ROM) {
-+		ret = ec_init_reset_mode();
-+		addr = info->start_addr + EC_START_ADDR;
-+		printk(KERN_INFO "PROGRAM_FLAG_ROM..............\n");
-+	} else if (flag == PROGRAM_FLAG_IE) {
-+		ret = ec_init_idle_mode();
-+		ec_disable_WDD();
-+		addr = info->start_addr + IE_START_ADDR;
-+		printk(KERN_INFO "PROGRAM_FLAG_IE..............\n");
-+	} else {
-+		return 0;
-+	}
-+
-+	if (ret < 0) {
-+		if (flag == PROGRAM_FLAG_IE)
-+			ec_enable_WDD();
-+		return ret;
-+	}
-+
-+	size = info->size;
-+	ptr = info->buf;
-+	printk(KERN_INFO "starting update ec ROM..............\n");
-+
-+	ret = ec_unit_erase(SPICMD_BLK_ERASE, addr);
-+	if (ret) {
-+		printk(KERN_ERR "program ec : erase block failed.\n");
-+		goto out;
-+	}
-+	printk(KERN_ERR "program ec : erase block OK.\n");
-+
-+	i = 0;
-+	while (i < size) {
-+		data = *(ptr + i);
-+		ec_write_byte(addr, data);
-+		ec_read_byte(addr, &val);
-+		if (val != data) {
-+			ec_write_byte(addr, data);
-+			ec_read_byte(addr, &val);
-+			if (val != data) {
-+				printk(KERN_INFO
-+				"EC : Second flash program failed at:\t");
-+				printk(KERN_INFO
-+				"addr : 0x%x, source : 0x%x, dest: 0x%x\n",
-+				     addr, data, val);
-+				printk(KERN_INFO "This should not happen... STOP\n");
-+				break;
-+			}
-+		}
-+		i++;
-+		addr++;
-+	}
-+
-+#ifdef	EC_ROM_PROTECTION
-+	/* we should start spi access firstly */
-+	ec_start_spi();
-+
-+	/* enable write spi flash */
-+	ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE);
-+	if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) {
-+		printk(KERN_ERR
-+		       "EC_PROGRAM_ROM : SPICMD_WRITE_ENABLE failed.\n");
-+		goto out1;
-+	}
-+
-+	/* protect the status register of rom */
-+	ec_write(REG_XBISPICMD, SPICMD_READ_STATUS);
-+	if (rom_instruction_cycle(SPICMD_READ_STATUS) == EC_STATE_BUSY) {
-+		printk(KERN_ERR
-+		       "EC_PROGRAM_ROM : SPICMD_READ_STATUS failed.\n");
-+		goto out1;
-+	}
-+	status = ec_read(REG_XBISPIDAT);
-+
-+	ec_write(REG_XBISPIDAT, status | 0x1C);
-+	if (ec_instruction_cycle() < 0) {
-+		printk(KERN_ERR
-+		       "EC_PROGRAM_ROM : write status value failed.\n");
-+		goto out1;
-+	}
-+
-+	ec_write(REG_XBISPICMD, SPICMD_WRITE_STATUS);
-+	if (rom_instruction_cycle(SPICMD_WRITE_STATUS) == EC_STATE_BUSY) {
-+		printk(KERN_ERR
-+		       "EC_PROGRAM_ROM : SPICMD_WRITE_STATUS failed.\n");
-+		goto out1;
-+	}
-+#endif
-+
-+	/* disable the write action to spi rom */
-+	ec_write(REG_XBISPICMD, SPICMD_WRITE_DISABLE);
-+	if (rom_instruction_cycle(SPICMD_WRITE_DISABLE) == EC_STATE_BUSY) {
-+		printk(KERN_ERR
-+		       "EC_PROGRAM_ROM : SPICMD_WRITE_DISABLE failed.\n");
-+		goto out1;
-+	}
-+
-+ out1:
-+	/* we should stop spi access firstly */
-+	ec_stop_spi();
-+ out:
-+	/* for security */
-+	for (j = 0; j < 2000; j++)
-+		udelay(1000);
-+
-+	/* modify for program serial No.
-+	 * after program No exit idle mode
-+	 * and enable WDD
-+	 */
-+	if (flag == PROGRAM_FLAG_ROM) {
-+		/* exit from the reset mode */
-+		ec_exit_reset_mode();
-+	} else {
-+		/* ec exit from idle mode */
-+		ret = ec_exit_idle_mode();
-+		ec_enable_WDD();
-+		if (ret < 0)
-+			return ret;
-+	}
-+
-+	return 0;
-+}
-+
-+/* ioctl  */
-+static int misc_ioctl(struct inode *inode, struct file *filp, u_int cmd,
-+		      u_long arg)
-+{
-+	struct ec_info ecinfo;
-+	void __user *ptr = (void __user *)arg;
-+	struct ec_reg *ecreg = (struct ec_reg *)(filp->private_data);
-+	int ret = 0;
-+
-+	switch (cmd) {
-+	case IOCTL_RDREG:
-+		ret = copy_from_user(ecreg, ptr, sizeof(struct ec_reg));
-+		if (ret) {
-+			printk(KERN_ERR "reg read : copy from user error.\n");
-+			return -EFAULT;
-+		}
-+		if ((ecreg->addr > EC_MAX_REGADDR)
-+		    || (ecreg->addr < EC_MIN_REGADDR)) {
-+			printk(KERN_ERR
-+			       "reg read : out of register address range.\n");
-+			return -EINVAL;
-+		}
-+		ecreg->val = ec_read(ecreg->addr);
-+		ret = copy_to_user(ptr, ecreg, sizeof(struct ec_reg));
-+		if (ret) {
-+			printk(KERN_ERR "reg read : copy to user error.\n");
-+			return -EFAULT;
-+		}
-+		break;
-+	case IOCTL_WRREG:
-+		ret = copy_from_user(ecreg, ptr, sizeof(struct ec_reg));
-+		if (ret) {
-+			printk(KERN_ERR "reg write : copy from user error.\n");
-+			return -EFAULT;
-+		}
-+		if ((ecreg->addr > EC_MAX_REGADDR)
-+		    || (ecreg->addr < EC_MIN_REGADDR)) {
-+			printk(KERN_ERR
-+			       "reg write : out of register address range.\n");
-+			return -EINVAL;
-+		}
-+		ec_write(ecreg->addr, ecreg->val);
-+		break;
-+	case IOCTL_READ_EC:
-+		ret = copy_from_user(ecreg, ptr, sizeof(struct ec_reg));
-+		if (ret) {
-+			printk(KERN_ERR "spi read : copy from user error.\n");
-+			return -EFAULT;
-+		}
-+		if ((ecreg->addr > EC_RAM_ADDR)
-+		    && (ecreg->addr < EC_MAX_REGADDR)) {
-+			printk(KERN_ERR
-+			       "spi read : out of register address range.\n");
-+			return -EINVAL;
-+		}
-+		ec_read_byte(ecreg->addr, &(ecreg->val));
-+		ret = copy_to_user(ptr, ecreg, sizeof(struct ec_reg));
-+		if (ret) {
-+			printk(KERN_ERR "spi read : copy to user error.\n");
-+			return -EFAULT;
-+		}
-+		break;
-+	case IOCTL_PROGRAM_IE:
-+		ecinfo.start_addr = EC_START_ADDR;
-+		ecinfo.size = EC_CONTENT_MAX_SIZE;
-+		ecinfo.buf = (u8 *) kmalloc(ecinfo.size, GFP_KERNEL);
-+		if (ecinfo.buf == NULL) {
-+			printk(KERN_ERR "program ie : kmalloc failed.\n");
-+			return -ENOMEM;
-+		}
-+		ret = copy_from_user(ecinfo.buf, (u8 *) ptr, ecinfo.size);
-+		if (ret) {
-+			printk(KERN_ERR "program ie : copy from user error.\n");
-+			kfree(ecinfo.buf);
-+			ecinfo.buf = NULL;
-+			return -EFAULT;
-+		}
-+
-+		/* use ec_program_rom to write serial No */
-+		ec_program_rom(&ecinfo, PROGRAM_FLAG_IE);
-+
-+		kfree(ecinfo.buf);
-+		ecinfo.buf = NULL;
-+		break;
-+	case IOCTL_PROGRAM_EC:
-+		ecinfo.start_addr = EC_START_ADDR;
-+		if (get_user((ecinfo.size), (u32 *) ptr)) {
-+			printk(KERN_ERR "program ec : get user error.\n");
-+			return -EFAULT;
-+		}
-+		if ((ecinfo.size) > EC_CONTENT_MAX_SIZE) {
-+			printk(KERN_ERR "program ec : size out of limited.\n");
-+			return -EINVAL;
-+		}
-+		ecinfo.buf = (u8 *) kmalloc(ecinfo.size, GFP_KERNEL);
-+		if (ecinfo.buf == NULL) {
-+			printk(KERN_ERR "program ec : kmalloc failed.\n");
-+			return -ENOMEM;
-+		}
-+		ret = copy_from_user(ecinfo.buf, ((u8 *) ptr + 4), ecinfo.size);
-+		if (ret) {
-+			printk(KERN_ERR "program ec : copy from user error.\n");
-+			kfree(ecinfo.buf);
-+			ecinfo.buf = NULL;
-+			return -EFAULT;
-+		}
-+
-+		ec_program_rom(&ecinfo, PROGRAM_FLAG_ROM);
-+
-+		kfree(ecinfo.buf);
-+		ecinfo.buf = NULL;
-+		break;
-+
-+	default:
-+		break;
-+	}
-+
-+	return 0;
-+}
-+
-+static long misc_compat_ioctl(struct file *file, unsigned int cmd,
-+			      unsigned long arg)
-+{
-+	return misc_ioctl(file->f_dentry->d_inode, file, cmd, arg);
-+}
-+
-+static int misc_open(struct inode *inode, struct file *filp)
-+{
-+	struct ec_reg *ecreg = NULL;
-+	ecreg = kmalloc(sizeof(struct ec_reg), GFP_KERNEL);
-+	if (ecreg)
-+		filp->private_data = ecreg;
-+
-+	return ecreg ? 0 : -ENOMEM;
-+}
-+
-+static int misc_release(struct inode *inode, struct file *filp)
-+{
-+	struct ec_reg *ecreg = (struct ec_reg *)(filp->private_data);
-+
-+	filp->private_data = NULL;
-+	kfree(ecreg);
-+
-+	return 0;
-+}
-+
-+static const struct file_operations ecmisc_fops = {
-+	.open = misc_open,
-+	.release = misc_release,
-+	.read = NULL,
-+	.write = NULL,
-+#ifdef	CONFIG_64BIT
-+	.compat_ioctl = misc_compat_ioctl,
-+#else
-+	.ioctl = misc_ioctl,
-+#endif
-+};
-+
-+static struct miscdevice ecmisc_device = {
-+	.minor = MISC_DYNAMIC_MINOR,
-+	.name = EC_MISC_DEV,
-+	.fops = &ecmisc_fops
-+};
-+
-+static int __init ecmisc_init(void)
-+{
-+	int ret;
-+
-+	printk(KERN_INFO "EC misc device init.\n");
-+	ret = misc_register(&ecmisc_device);
-+
-+	return ret;
-+}
-+
-+static void __exit ecmisc_exit(void)
-+{
-+	printk(KERN_INFO "EC misc device exit.\n");
-+	misc_deregister(&ecmisc_device);
-+}
-+
-+module_init(ecmisc_init);
-+module_exit(ecmisc_exit);
-+
-+MODULE_AUTHOR("liujl <liujl@lemote.com>");
-+MODULE_DESCRIPTION("Driver for flushing/dumping ROM of EC on YeeLoong laptop");
-+MODULE_LICENSE("GPL");
-diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c
-new file mode 100644
-index 0000000..c285a67
---- /dev/null
-+++ b/drivers/platform/mips/yeeloong_laptop.c
-@@ -0,0 +1,1360 @@
-+/*
-+ * Driver for YeeLoong laptop extras
-+ *
-+ *  Copyright (C) 2009 Lemote Inc.
-+ *  Author: Wu Zhangjin <wuzhangjin@gmail.com>, Liu Junliang <liujl@lemote.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/err.h>
-+#include <linux/module.h>
-+#include <linux/platform_device.h>
-+#include <linux/backlight.h>	/* for backlight subdriver */
-+#include <linux/fb.h>
-+#include <linux/hwmon.h>	/* for hwmon subdriver */
-+#include <linux/hwmon-sysfs.h>
-+#include <linux/video_output.h>	/* for video output subdriver */
-+#include <linux/lcd.h>		/* for lcd output subdriver */
-+#include <linux/input.h>	/* for hotkey subdriver */
-+#include <linux/input/sparse-keymap.h>
-+#include <linux/interrupt.h>
-+#include <linux/delay.h>
-+#include <linux/power_supply.h>	/* for AC & Battery subdriver */
-+#include <linux/reboot.h>	/* for register_reboot_notifier */
-+#include <linux/suspend.h>	/* for register_pm_notifier */
-+
-+#include <cs5536/cs5536.h>
-+
-+#include <loongson.h>		/* for loongson_cmdline */
-+#include <ec_kb3310b.h>
-+
-+#define ON	1
-+#define OFF	0
-+#define EVENT_START EVENT_LID
-+
-+/* common function */
-+#define EC_VER_LEN 64
-+
-+static int ec_version_before(char *version)
-+{
-+	char *p, ec_ver[EC_VER_LEN];
-+
-+	p = strstr(loongson_cmdline, "EC_VER=");
-+	if (!p)
-+		memset(ec_ver, 0, EC_VER_LEN);
-+	else {
-+		strncpy(ec_ver, p, EC_VER_LEN);
-+		p = strstr(ec_ver, " ");
-+		if (p)
-+			*p = '\0';
-+	}
-+
-+	return (strncasecmp(ec_ver, version, 64) < 0);
-+}
-+
-+/* backlight subdriver */
-+#define MIN_BRIGHTNESS	1
-+#define MAX_BRIGHTNESS	8
-+
-+static int yeeloong_set_brightness(struct backlight_device *bd)
-+{
-+	unsigned char level;
-+	static unsigned char old_level;
-+
-+	level = (bd->props.fb_blank == FB_BLANK_UNBLANK &&
-+		 bd->props.power == FB_BLANK_UNBLANK) ?
-+	    bd->props.brightness : 0;
-+
-+	level = clamp_val(level, MIN_BRIGHTNESS, MAX_BRIGHTNESS);
-+
-+	/* Avoid to modify the brightness when EC is tuning it */
-+	if (old_level != level) {
-+		if (ec_read(REG_DISPLAY_BRIGHTNESS) == old_level)
-+			ec_write(REG_DISPLAY_BRIGHTNESS, level);
-+		old_level = level;
-+	}
-+
-+	return 0;
-+}
-+
-+static int yeeloong_get_brightness(struct backlight_device *bd)
-+{
-+	return ec_read(REG_DISPLAY_BRIGHTNESS);
-+}
-+
-+static struct backlight_ops backlight_ops = {
-+	.get_brightness = yeeloong_get_brightness,
-+	.update_status = yeeloong_set_brightness,
-+};
-+
-+static struct backlight_device *yeeloong_backlight_dev;
-+
-+static int yeeloong_backlight_init(void)
-+{
-+	int ret;
-+	struct backlight_properties props;
-+
-+	memset(&props, 0, sizeof(struct backlight_properties));
-+	props.max_brightness = MAX_BRIGHTNESS;
-+	props.type = BACKLIGHT_PLATFORM;
-+	yeeloong_backlight_dev = backlight_device_register("backlight0", NULL,
-+			NULL, &backlight_ops, &props);
-+
-+	if (IS_ERR(yeeloong_backlight_dev)) {
-+		ret = PTR_ERR(yeeloong_backlight_dev);
-+		yeeloong_backlight_dev = NULL;
-+		return ret;
-+	}
-+
-+	yeeloong_backlight_dev->props.brightness =
-+		yeeloong_get_brightness(yeeloong_backlight_dev);
-+	backlight_update_status(yeeloong_backlight_dev);
-+
-+	return 0;
-+}
-+
-+static void yeeloong_backlight_exit(void)
-+{
-+	if (yeeloong_backlight_dev) {
-+		backlight_device_unregister(yeeloong_backlight_dev);
-+		yeeloong_backlight_dev = NULL;
-+	}
-+}
-+
-+/* AC & Battery subdriver */
-+
-+static struct power_supply yeeloong_ac, yeeloong_bat;
-+
-+#define RET (val->intval)
-+
-+#define BAT_CAP_CRITICAL 5
-+#define BAT_CAP_HIGH     95
-+
-+#define get_bat(type) \
-+	ec_read(REG_BAT_##type)
-+
-+#define get_bat_l(type) \
-+	((get_bat(type##_HIGH) << 8) | get_bat(type##_LOW))
-+
-+static int yeeloong_get_ac_props(struct power_supply *psy,
-+				enum power_supply_property psp,
-+				union power_supply_propval *val)
-+{
-+	if (psp == POWER_SUPPLY_PROP_ONLINE)
-+		RET = !!(get_bat(POWER) & BIT_BAT_POWER_ACIN);
-+
-+	return 0;
-+}
-+
-+static enum power_supply_property yeeloong_ac_props[] = {
-+	POWER_SUPPLY_PROP_ONLINE,
-+};
-+
-+static struct power_supply yeeloong_ac = {
-+	.name = "yeeloong-ac",
-+	.type = POWER_SUPPLY_TYPE_MAINS,
-+	.properties = yeeloong_ac_props,
-+	.num_properties = ARRAY_SIZE(yeeloong_ac_props),
-+	.get_property = yeeloong_get_ac_props,
-+};
-+
-+static inline bool is_bat_in(void)
-+{
-+	return !!(get_bat(STATUS) & BIT_BAT_STATUS_IN);
-+}
-+
-+static int get_bat_temp(void)
-+{
-+	return get_bat_l(TEMPERATURE) * 10;
-+}
-+
-+static int get_bat_current(void)
-+{
-+	return -(s16)get_bat_l(CURRENT);
-+}
-+
-+static int get_bat_voltage(void)
-+{
-+	return get_bat_l(VOLTAGE);
-+}
-+
-+static char *get_manufacturer(void)
-+{
-+	return (get_bat(VENDOR) == FLAG_BAT_VENDOR_SANYO) ? "SANYO" : "SIMPLO";
-+}
-+
-+static int get_relative_cap(void)
-+{
-+	/*
-+	 * When the relative capacity becomes 2, the hardware is observed to
-+	 * have been turned off forcely. so, we must tune it be suitable to
-+	 * make the software do related actions.
-+	 */
-+	int tmp = get_bat_l(RELATIVE_CAP);
-+
-+	if (tmp <= (BAT_CAP_CRITICAL * 2))
-+		tmp -= 3;
-+
-+	return tmp;
-+}
-+
-+static int yeeloong_get_bat_props(struct power_supply *psy,
-+				     enum power_supply_property psp,
-+				     union power_supply_propval *val)
-+{
-+	switch (psp) {
-+	/* Fixed information */
-+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-+		/* mV -> µV */
-+		RET = get_bat_l(DESIGN_VOL) * 1000;
-+		break;
-+	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-+		/* mAh->µAh */
-+		RET = get_bat_l(DESIGN_CAP) * 1000;
-+		break;
-+	case POWER_SUPPLY_PROP_CHARGE_FULL:
-+		/* µAh */
-+		RET = get_bat_l(FULLCHG_CAP) * 1000;
-+		break;
-+	case POWER_SUPPLY_PROP_MANUFACTURER:
-+		val->strval = get_manufacturer();
-+		break;
-+	/* Dynamic information */
-+	case POWER_SUPPLY_PROP_PRESENT:
-+		RET = is_bat_in();
-+		break;
-+	case POWER_SUPPLY_PROP_CURRENT_NOW:
-+		/* mA -> µA */
-+		RET = is_bat_in() ? get_bat_current() * 1000 : 0;
-+		break;
-+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-+		/* mV -> µV */
-+		RET = is_bat_in() ? get_bat_voltage() * 1000 : 0;
-+		break;
-+	case POWER_SUPPLY_PROP_TEMP:
-+		/* Celcius */
-+		RET = is_bat_in() ? get_bat_temp() : 0;
-+		break;
-+	case POWER_SUPPLY_PROP_CAPACITY:
-+		RET = is_bat_in() ? get_relative_cap() : 0;
-+		break;
-+	case POWER_SUPPLY_PROP_CAPACITY_LEVEL: {
-+		int status;
-+
-+		if (!is_bat_in()) {
-+			RET = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
-+			break;
-+		}
-+
-+		status = get_bat(STATUS);
-+		RET = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
-+
-+		if (unlikely(status & BIT_BAT_STATUS_DESTROY)) {
-+			RET = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
-+			break;
-+		}
-+
-+		if (status & BIT_BAT_STATUS_FULL)
-+			RET = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
-+		else {
-+			int curr_cap = get_relative_cap();
-+
-+			if (status & BIT_BAT_STATUS_LOW) {
-+				RET = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
-+				if (curr_cap <= BAT_CAP_CRITICAL)
-+					RET = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
-+			} else if (curr_cap >= BAT_CAP_HIGH)
-+				RET = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
-+		}
-+	} break;
-+	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
-+		/* seconds */
-+		RET = is_bat_in() ? (get_relative_cap() - 3) * 54 + 142 : 0;
-+		break;
-+	case POWER_SUPPLY_PROP_STATUS: {
-+			int charge = get_bat(CHARGE);
-+
-+			RET = POWER_SUPPLY_STATUS_UNKNOWN;
-+			if (charge & FLAG_BAT_CHARGE_DISCHARGE)
-+				RET = POWER_SUPPLY_STATUS_DISCHARGING;
-+			else if (charge & FLAG_BAT_CHARGE_CHARGE)
-+				RET = POWER_SUPPLY_STATUS_CHARGING;
-+	} break;
-+	case POWER_SUPPLY_PROP_HEALTH: {
-+			int status;
-+
-+			if (!is_bat_in()) {
-+				RET = POWER_SUPPLY_HEALTH_UNKNOWN;
-+				break;
-+			}
-+
-+			status = get_bat(STATUS);
-+			RET = POWER_SUPPLY_HEALTH_GOOD;
-+
-+			if (status & (BIT_BAT_STATUS_DESTROY |
-+						BIT_BAT_STATUS_LOW))
-+				RET = POWER_SUPPLY_HEALTH_DEAD;
-+			if (get_bat(CHARGE_STATUS) &
-+					BIT_BAT_CHARGE_STATUS_OVERTEMP)
-+				RET = POWER_SUPPLY_HEALTH_OVERHEAT;
-+	} break;
-+	case POWER_SUPPLY_PROP_CHARGE_NOW:	/* 1/100(%)*1000 µAh */
-+		RET = get_relative_cap() * get_bat_l(FULLCHG_CAP) * 10;
-+		break;
-+	default:
-+		return -EINVAL;
-+	}
-+	return 0;
-+}
-+#undef RET
-+
-+static enum power_supply_property yeeloong_bat_props[] = {
-+	POWER_SUPPLY_PROP_STATUS,
-+	POWER_SUPPLY_PROP_PRESENT,
-+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-+	POWER_SUPPLY_PROP_CHARGE_FULL,
-+	POWER_SUPPLY_PROP_CHARGE_NOW,
-+	POWER_SUPPLY_PROP_CURRENT_NOW,
-+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
-+	POWER_SUPPLY_PROP_HEALTH,
-+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-+	POWER_SUPPLY_PROP_CAPACITY,
-+	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-+	POWER_SUPPLY_PROP_TEMP,
-+	POWER_SUPPLY_PROP_MANUFACTURER,
-+};
-+
-+static struct power_supply yeeloong_bat = {
-+	.name = "yeeloong-bat",
-+	.type = POWER_SUPPLY_TYPE_BATTERY,
-+	.properties = yeeloong_bat_props,
-+	.num_properties = ARRAY_SIZE(yeeloong_bat_props),
-+	.get_property = yeeloong_get_bat_props,
-+};
-+
-+static int ac_bat_initialized;
-+
-+static int yeeloong_bat_init(void)
-+{
-+	int ret;
-+
-+	ret = power_supply_register(NULL, &yeeloong_ac);
-+	if (ret)
-+		return ret;
-+	ret = power_supply_register(NULL, &yeeloong_bat);
-+	if (ret) {
-+		power_supply_unregister(&yeeloong_ac);
-+		return ret;
-+	}
-+	ac_bat_initialized = 1;
-+
-+	return 0;
-+}
-+
-+static void yeeloong_bat_exit(void)
-+{
-+	ac_bat_initialized = 0;
-+
-+	power_supply_unregister(&yeeloong_ac);
-+	power_supply_unregister(&yeeloong_bat);
-+}
-+/* hwmon subdriver */
-+
-+#define MIN_FAN_SPEED 0
-+#define MAX_FAN_SPEED 3
-+
-+#define get_fan(type) \
-+	ec_read(REG_FAN_##type)
-+
-+#define set_fan(type, val) \
-+	ec_write(REG_FAN_##type, val)
-+
-+static inline int get_fan_speed_level(void)
-+{
-+	return get_fan(SPEED_LEVEL);
-+}
-+static inline void set_fan_speed_level(int speed)
-+{
-+	set_fan(SPEED_LEVEL, speed);
-+}
-+
-+static inline int get_fan_mode(void)
-+{
-+	return get_fan(AUTO_MAN_SWITCH);
-+}
-+static inline void set_fan_mode(int mode)
-+{
-+	set_fan(AUTO_MAN_SWITCH, mode);
-+}
-+
-+/*
-+ * 3 different modes: Full speed(0); manual mode(1); auto mode(2)
-+ */
-+static int get_fan_pwm_enable(void)
-+{
-+	return (get_fan_mode() == BIT_FAN_AUTO) ? 2 :
-+		(get_fan_speed_level() == MAX_FAN_SPEED) ? 0 : 1;
-+}
-+
-+static void set_fan_pwm_enable(int mode)
-+{
-+	set_fan_mode((mode == 2) ? BIT_FAN_AUTO : BIT_FAN_MANUAL);
-+	if (mode == 0)
-+		set_fan_speed_level(MAX_FAN_SPEED);
-+}
-+
-+static int get_fan_pwm(void)
-+{
-+	return get_fan_speed_level();
-+}
-+
-+static void set_fan_pwm(int value)
-+{
-+	if (get_fan_mode() != BIT_FAN_MANUAL)
-+		return;
-+
-+	value = clamp_val(value, MIN_FAN_SPEED, MAX_FAN_SPEED);
-+
-+	/* We must ensure the fan is on */
-+	if (value > 0)
-+		set_fan(CONTROL, ON);
-+
-+	set_fan_speed_level(value);
-+}
-+
-+static inline int get_fan_speed(void)
-+{
-+	return ((get_fan(SPEED_HIGH) & 0x0f) << 8) | get_fan(SPEED_LOW);
-+}
-+
-+static int get_fan_rpm(void)
-+{
-+	return FAN_SPEED_DIVIDER / get_fan_speed();
-+}
-+
-+static int get_cpu_temp(void)
-+{
-+	return (s8)ec_read(REG_TEMPERATURE_VALUE) * 1000;
-+}
-+
-+static int get_cpu_temp_max(void)
-+{
-+	return 60 * 1000;
-+}
-+
-+static int get_bat_temp_alarm(void)
-+{
-+	return !!(get_bat(CHARGE_STATUS) & BIT_BAT_CHARGE_STATUS_OVERTEMP);
-+}
-+
-+static ssize_t store_sys_hwmon(void (*set) (int), const char *buf, size_t count)
-+{
-+	int ret;
-+	unsigned long value;
-+
-+	if (!count)
-+		return 0;
-+
-+	ret = strict_strtoul(buf, 10, &value);
-+	if (ret)
-+		return ret;
-+
-+	set(value);
-+
-+	return count;
-+}
-+
-+static ssize_t show_sys_hwmon(int (*get) (void), char *buf)
-+{
-+	return sprintf(buf, "%d\n", get());
-+}
-+
-+#define CREATE_SENSOR_ATTR(_name, _mode, _set, _get)		\
-+	static ssize_t show_##_name(struct device *dev,			\
-+				    struct device_attribute *attr,	\
-+				    char *buf)				\
-+	{								\
-+		return show_sys_hwmon(_set, buf);			\
-+	}								\
-+	static ssize_t store_##_name(struct device *dev,		\
-+				     struct device_attribute *attr,	\
-+				     const char *buf, size_t count)	\
-+	{								\
-+		return store_sys_hwmon(_get, buf, count);		\
-+	}								\
-+	static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
-+
-+CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, get_fan_rpm, NULL);
-+CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR, get_fan_pwm, set_fan_pwm);
-+CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, get_fan_pwm_enable,
-+		set_fan_pwm_enable);
-+CREATE_SENSOR_ATTR(temp1_input, S_IRUGO, get_cpu_temp, NULL);
-+CREATE_SENSOR_ATTR(temp1_max, S_IRUGO, get_cpu_temp_max, NULL);
-+CREATE_SENSOR_ATTR(temp2_input, S_IRUGO, get_bat_temp, NULL);
-+CREATE_SENSOR_ATTR(temp2_max_alarm, S_IRUGO, get_bat_temp_alarm, NULL);
-+CREATE_SENSOR_ATTR(curr1_input, S_IRUGO, get_bat_current, NULL);
-+CREATE_SENSOR_ATTR(in1_input, S_IRUGO, get_bat_voltage, NULL);
-+
-+static ssize_t
-+show_name(struct device *dev, struct device_attribute *attr, char *buf)
-+{
-+	return sprintf(buf, "yeeloong\n");
-+}
-+
-+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
-+
-+static struct attribute *hwmon_attributes[] = {
-+	&sensor_dev_attr_pwm1.dev_attr.attr,
-+	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
-+	&sensor_dev_attr_fan1_input.dev_attr.attr,
-+	&sensor_dev_attr_temp1_input.dev_attr.attr,
-+	&sensor_dev_attr_temp1_max.dev_attr.attr,
-+	&sensor_dev_attr_temp2_input.dev_attr.attr,
-+	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
-+	&sensor_dev_attr_curr1_input.dev_attr.attr,
-+	&sensor_dev_attr_in1_input.dev_attr.attr,
-+	&sensor_dev_attr_name.dev_attr.attr,
-+	NULL
-+};
-+
-+static struct attribute_group hwmon_attribute_group = {
-+	.attrs = hwmon_attributes
-+};
-+
-+static struct device *yeeloong_hwmon_dev;
-+
-+static int yeeloong_hwmon_init(void)
-+{
-+	int ret;
-+
-+	yeeloong_hwmon_dev = hwmon_device_register(NULL);
-+	if (IS_ERR(yeeloong_hwmon_dev)) {
-+		yeeloong_hwmon_dev = NULL;
-+		return PTR_ERR(yeeloong_hwmon_dev);
-+	}
-+	ret = sysfs_create_group(&yeeloong_hwmon_dev->kobj,
-+				 &hwmon_attribute_group);
-+	if (ret) {
-+		hwmon_device_unregister(yeeloong_hwmon_dev);
-+		yeeloong_hwmon_dev = NULL;
-+		return ret;
-+	}
-+	/* ensure fan is set to auto mode */
-+	set_fan_pwm_enable(2);
-+
-+	return 0;
-+}
-+
-+static void yeeloong_hwmon_exit(void)
-+{
-+	if (yeeloong_hwmon_dev) {
-+		sysfs_remove_group(&yeeloong_hwmon_dev->kobj,
-+				   &hwmon_attribute_group);
-+		hwmon_device_unregister(yeeloong_hwmon_dev);
-+		yeeloong_hwmon_dev = NULL;
-+	}
-+}
-+
-+/* video output subdriver */
-+
-+#define LCD	0
-+#define CRT	1
-+#define VOD_NUM	2	/* The total number of video output device*/
-+
-+static struct output_device *vod[VOD_NUM];
-+
-+static int vor[] = {REG_DISPLAY_LCD, REG_CRT_DETECT};
-+
-+static int get_vo_dev(struct output_device *od)
-+{
-+	int i, dev;
-+
-+	dev = -1;
-+	for (i = 0; i < VOD_NUM; i++)
-+		if (od == vod[i])
-+			dev = i;
-+
-+	return dev;
-+}
-+
-+static int vo_get_status(int dev)
-+{
-+	return ec_read(vor[dev]);
-+}
-+
-+static int yeeloong_vo_get_status(struct output_device *od)
-+{
-+	int vd;
-+
-+	vd = get_vo_dev(od);
-+	if (vd != -1)
-+		return vo_get_status(vd);
-+
-+	return -ENODEV;
-+}
-+
-+static void vo_set_state(int dev, int state)
-+{
-+	int addr;
-+	unsigned long value;
-+
-+	switch (dev) {
-+	case LCD:
-+		addr = 0x31;
-+		break;
-+	case CRT:
-+		addr = 0x21;
-+		break;
-+	default:
-+		/* return directly if the wrong video output device */
-+		return;
-+	}
-+
-+	outb(addr, 0x3c4);
-+	value = inb(0x3c5);
-+
-+	switch (dev) {
-+	case LCD:
-+		value |= (state ? 0x03 : 0x02);
-+		break;
-+	case CRT:
-+		if (state)
-+			clear_bit(7, &value);
-+		else
-+			set_bit(7, &value);
-+		break;
-+	default:
-+		break;
-+	}
-+
-+	outb(addr, 0x3c4);
-+	outb(value, 0x3c5);
-+
-+	if (dev == LCD)
-+		ec_write(REG_BACKLIGHT_CTRL, state);
-+}
-+
-+static int yeeloong_vo_set_state(struct output_device *od)
-+{
-+	int vd;
-+
-+	vd = get_vo_dev(od);
-+	if (vd == -1)
-+		return -ENODEV;
-+
-+	if (vd == CRT && !vo_get_status(vd))
-+		return 0;
-+
-+	vo_set_state(vd, !!od->request_state);
-+
-+	return 0;
-+}
-+
-+static struct output_properties vop = {
-+	.set_state = yeeloong_vo_set_state,
-+	.get_status = yeeloong_vo_get_status,
-+};
-+
-+static int yeeloong_vo_init(void)
-+{
-+	int ret, i;
-+	char dev_name[VOD_NUM][4] = {"LCD", "CRT"};
-+
-+	/* Register video output device: lcd, crt */
-+	for (i = 0; i < VOD_NUM; i++) {
-+		vod[i] = video_output_register(dev_name[i], NULL, NULL, &vop);
-+		if (IS_ERR(vod[i])) {
-+			if (i != 0)
-+				video_output_unregister(vod[i-1]);
-+			ret = PTR_ERR(vod[i]);
-+			vod[i] = NULL;
-+			return ret;
-+		}
-+	}
-+	/* Ensure LCD is on by default */
-+	vo_set_state(LCD, ON);
-+
-+	/*
-+	 * Turn off CRT by default, and will be enabled when the CRT
-+	 * connectting event reported by SCI
-+	 */
-+	vo_set_state(CRT, OFF);
-+
-+	return 0;
-+}
-+
-+static void yeeloong_vo_exit(void)
-+{
-+	int i;
-+
-+	for (i = 0; i < VOD_NUM; i++) {
-+		if (vod[i]) {
-+			video_output_unregister(vod[i]);
-+			vod[i] = NULL;
-+		}
-+	}
-+}
-+
-+/* lcd subdriver */
-+
-+struct lcd_device *lcd[VOD_NUM];
-+
-+static int get_lcd_dev(struct lcd_device *ld)
-+{
-+	int i, dev;
-+
-+	dev = -1;
-+	for (i = 0; i < VOD_NUM; i++)
-+		if (ld == lcd[i])
-+			dev = i;
-+
-+	return dev;
-+}
-+
-+static int yeeloong_lcd_set_power(struct lcd_device *ld, int power)
-+{
-+	int dev = get_lcd_dev(ld);
-+
-+	if (power == FB_BLANK_UNBLANK)
-+		vo_set_state(dev, ON);
-+	if (power == FB_BLANK_POWERDOWN)
-+		vo_set_state(dev, OFF);
-+
-+	return 0;
-+}
-+
-+static int yeeloong_lcd_get_power(struct lcd_device *ld)
-+{
-+	return vo_get_status(get_lcd_dev(ld));
-+}
-+
-+static struct lcd_ops lcd_ops = {
-+	.set_power = yeeloong_lcd_set_power,
-+	.get_power = yeeloong_lcd_get_power,
-+};
-+
-+static int yeeloong_lcd_init(void)
-+{
-+	int ret, i;
-+	char dev_name[VOD_NUM][4] = {"LCD", "CRT"};
-+
-+	/* Register video output device: lcd, crt */
-+	for (i = 0; i < VOD_NUM; i++) {
-+		lcd[i] = lcd_device_register(dev_name[i], NULL, NULL, &lcd_ops);
-+		if (IS_ERR(lcd[i])) {
-+			if (i != 0)
-+				lcd_device_unregister(lcd[i-1]);
-+			ret = PTR_ERR(lcd[i]);
-+			lcd[i] = NULL;
-+			return ret;
-+		}
-+	}
-+#if 0
-+	/* This has been done by the vide output driver */
-+
-+	/* Ensure LCD is on by default */
-+	vo_set_state(LCD, ON);
-+
-+	/*
-+	 * Turn off CRT by default, and will be enabled when the CRT
-+	 * connectting event reported by SCI
-+	 */
-+	vo_set_state(CRT, OFF);
-+#endif
-+	return 0;
-+}
-+
-+static void yeeloong_lcd_exit(void)
-+{
-+	int i;
-+
-+	for (i = 0; i < VOD_NUM; i++) {
-+		if (lcd[i]) {
-+			lcd_device_unregister(lcd[i]);
-+			lcd[i] = NULL;
-+		}
-+	}
-+}
-+
-+/* hotkey subdriver */
-+
-+static struct input_dev *yeeloong_hotkey_dev;
-+
-+static atomic_t reboot_flag, sleep_flag;
-+#define in_sleep() (&sleep_flag)
-+#define in_reboot() (&reboot_flag)
-+
-+static const struct key_entry yeeloong_keymap[] = {
-+	{KE_SW, EVENT_LID, { SW_LID } },
-+	{KE_KEY, EVENT_CAMERA, { KEY_CAMERA } }, /* Fn + ESC */
-+	{KE_KEY, EVENT_SLEEP, { KEY_SLEEP } }, /* Fn + F1 */
-+	{KE_KEY, EVENT_BLACK_SCREEN, { KEY_DISPLAYTOGGLE } }, /* Fn + F2 */
-+	{KE_KEY, EVENT_DISPLAY_TOGGLE, { KEY_SWITCHVIDEOMODE } }, /* Fn + F3 */
-+	{KE_KEY, EVENT_AUDIO_MUTE, { KEY_MUTE } }, /* Fn + F4 */
-+	{KE_KEY, EVENT_WLAN, { KEY_WLAN } }, /* Fn + F5 */
-+	{KE_KEY, EVENT_DISPLAY_BRIGHTNESS, { KEY_BRIGHTNESSUP } }, /* Fn + up */
-+	{KE_KEY, EVENT_DISPLAY_BRIGHTNESS, { KEY_BRIGHTNESSDOWN } }, /* Fn + down */
-+	{KE_KEY, EVENT_AUDIO_VOLUME, { KEY_VOLUMEUP } }, /* Fn + right */
-+	{KE_KEY, EVENT_AUDIO_VOLUME, { KEY_VOLUMEDOWN } }, /* Fn + left */
-+	{KE_END, 0}
-+};
-+
-+static int is_fake_event(u16 keycode)
-+{
-+	switch (keycode) {
-+	case KEY_SLEEP:
-+	case SW_LID:
-+		return atomic_read(in_sleep()) | atomic_read(in_reboot());
-+		break;
-+	default:
-+		break;
-+	}
-+	return 0;
-+}
-+
-+static struct key_entry *get_event_key_entry(int event, int status)
-+{
-+	struct key_entry *ke;
-+	static int old_brightness_status = -1;
-+	static int old_volume_status = -1;
-+
-+	ke = sparse_keymap_entry_from_scancode(yeeloong_hotkey_dev, event);
-+	if (!ke)
-+		return NULL;
-+
-+	switch (event) {
-+	case EVENT_DISPLAY_BRIGHTNESS:
-+		/* current status > old one, means up */
-+		if ((status < old_brightness_status) || (0 == status))
-+			ke++;
-+		old_brightness_status = status;
-+		break;
-+	case EVENT_AUDIO_VOLUME:
-+		if ((status < old_volume_status) || (0 == status))
-+			ke++;
-+		old_volume_status = status;
-+		break;
-+	default:
-+		break;
-+	}
-+
-+	return ke;
-+}
-+
-+static int report_lid_switch(int status)
-+{
-+	static int old_status;
-+
-+	/*
-+	 * LID is a switch button, so, two continuous same status should be
-+	 * ignored
-+	 */
-+	if (old_status != status) {
-+		input_report_switch(yeeloong_hotkey_dev, SW_LID, !status);
-+		input_sync(yeeloong_hotkey_dev);
-+	}
-+	old_status = status;
-+
-+	return status;
-+}
-+
-+static int crt_detect_handler(int status)
-+{
-+	/*
-+	 * When CRT is inserted, enable its output and disable the LCD output,
-+	 * otherwise, do reversely.
-+	 */
-+	vo_set_state(CRT, status);
-+	vo_set_state(LCD, !status);
-+
-+	return status;
-+}
-+
-+static int displaytoggle_handler(int status)
-+{
-+	/* EC(>=PQ1D26) does this job for us, we can not do it again,
-+	 * otherwise, the brightness will not resume to the normal level! */
-+	if (ec_version_before("EC_VER=PQ1D26"))
-+		vo_set_state(LCD, status);
-+
-+	return status;
-+}
-+
-+static int mypow(int x, int y)
-+{
-+	int i, j = x;
-+
-+	for (i = 1; i < y; i++)
-+		j *= j;
-+
-+	return j;
-+}
-+
-+static int switchvideomode_handler(int status)
-+{
-+	/* Default status: CRT|LCD = 0|1 = 1 */
-+	static int bin_state = 1;
-+	int i;
-+
-+	/*
-+	 * Only enable switch video output button
-+	 * when CRT is connected
-+	 */
-+	if (!vo_get_status(CRT))
-+		return 0;
-+	/*
-+	 * 2. no CRT connected: LCD on, CRT off
-+	 * 3. BOTH on
-+	 * 0. BOTH off
-+	 * 1. LCD off, CRT on
-+	 */
-+
-+	bin_state++;
-+	if (bin_state > mypow(2, VOD_NUM) - 1)
-+		bin_state = 0;
-+	
-+	for (i = 0; i < VOD_NUM; i++)
-+		vo_set_state(i, bin_state & (1 << i));
-+
-+	return bin_state;
-+}
-+
-+static int camera_handler(int status)
-+{
-+	int value;
-+
-+	value = ec_read(REG_CAMERA_CONTROL);
-+	ec_write(REG_CAMERA_CONTROL, value | (1 << 1));
-+
-+	return status;
-+}
-+
-+static int usb2_handler(int status)
-+{
-+	pr_emerg("USB2 Over Current occurred\n");
-+
-+	return status;
-+}
-+
-+static int usb0_handler(int status)
-+{
-+	pr_emerg("USB0 Over Current occurred\n");
-+
-+	return status;
-+}
-+
-+static int ac_bat_handler(int status)
-+{
-+	if (ac_bat_initialized) {
-+		power_supply_changed(&yeeloong_ac);
-+		power_supply_changed(&yeeloong_bat);
-+	}
-+
-+	return status;
-+}
-+
-+struct sci_event {
-+	int reg;
-+	sci_handler handler;
-+};
-+
-+static const struct sci_event se[] = {
-+	[EVENT_AC_BAT] = {0, ac_bat_handler},
-+	[EVENT_AUDIO_MUTE] = {REG_AUDIO_MUTE, NULL},
-+	[EVENT_AUDIO_VOLUME] = {REG_AUDIO_VOLUME, NULL},
-+	[EVENT_CRT_DETECT] = {REG_CRT_DETECT, crt_detect_handler},
-+	[EVENT_CAMERA] = {REG_CAMERA_STATUS, camera_handler},
-+	[EVENT_BLACK_SCREEN] = {REG_DISPLAY_LCD, displaytoggle_handler},
-+	[EVENT_DISPLAY_BRIGHTNESS] = {REG_DISPLAY_BRIGHTNESS, NULL},
-+	[EVENT_LID] = {REG_LID_DETECT, NULL},
-+	[EVENT_DISPLAY_TOGGLE] = {0, switchvideomode_handler},
-+	[EVENT_USB_OC0] = {REG_USB2_FLAG, usb0_handler},
-+	[EVENT_USB_OC2] = {REG_USB2_FLAG, usb2_handler},
-+	[EVENT_WLAN] = {REG_WLAN, NULL},
-+};
-+
-+static void do_event_action(int event)
-+{
-+	int status = -1;
-+	struct key_entry *ke;
-+	struct sci_event *sep;
-+
-+	sep = (struct sci_event *)&se[event];
-+
-+	if (sep->reg != 0)
-+		status = ec_read(sep->reg);
-+
-+	if (status == -1) {
-+		/* ec_read hasn't been called, status is invalid */
-+		return;
-+	}
-+
-+	if (sep->handler != NULL)
-+		status = sep->handler(status);
-+
-+	pr_debug("%s: event: %d status: %d\n", __func__, event, status);
-+
-+	/* Report current key to user-space */
-+	ke = get_event_key_entry(event, status);
-+
-+	/*
-+	 * Ignore the LID and SLEEP event when we are already in sleep or
-+	 * reboot state, this will avoid the recursive pm operations. but note:
-+	 * the report_lid_switch() called in arch/mips/loongson/lemote-2f/pm.c
-+	 * is necessary, because it is used to wake the system from sleep
-+	 * state. In the future, perhaps SW_LID should works like SLEEP, no
-+	 * need to function as a SWITCH, just report the state when the LID is
-+	 * closed is enough, this event can tell the software to "SLEEP", no
-+	 * need to tell the softwares when we are resuming from "SLEEP".
-+	 */
-+	if (ke && !is_fake_event(ke->keycode)) {
-+		if (ke->keycode == SW_LID)
-+			report_lid_switch(status);
-+		else
-+			sparse_keymap_report_entry(yeeloong_hotkey_dev, ke, 1,
-+					true);
-+	}
-+}
-+
-+/*
-+ * SCI(system control interrupt) main interrupt routine
-+ *
-+ * We will do the query and get event number together so the interrupt routine
-+ * should be longer than 120us now at least 3ms elpase for it.
-+ */
-+static irqreturn_t sci_irq_handler(int irq, void *dev_id)
-+{
-+	int ret, event;
-+
-+	if (SCI_IRQ_NUM != irq)
-+		return IRQ_NONE;
-+
-+	/* Query the event number */
-+	ret = ec_query_event_num();
-+	if (ret < 0)
-+		return IRQ_NONE;
-+
-+	event = ec_get_event_num();
-+	if (event < EVENT_START || event > EVENT_END)
-+		return IRQ_NONE;
-+
-+	/* Execute corresponding actions */
-+	do_event_action(event);
-+
-+	return IRQ_HANDLED;
-+}
-+
-+/*
-+ * Config and init some msr and gpio register properly.
-+ */
-+static int sci_irq_init(void)
-+{
-+	u32 hi, lo;
-+	u32 gpio_base;
-+	unsigned long flags;
-+	int ret;
-+
-+	/* Get gpio base */
-+	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &lo);
-+	gpio_base = lo & 0xff00;
-+
-+	/* Filter the former kb3310 interrupt for security */
-+	ret = ec_query_event_num();
-+	if (ret)
-+		return ret;
-+
-+	/* For filtering next number interrupt */
-+	udelay(10000);
-+
-+	/* Set gpio native registers and msrs for GPIO27 SCI EVENT PIN
-+	 * gpio :
-+	 *      input, pull-up, no-invert, event-count and value 0,
-+	 *      no-filter, no edge mode
-+	 *      gpio27 map to Virtual gpio0
-+	 * msr :
-+	 *      no primary and lpc
-+	 *      Unrestricted Z input to IG10 from Virtual gpio 0.
-+	 */
-+	local_irq_save(flags);
-+	_rdmsr(0x80000024, &hi, &lo);
-+	lo &= ~(1 << 10);
-+	_wrmsr(0x80000024, hi, lo);
-+	_rdmsr(0x80000025, &hi, &lo);
-+	lo &= ~(1 << 10);
-+	_wrmsr(0x80000025, hi, lo);
-+	_rdmsr(0x80000023, &hi, &lo);
-+	lo |= (0x0a << 0);
-+	_wrmsr(0x80000023, hi, lo);
-+	local_irq_restore(flags);
-+
-+	/* Set gpio27 as sci interrupt
-+	 *
-+	 * input, pull-up, no-fliter, no-negedge, invert
-+	 * the sci event is just about 120us
-+	 */
-+	asm(".set noreorder\n");
-+	/*  input enable */
-+	outl(0x00000800, (gpio_base | 0xA0));
-+	/*  revert the input */
-+	outl(0x00000800, (gpio_base | 0xA4));
-+	/*  event-int enable */
-+	outl(0x00000800, (gpio_base | 0xB8));
-+	asm(".set reorder\n");
-+
-+	return 0;
-+}
-+
-+static int notify_reboot(struct notifier_block *nb, unsigned long event, void *buf)
-+{
-+	switch (event) {
-+	case SYS_RESTART:
-+	case SYS_HALT:
-+	case SYS_POWER_OFF:
-+		atomic_set(in_reboot(), 1);
-+		break;
-+	default:
-+		return NOTIFY_DONE;
-+	}
-+
-+	return NOTIFY_OK;
-+}
-+
-+static int notify_pm(struct notifier_block *nb, unsigned long event, void *buf)
-+{
-+	switch (event) {
-+	case PM_HIBERNATION_PREPARE:
-+	case PM_SUSPEND_PREPARE:
-+		atomic_inc(in_sleep());
-+		break;
-+	case PM_POST_HIBERNATION:
-+	case PM_POST_SUSPEND:
-+	case PM_RESTORE_PREPARE:	/* do we need this ?? */
-+		atomic_dec(in_sleep());
-+		break;
-+	default:
-+		return NOTIFY_DONE;
-+	}
-+
-+	pr_debug("%s: event = %lu, in_sleep() = %d\n", __func__, event,
-+			atomic_read(in_sleep()));
-+
-+	return NOTIFY_OK;
-+}
-+
-+static struct notifier_block reboot_notifier = {
-+	.notifier_call = notify_reboot,
-+};
-+
-+static struct notifier_block pm_notifier = {
-+	.notifier_call = notify_pm,
-+};
-+
-+static int yeeloong_hotkey_init(void)
-+{
-+	int ret = 0;
-+
-+	ret = register_reboot_notifier(&reboot_notifier);
-+	if (ret) {
-+		pr_err("Can't register reboot notifier\n");
-+		goto end;
-+	}
-+
-+	ret = register_pm_notifier(&pm_notifier);
-+	if (ret) {
-+		pr_err("Can't register pm notifier\n");
-+		goto free_reboot_notifier;
-+	}
-+
-+	ret = sci_irq_init();
-+	if (ret) {
-+		pr_err("Can't init SCI interrupt\n");
-+		goto free_pm_notifier;
-+	}
-+
-+	ret = request_threaded_irq(SCI_IRQ_NUM, NULL, &sci_irq_handler,
-+			IRQF_ONESHOT, "sci", NULL);
-+	if (ret) {
-+		pr_err("Can't thread SCI interrupt handler\n");
-+		goto free_pm_notifier;
-+	}
-+
-+	yeeloong_hotkey_dev = input_allocate_device();
-+
-+	if (!yeeloong_hotkey_dev) {
-+		ret = -ENOMEM;
-+		goto free_irq;
-+	}
-+
-+	yeeloong_hotkey_dev->name = "HotKeys";
-+	yeeloong_hotkey_dev->phys = "button/input0";
-+	yeeloong_hotkey_dev->id.bustype = BUS_HOST;
-+	yeeloong_hotkey_dev->dev.parent = NULL;
-+
-+	ret = sparse_keymap_setup(yeeloong_hotkey_dev, yeeloong_keymap, NULL);
-+	if (ret) {
-+		pr_err("Failed to setup input device keymap\n");
-+		goto free_dev;
-+	}
-+
-+	ret = input_register_device(yeeloong_hotkey_dev);
-+	if (ret)
-+		goto free_keymap;
-+
-+	/* Update the current status of LID */
-+	report_lid_switch(ON);
-+
-+#ifdef CONFIG_LOONGSON_SUSPEND
-+	/* Install the real yeeloong_report_lid_status for pm.c */
-+	yeeloong_report_lid_status = report_lid_switch;
-+#endif
-+	return 0;
-+
-+free_keymap:
-+	sparse_keymap_free(yeeloong_hotkey_dev);
-+free_dev:
-+	input_free_device(yeeloong_hotkey_dev);
-+free_irq:
-+	free_irq(SCI_IRQ_NUM, NULL);
-+free_pm_notifier:
-+	unregister_pm_notifier(&pm_notifier);
-+free_reboot_notifier:
-+	unregister_reboot_notifier(&reboot_notifier);
-+end:
-+	return ret;
-+}
-+
-+static void yeeloong_hotkey_exit(void)
-+{
-+	/* Free irq */
-+	free_irq(SCI_IRQ_NUM, NULL);
-+
-+#ifdef CONFIG_LOONGSON_SUSPEND
-+	/* Uninstall yeeloong_report_lid_status for pm.c */
-+	if (yeeloong_report_lid_status == report_lid_switch)
-+		yeeloong_report_lid_status = NULL;
-+#endif
-+
-+	if (yeeloong_hotkey_dev) {
-+		sparse_keymap_free(yeeloong_hotkey_dev);
-+		input_unregister_device(yeeloong_hotkey_dev);
-+		yeeloong_hotkey_dev = NULL;
-+	}
-+}
-+
-+#ifdef CONFIG_PM
-+static void usb_ports_set(int status)
-+{
-+	status = !!status;
-+
-+	ec_write(REG_USB0_FLAG, status);
-+	ec_write(REG_USB1_FLAG, status);
-+	ec_write(REG_USB2_FLAG, status);
-+}
-+
-+static int yeeloong_suspend(struct device *dev)
-+
-+{
-+	if (ec_version_before("EC_VER=PQ1D27"))
-+		vo_set_state(LCD, OFF);
-+	vo_set_state(CRT, OFF);
-+	usb_ports_set(OFF);
-+
-+	return 0;
-+}
-+
-+static int yeeloong_resume(struct device *dev)
-+{
-+	int ret;
-+
-+	if (ec_version_before("EC_VER=PQ1D27"))
-+		vo_set_state(LCD, ON);
-+	vo_set_state(CRT, ON);
-+	usb_ports_set(ON);
-+
-+	ret = sci_irq_init();
-+	if (ret)
-+		return -EFAULT;
-+
-+	return 0;
-+}
-+
-+static const SIMPLE_DEV_PM_OPS(yeeloong_pm_ops, yeeloong_suspend,
-+	yeeloong_resume);
-+#endif
-+
-+static struct platform_device_id platform_device_ids[] = {
-+	{
-+		.name = "yeeloong_laptop",
-+	},
-+	{}
-+};
-+
-+MODULE_DEVICE_TABLE(platform, platform_device_ids);
-+
-+static struct platform_driver platform_driver = {
-+	.driver = {
-+		.name = "yeeloong_laptop",
-+		.owner = THIS_MODULE,
-+#ifdef CONFIG_PM
-+		.pm = &yeeloong_pm_ops,
-+#endif
-+	},
-+	.id_table = platform_device_ids,
-+};
-+
-+static int __init yeeloong_init(void)
-+{
-+	int ret;
-+
-+	pr_info("YeeLoong Laptop platform specific driver loaded.\n");
-+
-+	/* Register platform stuff */
-+	ret = platform_driver_register(&platform_driver);
-+	if (ret) {
-+		pr_err("Failed to register YeeLoong platform driver.\n");
-+		return ret;
-+	}
-+
-+#define yeeloong_init_drv(drv, alias) do {			\
-+	pr_info("Registered YeeLoong " alias " driver.\n");	\
-+	ret = yeeloong_ ## drv ## _init();			\
-+	if (ret) {						\
-+		pr_err("Failed to register YeeLoong " alias " driver.\n");	\
-+		yeeloong_ ## drv ## _exit();			\
-+		return ret;					\
-+	}							\
-+} while (0)
-+
-+	yeeloong_init_drv(backlight, "backlight");
-+	yeeloong_init_drv(bat, "battery and AC");
-+	yeeloong_init_drv(hwmon, "hardware monitor");
-+	yeeloong_init_drv(vo, "video output");
-+	yeeloong_init_drv(lcd, "lcd output");
-+	yeeloong_init_drv(hotkey, "hotkey input");
-+
-+	return 0;
-+}
-+
-+static void __exit yeeloong_exit(void)
-+{
-+	yeeloong_hotkey_exit();
-+	yeeloong_lcd_exit();
-+	yeeloong_vo_exit();
-+	yeeloong_hwmon_exit();
-+	yeeloong_bat_exit();
-+	yeeloong_backlight_exit();
-+	platform_driver_unregister(&platform_driver);
-+
-+	pr_info("YeeLoong platform specific driver unloaded.\n");
-+}
-+
-+module_init(yeeloong_init);
-+module_exit(yeeloong_exit);
-+
-+MODULE_AUTHOR("Wu Zhangjin <wuzhangjin@gmail.com>; Liu Junliang <liujl@lemote.com>");
-+MODULE_DESCRIPTION("YeeLoong laptop driver");
-+MODULE_LICENSE("GPL");
-diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
-index db933de..1232c4b 100644
---- a/drivers/rtc/Kconfig
-+++ b/drivers/rtc/Kconfig
-@@ -649,6 +649,7 @@ comment "Platform RTC drivers"
- config RTC_DRV_CMOS
- 	tristate "PC-style 'CMOS'"
- 	depends on X86 || ARM || M32R || PPC || MIPS || SPARC64
-+	depends on !DEXXON_GDIUM
- 	default y if X86
- 	help
- 	  Say "yes" here to get direct support for the real time clock
-diff --git a/drivers/staging/sm7xxfb/sm7xxfb.c b/drivers/staging/sm7xxfb/sm7xxfb.c
-index 6176d98..e40ce80 100644
---- a/drivers/staging/sm7xxfb/sm7xxfb.c
-+++ b/drivers/staging/sm7xxfb/sm7xxfb.c
-@@ -101,6 +101,7 @@ static struct vesa_mode vesa_mode_table[] = {
- 	{"0x307", 1280, 1024, 8},
- 
- 	{"0x311", 640,  480,  16},
-+	{"0x313", 800,  480,  16},
- 	{"0x314", 800,  600,  16},
- 	{"0x317", 1024, 768,  16},
- 	{"0x31A", 1280, 1024, 16},
-diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
-index 3586460..15f66e5 100644
---- a/drivers/usb/host/ohci-hcd.c
-+++ b/drivers/usb/host/ohci-hcd.c
-@@ -864,9 +864,13 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
- 	}
- 
- 	if (ints & OHCI_INTR_WDH) {
--		spin_lock (&ohci->lock);
--		dl_done_list (ohci);
--		spin_unlock (&ohci->lock);
-+		if (ohci->hcca->done_head == 0) {
-+			ints &= ~OHCI_INTR_WDH;
-+		} else {
-+			spin_lock (&ohci->lock);
-+			dl_done_list (ohci);
-+			spin_unlock (&ohci->lock);
-+		}
- 	}
- 
- 	if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) {
-diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
-index 2f3aceb..6647c3f 100644
---- a/drivers/usb/host/pci-quirks.c
-+++ b/drivers/usb/host/pci-quirks.c
-@@ -454,6 +454,7 @@ void usb_amd_dev_put(void)
- }
- EXPORT_SYMBOL_GPL(usb_amd_dev_put);
- 
-+#if defined(CONFIG_USB_UHCI_HCD) || defined(CONFIG_USB_UHCI_HCD_MODULE)
- /*
-  * Make sure the controller is completely inactive, unable to
-  * generate interrupts or do DMA.
-@@ -561,12 +562,16 @@ static void quirk_usb_handoff_uhci(struct pci_dev *pdev)
- 	if (base)
- 		uhci_check_and_reset_hc(pdev, base);
- }
-+#else
-+#define quirk_usb_handoff_uhci(x) do { } while (0)
-+#endif /* CONFIG_USB_UHCI_HCD* */
- 
- static int mmio_resource_enabled(struct pci_dev *pdev, int idx)
- {
- 	return pci_resource_start(pdev, idx) && mmio_enabled(pdev);
- }
- 
-+#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
- static void quirk_usb_handoff_ohci(struct pci_dev *pdev)
- {
- 	void __iomem *base;
-@@ -633,7 +638,11 @@ static void quirk_usb_handoff_ohci(struct pci_dev *pdev)
- 	/* Now the controller is safely in SUSPEND and nothing can wake it up */
- 	iounmap(base);
- }
-+#else
-+#define quirk_usb_handoff_ohci(x) do { } while(0)
-+#endif /* CONFIG_USB_OHCI_HCD* */
- 
-+#if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_EHCI_HCD_MODULE)
- static const struct dmi_system_id ehci_dmi_nohandoff_table[] = {
- 	{
- 		/*  Pegatron Lucid (ExoPC) */
-@@ -806,6 +815,9 @@ static void quirk_usb_disable_ehci(struct pci_dev *pdev)
- 
- 	iounmap(base);
- }
-+#else
-+#define quirk_usb_disable_ehci(x) do { } while (0)
-+#endif /* CONFIG_USB_EHCI_HCD* */
- 
- /*
-  * handshake - spin reading a register until handshake completes
-@@ -945,6 +957,7 @@ void usb_disable_xhci_ports(struct pci_dev *xhci_pdev)
- }
- EXPORT_SYMBOL_GPL(usb_disable_xhci_ports);
- 
-+#if defined(CONFIG_USB_XHCI_HCD) || defined(CONFIG_USB_XHCI_HCD_MODULE)
- /**
-  * PCI Quirks for xHCI.
-  *
-@@ -1052,6 +1065,9 @@ hc_init:
- 
- 	iounmap(base);
- }
-+#else
-+#define quirk_usb_handoff_xhci(x) do { } while (0)
-+#endif /* CONFIG_USB_UHCI_HCD* */
- 
- static void quirk_usb_early_handoff(struct pci_dev *pdev)
- {
-diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
-index 9da566a..fffecfb 100644
---- a/drivers/usb/serial/option.c
-+++ b/drivers/usb/serial/option.c
-@@ -79,6 +79,9 @@ static void option_instat_callback(struct urb *urb);
- #define OPTION_PRODUCT_ETNA_KOI_MODEM		0x7100
- #define OPTION_PRODUCT_GTM380_MODEM		0x7201
- 
-+#define HUAWO_VENDOR_ID				0x21F5
-+#define HUAWO_PRODUCT_E1621			0x2008
-+
- #define HUAWEI_VENDOR_ID			0x12D1
- #define HUAWEI_PRODUCT_E173			0x140C
- #define HUAWEI_PRODUCT_E1750			0x1406
-@@ -610,6 +613,7 @@ static const struct usb_device_id option_ids[] = {
- 	{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) },
- 	{ USB_DEVICE(QUANTA_VENDOR_ID, 0xea42),
- 		.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
-+	{ USB_DEVICE(HUAWO_VENDOR_ID, HUAWO_PRODUCT_E1621) },	/* QUANTA 6500 chips, Unicom extensive use of this card */
- 	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c05, USB_CLASS_COMM, 0x02, 0xff) },
- 	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c1f, USB_CLASS_COMM, 0x02, 0xff) },
- 	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c23, USB_CLASS_COMM, 0x02, 0xff) },
-diff --git a/include/linux/sm501.h b/include/linux/sm501.h
-index 02fde50..a8677f0 100644
---- a/include/linux/sm501.h
-+++ b/include/linux/sm501.h
-@@ -27,6 +27,9 @@ extern unsigned long sm501_set_clock(struct device *dev,
- extern unsigned long sm501_find_clock(struct device *dev,
- 				      int clksrc, unsigned long req_freq);
- 
-+extern void sm501_configure_gpio(struct device *dev,
-+				unsigned int gpio, unsigned char mode);
-+
- /* sm501_misc_control
-  *
-  * Modify the SM501's MISC_CONTROL register
-@@ -122,6 +125,7 @@ struct sm501_reg_init {
- #define SM501_USE_AC97		(1<<7)
- #define SM501_USE_I2S		(1<<8)
- #define SM501_USE_GPIO		(1<<9)
-+#define SM501_USE_PWM		(1<<10)
- 
- #define SM501_USE_ALL		(0xffffffff)
- 
-diff --git a/init/calibrate.c b/init/calibrate.c
-index 520702d..e78762a 100644
---- a/init/calibrate.c
-+++ b/init/calibrate.c
-@@ -21,6 +21,7 @@ static int __init lpj_setup(char *str)
- 
- __setup("lpj=", lpj_setup);
- 
-+#ifndef ARCH_HAS_PREPARED_LPJ
- #ifdef ARCH_HAS_READ_CURRENT_TIMER
- 
- /* This routine uses the read_current_timer() routine and gets the
-@@ -171,6 +172,7 @@ static unsigned long calibrate_delay_direct(void)
- 	return 0;
- }
- #endif
-+#endif	/* ARCH_HAS_PREPARED_LPJ */
- 
- /*
-  * This is the number of bits of precision for the loops_per_jiffy.  Each
-@@ -282,6 +284,7 @@ void calibrate_delay(void)
- 		lpj = lpj_fine;
- 		pr_info("Calibrating delay loop (skipped), "
- 			"value calculated using timer frequency.. ");
-+#ifndef ARCH_HAS_PREPARED_LPJ
- 	} else if ((lpj = calibrate_delay_is_known())) {
- 		;
- 	} else if ((lpj = calibrate_delay_direct()) != 0) {
-@@ -292,6 +295,7 @@ void calibrate_delay(void)
- 		if (!printed)
- 			pr_info("Calibrating delay loop... ");
- 		lpj = calibrate_delay_converge();
-+#endif	/* ARCH_HAS_PREPARED_LPJ */
- 	}
- 	per_cpu(cpu_loops_per_jiffy, this_cpu) = lpj;
- 	if (!printed)
-diff --git a/net/rfkill/core.c b/net/rfkill/core.c
-index ed7e0b4..6cb1ae8 100644
---- a/net/rfkill/core.c
-+++ b/net/rfkill/core.c
-@@ -111,7 +111,7 @@ static LIST_HEAD(rfkill_list);	/* list of registered rf switches */
- static DEFINE_MUTEX(rfkill_global_mutex);
- static LIST_HEAD(rfkill_fds);	/* list of open fds of /dev/rfkill */
- 
--static unsigned int rfkill_default_state = 1;
-+static unsigned int rfkill_default_state;	/* default: 0 = radio off */
- module_param_named(default_state, rfkill_default_state, uint, 0444);
- MODULE_PARM_DESC(default_state,
- 		 "Default initial state for all radio types, 0 = radio off");
-diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
-index 91280b8..6b147ae 100755
---- a/scripts/recordmcount.pl
-+++ b/scripts/recordmcount.pl
-@@ -307,14 +307,33 @@ if ($arch eq "x86_64") {
-     $cc .= " -m64";
-     $objcopy .= " -O elf64-sparc";
- } elsif ($arch eq "mips") {
--    # To enable module support, we need to enable the -mlong-calls option
--    # of gcc for module, after using this option, we can not get the real
--    # offset of the calling to _mcount, but the offset of the lui
--    # instruction or the addiu one. herein, we record the address of the
--    # first one, and then we can replace this instruction by a branch
--    # instruction to jump over the profiling function to filter the
--    # indicated functions, or swith back to the lui instruction to trace
--    # them, which means dynamic tracing.
-+    # <For kernel>
-+    # To disable tracing, just replace "jal _mcount" with nop;
-+    # to enable tracing, replace back. so, the offset 14 is
-+    # needed to be recorded.
-+    #
-+    #     10:   03e0082d        move    at,ra
-+    #	  14:   0c000000        jal     0
-+    #                    14: R_MIPS_26   _mcount
-+    #                    14: R_MIPS_NONE *ABS*
-+    #                    14: R_MIPS_NONE *ABS*
-+    #	 18:   00020021        nop
-+    #
-+    # <For module>
-+    #
-+    # If no long call(-mlong-calls), the same to kernel.
-+    #
-+    # If the module space differs from the kernel space, long
-+    # call is needed, as a result, the address of _mcount is
-+    # needed to be recorded in a register and then jump from
-+    # module space to kernel space via "jalr <register>". To
-+    # disable tracing, "jalr <register>" can be replaced by
-+    # nop; to enable tracing, replace it back. Since the
-+    # offset of "jalr <register>" is not easy to be matched,
-+    # the offset of the 1st _mcount below is recorded and to
-+    # disable tracing, "lui v1, 0x0" is substituted with "b
-+    # label", which jumps over "jalr <register>"; to enable
-+    # tracing, replace it back.
-     #
-     #       c:	3c030000 	lui	v1,0x0
-     #			c: R_MIPS_HI16	_mcount
-@@ -326,19 +345,12 @@ if ($arch eq "x86_64") {
-     #			10: R_MIPS_NONE	*ABS*
-     #      14:	03e0082d 	move	at,ra
-     #      18:	0060f809 	jalr	v1
-+    #                     label:
-     #
--    # for the kernel:
--    #
--    #     10:   03e0082d        move    at,ra
--    #	  14:   0c000000        jal     0 <loongson_halt>
--    #                    14: R_MIPS_26   _mcount
--    #                    14: R_MIPS_NONE *ABS*
--    #                    14: R_MIPS_NONE *ABS*
--    #	 18:   00020021        nop
-     if ($is_module eq "0") {
- 	    $mcount_regex = "^\\s*([0-9a-fA-F]+): R_MIPS_26\\s+_mcount\$";
-     } else {
--	    $mcount_regex = "^\\s*([0-9a-fA-F]+): R_MIPS_HI16\\s+_mcount\$";
-+	    $mcount_regex = "^\\s*([0-9a-fA-F]+): R_MIPS_(HI16|26)\\s+_mcount\$";
-     }
-     $objdump .= " -Melf-trad".$endian."mips ";
- 
-diff --git a/scripts/sstrip.sh b/scripts/sstrip.sh
-new file mode 100755
-index 0000000..49b973a
---- /dev/null
-+++ b/scripts/sstrip.sh
-@@ -0,0 +1,59 @@
-+#!/bin/bash
-+# sstrip.sh -- strip the section table of an elf file
-+#
-+# Copyright (C) 2010 Wu Zhangjin, wuzhangjin@gmail.com
-+# Licensed under the GPLv2
-+#
-+# Since the section table is useless for the embedded device, it can be
-+# stripped out.
-+#
-+# Note: Some bootloader may check the section table but most of the time, it
-+# may be not really used, If it really need the section table, it may need the
-+# decompressed kernel image.
-+
-+# Usage
-+
-+function usage
-+{
-+cat <<EOF
-+
-+	# sstrip.sh -- strip the section table of an elf file
-+
-+	# Input: elf file
-+	# Output: truncated elf file without the section table
-+	# Usage: sstrip.sh /path/to/image
-+
-+EOF
-+}
-+
-+# Do some necessary check
-+IMAGE=$1
-+
-+[ -z "${IMAGE}" ] && echo "$0 : No indicated file to be stripped" && usage && exit -1
-+[ ! -f "${IMAGE}" ] && echo "$0 : ${IMAGE} : No such file" && exit -1
-+FILE_TYPE=`dd if=${IMAGE} bs=1 skip=1 count=3 2>/dev/null`
-+[ "xELF" != "x${FILE_TYPE}" ] && echo "$0: ${IMAGE} is not an ELF file" && exit -1
-+
-+[ "x${V}" == "x1" ] && orig_filesz=`wc -c ${IMAGE} | cut -d' ' -f1`
-+
-+# Get the offset of the section table, here get the end of the program section
-+filesz=$((`${OBJDUMP} -p ${IMAGE} | grep -m1 filesz | tr -s ' ' | cut -d' ' -f3`))
-+
-+# Truncate it via the dd tool
-+dd if=/dev/null bs=1 of=${IMAGE} seek=${filesz} 2>/dev/null
-+
-+# Clear the section table information in the ELF header
-+# The last 6 bytes of the ELF header are the section table information
-+echo -ne "\x00\x00\x00\x00\x00\x00" | dd of=${IMAGE} bs=1 seek=46 count=6 conv=notrunc 2>/dev/null
-+
-+# Debug
-+if [ "x${V}" == "x1" ]; then
-+	echo "----------------------------------------------------------------"
-+	echo "Strip the section table at ${filesz} of ${IMAGE}"
-+	echo "----------------------------------------------------------------"
-+	echo "       sstrip: $0"
-+	echo "      objdump: ${OBJDUMP}"
-+	echo "original size: ${orig_filesz}"
-+	echo "current  size: ${filesz}"
-+	echo "reduced  size: $((${orig_filesz} - ${filesz}))"
-+fi
diff --git a/kernels/linux-libre-lts-knock/3.14.26-8475f027b4-loongson-community.patch b/kernels/linux-libre-lts-knock/3.14.26-8475f027b4-loongson-community.patch
new file mode 100644
index 000000000..20f540673
--- /dev/null
+++ b/kernels/linux-libre-lts-knock/3.14.26-8475f027b4-loongson-community.patch
@@ -0,0 +1,10872 @@
+diff --git a/Makefile b/Makefile
+index 63a5ee8..9b779e2 100644
+--- a/Makefile
++++ b/Makefile
+@@ -244,8 +244,8 @@ CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
+ 
+ HOSTCC       = gcc
+ HOSTCXX      = g++
+-HOSTCFLAGS   = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer
+-HOSTCXXFLAGS = -O2
++HOSTCFLAGS   = -Wall -Wmissing-prototypes -Wstrict-prototypes -O3 -fomit-frame-pointer
++HOSTCXXFLAGS = -O3
+ 
+ # Decide whether to build built-in, modular, or both.
+ # Normally, just do built-in.
+@@ -582,7 +582,7 @@ all: vmlinux
+ ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
+ KBUILD_CFLAGS	+= -Os $(call cc-disable-warning,maybe-uninitialized,)
+ else
+-KBUILD_CFLAGS	+= -O2
++KBUILD_CFLAGS	+= -O3
+ endif
+ 
+ include $(srctree)/arch/$(SRCARCH)/Makefile
+diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
+index 95fa1f1..82dc7e8 100644
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -278,7 +278,7 @@ config LASAT
+ 
+ config MACH_LOONGSON
+ 	bool "Loongson family of machines"
+-	select SYS_SUPPORTS_ZBOOT
++	select SYS_SUPPORTS_ZBOOT_UART16550
+ 	help
+ 	  This enables the support of Loongson family of machines.
+ 
+@@ -885,6 +885,60 @@ config CSRC_IOASIC
+ config CSRC_R4K
+ 	bool
+ 
++config MIPS_USER_RDTSC
++	bool "Emulate rdtsc instruction for MIPS"
++	depends on CSRC_R4K && MIPS32_O32
++	default n
++	help
++	  This optoin enables the Emulated rdtsc support for MIPS, which allows
++	  the user-space applications read the R4k count directly. Currently,
++	  this only support the CONFIG_MIPS32_O32 and R4K, but future, we may
++	  add support for scall64-{n32,64}.S and scall32-32.S and for the count
++	  registers provided by the other MIPS variants.
++
++	  This emulation based on the syscall instruction, by default, the
++	  syscall is encoded as 0x0000000c, except the 0xc, the other parts can
++	  be encoded as specific meaning. when a syscall instruction is issued,
++	  through checking the encoding of the instruction, when the encoding
++	  is the generic 0x000000c, we do the generic syscall work, if
++	  something other is encoded in, we can do relevant things, except for
++	  the light-weight things, such as read a register. herein, we read the
++	  count register whenever there is something encoded in the syscall
++	  instruction. In the future, we may be possible to abstract more
++	  light-weight & frequently-used operations and add a
++	  sys_call_table-like table to store the entries of some light-weight
++	  operations and encode 1,2,3... into the syscall instruction and jump
++	  to respective entry for diffrent numbers, as a result, we get
++	  fast-syscall and which may speed up the user-space applications and
++	  even be possibly improve the determinism.
++
++	  *Example*
++
++	  #include <stdio.h>
++	  #include <stdint.h>
++
++	  /*
++	   * Currently, our return value is only 32bit, In the long run,
++	   * this should be uint64_t, just like clock_gettime(), but it
++	   * should has high precision/low overhead than clock_gettime()
++	   */
++	  uint32_t rdtsc(void)
++	  {
++		  /*
++		   * Linux will store the value of the count register into
++		   * the v0 register, which is just the return value of this
++		   * function, so, please ignore the compiling warning.
++		   */
++		  __asm__ __volatile__ (
++			  "syscall 1\n"
++		  :::"$2");
++	  }
++
++	  int main(int argc, char *argv[])
++	  {
++		  return printf("cycles: %u\n", rdtsc());
++	  }
++
+ config CSRC_GIC
+ 	bool
+ 
+@@ -1492,6 +1546,15 @@ config CPU_LOONGSON2
+ 	bool
+ 	select CPU_SUPPORTS_32BIT_KERNEL
+ 	select CPU_SUPPORTS_64BIT_KERNEL
++	select CPU_SUPPORTS_HIGHMEM if ! EMBEDDED
++	select ARCH_WANT_OPTIONAL_GPIOLIB
++
++config CPU_LOONGSON1
++	bool
++	select CPU_MIPS32
++	select CPU_MIPSR2
++	select CPU_HAS_PREFETCH
++	select CPU_SUPPORTS_32BIT_KERNEL
+ 	select CPU_SUPPORTS_HIGHMEM
+ 	select CPU_SUPPORTS_HUGEPAGES
+ 
+@@ -2110,7 +2173,7 @@ config SYS_SUPPORTS_MICROMIPS
+ 
+ config ARCH_FLATMEM_ENABLE
+ 	def_bool y
+-	depends on !NUMA && !CPU_LOONGSON2
++	depends on !NUMA && !(CPU_LOONGSON2 && HIBERNATION)
+ 
+ config ARCH_DISCONTIGMEM_ENABLE
+ 	bool
+diff --git a/arch/mips/Kconfig.debug b/arch/mips/Kconfig.debug
+index b147e70..f32dfb0 100644
+--- a/arch/mips/Kconfig.debug
++++ b/arch/mips/Kconfig.debug
+@@ -7,9 +7,9 @@ config TRACE_IRQFLAGS_SUPPORT
+ source "lib/Kconfig.debug"
+ 
+ config EARLY_PRINTK
+-	bool "Early printk" if EXPERT
++	bool "Early printk"
+ 	depends on SYS_HAS_EARLY_PRINTK
+-	default y
++	default n 
+ 	help
+ 	  This option enables special console drivers which allow the kernel
+ 	  to print messages very early in the bootup process.
+diff --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile
+index 61af6b6..8598044 100644
+--- a/arch/mips/boot/compressed/Makefile
++++ b/arch/mips/boot/compressed/Makefile
+@@ -30,9 +30,10 @@ KBUILD_AFLAGS := $(LINUXINCLUDE) $(KBUILD_AFLAGS) -D__ASSEMBLY__ \
+ targets := head.o decompress.o string.o dbg.o uart-16550.o uart-alchemy.o
+ 
+ # decompressor objects (linked with vmlinuz)
+-vmlinuzobjs-y := $(obj)/head.o $(obj)/decompress.o $(obj)/string.o $(obj)/dbg.o
++vmlinuzobjs-y := $(obj)/head.o $(obj)/decompress.o $(obj)/string.o
+ 
+ ifdef CONFIG_DEBUG_ZBOOT
++vmlinuzobjs-y += $(obj)/dbg.o
+ vmlinuzobjs-$(CONFIG_SYS_SUPPORTS_ZBOOT_UART16550) += $(obj)/uart-16550.o
+ vmlinuzobjs-$(CONFIG_MIPS_ALCHEMY)		   += $(obj)/uart-alchemy.o
+ endif
+@@ -79,9 +80,18 @@ quiet_cmd_zld = LD	$@
+       cmd_zld = $(LD) $(LDFLAGS) -Ttext $(VMLINUZ_LOAD_ADDRESS) -T $< $(vmlinuzobjs-y) -o $@
+ quiet_cmd_strip = STRIP	  $@
+       cmd_strip = $(STRIP) -s $@
++ifdef CONFIG_EMBEDDED
++quiet_cmd_sstrip = SSTRIP  $@
++      cmd_sstrip = $(srctree)/scripts/sstrip.sh $@
++endif
+ vmlinuz: $(src)/ld.script $(vmlinuzobjs-y) $(obj)/calc_vmlinuz_load_addr
+ 	$(call cmd,zld)
+ 	$(call cmd,strip)
++	$(call cmd,sstrip)
++
++vmlinuz.unsstrip: $(src)/ld.script $(vmlinuzobjs-y) $(obj)/calc_vmlinuz_load_addr
++	$(call cmd,zld)
++	$(call cmd,strip)
+ 
+ #
+ # Some DECstations need all possible sections of an ECOFF executable
+@@ -94,14 +104,14 @@ endif
+ hostprogs-y += ../elf2ecoff
+ 
+ ifdef CONFIG_32BIT
+-	VMLINUZ = vmlinuz
++	VMLINUZ = vmlinuz.unsstrip
+ else
+ 	VMLINUZ = vmlinuz.32
+ endif
+ 
+ quiet_cmd_32 = OBJCOPY $@
+       cmd_32 = $(OBJCOPY) -O $(32bit-bfd) $(OBJCOPYFLAGS) $< $@
+-vmlinuz.32: vmlinuz
++vmlinuz.32: vmlinuz.unsstrip
+ 	$(call cmd,32)
+ 
+ quiet_cmd_ecoff = ECOFF	  $@
+@@ -110,11 +120,11 @@ vmlinuz.ecoff: $(obj)/../elf2ecoff $(VMLINUZ)
+ 	$(call cmd,ecoff)
+ 
+ OBJCOPYFLAGS_vmlinuz.bin := $(OBJCOPYFLAGS) -O binary
+-vmlinuz.bin: vmlinuz
++vmlinuz.bin: vmlinuz.unsstrip
+ 	$(call cmd,objcopy)
+ 
+ OBJCOPYFLAGS_vmlinuz.srec := $(OBJCOPYFLAGS) -S -O srec
+-vmlinuz.srec: vmlinuz
++vmlinuz.srec: vmlinuz.unsstrip
+ 	$(call cmd,objcopy)
+ 
+-clean-files := $(objtree)/vmlinuz $(objtree)/vmlinuz.{32,ecoff,bin,srec}
++clean-files := $(objtree)/vmlinuz $(objtree)/vmlinuz.{32,ecoff,bin,srec,unsstrip}
+diff --git a/arch/mips/boot/compressed/decompress.c b/arch/mips/boot/compressed/decompress.c
+index 5244cec..965734f1 100644
+--- a/arch/mips/boot/compressed/decompress.c
++++ b/arch/mips/boot/compressed/decompress.c
+@@ -28,8 +28,13 @@ unsigned long free_mem_end_ptr;
+ extern unsigned char __image_begin, __image_end;
+ 
+ /* debug interfaces  */
++#ifdef CONFIG_DEBUG_ZBOOT
+ extern void puts(const char *s);
+ extern void puthex(unsigned long long val);
++#else
++#define puts(s)
++#define puthex(val)
++#endif
+ 
+ void error(char *x)
+ {
+diff --git a/arch/mips/boot/compressed/ld.script b/arch/mips/boot/compressed/ld.script
+index 5a33409..de04ac9 100644
+--- a/arch/mips/boot/compressed/ld.script
++++ b/arch/mips/boot/compressed/ld.script
+@@ -49,5 +49,6 @@ SECTIONS
+ 		*(.reginfo)
+ 		*(.comment)
+ 		*(.note)
++		*(.gnu.attributes)
+ 	}
+ }
+diff --git a/arch/mips/include/asm/dma-mapping.h b/arch/mips/include/asm/dma-mapping.h
+index 84238c5..28e0b03 100644
+--- a/arch/mips/include/asm/dma-mapping.h
++++ b/arch/mips/include/asm/dma-mapping.h
+@@ -6,9 +6,7 @@
+ #include <asm/cache.h>
+ #include <asm-generic/dma-coherent.h>
+ 
+-#ifndef CONFIG_SGI_IP27 /* Kludge to fix 2.6.39 build for IP27 */
+ #include <dma-coherence.h>
+-#endif
+ 
+ extern struct dma_map_ops *mips_dma_map_ops;
+ 
+diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
+index a0ee0cb..c6df1c4 100644
+--- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
++++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
+@@ -299,7 +299,42 @@ extern void _wrmsr(u32 msr, u32 hi, u32 lo);
+ 
+ /****************** NATIVE ***************************/
+ /* GPIO : I/O SPACE; REG : 32BITS */
+-#define GPIOL_OUT_VAL		0x00
+-#define GPIOL_OUT_EN		0x04
++#define	GPIOL_OUT_VAL		0x00
++#define	GPIOL_OUT_EN		0x04
++#define	GPIOL_OUT_AUX1_SEL	0x10
++/* SMB : I/O SPACE, REG : 8BITS WIDTH */
++#define	SMB_SDA			0x00
++#define	SMB_STS			0x01
++#define	SMB_STS_SLVSTP		(1 << 7)
++#define	SMB_STS_SDAST		(1 << 6)
++#define	SMB_STS_BER		(1 << 5)
++#define	SMB_STS_NEGACK		(1 << 4)
++#define	SMB_STS_STASTR		(1 << 3)
++#define	SMB_STS_NMATCH		(1 << 2)
++#define	SMB_STS_MASTER		(1 << 1)
++#define	SMB_STS_XMIT		(1 << 0)
++#define	SMB_CTRL_STS		0x02
++#define	SMB_CSTS_TGSTL		(1 << 5)
++#define	SMB_CSTS_TSDA		(1 << 4)
++#define	SMB_CSTS_GCMTCH		(1 << 3)
++#define	SMB_CSTS_MATCH		(1 << 2)
++#define	SMB_CSTS_BB		(1 << 1)
++#define	SMB_CSTS_BUSY		(1 << 0)
++#define	SMB_CTRL1		0x03
++#define	SMB_CTRL1_STASTRE	(1 << 7)
++#define	SMB_CTRL1_NMINTE	(1 << 6)
++#define	SMB_CTRL1_GCMEN		(1 << 5)
++#define	SMB_CTRL1_ACK		(1 << 4)
++#define	SMB_CTRL1_RSVD		(1 << 3)
++#define	SMB_CTRL1_INTEN		(1 << 2)
++#define	SMB_CTRL1_STOP		(1 << 1)
++#define	SMB_CTRL1_START		(1 << 0)
++#define	SMB_ADDR		0x04
++#define	SMB_ADDR_SAEN		(1 << 7)
++#define	SMB_CONTROLLER_ADDR	(0xef << 0)
++#define	SMB_CTRL2		0x05
++#define	SMB_FREQ		(0x20 << 1)
++#define	SMB_ENABLE		(0x01 << 0)
++#define	SMB_CTRL3		0x06
+ 
+ #endif				/* _CS5536_H */
+diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
+index 021d017..d058e46 100644
+--- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
++++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
+@@ -10,26 +10,45 @@
+ 
+ #ifdef CONFIG_CS5536_MFGPT
+ extern void setup_mfgpt0_timer(void);
+-extern void disable_mfgpt0_counter(void);
+-extern void enable_mfgpt0_counter(void);
++extern void disable_mfgpt_counter(void);
++extern void enable_mfgpt_counter(void);
+ #else
+ static inline void __maybe_unused setup_mfgpt0_timer(void)
+ {
+ }
+-static inline void __maybe_unused disable_mfgpt0_counter(void)
++static inline void __maybe_unused disable_mfgpt_counter(void)
+ {
+ }
+-static inline void __maybe_unused enable_mfgpt0_counter(void)
++static inline void __maybe_unused enable_mfgpt_counter(void)
+ {
+ }
+ #endif
+ 
+-#define MFGPT_TICK_RATE 14318000
+-#define COMPARE	 ((MFGPT_TICK_RATE + HZ/2) / HZ)
++#define MFGPT_CLK_RATE(c)		((14318000UL-32768)*c + 32768)
++#define MFGPT_TICK_RATE(c, scale)	(MFGPT_CLK_RATE(c) / (1 << scale))
++#define MFGPT_COMPARE(c, scale)		((MFGPT_TICK_RATE(c, scale)+HZ/2)/HZ)
+ 
+-#define MFGPT_BASE	mfgpt_base
+-#define MFGPT0_CMP2	(MFGPT_BASE + 2)
+-#define MFGPT0_CNT	(MFGPT_BASE + 4)
+-#define MFGPT0_SETUP	(MFGPT_BASE + 6)
++#define MFGPT_SETUP_ENABLE		(1 << 15)
++#define MFGPT_SETUP_ACK			(3 << 13)
++#define MFGPT_SETUP_SETUP		(1 << 12)
++#define MFGPT_SETUP_CMP2EVT		(3 <<  8)
++#define MFGPT_SETUP_CMP1EVT		(3 <<  6)
++#define MFGPT_SETUP_CLOCK(c)		(c <<  4)
++#define MFGPT_SETUP_SCALE(scale)	scale
++
++#define MFGPT0_CMP1	mfgpt_base
++#define MFGPT0_CMP2	(mfgpt_base + 0x02)
++#define MFGPT0_CNT	(mfgpt_base + 0x04)
++#define MFGPT0_SETUP	(mfgpt_base + 0x06)
++
++#define MFGPT1_CMP1	(mfgpt_base + 0x08)
++#define MFGPT1_CMP2	(mfgpt_base + 0x0A)
++#define MFGPT1_CNT	(mfgpt_base + 0x0C)
++#define MFGPT1_SETUP	(mfgpt_base + 0x0E)
++
++#define MFGPT2_CMP1	(mfgpt_base + 0x10)
++#define MFGPT2_CMP2	(mfgpt_base + 0x12)
++#define MFGPT2_CNT	(mfgpt_base + 0x14)
++#define MFGPT2_SETUP	(mfgpt_base + 0x16)
+ 
+ #endif /*!_CS5536_MFGPT_H */
+diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_pci.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_pci.h
+index 8a7ecb4..ac01334 100644
+--- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_pci.h
++++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_pci.h
+@@ -13,6 +13,7 @@
+ 
+ #include <linux/types.h>
+ #include <linux/pci_regs.h>
++#include <linux/pci_ids.h>
+ 
+ extern void cs5536_pci_conf_write4(int function, int reg, u32 value);
+ extern u32 cs5536_pci_conf_read4(int function, int reg);
+diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_vsm.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_vsm.h
+index 1f17c18..9bc368f0d 100644
+--- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_vsm.h
++++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_vsm.h
+@@ -17,15 +17,43 @@ typedef u32 (*cs5536_pci_vsm_read)(int reg);
+ extern void pci_##name##_write_reg(int reg, u32 value); \
+ extern u32 pci_##name##_read_reg(int reg);
+ 
++#define DEFINE_CS5536_MODULE(name) \
++static void pci_##name##_write_reg(int reg, u32 value) {} \
++static u32 pci_##name##_read_reg(int reg) { return 0; }
++
++/* isa module */
++#ifdef CONFIG_CS5536_ISA
++DECLARE_CS5536_MODULE(isa)
++#else
++DEFINE_CS5536_MODULE(isa)
++#endif
++
+ /* ide module */
++#ifdef CONFIG_CS5536_IDE
+ DECLARE_CS5536_MODULE(ide)
++#else
++DEFINE_CS5536_MODULE(ide)
++#endif
++
+ /* acc module */
++#ifdef CONFIG_CS5536_AUDIO
+ DECLARE_CS5536_MODULE(acc)
++#else
++DEFINE_CS5536_MODULE(acc)
++#endif
++
+ /* ohci module */
++#ifdef CONFIG_CS5536_OHCI
+ DECLARE_CS5536_MODULE(ohci)
+-/* isa module */
+-DECLARE_CS5536_MODULE(isa)
++#else
++DEFINE_CS5536_MODULE(ohci)
++#endif
++
+ /* ehci module */
++#ifdef CONFIG_CS5536_EHCI
+ DECLARE_CS5536_MODULE(ehci)
++#else
++DEFINE_CS5536_MODULE(ehci)
++#endif
+ 
+ #endif				/* _CS5536_VSM_H */
+diff --git a/arch/mips/include/asm/mach-loongson/gpio.h b/arch/mips/include/asm/mach-loongson/gpio.h
+index 211a7b7..f15db3c 100644
+--- a/arch/mips/include/asm/mach-loongson/gpio.h
++++ b/arch/mips/include/asm/mach-loongson/gpio.h
+@@ -13,12 +13,16 @@
+ #ifndef __STLS2F_GPIO_H
+ #define __STLS2F_GPIO_H
+ 
++#ifdef CONFIG_GPIOLIB
++#define ARCH_NR_GPIOS 4
+ #include <asm-generic/gpio.h>
+ 
+ extern void gpio_set_value(unsigned gpio, int value);
+ extern int gpio_get_value(unsigned gpio);
+ extern int gpio_cansleep(unsigned gpio);
+ 
++#endif
++
+ /* The chip can do interrupt
+  * but it has not been tested and doc not clear
+  */
+diff --git a/arch/mips/include/asm/mach-loongson/loongson.h b/arch/mips/include/asm/mach-loongson/loongson.h
+index b286534..222d179 100644
+--- a/arch/mips/include/asm/mach-loongson/loongson.h
++++ b/arch/mips/include/asm/mach-loongson/loongson.h
+@@ -32,17 +32,13 @@ extern void __init prom_init_memory(void);
+ extern void __init prom_init_cmdline(void);
+ extern void __init prom_init_machtype(void);
+ extern void __init prom_init_env(void);
+-#ifdef CONFIG_LOONGSON_UART_BASE
+-extern unsigned long _loongson_uart_base, loongson_uart_base;
+-extern void prom_init_loongson_uart_base(void);
+-#endif
++extern void __init prom_init_uart_base(void);
+ 
+-static inline void prom_init_uart_base(void)
+-{
+-#ifdef CONFIG_LOONGSON_UART_BASE
+-	prom_init_loongson_uart_base();
+-#endif
+-}
++/*
++ * Copy kernel command line from arcs_cmdline
++ */
++#include <asm/setup.h>
++extern char loongson_cmdline[COMMAND_LINE_SIZE];
+ 
+ /* irq operation functions */
+ extern void bonito_irqdispatch(void);
+@@ -249,6 +245,12 @@ extern struct cpufreq_frequency_table loongson2_clockmod_table[];
+ 
+ /* Chip Config */
+ #define LOONGSON_CHIPCFG0		LOONGSON_REG(LOONGSON_REGBASE + 0x80)
++#define LOONGSON_GET_CPUFREQ()		(LOONGSON_CHIPCFG0 & 7)
++
++#define LOONGSON_SET_CPUFREQ(level)	do { \
++	LOONGSON_CHIPCFG0 = (LOONGSON_CHIPCFG0 & (~7)) | (level); \
++} while (0)
++
+ #endif
+ 
+ /*
+diff --git a/arch/mips/include/asm/mach-loongson/machine.h b/arch/mips/include/asm/mach-loongson/machine.h
+index 3810d5c..d219499 100644
+--- a/arch/mips/include/asm/mach-loongson/machine.h
++++ b/arch/mips/include/asm/mach-loongson/machine.h
+@@ -24,4 +24,10 @@
+ 
+ #endif
+ 
++#ifdef CONFIG_DEXXON_GDIUM
++
++#define LOONGSON_MACHTYPE MACH_DEXXON_GDIUM2F10
++
++#endif
++
+ #endif /* __ASM_MACH_LOONGSON_MACHINE_H */
+diff --git a/arch/mips/include/asm/mach-loongson1/clock.h b/arch/mips/include/asm/mach-loongson1/clock.h
+new file mode 100644
+index 0000000..dd1afdb
+--- /dev/null
++++ b/arch/mips/include/asm/mach-loongson1/clock.h
+@@ -0,0 +1,53 @@
++#ifndef __ASM_MACH_LOONGSON1_CLOCK_H
++#define __ASM_MACH_LOONGSON1_CLOCK_H
++
++#include <linux/kref.h>
++#include <linux/list.h>
++#include <linux/seq_file.h>
++#include <linux/clk.h>
++
++extern void (*cpu_wait) (void);
++
++struct clk;
++
++struct clk_ops {
++	void (*init) (struct clk *clk);
++	void (*enable) (struct clk *clk);
++	void (*disable) (struct clk *clk);
++	void (*recalc) (struct clk *clk);
++	int (*set_rate) (struct clk *clk, unsigned long rate, int algo_id);
++	long (*round_rate) (struct clk *clk, unsigned long rate);
++};
++
++struct clk {
++	struct list_head node;
++	const char *name;
++	int id;
++	struct module *owner;
++
++	struct clk *parent;
++	struct clk_ops *ops;
++
++	struct kref kref;
++
++	unsigned long rate;
++	unsigned long flags;
++};
++
++#define CLK_ALWAYS_ENABLED	(1 << 0)
++#define CLK_RATE_PROPAGATES	(1 << 1)
++
++/* Should be defined by processor-specific code */
++void arch_init_clk_ops(struct clk_ops **, int type);
++
++int clk_init(void);
++
++int __clk_enable(struct clk *);
++void __clk_disable(struct clk *);
++
++void clk_recalc_rate(struct clk *);
++
++int clk_register(struct clk *);
++void clk_unregister(struct clk *);
++
++#endif				/* __ASM_MIPS_CLOCK_H */
+diff --git a/arch/mips/include/asm/mach-loongson1/regs-intc.h b/arch/mips/include/asm/mach-loongson1/regs-intc.h
+new file mode 100644
+index 0000000..6d5db23
+--- /dev/null
++++ b/arch/mips/include/asm/mach-loongson1/regs-intc.h
+@@ -0,0 +1,25 @@
++/*
++ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
++ *
++ * Loongson1 Interrupt register definitions.
++ *
++ * 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.
++ */
++
++#ifndef __ASM_MACH_LOONGSON1_REGS_INTC_H
++#define __ASM_MACH_LOONGSON1_REGS_INTC_H
++
++#define LS1X_INTC_REG(n, x) \
++		(ioremap(LS1X_INTC_BASE + (n * 0x18) + (x), 4))
++
++#define LS1X_INTC_INTISR(n)		LS1X_INTC_REG(n, 0x0)
++#define LS1X_INTC_INTIEN(n)		LS1X_INTC_REG(n, 0x4)
++#define LS1X_INTC_INTSET(n)		LS1X_INTC_REG(n, 0x8)
++#define LS1X_INTC_INTCLR(n)		LS1X_INTC_REG(n, 0xc)
++#define LS1X_INTC_INTPOL(n)		LS1X_INTC_REG(n, 0x10)
++#define LS1X_INTC_INTEDGE(n)		LS1X_INTC_REG(n, 0x14)
++
++#endif /* __ASM_MACH_LOONGSON1_REGS_INTC_H */
+diff --git a/arch/mips/include/asm/module.h b/arch/mips/include/asm/module.h
+index 44b705d..c28a782 100644
+--- a/arch/mips/include/asm/module.h
++++ b/arch/mips/include/asm/module.h
+@@ -126,6 +126,8 @@ search_module_dbetables(unsigned long addr)
+ #define MODULE_PROC_FAMILY "LOONGSON1 "
+ #elif defined CONFIG_CPU_LOONGSON2
+ #define MODULE_PROC_FAMILY "LOONGSON2 "
++#elif defined CONFIG_CPU_LOONGSON1
++#define MODULE_PROC_FAMILY "LOONGSON1 "
+ #elif defined CONFIG_CPU_CAVIUM_OCTEON
+ #define MODULE_PROC_FAMILY "OCTEON "
+ #elif defined CONFIG_CPU_XLR
+diff --git a/arch/mips/include/asm/timex.h b/arch/mips/include/asm/timex.h
+index c542475..74fef34 100644
+--- a/arch/mips/include/asm/timex.h
++++ b/arch/mips/include/asm/timex.h
+@@ -10,6 +10,10 @@
+ 
+ #ifdef __KERNEL__
+ 
++#ifdef CONFIG_CSRC_R4K
++#define ARCH_HAS_PREPARED_LPJ
++#endif
++
+ #include <asm/cpu-features.h>
+ #include <asm/mipsregs.h>
+ #include <asm/cpu-type.h>
+diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h
+index f25181b..d243152 100644
+--- a/arch/mips/include/uapi/asm/inst.h
++++ b/arch/mips/include/uapi/asm/inst.h
+@@ -62,6 +62,8 @@ enum spec_op {
+ enum spec2_op {
+ 	madd_op, maddu_op, mul_op, spec2_3_unused_op,
+ 	msub_op, msubu_op, /* more unused ops */
++	loongson_madd_op = 0x18, loongson_msub_op,
++	loongson_nmadd_op, loongson_nmsub_op,
+ 	clz_op = 0x20, clo_op,
+ 	dclz_op = 0x24, dclo_op,
+ 	sdbpp_op = 0x3f
+@@ -135,7 +137,7 @@ enum cop0_com_func {
+  */
+ enum cop1_fmt {
+ 	s_fmt, d_fmt, e_fmt, q_fmt,
+-	w_fmt, l_fmt
++	w_fmt, l_fmt, ps_fmt
+ };
+ 
+ /*
+@@ -164,7 +166,8 @@ enum cop1_sdw_func {
+ enum cop1x_func {
+ 	lwxc1_op     =	0x00, ldxc1_op	   =  0x01,
+ 	swxc1_op     =  0x08, sdxc1_op	   =  0x09,
+-	pfetch_op    =	0x0f, madd_s_op	   =  0x20,
++	pfetch_op    =	0x0f,
++	prefx_op     =  0x17, madd_s_op    =  0x20,
+ 	madd_d_op    =	0x21, madd_e_op	   =  0x22,
+ 	msub_s_op    =	0x28, msub_d_op	   =  0x29,
+ 	msub_e_op    =	0x2a, nmadd_s_op   =  0x30,
+diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
+index 6788727d..0f81805 100644
+--- a/arch/mips/kernel/scall64-o32.S
++++ b/arch/mips/kernel/scall64-o32.S
+@@ -26,6 +26,18 @@
+ 
+ 	.align	5
+ NESTED(handle_sys, PT_SIZE, sp)
++#ifdef CONFIG_MIPS_USER_RDTSC
++	MFC0	k0, CP0_EPC
++	lw	k1, 0(k0)
++	sltiu	k1, k1, 0x1c
++	bne	k1, zero, 1f		# Normal syscall code: 0x0c < 0x1c
++	 nop
++	mfc0	v0, CP0_COUNT 		# Get TSC
++	PTR_ADDIU	k0, 4		# ret from syscall
++	MTC0	k0, CP0_EPC
++	eret
++1:
++#endif /* CONFIG_MIPS_USER_RDTSC */
+ 	.set	noat
+ 	SAVE_SOME
+ 	TRACE_IRQS_ON_RELOAD
+diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c
+index dcb8e5d..45177bc 100644
+--- a/arch/mips/kernel/time.c
++++ b/arch/mips/kernel/time.c
+@@ -120,6 +120,11 @@ static __init int cpu_has_mfc0_count_bug(void)
+ 
+ void __init time_init(void)
+ {
++#ifdef CONFIG_HR_SCHED_CLOCK
++	if (!mips_clockevent_init() || !cpu_has_mfc0_count_bug())
++		write_c0_count(0);
++#endif
++
+ 	plat_time_init();
+ 
+ 	/*
+diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
+index eeddc58..d7bec00 100644
+--- a/arch/mips/lib/Makefile
++++ b/arch/mips/lib/Makefile
+@@ -2,10 +2,14 @@
+ # Makefile for MIPS-specific library files..
+ #
+ 
+-lib-y	+= bitops.o csum_partial.o delay.o memcpy.o memset.o \
++lib-y	+= bitops.o csum_partial.o memcpy.o memset.o \
+ 	   mips-atomic.o strlen_user.o strncpy_user.o \
+ 	   strnlen_user.o uncached.o
+ 
++ifndef CONFIG_CSRC_R4K
++lib-y	+= delay.o
++endif
++
+ obj-y			+= iomap.o
+ obj-$(CONFIG_PCI)	+= iomap-pci.o
+ 
+diff --git a/arch/mips/loongson/Kconfig b/arch/mips/loongson/Kconfig
+index 263beb9..d56d594 100644
+--- a/arch/mips/loongson/Kconfig
++++ b/arch/mips/loongson/Kconfig
+@@ -32,12 +32,12 @@ config LEMOTE_FULOONG2E
+ 
+ config LEMOTE_MACH2F
+ 	bool "Lemote Loongson 2F family machines"
+-	select ARCH_SPARSEMEM_ENABLE
++	select ARCH_SPARSEMEM_ENABLE if HIBERNATION
+ 	select BOARD_SCACHE
+ 	select BOOT_ELF32
+ 	select CEVT_R4K if ! MIPS_EXTERNAL_TIMER
+ 	select CPU_HAS_WB
+-	select CS5536
++	select CS5536 if PCI
+ 	select CSRC_R4K if ! MIPS_EXTERNAL_TIMER
+ 	select DMA_NONCOHERENT
+ 	select GENERIC_ISA_DMA_SUPPORT_BROKEN
+@@ -45,23 +45,68 @@ config LEMOTE_MACH2F
+ 	select HW_HAS_PCI
+ 	select I8259
+ 	select IRQ_CPU
+-	select ISA
+ 	select SYS_HAS_CPU_LOONGSON2F
+ 	select SYS_HAS_EARLY_PRINTK
+ 	select SYS_SUPPORTS_32BIT_KERNEL
+ 	select SYS_SUPPORTS_64BIT_KERNEL
+-	select SYS_SUPPORTS_HIGHMEM
++	select SYS_SUPPORTS_HIGHMEM if ! EMBEDDED
+ 	select SYS_SUPPORTS_LITTLE_ENDIAN
+-	select LOONGSON_MC146818
++	select LOONGSON_MC146818 if RTC_DRV_CMOS
+ 	help
+ 	  Lemote Loongson 2F family machines utilize the 2F revision of
+ 	  Loongson processor and the AMD CS5536 south bridge.
+ 
+ 	  These family machines include fuloong2f mini PC, yeeloong2f notebook,
+ 	  LingLoong allinone PC and so forth.
++
++config DEXXON_GDIUM
++	bool "Dexxon Gdium Netbook"
++	select ARCH_SPARSEMEM_ENABLE
++	select BOARD_SCACHE
++	select BOOT_ELF32
++	select CEVT_R4K if ! MIPS_EXTERNAL_TIMER
++	select CPU_HAS_WB
++	select CSRC_R4K if ! MIPS_EXTERNAL_TIMER
++	select DMA_NONCOHERENT
++	select GENERIC_ISA_DMA_SUPPORT_BROKEN
++	select HW_HAS_PCI
++	select I8259
++	select IRQ_CPU
++	select ISA
++	select SYS_HAS_CPU_LOONGSON2F
++	select SYS_HAS_EARLY_PRINTK
++	select SYS_SUPPORTS_32BIT_KERNEL
++	select SYS_SUPPORTS_64BIT_KERNEL
++	select SYS_SUPPORTS_HIGHMEM
++	select SYS_SUPPORTS_LITTLE_ENDIAN
++	select ARCH_REQUIRE_GPIOLIB
++	select HAVE_PWM if MFD_SM501
++	help
++	  Dexxon gdium netbook based on Loongson 2F and SM502.
+ endchoice
+ 
+ config CS5536
++	select CS5536_IDE if (PATA_AMD || BLK_DEV_AMD74XX || PATA_CS5536)
++	select CS5536_OHCI if USB_OHCI_HCD
++	select CS5536_EHCI if USB_EHCI_HCD
++	select CS5536_AUDIO if SND_CS5535AUDIO
++	select CS5536_ISA
++	bool
++
++config CS5536_ISA
++	select ISA
++	bool
++
++config CS5536_IDE
++	bool
++
++config CS5536_OHCI
++	bool
++
++config CS5536_EHCI
++	bool
++
++config CS5536_AUDIO
+ 	bool
+ 
+ config CS5536_MFGPT
+@@ -81,13 +126,25 @@ config LOONGSON_SUSPEND
+ 	default y
+ 	depends on CPU_SUPPORTS_CPUFREQ && SUSPEND
+ 
+-config LOONGSON_UART_BASE
+-	bool
+-	default y
+-	depends on EARLY_PRINTK || SERIAL_8250
+-
+ config LOONGSON_MC146818
+ 	bool
+ 	default n
+ 
++config GDIUM_PWM_CLOCK
++	tristate "Gdium PWM Timer"
++	default n
++	depends on HAVE_PWM && EXPERIMENTAL && BROKEN
++	select MIPS_EXTERNAL_TIMER
++	help
++	  This options enables the experimental sm501-pwm based clock. With it,
++	  you may be possible to use the loongson2f cpufreq driver.
++
++config GDIUM_VERSION
++	int "Configure Gdium Version"
++	depends on DEXXON_GDIUM
++	default "3"
++	help
++	  I have no information about how to determine which version your board
++	  is, If the default config doesn't work for it, please change it to
++	  smaller ones.
+ endif # MACH_LOONGSON
+diff --git a/arch/mips/loongson/Makefile b/arch/mips/loongson/Makefile
+index 0dc0055..4b69866 100644
+--- a/arch/mips/loongson/Makefile
++++ b/arch/mips/loongson/Makefile
+@@ -15,3 +15,9 @@ obj-$(CONFIG_LEMOTE_FULOONG2E)	+= fuloong-2e/
+ #
+ 
+ obj-$(CONFIG_LEMOTE_MACH2F)  += lemote-2f/
++
++#
++# Dexxon gdium netbook, based on loongson 2F and SM502
++#
++
++obj-$(CONFIG_DEXXON_GDIUM)  += gdium/
+diff --git a/arch/mips/loongson/Platform b/arch/mips/loongson/Platform
+index 29692e5..6be5dff 100644
+--- a/arch/mips/loongson/Platform
++++ b/arch/mips/loongson/Platform
+@@ -30,3 +30,4 @@ platform-$(CONFIG_MACH_LOONGSON) += loongson/
+ cflags-$(CONFIG_MACH_LOONGSON) += -I$(srctree)/arch/mips/include/asm/mach-loongson -mno-branch-likely
+ load-$(CONFIG_LEMOTE_FULOONG2E) += 0xffffffff80100000
+ load-$(CONFIG_LEMOTE_MACH2F) += 0xffffffff80200000
++load-$(CONFIG_DEXXON_GDIUM) += 0xffffffff80200000
+diff --git a/arch/mips/loongson/common/Makefile b/arch/mips/loongson/common/Makefile
+index 9005a8d6..73f1f9f 100644
+--- a/arch/mips/loongson/common/Makefile
++++ b/arch/mips/loongson/common/Makefile
+@@ -11,9 +11,7 @@ obj-$(CONFIG_PCI) += pci.o
+ # Serial port support
+ #
+ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+-loongson-serial-$(CONFIG_SERIAL_8250) := serial.o
+-obj-y += $(loongson-serial-m) $(loongson-serial-y)
+-obj-$(CONFIG_LOONGSON_UART_BASE) += uart_base.o
++obj-$(CONFIG_SERIAL_8250) += serial.o
+ obj-$(CONFIG_LOONGSON_MC146818) += rtc.o
+ 
+ #
+diff --git a/arch/mips/loongson/common/cmdline.c b/arch/mips/loongson/common/cmdline.c
+index 72fed00..96d5919 100644
+--- a/arch/mips/loongson/common/cmdline.c
++++ b/arch/mips/loongson/common/cmdline.c
+@@ -17,10 +17,15 @@
+  * Free Software Foundation;  either version 2 of the  License, or (at your
+  * option) any later version.
+  */
++#include <linux/module.h>
+ #include <asm/bootinfo.h>
+ 
+ #include <loongson.h>
+ 
++/* the kernel command line copied from arcs_cmdline */
++char loongson_cmdline[COMMAND_LINE_SIZE];
++EXPORT_SYMBOL(loongson_cmdline);
++
+ void __init prom_init_cmdline(void)
+ {
+ 	int prom_argc;
+@@ -45,4 +50,31 @@ void __init prom_init_cmdline(void)
+ 	}
+ 
+ 	prom_init_machtype();
++
++	/* append machine specific command line */
++	switch (mips_machtype) {
++	case MACH_LEMOTE_LL2F:
++		if ((strstr(arcs_cmdline, "video=")) == NULL)
++			strcat(arcs_cmdline, " video=sisfb:1360x768-16@60");
++		break;
++	case MACH_LEMOTE_FL2F:
++		if ((strstr(arcs_cmdline, "ide_core.ignore_cable=")) == NULL)
++			strcat(arcs_cmdline, " ide_core.ignore_cable=0");
++		break;
++	case MACH_LEMOTE_ML2F7:
++		/* Mengloong-2F has a 800x480 screen */
++		if ((strstr(arcs_cmdline, "vga=")) == NULL)
++			strcat(arcs_cmdline, " vga=0x313");
++		break;
++	case MACH_DEXXON_GDIUM2F10:
++		/* gdium has a 1024x600 screen */
++		if ((strstr(arcs_cmdline, "video=")) == NULL)
++			strcat(arcs_cmdline, " video=sm501fb:1024x600@60");
++		break;
++	default:
++		break;
++	}
++
++	/* copy arcs_cmdline into loongson_cmdline */
++	strncpy(loongson_cmdline, arcs_cmdline, COMMAND_LINE_SIZE);
+ }
+diff --git a/arch/mips/loongson/common/cs5536/Makefile b/arch/mips/loongson/common/cs5536/Makefile
+index f12e640..70f6057 100644
+--- a/arch/mips/loongson/common/cs5536/Makefile
++++ b/arch/mips/loongson/common/cs5536/Makefile
+@@ -2,8 +2,13 @@
+ # Makefile for CS5536 support.
+ #
+ 
+-obj-$(CONFIG_CS5536) += cs5536_pci.o cs5536_ide.o cs5536_acc.o cs5536_ohci.o \
+-			cs5536_isa.o cs5536_ehci.o
++obj-$(CONFIG_CS5536)	+= cs5536_pci.o
++
++obj-$(CONFIG_ISA)		+= cs5536_isa.o
++obj-$(CONFIG_CS5536_IDE)	+= cs5536_ide.o
++obj-$(CONFIG_CS5536_AUDIO)	+= cs5536_acc.o
++obj-$(CONFIG_CS5536_OHCI)	+= cs5536_ohci.o
++obj-$(CONFIG_CS5536_EHCI)	+= cs5536_ehci.o
+ 
+ #
+ # Enable cs5536 mfgpt Timer
+diff --git a/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c b/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c
+index c639b9d..a7078ae 100644
+--- a/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c
++++ b/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c
+@@ -7,6 +7,9 @@
+  * Copyright (C) 2009 Lemote Inc.
+  * Author: Wu zhangjin, wuzhangjin@gmail.com
+  *
++ * Copyright (C) 2010 Lemote Inc.
++ * Author: Gang Liang, randomizedthinking@gmail.com
++ *
+  * Reference: AMD Geode(TM) CS5536 Companion Device Data Book
+  *
+  *  This program is free software; you can redistribute	 it and/or modify it
+@@ -15,11 +18,24 @@
+  *  option) any later version.
+  */
+ 
++/*
++ * The MFGPT base address is variable, i.e., it could change over time. In
++ * reality, it only changes once when setting up the PCI memory mapping (occurs
++ * about 0.2 second from boot).  But because of this, we have to read in the
++ * mfgpt base address repeatly in the beginning of various routines, most
++ * noticeably, mfgpt1_read_cycle (for sched_clock), and mfgpt1_interrupt.
++ *
++ * The source of problem is that PMON and the current cs5536 set up pci
++ * register window differently (to be further confirmed). Can we set
++ * them the same so as to save the trouble here?
++ *
++ * Now an ugly hack is used to save a few CPU cycles... likely an
++ * over-optimization. Feel free to remove it.
++ */
++
+ #include <linux/io.h>
+ #include <linux/init.h>
+ #include <linux/module.h>
+-#include <linux/jiffies.h>
+-#include <linux/spinlock.h>
+ #include <linux/interrupt.h>
+ #include <linux/clockchips.h>
+ 
+@@ -27,108 +43,143 @@
+ 
+ #include <cs5536/cs5536_mfgpt.h>
+ 
+-DEFINE_SPINLOCK(mfgpt_lock);
+-EXPORT_SYMBOL(mfgpt_lock);
++static void mfgpt0_set_mode(enum clock_event_mode, struct clock_event_device*);
++static int mfgpt0_next_event(unsigned long, struct clock_event_device*);
++static irqreturn_t mfgpt0_interrupt(int irq, void *dev_id);
++static void mfgpt0_start_timer(u16 delta);
+ 
++static cycle_t mfgpt1_read_cycle(struct clocksource *cs);
++
++static enum clock_event_mode mfgpt0_mode = CLOCK_EVT_MODE_SHUTDOWN;
+ static u32 mfgpt_base;
+ 
+-/*
+- * Initialize the MFGPT timer.
+- *
+- * This is also called after resume to bring the MFGPT into operation again.
+- */
++static struct clock_event_device mfgpt0_clockevent = {
++	.name = "mfgpt0",
++	.features = CLOCK_EVT_MODE_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
++	.set_mode = mfgpt0_set_mode,
++	.set_next_event = mfgpt0_next_event,
++	.rating = 220,
++	.irq = CS5536_MFGPT_INTR,
++};
++
++static struct irqaction irq5 = {
++	.handler = mfgpt0_interrupt,
++	.flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER,
++	.name = "mfgpt0-timer"
++};
++
++static struct clocksource mfgpt1_clocksource = {
++	.name = "mfgpt1",
++	.rating = 210,
++	.read = mfgpt1_read_cycle,
++	.mask = CLOCKSOURCE_MASK(16),
++	.flags = CLOCK_SOURCE_IS_CONTINUOUS
++};
+ 
+-/* disable counter */
+-void disable_mfgpt0_counter(void)
++static inline void enable_mfgpt0_counter(void)
+ {
+-	outw(inw(MFGPT0_SETUP) & 0x7fff, MFGPT0_SETUP);
++	u32 basehi;
++	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
++
++	/* clockevent: 14M, divisor = 8 (scale=3), CMP2 event mode */
++	outw(MFGPT_SETUP_ACK | MFGPT_SETUP_CMP2EVT |
++	     MFGPT_SETUP_CLOCK(1) | MFGPT_SETUP_SCALE(3), MFGPT0_SETUP);
++	outw(0, MFGPT0_CNT);
++	outw(MFGPT_COMPARE(1, 3), MFGPT0_CMP2);
++	outw(0xFFFF, MFGPT0_SETUP);
+ }
+-EXPORT_SYMBOL(disable_mfgpt0_counter);
+ 
+-/* enable counter, comparator2 to event mode, 14.318MHz clock */
+-void enable_mfgpt0_counter(void)
++static inline void enable_mfgpt1_counter(void)
+ {
+-	outw(0xe310, MFGPT0_SETUP);
++	u32 basehi;
++	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
++
++	/* clocksource: 32K w/ divisor = 2 (scale=1) */
++	outw(MFGPT_SETUP_ACK | MFGPT_SETUP_CLOCK(0) |
++		MFGPT_SETUP_SCALE(1), MFGPT1_SETUP);
++
++	outw(0, MFGPT1_CNT);
++	outw(0xFFFF, MFGPT1_CMP2);  /* CNT won't tick with no CMP set */
++	outw(0xFFFF, MFGPT1_SETUP);
+ }
+-EXPORT_SYMBOL(enable_mfgpt0_counter);
+ 
+-static void init_mfgpt_timer(enum clock_event_mode mode,
+-			     struct clock_event_device *evt)
++void enable_mfgpt_counter(void)
+ {
+-	spin_lock(&mfgpt_lock);
+-
+-	switch (mode) {
+-	case CLOCK_EVT_MODE_PERIODIC:
+-		outw(COMPARE, MFGPT0_CMP2);	/* set comparator2 */
+-		outw(0, MFGPT0_CNT);	/* set counter to 0 */
+-		enable_mfgpt0_counter();
+-		break;
+-
+-	case CLOCK_EVT_MODE_SHUTDOWN:
+-	case CLOCK_EVT_MODE_UNUSED:
+-		if (evt->mode == CLOCK_EVT_MODE_PERIODIC ||
+-		    evt->mode == CLOCK_EVT_MODE_ONESHOT)
+-			disable_mfgpt0_counter();
+-		break;
+-
+-	case CLOCK_EVT_MODE_ONESHOT:
+-		/* The oneshot mode have very high deviation, Not use it! */
+-		break;
+-
+-	case CLOCK_EVT_MODE_RESUME:
+-		/* Nothing to do here */
+-		break;
+-	}
+-	spin_unlock(&mfgpt_lock);
++	/* TODO: add a mfgpt system hard reset here
++	 * timers might not reset correctly when OS crashes
++	 */
++
++	enable_mfgpt0_counter();
++	enable_mfgpt1_counter();
+ }
++EXPORT_SYMBOL(enable_mfgpt_counter);
+ 
+-static struct clock_event_device mfgpt_clockevent = {
+-	.name = "mfgpt",
+-	.features = CLOCK_EVT_FEAT_PERIODIC,
+-	.set_mode = init_mfgpt_timer,
+-	.irq = CS5536_MFGPT_INTR,
+-};
++void disable_mfgpt_counter(void)
++{
++	outw(0x7FFF, MFGPT0_SETUP);
++	outw(0x7FFF, MFGPT1_SETUP);
++}
++EXPORT_SYMBOL(disable_mfgpt_counter);
+ 
+-static irqreturn_t timer_interrupt(int irq, void *dev_id)
++static void mfgpt0_start_timer(u16 delta)
+ {
+-	u32 basehi;
++	outw(0x7FFF, MFGPT0_SETUP);
++	outw(0,      MFGPT0_CNT);
++	outw(delta,  MFGPT0_CMP2);
++	outw(0xFFFF, MFGPT0_SETUP);
++}
+ 
+-	/*
+-	 * get MFGPT base address
+-	 *
+-	 * NOTE: do not remove me, it's need for the value of mfgpt_base is
+-	 * variable
+-	 */
++static void mfgpt0_set_mode(enum clock_event_mode mode,
++		struct clock_event_device *evt)
++{
++	outw(0x7FFF, MFGPT0_SETUP);
++	if (mode == CLOCK_EVT_MODE_PERIODIC)
++		mfgpt0_start_timer(MFGPT_COMPARE(1, 3));
++
++	mfgpt0_mode = mode;
++}
++
++static int mfgpt0_next_event(unsigned long delta,
++		struct clock_event_device *evt)
++{
++	mfgpt0_start_timer(delta);
++	return 0;
++}
++
++static irqreturn_t mfgpt0_interrupt(int irq, void *dev_id)
++{
++	u32 basehi;
+ 	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
+ 
+-	/* ack */
+-	outw(inw(MFGPT0_SETUP) | 0x4000, MFGPT0_SETUP);
++	/* stop the timer and ack the interrupt */
++	outw(0x7FFF, MFGPT0_SETUP);
+ 
+-	mfgpt_clockevent.event_handler(&mfgpt_clockevent);
++	if (mfgpt0_mode == CLOCK_EVT_MODE_SHUTDOWN)
++		return IRQ_HANDLED;
+ 
++	/* restart timer for periodic mode */
++	if (mfgpt0_mode == CLOCK_EVT_MODE_PERIODIC)
++		outw(0xFFFF, MFGPT0_SETUP);
++
++	mfgpt0_clockevent.event_handler(&mfgpt0_clockevent);
+ 	return IRQ_HANDLED;
+ }
+ 
+-static struct irqaction irq5 = {
+-	.handler = timer_interrupt,
+-	.flags = IRQF_NOBALANCING | IRQF_TIMER,
+-	.name = "timer"
+-};
+-
+ /*
+  * Initialize the conversion factor and the min/max deltas of the clock event
+  * structure and register the clock event source with the framework.
+  */
+ void __init setup_mfgpt0_timer(void)
+ {
+-	u32 basehi;
+-	struct clock_event_device *cd = &mfgpt_clockevent;
++	struct clock_event_device *cd = &mfgpt0_clockevent;
+ 	unsigned int cpu = smp_processor_id();
+-
+ 	cd->cpumask = cpumask_of(cpu);
+-	clockevent_set_clock(cd, MFGPT_TICK_RATE);
+-	cd->max_delta_ns = clockevent_delta2ns(0xffff, cd);
+-	cd->min_delta_ns = clockevent_delta2ns(0xf, cd);
++
++	cd->shift = 22;
++	cd->mult  = div_sc(MFGPT_TICK_RATE(1, 3), NSEC_PER_SEC, cd->shift);
++
++	cd->min_delta_ns = clockevent_delta2ns(0xF, cd);
++	cd->max_delta_ns = clockevent_delta2ns(0xFFFF, cd);
+ 
+ 	/* Enable MFGPT0 Comparator 2 Output to the Interrupt Mapper */
+ 	_wrmsr(DIVIL_MSR_REG(MFGPT_IRQ), 0, 0x100);
+@@ -136,79 +187,24 @@ void __init setup_mfgpt0_timer(void)
+ 	/* Enable Interrupt Gate 5 */
+ 	_wrmsr(DIVIL_MSR_REG(PIC_ZSEL_LOW), 0, 0x50000);
+ 
+-	/* get MFGPT base address */
+-	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
+-
++	enable_mfgpt0_counter();
+ 	clockevents_register_device(cd);
+-
+ 	setup_irq(CS5536_MFGPT_INTR, &irq5);
+ }
+ 
+-/*
+- * Since the MFGPT overflows every tick, its not very useful
+- * to just read by itself. So use jiffies to emulate a free
+- * running counter:
+- */
+-static cycle_t mfgpt_read(struct clocksource *cs)
++static cycle_t mfgpt1_read_cycle(struct clocksource *cs)
+ {
+-	unsigned long flags;
+-	int count;
+-	u32 jifs;
+-	static int old_count;
+-	static u32 old_jifs;
+-
+-	spin_lock_irqsave(&mfgpt_lock, flags);
+-	/*
+-	 * Although our caller may have the read side of xtime_lock,
+-	 * this is now a seqlock, and we are cheating in this routine
+-	 * by having side effects on state that we cannot undo if
+-	 * there is a collision on the seqlock and our caller has to
+-	 * retry.  (Namely, old_jifs and old_count.)  So we must treat
+-	 * jiffies as volatile despite the lock.  We read jiffies
+-	 * before latching the timer count to guarantee that although
+-	 * the jiffies value might be older than the count (that is,
+-	 * the counter may underflow between the last point where
+-	 * jiffies was incremented and the point where we latch the
+-	 * count), it cannot be newer.
+-	 */
+-	jifs = jiffies;
+-	/* read the count */
+-	count = inw(MFGPT0_CNT);
+-
+-	/*
+-	 * It's possible for count to appear to go the wrong way for this
+-	 * reason:
+-	 *
+-	 *  The timer counter underflows, but we haven't handled the resulting
+-	 *  interrupt and incremented jiffies yet.
+-	 *
+-	 * Previous attempts to handle these cases intelligently were buggy, so
+-	 * we just do the simple thing now.
+-	 */
+-	if (count < old_count && jifs == old_jifs)
+-		count = old_count;
+-
+-	old_count = count;
+-	old_jifs = jifs;
+-
+-	spin_unlock_irqrestore(&mfgpt_lock, flags);
+-
+-	return (cycle_t) (jifs * COMPARE) + count;
++	return inw(MFGPT1_CNT);
+ }
+ 
+-static struct clocksource clocksource_mfgpt = {
+-	.name = "mfgpt",
+-	.rating = 120, /* Functional for real use, but not desired */
+-	.read = mfgpt_read,
+-	.mask = CLOCKSOURCE_MASK(32),
+-};
+-
+-int __init init_mfgpt_clocksource(void)
++int __init init_mfgpt1_clocksource(void)
+ {
+ 	if (num_possible_cpus() > 1)	/* MFGPT does not scale! */
+ 		return 0;
+ 
+-	return clocksource_register_hz(&clocksource_mfgpt, MFGPT_TICK_RATE);
++	enable_mfgpt1_counter();
++
++	return clocksource_register_hz(&mfgpt1_clocksource, MFGPT_TICK_RATE(0, 1));
+ }
+ 
+-arch_initcall(init_mfgpt_clocksource);
++arch_initcall(init_mfgpt1_clocksource);
+diff --git a/arch/mips/loongson/common/early_printk.c b/arch/mips/loongson/common/early_printk.c
+index ced461b..89aecbf 100644
+--- a/arch/mips/loongson/common/early_printk.c
++++ b/arch/mips/loongson/common/early_printk.c
+@@ -10,9 +10,13 @@
+  *  option) any later version.
+  */
+ #include <linux/serial_reg.h>
++#include <linux/module.h>
++#include <asm/bootinfo.h>
+ 
+ #include <loongson.h>
+ 
++unsigned long _loongson_uart_base;
++
+ #define PORT(base, offset) (u8 *)(base + offset)
+ 
+ static inline unsigned int serial_in(unsigned char *base, int offset)
+@@ -39,3 +43,29 @@ void prom_putchar(char c)
+ 
+ 	serial_out(uart_base, UART_TX, c);
+ }
++
++void __init prom_init_uart_base(void)
++{
++	unsigned long loongson_uart_base;
++
++	switch (mips_machtype) {
++	case MACH_LEMOTE_FL2E:
++		loongson_uart_base = LOONGSON_PCIIO_BASE + 0x3f8;
++		break;
++	case MACH_LEMOTE_FL2F:
++	case MACH_LEMOTE_LL2F:
++		loongson_uart_base = LOONGSON_PCIIO_BASE + 0x2f8;
++		break;
++	case MACH_LEMOTE_ML2F7:
++	case MACH_LEMOTE_YL2F89:
++	case MACH_DEXXON_GDIUM2F10:
++	case MACH_LEMOTE_NAS:
++	default:
++		/* The CPU provided serial port */
++		loongson_uart_base = LOONGSON_LIO1_BASE + 0x3f8;
++		break;
++	}
++
++	_loongson_uart_base =
++		(unsigned long)ioremap_nocache(loongson_uart_base, 8);
++}
+diff --git a/arch/mips/loongson/common/init.c b/arch/mips/loongson/common/init.c
+index ae7af1f..3083978 100644
+--- a/arch/mips/loongson/common/init.c
++++ b/arch/mips/loongson/common/init.c
+@@ -30,8 +30,10 @@ void __init prom_init(void)
+ 	prom_init_env();
+ 	prom_init_memory();
+ 
++#ifdef CONFIG_EARLY_PRINTK
+ 	/*init the uart base address */
+ 	prom_init_uart_base();
++#endif
+ }
+ 
+ void __init prom_free_prom_memory(void)
+diff --git a/arch/mips/loongson/common/irq.c b/arch/mips/loongson/common/irq.c
+index 687003b..d62fa77 100644
+--- a/arch/mips/loongson/common/irq.c
++++ b/arch/mips/loongson/common/irq.c
+@@ -10,6 +10,10 @@
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+ 
++#include <asm/irq_cpu.h>
++#include <asm/i8259.h>
++#include <asm/mipsregs.h>
++
+ #include <loongson.h>
+ /*
+  * the first level int-handler will jump here if it is a bonito irq
+@@ -48,20 +52,32 @@ asmlinkage void plat_irq_dispatch(void)
+ void __init arch_init_irq(void)
+ {
+ 	/*
+-	 * Clear all of the interrupts while we change the able around a bit.
+-	 * int-handler is not on bootstrap
++	 * The vector addresses of the generic exceptions are in the cached
++	 * address space.
+ 	 */
+-	clear_c0_status(ST0_IM | ST0_BEV);
++	clear_c0_status(ST0_BEV);
+ 
+-	/* no steer */
++	/* No steer */
+ 	LOONGSON_INTSTEER = 0;
+ 
+ 	/*
+-	 * Mask out all interrupt by writing "1" to all bit position in
+-	 * the interrupt reset reg.
++	 * Clear all interrupts
+ 	 */
+ 	LOONGSON_INTENCLR = ~0;
+ 
++	/*
++	 * Sets the first-level interrupt dispatcher:
++	 *
++	 * 0-15: i8259 interrupt (If CONFIG_I8259 selected)
++	 * 16-23: mips cpu interrupt
++	 * 32-63: bonito irq
++	 */
++	mips_cpu_irq_init();
++	bonito_irq_init();
++#ifdef CONFIG_I8259
++	init_i8259_irqs();
++#endif
++
+ 	/* machine specific irq init */
+ 	mach_init_irq();
+ }
+diff --git a/arch/mips/loongson/common/mem.c b/arch/mips/loongson/common/mem.c
+index 8626a42..7aea259 100644
+--- a/arch/mips/loongson/common/mem.c
++++ b/arch/mips/loongson/common/mem.c
+@@ -14,39 +14,24 @@
+ #include <mem.h>
+ #include <pci.h>
+ 
++#define MB(x) ((x) << 20)
++
+ void __init prom_init_memory(void)
+ {
+-	add_memory_region(0x0, (memsize << 20), BOOT_MEM_RAM);
+-
+-	add_memory_region(memsize << 20, LOONGSON_PCI_MEM_START - (memsize <<
+-				20), BOOT_MEM_RESERVED);
+-
++	add_memory_region(0x0, MB(memsize), BOOT_MEM_RAM);
++	add_memory_region(MB(memsize), LOONGSON_PCI_MEM_START - MB(memsize), BOOT_MEM_RESERVED);
+ #ifdef CONFIG_CPU_SUPPORTS_ADDRWINCFG
+-	{
+-		int bit;
+-
+-		bit = fls(memsize + highmemsize);
+-		if (bit != ffs(memsize + highmemsize))
+-			bit += 20;
+-		else
+-			bit = bit + 20 - 1;
+-
+-		/* set cpu window3 to map CPU to DDR: 2G -> 2G */
+-		LOONGSON_ADDRWIN_CPUTODDR(ADDRWIN_WIN3, 0x80000000ul,
+-					  0x80000000ul, (1 << bit));
+-		mmiowb();
+-	}
+-#endif /* !CONFIG_CPU_SUPPORTS_ADDRWINCFG */
++	/* set cpu window3 to map CPU to DDR: 2G -> 0G */
++	LOONGSON_ADDRWIN_CPUTODDR(ADDRWIN_WIN3, 0x80000000ul, 0, MB(memsize + highmemsize));
++	mmiowb();
++#endif
+ 
+ #ifdef CONFIG_64BIT
+ 	if (highmemsize > 0)
+-		add_memory_region(LOONGSON_HIGHMEM_START,
+-				  highmemsize << 20, BOOT_MEM_RAM);
+-
++		add_memory_region(LOONGSON_HIGHMEM_START, MB(highmemsize), BOOT_MEM_RAM);
+ 	add_memory_region(LOONGSON_PCI_MEM_END + 1, LOONGSON_HIGHMEM_START -
+-			  LOONGSON_PCI_MEM_END - 1, BOOT_MEM_RESERVED);
+-
+-#endif /* !CONFIG_64BIT */
++			LOONGSON_PCI_MEM_END - 1, BOOT_MEM_RESERVED);
++#endif
+ }
+ 
+ /* override of arch/mips/mm/cache.c: __uncached_access */
+diff --git a/arch/mips/loongson/common/mtd.c b/arch/mips/loongson/common/mtd.c
+new file mode 100644
+index 0000000..49a57a7
+--- /dev/null
++++ b/arch/mips/loongson/common/mtd.c
+@@ -0,0 +1,91 @@
++/*
++ *  Driver for flushing/dumping ROM of PMON on loongson family machines
++ *
++ *  Copyright (C) 2008-2009 Lemote Inc.
++ *  Author: Yan Hua <yanh@lemote.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/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/io.h>
++
++#include <loongson.h>
++
++#define FLASH_PHYS_ADDR LOONGSON_BOOT_BASE
++#define FLASH_SIZE 0x080000
++
++#define FLASH_PARTITION0_ADDR 0x00000000
++#define FLASH_PARTITION0_SIZE 0x00080000
++
++struct map_info flash_map = {
++	.name = "flash device",
++	.size = FLASH_SIZE,
++	.bankwidth = 1,
++};
++
++struct mtd_partition flash_parts[] = {
++	{
++	 .name = "Bootloader",
++	 .offset = FLASH_PARTITION0_ADDR,
++	 .size = FLASH_PARTITION0_SIZE},
++};
++
++#define PARTITION_COUNT ARRAY_SIZE(flash_parts)
++
++static struct mtd_info *mymtd;
++
++int __init init_flash(void)
++{
++	printk(KERN_NOTICE "flash device: %x at %x\n",
++	       FLASH_SIZE, FLASH_PHYS_ADDR);
++
++	flash_map.phys = FLASH_PHYS_ADDR;
++	flash_map.virt = ioremap(FLASH_PHYS_ADDR, FLASH_SIZE);
++
++	if (!flash_map.virt) {
++		printk(KERN_NOTICE "Failed to ioremap\n");
++		return -EIO;
++	}
++
++	simple_map_init(&flash_map);
++
++	mymtd = do_map_probe("cfi_probe", &flash_map);
++	if (mymtd) {
++		add_mtd_partitions(mymtd, flash_parts, PARTITION_COUNT);
++		printk(KERN_NOTICE "pmon flash device initialized\n");
++		return 0;
++	}
++
++	iounmap((void *)flash_map.virt);
++	return -ENXIO;
++}
++
++static void __exit cleanup_flash(void)
++{
++	if (mymtd) {
++		del_mtd_partitions(mymtd);
++		map_destroy(mymtd);
++	}
++	if (flash_map.virt) {
++		iounmap((void *)flash_map.virt);
++		flash_map.virt = 0;
++	}
++}
++
++module_init(init_flash);
++module_exit(cleanup_flash);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Yanhua <yanh@lemote.com>");
++MODULE_DESCRIPTION("MTD driver for pmon flushing/dumping");
+diff --git a/arch/mips/loongson/common/pci.c b/arch/mips/loongson/common/pci.c
+index fa77844..a992931 100644
+--- a/arch/mips/loongson/common/pci.c
++++ b/arch/mips/loongson/common/pci.c
+@@ -50,11 +50,11 @@ static void __init setup_pcimap(void)
+ 		LOONGSON_PCIMAP_WIN(0, 0);
+ 
+ 	/*
+-	 * PCI-DMA to local mapping: [2G,2G+256M] -> [0M,256M]
++	 * PCI-DMA to local mapping: [2G,4G] -> [0M,2G]
+ 	 */
+ 	LOONGSON_PCIBASE0 = 0x80000000ul;   /* base: 2G -> mmap: 0M */
+-	/* size: 256M, burst transmission, pre-fetch enable, 64bit */
+-	LOONGSON_PCI_HIT0_SEL_L = 0xc000000cul;
++	/* size: 2G, burst transmission, pre-fetch enable, 64bit */
++	LOONGSON_PCI_HIT0_SEL_L = 0x8000000cul;
+ 	LOONGSON_PCI_HIT0_SEL_H = 0xfffffffful;
+ 	LOONGSON_PCI_HIT1_SEL_L = 0x00000006ul; /* set this BAR as invalid */
+ 	LOONGSON_PCI_HIT1_SEL_H = 0x00000000ul;
+diff --git a/arch/mips/loongson/common/serial.c b/arch/mips/loongson/common/serial.c
+index 5f2b78a..c828600 100644
+--- a/arch/mips/loongson/common/serial.c
++++ b/arch/mips/loongson/common/serial.c
+@@ -10,6 +10,7 @@
+  * Author: Wu Zhangjin (wuzhangjin@gmail.com)
+  */
+ 
++#include <linux/module.h>
+ #include <linux/io.h>
+ #include <linux/init.h>
+ #include <linux/serial_8250.h>
+@@ -19,58 +20,44 @@
+ #include <loongson.h>
+ #include <machine.h>
+ 
+-#define PORT(int)			\
++#define PORT(int, base_baud, io_type, port)			\
+ {								\
+ 	.irq		= int,					\
+-	.uartclk	= 1843200,				\
+-	.iotype		= UPIO_PORT,				\
++	.uartclk	= base_baud,				\
++	.iotype		= io_type,				\
+ 	.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,	\
+ 	.regshift	= 0,					\
++	.iobase	= port,						\
+ }
+ 
+-#define PORT_M(int)				\
+-{								\
+-	.irq		= MIPS_CPU_IRQ_BASE + (int),		\
+-	.uartclk	= 3686400,				\
+-	.iotype		= UPIO_MEM,				\
+-	.membase	= (void __iomem *)NULL,			\
+-	.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,	\
+-	.regshift	= 0,					\
+-}
+-
+-static struct plat_serial8250_port uart8250_data[][2] = {
+-	[MACH_LOONGSON_UNKNOWN]		{},
+-	[MACH_LEMOTE_FL2E]		{PORT(4), {} },
+-	[MACH_LEMOTE_FL2F]		{PORT(3), {} },
+-	[MACH_LEMOTE_ML2F7]		{PORT_M(3), {} },
+-	[MACH_LEMOTE_YL2F89]		{PORT_M(3), {} },
+-	[MACH_DEXXON_GDIUM2F10]		{PORT_M(3), {} },
+-	[MACH_LEMOTE_NAS]		{PORT_M(3), {} },
+-	[MACH_LEMOTE_LL2F]		{PORT(3), {} },
+-	[MACH_LOONGSON_END]		{},
++static struct plat_serial8250_port uart8250_data[] = {
++	/* ttyS0: cpu_uart0 Yeeloong, Gdium, UNAS, ...  */
++	PORT((MIPS_CPU_IRQ_BASE + 3), 3686400, UPIO_MEM, 0x3f8),
++	/* ttyS1: sb_uart1 2E */
++	PORT(4, 1843200, UPIO_PORT, 0x3f8),
++	/* ttyS2: sb_uart2 fuloong2f */
++	PORT(3, 1843200, UPIO_PORT, 0x2f8),
++	{},
+ };
+ 
+ static struct platform_device uart8250_device = {
+ 	.name = "serial8250",
+ 	.id = PLAT8250_DEV_PLATFORM,
++	.dev = {
++		.platform_data = uart8250_data,
++	},
+ };
+ 
+ static int __init serial_init(void)
+ {
+-	unsigned char iotype;
+-
+-	iotype = uart8250_data[mips_machtype][0].iotype;
+-
+-	if (UPIO_MEM == iotype)
+-		uart8250_data[mips_machtype][0].membase =
+-			(void __iomem *)_loongson_uart_base;
+-	else if (UPIO_PORT == iotype)
+-		uart8250_data[mips_machtype][0].iobase =
+-		    loongson_uart_base - LOONGSON_PCIIO_BASE;
+-
+-	uart8250_device.dev.platform_data = uart8250_data[mips_machtype];
++	uart8250_data[0].membase = (void __iomem *)ioremap_nocache(
++			LOONGSON_LIO1_BASE + uart8250_data[0].iobase, 8);
+ 
+-	return platform_device_register(&uart8250_device);
++	platform_device_register(&uart8250_device);
++	return 0;
+ }
+ 
+ device_initcall(serial_init);
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("liu shiwei <liushiwei@anheng.com.cn>");
++MODULE_DESCRIPTION("loongson serial");
+diff --git a/arch/mips/loongson/common/time.c b/arch/mips/loongson/common/time.c
+index 262a1f6..eebbeef 100644
+--- a/arch/mips/loongson/common/time.c
++++ b/arch/mips/loongson/common/time.c
+@@ -10,6 +10,8 @@
+  *  Free Software Foundation;  either version 2 of the	License, or (at your
+  *  option) any later version.
+  */
++#include <linux/rtc.h>
++
+ #include <asm/mc146818-time.h>
+ #include <asm/time.h>
+ 
+@@ -24,8 +26,81 @@ void __init plat_time_init(void)
+ 	setup_mfgpt0_timer();
+ }
+ 
++#ifdef CONFIG_LOONGSON_MC146818
+ void read_persistent_clock(struct timespec *ts)
+ {
+ 	ts->tv_sec = mc146818_get_cmos_time();
+ 	ts->tv_nsec = 0;
+ }
++#else
++
++/* If no CMOS RTC, use the one below */
++
++/*
++ * Cloned from drivers/rtc/hctosys.c
++ *
++ * If CONFIG_RTC_HCTOSYS=y is enabled, the system time can be set from the
++ * hardware clock(when boot and resuming from suspend), this may be also done
++ * (duplicately) by the timekeeper, which may need to be avoided(TODO).
++ *
++ * read_persistent_clock() may be useful in some places, e.g. there is not
++ * peristent clock in the system, we can use this to recover the system time.
++ *
++ * Note: The device indicated by CONFIG_RTC_HCTOSYS_DEVICE must be the one
++ * created by the RTC driver. Use Gdium as an example, We must disable the
++ * rt_cmos driver If we want to use the rtc_m41t80 driver for
++ * CONFIG_RTC_HCTOSYS_DEVICE is configured as /dev/rtc0, if rtc_cmos is
++ * enabled, rtc_cmos driver will be used, but it is not supported by Gdium.
++ * So, for Gdium, please ensure "# CONFIG_RTC_DRV_CMOS is not set"
++ */
++
++#ifdef CONFIG_RTC_HCTOSYS
++void read_persistent_clock(struct timespec *ts)
++{
++	int err = -ENODEV;
++	struct rtc_time tm;
++	struct rtc_device *rtc;
++
++	/* We can not access the RTC device before it is initialized ... */
++	if (rtc_hctosys_ret != 0) {
++		ts->tv_sec = 0;
++		ts->tv_nsec = 0;
++		return;
++	}
++
++	rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
++
++	if (rtc == NULL) {
++		pr_err("%s: unable to open rtc device (%s)\n",
++			__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
++		goto err_open;
++	}
++
++	err = rtc_read_time(rtc, &tm);
++	if (err) {
++		dev_err(rtc->dev.parent,
++			"hctosys: unable to read the hardware clock\n");
++		goto err_read;
++
++	}
++
++	err = rtc_valid_tm(&tm);
++	if (err) {
++		dev_err(rtc->dev.parent,
++			"hctosys: invalid date/time\n");
++		goto err_invalid;
++	}
++
++	ts->tv_nsec = NSEC_PER_SEC >> 1,
++	rtc_tm_to_time(&tm, &ts->tv_sec);
++
++err_invalid:
++err_read:
++	rtc_class_close(rtc);
++
++err_open:
++	rtc_hctosys_ret = err;
++}
++#endif /* CONFIG_RTC_HCTOSYS */
++
++#endif /* !CONFIG_LOONGSON_MC146818 */
+diff --git a/arch/mips/loongson/common/uart_base.c b/arch/mips/loongson/common/uart_base.c
+deleted file mode 100644
+index e192ad0..0000000
+--- a/arch/mips/loongson/common/uart_base.c
++++ /dev/null
+@@ -1,45 +0,0 @@
+-/*
+- * Copyright (C) 2009 Lemote Inc.
+- * Author: Wu Zhangjin, wuzhangjin@gmail.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 <asm/bootinfo.h>
+-
+-#include <loongson.h>
+-
+-/* ioremapped */
+-unsigned long _loongson_uart_base;
+-EXPORT_SYMBOL(_loongson_uart_base);
+-/* raw */
+-unsigned long loongson_uart_base;
+-EXPORT_SYMBOL(loongson_uart_base);
+-
+-void prom_init_loongson_uart_base(void)
+-{
+-	switch (mips_machtype) {
+-	case MACH_LEMOTE_FL2E:
+-		loongson_uart_base = LOONGSON_PCIIO_BASE + 0x3f8;
+-		break;
+-	case MACH_LEMOTE_FL2F:
+-	case MACH_LEMOTE_LL2F:
+-		loongson_uart_base = LOONGSON_PCIIO_BASE + 0x2f8;
+-		break;
+-	case MACH_LEMOTE_ML2F7:
+-	case MACH_LEMOTE_YL2F89:
+-	case MACH_DEXXON_GDIUM2F10:
+-	case MACH_LEMOTE_NAS:
+-	default:
+-		/* The CPU provided serial port */
+-		loongson_uart_base = LOONGSON_LIO1_BASE + 0x3f8;
+-		break;
+-	}
+-
+-	_loongson_uart_base =
+-		(unsigned long)ioremap_nocache(loongson_uart_base, 8);
+-}
+diff --git a/arch/mips/loongson/fuloong-2e/irq.c b/arch/mips/loongson/fuloong-2e/irq.c
+index ef5ec8f..232930e 100644
+--- a/arch/mips/loongson/fuloong-2e/irq.c
++++ b/arch/mips/loongson/fuloong-2e/irq.c
+@@ -9,7 +9,6 @@
+  */
+ #include <linux/interrupt.h>
+ 
+-#include <asm/irq_cpu.h>
+ #include <asm/i8259.h>
+ 
+ #include <loongson.h>
+@@ -57,11 +56,6 @@ void __init mach_init_irq(void)
+ 	LOONGSON_INTEDGE = LOONGSON_ICU_SYSTEMERR | LOONGSON_ICU_MASTERERR |
+ 	    LOONGSON_ICU_RETRYERR | LOONGSON_ICU_MBOXES;
+ 
+-	/* Sets the first-level interrupt dispatcher. */
+-	mips_cpu_irq_init();
+-	init_i8259_irqs();
+-	bonito_irq_init();
+-
+ 	/* bonito irq at IP2 */
+ 	setup_irq(MIPS_CPU_IRQ_BASE + 2, &cascade_irqaction);
+ 	/* 8259 irq at IP5 */
+diff --git a/arch/mips/loongson/gdium/Makefile b/arch/mips/loongson/gdium/Makefile
+new file mode 100644
+index 0000000..f3f4f51
+--- /dev/null
++++ b/arch/mips/loongson/gdium/Makefile
+@@ -0,0 +1,6 @@
++# Makefile for gdium
++
++obj-y += irq.o reset.o platform.o
++
++obj-$(CONFIG_MFD_SM501) += sm501-pwm.o
++obj-$(CONFIG_GDIUM_PWM_CLOCK) += gdium-clock.o
+diff --git a/arch/mips/loongson/gdium/gdium-clock.c b/arch/mips/loongson/gdium/gdium-clock.c
+new file mode 100644
+index 0000000..fdbf42a
+--- /dev/null
++++ b/arch/mips/loongson/gdium/gdium-clock.c
+@@ -0,0 +1,234 @@
++/*
++ * Doesn't work really well. When used, the clocksource is producing
++ * bad timings and the clockevent can't be used (don't have one shot feature
++ * thus can't switch on the fly and the pwm is initialised too late to be able
++ * to use it at boot time).
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <linux/pwm.h>
++#include <linux/clocksource.h>
++#include <linux/debugfs.h>
++#include <asm/irq_cpu.h>
++#include <asm/mipsregs.h>
++#include <asm/mips-boards/bonito64.h>
++#include <asm/time.h>
++
++#include <loongson.h>
++
++#define CLOCK_PWM		1
++#define CLOCK_PWM_FREQ		1500000				/* Freq in Hz */
++#define CLOCK_LATCH		((CLOCK_PWM_FREQ + HZ/2) / HZ)
++#define CLOCK_PWM_PERIOD	(1000000000/CLOCK_PWM_FREQ)	/* period ns  */
++#define CLOCK_PWM_DUTY		50
++#define CLOCK_PWM_IRQ		(MIPS_CPU_IRQ_BASE + 4)
++
++static const char drv_name[] = "gdium-clock";
++
++static struct pwm_device *clock_pwm;
++
++static DEFINE_SPINLOCK(clock_pwm_lock);
++static uint64_t clock_tick;
++
++static irqreturn_t gdium_pwm_clock_interrupt(int irq, void *dev_id)
++{
++	struct clock_event_device *cd = dev_id;
++	unsigned long flag;
++
++	spin_lock_irqsave(&clock_pwm_lock, flag);
++	clock_tick++;
++	/* wait intn2 to finish */
++	do {
++		LOONGSON_INTENCLR = (1 << 13);
++	} while (LOONGSON_INTISR & (1 << 13));
++	spin_unlock_irqrestore(&clock_pwm_lock, flag);
++
++	if (cd && cd->event_handler)
++		cd->event_handler(cd);
++
++	return IRQ_HANDLED;
++}
++
++static cycle_t gdium_pwm_clock_read(struct clocksource *cs)
++{
++	unsigned long flag;
++	uint32_t jifs;
++	uint64_t ticks;
++
++	spin_lock_irqsave(&clock_pwm_lock, flag);
++	jifs = jiffies;
++	ticks = clock_tick;
++	spin_unlock_irqrestore(&clock_pwm_lock, flag);
++	/* return (cycle_t)ticks; */
++	return (cycle_t)(CLOCK_LATCH * jifs);
++}
++
++static struct clocksource gdium_pwm_clock_clocksource = {
++	.name   = "gdium_csrc",
++	.read   = gdium_pwm_clock_read,
++	.mask   = CLOCKSOURCE_MASK(64),
++	.flags	= CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_MUST_VERIFY,
++	.shift	= 20,
++};
++
++/* Debug fs */
++static int gdium_pwm_clock_show(struct seq_file *s, void *p)
++{
++	unsigned long flag;
++	uint64_t ticks;
++
++	spin_lock_irqsave(&clock_pwm_lock, flag);
++	ticks = clock_tick;
++	spin_unlock_irqrestore(&clock_pwm_lock, flag);
++	seq_printf(s, "%lld\n", ticks);
++	return 0;
++}
++
++static int gdium_pwm_clock_open(struct inode *inode, struct file *file)
++{
++	return single_open(file, gdium_pwm_clock_show, inode->i_private);
++}
++
++static const struct file_operations gdium_pwm_clock_fops = {
++	.open		= gdium_pwm_clock_open,
++	.read		= seq_read,
++	.llseek		= seq_lseek,
++	.release	= single_release,
++	.owner		= THIS_MODULE,
++};
++static struct dentry   *debugfs_file;
++
++static void gdium_pwm_clock_set_mode(enum clock_event_mode mode,
++		struct clock_event_device *evt)
++{
++	/* Nothing to do ...  */
++}
++
++static struct clock_event_device gdium_pwm_clock_cevt = {
++	.name           = "gdium_cevt",
++	.features       = CLOCK_EVT_FEAT_PERIODIC,
++	/* .mult, .shift, .max_delta_ns and .min_delta_ns left uninitialized */
++	.rating         = 299,
++	.irq            = CLOCK_PWM_IRQ,
++	.set_mode       = gdium_pwm_clock_set_mode,
++};
++
++static struct platform_device_id platform_device_ids[] = {
++	{
++		.name = "gdium-pwmclk",
++	},
++	{}
++};
++MODULE_DEVICE_TABLE(platform, platform_device_ids);
++
++static struct platform_driver gdium_pwm_clock_driver = {
++	.driver		= {
++		.name   = drv_name,
++		.owner  = THIS_MODULE,
++	},
++	.id_table = platform_device_ids,
++};
++
++static int gdium_pwm_clock_drvinit(void)
++{
++	int ret;
++	struct clocksource *cs = &gdium_pwm_clock_clocksource;
++	struct clock_event_device *cd = &gdium_pwm_clock_cevt;
++	unsigned int cpu = smp_processor_id();
++
++	clock_tick = 0;
++
++	clock_pwm = pwm_request(CLOCK_PWM, drv_name);
++	if (clock_pwm == NULL) {
++		pr_err("unable to request PWM for Gdium clock\n");
++		return -EBUSY;
++	}
++	ret = pwm_config(clock_pwm, CLOCK_PWM_DUTY, CLOCK_PWM_PERIOD);
++	if (ret) {
++		pr_err("unable to configure PWM for Gdium clock\n");
++		goto err_pwm_request;
++	}
++	ret = pwm_enable(clock_pwm);
++	if (ret) {
++		pr_err("unable to enable PWM for Gdium clock\n");
++		goto err_pwm_request;
++	}
++
++	cd->cpumask = cpumask_of(cpu);
++
++	cd->shift = 22;
++	cd->mult  = div_sc(CLOCK_PWM_FREQ, NSEC_PER_SEC, cd->shift);
++	cd->max_delta_ns = clockevent_delta2ns(0x7FFF, cd);
++	cd->min_delta_ns = clockevent_delta2ns(0xF, cd);
++	clockevents_register_device(&gdium_pwm_clock_cevt);
++
++	/* SM501 PWM1 connected to intn2 <->ip4 */
++	LOONGSON_INTPOL = (1 << 13);
++	LOONGSON_INTEDGE &= ~(1 << 13);
++	ret = request_irq(CLOCK_PWM_IRQ, gdium_pwm_clock_interrupt, IRQF_DISABLED, drv_name, &gdium_pwm_clock_cevt);
++	if (ret) {
++		pr_err("Can't claim irq\n");
++		goto err_pwm_disable;
++	}
++
++	cs->rating = 200;
++	cs->mult = clocksource_hz2mult(CLOCK_PWM_FREQ, cs->shift);
++	ret = clocksource_register(&gdium_pwm_clock_clocksource);
++	if (ret) {
++		pr_err("Can't register clocksource\n");
++		goto err_irq;
++	}
++	pr_info("Clocksource registered with shift %d and mult %d\n",
++			cs->shift, cs->mult);
++
++	debugfs_file = debugfs_create_file(drv_name, S_IFREG | S_IRUGO,
++			NULL, NULL, &gdium_pwm_clock_fops);
++
++	return 0;
++
++err_irq:
++	free_irq(CLOCK_PWM_IRQ, &gdium_pwm_clock_cevt);
++err_pwm_disable:
++	pwm_disable(clock_pwm);
++err_pwm_request:
++	pwm_free(clock_pwm);
++	return ret;
++}
++
++static void gdium_pwm_clock_drvexit(void)
++{
++	free_irq(CLOCK_PWM_IRQ, &gdium_pwm_clock_cevt);
++	pwm_disable(clock_pwm);
++	pwm_free(clock_pwm);
++}
++
++
++static int __devinit gdium_pwm_clock_init(void)
++{
++	int ret = gdium_pwm_clock_drvinit();
++
++	if (ret) {
++		pr_err("Fail to register gdium clock driver\n");
++		return ret;
++	}
++
++	return platform_driver_register(&gdium_pwm_clock_driver);
++}
++
++static void __exit gdium_pwm_clock_cleanup(void)
++{
++	gdium_pwm_clock_drvexit();
++	platform_driver_unregister(&gdium_pwm_clock_driver);
++}
++
++module_init(gdium_pwm_clock_init);
++module_exit(gdium_pwm_clock_cleanup);
++
++MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
++MODULE_DESCRIPTION("Gdium PWM clock driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:gdium-pwmclk");
+diff --git a/arch/mips/loongson/gdium/irq.c b/arch/mips/loongson/gdium/irq.c
+new file mode 100644
+index 0000000..2415d20
+--- /dev/null
++++ b/arch/mips/loongson/gdium/irq.c
+@@ -0,0 +1,55 @@
++/*
++ * Copyright (C) 2007 Lemote Inc.
++ * Author: Fuxin Zhang, zhangfx@lemote.com
++ *
++ * Copyright (c) 2010 yajin <yajin@vm-kernel.org>
++ *
++ *  This program is free software; you can redistribute  it and/or modify it
++ *  under  the terms of  the GNU General  Public License as published by the
++ *  Free Software Foundation;  either version 2 of the  License, or (at your
++ *  option) any later version.
++ */
++
++#include <linux/interrupt.h>
++#include <linux/module.h>
++
++#include <loongson.h>
++#include <machine.h>
++
++#define LOONGSON_TIMER_IRQ      (MIPS_CPU_IRQ_BASE + 7) /* cpu timer */
++#define LOONGSON_NORTH_BRIDGE_IRQ       (MIPS_CPU_IRQ_BASE + 6) /* bonito */
++#define LOONGSON_UART_IRQ       (MIPS_CPU_IRQ_BASE + 3) /* cpu serial port */
++
++void mach_irq_dispatch(unsigned int pending)
++{
++	if (pending & CAUSEF_IP7)
++		do_IRQ(LOONGSON_TIMER_IRQ);
++	else if (pending & CAUSEF_IP6) {        /* North Bridge, Perf counter */
++		do_perfcnt_IRQ();
++		bonito_irqdispatch();
++	} else if (pending & CAUSEF_IP3)        /* CPU UART */
++		do_IRQ(LOONGSON_UART_IRQ);
++#if defined(CONFIG_GDIUM_PWM_CLOCK) || defined(CONFIG_GDIUM_PWM_CLOCK_MODULE)
++	else if (pending & CAUSEF_IP4)		/* SM501 PWM clock */
++		do_IRQ(MIPS_CPU_IRQ_BASE + 4);
++#endif
++	else
++		spurious_interrupt();
++}
++
++static irqreturn_t ip6_action(int cpl, void *dev_id)
++{
++	return IRQ_HANDLED;
++}
++
++struct irqaction ip6_irqaction = {
++	.handler = ip6_action,
++	.name = "cascade",
++	.flags = IRQF_SHARED,
++};
++
++void __init mach_init_irq(void)
++{
++	/* setup north bridge irq (bonito) */
++	setup_irq(LOONGSON_NORTH_BRIDGE_IRQ, &ip6_irqaction);
++}
+diff --git a/arch/mips/loongson/gdium/platform.c b/arch/mips/loongson/gdium/platform.c
+new file mode 100644
+index 0000000..ffafba4
+--- /dev/null
++++ b/arch/mips/loongson/gdium/platform.c
+@@ -0,0 +1,135 @@
++/*
++ * Copyright (c) 2009 Philippe Vachon <philippe@cowpig.ca>
++ *
++ * 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/init.h>
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/pwm_backlight.h>
++#include <linux/i2c.h>
++#include <linux/i2c-gpio.h>
++
++#define GDIUM_GPIO_BASE 224
++
++static struct i2c_board_info __initdata sm502dev_i2c_devices[] = {
++	{
++		I2C_BOARD_INFO("lm75", 0x48),
++	},
++	{
++		I2C_BOARD_INFO("m41t83", 0x68),
++	},
++	{
++		I2C_BOARD_INFO("gdium-laptop", 0x40),
++	},
++};
++
++static int sm502dev_backlight_init(struct device *dev)
++{
++	/* Add gpio request stuff here */
++	return 0;
++}
++
++static void sm502dev_backlight_exit(struct device *dev)
++{
++	/* Add gpio free stuff here */
++}
++
++static struct platform_pwm_backlight_data backlight_data = {
++	.pwm_id		= 0,
++	.max_brightness	= 15,
++	.dft_brightness	= 8,
++	.pwm_period_ns	= 50000, /* 20 kHz */
++	.init		= sm502dev_backlight_init,
++	.exit		= sm502dev_backlight_exit,
++};
++
++static struct platform_device backlight = {
++	.name = "pwm-backlight",
++	.dev  = {
++		.platform_data = &backlight_data,
++	},
++	.id   = -1,
++};
++
++/*
++ * Warning this stunt is very dangerous
++ * as the sm501 gpio have dynamic numbers...
++ */
++/* bus 0 is the one for the ST7, DS75 etc... */
++static struct i2c_gpio_platform_data i2c_gpio0_data = {
++#if CONFIG_GDIUM_VERSION > 2
++	.sda_pin	= GDIUM_GPIO_BASE + 13,
++	.scl_pin	= GDIUM_GPIO_BASE + 6,
++#else
++	.sda_pin        = 192+15,
++	.scl_pin        = 192+14,
++#endif
++	.udelay		= 5,
++	.timeout	= HZ / 10,
++	.sda_is_open_drain = 0,
++	.scl_is_open_drain = 0,
++};
++
++static struct platform_device i2c_gpio0_device = {
++	.name	= "i2c-gpio",
++	.id	= 0,
++	.dev	= { .platform_data  = &i2c_gpio0_data, },
++};
++
++/* bus 1 is for the CRT/VGA external screen */
++static struct i2c_gpio_platform_data i2c_gpio1_data = {
++	.sda_pin	= GDIUM_GPIO_BASE + 10,
++	.scl_pin	= GDIUM_GPIO_BASE + 9,
++	.udelay		= 5,
++	.timeout	= HZ / 10,
++	.sda_is_open_drain = 0,
++	.scl_is_open_drain = 0,
++};
++
++static struct platform_device i2c_gpio1_device = {
++	.name	= "i2c-gpio",
++	.id	= 1,
++	.dev	= { .platform_data  = &i2c_gpio1_data, },
++};
++
++static struct platform_device gdium_clock = {
++	.name		= "gdium-pwmclk",
++	.id		= -1,
++};
++
++static struct platform_device *devices[] __initdata = {
++	&i2c_gpio0_device,
++	&i2c_gpio1_device,
++	&backlight,
++	&gdium_clock,
++};
++
++static int __init gdium_platform_devices_setup(void)
++{
++	int ret;
++
++	pr_info("Registering gdium platform devices\n");
++
++	ret = i2c_register_board_info(0, sm502dev_i2c_devices,
++		ARRAY_SIZE(sm502dev_i2c_devices));
++
++	if (ret != 0) {
++		pr_info("Error while registering platform devices: %d\n", ret);
++		return ret;
++	}
++
++	platform_add_devices(devices, ARRAY_SIZE(devices));
++
++	return 0;
++}
++
++/*
++ * some devices are on the pwm stuff which is behind the mfd which is
++ * behind the pci bus so arch_initcall can't work because too early
++ */
++late_initcall(gdium_platform_devices_setup);
+diff --git a/arch/mips/loongson/gdium/reset.c b/arch/mips/loongson/gdium/reset.c
+new file mode 100644
+index 0000000..8289f95
+--- /dev/null
++++ b/arch/mips/loongson/gdium/reset.c
+@@ -0,0 +1,22 @@
++/* Board-specific reboot/shutdown routines
++ *
++ * Copyright (C) 2010 yajin <yajin@vm-kernel.org>
++ *
++ * This program is free software; you can redistribute  it and/or modify it
++ * under  the terms of  the GNU General  Public License as published by the
++ * Free Software Foundation;  either version 2 of the  License, or (at your
++ * option) any later version.
++ */
++#include <loongson.h>
++
++void mach_prepare_shutdown(void)
++{
++	LOONGSON_GPIOIE &= ~(1<<1);
++	LOONGSON_GPIODATA |= (1<<1);
++}
++
++void mach_prepare_reboot(void)
++{
++	LOONGSON_GPIOIE &= ~(1<<2);
++	LOONGSON_GPIODATA &= ~(1<<2);
++}
+diff --git a/arch/mips/loongson/gdium/sm501-pwm.c b/arch/mips/loongson/gdium/sm501-pwm.c
+new file mode 100644
+index 0000000..5af3b23
+--- /dev/null
++++ b/arch/mips/loongson/gdium/sm501-pwm.c
+@@ -0,0 +1,465 @@
++/*
++ * SM501 PWM clock
++ * Copyright (C) 2009-2010 Arnaud Patard <apatard@mandriva.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/kernel.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/pwm.h>
++#include <linux/sm501.h>
++#include <linux/sm501-regs.h>
++#include <linux/debugfs.h>
++#include <linux/seq_file.h>
++
++static const char drv_name[] = "sm501-pwm";
++
++#define INPUT_CLOCK		96 /* MHz */
++#define PWM_COUNT		3
++
++#define SM501PWM_HIGH_COUNTER	(1<<20)
++#define SM501PWM_LOW_COUNTER	(1<<8)
++#define SM501PWM_CLOCK_DIVIDE	(1>>4)
++#define SM501PWM_IP		(1<<3)
++#define SM501PWM_I		(1<<2)
++#define SM501PWM_E		(1<<0)
++
++struct pwm_device {
++	struct list_head	node;
++	struct device		*dev;
++	void __iomem		*regs;
++	int			duty_ns;
++	int			period_ns;
++	char			enabled;
++	void			(*handler)(struct pwm_device *pwm);
++
++	const char		*label;
++	unsigned int		use_count;
++	unsigned int		pwm_id;
++};
++
++struct sm501pwm_info {
++	void __iomem	*regs;
++	int		irq;
++	struct resource *res;
++	struct device	*dev;
++	struct dentry	*debugfs;
++
++	struct pwm_device pwm[3];
++};
++
++int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
++{
++	unsigned int high, low, divider;
++	int divider1, divider2;
++	unsigned long long delay;
++
++	if (!pwm || !pwm->regs || period_ns == 0 || duty_ns > period_ns)
++		return -EINVAL;
++
++	/* Get delay
++	 * We're loosing some precision but multiplying then dividing
++	 * will overflow
++	 */
++	if (period_ns > 1000) {
++		delay = period_ns / 1000;
++		delay *= INPUT_CLOCK;
++	} else {
++		delay = period_ns * 96;
++		delay /= 1000;
++	}
++
++	/* Get the number of clock low and high */
++	high  = delay * duty_ns / period_ns;
++	low = delay - high;
++
++	/* Get divider to make 'low' and 'high' fit into 12 bits */
++	/* No need to say that the divider must be >= 0 */
++	divider1 = fls(low)-12;
++	divider2 = fls(high)-12;
++
++	if (divider1 < 0)
++		divider1 = 0;
++	if (divider2 < 0)
++		divider2 = 0;
++
++	divider = max(divider1, divider2);
++
++	low >>= divider;
++	high >>= divider;
++
++	pwm->duty_ns = duty_ns;
++	pwm->period_ns = period_ns;
++
++	writel((high<<20)|(low<<8)|(divider<<4), pwm->regs);
++	return 0;
++}
++EXPORT_SYMBOL(pwm_config);
++
++int pwm_enable(struct pwm_device *pwm)
++{
++	u32 reg;
++
++	if (!pwm)
++		return -EINVAL;
++
++	switch (pwm->pwm_id) {
++	case 0:
++		sm501_configure_gpio(pwm->dev->parent, 29, 1);
++		break;
++	case 1:
++		sm501_configure_gpio(pwm->dev->parent, 30, 1);
++		break;
++	case 2:
++		sm501_configure_gpio(pwm->dev->parent, 31, 1);
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	reg = readl(pwm->regs);
++	reg |= (SM501PWM_IP | SM501PWM_E);
++	writel(reg, pwm->regs);
++	pwm->enabled = 1;
++
++	return 0;
++}
++EXPORT_SYMBOL(pwm_enable);
++
++void pwm_disable(struct pwm_device *pwm)
++{
++	u32 reg;
++
++	if (!pwm)
++		return;
++
++	reg = readl(pwm->regs);
++	reg &= ~(SM501PWM_IP | SM501PWM_E);
++	writel(reg, pwm->regs);
++
++	switch (pwm->pwm_id) {
++	case 0:
++		sm501_configure_gpio(pwm->dev->parent, 29, 0);
++		break;
++	case 1:
++		sm501_configure_gpio(pwm->dev->parent, 30, 0);
++		break;
++	case 2:
++		sm501_configure_gpio(pwm->dev->parent, 31, 0);
++		break;
++	default:
++		break;
++	}
++	pwm->enabled = 0;
++}
++EXPORT_SYMBOL(pwm_disable);
++
++static DEFINE_MUTEX(pwm_lock);
++static LIST_HEAD(pwm_list);
++
++struct pwm_device *pwm_request(int pwm_id, const char *label)
++{
++	struct pwm_device *pwm;
++	int found = 0;
++
++	mutex_lock(&pwm_lock);
++
++	list_for_each_entry(pwm, &pwm_list, node) {
++		if (pwm->pwm_id == pwm_id && pwm->use_count == 0) {
++			pwm->use_count++;
++			pwm->label = label;
++			found = 1;
++			break;
++		}
++	}
++
++	mutex_unlock(&pwm_lock);
++
++	return (found) ? pwm : NULL;
++}
++EXPORT_SYMBOL(pwm_request);
++
++void pwm_free(struct pwm_device *pwm)
++{
++	mutex_lock(&pwm_lock);
++
++	if (pwm->use_count) {
++		pwm->use_count--;
++		pwm->label = NULL;
++	} else
++		dev_warn(pwm->dev, "PWM device already freed\n");
++
++	mutex_unlock(&pwm_lock);
++}
++EXPORT_SYMBOL(pwm_free);
++
++int pwm_int_enable(struct pwm_device *pwm)
++{
++	unsigned long conf;
++
++	if (!pwm || !pwm->regs || !pwm->handler)
++		return -EINVAL;
++
++	conf = readl(pwm->regs);
++	conf |= SM501PWM_I;
++	writel(conf, pwm->regs);
++	return 0;
++}
++EXPORT_SYMBOL(pwm_int_enable);
++
++int pwm_int_disable(struct pwm_device *pwm)
++{
++	unsigned long conf;
++
++	if (!pwm || !pwm->regs || !pwm->handler)
++		return -EINVAL;
++
++	conf = readl(pwm->regs);
++	conf &= ~SM501PWM_I;
++	writel(conf, pwm->regs);
++	return 0;
++}
++EXPORT_SYMBOL(pwm_int_disable);
++
++int pwm_set_handler(struct pwm_device *pwm,
++		    void (*handler)(struct pwm_device *pwm))
++{
++	if (!pwm || !handler)
++		return -EINVAL;
++	pwm->handler = handler;
++	return 0;
++}
++EXPORT_SYMBOL(pwm_set_handler);
++
++static irqreturn_t sm501pwm_irq(int irq, void *dev_id)
++{
++	unsigned long value;
++	struct sm501pwm_info *info = (struct sm501pwm_info *)dev_id;
++	struct pwm_device *pwm;
++	int i;
++
++	value = sm501_modify_reg(info->dev->parent, SM501_IRQ_STATUS, 0, 0);
++
++	/* Check is the interrupt is for us */
++	if (value & (1<<22)) {
++		for (i = 0 ; i < PWM_COUNT ; i++) {
++			/*
++			 * Find which pwm triggered the interrupt
++			 * and ack
++			 */
++			value = readl(info->regs + i*4);
++			if (value & SM501PWM_IP)
++				writel(value | SM501PWM_IP, info->regs + i*4);
++
++			pwm = &info->pwm[i];
++			if (pwm->handler)
++				pwm->handler(pwm);
++		}
++		return IRQ_HANDLED;
++	}
++
++	return IRQ_NONE;
++}
++
++static void add_pwm(int id, struct sm501pwm_info *info)
++{
++	struct pwm_device *pwm = &info->pwm[id];
++
++	pwm->use_count	= 0;
++	pwm->pwm_id	= id;
++	pwm->dev	= info->dev;
++	pwm->regs	= info->regs + id * 4;
++
++	mutex_lock(&pwm_lock);
++	list_add_tail(&pwm->node, &pwm_list);
++	mutex_unlock(&pwm_lock);
++}
++
++static void del_pwm(int id, struct sm501pwm_info *info)
++{
++	struct pwm_device *pwm = &info->pwm[id];
++
++	pwm->use_count  = 0;
++	pwm->pwm_id     = -1;
++	mutex_lock(&pwm_lock);
++	list_del(&pwm->node);
++	mutex_unlock(&pwm_lock);
++}
++
++/* Debug fs */
++static int sm501pwm_show(struct seq_file *s, void *p)
++{
++	struct pwm_device *pwm;
++
++	mutex_lock(&pwm_lock);
++	list_for_each_entry(pwm, &pwm_list, node) {
++		if (pwm->use_count) {
++			seq_printf(s, "pwm-%d (%12s) %d %d %s\n",
++					pwm->pwm_id, pwm->label,
++					pwm->duty_ns, pwm->period_ns,
++					pwm->enabled ? "on" : "off");
++			seq_printf(s, "       %08x\n", readl(pwm->regs));
++		}
++	}
++	mutex_unlock(&pwm_lock);
++
++	return 0;
++}
++
++static int sm501pwm_open(struct inode *inode, struct file *file)
++{
++	return single_open(file, sm501pwm_show, inode->i_private);
++}
++
++static const struct file_operations sm501pwm_fops = {
++	.open		= sm501pwm_open,
++	.read		= seq_read,
++	.llseek		= seq_lseek,
++	.release	= single_release,
++	.owner		= THIS_MODULE,
++};
++
++static int __init sm501pwm_probe(struct platform_device *pdev)
++{
++	struct sm501pwm_info *info;
++	struct device   *dev = &pdev->dev;
++	struct resource *res;
++	int ret = 0;
++	int res_len;
++	int i;
++
++	info = kzalloc(sizeof(struct sm501pwm_info), GFP_KERNEL);
++	if (!info) {
++		dev_err(dev, "Allocation failure\n");
++		ret = -ENOMEM;
++		goto err;
++	}
++	info->dev = dev;
++	platform_set_drvdata(pdev, info);
++
++	/* Get irq number */
++	info->irq = platform_get_irq(pdev, 0);
++	if (!info->irq) {
++		dev_err(dev, "no irq found\n");
++		ret = -ENODEV;
++		goto err_alloc;
++	}
++
++	/* Get regs address */
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	if (res == NULL) {
++		dev_err(dev, "No memory resource found\n");
++		ret = -ENODEV;
++		goto err_alloc;
++	}
++	info->res = res;
++	res_len = (res->end - res->start)+1;
++
++	if (!request_mem_region(res->start, res_len, drv_name)) {
++		dev_err(dev, "Can't request iomem resource\n");
++		ret = -EBUSY;
++		goto err_alloc;
++	}
++
++	info->regs = ioremap(res->start, res_len);
++	if (!info->regs) {
++		dev_err(dev, "ioremap failed\n");
++		ret = -ENOMEM;
++		goto err_mem;
++	}
++
++	ret = request_irq(info->irq, sm501pwm_irq, IRQF_SHARED, drv_name, info);
++	if (ret != 0) {
++		dev_err(dev, "can't get irq\n");
++		goto err_map;
++	}
++
++
++	sm501_unit_power(info->dev->parent, SM501_GATE_GPIO, 1);
++
++	for (i = 0; i < 3; i++)
++		add_pwm(i, info);
++
++	dev_info(dev, "SM501 PWM Found at %lx irq %d\n",
++		 (unsigned long)info->res->start, info->irq);
++
++	info->debugfs = debugfs_create_file("pwm", S_IFREG | S_IRUGO,
++				NULL, info, &sm501pwm_fops);
++
++
++	return 0;
++
++err_map:
++	iounmap(info->regs);
++
++err_mem:
++	release_mem_region(res->start, res_len);
++
++err_alloc:
++	kfree(info);
++	platform_set_drvdata(pdev, NULL);
++err:
++	return ret;
++}
++
++static int sm501pwm_remove(struct platform_device *pdev)
++{
++	struct sm501pwm_info *info = platform_get_drvdata(pdev);
++	int i;
++
++	if (info->debugfs)
++		debugfs_remove(info->debugfs);
++
++	for (i = 0; i < 3; i++) {
++		pwm_disable(&info->pwm[i]);
++		del_pwm(i, info);
++	}
++
++	sm501_unit_power(info->dev->parent, SM501_GATE_GPIO, 0);
++	sm501_modify_reg(info->dev->parent, SM501_IRQ_STATUS, 0, 1<<22);
++
++	free_irq(info->irq, info);
++	iounmap(info->regs);
++	release_mem_region(info->res->start,
++			   (info->res->end - info->res->start)+1);
++	kfree(info);
++	platform_set_drvdata(pdev, NULL);
++
++	return 0;
++}
++
++static struct platform_driver sm501pwm_driver = {
++	.probe		= sm501pwm_probe,
++	.remove		= sm501pwm_remove,
++	.driver		= {
++		.name	= drv_name,
++		.owner	= THIS_MODULE,
++	},
++};
++
++static int __devinit sm501pwm_init(void)
++{
++	return platform_driver_register(&sm501pwm_driver);
++}
++
++static void __exit sm501pwm_cleanup(void)
++{
++	platform_driver_unregister(&sm501pwm_driver);
++}
++
++module_init(sm501pwm_init);
++module_exit(sm501pwm_cleanup);
++
++MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
++MODULE_DESCRIPTION("SM501 PWM driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:sm501-pwm");
+diff --git a/arch/mips/loongson/lemote-2f/Makefile b/arch/mips/loongson/lemote-2f/Makefile
+index 4f9eaa3..f945bd7a 100644
+--- a/arch/mips/loongson/lemote-2f/Makefile
++++ b/arch/mips/loongson/lemote-2f/Makefile
+@@ -2,7 +2,7 @@
+ # Makefile for lemote loongson2f family machines
+ #
+ 
+-obj-y += clock.o machtype.o irq.o reset.o ec_kb3310b.o
++obj-y += clock.o machtype.o irq.o reset.o ec_kb3310b.o platform.o
+ 
+ #
+ # Suspend Support
+diff --git a/arch/mips/loongson/lemote-2f/irq.c b/arch/mips/loongson/lemote-2f/irq.c
+index 6f8682e..2d54037 100644
+--- a/arch/mips/loongson/lemote-2f/irq.c
++++ b/arch/mips/loongson/lemote-2f/irq.c
+@@ -11,9 +11,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/module.h>
+ 
+-#include <asm/irq_cpu.h>
+ #include <asm/i8259.h>
+-#include <asm/mipsregs.h>
+ 
+ #include <loongson.h>
+ #include <machine.h>
+@@ -117,11 +115,6 @@ void __init mach_init_irq(void)
+ 	LOONGSON_INTPOL = LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1;
+ 	LOONGSON_INTEDGE &= ~(LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1);
+ 
+-	/* Sets the first-level interrupt dispatcher. */
+-	mips_cpu_irq_init();
+-	init_i8259_irqs();
+-	bonito_irq_init();
+-
+ 	/* setup north bridge irq (bonito) */
+ 	setup_irq(LOONGSON_NORTH_BRIDGE_IRQ, &ip6_irqaction);
+ 	/* setup source bridge irq (i8259) */
+diff --git a/arch/mips/loongson/lemote-2f/platform.c b/arch/mips/loongson/lemote-2f/platform.c
+new file mode 100644
+index 0000000..5316360
+--- /dev/null
++++ b/arch/mips/loongson/lemote-2f/platform.c
+@@ -0,0 +1,48 @@
++/*
++ * Copyright (C) 2009 Lemote Inc.
++ * Author: Wu Zhangjin, wuzhangjin@gmail.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/err.h>
++#include <linux/platform_device.h>
++
++#include <asm/bootinfo.h>
++
++static struct platform_device yeeloong_pdev = {
++	.name = "yeeloong_laptop",
++	.id = -1,
++};
++
++static struct platform_device lynloong_pdev = {
++	.name = "lynloong_pc",
++	.id = -1,
++};
++
++static int __init lemote2f_platform_init(void)
++{
++	struct platform_device *pdev = NULL;
++
++	switch (mips_machtype) {
++	case MACH_LEMOTE_YL2F89:
++		pdev = &yeeloong_pdev;
++		break;
++	case MACH_LEMOTE_LL2F:
++		pdev = &lynloong_pdev;
++		break;
++	default:
++		break;
++
++	}
++
++	if (pdev != NULL)
++		return platform_device_register(pdev);
++
++	return -ENODEV;
++}
++
++arch_initcall(lemote2f_platform_init);
+diff --git a/arch/mips/loongson/lemote-2f/pm.c b/arch/mips/loongson/lemote-2f/pm.c
+index cac4d38..2b6e0ef 100644
+--- a/arch/mips/loongson/lemote-2f/pm.c
++++ b/arch/mips/loongson/lemote-2f/pm.c
+@@ -140,10 +140,10 @@ int wakeup_loongson(void)
+ 
+ void __weak mach_suspend(void)
+ {
+-	disable_mfgpt0_counter();
++	disable_mfgpt_counter();
+ }
+ 
+ void __weak mach_resume(void)
+ {
+-	enable_mfgpt0_counter();
++	enable_mfgpt_counter();
+ }
+diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
+index 0b4e2e3..60fad2c 100644
+--- a/arch/mips/math-emu/cp1emu.c
++++ b/arch/mips/math-emu/cp1emu.c
+@@ -7,6 +7,9 @@
+  * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
+  * Copyright (C) 2000  MIPS Technologies, Inc.
+  *
++ * Loongson instruction support
++ * Copyright (C) 2011  Mark H Weaver <mhw@netris.org>
++ *
+  *  This program is free software; you can distribute it and/or modify it
+  *  under the terms of the GNU General Public License (Version 2) as
+  *  published by the Free Software Foundation.
+@@ -58,6 +61,14 @@
+ #endif
+ #define __mips 4
+ 
++#ifdef __loongson_fp
++#undef __loongson_fp
++#endif
++#if __mips >= 4 && __mips != 32
++/* Include support for Loongson floating point instructions */
++#define __loongson_fp 1
++#endif
++
+ /* Function which emulates a floating point instruction. */
+ 
+ static int fpu_emu(struct pt_regs *, struct mips_fpu_struct *,
+@@ -67,6 +78,10 @@ static int fpu_emu(struct pt_regs *, struct mips_fpu_struct *,
+ static int fpux_emu(struct pt_regs *,
+ 	struct mips_fpu_struct *, mips_instruction, void *__user *);
+ #endif
++#ifdef __loongson_fp
++static int loongson_spec2_emu(struct pt_regs *,
++	struct mips_fpu_struct *, mips_instruction, void *__user *);
++#endif
+ 
+ /* Further private data for which no space exists in mips_fpu_struct */
+ 
+@@ -896,6 +911,14 @@ static inline int cop1_64bit(struct pt_regs *xcp)
+ #define DPFROMREG(dp, x)	DIFROMREG((dp).bits, x)
+ #define DPTOREG(dp, x)	DITOREG((dp).bits, x)
+ 
++/* Support for Loongson paired single floating-point format */
++#define PSIFROMREG(si1, si2, x) ({ u64 di; DIFROMREG(di, x);		\
++			(si1) = (u32)di; (si2) = (u32)(di >> 32); })
++#define PSITOREG(si1, si2, x) DITOREG((si1) | ((u64)(si2) << 32), x)
++
++#define PSPFROMREG(sp1, sp2, x) PSIFROMREG((sp1).bits, (sp2).bits, x)
++#define PSPTOREG(sp1, sp2, x)	PSITOREG((sp1).bits, (sp2).bits, x)
++
+ /*
+  * Emulate the single floating point instruction pointed at by EPC.
+  * Two instructions if the instruction is in a branch delay slot.
+@@ -1291,6 +1314,15 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ 		break;
+ #endif
+ 
++#ifdef __loongson_fp
++	case spec2_op:{
++		int sig = loongson_spec2_emu(xcp, ctx, ir, fault_addr);
++		if (sig)
++			return sig;
++		break;
++	}
++#endif
++
+ 	default:
+ sigill:
+ 		return SIGILL;
+@@ -1370,6 +1402,172 @@ DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub, );
+ DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg);
+ DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg);
+ 
++#ifdef __loongson_fp
++static int loongson_spec2_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
++	mips_instruction ir, void *__user *fault_addr)
++{
++	int rfmt;		/* resulting format */
++	unsigned rcsr = 0;	/* resulting csr */
++	union {
++		ieee754dp d;
++		struct {
++			ieee754sp s;
++			ieee754sp s2;
++		};
++	} rv;			/* resulting value */
++
++	/* XXX maybe add a counter for loongson spec2 fp instructions? */
++	/* MIPS_FPU_EMU_INC_STATS(cp1xops); */
++
++	switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) {
++	case s_fmt:{
++		ieee754sp(*handler) (ieee754sp, ieee754sp, ieee754sp);
++		ieee754sp fd, fs, ft;
++
++		switch (MIPSInst_FUNC(ir)) {
++		case loongson_madd_op:
++			handler = fpemu_sp_madd;
++			goto scoptop;
++		case loongson_msub_op:
++			handler = fpemu_sp_msub;
++			goto scoptop;
++		case loongson_nmadd_op:
++			handler = fpemu_sp_nmadd;
++			goto scoptop;
++		case loongson_nmsub_op:
++			handler = fpemu_sp_nmsub;
++			goto scoptop;
++
++		      scoptop:
++			SPFROMREG(fd, MIPSInst_FD(ir));
++			SPFROMREG(fs, MIPSInst_FS(ir));
++			SPFROMREG(ft, MIPSInst_FT(ir));
++			rv.s = (*handler) (fd, fs, ft);
++
++		      copcsr:
++			if (ieee754_cxtest(IEEE754_INEXACT))
++				rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S;
++			if (ieee754_cxtest(IEEE754_UNDERFLOW))
++				rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S;
++			if (ieee754_cxtest(IEEE754_OVERFLOW))
++				rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S;
++			if (ieee754_cxtest(IEEE754_INVALID_OPERATION))
++				rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S;
++
++			break;
++
++		default:
++			return SIGILL;
++		}
++		break;
++	}
++
++	case d_fmt:{
++		ieee754dp(*handler) (ieee754dp, ieee754dp, ieee754dp);
++		ieee754dp fd, fs, ft;
++
++		switch (MIPSInst_FUNC(ir)) {
++		case loongson_madd_op:
++			handler = fpemu_dp_madd;
++			goto dcoptop;
++		case loongson_msub_op:
++			handler = fpemu_dp_msub;
++			goto dcoptop;
++		case loongson_nmadd_op:
++			handler = fpemu_dp_nmadd;
++			goto dcoptop;
++		case loongson_nmsub_op:
++			handler = fpemu_dp_nmsub;
++			goto dcoptop;
++
++		      dcoptop:
++			DPFROMREG(fd, MIPSInst_FD(ir));
++			DPFROMREG(fs, MIPSInst_FS(ir));
++			DPFROMREG(ft, MIPSInst_FT(ir));
++			rv.d = (*handler) (fd, fs, ft);
++			goto copcsr;
++
++		default:
++			return SIGILL;
++		}
++		break;
++	}
++
++	case ps_fmt:{
++		ieee754sp(*handler) (ieee754sp, ieee754sp, ieee754sp);
++		struct _ieee754_csr ieee754_csr_save;
++		ieee754sp fd1, fs1, ft1;
++		ieee754sp fd2, fs2, ft2;
++
++		switch (MIPSInst_FUNC(ir)) {
++		case loongson_madd_op:
++			handler = fpemu_sp_madd;
++			goto pscoptop;
++		case loongson_msub_op:
++			handler = fpemu_sp_msub;
++			goto pscoptop;
++		case loongson_nmadd_op:
++			handler = fpemu_sp_nmadd;
++			goto pscoptop;
++		case loongson_nmsub_op:
++			handler = fpemu_sp_nmsub;
++			goto pscoptop;
++
++		      pscoptop:
++			PSPFROMREG(fd1, fd2, MIPSInst_FD(ir));
++			PSPFROMREG(fs1, fs2, MIPSInst_FS(ir));
++			PSPFROMREG(ft1, ft2, MIPSInst_FT(ir));
++			rv.s = (*handler) (fd1, fs1, ft1);
++			ieee754_csr_save = ieee754_csr;
++			rv.s2 = (*handler) (fd2, fs2, ft2);
++			ieee754_csr.cx |= ieee754_csr_save.cx;
++			ieee754_csr.sx |= ieee754_csr_save.sx;
++			goto copcsr;
++
++		default:
++			return SIGILL;
++		}
++		break;
++	}
++
++	default:
++		return SIGILL;
++	}
++
++	/*
++	 * Update the fpu CSR register for this operation.
++	 * If an exception is required, generate a tidy SIGFPE exception,
++	 * without updating the result register.
++	 * Note: cause exception bits do not accumulate, they are rewritten
++	 * for each op; only the flag/sticky bits accumulate.
++	 */
++	ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr;
++	if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
++		/*printk ("SIGFPE: fpu csr = %08x\n",ctx->fcr31); */
++		return SIGFPE;
++	}
++
++	/*
++	 * Now we can safely write the result back to the register file.
++	 */
++	switch (rfmt) {
++	case d_fmt:
++		DPTOREG(rv.d, MIPSInst_FD(ir));
++		break;
++	case s_fmt:
++		SPTOREG(rv.s, MIPSInst_FD(ir));
++		break;
++	case ps_fmt:
++		PSPTOREG(rv.s, rv.s2, MIPSInst_FD(ir));
++		break;
++	default:
++		return SIGILL;
++	}
++
++	return 0;
++}
++#endif
++
+ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ 	mips_instruction ir, void *__user *fault_addr)
+ {
+@@ -1463,7 +1661,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ 			break;
+ 
+ 		default:
+-			return SIGILL;
++			goto SIGILL_unless_prefx_op;
+ 		}
+ 		break;
+ 	}
+@@ -1533,7 +1731,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ 			goto copcsr;
+ 
+ 		default:
+-			return SIGILL;
++			goto SIGILL_unless_prefx_op;
+ 		}
+ 		break;
+ 	}
+@@ -1546,6 +1744,11 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ 		break;
+ 
+ 	default:
++	      SIGILL_unless_prefx_op:
++		if (MIPSInst_FUNC(ir) == prefx_op) {
++			/* ignore prefx operation */
++			break;
++		}
+ 		return SIGILL;
+ 	}
+ 
+@@ -1566,7 +1769,12 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ 	unsigned cond;
+ 	union {
+ 		ieee754dp d;
+-		ieee754sp s;
++		struct {
++			ieee754sp s;
++#ifdef __loongson_fp
++			ieee754sp s2; /* for Loongson paired singles */
++#endif
++		};
+ 		int w;
+ #ifdef __mips64
+ 		s64 l;
+@@ -1638,7 +1846,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ 		case fmov_op:
+ 			/* an easy one */
+ 			SPFROMREG(rv.s, MIPSInst_FS(ir));
+-			goto copcsr;
++			break;
+ 
+ 			/* binary op on handler */
+ 		      scopbop:
+@@ -1825,7 +2033,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ 		case fmov_op:
+ 			/* an easy one */
+ 			DPFROMREG(rv.d, MIPSInst_FS(ir));
+-			goto copcsr;
++			break;
+ 
+ 			/* binary op on handler */
+ 		      dcopbop:{
+@@ -1936,6 +2144,83 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ 		break;
+ 	}
+ 
++#ifdef __loongson_fp
++	case ps_fmt:{		/* 6 */
++		/* Support for Loongson paired single fp instructions */
++		union {
++			ieee754sp(*b) (ieee754sp, ieee754sp);
++			ieee754sp(*u) (ieee754sp);
++		} handler;
++
++		switch (MIPSInst_FUNC(ir)) {
++			/* binary ops */
++		case fadd_op:
++			handler.b = ieee754sp_add;
++			goto pscopbop;
++		case fsub_op:
++			handler.b = ieee754sp_sub;
++			goto pscopbop;
++		case fmul_op:
++			handler.b = ieee754sp_mul;
++			goto pscopbop;
++
++			/* unary  ops */
++		case fabs_op:
++			handler.u = ieee754sp_abs;
++			goto pscopuop;
++		case fneg_op:
++			handler.u = ieee754sp_neg;
++			goto pscopuop;
++		case fmov_op:
++			/* an easy one */
++			PSPFROMREG(rv.s, rv.s2, MIPSInst_FS(ir));
++			break;
++
++		      pscopbop: /* paired binary op handler */
++			{
++				struct _ieee754_csr ieee754_csr_save;
++				ieee754sp fs1, ft1;
++				ieee754sp fs2, ft2;
++
++				PSPFROMREG(fs1, fs2, MIPSInst_FS(ir));
++				PSPFROMREG(ft1, ft2, MIPSInst_FT(ir));
++				rv.s  = (*handler.b) (fs1, ft1);
++				ieee754_csr_save = ieee754_csr;
++				rv.s2 = (*handler.b) (fs2, ft2);
++				ieee754_csr.cx |= ieee754_csr_save.cx;
++				ieee754_csr.sx |= ieee754_csr_save.sx;
++				goto copcsr;
++			}
++		      pscopuop: /* paired unary op handler */
++			{
++				struct _ieee754_csr ieee754_csr_save;
++				ieee754sp fs1;
++				ieee754sp fs2;
++
++				PSPFROMREG(fs1, fs2, MIPSInst_FS(ir));
++				rv.s  = (*handler.u) (fs1);
++				ieee754_csr_save = ieee754_csr;
++				rv.s2 = (*handler.u) (fs2);
++				ieee754_csr.cx |= ieee754_csr_save.cx;
++				ieee754_csr.sx |= ieee754_csr_save.sx;
++				goto copcsr;
++			}
++			break;
++
++		default:
++			if (MIPSInst_FUNC(ir) >= fcmp_op) {
++				/* Loongson fp hardware handles all
++				   cases of fp compare insns, so we
++				   shouldn't have to */
++				printk ("Loongson paired-single fp compare"
++					" unimplemented in cp1emu.c\n");
++			}
++			return SIGILL;
++		}
++		break;
++	}
++#endif
++
+ 	case w_fmt:{
+ 		ieee754sp fs;
+ 
+@@ -2025,6 +2310,11 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ 		DITOREG(rv.l, MIPSInst_FD(ir));
+ 		break;
+ #endif
++#ifdef __loongson_fp
++	case ps_fmt:
++		PSPTOREG(rv.s, rv.s2, MIPSInst_FD(ir));
++		break;
++#endif
+ 	default:
+ 		return SIGILL;
+ 	}
+diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c
+index 44b6dff..e782fae 100644
+--- a/arch/mips/mm/dma-default.c
++++ b/arch/mips/mm/dma-default.c
+@@ -336,7 +336,7 @@ int mips_dma_supported(struct device *dev, u64 mask)
+ 	return plat_dma_supported(dev, mask);
+ }
+ 
+-void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
++void mips_dma_cache_sync(struct device *dev, void *vaddr, size_t size,
+ 			 enum dma_data_direction direction)
+ {
+ 	BUG_ON(direction == DMA_NONE);
+@@ -345,8 +345,6 @@ void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
+ 		__dma_sync_virtual(vaddr, size, direction);
+ }
+ 
+-EXPORT_SYMBOL(dma_cache_sync);
+-
+ static struct dma_map_ops mips_default_dma_map_ops = {
+ 	.alloc = mips_dma_alloc_coherent,
+ 	.free = mips_dma_free_coherent,
+diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile
+index 137f2a6..b9845dc 100644
+--- a/arch/mips/pci/Makefile
++++ b/arch/mips/pci/Makefile
+@@ -29,6 +29,7 @@ obj-$(CONFIG_LASAT)		+= pci-lasat.o
+ obj-$(CONFIG_MIPS_COBALT)	+= fixup-cobalt.o
+ obj-$(CONFIG_LEMOTE_FULOONG2E)	+= fixup-fuloong2e.o ops-loongson2.o
+ obj-$(CONFIG_LEMOTE_MACH2F)	+= fixup-lemote2f.o ops-loongson2.o
++obj-$(CONFIG_DEXXON_GDIUM)      += fixup-gdium.o ops-loongson2.o
+ obj-$(CONFIG_MIPS_MALTA)	+= fixup-malta.o pci-malta.o
+ obj-$(CONFIG_PMC_MSP7120_GW)	+= fixup-pmcmsp.o ops-pmcmsp.o
+ obj-$(CONFIG_PMC_MSP7120_EVAL)	+= fixup-pmcmsp.o ops-pmcmsp.o
+diff --git a/arch/mips/pci/fixup-gdium.c b/arch/mips/pci/fixup-gdium.c
+new file mode 100644
+index 0000000..b296220
+--- /dev/null
++++ b/arch/mips/pci/fixup-gdium.c
+@@ -0,0 +1,90 @@
++/*
++ * Copyright (C) 2010 yajin <yajin@vm-kernel.org>
++ *
++ *  This program is free software; you can redistribute  it and/or modify it
++ *  under  the terms of  the GNU General  Public License as published by the
++ *  Free Software Foundation;  either version 2 of the  License, or (at your
++ *  option) any later version.
++ */
++
++#include <linux/init.h>
++#include <linux/pci.h>
++
++#include <loongson.h>
++/*
++ * http://www.pcidatabase.com
++ * GDIUM has different PCI mapping
++ *  slot 13 (0x1814/0x0301) -> RaLink rt2561 Wireless-G PCI
++ *  slog 14 (0x126f/0x0501) -> sm501
++ *  slot 15 (0x1033/0x0035) -> NEC Dual OHCI controllers
++ *                             plus Single EHCI controller
++ *  slot 16 (0x10ec/0x8139) -> Realtek 8139c
++ *  slot 17 (0x1033/0x00e0) -> NEC USB 2.0 Host Controller
++ */
++
++#undef INT_IRQA
++#undef INT_IRQB
++#undef INT_IRQC
++#undef INT_IRQD
++#define INT_IRQA 36
++#define INT_IRQB 37
++#define INT_IRQC 38
++#define INT_IRQD 39
++
++int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
++{
++	int irq = 0;
++
++	switch (slot) {
++	case 13:
++		irq = INT_IRQC + ((pin - 1) & 3);
++		break;
++	case 14:
++		irq = INT_IRQA;
++		break;
++	case 15:
++#if CONFIG_GDIUM_VERSION > 2
++		irq = INT_IRQB;
++#else
++		irq = INT_IRQA + ((pin - 1) & 3);
++#endif
++		break;
++	case 16:
++		irq = INT_IRQD;
++		break;
++#if CONFIG_GDIUM_VERSION > 2
++	case 17:
++		irq = INT_IRQC;
++		break;
++#endif
++	default:
++		pr_info(" strange pci slot number %d on gdium.\n", slot);
++		break;
++	}
++	return irq;
++}
++
++/* Do platform specific device initialization at pci_enable_device() time */
++int pcibios_plat_dev_init(struct pci_dev *dev)
++{
++	return 0;
++}
++
++/* Fixups for the USB host controllers */
++static void __init gdium_usb_host_fixup(struct pci_dev *dev)
++{
++	unsigned int val;
++	pci_read_config_dword(dev, 0xe0, &val);
++#if CONFIG_GDIUM_VERSION > 2
++	pci_write_config_dword(dev, 0xe0, (val & ~3) | 0x3);
++#else
++	pci_write_config_dword(dev, 0xe0, (val & ~7) | 0x5);
++	pci_write_config_dword(dev, 0xe4, 1<<5);
++#endif
++}
++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB,
++				gdium_usb_host_fixup);
++#if CONFIG_GDIUM_VERSION > 2
++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_CT_65550,
++				gdium_usb_host_fixup);
++#endif
+diff --git a/arch/mips/power/hibernate.S b/arch/mips/power/hibernate.S
+index 32a7c82..7e0277a 100644
+--- a/arch/mips/power/hibernate.S
++++ b/arch/mips/power/hibernate.S
+@@ -43,7 +43,6 @@ LEAF(swsusp_arch_resume)
+ 	bne t1, t3, 1b
+ 	PTR_L t0, PBE_NEXT(t0)
+ 	bnez t0, 0b
+-	jal local_flush_tlb_all /* Avoid TLB mismatch after kernel resume */
+ 	PTR_LA t0, saved_regs
+ 	PTR_L ra, PT_R31(t0)
+ 	PTR_L sp, PT_R29(t0)
+diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c
+index 0448860..fa7cfab 100644
+--- a/drivers/ata/pata_cs5536.c
++++ b/drivers/ata/pata_cs5536.c
+@@ -46,8 +46,6 @@ static int use_msr;
+ module_param_named(msr, use_msr, int, 0644);
+ MODULE_PARM_DESC(msr, "Force using MSR to configure IDE function (Default: 0)");
+ #else
+-#undef rdmsr	/* avoid accidental MSR usage on, e.g. x86-64 */
+-#undef wrmsr
+ #define rdmsr(x, y, z) do { } while (0)
+ #define wrmsr(x, y, z) do { } while (0)
+ #define use_msr 0
+diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
+index f722001..5af02de 100644
+--- a/drivers/hid/Kconfig
++++ b/drivers/hid/Kconfig
+@@ -786,6 +786,13 @@ config HID_ZYDACRON
+ 	---help---
+ 	Support for Zydacron remote control.
+ 
++config HID_GDIUM
++	bool "Gdium Fn keys support" if EMBEDDED
++	depends on USB_HID && DEXXON_GDIUM
++	default !EMBEDDED
++	---help---
++	Support for Functions keys available on Gdiums.
++
+ config HID_SENSOR_HUB
+ 	tristate "HID Sensors framework support"
+ 	depends on HID
+diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
+index 30e4431..e41ca68 100644
+--- a/drivers/hid/Makefile
++++ b/drivers/hid/Makefile
+@@ -115,6 +115,7 @@ obj-$(CONFIG_HID_ZEROPLUS)	+= hid-zpff.o
+ obj-$(CONFIG_HID_ZYDACRON)	+= hid-zydacron.o
+ obj-$(CONFIG_HID_WACOM)		+= hid-wacom.o
+ obj-$(CONFIG_HID_WALTOP)	+= hid-waltop.o
++obj-$(CONFIG_HID_GDIUM)		+= hid-gdium.o
+ obj-$(CONFIG_HID_WIIMOTE)	+= hid-wiimote.o
+ obj-$(CONFIG_HID_SENSOR_HUB)	+= hid-sensor-hub.o
+ 
+diff --git a/drivers/hid/hid-gdium.c b/drivers/hid/hid-gdium.c
+new file mode 100644
+index 0000000..67cc095
+--- /dev/null
++++ b/drivers/hid/hid-gdium.c
+@@ -0,0 +1,210 @@
++/*
++ * hid-gdium  --  Gdium laptop function keys
++ *
++ * Arnaud Patard <apatard@mandriva.com>
++ *
++ * Based on hid-apple.c
++ *
++ *  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/device.h>
++#include <linux/hid.h>
++#include <linux/module.h>
++#include <linux/usb.h>
++
++#include "hid-ids.h"
++
++#define GDIUM_FN_ON	1
++
++static int fnmode = GDIUM_FN_ON;
++module_param(fnmode, int, 0644);
++MODULE_PARM_DESC(fnmode, "Mode of fn key on Gdium (0 = disabled, 1 = Enabled)");
++
++struct gdium_data {
++	unsigned int fn_on;
++};
++
++
++struct gdium_key_translation {
++	u16 from;
++	u16 to;
++};
++
++static struct gdium_key_translation gdium_fn_keys[] = {
++	{ KEY_F1,	KEY_CAMERA },
++	{ KEY_F2,	KEY_CONNECT },
++	{ KEY_F3,	KEY_MUTE },
++	{ KEY_F4,	KEY_VOLUMEUP},
++	{ KEY_F5,	KEY_VOLUMEDOWN },
++	{ KEY_F6,	KEY_SWITCHVIDEOMODE },
++	{ KEY_F7,	KEY_F19 }, /* F7+12. Have to use existant keycodes */
++	{ KEY_F8,	KEY_BRIGHTNESSUP },
++	{ KEY_F9,	KEY_BRIGHTNESSDOWN },
++	{ KEY_F10,	KEY_SLEEP },
++	{ KEY_F11,	KEY_PROG1 },
++	{ KEY_F12,	KEY_PROG2 },
++	{ KEY_UP,	KEY_PAGEUP },
++	{ KEY_DOWN,	KEY_PAGEDOWN },
++	{ KEY_INSERT,	KEY_NUMLOCK },
++	{ KEY_DELETE,	KEY_SCROLLLOCK },
++	{ KEY_T,	KEY_STOPCD },
++	{ KEY_F,	KEY_PREVIOUSSONG },
++	{ KEY_H,	KEY_NEXTSONG },
++	{ KEY_G,        KEY_PLAYPAUSE },
++	{ }
++};
++
++static struct gdium_key_translation *gdium_find_translation(
++		struct gdium_key_translation *table, u16 from)
++{
++	struct gdium_key_translation *trans;
++
++	/* Look for the translation */
++	for (trans = table; trans->from; trans++)
++		if (trans->from == from)
++			return trans;
++	return NULL;
++}
++
++static int hidinput_gdium_event(struct hid_device *hid, struct input_dev *input,
++		struct hid_usage *usage, __s32 value)
++{
++	struct gdium_data *data = hid_get_drvdata(hid);
++	struct gdium_key_translation *trans;
++	int do_translate;
++
++	if (usage->type != EV_KEY)
++		return 0;
++
++	if ((usage->code == KEY_FN)) {
++		data->fn_on = !!value;
++		input_event(input, usage->type, usage->code, value);
++		return 1;
++	}
++
++	if (fnmode) {
++		trans = gdium_find_translation(gdium_fn_keys, usage->code);
++		if (trans) {
++			do_translate = data->fn_on;
++			if (do_translate) {
++				input_event(input, usage->type, trans->to, value);
++				return 1;
++			}
++		}
++	}
++
++	return 0;
++}
++
++static int gdium_input_event(struct hid_device *hdev, struct hid_field *field,
++			struct hid_usage *usage, __s32 value)
++{
++	if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || !usage->type)
++		return 0;
++
++	if (hidinput_gdium_event(hdev, field->hidinput->input, usage, value))
++		return 1;
++
++	return 0;
++}
++
++
++static void gdium_input_setup(struct input_dev *input)
++{
++	struct gdium_key_translation *trans;
++
++	set_bit(KEY_NUMLOCK, input->keybit);
++
++	/* Enable all needed keys */
++	for (trans = gdium_fn_keys; trans->from; trans++)
++		set_bit(trans->to, input->keybit);
++}
++
++static int gdium_input_mapping(struct hid_device *hdev, struct hid_input *hi,
++		struct hid_field *field, struct hid_usage *usage,
++		unsigned long **bit, int *max)
++{
++	if (((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD)
++			&& ((usage->hid & HID_USAGE) == 0x82)) {
++		hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_FN);
++		gdium_input_setup(hi->input);
++		return 1;
++	}
++	return 0;
++}
++
++static int gdium_input_probe(struct hid_device *hdev, const struct hid_device_id *id)
++{
++	struct gdium_data *data;
++	int ret;
++
++	data = kzalloc(sizeof(*data), GFP_KERNEL);
++	if (!data) {
++		dev_err(&hdev->dev, "can't alloc gdium keyboard data\n");
++		return -ENOMEM;
++	}
++
++	hid_set_drvdata(hdev, data);
++
++	ret = hid_parse(hdev);
++	if (ret) {
++		dev_err(&hdev->dev, "parse failed\n");
++		goto err_free;
++	}
++
++	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
++	if (ret) {
++		dev_err(&hdev->dev, "hw start failed\n");
++		goto err_free;
++	}
++
++	return 0;
++err_free:
++	kfree(data);
++	return ret;
++}
++static void gdium_input_remove(struct hid_device *hdev)
++{
++	hid_hw_stop(hdev);
++	kfree(hid_get_drvdata(hdev));
++}
++
++static const struct hid_device_id gdium_input_devices[] = {
++	{ HID_USB_DEVICE(USB_VENDOR_ID_GDIUM, USB_DEVICE_ID_GDIUM) },
++	{}
++};
++MODULE_DEVICE_TABLE(hid, gdium_input_devices);
++
++static struct hid_driver gdium_input_driver = {
++	.name = "gdium-fnkeys",
++	.id_table = gdium_input_devices,
++	.probe = gdium_input_probe,
++	.remove = gdium_input_remove,
++	.event = gdium_input_event,
++	.input_mapping = gdium_input_mapping,
++};
++
++static int gdium_input_init(void)
++{
++	int ret;
++
++	ret = hid_register_driver(&gdium_input_driver);
++	if (ret)
++		 pr_err("can't register gdium keyboard driver\n");
++
++	return ret;
++}
++static void gdium_input_exit(void)
++{
++	hid_unregister_driver(&gdium_input_driver);
++}
++
++module_init(gdium_input_init);
++module_exit(gdium_input_exit);
++MODULE_LICENSE("GPL");
++
+diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
+index 91bc66b..49f06b8 100644
+--- a/drivers/hid/hid-ids.h
++++ b/drivers/hid/hid-ids.h
+@@ -961,6 +961,9 @@
+ #define USB_VENDOR_ID_ZYTRONIC		0x14c8
+ #define USB_DEVICE_ID_ZYTRONIC_ZXY100	0x0005
+ 
++#define USB_VENDOR_ID_GDIUM		0x04B4
++#define USB_DEVICE_ID_GDIUM		0xe001
++
+ #define USB_VENDOR_ID_PRIMAX	0x0461
+ #define USB_DEVICE_ID_PRIMAX_KEYBOARD	0x4e05
+ 
+diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
+index c5eec02..9e4eb1d 100644
+--- a/drivers/i2c/busses/Kconfig
++++ b/drivers/i2c/busses/Kconfig
+@@ -1002,7 +1002,7 @@ config SCx200_I2C_SDA
+ 
+ config SCx200_ACB
+ 	tristate "Geode ACCESS.bus support"
+-	depends on X86_32 && PCI
++	depends on PCI
+ 	help
+ 	  Enable the use of the ACCESS.bus controllers on the Geode SCx200 and
+ 	  SC1100 processors and the CS5535 and CS5536 Geode companion devices.
+diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
+index 376f2dc..b576801 100644
+--- a/drivers/ide/ide-iops.c
++++ b/drivers/ide/ide-iops.c
+@@ -27,6 +27,10 @@
+ #include <asm/uaccess.h>
+ #include <asm/io.h>
+ 
++#ifdef CONFIG_LEMOTE_MACH2F
++#include <asm/bootinfo.h>
++#endif
++
+ void SELECT_MASK(ide_drive_t *drive, int mask)
+ {
+ 	const struct ide_port_ops *port_ops = drive->hwif->port_ops;
+@@ -300,6 +304,11 @@ void ide_check_nien_quirk_list(ide_drive_t *drive)
+ {
+ 	const char **list, *m = (char *)&drive->id[ATA_ID_PROD];
+ 
++#ifdef CONFIG_LEMOTE_MACH2F
++	if (mips_machtype != MACH_LEMOTE_YL2F89)
++		return;
++#endif
++
+ 	for (list = nien_quirk_list; *list != NULL; list++)
+ 		if (strstr(m, *list) != NULL) {
+ 			drive->dev_flags |= IDE_DFLAG_NIEN_QUIRK;
+diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
+index e7dc441..124e8c3 100644
+--- a/drivers/mfd/sm501.c
++++ b/drivers/mfd/sm501.c
+@@ -58,7 +58,7 @@ struct sm501_gpio {
+ struct sm501_gpio {
+ 	/* no gpio support, empty definition for sm501_devdata. */
+ };
+-#endif
++#endif	/* CONFIG_MFD_SM501_GPIO */
+ 
+ struct sm501_devdata {
+ 	spinlock_t			 reg_lock;
+@@ -1135,6 +1135,22 @@ static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
+ {
+ 	return sm->gpio.registered;
+ }
++
++void sm501_configure_gpio(struct device *dev, unsigned int gpio, unsigned
++		char mode)
++{
++	unsigned long set, reg, offset = gpio;
++
++	if (offset >= 32) {
++		reg = SM501_GPIO63_32_CONTROL;
++		offset = gpio - 32;
++	} else
++		reg = SM501_GPIO31_0_CONTROL;
++
++	set = mode ? 1 << offset : 0;
++
++	sm501_modify_reg(dev, reg, set, 0);
++}
+ #else
+ static inline int sm501_register_gpio(struct sm501_devdata *sm)
+ {
+@@ -1154,7 +1170,13 @@ static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
+ {
+ 	return 0;
+ }
+-#endif
++
++void sm501_configure_gpio(struct device *dev, unsigned int gpio,
++			 unsigned char mode)
++{
++}
++#endif	/* CONFIG_MFD_SM501_GPIO */
++EXPORT_SYMBOL_GPL(sm501_configure_gpio);
+ 
+ static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm,
+ 					    struct sm501_platdata_gpio_i2c *iic)
+@@ -1209,6 +1231,20 @@ static int sm501_register_gpio_i2c(struct sm501_devdata *sm,
+ 	return 0;
+ }
+ 
++/* register sm501 PWM device */
++static int sm501_register_pwm(struct sm501_devdata *sm)
++{
++	struct platform_device *pdev;
++
++	pdev = sm501_create_subdev(sm, "sm501-pwm", 2, 0);
++	if (!pdev)
++		return -ENOMEM;
++	sm501_create_subio(sm, &pdev->resource[0], 0x10020, 0xC);
++	sm501_create_irq(sm, &pdev->resource[1]);
++
++	return sm501_register_device(sm, pdev);
++}
++
+ /* sm501_dbg_regs
+  *
+  * Debug attribute to attach to parent device to show core registers
+@@ -1367,6 +1403,8 @@ static int sm501_init_dev(struct sm501_devdata *sm)
+ 			sm501_register_uart(sm, idata->devices);
+ 		if (idata->devices & SM501_USE_GPIO)
+ 			sm501_register_gpio(sm);
++		if (idata->devices & SM501_USE_PWM)
++			sm501_register_pwm(sm);
+ 	}
+ 
+ 	if (pdata && pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) {
+@@ -1553,10 +1591,15 @@ static struct sm501_initdata sm501_pci_initdata = {
+ 	.devices	= SM501_USE_ALL,
+ 
+ 	/* Errata AB-3 says that 72MHz is the fastest available
+-	 * for 33MHZ PCI with proper bus-mastering operation */
+-
++	 * for 33MHZ PCI with proper bus-mastering operation
++	 * For gdium, it works under 84&112M clock freq.*/
++#ifdef CONFIG_DEXXON_GDIUM
++	.mclk		= 84 * MHZ,
++	.m1xclk		= 112 * MHZ,
++#else
+ 	.mclk		= 72 * MHZ,
+ 	.m1xclk		= 144 * MHZ,
++#endif
+ };
+ 
+ static struct sm501_platdata_fbsub sm501_pdata_fbsub = {
+diff --git a/drivers/net/titan_ge.c b/drivers/net/titan_ge.c
+new file mode 100644
+index 0000000..dc137bf8
+--- /dev/null
++++ b/drivers/net/titan_ge.c
+@@ -0,0 +1,2069 @@
++/*
++ * drivers/net/titan_ge.c - Driver for Titan ethernet ports
++ *
++ * Copyright (C) 2003 PMC-Sierra Inc.
++ * Author : Manish Lachwani (lachwani@pmc-sierra.com)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++
++/*
++ * The MAC unit of the Titan consists of the following:
++ *
++ * -> XDMA Engine to move data to from the memory to the MAC packet FIFO
++ * -> FIFO is where the incoming and outgoing data is placed
++ * -> TRTG is the unit that pulls the data from the FIFO for Tx and pushes
++ *    the data into the FIFO for Rx
++ * -> TMAC is the outgoing MAC interface and RMAC is the incoming.
++ * -> AFX is the address filtering block
++ * -> GMII block to communicate with the PHY
++ *
++ * Rx will look like the following:
++ * GMII --> RMAC --> AFX --> TRTG --> Rx FIFO --> XDMA --> CPU memory
++ *
++ * Tx will look like the following:
++ * CPU memory --> XDMA --> Tx FIFO --> TRTG --> TMAC --> GMII
++ *
++ * The Titan driver has support for the following performance features:
++ * -> Rx side checksumming
++ * -> Jumbo Frames
++ * -> Interrupt Coalscing
++ * -> Rx NAPI
++ * -> SKB Recycling
++ * -> Transmit/Receive descriptors in SRAM
++ * -> Fast routing for IP forwarding
++ */
++
++#include <linux/dma-mapping.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++#include <linux/errno.h>
++#include <linux/ip.h>
++#include <linux/init.h>
++#include <linux/in.h>
++#include <linux/platform_device.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#include <linux/mii.h>
++#include <linux/delay.h>
++#include <linux/skbuff.h>
++#include <linux/prefetch.h>
++
++/* For MII specifc registers, titan_mdio.h should be included */
++#include <net/ip.h>
++
++#include <asm/bitops.h>
++#include <asm/io.h>
++#include <asm/types.h>
++#include <asm/pgtable.h>
++#include <asm/system.h>
++#include <asm/titan_dep.h>
++
++#include "titan_ge.h"
++#include "titan_mdio.h"
++
++/* Static Function Declarations	 */
++static int titan_ge_eth_open(struct net_device *);
++static void titan_ge_eth_stop(struct net_device *);
++static struct net_device_stats *titan_ge_get_stats(struct net_device *);
++static int titan_ge_init_rx_desc_ring(titan_ge_port_info *, int, int,
++				      unsigned long, unsigned long,
++				      unsigned long);
++static int titan_ge_init_tx_desc_ring(titan_ge_port_info *, int,
++				      unsigned long, unsigned long);
++
++static int titan_ge_open(struct net_device *);
++static int titan_ge_start_xmit(struct sk_buff *, struct net_device *);
++static int titan_ge_stop(struct net_device *);
++
++static unsigned long titan_ge_tx_coal(unsigned long, int);
++
++static void titan_ge_port_reset(unsigned int);
++static int titan_ge_free_tx_queue(titan_ge_port_info *);
++static int titan_ge_rx_task(struct net_device *, titan_ge_port_info *);
++static int titan_ge_port_start(struct net_device *, titan_ge_port_info *);
++
++static int titan_ge_return_tx_desc(titan_ge_port_info *, int);
++
++/*
++ * Some configuration for the FIFO and the XDMA channel needs
++ * to be done only once for all the ports. This flag controls
++ * that
++ */
++static unsigned long config_done;
++
++/*
++ * One time out of memory flag
++ */
++static unsigned int oom_flag;
++
++static int titan_ge_poll(struct net_device *netdev, int *budget);
++
++static int titan_ge_receive_queue(struct net_device *, unsigned int);
++
++static struct platform_device *titan_ge_device[3];
++
++/* MAC Address */
++extern unsigned char titan_ge_mac_addr_base[6];
++
++unsigned long titan_ge_base;
++static unsigned long titan_ge_sram;
++
++static char titan_string[] = "titan";
++
++/*
++ * The Titan GE has two alignment requirements:
++ * -> skb->data to be cacheline aligned (32 byte)
++ * -> IP header alignment to 16 bytes
++ *
++ * The latter is not implemented. So, that results in an extra copy on
++ * the Rx. This is a big performance hog. For the former case, the
++ * dev_alloc_skb() has been replaced with titan_ge_alloc_skb(). The size
++ * requested is calculated:
++ *
++ * Ethernet Frame Size : 1518
++ * Ethernet Header     : 14
++ * Future Titan change for IP header alignment : 2
++ *
++ * Hence, we allocate (1518 + 14 + 2+ 64) = 1580 bytes.  For IP header
++ * alignment, we use skb_reserve().
++ */
++
++#define ALIGNED_RX_SKB_ADDR(addr) \
++	((((unsigned long)(addr) + (64UL - 1UL)) \
++	& ~(64UL - 1UL)) - (unsigned long)(addr))
++
++#define titan_ge_alloc_skb(__length, __gfp_flags) \
++({      struct sk_buff *__skb; \
++	__skb = alloc_skb((__length) + 64, (__gfp_flags)); \
++	if(__skb) { \
++		int __offset = (int) ALIGNED_RX_SKB_ADDR(__skb->data); \
++		if(__offset) \
++			skb_reserve(__skb, __offset); \
++	} \
++	__skb; \
++})
++
++/*
++ * Configure the GMII block of the Titan based on what the PHY tells us
++ */
++static void titan_ge_gmii_config(int port_num)
++{
++	unsigned int reg_data = 0, phy_reg;
++	int err;
++
++	err = titan_ge_mdio_read(port_num, TITAN_GE_MDIO_PHY_STATUS, &phy_reg);
++
++	if (err == TITAN_GE_MDIO_ERROR) {
++		printk(KERN_ERR
++		       "Could not read PHY control register 0x11 \n");
++		printk(KERN_ERR
++			"Setting speed to 1000 Mbps and Duplex to Full \n");
++
++		return;
++	}
++
++	err = titan_ge_mdio_write(port_num, TITAN_GE_MDIO_PHY_IE, 0);
++
++	if (phy_reg & 0x8000) {
++		if (phy_reg & 0x2000) {
++			/* Full Duplex and 1000 Mbps */
++			TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_MODE +
++					(port_num << 12)), 0x201);
++		}  else {
++			/* Half Duplex and 1000 Mbps */
++			TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_MODE +
++					(port_num << 12)), 0x2201);
++			}
++	}
++	if (phy_reg & 0x4000) {
++		if (phy_reg & 0x2000) {
++			/* Full Duplex and 100 Mbps */
++			TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_MODE +
++					(port_num << 12)), 0x100);
++		} else {
++			/* Half Duplex and 100 Mbps */
++			TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_MODE +
++					(port_num << 12)), 0x2100);
++		}
++	}
++	reg_data = TITAN_GE_READ(TITAN_GE_GMII_CONFIG_GENERAL +
++				(port_num << 12));
++	reg_data |= 0x3;
++	TITAN_GE_WRITE((TITAN_GE_GMII_CONFIG_GENERAL +
++			(port_num << 12)), reg_data);
++}
++
++/*
++ * Enable the TMAC if it is not
++ */
++static void titan_ge_enable_tx(unsigned int port_num)
++{
++	unsigned long reg_data;
++
++	reg_data = TITAN_GE_READ(TITAN_GE_TMAC_CONFIG_1 + (port_num << 12));
++	if (!(reg_data & 0x8000)) {
++		printk("TMAC disabled for port %d!! \n", port_num);
++
++		reg_data |= 0x0001;	/* Enable TMAC */
++		reg_data |= 0x4000;	/* CRC Check Enable */
++		reg_data |= 0x2000;	/* Padding enable */
++		reg_data |= 0x0800;	/* CRC Add enable */
++		reg_data |= 0x0080;	/* PAUSE frame */
++
++		TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_1 +
++				(port_num << 12)), reg_data);
++	}
++}
++
++/*
++ * Tx Timeout function
++ */
++static void titan_ge_tx_timeout(struct net_device *netdev)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++
++	printk(KERN_INFO "%s: TX timeout  ", netdev->name);
++	printk(KERN_INFO "Resetting card \n");
++
++	/* Do the reset outside of interrupt context */
++	schedule_work(&titan_ge_eth->tx_timeout_task);
++}
++
++/*
++ * Update the AFX tables for UC and MC for slice 0 only
++ */
++static void titan_ge_update_afx(titan_ge_port_info * titan_ge_eth)
++{
++	int port = titan_ge_eth->port_num;
++	unsigned int i;
++	volatile unsigned long reg_data = 0;
++	u8 p_addr[6];
++
++	memcpy(p_addr, titan_ge_eth->port_mac_addr, 6);
++
++	/* Set the MAC address here for TMAC and RMAC */
++	TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_HI + (port << 12)),
++		       ((p_addr[5] << 8) | p_addr[4]));
++	TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_MID + (port << 12)),
++		       ((p_addr[3] << 8) | p_addr[2]));
++	TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_LOW + (port << 12)),
++		       ((p_addr[1] << 8) | p_addr[0]));
++
++	TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_HI + (port << 12)),
++		       ((p_addr[5] << 8) | p_addr[4]));
++	TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_MID + (port << 12)),
++		       ((p_addr[3] << 8) | p_addr[2]));
++	TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_LOW + (port << 12)),
++		       ((p_addr[1] << 8) | p_addr[0]));
++
++	TITAN_GE_WRITE((0x112c | (port << 12)), 0x1);
++	/* Configure the eight address filters */
++	for (i = 0; i < 8; i++) {
++		/* Select each of the eight filters */
++		TITAN_GE_WRITE((TITAN_GE_AFX_ADDRS_FILTER_CTRL_2 +
++				(port << 12)), i);
++
++		/* Configure the match */
++		reg_data = 0x9;	/* Forward Enable Bit */
++		TITAN_GE_WRITE((TITAN_GE_AFX_ADDRS_FILTER_CTRL_0 +
++				(port << 12)), reg_data);
++
++		/* Finally, AFX Exact Match Address Registers */
++		TITAN_GE_WRITE((TITAN_GE_AFX_EXACT_MATCH_LOW + (port << 12)),
++			       ((p_addr[1] << 8) | p_addr[0]));
++		TITAN_GE_WRITE((TITAN_GE_AFX_EXACT_MATCH_MID + (port << 12)),
++			       ((p_addr[3] << 8) | p_addr[2]));
++		TITAN_GE_WRITE((TITAN_GE_AFX_EXACT_MATCH_HIGH + (port << 12)),
++			       ((p_addr[5] << 8) | p_addr[4]));
++
++		/* VLAN id set to 0 */
++		TITAN_GE_WRITE((TITAN_GE_AFX_EXACT_MATCH_VID +
++				(port << 12)), 0);
++	}
++}
++
++/*
++ * Actual Routine to reset the adapter when the timeout occurred
++ */
++static void titan_ge_tx_timeout_task(struct net_device *netdev)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++	int port = titan_ge_eth->port_num;
++
++	printk("Titan GE: Transmit timed out. Resetting ... \n");
++
++	/* Dump debug info */
++	printk(KERN_ERR "TRTG cause : %x \n",
++			TITAN_GE_READ(0x100c + (port << 12)));
++
++	/* Fix this for the other ports */
++	printk(KERN_ERR "FIFO cause : %x \n", TITAN_GE_READ(0x482c));
++	printk(KERN_ERR "IE cause : %x \n", TITAN_GE_READ(0x0040));
++	printk(KERN_ERR "XDMA GDI ERROR : %x \n",
++			TITAN_GE_READ(0x5008 + (port << 8)));
++	printk(KERN_ERR "CHANNEL ERROR: %x \n",
++			TITAN_GE_READ(TITAN_GE_CHANNEL0_INTERRUPT
++						+ (port << 8)));
++
++	netif_device_detach(netdev);
++	titan_ge_port_reset(titan_ge_eth->port_num);
++	titan_ge_port_start(netdev, titan_ge_eth);
++	netif_device_attach(netdev);
++}
++
++/*
++ * Change the MTU of the Ethernet Device
++ */
++static int titan_ge_change_mtu(struct net_device *netdev, int new_mtu)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++	unsigned long flags;
++
++	if ((new_mtu > 9500) || (new_mtu < 64))
++		return -EINVAL;
++
++	spin_lock_irqsave(&titan_ge_eth->lock, flags);
++
++	netdev->mtu = new_mtu;
++
++	/* Now we have to reopen the interface so that SKBs with the new
++	 * size will be allocated */
++
++	if (netif_running(netdev)) {
++		titan_ge_eth_stop(netdev);
++
++		if (titan_ge_eth_open(netdev) != TITAN_OK) {
++			printk(KERN_ERR
++			       "%s: Fatal error on opening device\n",
++			       netdev->name);
++			spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
++			return -1;
++		}
++	}
++
++	spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
++	return 0;
++}
++
++/*
++ * Titan Gbe Interrupt Handler. All the three ports send interrupt to one line
++ * only. Once an interrupt is triggered, figure out the port and then check
++ * the channel.
++ */
++static irqreturn_t titan_ge_int_handler(int irq, void *dev_id)
++{
++	struct net_device *netdev = (struct net_device *) dev_id;
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++	unsigned int port_num = titan_ge_eth->port_num;
++	unsigned int reg_data;
++	unsigned int eth_int_cause_error = 0, is;
++	unsigned long eth_int_cause1;
++	int err = 0;
++#ifdef CONFIG_SMP
++	unsigned long eth_int_cause2;
++#endif
++
++	/* Ack the CPU interrupt */
++	switch (port_num) {
++	case 0:
++		is = OCD_READ(RM9000x2_OCD_INTP0STATUS1);
++		OCD_WRITE(RM9000x2_OCD_INTP0CLEAR1, is);
++
++#ifdef CONFIG_SMP
++		is = OCD_READ(RM9000x2_OCD_INTP1STATUS1);
++		OCD_WRITE(RM9000x2_OCD_INTP1CLEAR1, is);
++#endif
++		break;
++
++	case 1:
++		is = OCD_READ(RM9000x2_OCD_INTP0STATUS0);
++		OCD_WRITE(RM9000x2_OCD_INTP0CLEAR0, is);
++
++#ifdef CONFIG_SMP
++		is = OCD_READ(RM9000x2_OCD_INTP1STATUS0);
++		OCD_WRITE(RM9000x2_OCD_INTP1CLEAR0, is);
++#endif
++		break;
++
++	case 2:
++		is = OCD_READ(RM9000x2_OCD_INTP0STATUS4);
++		OCD_WRITE(RM9000x2_OCD_INTP0CLEAR4, is);
++
++#ifdef CONFIG_SMP
++		is = OCD_READ(RM9000x2_OCD_INTP1STATUS4);
++		OCD_WRITE(RM9000x2_OCD_INTP1CLEAR4, is);
++#endif
++	}
++
++	eth_int_cause1 = TITAN_GE_READ(TITAN_GE_INTR_XDMA_CORE_A);
++#ifdef CONFIG_SMP
++	eth_int_cause2 = TITAN_GE_READ(TITAN_GE_INTR_XDMA_CORE_B);
++#endif
++
++	/* Spurious interrupt */
++#ifdef CONFIG_SMP
++	if ( (eth_int_cause1 == 0) && (eth_int_cause2 == 0)) {
++#else
++	if (eth_int_cause1 == 0) {
++#endif
++		eth_int_cause_error = TITAN_GE_READ(TITAN_GE_CHANNEL0_INTERRUPT +
++					(port_num << 8));
++
++		if (eth_int_cause_error == 0)
++			return IRQ_NONE;
++	}
++
++	/* Handle Tx first. No need to ack interrupts */
++#ifdef CONFIG_SMP
++	if ( (eth_int_cause1 & 0x20202) ||
++		(eth_int_cause2 & 0x20202) )
++#else
++	if (eth_int_cause1 & 0x20202)
++#endif
++		titan_ge_free_tx_queue(titan_ge_eth);
++
++	/* Handle the Rx next */
++#ifdef CONFIG_SMP
++	if ( (eth_int_cause1 & 0x10101) ||
++		(eth_int_cause2 & 0x10101)) {
++#else
++	if (eth_int_cause1 & 0x10101) {
++#endif
++		if (netif_rx_schedule_prep(netdev)) {
++			unsigned int ack;
++
++			ack = TITAN_GE_READ(TITAN_GE_INTR_XDMA_IE);
++			/* Disable Tx and Rx both */
++			if (port_num == 0)
++				ack &= ~(0x3);
++			if (port_num == 1)
++				ack &= ~(0x300);
++
++			if (port_num == 2)
++				ack &= ~(0x30000);
++
++			/* Interrupts have been disabled */
++			TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_IE, ack);
++
++			__netif_rx_schedule(netdev);
++		}
++	}
++
++	/* Handle error interrupts */
++	if (eth_int_cause_error && (eth_int_cause_error != 0x2)) {
++		printk(KERN_ERR
++			"XDMA Channel Error : %x  on port %d\n",
++			eth_int_cause_error, port_num);
++
++		printk(KERN_ERR
++			"XDMA GDI Hardware error : %x  on port %d\n",
++			TITAN_GE_READ(0x5008 + (port_num << 8)), port_num);
++
++		printk(KERN_ERR
++			"XDMA currently has %d Rx descriptors \n",
++			TITAN_GE_READ(0x5048 + (port_num << 8)));
++
++		printk(KERN_ERR
++			"XDMA currently has prefetcted %d Rx descriptors \n",
++			TITAN_GE_READ(0x505c + (port_num << 8)));
++
++		TITAN_GE_WRITE((TITAN_GE_CHANNEL0_INTERRUPT +
++			       (port_num << 8)), eth_int_cause_error);
++	}
++
++	/*
++	 * PHY interrupt to inform abt the changes. Reading the
++	 * PHY Status register will clear the interrupt
++	 */
++	if ((!(eth_int_cause1 & 0x30303)) &&
++		(eth_int_cause_error == 0)) {
++		err =
++		    titan_ge_mdio_read(port_num,
++			       TITAN_GE_MDIO_PHY_IS, &reg_data);
++
++		if (reg_data & 0x0400) {
++			/* Link status change */
++			titan_ge_mdio_read(port_num,
++				   TITAN_GE_MDIO_PHY_STATUS, &reg_data);
++			if (!(reg_data & 0x0400)) {
++				/* Link is down */
++				netif_carrier_off(netdev);
++				netif_stop_queue(netdev);
++			} else {
++				/* Link is up */
++				netif_carrier_on(netdev);
++				netif_wake_queue(netdev);
++
++				/* Enable the queue */
++				titan_ge_enable_tx(port_num);
++			}
++		}
++	}
++
++	return IRQ_HANDLED;
++}
++
++/*
++ * Multicast and Promiscuous mode set. The
++ * set_multi entry point is called whenever the
++ * multicast address list or the network interface
++ * flags are updated.
++ */
++static void titan_ge_set_multi(struct net_device *netdev)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++	unsigned int port_num = titan_ge_eth->port_num;
++	unsigned long reg_data;
++
++	reg_data = TITAN_GE_READ(TITAN_GE_AFX_ADDRS_FILTER_CTRL_1 +
++				(port_num << 12));
++
++	if (netdev->flags & IFF_PROMISC) {
++		reg_data |= 0x2;
++	}
++	else if (netdev->flags & IFF_ALLMULTI) {
++		reg_data |= 0x01;
++		reg_data |= 0x400; /* Use the 64-bit Multicast Hash bin */
++	}
++	else {
++		reg_data = 0x2;
++	}
++
++	TITAN_GE_WRITE((TITAN_GE_AFX_ADDRS_FILTER_CTRL_1 +
++			(port_num << 12)), reg_data);
++	if (reg_data & 0x01) {
++		TITAN_GE_WRITE((TITAN_GE_AFX_MULTICAST_HASH_LOW +
++				(port_num << 12)), 0xffff);
++		TITAN_GE_WRITE((TITAN_GE_AFX_MULTICAST_HASH_MIDLOW +
++				(port_num << 12)), 0xffff);
++		TITAN_GE_WRITE((TITAN_GE_AFX_MULTICAST_HASH_MIDHI +
++				(port_num << 12)), 0xffff);
++		TITAN_GE_WRITE((TITAN_GE_AFX_MULTICAST_HASH_HI +
++				(port_num << 12)), 0xffff);
++	}
++}
++
++/*
++ * Open the network device
++ */
++static int titan_ge_open(struct net_device *netdev)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++	unsigned int port_num = titan_ge_eth->port_num;
++	unsigned int irq = TITAN_ETH_PORT_IRQ - port_num;
++	int retval;
++
++	retval = request_irq(irq, titan_ge_int_handler,
++		     SA_INTERRUPT | SA_SAMPLE_RANDOM , netdev->name, netdev);
++
++	if (retval != 0) {
++		printk(KERN_ERR "Cannot assign IRQ number to TITAN GE \n");
++		return -1;
++	}
++
++	netdev->irq = irq;
++	printk(KERN_INFO "Assigned IRQ %d to port %d\n", irq, port_num);
++
++	spin_lock_irq(&(titan_ge_eth->lock));
++
++	if (titan_ge_eth_open(netdev) != TITAN_OK) {
++		spin_unlock_irq(&(titan_ge_eth->lock));
++		printk("%s: Error opening interface \n", netdev->name);
++		free_irq(netdev->irq, netdev);
++		return -EBUSY;
++	}
++
++	spin_unlock_irq(&(titan_ge_eth->lock));
++
++	return 0;
++}
++
++/*
++ * Allocate the SKBs for the Rx ring. Also used
++ * for refilling the queue
++ */
++static int titan_ge_rx_task(struct net_device *netdev,
++				titan_ge_port_info *titan_ge_port)
++{
++	struct device *device = &titan_ge_device[titan_ge_port->port_num]->dev;
++	volatile titan_ge_rx_desc *rx_desc;
++	struct sk_buff *skb;
++	int rx_used_desc;
++	int count = 0;
++
++	while (titan_ge_port->rx_ring_skbs < titan_ge_port->rx_ring_size) {
++
++	/* First try to get the skb from the recycler */
++#ifdef TITAN_GE_JUMBO_FRAMES
++		skb = titan_ge_alloc_skb(TITAN_GE_JUMBO_BUFSIZE, GFP_ATOMIC);
++#else
++		skb = titan_ge_alloc_skb(TITAN_GE_STD_BUFSIZE, GFP_ATOMIC);
++#endif
++		if (unlikely(!skb)) {
++			/* OOM, set the flag */
++			printk("OOM \n");
++			oom_flag = 1;
++			break;
++		}
++		count++;
++		skb->dev = netdev;
++
++		titan_ge_port->rx_ring_skbs++;
++
++		rx_used_desc = titan_ge_port->rx_used_desc_q;
++		rx_desc = &(titan_ge_port->rx_desc_area[rx_used_desc]);
++
++#ifdef TITAN_GE_JUMBO_FRAMES
++		rx_desc->buffer_addr = dma_map_single(device, skb->data,
++				TITAN_GE_JUMBO_BUFSIZE - 2, DMA_FROM_DEVICE);
++#else
++		rx_desc->buffer_addr = dma_map_single(device, skb->data,
++				TITAN_GE_STD_BUFSIZE - 2, DMA_FROM_DEVICE);
++#endif
++
++		titan_ge_port->rx_skb[rx_used_desc] = skb;
++		rx_desc->cmd_sts = TITAN_GE_RX_BUFFER_OWNED;
++
++		titan_ge_port->rx_used_desc_q =
++			(rx_used_desc + 1) % TITAN_GE_RX_QUEUE;
++	}
++
++	return count;
++}
++
++/*
++ * Actual init of the Tital GE port. There is one register for
++ * the channel configuration
++ */
++static void titan_port_init(struct net_device *netdev,
++			    titan_ge_port_info * titan_ge_eth)
++{
++	unsigned long reg_data;
++
++	titan_ge_port_reset(titan_ge_eth->port_num);
++
++	/* First reset the TMAC */
++	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG);
++	reg_data |= 0x80000000;
++	TITAN_GE_WRITE(TITAN_GE_CHANNEL0_CONFIG, reg_data);
++
++	udelay(30);
++
++	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG);
++	reg_data &= ~(0xc0000000);
++	TITAN_GE_WRITE(TITAN_GE_CHANNEL0_CONFIG, reg_data);
++
++	/* Now reset the RMAC */
++	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG);
++	reg_data |= 0x00080000;
++	TITAN_GE_WRITE(TITAN_GE_CHANNEL0_CONFIG, reg_data);
++
++	udelay(30);
++
++	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG);
++	reg_data &= ~(0x000c0000);
++	TITAN_GE_WRITE(TITAN_GE_CHANNEL0_CONFIG, reg_data);
++}
++
++/*
++ * Start the port. All the hardware specific configuration
++ * for the XDMA, Tx FIFO, Rx FIFO, TMAC, RMAC, TRTG and AFX
++ * go here
++ */
++static int titan_ge_port_start(struct net_device *netdev,
++				titan_ge_port_info * titan_port)
++{
++	volatile unsigned long reg_data, reg_data1;
++	int port_num = titan_port->port_num;
++	int count = 0;
++	unsigned long reg_data_1;
++
++	if (config_done == 0) {
++		reg_data = TITAN_GE_READ(0x0004);
++		reg_data |= 0x100;
++		TITAN_GE_WRITE(0x0004, reg_data);
++
++		reg_data &= ~(0x100);
++		TITAN_GE_WRITE(0x0004, reg_data);
++
++		/* Turn on GMII/MII mode and turn off TBI mode */
++		reg_data = TITAN_GE_READ(TITAN_GE_TSB_CTRL_1);
++		reg_data |= 0x00000700;
++		reg_data &= ~(0x00800000); /* Fencing */
++
++		TITAN_GE_WRITE(0x000c, 0x00001100);
++
++		TITAN_GE_WRITE(TITAN_GE_TSB_CTRL_1, reg_data);
++
++		/* Set the CPU Resource Limit register */
++		TITAN_GE_WRITE(0x00f8, 0x8);
++
++		/* Be conservative when using the BIU buffers */
++		TITAN_GE_WRITE(0x0068, 0x4);
++	}
++
++	titan_port->tx_threshold = 0;
++	titan_port->rx_threshold = 0;
++
++	/* We need to write the descriptors for Tx and Rx */
++	TITAN_GE_WRITE((TITAN_GE_CHANNEL0_TX_DESC + (port_num << 8)),
++		       (unsigned long) titan_port->tx_dma);
++	TITAN_GE_WRITE((TITAN_GE_CHANNEL0_RX_DESC + (port_num << 8)),
++		       (unsigned long) titan_port->rx_dma);
++
++	if (config_done == 0) {
++		/* Step 1:  XDMA config	*/
++		reg_data = TITAN_GE_READ(TITAN_GE_XDMA_CONFIG);
++		reg_data &= ~(0x80000000);      /* clear reset */
++		reg_data |= 0x1 << 29;	/* sparse tx descriptor spacing */
++		reg_data |= 0x1 << 28;	/* sparse rx descriptor spacing */
++		reg_data |= (0x1 << 23) | (0x1 << 24);  /* Descriptor Coherency */
++		reg_data |= (0x1 << 21) | (0x1 << 22);  /* Data Coherency */
++		TITAN_GE_WRITE(TITAN_GE_XDMA_CONFIG, reg_data);
++	}
++
++	/* IR register for the XDMA */
++	reg_data = TITAN_GE_READ(TITAN_GE_GDI_INTERRUPT_ENABLE + (port_num << 8));
++	reg_data |= 0x80068000; /* No Rx_OOD */
++	TITAN_GE_WRITE((TITAN_GE_GDI_INTERRUPT_ENABLE + (port_num << 8)), reg_data);
++
++	/* Start the Tx and Rx XDMA controller */
++	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG + (port_num << 8));
++	reg_data &= 0x4fffffff;     /* Clear tx reset */
++	reg_data &= 0xfff4ffff;     /* Clear rx reset */
++
++#ifdef TITAN_GE_JUMBO_FRAMES
++	reg_data |= 0xa0 | 0x30030000;
++#else
++	reg_data |= 0x40 | 0x20030000;
++#endif
++
++#ifndef CONFIG_SMP
++	reg_data &= ~(0x10);
++	reg_data |= 0x0f; /* All of the packet */
++#endif
++
++	TITAN_GE_WRITE((TITAN_GE_CHANNEL0_CONFIG + (port_num << 8)), reg_data);
++
++	/* Rx desc count */
++	count = titan_ge_rx_task(netdev, titan_port);
++	TITAN_GE_WRITE((0x5048 + (port_num << 8)), count);
++	count = TITAN_GE_READ(0x5048 + (port_num << 8));
++
++	udelay(30);
++
++	/*
++	 * Step 2:  Configure the SDQPF, i.e. FIFO
++	 */
++	if (config_done == 0) {
++		reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_RXFIFO_CTL);
++		reg_data = 0x1;
++		TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_CTL, reg_data);
++		reg_data &= ~(0x1);
++		TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_CTL, reg_data);
++		reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_RXFIFO_CTL);
++		TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_CTL, reg_data);
++
++		reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_TXFIFO_CTL);
++		reg_data = 0x1;
++		TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_CTL, reg_data);
++		reg_data &= ~(0x1);
++		TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_CTL, reg_data);
++		reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_TXFIFO_CTL);
++		TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_CTL, reg_data);
++	}
++	/*
++	 * Enable RX FIFO 0, 4 and 8
++	 */
++	if (port_num == 0) {
++		reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_RXFIFO_0);
++
++		reg_data |= 0x100000;
++		reg_data |= (0xff << 10);
++
++		TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_0, reg_data);
++		/*
++		 * BAV2,BAV and DAV settings for the Rx FIFO
++		 */
++		reg_data1 = TITAN_GE_READ(0x4844);
++		reg_data1 |= ( (0x10 << 20) | (0x10 << 10) | 0x1);
++		TITAN_GE_WRITE(0x4844, reg_data1);
++
++		reg_data &= ~(0x00100000);
++		reg_data |= 0x200000;
++
++		TITAN_GE_WRITE(TITAN_GE_SDQPF_RXFIFO_0, reg_data);
++
++		reg_data = TITAN_GE_READ(TITAN_GE_SDQPF_TXFIFO_0);
++		reg_data |= 0x100000;
++
++		TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_0, reg_data);
++
++		reg_data |= (0xff << 10);
++
++		TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_0, reg_data);
++
++		/*
++		 * BAV2, BAV and DAV settings for the Tx FIFO
++		 */
++		reg_data1 = TITAN_GE_READ(0x4944);
++		reg_data1 = ( (0x1 << 20) | (0x1 << 10) | 0x10);
++
++		TITAN_GE_WRITE(0x4944, reg_data1);
++
++		reg_data &= ~(0x00100000);
++		reg_data |= 0x200000;
++
++		TITAN_GE_WRITE(TITAN_GE_SDQPF_TXFIFO_0, reg_data);
++
++	}
++
++	if (port_num == 1) {
++		reg_data = TITAN_GE_READ(0x4870);
++
++		reg_data |= 0x100000;
++		reg_data |= (0xff << 10) | (0xff + 1);
++
++		TITAN_GE_WRITE(0x4870, reg_data);
++		/*
++		 * BAV2,BAV and DAV settings for the Rx FIFO
++		 */
++		reg_data1 = TITAN_GE_READ(0x4874);
++		reg_data1 |= ( (0x10 << 20) | (0x10 << 10) | 0x1);
++		TITAN_GE_WRITE(0x4874, reg_data1);
++
++		reg_data &= ~(0x00100000);
++		reg_data |= 0x200000;
++
++		TITAN_GE_WRITE(0x4870, reg_data);
++
++		reg_data = TITAN_GE_READ(0x494c);
++		reg_data |= 0x100000;
++
++		TITAN_GE_WRITE(0x494c, reg_data);
++		reg_data |= (0xff << 10) | (0xff + 1);
++		TITAN_GE_WRITE(0x494c, reg_data);
++
++		/*
++		 * BAV2, BAV and DAV settings for the Tx FIFO
++		 */
++		reg_data1 = TITAN_GE_READ(0x4950);
++		reg_data1 = ( (0x1 << 20) | (0x1 << 10) | 0x10);
++
++		TITAN_GE_WRITE(0x4950, reg_data1);
++
++		reg_data &= ~(0x00100000);
++		reg_data |= 0x200000;
++
++		TITAN_GE_WRITE(0x494c, reg_data);
++	}
++
++	/*
++	 * Titan 1.2 revision does support port #2
++	 */
++	if (port_num == 2) {
++		/*
++		 * Put the descriptors in the SRAM
++		 */
++		reg_data = TITAN_GE_READ(0x48a0);
++
++		reg_data |= 0x100000;
++		reg_data |= (0xff << 10) | (2*(0xff + 1));
++
++		TITAN_GE_WRITE(0x48a0, reg_data);
++		/*
++		 * BAV2,BAV and DAV settings for the Rx FIFO
++		 */
++		reg_data1 = TITAN_GE_READ(0x48a4);
++		reg_data1 |= ( (0x10 << 20) | (0x10 << 10) | 0x1);
++		TITAN_GE_WRITE(0x48a4, reg_data1);
++
++		reg_data &= ~(0x00100000);
++		reg_data |= 0x200000;
++
++		TITAN_GE_WRITE(0x48a0, reg_data);
++
++		reg_data = TITAN_GE_READ(0x4958);
++		reg_data |= 0x100000;
++
++		TITAN_GE_WRITE(0x4958, reg_data);
++		reg_data |= (0xff << 10) | (2*(0xff + 1));
++		TITAN_GE_WRITE(0x4958, reg_data);
++
++		/*
++		 * BAV2, BAV and DAV settings for the Tx FIFO
++		 */
++		reg_data1 = TITAN_GE_READ(0x495c);
++		reg_data1 = ( (0x1 << 20) | (0x1 << 10) | 0x10);
++
++		TITAN_GE_WRITE(0x495c, reg_data1);
++
++		reg_data &= ~(0x00100000);
++		reg_data |= 0x200000;
++
++		TITAN_GE_WRITE(0x4958, reg_data);
++	}
++
++	if (port_num == 2) {
++		reg_data = TITAN_GE_READ(0x48a0);
++
++		reg_data |= 0x100000;
++		reg_data |= (0xff << 10) | (2*(0xff + 1));
++
++		TITAN_GE_WRITE(0x48a0, reg_data);
++		/*
++		 * BAV2,BAV and DAV settings for the Rx FIFO
++		 */
++		reg_data1 = TITAN_GE_READ(0x48a4);
++		reg_data1 |= ( (0x10 << 20) | (0x10 << 10) | 0x1);
++		TITAN_GE_WRITE(0x48a4, reg_data1);
++
++		reg_data &= ~(0x00100000);
++		reg_data |= 0x200000;
++
++		TITAN_GE_WRITE(0x48a0, reg_data);
++
++		reg_data = TITAN_GE_READ(0x4958);
++		reg_data |= 0x100000;
++
++		TITAN_GE_WRITE(0x4958, reg_data);
++		reg_data |= (0xff << 10) | (2*(0xff + 1));
++		TITAN_GE_WRITE(0x4958, reg_data);
++
++		/*
++		 * BAV2, BAV and DAV settings for the Tx FIFO
++		 */
++		reg_data1 = TITAN_GE_READ(0x495c);
++		reg_data1 = ( (0x1 << 20) | (0x1 << 10) | 0x10);
++
++		TITAN_GE_WRITE(0x495c, reg_data1);
++
++		reg_data &= ~(0x00100000);
++		reg_data |= 0x200000;
++
++		TITAN_GE_WRITE(0x4958, reg_data);
++	}
++
++	/*
++	 * Step 3:  TRTG block enable
++	 */
++	reg_data = TITAN_GE_READ(TITAN_GE_TRTG_CONFIG + (port_num << 12));
++
++	/*
++	 * This is the 1.2 revision of the chip. It has fix for the
++	 * IP header alignment. Now, the IP header begins at an
++	 * aligned address and this wont need an extra copy in the
++	 * driver. This performance drawback existed in the previous
++	 * versions of the silicon
++	 */
++	reg_data_1 = TITAN_GE_READ(0x103c + (port_num << 12));
++	reg_data_1 |= 0x40000000;
++	TITAN_GE_WRITE((0x103c + (port_num << 12)), reg_data_1);
++
++	reg_data_1 |= 0x04000000;
++	TITAN_GE_WRITE((0x103c + (port_num << 12)), reg_data_1);
++
++	mdelay(5);
++
++	reg_data_1 &= ~(0x04000000);
++	TITAN_GE_WRITE((0x103c + (port_num << 12)), reg_data_1);
++
++	mdelay(5);
++
++	reg_data |= 0x0001;
++	TITAN_GE_WRITE((TITAN_GE_TRTG_CONFIG + (port_num << 12)), reg_data);
++
++	/*
++	 * Step 4:  Start the Tx activity
++	 */
++	TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_2 + (port_num << 12)), 0xe197);
++#ifdef TITAN_GE_JUMBO_FRAMES
++	TITAN_GE_WRITE((0x1258 + (port_num << 12)), 0x4000);
++#endif
++	reg_data = TITAN_GE_READ(TITAN_GE_TMAC_CONFIG_1 + (port_num << 12));
++	reg_data |= 0x0001;	/* Enable TMAC */
++	reg_data |= 0x6c70;	/* PAUSE also set */
++
++	TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_1 + (port_num << 12)), reg_data);
++
++	udelay(30);
++
++	/* Destination Address drop bit */
++	reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_2 + (port_num << 12));
++	reg_data |= 0x218;        /* DA_DROP bit and pause */
++	TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_2 + (port_num << 12)), reg_data);
++
++	TITAN_GE_WRITE((0x1218 + (port_num << 12)), 0x3);
++
++#ifdef TITAN_GE_JUMBO_FRAMES
++	TITAN_GE_WRITE((0x1208 + (port_num << 12)), 0x4000);
++#endif
++	/* Start the Rx activity */
++	reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_1 + (port_num << 12));
++	reg_data |= 0x0001;	/* RMAC Enable */
++	reg_data |= 0x0010;	/* CRC Check enable */
++	reg_data |= 0x0040;	/* Min Frame check enable */
++	reg_data |= 0x4400;	/* Max Frame check enable */
++
++	TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_1 + (port_num << 12)), reg_data);
++
++	udelay(30);
++
++	/*
++	 * Enable the Interrupts for Tx and Rx
++	 */
++	reg_data1 = TITAN_GE_READ(TITAN_GE_INTR_XDMA_IE);
++
++	if (port_num == 0) {
++		reg_data1 |= 0x3;
++#ifdef CONFIG_SMP
++		TITAN_GE_WRITE(0x0038, 0x003);
++#else
++		TITAN_GE_WRITE(0x0038, 0x303);
++#endif
++	}
++
++	if (port_num == 1) {
++		reg_data1 |= 0x300;
++	}
++
++	if (port_num == 2)
++		reg_data1 |= 0x30000;
++
++	TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_IE, reg_data1);
++	TITAN_GE_WRITE(0x003c, 0x300);
++
++	if (config_done == 0) {
++		TITAN_GE_WRITE(0x0024, 0x04000024);	/* IRQ vector */
++		TITAN_GE_WRITE(0x0020, 0x000fb000);	/* INTMSG base */
++	}
++
++	/* Priority */
++	reg_data = TITAN_GE_READ(0x1038 + (port_num << 12));
++	reg_data &= ~(0x00f00000);
++	TITAN_GE_WRITE((0x1038 + (port_num << 12)), reg_data);
++
++	/* Step 5:  GMII config */
++	titan_ge_gmii_config(port_num);
++
++	if (config_done == 0) {
++		TITAN_GE_WRITE(0x1a80, 0);
++		config_done = 1;
++	}
++
++	return TITAN_OK;
++}
++
++/*
++ * Function to queue the packet for the Ethernet device
++ */
++static void titan_ge_tx_queue(titan_ge_port_info * titan_ge_eth,
++				struct sk_buff * skb)
++{
++	struct device *device = &titan_ge_device[titan_ge_eth->port_num]->dev;
++	unsigned int curr_desc = titan_ge_eth->tx_curr_desc_q;
++	volatile titan_ge_tx_desc *tx_curr;
++	int port_num = titan_ge_eth->port_num;
++
++	tx_curr = &(titan_ge_eth->tx_desc_area[curr_desc]);
++	tx_curr->buffer_addr =
++		dma_map_single(device, skb->data, skb_headlen(skb),
++			       DMA_TO_DEVICE);
++
++	titan_ge_eth->tx_skb[curr_desc] = (struct sk_buff *) skb;
++	tx_curr->buffer_len = skb_headlen(skb);
++
++	/* Last descriptor enables interrupt and changes ownership */
++	tx_curr->cmd_sts = 0x1 | (1 << 15) | (1 << 5);
++
++	/* Kick the XDMA to start the transfer from memory to the FIFO */
++	TITAN_GE_WRITE((0x5044 + (port_num << 8)), 0x1);
++
++	/* Current descriptor updated */
++	titan_ge_eth->tx_curr_desc_q = (curr_desc + 1) % TITAN_GE_TX_QUEUE;
++
++	/* Prefetch the next descriptor */
++	prefetch((const void *)
++		 &titan_ge_eth->tx_desc_area[titan_ge_eth->tx_curr_desc_q]);
++}
++
++/*
++ * Actually does the open of the Ethernet device
++ */
++static int titan_ge_eth_open(struct net_device *netdev)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++	unsigned int port_num = titan_ge_eth->port_num;
++	struct device *device = &titan_ge_device[port_num]->dev;
++	unsigned long reg_data;
++	unsigned int phy_reg;
++	int err = 0;
++
++	/* Stop the Rx activity */
++	reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_1 + (port_num << 12));
++	reg_data &= ~(0x00000001);
++	TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_1 + (port_num << 12)), reg_data);
++
++	/* Clear the port interrupts */
++	TITAN_GE_WRITE((TITAN_GE_CHANNEL0_INTERRUPT + (port_num << 8)), 0x0);
++
++	if (config_done == 0) {
++		TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_A, 0);
++		TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_B, 0);
++	}
++
++	/* Set the MAC Address */
++	memcpy(titan_ge_eth->port_mac_addr, netdev->dev_addr, 6);
++
++	if (config_done == 0)
++		titan_port_init(netdev, titan_ge_eth);
++
++	titan_ge_update_afx(titan_ge_eth);
++
++	/* Allocate the Tx ring now */
++	titan_ge_eth->tx_ring_skbs = 0;
++	titan_ge_eth->tx_ring_size = TITAN_GE_TX_QUEUE;
++
++	/* Allocate space in the SRAM for the descriptors */
++	titan_ge_eth->tx_desc_area = (titan_ge_tx_desc *)
++		(titan_ge_sram + TITAN_TX_RING_BYTES * port_num);
++	titan_ge_eth->tx_dma = TITAN_SRAM_BASE + TITAN_TX_RING_BYTES * port_num;
++
++	if (!titan_ge_eth->tx_desc_area) {
++		printk(KERN_ERR
++		       "%s: Cannot allocate Tx Ring (size %d bytes) for port %d\n",
++		       netdev->name, TITAN_TX_RING_BYTES, port_num);
++		return -ENOMEM;
++	}
++
++	memset(titan_ge_eth->tx_desc_area, 0, titan_ge_eth->tx_desc_area_size);
++
++	/* Now initialize the Tx descriptor ring */
++	titan_ge_init_tx_desc_ring(titan_ge_eth,
++				   titan_ge_eth->tx_ring_size,
++				   (unsigned long) titan_ge_eth->tx_desc_area,
++				   (unsigned long) titan_ge_eth->tx_dma);
++
++	/* Allocate the Rx ring now */
++	titan_ge_eth->rx_ring_size = TITAN_GE_RX_QUEUE;
++	titan_ge_eth->rx_ring_skbs = 0;
++
++	titan_ge_eth->rx_desc_area =
++		(titan_ge_rx_desc *)(titan_ge_sram + 0x1000 + TITAN_RX_RING_BYTES * port_num);
++
++	titan_ge_eth->rx_dma = TITAN_SRAM_BASE + 0x1000 + TITAN_RX_RING_BYTES * port_num;
++
++	if (!titan_ge_eth->rx_desc_area) {
++		printk(KERN_ERR "%s: Cannot allocate Rx Ring (size %d bytes)\n",
++		       netdev->name, TITAN_RX_RING_BYTES);
++
++		printk(KERN_ERR "%s: Freeing previously allocated TX queues...",
++		       netdev->name);
++
++		dma_free_coherent(device, titan_ge_eth->tx_desc_area_size,
++				    (void *) titan_ge_eth->tx_desc_area,
++				    titan_ge_eth->tx_dma);
++
++		return -ENOMEM;
++	}
++
++	memset(titan_ge_eth->rx_desc_area, 0, titan_ge_eth->rx_desc_area_size);
++
++	/* Now initialize the Rx ring */
++#ifdef TITAN_GE_JUMBO_FRAMES
++	if ((titan_ge_init_rx_desc_ring
++	    (titan_ge_eth, titan_ge_eth->rx_ring_size, TITAN_GE_JUMBO_BUFSIZE,
++	     (unsigned long) titan_ge_eth->rx_desc_area, 0,
++	      (unsigned long) titan_ge_eth->rx_dma)) == 0)
++#else
++	if ((titan_ge_init_rx_desc_ring
++	     (titan_ge_eth, titan_ge_eth->rx_ring_size, TITAN_GE_STD_BUFSIZE,
++	      (unsigned long) titan_ge_eth->rx_desc_area, 0,
++	      (unsigned long) titan_ge_eth->rx_dma)) == 0)
++#endif
++		panic("%s: Error initializing RX Ring\n", netdev->name);
++
++	/* Fill the Rx ring with the SKBs */
++	titan_ge_port_start(netdev, titan_ge_eth);
++
++	/*
++	 * Check if Interrupt Coalscing needs to be turned on. The
++	 * values specified in the register is multiplied by
++	 * (8 x 64 nanoseconds) to determine when an interrupt should
++	 * be sent to the CPU.
++	 */
++
++	if (TITAN_GE_TX_COAL) {
++		titan_ge_eth->tx_int_coal =
++		    titan_ge_tx_coal(TITAN_GE_TX_COAL, port_num);
++	}
++
++	err = titan_ge_mdio_read(port_num, TITAN_GE_MDIO_PHY_STATUS, &phy_reg);
++	if (err == TITAN_GE_MDIO_ERROR) {
++		printk(KERN_ERR
++		       "Could not read PHY control register 0x11 \n");
++		return TITAN_ERROR;
++	}
++	if (!(phy_reg & 0x0400)) {
++		netif_carrier_off(netdev);
++		netif_stop_queue(netdev);
++		return TITAN_ERROR;
++	} else {
++		netif_carrier_on(netdev);
++		netif_start_queue(netdev);
++	}
++
++	return TITAN_OK;
++}
++
++/*
++ * Queue the packet for Tx. Currently no support for zero copy,
++ * checksum offload and Scatter Gather. The chip does support
++ * Scatter Gather only. But, that wont help here since zero copy
++ * requires support for Tx checksumming also.
++ */
++int titan_ge_start_xmit(struct sk_buff *skb, struct net_device *netdev)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++	unsigned long flags;
++	struct net_device_stats *stats;
++//printk("titan_ge_start_xmit\n");
++
++	stats = &titan_ge_eth->stats;
++	spin_lock_irqsave(&titan_ge_eth->lock, flags);
++
++	if ((TITAN_GE_TX_QUEUE - titan_ge_eth->tx_ring_skbs) <=
++	    (skb_shinfo(skb)->nr_frags + 1)) {
++		netif_stop_queue(netdev);
++		spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
++		printk(KERN_ERR "Tx OOD \n");
++		return 1;
++	}
++
++	titan_ge_tx_queue(titan_ge_eth, skb);
++	titan_ge_eth->tx_ring_skbs++;
++
++	if (TITAN_GE_TX_QUEUE <= (titan_ge_eth->tx_ring_skbs + 4)) {
++		spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
++		titan_ge_free_tx_queue(titan_ge_eth);
++		spin_lock_irqsave(&titan_ge_eth->lock, flags);
++	}
++
++	stats->tx_bytes += skb->len;
++	stats->tx_packets++;
++
++	spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
++
++	netdev->trans_start = jiffies;
++
++	return 0;
++}
++
++/*
++ * Actually does the Rx. Rx side checksumming supported.
++ */
++static int titan_ge_rx(struct net_device *netdev, int port_num,
++			titan_ge_port_info * titan_ge_port,
++		       titan_ge_packet * packet)
++{
++	int rx_curr_desc, rx_used_desc;
++	volatile titan_ge_rx_desc *rx_desc;
++
++	rx_curr_desc = titan_ge_port->rx_curr_desc_q;
++	rx_used_desc = titan_ge_port->rx_used_desc_q;
++
++	if (((rx_curr_desc + 1) % TITAN_GE_RX_QUEUE) == rx_used_desc)
++		return TITAN_ERROR;
++
++	rx_desc = &(titan_ge_port->rx_desc_area[rx_curr_desc]);
++
++	if (rx_desc->cmd_sts & TITAN_GE_RX_BUFFER_OWNED)
++		return TITAN_ERROR;
++
++	packet->skb = titan_ge_port->rx_skb[rx_curr_desc];
++	packet->len = (rx_desc->cmd_sts & 0x7fff);
++
++	/*
++	 * At this point, we dont know if the checksumming
++	 * actually helps relieve CPU. So, keep it for
++	 * port 0 only
++	 */
++	packet->checksum = ntohs((rx_desc->buffer & 0xffff0000) >> 16);
++	packet->cmd_sts = rx_desc->cmd_sts;
++
++	titan_ge_port->rx_curr_desc_q = (rx_curr_desc + 1) % TITAN_GE_RX_QUEUE;
++
++	/* Prefetch the next descriptor */
++	prefetch((const void *)
++	       &titan_ge_port->rx_desc_area[titan_ge_port->rx_curr_desc_q + 1]);
++
++	return TITAN_OK;
++}
++
++/*
++ * Free the Tx queue of the used SKBs
++ */
++static int titan_ge_free_tx_queue(titan_ge_port_info *titan_ge_eth)
++{
++	unsigned long flags;
++
++	/* Take the lock */
++	spin_lock_irqsave(&(titan_ge_eth->lock), flags);
++
++	while (titan_ge_return_tx_desc(titan_ge_eth, titan_ge_eth->port_num) == 0)
++		if (titan_ge_eth->tx_ring_skbs != 1)
++			titan_ge_eth->tx_ring_skbs--;
++
++	spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
++
++	return TITAN_OK;
++}
++
++/*
++ * Threshold beyond which we do the cleaning of
++ * Tx queue and new allocation for the Rx
++ * queue
++ */
++#define	TX_THRESHOLD	4
++#define	RX_THRESHOLD	10
++
++/*
++ * Receive the packets and send it to the kernel.
++ */
++static int titan_ge_receive_queue(struct net_device *netdev, unsigned int max)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++	unsigned int port_num = titan_ge_eth->port_num;
++	titan_ge_packet packet;
++	struct net_device_stats *stats;
++	struct sk_buff *skb;
++	unsigned long received_packets = 0;
++	unsigned int ack;
++
++	stats = &titan_ge_eth->stats;
++
++	while ((--max)
++	       && (titan_ge_rx(netdev, port_num, titan_ge_eth, &packet) == TITAN_OK)) {
++		skb = (struct sk_buff *) packet.skb;
++
++		titan_ge_eth->rx_ring_skbs--;
++
++		if (--titan_ge_eth->rx_work_limit < 0)
++			break;
++		received_packets++;
++
++		stats->rx_packets++;
++		stats->rx_bytes += packet.len;
++
++		if ((packet.cmd_sts & TITAN_GE_RX_PERR) ||
++			(packet.cmd_sts & TITAN_GE_RX_OVERFLOW_ERROR) ||
++			(packet.cmd_sts & TITAN_GE_RX_TRUNC) ||
++			(packet.cmd_sts & TITAN_GE_RX_CRC_ERROR)) {
++				stats->rx_dropped++;
++				dev_kfree_skb_any(skb);
++
++				continue;
++		}
++		/*
++		 * Either support fast path or slow path. Decision
++		 * making can really slow down the performance. The
++		 * idea is to cut down the number of checks and improve
++		 * the fastpath.
++		 */
++
++		skb_put(skb, packet.len - 2);
++
++		/*
++		 * Increment data pointer by two since thats where
++		 * the MAC starts
++		 */
++		skb_reserve(skb, 2);
++		skb->protocol = eth_type_trans(skb, netdev);
++		netif_receive_skb(skb);
++
++		if (titan_ge_eth->rx_threshold > RX_THRESHOLD) {
++			ack = titan_ge_rx_task(netdev, titan_ge_eth);
++			TITAN_GE_WRITE((0x5048 + (port_num << 8)), ack);
++			titan_ge_eth->rx_threshold = 0;
++		} else
++			titan_ge_eth->rx_threshold++;
++
++		if (titan_ge_eth->tx_threshold > TX_THRESHOLD) {
++			titan_ge_eth->tx_threshold = 0;
++			titan_ge_free_tx_queue(titan_ge_eth);
++		}
++		else
++			titan_ge_eth->tx_threshold++;
++
++	}
++	return received_packets;
++}
++
++
++/*
++ * Enable the Rx side interrupts
++ */
++static void titan_ge_enable_int(unsigned int port_num,
++			titan_ge_port_info *titan_ge_eth,
++			struct net_device *netdev)
++{
++	unsigned long reg_data = TITAN_GE_READ(TITAN_GE_INTR_XDMA_IE);
++
++	if (port_num == 0)
++		reg_data |= 0x3;
++	if (port_num == 1)
++		reg_data |= 0x300;
++	if (port_num == 2)
++		reg_data |= 0x30000;
++
++	/* Re-enable interrupts */
++	TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_IE, reg_data);
++}
++
++/*
++ * Main function to handle the polling for Rx side NAPI.
++ * Receive interrupts have been disabled at this point.
++ * The poll schedules the transmit followed by receive.
++ */
++static int titan_ge_poll(struct net_device *netdev, int *budget)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++	int port_num = titan_ge_eth->port_num;
++	int work_done = 0;
++	unsigned long flags, status;
++
++	titan_ge_eth->rx_work_limit = *budget;
++	if (titan_ge_eth->rx_work_limit > netdev->quota)
++		titan_ge_eth->rx_work_limit = netdev->quota;
++
++	do {
++		/* Do the transmit cleaning work here */
++		titan_ge_free_tx_queue(titan_ge_eth);
++
++		/* Ack the Rx interrupts */
++		if (port_num == 0)
++			TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_A, 0x3);
++		if (port_num == 1)
++			TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_A, 0x300);
++		if (port_num == 2)
++			TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_CORE_A, 0x30000);
++
++		work_done += titan_ge_receive_queue(netdev, 0);
++
++		/* Out of quota and there is work to be done */
++		if (titan_ge_eth->rx_work_limit < 0)
++			goto not_done;
++
++		/* Receive alloc_skb could lead to OOM */
++		if (oom_flag == 1) {
++			oom_flag = 0;
++			goto oom;
++		}
++
++		status = TITAN_GE_READ(TITAN_GE_INTR_XDMA_CORE_A);
++	} while (status & 0x30300);
++
++	/* If we are here, then no more interrupts to process */
++	goto done;
++
++not_done:
++	*budget -= work_done;
++	netdev->quota -= work_done;
++	return 1;
++
++oom:
++	printk(KERN_ERR "OOM \n");
++	netif_rx_complete(netdev);
++	return 0;
++
++done:
++	/*
++	 * No more packets on the poll list. Turn the interrupts
++	 * back on and we should be able to catch the new
++	 * packets in the interrupt handler
++	 */
++	if (!work_done)
++		work_done = 1;
++
++	*budget -= work_done;
++	netdev->quota -= work_done;
++
++	spin_lock_irqsave(&titan_ge_eth->lock, flags);
++
++	/* Remove us from the poll list */
++	netif_rx_complete(netdev);
++
++	/* Re-enable interrupts */
++	titan_ge_enable_int(port_num, titan_ge_eth, netdev);
++
++	spin_unlock_irqrestore(&titan_ge_eth->lock, flags);
++
++	return 0;
++}
++
++/*
++ * Close the network device
++ */
++int titan_ge_stop(struct net_device *netdev)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++
++	spin_lock_irq(&(titan_ge_eth->lock));
++	titan_ge_eth_stop(netdev);
++	free_irq(netdev->irq, netdev);
++	spin_unlock_irq(&titan_ge_eth->lock);
++
++	return TITAN_OK;
++}
++
++/*
++ * Free the Tx ring
++ */
++static void titan_ge_free_tx_rings(struct net_device *netdev)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++	unsigned int port_num = titan_ge_eth->port_num;
++	unsigned int curr;
++	unsigned long reg_data;
++
++	/* Stop the Tx DMA */
++	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG +
++				(port_num << 8));
++	reg_data |= 0xc0000000;
++	TITAN_GE_WRITE((TITAN_GE_CHANNEL0_CONFIG +
++			(port_num << 8)), reg_data);
++
++	/* Disable the TMAC */
++	reg_data = TITAN_GE_READ(TITAN_GE_TMAC_CONFIG_1 +
++				(port_num << 12));
++	reg_data &= ~(0x00000001);
++	TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_1 +
++			(port_num << 12)), reg_data);
++
++	for (curr = 0;
++	     (titan_ge_eth->tx_ring_skbs) && (curr < TITAN_GE_TX_QUEUE);
++	     curr++) {
++		if (titan_ge_eth->tx_skb[curr]) {
++			dev_kfree_skb(titan_ge_eth->tx_skb[curr]);
++			titan_ge_eth->tx_ring_skbs--;
++		}
++	}
++
++	if (titan_ge_eth->tx_ring_skbs != 0)
++		printk
++		    ("%s: Error on Tx descriptor free - could not free %d"
++		     " descriptors\n", netdev->name,
++		     titan_ge_eth->tx_ring_skbs);
++
++#ifndef TITAN_RX_RING_IN_SRAM
++	dma_free_coherent(&titan_ge_device[port_num]->dev,
++			  titan_ge_eth->tx_desc_area_size,
++			  (void *) titan_ge_eth->tx_desc_area,
++			  titan_ge_eth->tx_dma);
++#endif
++}
++
++/*
++ * Free the Rx ring
++ */
++static void titan_ge_free_rx_rings(struct net_device *netdev)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++	unsigned int port_num = titan_ge_eth->port_num;
++	unsigned int curr;
++	unsigned long reg_data;
++
++	/* Stop the Rx DMA */
++	reg_data = TITAN_GE_READ(TITAN_GE_CHANNEL0_CONFIG +
++				(port_num << 8));
++	reg_data |= 0x000c0000;
++	TITAN_GE_WRITE((TITAN_GE_CHANNEL0_CONFIG +
++			(port_num << 8)), reg_data);
++
++	/* Disable the RMAC */
++	reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_1 +
++				(port_num << 12));
++	reg_data &= ~(0x00000001);
++	TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_1 +
++			(port_num << 12)), reg_data);
++
++	for (curr = 0;
++	     titan_ge_eth->rx_ring_skbs && (curr < TITAN_GE_RX_QUEUE);
++	     curr++) {
++		if (titan_ge_eth->rx_skb[curr]) {
++			dev_kfree_skb(titan_ge_eth->rx_skb[curr]);
++			titan_ge_eth->rx_ring_skbs--;
++		}
++	}
++
++	if (titan_ge_eth->rx_ring_skbs != 0)
++		printk(KERN_ERR
++		       "%s: Error in freeing Rx Ring. %d skb's still"
++		       " stuck in RX Ring - ignoring them\n", netdev->name,
++		       titan_ge_eth->rx_ring_skbs);
++
++#ifndef TITAN_RX_RING_IN_SRAM
++	dma_free_coherent(&titan_ge_device[port_num]->dev,
++			  titan_ge_eth->rx_desc_area_size,
++			  (void *) titan_ge_eth->rx_desc_area,
++			  titan_ge_eth->rx_dma);
++#endif
++}
++
++/*
++ * Actually does the stop of the Ethernet device
++ */
++static void titan_ge_eth_stop(struct net_device *netdev)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++
++	netif_stop_queue(netdev);
++
++	titan_ge_port_reset(titan_ge_eth->port_num);
++
++	titan_ge_free_tx_rings(netdev);
++	titan_ge_free_rx_rings(netdev);
++
++	/* Disable the Tx and Rx Interrupts for all channels */
++	TITAN_GE_WRITE(TITAN_GE_INTR_XDMA_IE, 0x0);
++}
++
++/*
++ * Update the MAC address. Note that we have to write the
++ * address in three station registers, 16 bits each. And this
++ * has to be done for TMAC and RMAC
++ */
++static void titan_ge_update_mac_address(struct net_device *netdev)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++	unsigned int port_num = titan_ge_eth->port_num;
++	u8 p_addr[6];
++
++	memcpy(titan_ge_eth->port_mac_addr, netdev->dev_addr, 6);
++	memcpy(p_addr, netdev->dev_addr, 6);
++
++	/* Update the Address Filtering Match tables */
++	titan_ge_update_afx(titan_ge_eth);
++
++	printk("Station MAC : %d %d %d %d %d %d  \n",
++		p_addr[5], p_addr[4], p_addr[3],
++		p_addr[2], p_addr[1], p_addr[0]);
++
++	/* Set the MAC address here for TMAC and RMAC */
++	TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_HI + (port_num << 12)),
++		       ((p_addr[5] << 8) | p_addr[4]));
++	TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_MID + (port_num << 12)),
++		       ((p_addr[3] << 8) | p_addr[2]));
++	TITAN_GE_WRITE((TITAN_GE_TMAC_STATION_LOW + (port_num << 12)),
++		       ((p_addr[1] << 8) | p_addr[0]));
++
++	TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_HI + (port_num << 12)),
++		       ((p_addr[5] << 8) | p_addr[4]));
++	TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_MID + (port_num << 12)),
++		       ((p_addr[3] << 8) | p_addr[2]));
++	TITAN_GE_WRITE((TITAN_GE_RMAC_STATION_LOW + (port_num << 12)),
++		       ((p_addr[1] << 8) | p_addr[0]));
++}
++
++/*
++ * Set the MAC address of the Ethernet device
++ */
++static int titan_ge_set_mac_address(struct net_device *dev, void *addr)
++{
++	titan_ge_port_info *tp = netdev_priv(dev);
++	struct sockaddr *sa = addr;
++
++	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
++
++	spin_lock_irq(&tp->lock);
++	titan_ge_update_mac_address(dev);
++	spin_unlock_irq(&tp->lock);
++
++	return 0;
++}
++
++/*
++ * Get the Ethernet device stats
++ */
++static struct net_device_stats *titan_ge_get_stats(struct net_device *netdev)
++{
++	titan_ge_port_info *titan_ge_eth = netdev_priv(netdev);
++
++	return &titan_ge_eth->stats;
++}
++
++/*
++ * Initialize the Rx descriptor ring for the Titan Ge
++ */
++static int titan_ge_init_rx_desc_ring(titan_ge_port_info * titan_eth_port,
++				      int rx_desc_num,
++				      int rx_buff_size,
++				      unsigned long rx_desc_base_addr,
++				      unsigned long rx_buff_base_addr,
++				      unsigned long rx_dma)
++{
++	volatile titan_ge_rx_desc *rx_desc;
++	unsigned long buffer_addr;
++	int index;
++	unsigned long titan_ge_rx_desc_bus = rx_dma;
++
++	buffer_addr = rx_buff_base_addr;
++	rx_desc = (titan_ge_rx_desc *) rx_desc_base_addr;
++
++	/* Check alignment */
++	if (rx_buff_base_addr & 0xF)
++		return 0;
++
++	/* Check Rx buffer size */
++	if ((rx_buff_size < 8) || (rx_buff_size > TITAN_GE_MAX_RX_BUFFER))
++		return 0;
++
++	/* 64-bit alignment
++	if ((rx_buff_base_addr + rx_buff_size) & 0x7)
++		return 0; */
++
++	/* Initialize the Rx desc ring */
++	for (index = 0; index < rx_desc_num; index++) {
++		titan_ge_rx_desc_bus += sizeof(titan_ge_rx_desc);
++		rx_desc[index].cmd_sts = 0;
++		rx_desc[index].buffer_addr = buffer_addr;
++		titan_eth_port->rx_skb[index] = NULL;
++		buffer_addr += rx_buff_size;
++	}
++
++	titan_eth_port->rx_curr_desc_q = 0;
++	titan_eth_port->rx_used_desc_q = 0;
++
++	titan_eth_port->rx_desc_area = (titan_ge_rx_desc *) rx_desc_base_addr;
++	titan_eth_port->rx_desc_area_size =
++	    rx_desc_num * sizeof(titan_ge_rx_desc);
++
++	titan_eth_port->rx_dma = rx_dma;
++
++	return TITAN_OK;
++}
++
++/*
++ * Initialize the Tx descriptor ring. Descriptors in the SRAM
++ */
++static int titan_ge_init_tx_desc_ring(titan_ge_port_info * titan_ge_port,
++				      int tx_desc_num,
++				      unsigned long tx_desc_base_addr,
++				      unsigned long tx_dma)
++{
++	titan_ge_tx_desc *tx_desc;
++	int index;
++	unsigned long titan_ge_tx_desc_bus = tx_dma;
++
++	if (tx_desc_base_addr & 0xF)
++		return 0;
++
++	tx_desc = (titan_ge_tx_desc *) tx_desc_base_addr;
++
++	for (index = 0; index < tx_desc_num; index++) {
++		titan_ge_port->tx_dma_array[index] =
++		    (dma_addr_t) titan_ge_tx_desc_bus;
++		titan_ge_tx_desc_bus += sizeof(titan_ge_tx_desc);
++		tx_desc[index].cmd_sts = 0x0000;
++		tx_desc[index].buffer_len = 0;
++		tx_desc[index].buffer_addr = 0x00000000;
++		titan_ge_port->tx_skb[index] = NULL;
++	}
++
++	titan_ge_port->tx_curr_desc_q = 0;
++	titan_ge_port->tx_used_desc_q = 0;
++
++	titan_ge_port->tx_desc_area = (titan_ge_tx_desc *) tx_desc_base_addr;
++	titan_ge_port->tx_desc_area_size =
++	    tx_desc_num * sizeof(titan_ge_tx_desc);
++
++	titan_ge_port->tx_dma = tx_dma;
++	return TITAN_OK;
++}
++
++/*
++ * Initialize the device as an Ethernet device
++ */
++static int __init titan_ge_probe(struct device *device)
++{
++	titan_ge_port_info *titan_ge_eth;
++	struct net_device *netdev;
++	int port = to_platform_device(device)->id;
++	int err;
++
++	netdev = alloc_etherdev(sizeof(titan_ge_port_info));
++	if (!netdev) {
++		err = -ENODEV;
++		goto out;
++	}
++
++	netdev->open = titan_ge_open;
++	netdev->stop = titan_ge_stop;
++	netdev->hard_start_xmit = titan_ge_start_xmit;
++	netdev->get_stats = titan_ge_get_stats;
++	netdev->set_multicast_list = titan_ge_set_multi;
++	netdev->set_mac_address = titan_ge_set_mac_address;
++
++	/* Tx timeout */
++	netdev->tx_timeout = titan_ge_tx_timeout;
++	netdev->watchdog_timeo = 2 * HZ;
++
++	/* Set these to very high values */
++	netdev->poll = titan_ge_poll;
++	netdev->weight = 64;
++
++	netdev->tx_queue_len = TITAN_GE_TX_QUEUE;
++	netif_carrier_off(netdev);
++	netdev->base_addr = 0;
++
++	netdev->change_mtu = titan_ge_change_mtu;
++
++	titan_ge_eth = netdev_priv(netdev);
++	/* Allocation of memory for the driver structures */
++
++	titan_ge_eth->port_num = port;
++
++	/* Configure the Tx timeout handler */
++	INIT_WORK(&titan_ge_eth->tx_timeout_task,
++		  (void (*)(void *)) titan_ge_tx_timeout_task, netdev);
++
++	spin_lock_init(&titan_ge_eth->lock);
++
++	/* set MAC addresses */
++	memcpy(netdev->dev_addr, titan_ge_mac_addr_base, 6);
++	netdev->dev_addr[5] += port;
++
++	err = register_netdev(netdev);
++
++	if (err)
++		goto out_free_netdev;
++
++	printk(KERN_NOTICE
++	       "%s: port %d with MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
++	       netdev->name, port, netdev->dev_addr[0],
++	       netdev->dev_addr[1], netdev->dev_addr[2],
++	       netdev->dev_addr[3], netdev->dev_addr[4],
++	       netdev->dev_addr[5]);
++
++	printk(KERN_NOTICE "Rx NAPI supported, Tx Coalescing ON \n");
++
++	return 0;
++
++out_free_netdev:
++	kfree(netdev);
++
++out:
++	return err;
++}
++
++static void __devexit titan_device_remove(struct device *device)
++{
++}
++
++/*
++ * Reset the Ethernet port
++ */
++static void titan_ge_port_reset(unsigned int port_num)
++{
++	unsigned int reg_data;
++
++	/* Stop the Tx port activity */
++	reg_data = TITAN_GE_READ(TITAN_GE_TMAC_CONFIG_1 +
++				(port_num << 12));
++	reg_data &= ~(0x0001);
++	TITAN_GE_WRITE((TITAN_GE_TMAC_CONFIG_1 +
++			(port_num << 12)), reg_data);
++
++	/* Stop the Rx port activity */
++	reg_data = TITAN_GE_READ(TITAN_GE_RMAC_CONFIG_1 +
++				(port_num << 12));
++	reg_data &= ~(0x0001);
++	TITAN_GE_WRITE((TITAN_GE_RMAC_CONFIG_1 +
++			(port_num << 12)), reg_data);
++
++	return;
++}
++
++/*
++ * Return the Tx desc after use by the XDMA
++ */
++static int titan_ge_return_tx_desc(titan_ge_port_info * titan_ge_eth, int port)
++{
++	int tx_desc_used;
++	struct sk_buff *skb;
++
++	tx_desc_used = titan_ge_eth->tx_used_desc_q;
++
++	/* return right away */
++	if (tx_desc_used == titan_ge_eth->tx_curr_desc_q)
++		return TITAN_ERROR;
++
++	/* Now the critical stuff */
++	skb = titan_ge_eth->tx_skb[tx_desc_used];
++
++	dev_kfree_skb_any(skb);
++
++	titan_ge_eth->tx_skb[tx_desc_used] = NULL;
++	titan_ge_eth->tx_used_desc_q =
++	    (tx_desc_used + 1) % TITAN_GE_TX_QUEUE;
++
++	return 0;
++}
++
++/*
++ * Coalescing for the Tx path
++ */
++static unsigned long titan_ge_tx_coal(unsigned long delay, int port)
++{
++	unsigned long rx_delay;
++
++	rx_delay = TITAN_GE_READ(TITAN_GE_INT_COALESCING);
++	delay = (delay << 16) | rx_delay;
++
++	TITAN_GE_WRITE(TITAN_GE_INT_COALESCING, delay);
++	TITAN_GE_WRITE(0x5038, delay);
++
++	return delay;
++}
++
++static struct device_driver titan_soc_driver = {
++	.name   = titan_string,
++	.bus    = &platform_bus_type,
++	.probe  = titan_ge_probe,
++	.remove = __devexit_p(titan_device_remove),
++};
++
++static void titan_platform_release (struct device *device)
++{
++	struct platform_device *pldev;
++
++	/* free device */
++	pldev = to_platform_device (device);
++	kfree (pldev);
++}
++
++/*
++ * Register the Titan GE with the kernel
++ */
++static int __init titan_ge_init_module(void)
++{
++	struct platform_device *pldev;
++	unsigned int version, device;
++	int i;
++
++	printk(KERN_NOTICE
++	       "PMC-Sierra TITAN 10/100/1000 Ethernet Driver \n");
++
++	titan_ge_base = (unsigned long) ioremap(TITAN_GE_BASE, TITAN_GE_SIZE);
++	if (!titan_ge_base) {
++		printk("Mapping Titan GE failed\n");
++		goto out;
++	}
++
++	device = TITAN_GE_READ(TITAN_GE_DEVICE_ID);
++	version = (device & 0x000f0000) >> 16;
++	device &= 0x0000ffff;
++
++	printk(KERN_NOTICE "Device Id : %x,  Version : %x \n", device, version);
++
++#ifdef TITAN_RX_RING_IN_SRAM
++	titan_ge_sram = (unsigned long) ioremap(TITAN_SRAM_BASE,
++						TITAN_SRAM_SIZE);
++	if (!titan_ge_sram) {
++		printk("Mapping Titan SRAM failed\n");
++		goto out_unmap_ge;
++	}
++#endif
++
++	if (driver_register(&titan_soc_driver)) {
++		printk(KERN_ERR "Driver registration failed\n");
++		goto out_unmap_sram;
++	}
++
++	for (i = 0; i < 3; i++) {
++		titan_ge_device[i] = NULL;
++
++		if (!(pldev = kmalloc (sizeof (*pldev), GFP_KERNEL)))
++			continue;
++
++		memset (pldev, 0, sizeof (*pldev));
++		pldev->name		= titan_string;
++		pldev->id		= i;
++		pldev->dev.release	= titan_platform_release;
++		titan_ge_device[i]	= pldev;
++
++		if (platform_device_register (pldev)) {
++			kfree (pldev);
++			titan_ge_device[i] = NULL;
++			continue;
++		}
++
++		if (!pldev->dev.driver) {
++			/*
++			 * The driver was not bound to this device, there was
++			 * no hardware at this address. Unregister it, as the
++			 * release fuction will take care of freeing the
++			 * allocated structure
++			 */
++			titan_ge_device[i] = NULL;
++			platform_device_unregister (pldev);
++		}
++	}
++
++	return 0;
++
++out_unmap_sram:
++	iounmap((void *)titan_ge_sram);
++
++out_unmap_ge:
++	iounmap((void *)titan_ge_base);
++
++out:
++	return -ENOMEM;
++}
++
++/*
++ * Unregister the Titan GE from the kernel
++ */
++static void __exit titan_ge_cleanup_module(void)
++{
++	int i;
++
++	driver_unregister(&titan_soc_driver);
++
++	for (i = 0; i < 3; i++) {
++		if (titan_ge_device[i]) {
++			platform_device_unregister (titan_ge_device[i]);
++			titan_ge_device[i] = NULL;
++		}
++	}
++
++	iounmap((void *)titan_ge_sram);
++	iounmap((void *)titan_ge_base);
++}
++
++MODULE_AUTHOR("Manish Lachwani <lachwani@pmc-sierra.com>");
++MODULE_DESCRIPTION("Titan GE Ethernet driver");
++MODULE_LICENSE("GPL");
++
++module_init(titan_ge_init_module);
++module_exit(titan_ge_cleanup_module);
+diff --git a/drivers/net/titan_ge.h b/drivers/net/titan_ge.h
+new file mode 100644
+index 0000000..3719f78
+--- /dev/null
++++ b/drivers/net/titan_ge.h
+@@ -0,0 +1,415 @@
++#ifndef _TITAN_GE_H_
++#define _TITAN_GE_H_
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/spinlock.h>
++#include <asm/byteorder.h>
++
++/*
++ * These functions should be later moved to a more generic location since there
++ * will be others accessing it also
++ */
++
++/*
++ * This is the way it works: LKB5 Base is at 0x0128. TITAN_BASE is defined in
++ * include/asm/titan_dep.h. TITAN_GE_BASE is the value in the TITAN_GE_LKB5
++ * register.
++ */
++
++#define	TITAN_GE_BASE	0xfe000000UL
++#define	TITAN_GE_SIZE	0x10000UL
++
++extern unsigned long titan_ge_base;
++
++#define	TITAN_GE_WRITE(offset, data) \
++		*(volatile u32 *)(titan_ge_base + (offset)) = (data)
++
++#define TITAN_GE_READ(offset) *(volatile u32 *)(titan_ge_base + (offset))
++
++#ifndef msec_delay
++#define msec_delay(x)   do { if(in_interrupt()) { \
++				/* Don't mdelay in interrupt context! */ \
++				BUG(); \
++			} else { \
++				set_current_state(TASK_UNINTERRUPTIBLE); \
++				schedule_timeout((x * HZ)/1000); \
++			} } while(0)
++#endif
++
++#define TITAN_GE_PORT_0
++
++#define	TITAN_SRAM_BASE		((OCD_READ(RM9000x2_OCD_LKB13) & ~1) << 4)
++#define	TITAN_SRAM_SIZE		0x2000UL
++
++/*
++ * We may need these constants
++ */
++#define TITAN_BIT0    0x00000001
++#define TITAN_BIT1    0x00000002
++#define TITAN_BIT2    0x00000004
++#define TITAN_BIT3    0x00000008
++#define TITAN_BIT4    0x00000010
++#define TITAN_BIT5    0x00000020
++#define TITAN_BIT6    0x00000040
++#define TITAN_BIT7    0x00000080
++#define TITAN_BIT8    0x00000100
++#define TITAN_BIT9    0x00000200
++#define TITAN_BIT10   0x00000400
++#define TITAN_BIT11   0x00000800
++#define TITAN_BIT12   0x00001000
++#define TITAN_BIT13   0x00002000
++#define TITAN_BIT14   0x00004000
++#define TITAN_BIT15   0x00008000
++#define TITAN_BIT16   0x00010000
++#define TITAN_BIT17   0x00020000
++#define TITAN_BIT18   0x00040000
++#define TITAN_BIT19   0x00080000
++#define TITAN_BIT20   0x00100000
++#define TITAN_BIT21   0x00200000
++#define TITAN_BIT22   0x00400000
++#define TITAN_BIT23   0x00800000
++#define TITAN_BIT24   0x01000000
++#define TITAN_BIT25   0x02000000
++#define TITAN_BIT26   0x04000000
++#define TITAN_BIT27   0x08000000
++#define TITAN_BIT28   0x10000000
++#define TITAN_BIT29   0x20000000
++#define TITAN_BIT30   0x40000000
++#define TITAN_BIT31   0x80000000
++
++/* Flow Control */
++#define	TITAN_GE_FC_NONE	0x0
++#define	TITAN_GE_FC_FULL	0x1
++#define	TITAN_GE_FC_TX_PAUSE	0x2
++#define	TITAN_GE_FC_RX_PAUSE	0x3
++
++/* Duplex Settings */
++#define	TITAN_GE_FULL_DUPLEX	0x1
++#define	TITAN_GE_HALF_DUPLEX	0x2
++
++/* Speed settings */
++#define	TITAN_GE_SPEED_1000	0x1
++#define	TITAN_GE_SPEED_100	0x2
++#define	TITAN_GE_SPEED_10	0x3
++
++/* Debugging info only */
++#undef TITAN_DEBUG
++
++/* Keep the rings in the Titan's SSRAM */
++#define TITAN_RX_RING_IN_SRAM
++
++#ifdef CONFIG_64BIT
++#define	TITAN_GE_IE_MASK	0xfffffffffb001b64
++#define	TITAN_GE_IE_STATUS	0xfffffffffb001b60
++#else
++#define	TITAN_GE_IE_MASK	0xfb001b64
++#define	TITAN_GE_IE_STATUS	0xfb001b60
++#endif
++
++/* Support for Jumbo Frames */
++#undef TITAN_GE_JUMBO_FRAMES
++
++/* Rx buffer size */
++#ifdef TITAN_GE_JUMBO_FRAMES
++#define	TITAN_GE_JUMBO_BUFSIZE	9080
++#else
++#define	TITAN_GE_STD_BUFSIZE	1580
++#endif
++
++/*
++ * Tx and Rx Interrupt Coalescing parameter. These values are
++ * for 1 Ghz processor. Rx coalescing can be taken care of
++ * by NAPI. NAPI is adaptive and hence useful. Tx coalescing
++ * is not adaptive. Hence, these values need to be adjusted
++ * based on load, CPU speed etc.
++ */
++#define	TITAN_GE_RX_COAL	150
++#define	TITAN_GE_TX_COAL	300
++
++#if defined(__BIG_ENDIAN)
++
++/* Define the Rx descriptor */
++typedef struct eth_rx_desc {
++	u32     reserved;	/* Unused 		*/
++	u32     buffer_addr;	/* CPU buffer address 	*/
++	u32	cmd_sts;	/* Command and Status	*/
++	u32	buffer;		/* XDMA buffer address	*/
++} titan_ge_rx_desc;
++
++/* Define the Tx descriptor */
++typedef struct eth_tx_desc {
++	u16     cmd_sts;	/* Command, Status and Buffer count */
++	u16	buffer_len;	/* Length of the buffer	*/
++	u32     buffer_addr;	/* Physical address of the buffer */
++} titan_ge_tx_desc;
++
++#elif defined(__LITTLE_ENDIAN)
++
++/* Define the Rx descriptor */
++typedef struct eth_rx_desc {
++	u32	buffer_addr;	/* CPU buffer address   */
++	u32	reserved;	/* Unused               */
++	u32	buffer;		/* XDMA buffer address  */
++	u32	cmd_sts;	/* Command and Status   */
++} titan_ge_rx_desc;
++
++/* Define the Tx descriptor */
++typedef struct eth_tx_desc {
++	u32     buffer_addr;	/* Physical address of the buffer */
++	u16     buffer_len;     /* Length of the buffer */
++	u16     cmd_sts;        /* Command, Status and Buffer count */
++} titan_ge_tx_desc;
++#endif
++
++/* Default Tx Queue Size */
++#define	TITAN_GE_TX_QUEUE	128
++#define TITAN_TX_RING_BYTES	(TITAN_GE_TX_QUEUE * sizeof(struct eth_tx_desc))
++
++/* Default Rx Queue Size */
++#define	TITAN_GE_RX_QUEUE	64
++#define TITAN_RX_RING_BYTES	(TITAN_GE_RX_QUEUE * sizeof(struct eth_rx_desc))
++
++/* Packet Structure */
++typedef struct _pkt_info {
++	unsigned int           len;
++	unsigned int            cmd_sts;
++	unsigned int            buffer;
++	struct sk_buff          *skb;
++	unsigned int		checksum;
++} titan_ge_packet;
++
++
++#define	PHYS_CNT	3
++
++/* Titan Port specific data structure */
++typedef struct _eth_port_ctrl {
++	unsigned int		port_num;
++	u8			port_mac_addr[6];
++
++	/* Rx descriptor pointers */
++	int 			rx_curr_desc_q, rx_used_desc_q;
++
++	/* Tx descriptor pointers */
++	int 			tx_curr_desc_q, tx_used_desc_q;
++
++	/* Rx descriptor area */
++	volatile titan_ge_rx_desc	*rx_desc_area;
++	unsigned int			rx_desc_area_size;
++	struct sk_buff*			rx_skb[TITAN_GE_RX_QUEUE];
++
++	/* Tx Descriptor area */
++	volatile titan_ge_tx_desc	*tx_desc_area;
++	unsigned int                    tx_desc_area_size;
++	struct sk_buff*                 tx_skb[TITAN_GE_TX_QUEUE];
++
++	/* Timeout task */
++	struct work_struct		tx_timeout_task;
++
++	/* DMA structures and handles */
++	dma_addr_t			tx_dma;
++	dma_addr_t			rx_dma;
++	dma_addr_t			tx_dma_array[TITAN_GE_TX_QUEUE];
++
++	/* Device lock */
++	spinlock_t			lock;
++
++	unsigned int			tx_ring_skbs;
++	unsigned int			rx_ring_size;
++	unsigned int			tx_ring_size;
++	unsigned int			rx_ring_skbs;
++
++	struct net_device_stats		stats;
++
++	/* Tx and Rx coalescing */
++	unsigned long			rx_int_coal;
++	unsigned long			tx_int_coal;
++
++	/* Threshold for replenishing the Rx and Tx rings */
++	unsigned int			tx_threshold;
++	unsigned int			rx_threshold;
++
++	/* NAPI work limit */
++	unsigned int			rx_work_limit;
++} titan_ge_port_info;
++
++/* Titan specific constants */
++#define	TITAN_ETH_PORT_IRQ		3
++
++/* Max Rx buffer */
++#define	TITAN_GE_MAX_RX_BUFFER		65536
++
++/* Tx and Rx Error */
++#define	TITAN_GE_ERROR
++
++/* Rx Descriptor Command and Status */
++
++#define	TITAN_GE_RX_CRC_ERROR		TITAN_BIT27	/* crc error */
++#define	TITAN_GE_RX_OVERFLOW_ERROR	TITAN_BIT15	/* overflow */
++#define TITAN_GE_RX_BUFFER_OWNED	TITAN_BIT21	/* buffer ownership */
++#define	TITAN_GE_RX_STP			TITAN_BIT31	/* start of packet */
++#define	TITAN_GE_RX_BAM			TITAN_BIT30	/* broadcast address match */
++#define TITAN_GE_RX_PAM			TITAN_BIT28	/* physical address match */
++#define TITAN_GE_RX_LAFM		TITAN_BIT29	/* logical address filter match */
++#define TITAN_GE_RX_VLAN		TITAN_BIT26	/* virtual lans */
++#define TITAN_GE_RX_PERR		TITAN_BIT19	/* packet error */
++#define TITAN_GE_RX_TRUNC		TITAN_BIT20	/* packet size greater than 32 buffers */
++
++/* Tx Descriptor Command */
++#define	TITAN_GE_TX_BUFFER_OWNED	TITAN_BIT5	/* buffer ownership */
++#define	TITAN_GE_TX_ENABLE_INTERRUPT	TITAN_BIT15	/* Interrupt Enable */
++
++/* Return Status */
++#define	TITAN_OK	0x1	/* Good Status */
++#define	TITAN_ERROR	0x2	/* Error Status */
++
++/* MIB specific register offset */
++#define TITAN_GE_MSTATX_STATS_BASE_LOW       0x0800  /* MSTATX COUNTL[15:0] */
++#define TITAN_GE_MSTATX_STATS_BASE_MID       0x0804  /* MSTATX COUNTM[15:0] */
++#define TITAN_GE_MSTATX_STATS_BASE_HI        0x0808  /* MSTATX COUNTH[7:0] */
++#define TITAN_GE_MSTATX_CONTROL              0x0828  /* MSTATX Control */
++#define TITAN_GE_MSTATX_VARIABLE_SELECT      0x082C  /* MSTATX Variable Select */
++
++/* MIB counter offsets, add to the TITAN_GE_MSTATX_STATS_BASE_XXX */
++#define TITAN_GE_MSTATX_RXFRAMESOK                   0x0040
++#define TITAN_GE_MSTATX_RXOCTETSOK                   0x0050
++#define TITAN_GE_MSTATX_RXFRAMES                     0x0060
++#define TITAN_GE_MSTATX_RXOCTETS                     0x0070
++#define TITAN_GE_MSTATX_RXUNICASTFRAMESOK            0x0080
++#define TITAN_GE_MSTATX_RXBROADCASTFRAMESOK          0x0090
++#define TITAN_GE_MSTATX_RXMULTICASTFRAMESOK          0x00A0
++#define TITAN_GE_MSTATX_RXTAGGEDFRAMESOK             0x00B0
++#define TITAN_GE_MSTATX_RXMACPAUSECONTROLFRAMESOK    0x00C0
++#define TITAN_GE_MSTATX_RXMACCONTROLFRAMESOK         0x00D0
++#define TITAN_GE_MSTATX_RXFCSERROR                   0x00E0
++#define TITAN_GE_MSTATX_RXALIGNMENTERROR             0x00F0
++#define TITAN_GE_MSTATX_RXSYMBOLERROR                0x0100
++#define TITAN_GE_MSTATX_RXLAYER1ERROR                0x0110
++#define TITAN_GE_MSTATX_RXINRANGELENGTHERROR         0x0120
++#define TITAN_GE_MSTATX_RXLONGLENGTHERROR            0x0130
++#define TITAN_GE_MSTATX_RXLONGLENGTHCRCERROR         0x0140
++#define TITAN_GE_MSTATX_RXSHORTLENGTHERROR           0x0150
++#define TITAN_GE_MSTATX_RXSHORTLLENGTHCRCERROR       0x0160
++#define TITAN_GE_MSTATX_RXFRAMES64OCTETS             0x0170
++#define TITAN_GE_MSTATX_RXFRAMES65TO127OCTETS        0x0180
++#define TITAN_GE_MSTATX_RXFRAMES128TO255OCTETS       0x0190
++#define TITAN_GE_MSTATX_RXFRAMES256TO511OCTETS       0x01A0
++#define TITAN_GE_MSTATX_RXFRAMES512TO1023OCTETS      0x01B0
++#define TITAN_GE_MSTATX_RXFRAMES1024TO1518OCTETS     0x01C0
++#define TITAN_GE_MSTATX_RXFRAMES1519TOMAXSIZE        0x01D0
++#define TITAN_GE_MSTATX_RXSTATIONADDRESSFILTERED     0x01E0
++#define TITAN_GE_MSTATX_RXVARIABLE                   0x01F0
++#define TITAN_GE_MSTATX_GENERICADDRESSFILTERED       0x0200
++#define TITAN_GE_MSTATX_UNICASTFILTERED              0x0210
++#define TITAN_GE_MSTATX_MULTICASTFILTERED            0x0220
++#define TITAN_GE_MSTATX_BROADCASTFILTERED            0x0230
++#define TITAN_GE_MSTATX_HASHFILTERED                 0x0240
++#define TITAN_GE_MSTATX_TXFRAMESOK                   0x0250
++#define TITAN_GE_MSTATX_TXOCTETSOK                   0x0260
++#define TITAN_GE_MSTATX_TXOCTETS                     0x0270
++#define TITAN_GE_MSTATX_TXTAGGEDFRAMESOK             0x0280
++#define TITAN_GE_MSTATX_TXMACPAUSECONTROLFRAMESOK    0x0290
++#define TITAN_GE_MSTATX_TXFCSERROR                   0x02A0
++#define TITAN_GE_MSTATX_TXSHORTLENGTHERROR           0x02B0
++#define TITAN_GE_MSTATX_TXLONGLENGTHERROR            0x02C0
++#define TITAN_GE_MSTATX_TXSYSTEMERROR                0x02D0
++#define TITAN_GE_MSTATX_TXMACERROR                   0x02E0
++#define TITAN_GE_MSTATX_TXCARRIERSENSEERROR          0x02F0
++#define TITAN_GE_MSTATX_TXSQETESTERROR               0x0300
++#define TITAN_GE_MSTATX_TXUNICASTFRAMESOK            0x0310
++#define TITAN_GE_MSTATX_TXBROADCASTFRAMESOK          0x0320
++#define TITAN_GE_MSTATX_TXMULTICASTFRAMESOK          0x0330
++#define TITAN_GE_MSTATX_TXUNICASTFRAMESATTEMPTED     0x0340
++#define TITAN_GE_MSTATX_TXBROADCASTFRAMESATTEMPTED   0x0350
++#define TITAN_GE_MSTATX_TXMULTICASTFRAMESATTEMPTED   0x0360
++#define TITAN_GE_MSTATX_TXFRAMES64OCTETS             0x0370
++#define TITAN_GE_MSTATX_TXFRAMES65TO127OCTETS        0x0380
++#define TITAN_GE_MSTATX_TXFRAMES128TO255OCTETS       0x0390
++#define TITAN_GE_MSTATX_TXFRAMES256TO511OCTETS       0x03A0
++#define TITAN_GE_MSTATX_TXFRAMES512TO1023OCTETS      0x03B0
++#define TITAN_GE_MSTATX_TXFRAMES1024TO1518OCTETS     0x03C0
++#define TITAN_GE_MSTATX_TXFRAMES1519TOMAXSIZE        0x03D0
++#define TITAN_GE_MSTATX_TXVARIABLE                   0x03E0
++#define TITAN_GE_MSTATX_RXSYSTEMERROR                0x03F0
++#define TITAN_GE_MSTATX_SINGLECOLLISION              0x0400
++#define TITAN_GE_MSTATX_MULTIPLECOLLISION            0x0410
++#define TITAN_GE_MSTATX_DEFERREDXMISSIONS            0x0420
++#define TITAN_GE_MSTATX_LATECOLLISIONS               0x0430
++#define TITAN_GE_MSTATX_ABORTEDDUETOXSCOLLS          0x0440
++
++/* Interrupt specific defines */
++#define TITAN_GE_DEVICE_ID         0x0000  /* Device ID */
++#define TITAN_GE_RESET             0x0004  /* Reset reg */
++#define TITAN_GE_TSB_CTRL_0        0x000C  /* TSB Control reg 0 */
++#define TITAN_GE_TSB_CTRL_1        0x0010  /* TSB Control reg 1 */
++#define TITAN_GE_INTR_GRP0_STATUS  0x0040  /* General Interrupt Group 0 Status */
++#define TITAN_GE_INTR_XDMA_CORE_A  0x0048  /* XDMA Channel Interrupt Status, Core A*/
++#define TITAN_GE_INTR_XDMA_CORE_B  0x004C  /* XDMA Channel Interrupt Status, Core B*/
++#define	TITAN_GE_INTR_XDMA_IE	   0x0058  /* XDMA Channel Interrupt Enable */
++#define TITAN_GE_SDQPF_ECC_INTR    0x480C  /* SDQPF ECC Interrupt Status */
++#define TITAN_GE_SDQPF_RXFIFO_CTL  0x4828  /* SDQPF RxFifo Control and Interrupt Enb*/
++#define TITAN_GE_SDQPF_RXFIFO_INTR 0x482C  /* SDQPF RxFifo Interrupt Status */
++#define TITAN_GE_SDQPF_TXFIFO_CTL  0x4928  /* SDQPF TxFifo Control and Interrupt Enb*/
++#define TITAN_GE_SDQPF_TXFIFO_INTR 0x492C  /* SDQPF TxFifo Interrupt Status */
++#define	TITAN_GE_SDQPF_RXFIFO_0	   0x4840  /* SDQPF RxFIFO Enable */
++#define	TITAN_GE_SDQPF_TXFIFO_0	   0x4940  /* SDQPF TxFIFO Enable */
++#define TITAN_GE_XDMA_CONFIG       0x5000  /* XDMA Global Configuration */
++#define TITAN_GE_XDMA_INTR_SUMMARY 0x5010  /* XDMA Interrupt Summary */
++#define TITAN_GE_XDMA_BUFADDRPRE   0x5018  /* XDMA Buffer Address Prefix */
++#define TITAN_GE_XDMA_DESCADDRPRE  0x501C  /* XDMA Descriptor Address Prefix */
++#define TITAN_GE_XDMA_PORTWEIGHT   0x502C  /* XDMA Port Weight Configuration */
++
++/* Rx MAC defines */
++#define TITAN_GE_RMAC_CONFIG_1               0x1200  /* RMAC Configuration 1 */
++#define TITAN_GE_RMAC_CONFIG_2               0x1204  /* RMAC Configuration 2 */
++#define TITAN_GE_RMAC_MAX_FRAME_LEN          0x1208  /* RMAC Max Frame Length */
++#define TITAN_GE_RMAC_STATION_HI             0x120C  /* Rx Station Address High */
++#define TITAN_GE_RMAC_STATION_MID            0x1210  /* Rx Station Address Middle */
++#define TITAN_GE_RMAC_STATION_LOW            0x1214  /* Rx Station Address Low */
++#define TITAN_GE_RMAC_LINK_CONFIG            0x1218  /* RMAC Link Configuration */
++
++/* Tx MAC defines */
++#define TITAN_GE_TMAC_CONFIG_1               0x1240  /* TMAC Configuration 1 */
++#define TITAN_GE_TMAC_CONFIG_2               0x1244  /* TMAC Configuration 2 */
++#define TITAN_GE_TMAC_IPG                    0x1248  /* TMAC Inter-Packet Gap */
++#define TITAN_GE_TMAC_STATION_HI             0x124C  /* Tx Station Address High */
++#define TITAN_GE_TMAC_STATION_MID            0x1250  /* Tx Station Address Middle */
++#define TITAN_GE_TMAC_STATION_LOW            0x1254  /* Tx Station Address Low */
++#define TITAN_GE_TMAC_MAX_FRAME_LEN          0x1258  /* TMAC Max Frame Length */
++#define TITAN_GE_TMAC_MIN_FRAME_LEN          0x125C  /* TMAC Min Frame Length */
++#define TITAN_GE_TMAC_PAUSE_FRAME_TIME       0x1260  /* TMAC Pause Frame Time */
++#define TITAN_GE_TMAC_PAUSE_FRAME_INTERVAL   0x1264  /* TMAC Pause Frame Interval */
++
++/* GMII register */
++#define TITAN_GE_GMII_INTERRUPT_STATUS       0x1348  /* GMII Interrupt Status */
++#define TITAN_GE_GMII_CONFIG_GENERAL         0x134C  /* GMII Configuration General */
++#define TITAN_GE_GMII_CONFIG_MODE            0x1350  /* GMII Configuration Mode */
++
++/* Tx and Rx XDMA defines */
++#define	TITAN_GE_INT_COALESCING		     0x5030 /* Interrupt Coalescing */
++#define	TITAN_GE_CHANNEL0_CONFIG	     0x5040 /* Channel 0 XDMA config */
++#define	TITAN_GE_CHANNEL0_INTERRUPT	     0x504c /* Channel 0 Interrupt Status */
++#define	TITAN_GE_GDI_INTERRUPT_ENABLE        0x5050 /* IE for the GDI Errors */
++#define	TITAN_GE_CHANNEL0_PACKET	     0x5060 /* Channel 0 Packet count */
++#define	TITAN_GE_CHANNEL0_BYTE		     0x5064 /* Channel 0 Byte count */
++#define	TITAN_GE_CHANNEL0_TX_DESC	     0x5054 /* Channel 0 Tx first desc */
++#define	TITAN_GE_CHANNEL0_RX_DESC	     0x5058 /* Channel 0 Rx first desc */
++
++/* AFX (Address Filter Exact) register offsets for Slice 0 */
++#define TITAN_GE_AFX_EXACT_MATCH_LOW         0x1100  /* AFX Exact Match Address Low*/
++#define TITAN_GE_AFX_EXACT_MATCH_MID         0x1104  /* AFX Exact Match Address Mid*/
++#define TITAN_GE_AFX_EXACT_MATCH_HIGH        0x1108  /* AFX Exact Match Address Hi */
++#define TITAN_GE_AFX_EXACT_MATCH_VID         0x110C  /* AFX Exact Match VID */
++#define TITAN_GE_AFX_MULTICAST_HASH_LOW      0x1110  /* AFX Multicast HASH Low */
++#define TITAN_GE_AFX_MULTICAST_HASH_MIDLOW   0x1114  /* AFX Multicast HASH MidLow */
++#define TITAN_GE_AFX_MULTICAST_HASH_MIDHI    0x1118  /* AFX Multicast HASH MidHi */
++#define TITAN_GE_AFX_MULTICAST_HASH_HI       0x111C  /* AFX Multicast HASH Hi */
++#define TITAN_GE_AFX_ADDRS_FILTER_CTRL_0     0x1120  /* AFX Address Filter Ctrl 0 */
++#define TITAN_GE_AFX_ADDRS_FILTER_CTRL_1     0x1124  /* AFX Address Filter Ctrl 1 */
++#define TITAN_GE_AFX_ADDRS_FILTER_CTRL_2     0x1128  /* AFX Address Filter Ctrl 2 */
++
++/* Traffic Groomer block */
++#define        TITAN_GE_TRTG_CONFIG	     0x1000  /* TRTG Config */
++
++#endif 				/* _TITAN_GE_H_ */
++
+diff --git a/drivers/net/titan_mdio.c b/drivers/net/titan_mdio.c
+new file mode 100644
+index 0000000..8a8785b
+--- /dev/null
++++ b/drivers/net/titan_mdio.c
+@@ -0,0 +1,217 @@
++/*
++ * drivers/net/titan_mdio.c - Driver for Titan ethernet ports
++ *
++ * Copyright (C) 2003 PMC-Sierra Inc.
++ * Author : Manish Lachwani (lachwani@pmc-sierra.com)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ *
++ * Management Data IO (MDIO) driver for the Titan GMII. Interacts with the Marvel PHY
++ * on the Titan. No support for the TBI as yet.
++ *
++ */
++
++#include	"titan_mdio.h"
++
++#define MDIO_DEBUG
++
++/*
++ * Local constants
++ */
++#define MAX_CLKA            1023
++#define MAX_PHY_DEV         31
++#define MAX_PHY_REG         31
++#define WRITEADDRS_OPCODE   0x0
++#define	READ_OPCODE	    0x2
++#define WRITE_OPCODE        0x1
++#define MAX_MDIO_POLL       100
++
++/*
++ * Titan MDIO and SCMB registers
++ */
++#define TITAN_GE_SCMB_CONTROL                0x01c0  /* SCMB Control */
++#define TITAN_GE_SCMB_CLKA	             0x01c4  /* SCMB Clock A */
++#define TITAN_GE_MDIO_COMMAND                0x01d0  /* MDIO Command */
++#define TITAN_GE_MDIO_DEVICE_PORT_ADDRESS    0x01d4  /* MDIO Device and Port addrs */
++#define TITAN_GE_MDIO_DATA                   0x01d8  /* MDIO Data */
++#define TITAN_GE_MDIO_INTERRUPTS             0x01dC  /* MDIO Interrupts */
++
++/*
++ * Function to poll the MDIO
++ */
++static int titan_ge_mdio_poll(void)
++{
++	int	i, val;
++
++	for (i = 0; i < MAX_MDIO_POLL; i++) {
++		val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND);
++
++		if (!(val & 0x8000))
++			return TITAN_GE_MDIO_GOOD;
++	}
++
++	return TITAN_GE_MDIO_ERROR;
++}
++
++
++/*
++ * Initialize and configure the MDIO
++ */
++int titan_ge_mdio_setup(titan_ge_mdio_config *titan_mdio)
++{
++	unsigned long	val;
++
++	/* Reset the SCMB and program into MDIO mode*/
++	TITAN_GE_MDIO_WRITE(TITAN_GE_SCMB_CONTROL, 0x9000);
++	TITAN_GE_MDIO_WRITE(TITAN_GE_SCMB_CONTROL, 0x1000);
++
++	/* CLK A */
++	val = TITAN_GE_MDIO_READ(TITAN_GE_SCMB_CLKA);
++	val = ( (val & ~(0x03ff)) | (titan_mdio->clka & 0x03ff));
++	TITAN_GE_MDIO_WRITE(TITAN_GE_SCMB_CLKA, val);
++
++	/* Preamble Suppresion */
++	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND);
++	val = ( (val & ~(0x0001)) | (titan_mdio->mdio_spre & 0x0001));
++	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val);
++
++	/* MDIO mode */
++	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS);
++	val = ( (val & ~(0x4000)) | (titan_mdio->mdio_mode & 0x4000));
++	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val);
++
++	return TITAN_GE_MDIO_GOOD;
++}
++
++/*
++ * Set the PHY address in indirect mode
++ */
++int titan_ge_mdio_inaddrs(int dev_addr, int reg_addr)
++{
++	volatile unsigned long	val;
++
++	/* Setup the PHY device */
++	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS);
++	val = ( (val & ~(0x1f00)) | ( (dev_addr << 8) & 0x1f00));
++	val = ( (val & ~(0x001f)) | ( reg_addr & 0x001f));
++	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val);
++
++	/* Write the new address */
++	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND);
++	val = ( (val & ~(0x0300)) | ( (WRITEADDRS_OPCODE << 8) & 0x0300));
++	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val);
++
++	return TITAN_GE_MDIO_GOOD;
++}
++
++/*
++ * Read the MDIO register. This is what the individual parametes mean:
++ *
++ * dev_addr : PHY ID
++ * reg_addr : register offset
++ *
++ * See the spec for the Titan MAC. We operate in the Direct Mode.
++ */
++
++#define MAX_RETRIES	2
++
++int titan_ge_mdio_read(int dev_addr, int reg_addr, unsigned int *pdata)
++{
++	volatile unsigned long	val;
++	int retries = 0;
++
++	/* Setup the PHY device */
++
++again:
++	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS);
++	val = ( (val & ~(0x1f00)) | ( (dev_addr << 8) & 0x1f00));
++	val = ( (val & ~(0x001f)) | ( reg_addr & 0x001f));
++	val |= 0x4000;
++	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val);
++
++	udelay(30);
++
++	/* Issue the read command */
++	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND);
++	val = ( (val & ~(0x0300)) | ( (READ_OPCODE << 8) & 0x0300));
++	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val);
++
++	udelay(30);
++
++	if (titan_ge_mdio_poll() != TITAN_GE_MDIO_GOOD)
++		return TITAN_GE_MDIO_ERROR;
++
++	*pdata = (unsigned int)TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DATA);
++	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_INTERRUPTS);
++
++	udelay(30);
++
++	if (val & 0x2) {
++		if (retries == MAX_RETRIES)
++			return TITAN_GE_MDIO_ERROR;
++		else {
++			retries++;
++			goto again;
++		}
++	}
++
++	return TITAN_GE_MDIO_GOOD;
++}
++
++/*
++ * Write to the MDIO register
++ *
++ * dev_addr : PHY ID
++ * reg_addr : register that needs to be written to
++ *
++ */
++int titan_ge_mdio_write(int dev_addr, int reg_addr, unsigned int data)
++{
++	volatile unsigned long	val;
++
++	if (titan_ge_mdio_poll() != TITAN_GE_MDIO_GOOD)
++		return TITAN_GE_MDIO_ERROR;
++
++	/* Setup the PHY device */
++	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS);
++	val = ( (val & ~(0x1f00)) | ( (dev_addr << 8) & 0x1f00));
++	val = ( (val & ~(0x001f)) | ( reg_addr & 0x001f));
++	val |= 0x4000;
++	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val);
++
++	udelay(30);
++
++	/* Setup the data to write */
++	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DATA, data);
++
++	udelay(30);
++
++	/* Issue the write command */
++	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND);
++	val = ( (val & ~(0x0300)) | ( (WRITE_OPCODE << 8) & 0x0300));
++	TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val);
++
++	udelay(30);
++
++	if (titan_ge_mdio_poll() != TITAN_GE_MDIO_GOOD)
++		return TITAN_GE_MDIO_ERROR;
++
++	val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_INTERRUPTS);
++	if (val & 0x2)
++		return TITAN_GE_MDIO_ERROR;
++
++	return TITAN_GE_MDIO_GOOD;
++}
++
+diff --git a/drivers/net/titan_mdio.h b/drivers/net/titan_mdio.h
+new file mode 100644
+index 0000000..5d23344
+--- /dev/null
++++ b/drivers/net/titan_mdio.h
+@@ -0,0 +1,56 @@
++/*
++ * MDIO used to interact with the PHY when using GMII/MII
++ */
++#ifndef _TITAN_MDIO_H
++#define _TITAN_MDIO_H
++
++#include <linux/netdevice.h>
++#include <linux/workqueue.h>
++#include <linux/delay.h>
++#include "titan_ge.h"
++
++
++#define	TITAN_GE_MDIO_ERROR	(-9000)
++#define	TITAN_GE_MDIO_GOOD	0
++
++#define	TITAN_GE_MDIO_BASE		titan_ge_base
++
++#define	TITAN_GE_MDIO_READ(offset)	\
++	*(volatile u32 *)(titan_ge_base + (offset))
++
++#define	TITAN_GE_MDIO_WRITE(offset, data)	\
++	*(volatile u32 *)(titan_ge_base + (offset)) = (data)
++
++
++/* GMII specific registers */
++#define	TITAN_GE_MARVEL_PHY_ID		0x00
++#define	TITAN_PHY_AUTONEG_ADV		0x04
++#define	TITAN_PHY_LP_ABILITY		0x05
++#define	TITAN_GE_MDIO_MII_CTRL		0x09
++#define	TITAN_GE_MDIO_MII_EXTENDED	0x0f
++#define	TITAN_GE_MDIO_PHY_CTRL		0x10
++#define	TITAN_GE_MDIO_PHY_STATUS	0x11
++#define	TITAN_GE_MDIO_PHY_IE		0x12
++#define	TITAN_GE_MDIO_PHY_IS		0x13
++#define	TITAN_GE_MDIO_PHY_LED		0x18
++#define	TITAN_GE_MDIO_PHY_LED_OVER	0x19
++#define	PHY_ANEG_TIME_WAIT		45	/* 45 seconds wait time */
++
++/*
++ * MDIO Config Structure
++ */
++typedef struct {
++	unsigned int		clka;
++	int			mdio_spre;
++	int			mdio_mode;
++} titan_ge_mdio_config;
++
++/*
++ * Function Prototypes
++ */
++int titan_ge_mdio_setup(titan_ge_mdio_config *);
++int titan_ge_mdio_inaddrs(int, int);
++int titan_ge_mdio_read(int, int, unsigned int *);
++int titan_ge_mdio_write(int, int, unsigned int);
++
++#endif /* _TITAN_MDIO_H */
+diff --git a/drivers/net/wireless/rtl818x/rtl8187/rfkill.c b/drivers/net/wireless/rtl818x/rtl8187/rfkill.c
+index 3411671..4d252c1 100644
+--- a/drivers/net/wireless/rtl818x/rtl8187/rfkill.c
++++ b/drivers/net/wireless/rtl818x/rtl8187/rfkill.c
+@@ -22,6 +22,10 @@
+ 
+ static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv)
+ {
++#ifdef CONFIG_LEMOTE_MACH2F
++	/* Allow users to activate rfkill through only the /sys interface */
++	return 1;
++#else
+ 	u8 gpio;
+ 
+ 	gpio = rtl818x_ioread8(priv, &priv->map->GPIO0);
+@@ -29,6 +33,7 @@ static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv)
+ 	gpio = rtl818x_ioread8(priv, &priv->map->GPIO1);
+ 
+ 	return gpio & priv->rfkill_mask;
++#endif
+ }
+ 
+ void rtl8187_rfkill_init(struct ieee80211_hw *hw)
+diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
+index 09fde58..eacabd1 100644
+--- a/drivers/platform/Kconfig
++++ b/drivers/platform/Kconfig
+@@ -4,5 +4,8 @@ endif
+ if GOLDFISH
+ source "drivers/platform/goldfish/Kconfig"
+ endif
++if MIPS
++source "drivers/platform/mips/Kconfig"
++endif
+ 
+ source "drivers/platform/chrome/Kconfig"
+diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
+index 3656b7b..ca26925 100644
+--- a/drivers/platform/Makefile
++++ b/drivers/platform/Makefile
+@@ -3,6 +3,7 @@
+ #
+ 
+ obj-$(CONFIG_X86)		+= x86/
++obj-$(CONFIG_MIPS)		+= mips/
+ obj-$(CONFIG_OLPC)		+= olpc/
+ obj-$(CONFIG_GOLDFISH)		+= goldfish/
+ obj-$(CONFIG_CHROME_PLATFORMS)	+= chrome/
+diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
+new file mode 100644
+index 0000000..722d690
+--- /dev/null
++++ b/drivers/platform/mips/Kconfig
+@@ -0,0 +1,60 @@
++#
++# MIPS Platform Specific Drivers
++#
++
++menuconfig MIPS_PLATFORM_DEVICES
++	bool "MIPS Platform Specific Device Drivers"
++	default y
++	help
++	  Say Y here to get to see options for device drivers of various
++	  MIPS platforms, including vendor-specific netbook/laptop/pc extension
++	  drivers.  This option alone does not add any kernel code.
++
++	  If you say N, all options in this submenu will be skipped and disabled.
++
++if MIPS_PLATFORM_DEVICES
++
++config LEMOTE_YEELOONG2F
++	tristate "Lemote YeeLoong Laptop"
++	depends on LEMOTE_MACH2F
++	select BACKLIGHT_LCD_SUPPORT
++	select LCD_CLASS_DEVICE
++	select BACKLIGHT_CLASS_DEVICE
++	select POWER_SUPPLY
++	select HWMON
++	select VIDEO_OUTPUT_CONTROL
++	select INPUT_SPARSEKMAP
++	select INPUT_EVDEV
++	depends on INPUT
++	default m
++	help
++	  YeeLoong netbook is a mini laptop made by Lemote, which is basically
++	  compatible to FuLoong2F mini PC, but it has an extra Embedded
++	  Controller(kb3310b) for battery, hotkey, backlight, temperature and
++	  fan management.
++
++config LEMOTE_LYNLOONG2F
++	tristate "Lemote LynLoong PC"
++	depends on LEMOTE_MACH2F
++	select BACKLIGHT_LCD_SUPPORT
++	select BACKLIGHT_CLASS_DEVICE
++	select VIDEO_OUTPUT_CONTROL
++	default m
++	help
++	  LynLoong PC is an AllINONE machine made by Lemote, which is basically
++	  compatible to FuLoong2F Mini PC, the only difference is that it has a
++	  size-fixed screen: 1360x768 with sisfb video driver. and also, it has
++	  its own specific suspend support.
++
++config GDIUM_LAPTOP
++	tristate "GDIUM laptop extras"
++	depends on DEXXON_GDIUM
++	select POWER_SUPPLY
++	select I2C
++	select INPUT_POLLDEV
++	default m
++	help
++	  This mini-driver drives the ST7 chipset present in the Gdium laptops.
++	  This gives battery support, wlan rfkill.
++
++endif # MIPS_PLATFORM_DEVICES
+diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile
+new file mode 100644
+index 0000000..f013e78
+--- /dev/null
++++ b/drivers/platform/mips/Makefile
+@@ -0,0 +1,9 @@
++#
++# Makefile for MIPS Platform-Specific Drivers
++#
++
++obj-$(CONFIG_LEMOTE_YEELOONG2F)	+= yeeloong_laptop.o # yeeloong_ecrom.o
++CFLAGS_yeeloong_laptop.o = -I$(srctree)/arch/mips/loongson/lemote-2f
++
++obj-$(CONFIG_LEMOTE_LYNLOONG2F)	+= lynloong_pc.o
++obj-$(CONFIG_GDIUM_LAPTOP)	+= gdium_laptop.o
+diff --git a/drivers/platform/mips/gdium_laptop.c b/drivers/platform/mips/gdium_laptop.c
+new file mode 100644
+index 0000000..41a65ad
+--- /dev/null
++++ b/drivers/platform/mips/gdium_laptop.c
+@@ -0,0 +1,927 @@
++/*
++ * gdium_laptop  --  Gdium laptop extras
++ *
++ * Arnaud Patard <apatard@mandriva.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/init.h>
++#include <linux/platform_device.h>
++#include <linux/input-polldev.h>
++#include <linux/debugfs.h>
++#include <linux/seq_file.h>
++#include <linux/i2c.h>
++#include <linux/mutex.h>
++#include <linux/power_supply.h>
++#include <linux/workqueue.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <asm/gpio.h>
++
++/* For input device */
++#define SCAN_INTERVAL		150
++
++/* For battery status */
++#define BAT_SCAN_INTERVAL	500
++
++#define EC_FIRM_VERSION		0
++
++#if CONFIG_GDIUM_VERSION > 2
++#define EC_REG_BASE		1
++#else
++#define EC_REG_BASE		0
++#endif
++
++#define EC_STATUS		(EC_REG_BASE+0)
++#define EC_STATUS_LID		(1<<0)
++#define EC_STATUS_PWRBUT	(1<<1)
++#define EC_STATUS_BATID		(1<<2)		/* this bit has no real meaning on v2.         */
++						/* Same as EC_STATUS_ADAPT                     */
++						/* but on v3 it's BATID which mean bat present */
++#define EC_STATUS_SYS_POWER	(1<<3)
++#define EC_STATUS_WLAN		(1<<4)
++#define EC_STATUS_ADAPT		(1<<5)
++
++#define EC_CTRL			(EC_REG_BASE+1)
++#define EC_CTRL_DDR_CLK		(1<<0)
++#define EC_CTRL_CHARGE_LED	(1<<1)
++#define EC_CTRL_BEEP		(1<<2)
++#define EC_CTRL_SUSB		(1<<3)	/* memory power */
++#define EC_CTRL_TRICKLE		(1<<4)
++#define EC_CTRL_WLAN_EN		(1<<5)
++#define EC_CTRL_SUSC		(1<<6) /* main power */
++#define EC_CTRL_CHARGE_EN	(1<<7)
++
++#define EC_BAT_LOW		(EC_REG_BASE+2)
++#define EC_BAT_HIGH		(EC_REG_BASE+3)
++
++#define EC_SIGN			(EC_REG_BASE+4)
++#define EC_SIGN_OS		0xAE /* write 0xae to control pm stuff */
++#define EC_SIGN_EC		0x00 /* write 0x00 to let the st7 manage pm stuff */
++
++#if 0
++#define EC_TEST			(EC_REG_BASE+5) /* Depending on firmware version this register */
++						/* may be the programmation register so don't play */
++						/* with it */
++#endif
++
++#define BAT_VOLT_PRESENT	500000	/* Min voltage to consider battery present uV */
++#define BAT_MIN			7000000	/* Min battery voltage in uV */
++#define BAT_MIN_MV		7000	/* Min battery voltage in mV */
++#define BAT_TRICKLE_EN		8000000	/* Charging at 1.4A before  8.0V and then charging at 0.25A */
++#define BAT_MAX			7950000	/* Max battery voltage ~8V in V */
++#define BAT_MAX_MV		7950	/* Max battery voltage ~8V in V */
++#define BAT_READ_ERROR		300000	/* battery read error of 0.3V */
++#define BAT_READ_ERROR_MV	300	/* battery read error of 0.3V */
++
++#define SM502_WLAN_ON		(224+16)/* SM502 GPIO16 may be used on gdium v2 (v3?) as wlan_on */
++					/* when R422 is connected */
++
++static unsigned char verbose;
++static unsigned char gpio16;
++static unsigned char ec;
++module_param(verbose, byte, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(verbose, "Add some debugging messages");
++module_param(gpio16, byte, S_IRUGO);
++MODULE_PARM_DESC(gpio16, "Enable wlan_on signal on SM502");
++module_param(ec, byte, S_IRUGO);
++MODULE_PARM_DESC(ec, "Let the ST7 handle the battery (default OS)");
++
++struct gdium_laptop_data {
++	struct i2c_client		*client;
++	struct input_polled_dev		*input_polldev;
++	struct dentry			*debugfs;
++	struct mutex			mutex;
++	struct platform_device		*bat_pdev;
++	struct power_supply		gdium_ac;
++	struct power_supply		gdium_battery;
++	struct workqueue_struct		*workqueue;
++	struct delayed_work		work;
++	char				charge_cmd;
++	/* important registers value */
++	char				status;
++	char				ctrl;
++	/* mV */
++	int				battery_level;
++	char				version;
++};
++
++/**********************************************************************/
++/* Low level I2C functions                                            */
++/* All are supposed to be called with mutex held                      */
++/**********************************************************************/
++/*
++ * Return battery voltage in mV
++ * >= 0 battery voltage
++ * < 0 error
++ */
++static s32 ec_read_battery(struct i2c_client *client)
++{
++	unsigned char bat_low, bat_high;
++	s32 data;
++	unsigned int ret;
++
++	/*
++	 * a = battery high
++	 * b = battery low
++	 * bat = a << 2 | b & 0x03;
++	 * battery voltage = (bat / 1024) * 5 * 2
++	 */
++	data = i2c_smbus_read_byte_data(client, EC_BAT_LOW);
++	if (data < 0) {
++		dev_err(&client->dev, "ec_read_bat: read bat_low failed\n");
++		return data;
++	}
++	bat_low = data & 0xff;
++	if (verbose)
++		dev_info(&client->dev, "bat_low %x\n", bat_low);
++
++	data = i2c_smbus_read_byte_data(client, EC_BAT_HIGH);
++	if (data < 0) {
++		dev_err(&client->dev, "ec_read_bat: read bat_high failed\n");
++		return data;
++	}
++	bat_high = data & 0xff;
++	if (verbose)
++		dev_info(&client->dev, "bat_high %x\n", bat_high);
++
++	ret = (bat_high << 2) | (bat_low & 3);
++	/*
++	 * mV
++	 */
++	ret = (ret * 5 * 2) * 1000 / 1024;
++
++	return ret;
++}
++
++static s32 ec_read_version(struct i2c_client *client)
++{
++#if CONFIG_GDIUM_VERSION > 2
++	return i2c_smbus_read_byte_data(client, EC_FIRM_VERSION);
++#else
++	return 0;
++#endif
++}
++
++static s32 ec_read_status(struct i2c_client *client)
++{
++	return i2c_smbus_read_byte_data(client, EC_STATUS);
++}
++
++static s32 ec_read_ctrl(struct i2c_client *client)
++{
++	return i2c_smbus_read_byte_data(client, EC_CTRL);
++}
++
++static s32 ec_write_ctrl(struct i2c_client *client, unsigned char newvalue)
++{
++	return i2c_smbus_write_byte_data(client, EC_CTRL, newvalue);
++}
++
++static s32 ec_read_sign(struct i2c_client *client)
++{
++	return i2c_smbus_read_byte_data(client, EC_SIGN);
++}
++
++static s32 ec_write_sign(struct i2c_client *client, unsigned char sign)
++{
++	unsigned char value;
++	s32 ret;
++
++	ret = i2c_smbus_write_byte_data(client, EC_SIGN, sign);
++	if (ret < 0) {
++		dev_err(&client->dev, "ec_set_control: write failed\n");
++		return ret;
++	}
++
++	value = ec_read_sign(client);
++	if (value != sign) {
++		dev_err(&client->dev, "Failed to set control to %s\n",
++				sign == EC_SIGN_OS ? "OS" : "EC");
++		return -EIO;
++	}
++
++	return 0;
++}
++
++#if 0
++static int ec_power_off(struct i2c_client *client)
++{
++	char value;
++	int ret;
++
++	value = ec_read_ctrl(client);
++	if (value < 0) {
++		dev_err(&client->dev, "ec_power_off: read failed\n");
++		return value;
++	}
++	value &= ~(EC_CTRL_SUSB | EC_CTRL_SUSC);
++	ret = ec_write_ctrl(client, value);
++	if (ret < 0) {
++		dev_err(&client->dev, "ec_power_off: write failed\n");
++		return ret;
++	}
++
++	return 0;
++}
++#endif
++
++static s32 ec_wlan_status(struct i2c_client *client)
++{
++	s32 value;
++
++	value = ec_read_ctrl(client);
++	if (value < 0)
++		return value;
++
++	return (value & EC_CTRL_WLAN_EN) ? 1 : 0;
++}
++
++static s32 ec_wlan_en(struct i2c_client *client, int on)
++{
++	s32 value;
++
++	value = ec_read_ctrl(client);
++	if (value < 0)
++		return value;
++
++	value &= ~EC_CTRL_WLAN_EN;
++	if (on)
++		value |= EC_CTRL_WLAN_EN;
++
++	return ec_write_ctrl(client, value&0xff);
++}
++
++#if 0
++static s32 ec_led_status(struct i2c_client *client)
++{
++	s32 value;
++
++	value = ec_read_ctrl(client);
++	if (value < 0)
++		return value;
++
++	return (value & EC_CTRL_CHARGE_LED) ? 1 : 0;
++}
++#endif
++
++/* Changing the charging led status has never worked */
++static s32 ec_led_en(struct i2c_client *client, int on)
++{
++#if 0
++	s32 value;
++
++	value = ec_read_ctrl(client);
++	if (value < 0)
++		return value;
++
++	value &= ~EC_CTRL_CHARGE_LED;
++	if (on)
++		value |= EC_CTRL_CHARGE_LED;
++	return ec_write_ctrl(client, value&0xff);
++#else
++	return 0;
++#endif
++}
++
++static s32 ec_charge_en(struct i2c_client *client, int on, int trickle)
++{
++	s32 value;
++	s32 set = 0;
++
++	value = ec_read_ctrl(client);
++	if (value < 0)
++		return value;
++
++	if (on)
++		set |= EC_CTRL_CHARGE_EN;
++	if (trickle)
++		set |= EC_CTRL_TRICKLE;
++
++	/* Be clever : don't change values if you don't need to */
++	if ((value & (EC_CTRL_CHARGE_EN | EC_CTRL_TRICKLE)) == set)
++		return 0;
++
++	value &= ~(EC_CTRL_CHARGE_EN | EC_CTRL_TRICKLE);
++	value |= set;
++	ec_led_en(client, on);
++	return ec_write_ctrl(client, (unsigned char)(value&0xff));
++
++}
++
++/**********************************************************************/
++/* Input functions                                                    */
++/**********************************************************************/
++struct gdium_keys {
++	int last_state;
++	int key_code;
++	int mask;
++	int type;
++};
++
++static struct gdium_keys gkeys[] = {
++	{
++		.key_code	= KEY_WLAN,
++		.mask		= EC_STATUS_WLAN,
++		.type		= EV_KEY,
++	},
++	{
++		.key_code	= KEY_POWER,
++		.mask		= EC_STATUS_PWRBUT,
++		.type		= EV_KEY, /*EV_PWR,*/
++	},
++	{
++		.key_code	= SW_LID,
++		.mask		= EC_STATUS_LID,
++		.type		= EV_SW,
++	},
++};
++
++static void gdium_laptop_keys_poll(struct input_polled_dev *dev)
++{
++	int state, i;
++	struct gdium_laptop_data *data = dev->private;
++	struct i2c_client *client = data->client;
++	struct input_dev *input = dev->input;
++	s32 status;
++
++	mutex_lock(&data->mutex);
++	status = ec_read_status(client);
++	mutex_unlock(&data->mutex);
++
++	if (status < 0) {
++		/*
++		 * Don't know exactly  which version of the firmware
++		 * has this bug but when the power button is pressed
++		 * there are i2c read errors :(
++		 */
++		if ((data->version >= 0x13) && !gkeys[1].last_state) {
++			input_event(input, EV_KEY, KEY_POWER, 1);
++			input_sync(input);
++			gkeys[1].last_state = 1;
++		}
++		return;
++	}
++
++	for (i = 0; i < ARRAY_SIZE(gkeys); i++) {
++		state = status & gkeys[i].mask;
++		if (state != gkeys[i].last_state) {
++			gkeys[i].last_state = state;
++			/* for power key, we want power & key press/release event */
++			if (gkeys[i].type == EV_PWR) {
++				input_event(input, EV_KEY, gkeys[i].key_code, !!state);
++				input_sync(input);
++			}
++			/* Disable wifi on key press but not key release */
++			/*
++			 * On firmware >= 0x13 the EC_STATUS_WLAN has it's
++			 * original meaning of Wifi status and no more the
++			 * wifi button status so we have to ignore the event
++			 * on theses versions
++			 */
++			if (state && (gkeys[i].key_code == KEY_WLAN) && (data->version < 0x13)) {
++				mutex_lock(&data->mutex);
++				ec_wlan_en(client, !ec_wlan_status(client));
++				if (gpio16)
++					gpio_set_value(SM502_WLAN_ON, !ec_wlan_status(client));
++				mutex_unlock(&data->mutex);
++			}
++
++			input_event(input, gkeys[i].type, gkeys[i].key_code, !!state);
++			input_sync(input);
++		}
++	}
++}
++
++static int gdium_laptop_input_init(struct gdium_laptop_data *data)
++{
++	struct i2c_client *client = data->client;
++	struct input_dev *input;
++	int ret, i;
++
++	data->input_polldev = input_allocate_polled_device();
++	if (!data->input_polldev) {
++		ret = -ENOMEM;
++		goto err;
++	}
++
++	input = data->input_polldev->input;
++	input->evbit[0] = BIT(EV_KEY) | BIT_MASK(EV_PWR) | BIT_MASK(EV_SW);
++	data->input_polldev->poll = gdium_laptop_keys_poll;
++	data->input_polldev->poll_interval = SCAN_INTERVAL;
++	data->input_polldev->private = data;
++	input->name = "gdium-keys";
++	input->dev.parent = &client->dev;
++
++	input->id.bustype = BUS_HOST;
++	input->id.vendor = 0x0001;
++	input->id.product = 0x0001;
++	input->id.version = 0x0100;
++
++	for (i = 0; i < ARRAY_SIZE(gkeys); i++)
++		input_set_capability(input, gkeys[i].type, gkeys[i].key_code);
++
++	ret = input_register_polled_device(data->input_polldev);
++	if (ret) {
++		dev_err(&client->dev, "Unable to register button device\n");
++		goto err_poll_dev;
++	}
++
++	return 0;
++
++err_poll_dev:
++	input_free_polled_device(data->input_polldev);
++err:
++	return ret;
++}
++
++static void gdium_laptop_input_exit(struct gdium_laptop_data *data)
++{
++	input_unregister_polled_device(data->input_polldev);
++	input_free_polled_device(data->input_polldev);
++}
++
++/**********************************************************************/
++/* Battery management                                                 */
++/**********************************************************************/
++static int gdium_ac_get_props(struct power_supply *psy,
++		enum power_supply_property psp,
++		union power_supply_propval *val)
++{
++	char status;
++	struct gdium_laptop_data *data = container_of(psy, struct gdium_laptop_data, gdium_ac);
++	int ret = 0;
++
++	if (!data) {
++		pr_err("gdium-ac: gdium_laptop_data not found\n");
++		return -EINVAL;
++	}
++
++	status = data->status;
++	switch (psp) {
++	case POWER_SUPPLY_PROP_ONLINE:
++		val->intval = !!(status & EC_STATUS_ADAPT);
++		break;
++	default:
++		ret = -EINVAL;
++		break;
++	}
++
++	return ret;
++}
++
++#undef RET
++#define RET (val->intval)
++
++static int gdium_battery_get_props(struct power_supply *psy,
++		enum power_supply_property psp,
++		union power_supply_propval *val)
++{
++	char status, ctrl;
++	struct gdium_laptop_data *data = container_of(psy, struct gdium_laptop_data, gdium_battery);
++	int percentage_capacity = 0, charge_now = 0, time_to_empty = 0;
++	int ret = 0, tmp;
++
++	if (!data) {
++		pr_err("gdium-battery: gdium_laptop_data not found\n");
++		return -EINVAL;
++	}
++
++	status = data->status;
++	ctrl   = data->ctrl;
++	switch (psp) {
++	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
++		/* uAh */
++		RET = 5000000;
++		break;
++	case POWER_SUPPLY_PROP_CURRENT_NOW:
++	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
++		/* This formula is gotten by gnuplot with the statistic data */
++		time_to_empty = (data->battery_level - BAT_MIN_MV + BAT_READ_ERROR_MV) * 113 - 29870;
++		if (psp == POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW) {
++			/* seconds */
++			RET = time_to_empty / 10;
++			break;
++		}
++		/* fall through */
++	case POWER_SUPPLY_PROP_CHARGE_NOW:
++	case POWER_SUPPLY_PROP_CAPACITY: {
++		tmp = data->battery_level * 1000;
++		/* > BAT_MIN to avoid negative values */
++		percentage_capacity = 0;
++		if ((status & EC_STATUS_BATID) && (tmp > BAT_MIN))
++			percentage_capacity = (tmp-BAT_MIN)*100/(BAT_MAX-BAT_MIN);
++
++		if (percentage_capacity > 100)
++			percentage_capacity = 100;
++
++		if (psp == POWER_SUPPLY_PROP_CAPACITY) {
++			RET = percentage_capacity;
++			break;
++		}
++		charge_now = 50000 * percentage_capacity;
++		if (psp == POWER_SUPPLY_PROP_CHARGE_NOW) {
++			/* uAh */
++			RET = charge_now;
++			break;
++		}
++	}	/* fall through */
++	case POWER_SUPPLY_PROP_STATUS: {
++		if (status & EC_STATUS_ADAPT)
++			if (ctrl & EC_CTRL_CHARGE_EN)
++				RET = POWER_SUPPLY_STATUS_CHARGING;
++			else
++				RET = POWER_SUPPLY_STATUS_NOT_CHARGING;
++		else
++			RET = POWER_SUPPLY_STATUS_DISCHARGING;
++
++		if (psp == POWER_SUPPLY_PROP_STATUS)
++			break;
++		/* mAh -> µA */
++		switch (RET) {
++		case POWER_SUPPLY_STATUS_CHARGING:
++			RET = -(data->charge_cmd == 2) ? 1400000 : 250000;
++			break;
++		case POWER_SUPPLY_STATUS_DISCHARGING:
++			RET = charge_now / time_to_empty * 36000;
++			break;
++		case POWER_SUPPLY_STATUS_NOT_CHARGING:
++		default:
++			RET = 0;
++			break;
++		}
++	} break;
++	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
++		RET = BAT_MAX+BAT_READ_ERROR;
++		break;
++	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
++		RET = BAT_MIN-BAT_READ_ERROR;
++		break;
++	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
++		/* mV -> uV */
++		RET = data->battery_level * 1000;
++		break;
++	case POWER_SUPPLY_PROP_PRESENT:
++#if CONFIG_GDIUM_VERSION > 2
++		RET = !!(status & EC_STATUS_BATID);
++#else
++		RET = !!(data->battery_level > BAT_VOLT_PRESENT);
++#endif
++		break;
++	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
++		tmp = data->battery_level * 1000;
++		RET = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
++		if (status & EC_STATUS_BATID) {
++			if (tmp >= BAT_MAX) {
++				RET = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
++				if (tmp >= BAT_MAX+BAT_READ_ERROR)
++					RET = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
++			} else if (tmp <= BAT_MIN+BAT_READ_ERROR) {
++				RET = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
++				if (tmp <= BAT_MIN)
++					RET = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
++			} else
++				RET = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
++		}
++		break;
++	case POWER_SUPPLY_PROP_CHARGE_TYPE:
++		if (ctrl & EC_CTRL_TRICKLE)
++			RET = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
++		else if (ctrl & EC_CTRL_CHARGE_EN)
++			RET = POWER_SUPPLY_CHARGE_TYPE_FAST;
++		else
++			RET = POWER_SUPPLY_CHARGE_TYPE_NONE;
++		break;
++	case POWER_SUPPLY_PROP_CURRENT_MAX:
++		/* 1.4A ? */
++		RET = 1400000;
++		break;
++	default:
++		break;
++	}
++
++	return ret;
++}
++#undef RET
++
++static enum power_supply_property gdium_ac_props[] = {
++	POWER_SUPPLY_PROP_ONLINE,
++};
++
++static enum power_supply_property gdium_battery_props[] = {
++	POWER_SUPPLY_PROP_STATUS,
++	POWER_SUPPLY_PROP_PRESENT,
++	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
++	POWER_SUPPLY_PROP_CHARGE_NOW,
++	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
++	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
++	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
++	POWER_SUPPLY_PROP_VOLTAGE_NOW,
++	POWER_SUPPLY_PROP_CURRENT_MAX,
++	POWER_SUPPLY_PROP_CURRENT_NOW,
++	POWER_SUPPLY_PROP_CAPACITY,
++	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
++	POWER_SUPPLY_PROP_CHARGE_TYPE,
++};
++
++static void gdium_laptop_battery_work(struct work_struct *work)
++{
++	struct gdium_laptop_data *data = container_of(work, struct gdium_laptop_data, work.work);
++	struct i2c_client *client;
++	int ret;
++	char old_status, old_charge_cmd;
++	char present;
++	s32 status;
++
++	mutex_lock(&data->mutex);
++	client	= data->client;
++	status	= ec_read_status(client);
++	ret	= ec_read_battery(client);
++
++	if ((status < 0) || (ret < 0))
++		goto i2c_read_error;
++
++	old_status = data->status;
++	old_charge_cmd = data->charge_cmd;
++	data->status = status;
++
++	/*
++	 * Charge only if :
++	 * - battery present
++	 * - ac adapter plugged in
++	 * - battery not fully charged
++	 */
++#if CONFIG_GDIUM_VERSION > 2
++	present = !!(data->status & EC_STATUS_BATID);
++#else
++	present = !!(ret > BAT_VOLT_PRESENT);
++#endif
++	data->battery_level = 0;
++	if (present) {
++		data->battery_level = (unsigned int)ret;
++		if (data->status & EC_STATUS_ADAPT)
++			data->battery_level -= BAT_READ_ERROR_MV;
++	}
++
++	data->charge_cmd = 0;
++	if ((data->status & EC_STATUS_ADAPT) && present && (data->battery_level <= BAT_MAX_MV))
++		data->charge_cmd = (ret < BAT_TRICKLE_EN) ? 2 : 3;
++
++	ec_charge_en(client, (data->charge_cmd >> 1) & 1, data->charge_cmd & 1);
++
++	/*
++	 * data->ctrl must be set _after_ calling ec_charge_en as this will change the
++	 * control register content
++	 */
++	data->ctrl = ec_read_ctrl(client);
++
++	if ((data->status & EC_STATUS_ADAPT) != (old_status & EC_STATUS_ADAPT)) {
++		power_supply_changed(&data->gdium_ac);
++		/* Send charging/discharging state change */
++		power_supply_changed(&data->gdium_battery);
++	} else if ((data->status & EC_STATUS_ADAPT) &&
++			((old_charge_cmd&2) != (data->charge_cmd&2)))
++		power_supply_changed(&data->gdium_battery);
++
++i2c_read_error:
++	mutex_unlock(&data->mutex);
++	queue_delayed_work(data->workqueue, &data->work, msecs_to_jiffies(BAT_SCAN_INTERVAL));
++}
++
++static int gdium_laptop_battery_init(struct gdium_laptop_data *data)
++{
++	int ret;
++
++	data->bat_pdev = platform_device_register_simple("gdium-battery", 0, NULL, 0);
++	if (IS_ERR(data->bat_pdev))
++		return PTR_ERR(data->bat_pdev);
++
++	data->gdium_battery.name		= data->bat_pdev->name;
++	data->gdium_battery.properties		= gdium_battery_props;
++	data->gdium_battery.num_properties	= ARRAY_SIZE(gdium_battery_props);
++	data->gdium_battery.get_property	= gdium_battery_get_props;
++	data->gdium_battery.use_for_apm		= 1;
++
++	ret = power_supply_register(&data->bat_pdev->dev, &data->gdium_battery);
++	if (ret)
++		goto err_platform;
++
++	data->gdium_ac.name			= "gdium-ac";
++	data->gdium_ac.type			= POWER_SUPPLY_TYPE_MAINS;
++	data->gdium_ac.properties		= gdium_ac_props;
++	data->gdium_ac.num_properties		= ARRAY_SIZE(gdium_ac_props);
++	data->gdium_ac.get_property		= gdium_ac_get_props;
++/*	data->gdium_ac.use_for_apm_ac		= 1,	*/
++
++	ret = power_supply_register(&data->bat_pdev->dev, &data->gdium_ac);
++	if (ret)
++		goto err_battery;
++
++	if (!ec) {
++		INIT_DELAYED_WORK(&data->work, gdium_laptop_battery_work);
++		data->workqueue = create_singlethread_workqueue("gdium-battery-work");
++		if (!data->workqueue) {
++			ret = -ESRCH;
++			goto err_work;
++		}
++		queue_delayed_work(data->workqueue, &data->work, msecs_to_jiffies(BAT_SCAN_INTERVAL));
++	}
++
++	return 0;
++
++err_work:
++err_battery:
++	power_supply_unregister(&data->gdium_battery);
++err_platform:
++	platform_device_unregister(data->bat_pdev);
++
++	return ret;
++}
++static void gdium_laptop_battery_exit(struct gdium_laptop_data *data)
++{
++	if (!ec) {
++		cancel_rearming_delayed_workqueue(data->workqueue, &data->work);
++		destroy_workqueue(data->workqueue);
++	}
++	power_supply_unregister(&data->gdium_battery);
++	power_supply_unregister(&data->gdium_ac);
++	platform_device_unregister(data->bat_pdev);
++}
++
++/* Debug fs */
++static int gdium_laptop_regs_show(struct seq_file *s, void *p)
++{
++	struct gdium_laptop_data *data = s->private;
++	struct i2c_client *client = data->client;
++
++	mutex_lock(&data->mutex);
++	seq_printf(s, "Version    : 0x%02x\n", (unsigned char)ec_read_version(client));
++	seq_printf(s, "Status     : 0x%02x\n", (unsigned char)ec_read_status(client));
++	seq_printf(s, "Ctrl       : 0x%02x\n", (unsigned char)ec_read_ctrl(client));
++	seq_printf(s, "Sign       : 0x%02x\n", (unsigned char)ec_read_sign(client));
++	seq_printf(s, "Bat Lo     : 0x%02x\n", (unsigned char)i2c_smbus_read_byte_data(client, EC_BAT_LOW));
++	seq_printf(s, "Bat Hi     : 0x%02x\n", (unsigned char)i2c_smbus_read_byte_data(client, EC_BAT_HIGH));
++	seq_printf(s, "Battery    : %d uV\n",  (unsigned int)ec_read_battery(client) * 1000);
++	seq_printf(s, "Charge cmd : %s %s\n", data->charge_cmd & 2 ? "C" : " ", data->charge_cmd & 1 ? "T" : " ");
++
++	mutex_unlock(&data->mutex);
++	return 0;
++}
++
++static int gdium_laptop_regs_open(struct inode *inode,
++					 struct file *file)
++{
++	return single_open(file, gdium_laptop_regs_show, inode->i_private);
++}
++
++static const struct file_operations gdium_laptop_regs_fops = {
++	.open		= gdium_laptop_regs_open,
++	.read		= seq_read,
++	.llseek		= seq_lseek,
++	.release	= single_release,
++	.owner		= THIS_MODULE,
++};
++
++
++static int gdium_laptop_probe(struct i2c_client *client, const struct i2c_device_id *id)
++{
++	struct gdium_laptop_data *data;
++	int ret;
++
++	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
++		dev_err(&client->dev,
++				"%s: no smbus_byte support !\n", __func__);
++		return -ENODEV;
++	}
++
++	data = kzalloc(sizeof(struct gdium_laptop_data), GFP_KERNEL);
++	if (!data)
++		return -ENOMEM;
++
++	i2c_set_clientdata(client, data);
++	data->client = client;
++	mutex_init(&data->mutex);
++
++	ret = ec_read_version(client);
++	if (ret < 0)
++		goto err_alloc;
++
++	data->version = (unsigned char)ret;
++
++	ret = gdium_laptop_input_init(data);
++	if (ret)
++		goto err_alloc;
++
++	ret = gdium_laptop_battery_init(data);
++	if (ret)
++		goto err_input;
++
++
++	if (!ec) {
++		ret = ec_write_sign(client, EC_SIGN_OS);
++		if (ret)
++			goto err_sign;
++	}
++
++	if (gpio16) {
++		ret = gpio_request(SM502_WLAN_ON, "wlan-on");
++		if (ret < 0)
++			goto err_sign;
++		gpio_set_value(SM502_WLAN_ON, ec_wlan_status(client));
++		gpio_direction_output(SM502_WLAN_ON, 1);
++	}
++
++	dev_info(&client->dev, "Found firmware 0x%02x\n", data->version);
++	data->debugfs = debugfs_create_file("gdium_laptop", S_IFREG | S_IRUGO,
++				NULL, data, &gdium_laptop_regs_fops);
++
++	return 0;
++
++err_sign:
++	gdium_laptop_battery_exit(data);
++err_input:
++	gdium_laptop_input_exit(data);
++err_alloc:
++	kfree(data);
++	return ret;
++}
++
++static int gdium_laptop_remove(struct i2c_client *client)
++{
++	struct gdium_laptop_data *data = i2c_get_clientdata(client);
++
++	if (gpio16)
++		gpio_free(SM502_WLAN_ON);
++	ec_write_sign(client, EC_SIGN_EC);
++	if (data->debugfs)
++		debugfs_remove(data->debugfs);
++
++	gdium_laptop_battery_exit(data);
++	gdium_laptop_input_exit(data);
++
++	kfree(data);
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int gdium_laptop_suspend(struct i2c_client *client, pm_message_t msg)
++{
++	struct gdium_laptop_data *data = i2c_get_clientdata(client);
++
++	if (!ec)
++		cancel_rearming_delayed_workqueue(data->workqueue, &data->work);
++	return 0;
++}
++
++static int gdium_laptop_resume(struct i2c_client *client)
++{
++	struct gdium_laptop_data *data = i2c_get_clientdata(client);
++
++	if (!ec)
++		queue_delayed_work(data->workqueue, &data->work, msecs_to_jiffies(BAT_SCAN_INTERVAL));
++	return 0;
++}
++#else
++#define gdium_laptop_suspend NULL
++#define gdium_laptop_resume NULL
++#endif
++static const struct i2c_device_id gdium_id[] = {
++	{ "gdium-laptop" },
++	{},
++};
++MODULE_DEVICE_TABLE(i2c, gdium_id);
++
++static struct i2c_driver gdium_laptop_driver = {
++	.driver = {
++		.name = "gdium-laptop",
++		.owner = THIS_MODULE,
++	},
++	.probe = gdium_laptop_probe,
++	.remove = gdium_laptop_remove,
++	.shutdown = gdium_laptop_remove,
++	.suspend = gdium_laptop_suspend,
++	.resume = gdium_laptop_resume,
++	.id_table = gdium_id,
++};
++
++static int __init gdium_laptop_init(void)
++{
++	return i2c_add_driver(&gdium_laptop_driver);
++}
++
++static void __exit gdium_laptop_exit(void)
++{
++	i2c_del_driver(&gdium_laptop_driver);
++}
++
++module_init(gdium_laptop_init);
++module_exit(gdium_laptop_exit);
++
++MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
++MODULE_DESCRIPTION("Gdium laptop extras");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/platform/mips/lynloong_pc.c b/drivers/platform/mips/lynloong_pc.c
+new file mode 100644
+index 0000000..68f29e4
+--- /dev/null
++++ b/drivers/platform/mips/lynloong_pc.c
+@@ -0,0 +1,515 @@
++/*
++ * Driver for LynLoong PC extras
++ *
++ *  Copyright (C) 2009 Lemote Inc.
++ *  Author: Wu Zhangjin <wuzhangjin@gmail.com>, Xiang Yu <xiangy@lemote.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/err.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/backlight.h>	/* for backlight subdriver */
++#include <linux/fb.h>
++#include <linux/video_output.h>	/* for video output subdriver */
++#include <linux/delay.h>	/* for suspend support */
++
++#include <cs5536/cs5536.h>
++#include <cs5536/cs5536_mfgpt.h>
++
++#include <loongson.h>
++
++static u32 gpio_base, mfgpt_base;
++
++static void set_gpio_reg_high(int gpio, int reg)
++{
++	u32 val;
++
++	val = inl(gpio_base + reg);
++	val |= (1 << gpio);
++	val &= ~(1 << (16 + gpio));
++	outl(val, gpio_base + reg);
++	mmiowb();
++}
++
++static void set_gpio_reg_low(int gpio, int reg)
++{
++	u32 val;
++
++	val = inl(gpio_base + reg);
++	val |= (1 << (16 + gpio));
++	val &= ~(1 << gpio);
++	outl(val, gpio_base + reg);
++	mmiowb();
++}
++
++static void set_gpio_output_low(int gpio)
++{
++	set_gpio_reg_high(gpio, GPIOL_OUT_EN);
++	set_gpio_reg_low(gpio, GPIOL_OUT_VAL);
++}
++
++static void set_gpio_output_high(int gpio)
++{
++	set_gpio_reg_high(gpio, GPIOL_OUT_EN);
++	set_gpio_reg_high(gpio, GPIOL_OUT_VAL);
++}
++
++/* backlight subdriver */
++
++#define MAX_BRIGHTNESS 100
++#define DEFAULT_BRIGHTNESS 50
++#define MIN_BRIGHTNESS 0
++static unsigned int level;
++
++DEFINE_SPINLOCK(backlight_lock);
++/* Tune the brightness */
++static void setup_mfgpt2(void)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&backlight_lock, flags);
++
++	/* Set MFGPT2 comparator 1,2 */
++	outw(MAX_BRIGHTNESS-level, MFGPT2_CMP1);
++	outw(MAX_BRIGHTNESS, MFGPT2_CMP2);
++	/* Clear MFGPT2 UP COUNTER */
++	outw(0, MFGPT2_CNT);
++	/* Enable counter, compare mode, 32k */
++	outw(0x8280, MFGPT2_SETUP);
++
++	spin_unlock_irqrestore(&backlight_lock, flags);
++}
++
++static int lynloong_set_brightness(struct backlight_device *bd)
++{
++	level = (bd->props.fb_blank == FB_BLANK_UNBLANK &&
++		 bd->props.power == FB_BLANK_UNBLANK) ?
++	    bd->props.brightness : 0;
++
++	if (level > MAX_BRIGHTNESS)
++		level = MAX_BRIGHTNESS;
++	else if (level < MIN_BRIGHTNESS)
++		level = MIN_BRIGHTNESS;
++
++	setup_mfgpt2();
++
++	return 0;
++}
++
++static int lynloong_get_brightness(struct backlight_device *bd)
++{
++	return level;
++}
++
++static struct backlight_ops backlight_ops = {
++	.get_brightness = lynloong_get_brightness,
++	.update_status = lynloong_set_brightness,
++};
++
++static struct backlight_device *lynloong_backlight_dev;
++
++static int lynloong_backlight_init(void)
++{
++	int ret;
++	u32 hi;
++	struct backlight_properties props;
++
++	/* Get gpio_base */
++	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &gpio_base);
++	/* Get mfgpt_base */
++	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &hi, &mfgpt_base);
++	/* Get gpio_base */
++	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &gpio_base);
++
++	/* Select for mfgpt */
++	set_gpio_reg_high(7, GPIOL_OUT_AUX1_SEL);
++	/* Enable brightness controlling */
++	set_gpio_output_high(7);
++
++	memset(&props, 0, sizeof(struct backlight_properties));
++	props.max_brightness = MAX_BRIGHTNESS;
++	props.type = BACKLIGHT_PLATFORM;
++	lynloong_backlight_dev = backlight_device_register("backlight0", NULL,
++			NULL, &backlight_ops, &props);
++
++	if (IS_ERR(lynloong_backlight_dev)) {
++		ret = PTR_ERR(lynloong_backlight_dev);
++		return ret;
++	}
++
++	lynloong_backlight_dev->props.brightness = DEFAULT_BRIGHTNESS;
++	backlight_update_status(lynloong_backlight_dev);
++
++	return 0;
++}
++
++static void lynloong_backlight_exit(void)
++{
++	if (lynloong_backlight_dev) {
++		backlight_device_unregister(lynloong_backlight_dev);
++		lynloong_backlight_dev = NULL;
++	}
++	/* Disable brightness controlling */
++	set_gpio_output_low(7);
++}
++
++/* video output driver */
++static int vo_status = 1;
++
++static int lcd_video_output_get(struct output_device *od)
++{
++	return vo_status;
++}
++
++static int lcd_video_output_set(struct output_device *od)
++{
++	int i;
++	unsigned long status;
++
++	status = !!od->request_state;
++
++	if (status == 0) {
++		/* Set the current status as off */
++		vo_status = 0;
++		/* Turn off the backlight */
++		set_gpio_output_low(11);
++		for (i = 0; i < 0x500; i++)
++			delay();
++		/* Turn off the LCD */
++		set_gpio_output_high(8);
++	} else {
++		/* Turn on the LCD */
++		set_gpio_output_low(8);
++		for (i = 0; i < 0x500; i++)
++			delay();
++		/* Turn on the backlight */
++		set_gpio_output_high(11);
++		/* Set the current status as on */
++		vo_status = 1;
++	}
++
++	return 0;
++}
++
++static struct output_properties lcd_output_properties = {
++	.set_state = lcd_video_output_set,
++	.get_status = lcd_video_output_get,
++};
++
++static struct output_device *lcd_output_dev;
++
++static void lynloong_lcd_vo_set(int status)
++{
++	lcd_output_dev->request_state = status;
++	lcd_video_output_set(lcd_output_dev);
++}
++
++static int lynloong_vo_init(void)
++{
++	int ret;
++
++	/* Register video output device: lcd */
++	lcd_output_dev = video_output_register("LCD", NULL, NULL,
++			&lcd_output_properties);
++
++	if (IS_ERR(lcd_output_dev)) {
++		ret = PTR_ERR(lcd_output_dev);
++		lcd_output_dev = NULL;
++		return ret;
++	}
++	/* Ensure LCD is on by default */
++	lynloong_lcd_vo_set(1);
++
++	return 0;
++}
++
++static void lynloong_vo_exit(void)
++{
++	if (lcd_output_dev) {
++		video_output_unregister(lcd_output_dev);
++		lcd_output_dev = NULL;
++	}
++}
++
++/* suspend support */
++
++#ifdef CONFIG_PM
++
++static u32 smb_base;
++
++/* I2C operations */
++
++static int i2c_wait(void)
++{
++	char c;
++	int i;
++
++	udelay(1000);
++	for (i = 0; i < 20; i++) {
++		c = inb(smb_base | SMB_STS);
++		if (c & (SMB_STS_BER | SMB_STS_NEGACK))
++			return -1;
++		if (c & SMB_STS_SDAST)
++			return 0;
++		udelay(100);
++	}
++	return -2;
++}
++
++static void i2c_read_single(int addr, int regNo, char *value)
++{
++	unsigned char c;
++
++	/* Start condition */
++	c = inb(smb_base | SMB_CTRL1);
++	outb(c | SMB_CTRL1_START, smb_base | SMB_CTRL1);
++	i2c_wait();
++
++	/* Send slave address */
++	outb(addr & 0xfe, smb_base | SMB_SDA);
++	i2c_wait();
++
++	/* Acknowledge smbus */
++	c = inb(smb_base | SMB_CTRL1);
++	outb(c | SMB_CTRL1_ACK, smb_base | SMB_CTRL1);
++
++	/* Send register index */
++	outb(regNo, smb_base | SMB_SDA);
++	i2c_wait();
++
++	/* Acknowledge smbus */
++	c = inb(smb_base | SMB_CTRL1);
++	outb(c | SMB_CTRL1_ACK, smb_base | SMB_CTRL1);
++
++	/* Start condition again */
++	c = inb(smb_base | SMB_CTRL1);
++	outb(c | SMB_CTRL1_START, smb_base | SMB_CTRL1);
++	i2c_wait();
++
++	/* Send salve address again */
++	outb(1 | addr, smb_base | SMB_SDA);
++	i2c_wait();
++
++	/* Acknowledge smbus */
++	c = inb(smb_base | SMB_CTRL1);
++	outb(c | SMB_CTRL1_ACK, smb_base | SMB_CTRL1);
++
++	/* Read data */
++	*value = inb(smb_base | SMB_SDA);
++
++	/* Stop condition */
++	outb(SMB_CTRL1_STOP, smb_base | SMB_CTRL1);
++	i2c_wait();
++}
++
++static void i2c_write_single(int addr, int regNo, char value)
++{
++	unsigned char c;
++
++	/* Start condition */
++	c = inb(smb_base | SMB_CTRL1);
++	outb(c | SMB_CTRL1_START, smb_base | SMB_CTRL1);
++	i2c_wait();
++	/* Send slave address */
++	outb(addr & 0xfe, smb_base | SMB_SDA);
++	i2c_wait();;
++
++	/* Send register index */
++	outb(regNo, smb_base | SMB_SDA);
++	i2c_wait();
++
++	/* Write data */
++	outb(value, smb_base | SMB_SDA);
++	i2c_wait();
++	/* Stop condition */
++	outb(SMB_CTRL1_STOP, smb_base | SMB_CTRL1);
++	i2c_wait();
++}
++
++static void stop_clock(int clk_reg, int clk_sel)
++{
++	u8 value;
++
++	i2c_read_single(0xd3, clk_reg, &value);
++	value &= ~(1 << clk_sel);
++	i2c_write_single(0xd2, clk_reg, value);
++}
++
++static void enable_clock(int clk_reg, int clk_sel)
++{
++	u8 value;
++
++	i2c_read_single(0xd3, clk_reg, &value);
++	value |= (1 << clk_sel);
++	i2c_write_single(0xd2, clk_reg, value);
++}
++
++static char cached_clk_freq;
++static char cached_pci_fixed_freq;
++
++static void decrease_clk_freq(void)
++{
++	char value;
++
++	i2c_read_single(0xd3, 1, &value);
++	cached_clk_freq = value;
++
++	/* Select frequency by software */
++	value |= (1 << 1);
++	/* CPU, 3V66, PCI : 100, 66, 33(1) */
++	value |= (1 << 2);
++	i2c_write_single(0xd2, 1, value);
++
++	/* Cache the pci frequency */
++	i2c_read_single(0xd3, 14, &value);
++	cached_pci_fixed_freq = value;
++
++	/* Enable PCI fix mode */
++	value |= (1 << 5);
++	/* 3V66, PCI : 64MHz, 32MHz */
++	value |= (1 << 3);
++	i2c_write_single(0xd2, 14, value);
++
++}
++
++static void resume_clk_freq(void)
++{
++	i2c_write_single(0xd2, 1, cached_clk_freq);
++	i2c_write_single(0xd2, 14, cached_pci_fixed_freq);
++}
++
++static void stop_clocks(void)
++{
++	/* CPU Clock Register */
++	stop_clock(2, 5);	/* not used */
++	stop_clock(2, 6);	/* not used */
++	stop_clock(2, 7);	/* not used */
++
++	/* PCI Clock Register */
++	stop_clock(3, 1);	/* 8100 */
++	stop_clock(3, 5);	/* SIS */
++	stop_clock(3, 0);	/* not used */
++	stop_clock(3, 6);	/* not used */
++
++	/* PCI 48M Clock Register */
++	stop_clock(4, 6);	/* USB grounding */
++	stop_clock(4, 5);	/* REF(5536_14M) */
++
++	/* 3V66 Control Register */
++	stop_clock(5, 0);	/* VCH_CLK..., grounding */
++}
++
++static void enable_clocks(void)
++{
++	enable_clock(3, 1);	/* 8100 */
++	enable_clock(3, 5);	/* SIS */
++
++	enable_clock(4, 6);
++	enable_clock(4, 5);	/* REF(5536_14M) */
++
++	enable_clock(5, 0);	/* VCH_CLOCK, grounding */
++}
++
++static int lynloong_suspend(struct device *dev)
++{
++	/* Disable AMP */
++	set_gpio_output_high(6);
++	/* Turn off LCD */
++	lynloong_lcd_vo_set(0);
++
++	/* Stop the clocks of some devices */
++	stop_clocks();
++
++	/* Decrease the external clock frequency */
++	decrease_clk_freq();
++
++	return 0;
++}
++
++static int lynloong_resume(struct device *dev)
++{
++	/* Turn on the LCD */
++	lynloong_lcd_vo_set(1);
++
++	/* Resume clock frequency, enable the relative clocks */
++	resume_clk_freq();
++	enable_clocks();
++
++	/* Enable AMP */
++	set_gpio_output_low(6);
++
++	return 0;
++}
++
++static const SIMPLE_DEV_PM_OPS(lynloong_pm_ops, lynloong_suspend,
++	lynloong_resume);
++#endif	/* !CONFIG_PM */
++
++static struct platform_device_id platform_device_ids[] = {
++	{
++		.name = "lynloong_pc",
++	},
++	{}
++};
++
++MODULE_DEVICE_TABLE(platform, platform_device_ids);
++
++static struct platform_driver platform_driver = {
++	.driver = {
++		.name = "lynloong_pc",
++		.owner = THIS_MODULE,
++#ifdef CONFIG_PM
++		.pm = &lynloong_pm_ops,
++#endif
++	},
++	.id_table = platform_device_ids,
++};
++
++static int __init lynloong_init(void)
++{
++	int ret;
++
++	pr_info("LynLoong platform specific driver loaded.\n");
++
++	/* Register platform stuff */
++	ret = platform_driver_register(&platform_driver);
++	if (ret) {
++		pr_err("Failed to register LynLoong platform driver.\n");
++		return ret;
++	}
++
++	ret = lynloong_backlight_init();
++	if (ret) {
++		pr_err("Failed to register LynLoong backlight driver.\n");
++		return ret;
++	}
++
++	ret = lynloong_vo_init();
++	if (ret) {
++		pr_err("Failed to register LynLoong backlight driver.\n");
++		lynloong_vo_exit();
++		return ret;
++	}
++
++	return 0;
++}
++
++static void __exit lynloong_exit(void)
++{
++	lynloong_vo_exit();
++	lynloong_backlight_exit();
++	platform_driver_unregister(&platform_driver);
++
++	pr_info("LynLoong platform specific driver unloaded.\n");
++}
++
++module_init(lynloong_init);
++module_exit(lynloong_exit);
++
++MODULE_AUTHOR("Wu Zhangjin <wuzhangjin@gmail.com>; Xiang Yu <xiangy@lemote.com>");
++MODULE_DESCRIPTION("LynLoong PC driver");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/platform/mips/yeeloong_ecrom.c b/drivers/platform/mips/yeeloong_ecrom.c
+new file mode 100644
+index 0000000..1bfe4cf
+--- /dev/null
++++ b/drivers/platform/mips/yeeloong_ecrom.c
+@@ -0,0 +1,944 @@
++/*
++ * Driver for flushing/dumping ROM of EC on YeeLoong laptop
++ *
++ * Copyright (C) 2009 Lemote Inc.
++ * Author: liujl <liujl@lemote.com>
++ *
++ * NOTE :
++ * 	The EC resources accessing and programming are supported.
++ */
++
++#include <linux/module.h>
++#include <linux/proc_fs.h>
++#include <linux/miscdevice.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++
++#include <ec_kb3310b.h>
++
++#define	EC_MISC_DEV		"ec_misc"
++#define EC_IOC_MAGIC		'E'
++
++/* ec registers range */
++#define	EC_MAX_REGADDR	0xFFFF
++#define	EC_MIN_REGADDR	0xF000
++#define	EC_RAM_ADDR	0xF800
++
++/* version burned address */
++#define	VER_ADDR	0xf7a1
++#define	VER_MAX_SIZE	7
++#define	EC_ROM_MAX_SIZE	0x10000
++
++/* ec internal register */
++#define	REG_POWER_MODE		0xF710
++#define	FLAG_NORMAL_MODE	0x00
++#define	FLAG_IDLE_MODE		0x01
++#define	FLAG_RESET_MODE		0x02
++
++/* ec update program flag */
++#define	PROGRAM_FLAG_NONE	0x00
++#define	PROGRAM_FLAG_IE		0x01
++#define	PROGRAM_FLAG_ROM	0x02
++
++/* XBI relative registers */
++#define REG_XBISEG0     0xFEA0
++#define REG_XBISEG1     0xFEA1
++#define REG_XBIRSV2     0xFEA2
++#define REG_XBIRSV3     0xFEA3
++#define REG_XBIRSV4     0xFEA4
++#define REG_XBICFG      0xFEA5
++#define REG_XBICS       0xFEA6
++#define REG_XBIWE       0xFEA7
++#define REG_XBISPIA0    0xFEA8
++#define REG_XBISPIA1    0xFEA9
++#define REG_XBISPIA2    0xFEAA
++#define REG_XBISPIDAT   0xFEAB
++#define REG_XBISPICMD   0xFEAC
++#define REG_XBISPICFG   0xFEAD
++#define REG_XBISPIDATR  0xFEAE
++#define REG_XBISPICFG2  0xFEAF
++
++/* commands definition for REG_XBISPICMD */
++#define	SPICMD_WRITE_STATUS		0x01
++#define	SPICMD_BYTE_PROGRAM		0x02
++#define	SPICMD_READ_BYTE		0x03
++#define	SPICMD_WRITE_DISABLE	0x04
++#define	SPICMD_READ_STATUS		0x05
++#define	SPICMD_WRITE_ENABLE		0x06
++#define	SPICMD_HIGH_SPEED_READ	0x0B
++#define	SPICMD_POWER_DOWN		0xB9
++#define	SPICMD_SST_EWSR			0x50
++#define	SPICMD_SST_SEC_ERASE	0x20
++#define	SPICMD_SST_BLK_ERASE	0x52
++#define	SPICMD_SST_CHIP_ERASE	0x60
++#define	SPICMD_FRDO				0x3B
++#define	SPICMD_SEC_ERASE		0xD7
++#define	SPICMD_BLK_ERASE		0xD8
++#define SPICMD_CHIP_ERASE		0xC7
++
++/* bits definition for REG_XBISPICFG */
++#define	SPICFG_AUTO_CHECK		0x01
++#define	SPICFG_SPI_BUSY			0x02
++#define	SPICFG_DUMMY_READ		0x04
++#define	SPICFG_EN_SPICMD		0x08
++#define	SPICFG_LOW_SPICS		0x10
++#define	SPICFG_EN_SHORT_READ	0x20
++#define	SPICFG_EN_OFFSET_READ	0x40
++#define	SPICFG_EN_FAST_READ		0x80
++
++/* watchdog timer registers */
++#define	REG_WDTCFG				0xfe80
++#define	REG_WDTPF				0xfe81
++#define REG_WDT					0xfe82
++
++/* lpc configure register */
++#define	REG_LPCCFG				0xfe95
++
++/* 8051 reg */
++#define	REG_PXCFG				0xff14
++
++/* Fan register in KB3310 */
++#define	REG_ECFAN_SPEED_LEVEL	0xf4e4
++#define	REG_ECFAN_SWITCH		0xf4d2
++
++/* the ec flash rom id number */
++#define	EC_ROM_PRODUCT_ID_SPANSION	0x01
++#define	EC_ROM_PRODUCT_ID_MXIC		0xC2
++#define	EC_ROM_PRODUCT_ID_AMIC		0x37
++#define	EC_ROM_PRODUCT_ID_EONIC		0x1C
++
++/* misc ioctl operations */
++#define	IOCTL_RDREG		_IOR(EC_IOC_MAGIC, 1, int)
++#define	IOCTL_WRREG		_IOW(EC_IOC_MAGIC, 2, int)
++#define	IOCTL_READ_EC		_IOR(EC_IOC_MAGIC, 3, int)
++#define	IOCTL_PROGRAM_IE	_IOW(EC_IOC_MAGIC, 4, int)
++#define	IOCTL_PROGRAM_EC	_IOW(EC_IOC_MAGIC, 5, int)
++
++/* start address for programming of EC content or IE */
++/*  ec running code start address */
++#define	EC_START_ADDR	0x00000000
++/*  ec information element storing address */
++#define	IE_START_ADDR	0x00020000
++
++/* EC state */
++#define	EC_STATE_IDLE	0x00	/*  ec in idle state */
++#define	EC_STATE_BUSY	0x01	/*  ec in busy state */
++
++/* timeout value for programming */
++#define	EC_FLASH_TIMEOUT	0x1000	/*  ec program timeout */
++/* command checkout timeout including cmd to port or state flag check */
++#define	EC_CMD_TIMEOUT		0x1000
++#define	EC_SPICMD_STANDARD_TIMEOUT	(4 * 1000)	/*  unit : us */
++#define	EC_MAX_DELAY_UNIT	(10)	/*  every time for polling */
++#define	SPI_FINISH_WAIT_TIME	10
++/* EC content max size */
++#define	EC_CONTENT_MAX_SIZE	(64 * 1024)
++#define	IE_CONTENT_MAX_SIZE	(0x100000 - IE_START_ADDR)
++
++/* the register operation access struct */
++struct ec_reg {
++	u32 addr;		/* the address of kb3310 registers */
++	u8 val;			/* the register value */
++};
++
++struct ec_info {
++	u32 start_addr;
++	u32 size;
++	u8 *buf;
++};
++
++/* open for using rom protection action */
++#define	EC_ROM_PROTECTION
++
++/* enable the chip reset mode */
++static int ec_init_reset_mode(void)
++{
++	int timeout;
++	unsigned char status = 0;
++	int ret = 0;
++
++	/* make chip goto reset mode */
++	ret = ec_query_seq(CMD_INIT_RESET_MODE);
++	if (ret < 0) {
++		printk(KERN_ERR "ec init reset mode failed.\n");
++		goto out;
++	}
++
++	/* make the action take active */
++	timeout = EC_CMD_TIMEOUT;
++	status = ec_read(REG_POWER_MODE) & FLAG_RESET_MODE;
++	while (timeout--) {
++		if (status) {
++			udelay(EC_REG_DELAY);
++			break;
++		}
++		status = ec_read(REG_POWER_MODE) & FLAG_RESET_MODE;
++		udelay(EC_REG_DELAY);
++	}
++	if (timeout <= 0) {
++		printk(KERN_ERR "ec rom fixup : can't check reset status.\n");
++		ret = -EINVAL;
++	} else
++		printk(KERN_INFO "(%d/%d)reset 0xf710 :  0x%x\n", timeout,
++			   EC_CMD_TIMEOUT - timeout, status);
++
++	/* set MCU to reset mode */
++	udelay(EC_REG_DELAY);
++	status = ec_read(REG_PXCFG);
++	status |= (1 << 0);
++	ec_write(REG_PXCFG, status);
++	udelay(EC_REG_DELAY);
++
++	/* disable FWH/LPC */
++	udelay(EC_REG_DELAY);
++	status = ec_read(REG_LPCCFG);
++	status &= ~(1 << 7);
++	ec_write(REG_LPCCFG, status);
++	udelay(EC_REG_DELAY);
++
++	printk(KERN_INFO "entering reset mode ok..............\n");
++
++ out:
++	return ret;
++}
++
++/* make ec exit from reset mode */
++static void ec_exit_reset_mode(void)
++{
++	unsigned char regval;
++
++	udelay(EC_REG_DELAY);
++	regval = ec_read(REG_LPCCFG);
++	regval |= (1 << 7);
++	ec_write(REG_LPCCFG, regval);
++	regval = ec_read(REG_PXCFG);
++	regval &= ~(1 << 0);
++	ec_write(REG_PXCFG, regval);
++	printk(KERN_INFO "exit reset mode ok..................\n");
++
++	return;
++}
++
++/* make ec disable WDD */
++static void ec_disable_WDD(void)
++{
++	unsigned char status;
++
++	udelay(EC_REG_DELAY);
++	status = ec_read(REG_WDTCFG);
++	ec_write(REG_WDTPF, 0x03);
++	ec_write(REG_WDTCFG, (status & 0x80) | 0x48);
++	printk(KERN_INFO "Disable WDD ok..................\n");
++
++	return;
++}
++
++/* make ec enable WDD */
++static void ec_enable_WDD(void)
++{
++	unsigned char status;
++
++	udelay(EC_REG_DELAY);
++	status = ec_read(REG_WDTCFG);
++	ec_write(REG_WDT, 0x28);	/* set WDT 5sec(0x28) */
++	ec_write(REG_WDTCFG, (status & 0x80) | 0x03);
++	printk(KERN_INFO "Enable WDD ok..................\n");
++
++	return;
++}
++
++/* make ec goto idle mode */
++static int ec_init_idle_mode(void)
++{
++	int timeout;
++	unsigned char status = 0;
++	int ret = 0;
++
++	ec_query_seq(CMD_INIT_IDLE_MODE);
++
++	/* make the action take active */
++	timeout = EC_CMD_TIMEOUT;
++	status = ec_read(REG_POWER_MODE) & FLAG_IDLE_MODE;
++	while (timeout--) {
++		if (status) {
++			udelay(EC_REG_DELAY);
++			break;
++		}
++		status = ec_read(REG_POWER_MODE) & FLAG_IDLE_MODE;
++		udelay(EC_REG_DELAY);
++	}
++	if (timeout <= 0) {
++		printk(KERN_ERR "ec rom fixup : can't check out the status.\n");
++		ret = -EINVAL;
++	} else
++		printk(KERN_INFO "(%d/%d)0xf710 :  0x%x\n", timeout,
++			   EC_CMD_TIMEOUT - timeout, ec_read(REG_POWER_MODE));
++
++	printk(KERN_INFO "entering idle mode ok...................\n");
++
++	return ret;
++}
++
++/* make ec exit from idle mode */
++static int ec_exit_idle_mode(void)
++{
++
++	ec_query_seq(CMD_EXIT_IDLE_MODE);
++
++	printk(KERN_INFO "exit idle mode ok...................\n");
++
++	return 0;
++}
++
++static int ec_instruction_cycle(void)
++{
++	unsigned long timeout;
++	int ret = 0;
++
++	timeout = EC_FLASH_TIMEOUT;
++	while (timeout-- >= 0) {
++		if (!(ec_read(REG_XBISPICFG) & SPICFG_SPI_BUSY))
++			break;
++	}
++	if (timeout <= 0) {
++		printk(KERN_ERR
++		       "EC_INSTRUCTION_CYCLE : timeout for check flag.\n");
++		ret = -EINVAL;
++		goto out;
++	}
++
++ out:
++	return ret;
++}
++
++/* To see if the ec is in busy state or not. */
++static inline int ec_flash_busy(unsigned long timeout)
++{
++	/* assurance the first command be going to rom */
++	if (ec_instruction_cycle() < 0)
++		return EC_STATE_BUSY;
++#if 1
++	timeout = timeout / EC_MAX_DELAY_UNIT;
++	while (timeout-- > 0) {
++		/* check the rom's status of busy flag */
++		ec_write(REG_XBISPICMD, SPICMD_READ_STATUS);
++		if (ec_instruction_cycle() < 0)
++			return EC_STATE_BUSY;
++		if ((ec_read(REG_XBISPIDAT) & 0x01) == 0x00)
++			return EC_STATE_IDLE;
++		udelay(EC_MAX_DELAY_UNIT);
++	}
++	if (timeout <= 0) {
++		printk(KERN_ERR
++		       "EC_FLASH_BUSY : timeout for check rom flag.\n");
++		return EC_STATE_BUSY;
++	}
++#else
++	/* check the rom's status of busy flag */
++	ec_write(REG_XBISPICMD, SPICMD_READ_STATUS);
++	if (ec_instruction_cycle() < 0)
++		return EC_STATE_BUSY;
++
++	timeout = timeout / EC_MAX_DELAY_UNIT;
++	while (timeout-- > 0) {
++		if ((ec_read(REG_XBISPIDAT) & 0x01) == 0x00)
++			return EC_STATE_IDLE;
++		udelay(EC_MAX_DELAY_UNIT);
++	}
++	if (timeout <= 0) {
++		printk(KERN_ERR
++		       "EC_FLASH_BUSY : timeout for check rom flag.\n");
++		return EC_STATE_BUSY;
++	}
++#endif
++
++	return EC_STATE_IDLE;
++}
++
++static int rom_instruction_cycle(unsigned char cmd)
++{
++	unsigned long timeout = 0;
++
++	switch (cmd) {
++	case SPICMD_READ_STATUS:
++	case SPICMD_WRITE_ENABLE:
++	case SPICMD_WRITE_DISABLE:
++	case SPICMD_READ_BYTE:
++	case SPICMD_HIGH_SPEED_READ:
++		timeout = 0;
++		break;
++	case SPICMD_WRITE_STATUS:
++		timeout = 300 * 1000;
++		break;
++	case SPICMD_BYTE_PROGRAM:
++		timeout = 5 * 1000;
++		break;
++	case SPICMD_SST_SEC_ERASE:
++	case SPICMD_SEC_ERASE:
++		timeout = 1000 * 1000;
++		break;
++	case SPICMD_SST_BLK_ERASE:
++	case SPICMD_BLK_ERASE:
++		timeout = 3 * 1000 * 1000;
++		break;
++	case SPICMD_SST_CHIP_ERASE:
++	case SPICMD_CHIP_ERASE:
++		timeout = 20 * 1000 * 1000;
++		break;
++	default:
++		timeout = EC_SPICMD_STANDARD_TIMEOUT;
++	}
++	if (timeout == 0)
++		return ec_instruction_cycle();
++	if (timeout < EC_SPICMD_STANDARD_TIMEOUT)
++		timeout = EC_SPICMD_STANDARD_TIMEOUT;
++
++	return ec_flash_busy(timeout);
++}
++
++/* delay for start/stop action */
++static void delay_spi(int n)
++{
++	while (n--)
++		inb(EC_IO_PORT_HIGH);
++}
++
++/* start the action to spi rom function */
++static void ec_start_spi(void)
++{
++	unsigned char val;
++
++	delay_spi(SPI_FINISH_WAIT_TIME);
++	val = ec_read(REG_XBISPICFG) | SPICFG_EN_SPICMD | SPICFG_AUTO_CHECK;
++	ec_write(REG_XBISPICFG, val);
++	delay_spi(SPI_FINISH_WAIT_TIME);
++}
++
++/* stop the action to spi rom function */
++static void ec_stop_spi(void)
++{
++	unsigned char val;
++
++	delay_spi(SPI_FINISH_WAIT_TIME);
++	val =
++	    ec_read(REG_XBISPICFG) & (~(SPICFG_EN_SPICMD | SPICFG_AUTO_CHECK));
++	ec_write(REG_XBISPICFG, val);
++	delay_spi(SPI_FINISH_WAIT_TIME);
++}
++
++/* read one byte from xbi interface */
++static int ec_read_byte(unsigned int addr, unsigned char *byte)
++{
++	int ret = 0;
++
++	/* enable spicmd writing. */
++	ec_start_spi();
++
++	/* enable write spi flash */
++	ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE);
++	if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) {
++		printk(KERN_ERR "EC_READ_BYTE : SPICMD_WRITE_ENABLE failed.\n");
++		ret = -EINVAL;
++		goto out;
++	}
++
++	/* write the address */
++	ec_write(REG_XBISPIA2, (addr & 0xff0000) >> 16);
++	ec_write(REG_XBISPIA1, (addr & 0x00ff00) >> 8);
++	ec_write(REG_XBISPIA0, (addr & 0x0000ff) >> 0);
++	/* start action */
++	ec_write(REG_XBISPICMD, SPICMD_HIGH_SPEED_READ);
++	if (rom_instruction_cycle(SPICMD_HIGH_SPEED_READ) == EC_STATE_BUSY) {
++		printk(KERN_ERR
++		       "EC_READ_BYTE : SPICMD_HIGH_SPEED_READ failed.\n");
++		ret = -EINVAL;
++		goto out;
++	}
++
++	*byte = ec_read(REG_XBISPIDAT);
++
++ out:
++	/* disable spicmd writing. */
++	ec_stop_spi();
++
++	return ret;
++}
++
++/* write one byte to ec rom */
++static int ec_write_byte(unsigned int addr, unsigned char byte)
++{
++	int ret = 0;
++
++	/* enable spicmd writing. */
++	ec_start_spi();
++
++	/* enable write spi flash */
++	ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE);
++	if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) {
++		printk(KERN_ERR
++		       "EC_WRITE_BYTE : SPICMD_WRITE_ENABLE failed.\n");
++		ret = -EINVAL;
++		goto out;
++	}
++
++	/* write the address */
++	ec_write(REG_XBISPIA2, (addr & 0xff0000) >> 16);
++	ec_write(REG_XBISPIA1, (addr & 0x00ff00) >> 8);
++	ec_write(REG_XBISPIA0, (addr & 0x0000ff) >> 0);
++	ec_write(REG_XBISPIDAT, byte);
++	/* start action */
++	ec_write(REG_XBISPICMD, SPICMD_BYTE_PROGRAM);
++	if (rom_instruction_cycle(SPICMD_BYTE_PROGRAM) == EC_STATE_BUSY) {
++		printk(KERN_ERR
++		       "EC_WRITE_BYTE : SPICMD_BYTE_PROGRAM failed.\n");
++		ret = -EINVAL;
++		goto out;
++	}
++
++ out:
++	/* disable spicmd writing. */
++	ec_stop_spi();
++
++	return ret;
++}
++
++/* unprotect SPI ROM */
++/* EC_ROM_unprotect function code */
++static int EC_ROM_unprotect(void)
++{
++	unsigned char status;
++
++	/* enable write spi flash */
++	ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE);
++	if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) {
++		printk(KERN_ERR
++		       "EC_UNIT_ERASE : SPICMD_WRITE_ENABLE failed.\n");
++		return 1;
++	}
++
++	/* unprotect the status register of rom */
++	ec_write(REG_XBISPICMD, SPICMD_READ_STATUS);
++	if (rom_instruction_cycle(SPICMD_READ_STATUS) == EC_STATE_BUSY) {
++		printk(KERN_ERR "EC_UNIT_ERASE : SPICMD_READ_STATUS failed.\n");
++		return 1;
++	}
++	status = ec_read(REG_XBISPIDAT);
++	ec_write(REG_XBISPIDAT, status & 0x02);
++	if (ec_instruction_cycle() < 0) {
++		printk(KERN_ERR "EC_UNIT_ERASE : write status value failed.\n");
++		return 1;
++	}
++
++	ec_write(REG_XBISPICMD, SPICMD_WRITE_STATUS);
++	if (rom_instruction_cycle(SPICMD_WRITE_STATUS) == EC_STATE_BUSY) {
++		printk(KERN_ERR
++		       "EC_UNIT_ERASE : SPICMD_WRITE_STATUS failed.\n");
++		return 1;
++	}
++
++	/* enable write spi flash */
++	ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE);
++	if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) {
++		printk(KERN_ERR
++		       "EC_UNIT_ERASE : SPICMD_WRITE_ENABLE failed.\n");
++		return 1;
++	}
++
++	return 0;
++}
++
++/* erase one block or chip or sector as needed */
++static int ec_unit_erase(unsigned char erase_cmd, unsigned int addr)
++{
++	unsigned char status;
++	int ret = 0, i = 0;
++	int unprotect_count = 3;
++	int check_flag = 0;
++
++	/* enable spicmd writing. */
++	ec_start_spi();
++
++#ifdef EC_ROM_PROTECTION
++	/* added for re-check SPICMD_READ_STATUS */
++	while (unprotect_count-- > 0) {
++		if (EC_ROM_unprotect()) {
++			ret = -EINVAL;
++			goto out;
++		}
++
++		/* first time:500ms --> 5.5sec -->10.5sec */
++		for (i = 0; i < ((2 - unprotect_count) * 100 + 10); i++)
++			udelay(50000);
++		ec_write(REG_XBISPICMD, SPICMD_READ_STATUS);
++		if (rom_instruction_cycle(SPICMD_READ_STATUS)
++				== EC_STATE_BUSY) {
++			printk(KERN_ERR
++			       "EC_PROGRAM_ROM : SPICMD_READ_STATUS failed.\n");
++		} else {
++			status = ec_read(REG_XBISPIDAT);
++			printk(KERN_INFO "Read unprotect status : 0x%x\n",
++				   status);
++			if ((status & 0x1C) == 0x00) {
++				printk(KERN_INFO
++					   "Read unprotect status OK1 : 0x%x\n",
++					   status & 0x1C);
++				check_flag = 1;
++				break;
++			}
++		}
++	}
++
++	if (!check_flag) {
++		printk(KERN_INFO "SPI ROM unprotect fail.\n");
++		return 1;
++	}
++#endif
++
++	/* block address fill */
++	if (erase_cmd == SPICMD_BLK_ERASE) {
++		ec_write(REG_XBISPIA2, (addr & 0x00ff0000) >> 16);
++		ec_write(REG_XBISPIA1, (addr & 0x0000ff00) >> 8);
++		ec_write(REG_XBISPIA0, (addr & 0x000000ff) >> 0);
++	}
++
++	/* erase the whole chip first */
++	ec_write(REG_XBISPICMD, erase_cmd);
++	if (rom_instruction_cycle(erase_cmd) == EC_STATE_BUSY) {
++		printk(KERN_ERR "EC_UNIT_ERASE : erase failed.\n");
++		ret = -EINVAL;
++		goto out;
++	}
++
++ out:
++	/* disable spicmd writing. */
++	ec_stop_spi();
++
++	return ret;
++}
++
++/* update the whole rom content with H/W mode
++ * PLEASE USING ec_unit_erase() FIRSTLY
++ */
++static int ec_program_rom(struct ec_info *info, int flag)
++{
++	unsigned int addr = 0;
++	unsigned long size = 0;
++	unsigned char *ptr = NULL;
++	unsigned char data;
++	unsigned char val = 0;
++	int ret = 0;
++	int i, j;
++	unsigned char status;
++
++	/* modify for program serial No.
++	 * set IE_START_ADDR & use idle mode,
++	 * disable WDD
++	 */
++	if (flag == PROGRAM_FLAG_ROM) {
++		ret = ec_init_reset_mode();
++		addr = info->start_addr + EC_START_ADDR;
++		printk(KERN_INFO "PROGRAM_FLAG_ROM..............\n");
++	} else if (flag == PROGRAM_FLAG_IE) {
++		ret = ec_init_idle_mode();
++		ec_disable_WDD();
++		addr = info->start_addr + IE_START_ADDR;
++		printk(KERN_INFO "PROGRAM_FLAG_IE..............\n");
++	} else {
++		return 0;
++	}
++
++	if (ret < 0) {
++		if (flag == PROGRAM_FLAG_IE)
++			ec_enable_WDD();
++		return ret;
++	}
++
++	size = info->size;
++	ptr = info->buf;
++	printk(KERN_INFO "starting update ec ROM..............\n");
++
++	ret = ec_unit_erase(SPICMD_BLK_ERASE, addr);
++	if (ret) {
++		printk(KERN_ERR "program ec : erase block failed.\n");
++		goto out;
++	}
++	printk(KERN_ERR "program ec : erase block OK.\n");
++
++	i = 0;
++	while (i < size) {
++		data = *(ptr + i);
++		ec_write_byte(addr, data);
++		ec_read_byte(addr, &val);
++		if (val != data) {
++			ec_write_byte(addr, data);
++			ec_read_byte(addr, &val);
++			if (val != data) {
++				printk(KERN_INFO
++				"EC : Second flash program failed at:\t");
++				printk(KERN_INFO
++				"addr : 0x%x, source : 0x%x, dest: 0x%x\n",
++				     addr, data, val);
++				printk(KERN_INFO "This should not happen... STOP\n");
++				break;
++			}
++		}
++		i++;
++		addr++;
++	}
++
++#ifdef	EC_ROM_PROTECTION
++	/* we should start spi access firstly */
++	ec_start_spi();
++
++	/* enable write spi flash */
++	ec_write(REG_XBISPICMD, SPICMD_WRITE_ENABLE);
++	if (rom_instruction_cycle(SPICMD_WRITE_ENABLE) == EC_STATE_BUSY) {
++		printk(KERN_ERR
++		       "EC_PROGRAM_ROM : SPICMD_WRITE_ENABLE failed.\n");
++		goto out1;
++	}
++
++	/* protect the status register of rom */
++	ec_write(REG_XBISPICMD, SPICMD_READ_STATUS);
++	if (rom_instruction_cycle(SPICMD_READ_STATUS) == EC_STATE_BUSY) {
++		printk(KERN_ERR
++		       "EC_PROGRAM_ROM : SPICMD_READ_STATUS failed.\n");
++		goto out1;
++	}
++	status = ec_read(REG_XBISPIDAT);
++
++	ec_write(REG_XBISPIDAT, status | 0x1C);
++	if (ec_instruction_cycle() < 0) {
++		printk(KERN_ERR
++		       "EC_PROGRAM_ROM : write status value failed.\n");
++		goto out1;
++	}
++
++	ec_write(REG_XBISPICMD, SPICMD_WRITE_STATUS);
++	if (rom_instruction_cycle(SPICMD_WRITE_STATUS) == EC_STATE_BUSY) {
++		printk(KERN_ERR
++		       "EC_PROGRAM_ROM : SPICMD_WRITE_STATUS failed.\n");
++		goto out1;
++	}
++#endif
++
++	/* disable the write action to spi rom */
++	ec_write(REG_XBISPICMD, SPICMD_WRITE_DISABLE);
++	if (rom_instruction_cycle(SPICMD_WRITE_DISABLE) == EC_STATE_BUSY) {
++		printk(KERN_ERR
++		       "EC_PROGRAM_ROM : SPICMD_WRITE_DISABLE failed.\n");
++		goto out1;
++	}
++
++ out1:
++	/* we should stop spi access firstly */
++	ec_stop_spi();
++ out:
++	/* for security */
++	for (j = 0; j < 2000; j++)
++		udelay(1000);
++
++	/* modify for program serial No.
++	 * after program No exit idle mode
++	 * and enable WDD
++	 */
++	if (flag == PROGRAM_FLAG_ROM) {
++		/* exit from the reset mode */
++		ec_exit_reset_mode();
++	} else {
++		/* ec exit from idle mode */
++		ret = ec_exit_idle_mode();
++		ec_enable_WDD();
++		if (ret < 0)
++			return ret;
++	}
++
++	return 0;
++}
++
++/* ioctl  */
++static int misc_ioctl(struct inode *inode, struct file *filp, u_int cmd,
++		      u_long arg)
++{
++	struct ec_info ecinfo;
++	void __user *ptr = (void __user *)arg;
++	struct ec_reg *ecreg = (struct ec_reg *)(filp->private_data);
++	int ret = 0;
++
++	switch (cmd) {
++	case IOCTL_RDREG:
++		ret = copy_from_user(ecreg, ptr, sizeof(struct ec_reg));
++		if (ret) {
++			printk(KERN_ERR "reg read : copy from user error.\n");
++			return -EFAULT;
++		}
++		if ((ecreg->addr > EC_MAX_REGADDR)
++		    || (ecreg->addr < EC_MIN_REGADDR)) {
++			printk(KERN_ERR
++			       "reg read : out of register address range.\n");
++			return -EINVAL;
++		}
++		ecreg->val = ec_read(ecreg->addr);
++		ret = copy_to_user(ptr, ecreg, sizeof(struct ec_reg));
++		if (ret) {
++			printk(KERN_ERR "reg read : copy to user error.\n");
++			return -EFAULT;
++		}
++		break;
++	case IOCTL_WRREG:
++		ret = copy_from_user(ecreg, ptr, sizeof(struct ec_reg));
++		if (ret) {
++			printk(KERN_ERR "reg write : copy from user error.\n");
++			return -EFAULT;
++		}
++		if ((ecreg->addr > EC_MAX_REGADDR)
++		    || (ecreg->addr < EC_MIN_REGADDR)) {
++			printk(KERN_ERR
++			       "reg write : out of register address range.\n");
++			return -EINVAL;
++		}
++		ec_write(ecreg->addr, ecreg->val);
++		break;
++	case IOCTL_READ_EC:
++		ret = copy_from_user(ecreg, ptr, sizeof(struct ec_reg));
++		if (ret) {
++			printk(KERN_ERR "spi read : copy from user error.\n");
++			return -EFAULT;
++		}
++		if ((ecreg->addr > EC_RAM_ADDR)
++		    && (ecreg->addr < EC_MAX_REGADDR)) {
++			printk(KERN_ERR
++			       "spi read : out of register address range.\n");
++			return -EINVAL;
++		}
++		ec_read_byte(ecreg->addr, &(ecreg->val));
++		ret = copy_to_user(ptr, ecreg, sizeof(struct ec_reg));
++		if (ret) {
++			printk(KERN_ERR "spi read : copy to user error.\n");
++			return -EFAULT;
++		}
++		break;
++	case IOCTL_PROGRAM_IE:
++		ecinfo.start_addr = EC_START_ADDR;
++		ecinfo.size = EC_CONTENT_MAX_SIZE;
++		ecinfo.buf = (u8 *) kmalloc(ecinfo.size, GFP_KERNEL);
++		if (ecinfo.buf == NULL) {
++			printk(KERN_ERR "program ie : kmalloc failed.\n");
++			return -ENOMEM;
++		}
++		ret = copy_from_user(ecinfo.buf, (u8 *) ptr, ecinfo.size);
++		if (ret) {
++			printk(KERN_ERR "program ie : copy from user error.\n");
++			kfree(ecinfo.buf);
++			ecinfo.buf = NULL;
++			return -EFAULT;
++		}
++
++		/* use ec_program_rom to write serial No */
++		ec_program_rom(&ecinfo, PROGRAM_FLAG_IE);
++
++		kfree(ecinfo.buf);
++		ecinfo.buf = NULL;
++		break;
++	case IOCTL_PROGRAM_EC:
++		ecinfo.start_addr = EC_START_ADDR;
++		if (get_user((ecinfo.size), (u32 *) ptr)) {
++			printk(KERN_ERR "program ec : get user error.\n");
++			return -EFAULT;
++		}
++		if ((ecinfo.size) > EC_CONTENT_MAX_SIZE) {
++			printk(KERN_ERR "program ec : size out of limited.\n");
++			return -EINVAL;
++		}
++		ecinfo.buf = (u8 *) kmalloc(ecinfo.size, GFP_KERNEL);
++		if (ecinfo.buf == NULL) {
++			printk(KERN_ERR "program ec : kmalloc failed.\n");
++			return -ENOMEM;
++		}
++		ret = copy_from_user(ecinfo.buf, ((u8 *) ptr + 4), ecinfo.size);
++		if (ret) {
++			printk(KERN_ERR "program ec : copy from user error.\n");
++			kfree(ecinfo.buf);
++			ecinfo.buf = NULL;
++			return -EFAULT;
++		}
++
++		ec_program_rom(&ecinfo, PROGRAM_FLAG_ROM);
++
++		kfree(ecinfo.buf);
++		ecinfo.buf = NULL;
++		break;
++
++	default:
++		break;
++	}
++
++	return 0;
++}
++
++static long misc_compat_ioctl(struct file *file, unsigned int cmd,
++			      unsigned long arg)
++{
++	return misc_ioctl(file->f_dentry->d_inode, file, cmd, arg);
++}
++
++static int misc_open(struct inode *inode, struct file *filp)
++{
++	struct ec_reg *ecreg = NULL;
++	ecreg = kmalloc(sizeof(struct ec_reg), GFP_KERNEL);
++	if (ecreg)
++		filp->private_data = ecreg;
++
++	return ecreg ? 0 : -ENOMEM;
++}
++
++static int misc_release(struct inode *inode, struct file *filp)
++{
++	struct ec_reg *ecreg = (struct ec_reg *)(filp->private_data);
++
++	filp->private_data = NULL;
++	kfree(ecreg);
++
++	return 0;
++}
++
++static const struct file_operations ecmisc_fops = {
++	.open = misc_open,
++	.release = misc_release,
++	.read = NULL,
++	.write = NULL,
++#ifdef	CONFIG_64BIT
++	.compat_ioctl = misc_compat_ioctl,
++#else
++	.ioctl = misc_ioctl,
++#endif
++};
++
++static struct miscdevice ecmisc_device = {
++	.minor = MISC_DYNAMIC_MINOR,
++	.name = EC_MISC_DEV,
++	.fops = &ecmisc_fops
++};
++
++static int __init ecmisc_init(void)
++{
++	int ret;
++
++	printk(KERN_INFO "EC misc device init.\n");
++	ret = misc_register(&ecmisc_device);
++
++	return ret;
++}
++
++static void __exit ecmisc_exit(void)
++{
++	printk(KERN_INFO "EC misc device exit.\n");
++	misc_deregister(&ecmisc_device);
++}
++
++module_init(ecmisc_init);
++module_exit(ecmisc_exit);
++
++MODULE_AUTHOR("liujl <liujl@lemote.com>");
++MODULE_DESCRIPTION("Driver for flushing/dumping ROM of EC on YeeLoong laptop");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c
+new file mode 100644
+index 0000000..c285a67
+--- /dev/null
++++ b/drivers/platform/mips/yeeloong_laptop.c
+@@ -0,0 +1,1360 @@
++/*
++ * Driver for YeeLoong laptop extras
++ *
++ *  Copyright (C) 2009 Lemote Inc.
++ *  Author: Wu Zhangjin <wuzhangjin@gmail.com>, Liu Junliang <liujl@lemote.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/err.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/backlight.h>	/* for backlight subdriver */
++#include <linux/fb.h>
++#include <linux/hwmon.h>	/* for hwmon subdriver */
++#include <linux/hwmon-sysfs.h>
++#include <linux/video_output.h>	/* for video output subdriver */
++#include <linux/lcd.h>		/* for lcd output subdriver */
++#include <linux/input.h>	/* for hotkey subdriver */
++#include <linux/input/sparse-keymap.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <linux/power_supply.h>	/* for AC & Battery subdriver */
++#include <linux/reboot.h>	/* for register_reboot_notifier */
++#include <linux/suspend.h>	/* for register_pm_notifier */
++
++#include <cs5536/cs5536.h>
++
++#include <loongson.h>		/* for loongson_cmdline */
++#include <ec_kb3310b.h>
++
++#define ON	1
++#define OFF	0
++#define EVENT_START EVENT_LID
++
++/* common function */
++#define EC_VER_LEN 64
++
++static int ec_version_before(char *version)
++{
++	char *p, ec_ver[EC_VER_LEN];
++
++	p = strstr(loongson_cmdline, "EC_VER=");
++	if (!p)
++		memset(ec_ver, 0, EC_VER_LEN);
++	else {
++		strncpy(ec_ver, p, EC_VER_LEN);
++		p = strstr(ec_ver, " ");
++		if (p)
++			*p = '\0';
++	}
++
++	return (strncasecmp(ec_ver, version, 64) < 0);
++}
++
++/* backlight subdriver */
++#define MIN_BRIGHTNESS	1
++#define MAX_BRIGHTNESS	8
++
++static int yeeloong_set_brightness(struct backlight_device *bd)
++{
++	unsigned char level;
++	static unsigned char old_level;
++
++	level = (bd->props.fb_blank == FB_BLANK_UNBLANK &&
++		 bd->props.power == FB_BLANK_UNBLANK) ?
++	    bd->props.brightness : 0;
++
++	level = clamp_val(level, MIN_BRIGHTNESS, MAX_BRIGHTNESS);
++
++	/* Avoid to modify the brightness when EC is tuning it */
++	if (old_level != level) {
++		if (ec_read(REG_DISPLAY_BRIGHTNESS) == old_level)
++			ec_write(REG_DISPLAY_BRIGHTNESS, level);
++		old_level = level;
++	}
++
++	return 0;
++}
++
++static int yeeloong_get_brightness(struct backlight_device *bd)
++{
++	return ec_read(REG_DISPLAY_BRIGHTNESS);
++}
++
++static struct backlight_ops backlight_ops = {
++	.get_brightness = yeeloong_get_brightness,
++	.update_status = yeeloong_set_brightness,
++};
++
++static struct backlight_device *yeeloong_backlight_dev;
++
++static int yeeloong_backlight_init(void)
++{
++	int ret;
++	struct backlight_properties props;
++
++	memset(&props, 0, sizeof(struct backlight_properties));
++	props.max_brightness = MAX_BRIGHTNESS;
++	props.type = BACKLIGHT_PLATFORM;
++	yeeloong_backlight_dev = backlight_device_register("backlight0", NULL,
++			NULL, &backlight_ops, &props);
++
++	if (IS_ERR(yeeloong_backlight_dev)) {
++		ret = PTR_ERR(yeeloong_backlight_dev);
++		yeeloong_backlight_dev = NULL;
++		return ret;
++	}
++
++	yeeloong_backlight_dev->props.brightness =
++		yeeloong_get_brightness(yeeloong_backlight_dev);
++	backlight_update_status(yeeloong_backlight_dev);
++
++	return 0;
++}
++
++static void yeeloong_backlight_exit(void)
++{
++	if (yeeloong_backlight_dev) {
++		backlight_device_unregister(yeeloong_backlight_dev);
++		yeeloong_backlight_dev = NULL;
++	}
++}
++
++/* AC & Battery subdriver */
++
++static struct power_supply yeeloong_ac, yeeloong_bat;
++
++#define RET (val->intval)
++
++#define BAT_CAP_CRITICAL 5
++#define BAT_CAP_HIGH     95
++
++#define get_bat(type) \
++	ec_read(REG_BAT_##type)
++
++#define get_bat_l(type) \
++	((get_bat(type##_HIGH) << 8) | get_bat(type##_LOW))
++
++static int yeeloong_get_ac_props(struct power_supply *psy,
++				enum power_supply_property psp,
++				union power_supply_propval *val)
++{
++	if (psp == POWER_SUPPLY_PROP_ONLINE)
++		RET = !!(get_bat(POWER) & BIT_BAT_POWER_ACIN);
++
++	return 0;
++}
++
++static enum power_supply_property yeeloong_ac_props[] = {
++	POWER_SUPPLY_PROP_ONLINE,
++};
++
++static struct power_supply yeeloong_ac = {
++	.name = "yeeloong-ac",
++	.type = POWER_SUPPLY_TYPE_MAINS,
++	.properties = yeeloong_ac_props,
++	.num_properties = ARRAY_SIZE(yeeloong_ac_props),
++	.get_property = yeeloong_get_ac_props,
++};
++
++static inline bool is_bat_in(void)
++{
++	return !!(get_bat(STATUS) & BIT_BAT_STATUS_IN);
++}
++
++static int get_bat_temp(void)
++{
++	return get_bat_l(TEMPERATURE) * 10;
++}
++
++static int get_bat_current(void)
++{
++	return -(s16)get_bat_l(CURRENT);
++}
++
++static int get_bat_voltage(void)
++{
++	return get_bat_l(VOLTAGE);
++}
++
++static char *get_manufacturer(void)
++{
++	return (get_bat(VENDOR) == FLAG_BAT_VENDOR_SANYO) ? "SANYO" : "SIMPLO";
++}
++
++static int get_relative_cap(void)
++{
++	/*
++	 * When the relative capacity becomes 2, the hardware is observed to
++	 * have been turned off forcely. so, we must tune it be suitable to
++	 * make the software do related actions.
++	 */
++	int tmp = get_bat_l(RELATIVE_CAP);
++
++	if (tmp <= (BAT_CAP_CRITICAL * 2))
++		tmp -= 3;
++
++	return tmp;
++}
++
++static int yeeloong_get_bat_props(struct power_supply *psy,
++				     enum power_supply_property psp,
++				     union power_supply_propval *val)
++{
++	switch (psp) {
++	/* Fixed information */
++	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
++		/* mV -> µV */
++		RET = get_bat_l(DESIGN_VOL) * 1000;
++		break;
++	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
++		/* mAh->µAh */
++		RET = get_bat_l(DESIGN_CAP) * 1000;
++		break;
++	case POWER_SUPPLY_PROP_CHARGE_FULL:
++		/* µAh */
++		RET = get_bat_l(FULLCHG_CAP) * 1000;
++		break;
++	case POWER_SUPPLY_PROP_MANUFACTURER:
++		val->strval = get_manufacturer();
++		break;
++	/* Dynamic information */
++	case POWER_SUPPLY_PROP_PRESENT:
++		RET = is_bat_in();
++		break;
++	case POWER_SUPPLY_PROP_CURRENT_NOW:
++		/* mA -> µA */
++		RET = is_bat_in() ? get_bat_current() * 1000 : 0;
++		break;
++	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
++		/* mV -> µV */
++		RET = is_bat_in() ? get_bat_voltage() * 1000 : 0;
++		break;
++	case POWER_SUPPLY_PROP_TEMP:
++		/* Celcius */
++		RET = is_bat_in() ? get_bat_temp() : 0;
++		break;
++	case POWER_SUPPLY_PROP_CAPACITY:
++		RET = is_bat_in() ? get_relative_cap() : 0;
++		break;
++	case POWER_SUPPLY_PROP_CAPACITY_LEVEL: {
++		int status;
++
++		if (!is_bat_in()) {
++			RET = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
++			break;
++		}
++
++		status = get_bat(STATUS);
++		RET = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
++
++		if (unlikely(status & BIT_BAT_STATUS_DESTROY)) {
++			RET = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
++			break;
++		}
++
++		if (status & BIT_BAT_STATUS_FULL)
++			RET = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
++		else {
++			int curr_cap = get_relative_cap();
++
++			if (status & BIT_BAT_STATUS_LOW) {
++				RET = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
++				if (curr_cap <= BAT_CAP_CRITICAL)
++					RET = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
++			} else if (curr_cap >= BAT_CAP_HIGH)
++				RET = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
++		}
++	} break;
++	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
++		/* seconds */
++		RET = is_bat_in() ? (get_relative_cap() - 3) * 54 + 142 : 0;
++		break;
++	case POWER_SUPPLY_PROP_STATUS: {
++			int charge = get_bat(CHARGE);
++
++			RET = POWER_SUPPLY_STATUS_UNKNOWN;
++			if (charge & FLAG_BAT_CHARGE_DISCHARGE)
++				RET = POWER_SUPPLY_STATUS_DISCHARGING;
++			else if (charge & FLAG_BAT_CHARGE_CHARGE)
++				RET = POWER_SUPPLY_STATUS_CHARGING;
++	} break;
++	case POWER_SUPPLY_PROP_HEALTH: {
++			int status;
++
++			if (!is_bat_in()) {
++				RET = POWER_SUPPLY_HEALTH_UNKNOWN;
++				break;
++			}
++
++			status = get_bat(STATUS);
++			RET = POWER_SUPPLY_HEALTH_GOOD;
++
++			if (status & (BIT_BAT_STATUS_DESTROY |
++						BIT_BAT_STATUS_LOW))
++				RET = POWER_SUPPLY_HEALTH_DEAD;
++			if (get_bat(CHARGE_STATUS) &
++					BIT_BAT_CHARGE_STATUS_OVERTEMP)
++				RET = POWER_SUPPLY_HEALTH_OVERHEAT;
++	} break;
++	case POWER_SUPPLY_PROP_CHARGE_NOW:	/* 1/100(%)*1000 µAh */
++		RET = get_relative_cap() * get_bat_l(FULLCHG_CAP) * 10;
++		break;
++	default:
++		return -EINVAL;
++	}
++	return 0;
++}
++#undef RET
++
++static enum power_supply_property yeeloong_bat_props[] = {
++	POWER_SUPPLY_PROP_STATUS,
++	POWER_SUPPLY_PROP_PRESENT,
++	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
++	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
++	POWER_SUPPLY_PROP_CHARGE_FULL,
++	POWER_SUPPLY_PROP_CHARGE_NOW,
++	POWER_SUPPLY_PROP_CURRENT_NOW,
++	POWER_SUPPLY_PROP_VOLTAGE_NOW,
++	POWER_SUPPLY_PROP_HEALTH,
++	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
++	POWER_SUPPLY_PROP_CAPACITY,
++	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
++	POWER_SUPPLY_PROP_TEMP,
++	POWER_SUPPLY_PROP_MANUFACTURER,
++};
++
++static struct power_supply yeeloong_bat = {
++	.name = "yeeloong-bat",
++	.type = POWER_SUPPLY_TYPE_BATTERY,
++	.properties = yeeloong_bat_props,
++	.num_properties = ARRAY_SIZE(yeeloong_bat_props),
++	.get_property = yeeloong_get_bat_props,
++};
++
++static int ac_bat_initialized;
++
++static int yeeloong_bat_init(void)
++{
++	int ret;
++
++	ret = power_supply_register(NULL, &yeeloong_ac);
++	if (ret)
++		return ret;
++	ret = power_supply_register(NULL, &yeeloong_bat);
++	if (ret) {
++		power_supply_unregister(&yeeloong_ac);
++		return ret;
++	}
++	ac_bat_initialized = 1;
++
++	return 0;
++}
++
++static void yeeloong_bat_exit(void)
++{
++	ac_bat_initialized = 0;
++
++	power_supply_unregister(&yeeloong_ac);
++	power_supply_unregister(&yeeloong_bat);
++}
++/* hwmon subdriver */
++
++#define MIN_FAN_SPEED 0
++#define MAX_FAN_SPEED 3
++
++#define get_fan(type) \
++	ec_read(REG_FAN_##type)
++
++#define set_fan(type, val) \
++	ec_write(REG_FAN_##type, val)
++
++static inline int get_fan_speed_level(void)
++{
++	return get_fan(SPEED_LEVEL);
++}
++static inline void set_fan_speed_level(int speed)
++{
++	set_fan(SPEED_LEVEL, speed);
++}
++
++static inline int get_fan_mode(void)
++{
++	return get_fan(AUTO_MAN_SWITCH);
++}
++static inline void set_fan_mode(int mode)
++{
++	set_fan(AUTO_MAN_SWITCH, mode);
++}
++
++/*
++ * 3 different modes: Full speed(0); manual mode(1); auto mode(2)
++ */
++static int get_fan_pwm_enable(void)
++{
++	return (get_fan_mode() == BIT_FAN_AUTO) ? 2 :
++		(get_fan_speed_level() == MAX_FAN_SPEED) ? 0 : 1;
++}
++
++static void set_fan_pwm_enable(int mode)
++{
++	set_fan_mode((mode == 2) ? BIT_FAN_AUTO : BIT_FAN_MANUAL);
++	if (mode == 0)
++		set_fan_speed_level(MAX_FAN_SPEED);
++}
++
++static int get_fan_pwm(void)
++{
++	return get_fan_speed_level();
++}
++
++static void set_fan_pwm(int value)
++{
++	if (get_fan_mode() != BIT_FAN_MANUAL)
++		return;
++
++	value = clamp_val(value, MIN_FAN_SPEED, MAX_FAN_SPEED);
++
++	/* We must ensure the fan is on */
++	if (value > 0)
++		set_fan(CONTROL, ON);
++
++	set_fan_speed_level(value);
++}
++
++static inline int get_fan_speed(void)
++{
++	return ((get_fan(SPEED_HIGH) & 0x0f) << 8) | get_fan(SPEED_LOW);
++}
++
++static int get_fan_rpm(void)
++{
++	return FAN_SPEED_DIVIDER / get_fan_speed();
++}
++
++static int get_cpu_temp(void)
++{
++	return (s8)ec_read(REG_TEMPERATURE_VALUE) * 1000;
++}
++
++static int get_cpu_temp_max(void)
++{
++	return 60 * 1000;
++}
++
++static int get_bat_temp_alarm(void)
++{
++	return !!(get_bat(CHARGE_STATUS) & BIT_BAT_CHARGE_STATUS_OVERTEMP);
++}
++
++static ssize_t store_sys_hwmon(void (*set) (int), const char *buf, size_t count)
++{
++	int ret;
++	unsigned long value;
++
++	if (!count)
++		return 0;
++
++	ret = strict_strtoul(buf, 10, &value);
++	if (ret)
++		return ret;
++
++	set(value);
++
++	return count;
++}
++
++static ssize_t show_sys_hwmon(int (*get) (void), char *buf)
++{
++	return sprintf(buf, "%d\n", get());
++}
++
++#define CREATE_SENSOR_ATTR(_name, _mode, _set, _get)		\
++	static ssize_t show_##_name(struct device *dev,			\
++				    struct device_attribute *attr,	\
++				    char *buf)				\
++	{								\
++		return show_sys_hwmon(_set, buf);			\
++	}								\
++	static ssize_t store_##_name(struct device *dev,		\
++				     struct device_attribute *attr,	\
++				     const char *buf, size_t count)	\
++	{								\
++		return store_sys_hwmon(_get, buf, count);		\
++	}								\
++	static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
++
++CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, get_fan_rpm, NULL);
++CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR, get_fan_pwm, set_fan_pwm);
++CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, get_fan_pwm_enable,
++		set_fan_pwm_enable);
++CREATE_SENSOR_ATTR(temp1_input, S_IRUGO, get_cpu_temp, NULL);
++CREATE_SENSOR_ATTR(temp1_max, S_IRUGO, get_cpu_temp_max, NULL);
++CREATE_SENSOR_ATTR(temp2_input, S_IRUGO, get_bat_temp, NULL);
++CREATE_SENSOR_ATTR(temp2_max_alarm, S_IRUGO, get_bat_temp_alarm, NULL);
++CREATE_SENSOR_ATTR(curr1_input, S_IRUGO, get_bat_current, NULL);
++CREATE_SENSOR_ATTR(in1_input, S_IRUGO, get_bat_voltage, NULL);
++
++static ssize_t
++show_name(struct device *dev, struct device_attribute *attr, char *buf)
++{
++	return sprintf(buf, "yeeloong\n");
++}
++
++static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
++
++static struct attribute *hwmon_attributes[] = {
++	&sensor_dev_attr_pwm1.dev_attr.attr,
++	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
++	&sensor_dev_attr_fan1_input.dev_attr.attr,
++	&sensor_dev_attr_temp1_input.dev_attr.attr,
++	&sensor_dev_attr_temp1_max.dev_attr.attr,
++	&sensor_dev_attr_temp2_input.dev_attr.attr,
++	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
++	&sensor_dev_attr_curr1_input.dev_attr.attr,
++	&sensor_dev_attr_in1_input.dev_attr.attr,
++	&sensor_dev_attr_name.dev_attr.attr,
++	NULL
++};
++
++static struct attribute_group hwmon_attribute_group = {
++	.attrs = hwmon_attributes
++};
++
++static struct device *yeeloong_hwmon_dev;
++
++static int yeeloong_hwmon_init(void)
++{
++	int ret;
++
++	yeeloong_hwmon_dev = hwmon_device_register(NULL);
++	if (IS_ERR(yeeloong_hwmon_dev)) {
++		yeeloong_hwmon_dev = NULL;
++		return PTR_ERR(yeeloong_hwmon_dev);
++	}
++	ret = sysfs_create_group(&yeeloong_hwmon_dev->kobj,
++				 &hwmon_attribute_group);
++	if (ret) {
++		hwmon_device_unregister(yeeloong_hwmon_dev);
++		yeeloong_hwmon_dev = NULL;
++		return ret;
++	}
++	/* ensure fan is set to auto mode */
++	set_fan_pwm_enable(2);
++
++	return 0;
++}
++
++static void yeeloong_hwmon_exit(void)
++{
++	if (yeeloong_hwmon_dev) {
++		sysfs_remove_group(&yeeloong_hwmon_dev->kobj,
++				   &hwmon_attribute_group);
++		hwmon_device_unregister(yeeloong_hwmon_dev);
++		yeeloong_hwmon_dev = NULL;
++	}
++}
++
++/* video output subdriver */
++
++#define LCD	0
++#define CRT	1
++#define VOD_NUM	2	/* The total number of video output device*/
++
++static struct output_device *vod[VOD_NUM];
++
++static int vor[] = {REG_DISPLAY_LCD, REG_CRT_DETECT};
++
++static int get_vo_dev(struct output_device *od)
++{
++	int i, dev;
++
++	dev = -1;
++	for (i = 0; i < VOD_NUM; i++)
++		if (od == vod[i])
++			dev = i;
++
++	return dev;
++}
++
++static int vo_get_status(int dev)
++{
++	return ec_read(vor[dev]);
++}
++
++static int yeeloong_vo_get_status(struct output_device *od)
++{
++	int vd;
++
++	vd = get_vo_dev(od);
++	if (vd != -1)
++		return vo_get_status(vd);
++
++	return -ENODEV;
++}
++
++static void vo_set_state(int dev, int state)
++{
++	int addr;
++	unsigned long value;
++
++	switch (dev) {
++	case LCD:
++		addr = 0x31;
++		break;
++	case CRT:
++		addr = 0x21;
++		break;
++	default:
++		/* return directly if the wrong video output device */
++		return;
++	}
++
++	outb(addr, 0x3c4);
++	value = inb(0x3c5);
++
++	switch (dev) {
++	case LCD:
++		value |= (state ? 0x03 : 0x02);
++		break;
++	case CRT:
++		if (state)
++			clear_bit(7, &value);
++		else
++			set_bit(7, &value);
++		break;
++	default:
++		break;
++	}
++
++	outb(addr, 0x3c4);
++	outb(value, 0x3c5);
++
++	if (dev == LCD)
++		ec_write(REG_BACKLIGHT_CTRL, state);
++}
++
++static int yeeloong_vo_set_state(struct output_device *od)
++{
++	int vd;
++
++	vd = get_vo_dev(od);
++	if (vd == -1)
++		return -ENODEV;
++
++	if (vd == CRT && !vo_get_status(vd))
++		return 0;
++
++	vo_set_state(vd, !!od->request_state);
++
++	return 0;
++}
++
++static struct output_properties vop = {
++	.set_state = yeeloong_vo_set_state,
++	.get_status = yeeloong_vo_get_status,
++};
++
++static int yeeloong_vo_init(void)
++{
++	int ret, i;
++	char dev_name[VOD_NUM][4] = {"LCD", "CRT"};
++
++	/* Register video output device: lcd, crt */
++	for (i = 0; i < VOD_NUM; i++) {
++		vod[i] = video_output_register(dev_name[i], NULL, NULL, &vop);
++		if (IS_ERR(vod[i])) {
++			if (i != 0)
++				video_output_unregister(vod[i-1]);
++			ret = PTR_ERR(vod[i]);
++			vod[i] = NULL;
++			return ret;
++		}
++	}
++	/* Ensure LCD is on by default */
++	vo_set_state(LCD, ON);
++
++	/*
++	 * Turn off CRT by default, and will be enabled when the CRT
++	 * connectting event reported by SCI
++	 */
++	vo_set_state(CRT, OFF);
++
++	return 0;
++}
++
++static void yeeloong_vo_exit(void)
++{
++	int i;
++
++	for (i = 0; i < VOD_NUM; i++) {
++		if (vod[i]) {
++			video_output_unregister(vod[i]);
++			vod[i] = NULL;
++		}
++	}
++}
++
++/* lcd subdriver */
++
++struct lcd_device *lcd[VOD_NUM];
++
++static int get_lcd_dev(struct lcd_device *ld)
++{
++	int i, dev;
++
++	dev = -1;
++	for (i = 0; i < VOD_NUM; i++)
++		if (ld == lcd[i])
++			dev = i;
++
++	return dev;
++}
++
++static int yeeloong_lcd_set_power(struct lcd_device *ld, int power)
++{
++	int dev = get_lcd_dev(ld);
++
++	if (power == FB_BLANK_UNBLANK)
++		vo_set_state(dev, ON);
++	if (power == FB_BLANK_POWERDOWN)
++		vo_set_state(dev, OFF);
++
++	return 0;
++}
++
++static int yeeloong_lcd_get_power(struct lcd_device *ld)
++{
++	return vo_get_status(get_lcd_dev(ld));
++}
++
++static struct lcd_ops lcd_ops = {
++	.set_power = yeeloong_lcd_set_power,
++	.get_power = yeeloong_lcd_get_power,
++};
++
++static int yeeloong_lcd_init(void)
++{
++	int ret, i;
++	char dev_name[VOD_NUM][4] = {"LCD", "CRT"};
++
++	/* Register video output device: lcd, crt */
++	for (i = 0; i < VOD_NUM; i++) {
++		lcd[i] = lcd_device_register(dev_name[i], NULL, NULL, &lcd_ops);
++		if (IS_ERR(lcd[i])) {
++			if (i != 0)
++				lcd_device_unregister(lcd[i-1]);
++			ret = PTR_ERR(lcd[i]);
++			lcd[i] = NULL;
++			return ret;
++		}
++	}
++#if 0
++	/* This has been done by the vide output driver */
++
++	/* Ensure LCD is on by default */
++	vo_set_state(LCD, ON);
++
++	/*
++	 * Turn off CRT by default, and will be enabled when the CRT
++	 * connectting event reported by SCI
++	 */
++	vo_set_state(CRT, OFF);
++#endif
++	return 0;
++}
++
++static void yeeloong_lcd_exit(void)
++{
++	int i;
++
++	for (i = 0; i < VOD_NUM; i++) {
++		if (lcd[i]) {
++			lcd_device_unregister(lcd[i]);
++			lcd[i] = NULL;
++		}
++	}
++}
++
++/* hotkey subdriver */
++
++static struct input_dev *yeeloong_hotkey_dev;
++
++static atomic_t reboot_flag, sleep_flag;
++#define in_sleep() (&sleep_flag)
++#define in_reboot() (&reboot_flag)
++
++static const struct key_entry yeeloong_keymap[] = {
++	{KE_SW, EVENT_LID, { SW_LID } },
++	{KE_KEY, EVENT_CAMERA, { KEY_CAMERA } }, /* Fn + ESC */
++	{KE_KEY, EVENT_SLEEP, { KEY_SLEEP } }, /* Fn + F1 */
++	{KE_KEY, EVENT_BLACK_SCREEN, { KEY_DISPLAYTOGGLE } }, /* Fn + F2 */
++	{KE_KEY, EVENT_DISPLAY_TOGGLE, { KEY_SWITCHVIDEOMODE } }, /* Fn + F3 */
++	{KE_KEY, EVENT_AUDIO_MUTE, { KEY_MUTE } }, /* Fn + F4 */
++	{KE_KEY, EVENT_WLAN, { KEY_WLAN } }, /* Fn + F5 */
++	{KE_KEY, EVENT_DISPLAY_BRIGHTNESS, { KEY_BRIGHTNESSUP } }, /* Fn + up */
++	{KE_KEY, EVENT_DISPLAY_BRIGHTNESS, { KEY_BRIGHTNESSDOWN } }, /* Fn + down */
++	{KE_KEY, EVENT_AUDIO_VOLUME, { KEY_VOLUMEUP } }, /* Fn + right */
++	{KE_KEY, EVENT_AUDIO_VOLUME, { KEY_VOLUMEDOWN } }, /* Fn + left */
++	{KE_END, 0}
++};
++
++static int is_fake_event(u16 keycode)
++{
++	switch (keycode) {
++	case KEY_SLEEP:
++	case SW_LID:
++		return atomic_read(in_sleep()) | atomic_read(in_reboot());
++		break;
++	default:
++		break;
++	}
++	return 0;
++}
++
++static struct key_entry *get_event_key_entry(int event, int status)
++{
++	struct key_entry *ke;
++	static int old_brightness_status = -1;
++	static int old_volume_status = -1;
++
++	ke = sparse_keymap_entry_from_scancode(yeeloong_hotkey_dev, event);
++	if (!ke)
++		return NULL;
++
++	switch (event) {
++	case EVENT_DISPLAY_BRIGHTNESS:
++		/* current status > old one, means up */
++		if ((status < old_brightness_status) || (0 == status))
++			ke++;
++		old_brightness_status = status;
++		break;
++	case EVENT_AUDIO_VOLUME:
++		if ((status < old_volume_status) || (0 == status))
++			ke++;
++		old_volume_status = status;
++		break;
++	default:
++		break;
++	}
++
++	return ke;
++}
++
++static int report_lid_switch(int status)
++{
++	static int old_status;
++
++	/*
++	 * LID is a switch button, so, two continuous same status should be
++	 * ignored
++	 */
++	if (old_status != status) {
++		input_report_switch(yeeloong_hotkey_dev, SW_LID, !status);
++		input_sync(yeeloong_hotkey_dev);
++	}
++	old_status = status;
++
++	return status;
++}
++
++static int crt_detect_handler(int status)
++{
++	/*
++	 * When CRT is inserted, enable its output and disable the LCD output,
++	 * otherwise, do reversely.
++	 */
++	vo_set_state(CRT, status);
++	vo_set_state(LCD, !status);
++
++	return status;
++}
++
++static int displaytoggle_handler(int status)
++{
++	/* EC(>=PQ1D26) does this job for us, we can not do it again,
++	 * otherwise, the brightness will not resume to the normal level! */
++	if (ec_version_before("EC_VER=PQ1D26"))
++		vo_set_state(LCD, status);
++
++	return status;
++}
++
++static int mypow(int x, int y)
++{
++	int i, j = x;
++
++	for (i = 1; i < y; i++)
++		j *= j;
++
++	return j;
++}
++
++static int switchvideomode_handler(int status)
++{
++	/* Default status: CRT|LCD = 0|1 = 1 */
++	static int bin_state = 1;
++	int i;
++
++	/*
++	 * Only enable switch video output button
++	 * when CRT is connected
++	 */
++	if (!vo_get_status(CRT))
++		return 0;
++	/*
++	 * 2. no CRT connected: LCD on, CRT off
++	 * 3. BOTH on
++	 * 0. BOTH off
++	 * 1. LCD off, CRT on
++	 */
++
++	bin_state++;
++	if (bin_state > mypow(2, VOD_NUM) - 1)
++		bin_state = 0;
++	
++	for (i = 0; i < VOD_NUM; i++)
++		vo_set_state(i, bin_state & (1 << i));
++
++	return bin_state;
++}
++
++static int camera_handler(int status)
++{
++	int value;
++
++	value = ec_read(REG_CAMERA_CONTROL);
++	ec_write(REG_CAMERA_CONTROL, value | (1 << 1));
++
++	return status;
++}
++
++static int usb2_handler(int status)
++{
++	pr_emerg("USB2 Over Current occurred\n");
++
++	return status;
++}
++
++static int usb0_handler(int status)
++{
++	pr_emerg("USB0 Over Current occurred\n");
++
++	return status;
++}
++
++static int ac_bat_handler(int status)
++{
++	if (ac_bat_initialized) {
++		power_supply_changed(&yeeloong_ac);
++		power_supply_changed(&yeeloong_bat);
++	}
++
++	return status;
++}
++
++struct sci_event {
++	int reg;
++	sci_handler handler;
++};
++
++static const struct sci_event se[] = {
++	[EVENT_AC_BAT] = {0, ac_bat_handler},
++	[EVENT_AUDIO_MUTE] = {REG_AUDIO_MUTE, NULL},
++	[EVENT_AUDIO_VOLUME] = {REG_AUDIO_VOLUME, NULL},
++	[EVENT_CRT_DETECT] = {REG_CRT_DETECT, crt_detect_handler},
++	[EVENT_CAMERA] = {REG_CAMERA_STATUS, camera_handler},
++	[EVENT_BLACK_SCREEN] = {REG_DISPLAY_LCD, displaytoggle_handler},
++	[EVENT_DISPLAY_BRIGHTNESS] = {REG_DISPLAY_BRIGHTNESS, NULL},
++	[EVENT_LID] = {REG_LID_DETECT, NULL},
++	[EVENT_DISPLAY_TOGGLE] = {0, switchvideomode_handler},
++	[EVENT_USB_OC0] = {REG_USB2_FLAG, usb0_handler},
++	[EVENT_USB_OC2] = {REG_USB2_FLAG, usb2_handler},
++	[EVENT_WLAN] = {REG_WLAN, NULL},
++};
++
++static void do_event_action(int event)
++{
++	int status = -1;
++	struct key_entry *ke;
++	struct sci_event *sep;
++
++	sep = (struct sci_event *)&se[event];
++
++	if (sep->reg != 0)
++		status = ec_read(sep->reg);
++
++	if (status == -1) {
++		/* ec_read hasn't been called, status is invalid */
++		return;
++	}
++
++	if (sep->handler != NULL)
++		status = sep->handler(status);
++
++	pr_debug("%s: event: %d status: %d\n", __func__, event, status);
++
++	/* Report current key to user-space */
++	ke = get_event_key_entry(event, status);
++
++	/*
++	 * Ignore the LID and SLEEP event when we are already in sleep or
++	 * reboot state, this will avoid the recursive pm operations. but note:
++	 * the report_lid_switch() called in arch/mips/loongson/lemote-2f/pm.c
++	 * is necessary, because it is used to wake the system from sleep
++	 * state. In the future, perhaps SW_LID should works like SLEEP, no
++	 * need to function as a SWITCH, just report the state when the LID is
++	 * closed is enough, this event can tell the software to "SLEEP", no
++	 * need to tell the softwares when we are resuming from "SLEEP".
++	 */
++	if (ke && !is_fake_event(ke->keycode)) {
++		if (ke->keycode == SW_LID)
++			report_lid_switch(status);
++		else
++			sparse_keymap_report_entry(yeeloong_hotkey_dev, ke, 1,
++					true);
++	}
++}
++
++/*
++ * SCI(system control interrupt) main interrupt routine
++ *
++ * We will do the query and get event number together so the interrupt routine
++ * should be longer than 120us now at least 3ms elpase for it.
++ */
++static irqreturn_t sci_irq_handler(int irq, void *dev_id)
++{
++	int ret, event;
++
++	if (SCI_IRQ_NUM != irq)
++		return IRQ_NONE;
++
++	/* Query the event number */
++	ret = ec_query_event_num();
++	if (ret < 0)
++		return IRQ_NONE;
++
++	event = ec_get_event_num();
++	if (event < EVENT_START || event > EVENT_END)
++		return IRQ_NONE;
++
++	/* Execute corresponding actions */
++	do_event_action(event);
++
++	return IRQ_HANDLED;
++}
++
++/*
++ * Config and init some msr and gpio register properly.
++ */
++static int sci_irq_init(void)
++{
++	u32 hi, lo;
++	u32 gpio_base;
++	unsigned long flags;
++	int ret;
++
++	/* Get gpio base */
++	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &lo);
++	gpio_base = lo & 0xff00;
++
++	/* Filter the former kb3310 interrupt for security */
++	ret = ec_query_event_num();
++	if (ret)
++		return ret;
++
++	/* For filtering next number interrupt */
++	udelay(10000);
++
++	/* Set gpio native registers and msrs for GPIO27 SCI EVENT PIN
++	 * gpio :
++	 *      input, pull-up, no-invert, event-count and value 0,
++	 *      no-filter, no edge mode
++	 *      gpio27 map to Virtual gpio0
++	 * msr :
++	 *      no primary and lpc
++	 *      Unrestricted Z input to IG10 from Virtual gpio 0.
++	 */
++	local_irq_save(flags);
++	_rdmsr(0x80000024, &hi, &lo);
++	lo &= ~(1 << 10);
++	_wrmsr(0x80000024, hi, lo);
++	_rdmsr(0x80000025, &hi, &lo);
++	lo &= ~(1 << 10);
++	_wrmsr(0x80000025, hi, lo);
++	_rdmsr(0x80000023, &hi, &lo);
++	lo |= (0x0a << 0);
++	_wrmsr(0x80000023, hi, lo);
++	local_irq_restore(flags);
++
++	/* Set gpio27 as sci interrupt
++	 *
++	 * input, pull-up, no-fliter, no-negedge, invert
++	 * the sci event is just about 120us
++	 */
++	asm(".set noreorder\n");
++	/*  input enable */
++	outl(0x00000800, (gpio_base | 0xA0));
++	/*  revert the input */
++	outl(0x00000800, (gpio_base | 0xA4));
++	/*  event-int enable */
++	outl(0x00000800, (gpio_base | 0xB8));
++	asm(".set reorder\n");
++
++	return 0;
++}
++
++static int notify_reboot(struct notifier_block *nb, unsigned long event, void *buf)
++{
++	switch (event) {
++	case SYS_RESTART:
++	case SYS_HALT:
++	case SYS_POWER_OFF:
++		atomic_set(in_reboot(), 1);
++		break;
++	default:
++		return NOTIFY_DONE;
++	}
++
++	return NOTIFY_OK;
++}
++
++static int notify_pm(struct notifier_block *nb, unsigned long event, void *buf)
++{
++	switch (event) {
++	case PM_HIBERNATION_PREPARE:
++	case PM_SUSPEND_PREPARE:
++		atomic_inc(in_sleep());
++		break;
++	case PM_POST_HIBERNATION:
++	case PM_POST_SUSPEND:
++	case PM_RESTORE_PREPARE:	/* do we need this ?? */
++		atomic_dec(in_sleep());
++		break;
++	default:
++		return NOTIFY_DONE;
++	}
++
++	pr_debug("%s: event = %lu, in_sleep() = %d\n", __func__, event,
++			atomic_read(in_sleep()));
++
++	return NOTIFY_OK;
++}
++
++static struct notifier_block reboot_notifier = {
++	.notifier_call = notify_reboot,
++};
++
++static struct notifier_block pm_notifier = {
++	.notifier_call = notify_pm,
++};
++
++static int yeeloong_hotkey_init(void)
++{
++	int ret = 0;
++
++	ret = register_reboot_notifier(&reboot_notifier);
++	if (ret) {
++		pr_err("Can't register reboot notifier\n");
++		goto end;
++	}
++
++	ret = register_pm_notifier(&pm_notifier);
++	if (ret) {
++		pr_err("Can't register pm notifier\n");
++		goto free_reboot_notifier;
++	}
++
++	ret = sci_irq_init();
++	if (ret) {
++		pr_err("Can't init SCI interrupt\n");
++		goto free_pm_notifier;
++	}
++
++	ret = request_threaded_irq(SCI_IRQ_NUM, NULL, &sci_irq_handler,
++			IRQF_ONESHOT, "sci", NULL);
++	if (ret) {
++		pr_err("Can't thread SCI interrupt handler\n");
++		goto free_pm_notifier;
++	}
++
++	yeeloong_hotkey_dev = input_allocate_device();
++
++	if (!yeeloong_hotkey_dev) {
++		ret = -ENOMEM;
++		goto free_irq;
++	}
++
++	yeeloong_hotkey_dev->name = "HotKeys";
++	yeeloong_hotkey_dev->phys = "button/input0";
++	yeeloong_hotkey_dev->id.bustype = BUS_HOST;
++	yeeloong_hotkey_dev->dev.parent = NULL;
++
++	ret = sparse_keymap_setup(yeeloong_hotkey_dev, yeeloong_keymap, NULL);
++	if (ret) {
++		pr_err("Failed to setup input device keymap\n");
++		goto free_dev;
++	}
++
++	ret = input_register_device(yeeloong_hotkey_dev);
++	if (ret)
++		goto free_keymap;
++
++	/* Update the current status of LID */
++	report_lid_switch(ON);
++
++#ifdef CONFIG_LOONGSON_SUSPEND
++	/* Install the real yeeloong_report_lid_status for pm.c */
++	yeeloong_report_lid_status = report_lid_switch;
++#endif
++	return 0;
++
++free_keymap:
++	sparse_keymap_free(yeeloong_hotkey_dev);
++free_dev:
++	input_free_device(yeeloong_hotkey_dev);
++free_irq:
++	free_irq(SCI_IRQ_NUM, NULL);
++free_pm_notifier:
++	unregister_pm_notifier(&pm_notifier);
++free_reboot_notifier:
++	unregister_reboot_notifier(&reboot_notifier);
++end:
++	return ret;
++}
++
++static void yeeloong_hotkey_exit(void)
++{
++	/* Free irq */
++	free_irq(SCI_IRQ_NUM, NULL);
++
++#ifdef CONFIG_LOONGSON_SUSPEND
++	/* Uninstall yeeloong_report_lid_status for pm.c */
++	if (yeeloong_report_lid_status == report_lid_switch)
++		yeeloong_report_lid_status = NULL;
++#endif
++
++	if (yeeloong_hotkey_dev) {
++		sparse_keymap_free(yeeloong_hotkey_dev);
++		input_unregister_device(yeeloong_hotkey_dev);
++		yeeloong_hotkey_dev = NULL;
++	}
++}
++
++#ifdef CONFIG_PM
++static void usb_ports_set(int status)
++{
++	status = !!status;
++
++	ec_write(REG_USB0_FLAG, status);
++	ec_write(REG_USB1_FLAG, status);
++	ec_write(REG_USB2_FLAG, status);
++}
++
++static int yeeloong_suspend(struct device *dev)
++
++{
++	if (ec_version_before("EC_VER=PQ1D27"))
++		vo_set_state(LCD, OFF);
++	vo_set_state(CRT, OFF);
++	usb_ports_set(OFF);
++
++	return 0;
++}
++
++static int yeeloong_resume(struct device *dev)
++{
++	int ret;
++
++	if (ec_version_before("EC_VER=PQ1D27"))
++		vo_set_state(LCD, ON);
++	vo_set_state(CRT, ON);
++	usb_ports_set(ON);
++
++	ret = sci_irq_init();
++	if (ret)
++		return -EFAULT;
++
++	return 0;
++}
++
++static const SIMPLE_DEV_PM_OPS(yeeloong_pm_ops, yeeloong_suspend,
++	yeeloong_resume);
++#endif
++
++static struct platform_device_id platform_device_ids[] = {
++	{
++		.name = "yeeloong_laptop",
++	},
++	{}
++};
++
++MODULE_DEVICE_TABLE(platform, platform_device_ids);
++
++static struct platform_driver platform_driver = {
++	.driver = {
++		.name = "yeeloong_laptop",
++		.owner = THIS_MODULE,
++#ifdef CONFIG_PM
++		.pm = &yeeloong_pm_ops,
++#endif
++	},
++	.id_table = platform_device_ids,
++};
++
++static int __init yeeloong_init(void)
++{
++	int ret;
++
++	pr_info("YeeLoong Laptop platform specific driver loaded.\n");
++
++	/* Register platform stuff */
++	ret = platform_driver_register(&platform_driver);
++	if (ret) {
++		pr_err("Failed to register YeeLoong platform driver.\n");
++		return ret;
++	}
++
++#define yeeloong_init_drv(drv, alias) do {			\
++	pr_info("Registered YeeLoong " alias " driver.\n");	\
++	ret = yeeloong_ ## drv ## _init();			\
++	if (ret) {						\
++		pr_err("Failed to register YeeLoong " alias " driver.\n");	\
++		yeeloong_ ## drv ## _exit();			\
++		return ret;					\
++	}							\
++} while (0)
++
++	yeeloong_init_drv(backlight, "backlight");
++	yeeloong_init_drv(bat, "battery and AC");
++	yeeloong_init_drv(hwmon, "hardware monitor");
++	yeeloong_init_drv(vo, "video output");
++	yeeloong_init_drv(lcd, "lcd output");
++	yeeloong_init_drv(hotkey, "hotkey input");
++
++	return 0;
++}
++
++static void __exit yeeloong_exit(void)
++{
++	yeeloong_hotkey_exit();
++	yeeloong_lcd_exit();
++	yeeloong_vo_exit();
++	yeeloong_hwmon_exit();
++	yeeloong_bat_exit();
++	yeeloong_backlight_exit();
++	platform_driver_unregister(&platform_driver);
++
++	pr_info("YeeLoong platform specific driver unloaded.\n");
++}
++
++module_init(yeeloong_init);
++module_exit(yeeloong_exit);
++
++MODULE_AUTHOR("Wu Zhangjin <wuzhangjin@gmail.com>; Liu Junliang <liujl@lemote.com>");
++MODULE_DESCRIPTION("YeeLoong laptop driver");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
+index db933de..1232c4b 100644
+--- a/drivers/rtc/Kconfig
++++ b/drivers/rtc/Kconfig
+@@ -649,6 +649,7 @@ comment "Platform RTC drivers"
+ config RTC_DRV_CMOS
+ 	tristate "PC-style 'CMOS'"
+ 	depends on X86 || ARM || M32R || PPC || MIPS || SPARC64
++	depends on !DEXXON_GDIUM
+ 	default y if X86
+ 	help
+ 	  Say "yes" here to get direct support for the real time clock
+diff --git a/drivers/staging/sm7xxfb/sm7xxfb.c b/drivers/staging/sm7xxfb/sm7xxfb.c
+index 6176d98..e40ce80 100644
+--- a/drivers/staging/sm7xxfb/sm7xxfb.c
++++ b/drivers/staging/sm7xxfb/sm7xxfb.c
+@@ -101,6 +101,7 @@ static struct vesa_mode vesa_mode_table[] = {
+ 	{"0x307", 1280, 1024, 8},
+ 
+ 	{"0x311", 640,  480,  16},
++	{"0x313", 800,  480,  16},
+ 	{"0x314", 800,  600,  16},
+ 	{"0x317", 1024, 768,  16},
+ 	{"0x31A", 1280, 1024, 16},
+diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
+index 34fc86c..1569247 100644
+--- a/drivers/usb/host/ohci-hcd.c
++++ b/drivers/usb/host/ohci-hcd.c
+@@ -864,9 +864,13 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
+ 	}
+ 
+ 	if (ints & OHCI_INTR_WDH) {
+-		spin_lock (&ohci->lock);
+-		dl_done_list (ohci);
+-		spin_unlock (&ohci->lock);
++		if (ohci->hcca->done_head == 0) {
++			ints &= ~OHCI_INTR_WDH;
++		} else {
++			spin_lock (&ohci->lock);
++			dl_done_list (ohci);
++			spin_unlock (&ohci->lock);
++		}
+ 	}
+ 
+ 	if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) {
+diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
+index 2f3aceb..6647c3f 100644
+--- a/drivers/usb/host/pci-quirks.c
++++ b/drivers/usb/host/pci-quirks.c
+@@ -454,6 +454,7 @@ void usb_amd_dev_put(void)
+ }
+ EXPORT_SYMBOL_GPL(usb_amd_dev_put);
+ 
++#if defined(CONFIG_USB_UHCI_HCD) || defined(CONFIG_USB_UHCI_HCD_MODULE)
+ /*
+  * Make sure the controller is completely inactive, unable to
+  * generate interrupts or do DMA.
+@@ -561,12 +562,16 @@ static void quirk_usb_handoff_uhci(struct pci_dev *pdev)
+ 	if (base)
+ 		uhci_check_and_reset_hc(pdev, base);
+ }
++#else
++#define quirk_usb_handoff_uhci(x) do { } while (0)
++#endif /* CONFIG_USB_UHCI_HCD* */
+ 
+ static int mmio_resource_enabled(struct pci_dev *pdev, int idx)
+ {
+ 	return pci_resource_start(pdev, idx) && mmio_enabled(pdev);
+ }
+ 
++#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+ static void quirk_usb_handoff_ohci(struct pci_dev *pdev)
+ {
+ 	void __iomem *base;
+@@ -633,7 +638,11 @@ static void quirk_usb_handoff_ohci(struct pci_dev *pdev)
+ 	/* Now the controller is safely in SUSPEND and nothing can wake it up */
+ 	iounmap(base);
+ }
++#else
++#define quirk_usb_handoff_ohci(x) do { } while(0)
++#endif /* CONFIG_USB_OHCI_HCD* */
+ 
++#if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_EHCI_HCD_MODULE)
+ static const struct dmi_system_id ehci_dmi_nohandoff_table[] = {
+ 	{
+ 		/*  Pegatron Lucid (ExoPC) */
+@@ -806,6 +815,9 @@ static void quirk_usb_disable_ehci(struct pci_dev *pdev)
+ 
+ 	iounmap(base);
+ }
++#else
++#define quirk_usb_disable_ehci(x) do { } while (0)
++#endif /* CONFIG_USB_EHCI_HCD* */
+ 
+ /*
+  * handshake - spin reading a register until handshake completes
+@@ -945,6 +957,7 @@ void usb_disable_xhci_ports(struct pci_dev *xhci_pdev)
+ }
+ EXPORT_SYMBOL_GPL(usb_disable_xhci_ports);
+ 
++#if defined(CONFIG_USB_XHCI_HCD) || defined(CONFIG_USB_XHCI_HCD_MODULE)
+ /**
+  * PCI Quirks for xHCI.
+  *
+@@ -1052,6 +1065,9 @@ hc_init:
+ 
+ 	iounmap(base);
+ }
++#else
++#define quirk_usb_handoff_xhci(x) do { } while (0)
++#endif /* CONFIG_USB_UHCI_HCD* */
+ 
+ static void quirk_usb_early_handoff(struct pci_dev *pdev)
+ {
+diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
+index 8b34841..54e494d 100644
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -79,6 +79,9 @@ static void option_instat_callback(struct urb *urb);
+ #define OPTION_PRODUCT_ETNA_KOI_MODEM		0x7100
+ #define OPTION_PRODUCT_GTM380_MODEM		0x7201
+ 
++#define HUAWO_VENDOR_ID				0x21F5
++#define HUAWO_PRODUCT_E1621			0x2008
++
+ #define HUAWEI_VENDOR_ID			0x12D1
+ #define HUAWEI_PRODUCT_E173			0x140C
+ #define HUAWEI_PRODUCT_E1750			0x1406
+@@ -633,6 +636,7 @@ static const struct usb_device_id option_ids[] = {
+ 	{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) },
+ 	{ USB_DEVICE(QUANTA_VENDOR_ID, 0xea42),
+ 		.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
++	{ USB_DEVICE(HUAWO_VENDOR_ID, HUAWO_PRODUCT_E1621) },	/* QUANTA 6500 chips, Unicom extensive use of this card */
+ 	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c05, USB_CLASS_COMM, 0x02, 0xff) },
+ 	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c1f, USB_CLASS_COMM, 0x02, 0xff) },
+ 	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c23, USB_CLASS_COMM, 0x02, 0xff) },
+diff --git a/include/linux/sm501.h b/include/linux/sm501.h
+index 02fde50..a8677f0 100644
+--- a/include/linux/sm501.h
++++ b/include/linux/sm501.h
+@@ -27,6 +27,9 @@ extern unsigned long sm501_set_clock(struct device *dev,
+ extern unsigned long sm501_find_clock(struct device *dev,
+ 				      int clksrc, unsigned long req_freq);
+ 
++extern void sm501_configure_gpio(struct device *dev,
++				unsigned int gpio, unsigned char mode);
++
+ /* sm501_misc_control
+  *
+  * Modify the SM501's MISC_CONTROL register
+@@ -122,6 +125,7 @@ struct sm501_reg_init {
+ #define SM501_USE_AC97		(1<<7)
+ #define SM501_USE_I2S		(1<<8)
+ #define SM501_USE_GPIO		(1<<9)
++#define SM501_USE_PWM		(1<<10)
+ 
+ #define SM501_USE_ALL		(0xffffffff)
+ 
+diff --git a/init/calibrate.c b/init/calibrate.c
+index 520702d..e78762a 100644
+--- a/init/calibrate.c
++++ b/init/calibrate.c
+@@ -21,6 +21,7 @@ static int __init lpj_setup(char *str)
+ 
+ __setup("lpj=", lpj_setup);
+ 
++#ifndef ARCH_HAS_PREPARED_LPJ
+ #ifdef ARCH_HAS_READ_CURRENT_TIMER
+ 
+ /* This routine uses the read_current_timer() routine and gets the
+@@ -171,6 +172,7 @@ static unsigned long calibrate_delay_direct(void)
+ 	return 0;
+ }
+ #endif
++#endif	/* ARCH_HAS_PREPARED_LPJ */
+ 
+ /*
+  * This is the number of bits of precision for the loops_per_jiffy.  Each
+@@ -282,6 +284,7 @@ void calibrate_delay(void)
+ 		lpj = lpj_fine;
+ 		pr_info("Calibrating delay loop (skipped), "
+ 			"value calculated using timer frequency.. ");
++#ifndef ARCH_HAS_PREPARED_LPJ
+ 	} else if ((lpj = calibrate_delay_is_known())) {
+ 		;
+ 	} else if ((lpj = calibrate_delay_direct()) != 0) {
+@@ -292,6 +295,7 @@ void calibrate_delay(void)
+ 		if (!printed)
+ 			pr_info("Calibrating delay loop... ");
+ 		lpj = calibrate_delay_converge();
++#endif	/* ARCH_HAS_PREPARED_LPJ */
+ 	}
+ 	per_cpu(cpu_loops_per_jiffy, this_cpu) = lpj;
+ 	if (!printed)
+diff --git a/net/rfkill/core.c b/net/rfkill/core.c
+index ed7e0b4..6cb1ae8 100644
+--- a/net/rfkill/core.c
++++ b/net/rfkill/core.c
+@@ -111,7 +111,7 @@ static LIST_HEAD(rfkill_list);	/* list of registered rf switches */
+ static DEFINE_MUTEX(rfkill_global_mutex);
+ static LIST_HEAD(rfkill_fds);	/* list of open fds of /dev/rfkill */
+ 
+-static unsigned int rfkill_default_state = 1;
++static unsigned int rfkill_default_state;	/* default: 0 = radio off */
+ module_param_named(default_state, rfkill_default_state, uint, 0444);
+ MODULE_PARM_DESC(default_state,
+ 		 "Default initial state for all radio types, 0 = radio off");
+diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
+index 91280b8..6b147ae 100755
+--- a/scripts/recordmcount.pl
++++ b/scripts/recordmcount.pl
+@@ -307,14 +307,33 @@ if ($arch eq "x86_64") {
+     $cc .= " -m64";
+     $objcopy .= " -O elf64-sparc";
+ } elsif ($arch eq "mips") {
+-    # To enable module support, we need to enable the -mlong-calls option
+-    # of gcc for module, after using this option, we can not get the real
+-    # offset of the calling to _mcount, but the offset of the lui
+-    # instruction or the addiu one. herein, we record the address of the
+-    # first one, and then we can replace this instruction by a branch
+-    # instruction to jump over the profiling function to filter the
+-    # indicated functions, or swith back to the lui instruction to trace
+-    # them, which means dynamic tracing.
++    # <For kernel>
++    # To disable tracing, just replace "jal _mcount" with nop;
++    # to enable tracing, replace back. so, the offset 14 is
++    # needed to be recorded.
++    #
++    #     10:   03e0082d        move    at,ra
++    #	  14:   0c000000        jal     0
++    #                    14: R_MIPS_26   _mcount
++    #                    14: R_MIPS_NONE *ABS*
++    #                    14: R_MIPS_NONE *ABS*
++    #	 18:   00020021        nop
++    #
++    # <For module>
++    #
++    # If no long call(-mlong-calls), the same to kernel.
++    #
++    # If the module space differs from the kernel space, long
++    # call is needed, as a result, the address of _mcount is
++    # needed to be recorded in a register and then jump from
++    # module space to kernel space via "jalr <register>". To
++    # disable tracing, "jalr <register>" can be replaced by
++    # nop; to enable tracing, replace it back. Since the
++    # offset of "jalr <register>" is not easy to be matched,
++    # the offset of the 1st _mcount below is recorded and to
++    # disable tracing, "lui v1, 0x0" is substituted with "b
++    # label", which jumps over "jalr <register>"; to enable
++    # tracing, replace it back.
+     #
+     #       c:	3c030000 	lui	v1,0x0
+     #			c: R_MIPS_HI16	_mcount
+@@ -326,19 +345,12 @@ if ($arch eq "x86_64") {
+     #			10: R_MIPS_NONE	*ABS*
+     #      14:	03e0082d 	move	at,ra
+     #      18:	0060f809 	jalr	v1
++    #                     label:
+     #
+-    # for the kernel:
+-    #
+-    #     10:   03e0082d        move    at,ra
+-    #	  14:   0c000000        jal     0 <loongson_halt>
+-    #                    14: R_MIPS_26   _mcount
+-    #                    14: R_MIPS_NONE *ABS*
+-    #                    14: R_MIPS_NONE *ABS*
+-    #	 18:   00020021        nop
+     if ($is_module eq "0") {
+ 	    $mcount_regex = "^\\s*([0-9a-fA-F]+): R_MIPS_26\\s+_mcount\$";
+     } else {
+-	    $mcount_regex = "^\\s*([0-9a-fA-F]+): R_MIPS_HI16\\s+_mcount\$";
++	    $mcount_regex = "^\\s*([0-9a-fA-F]+): R_MIPS_(HI16|26)\\s+_mcount\$";
+     }
+     $objdump .= " -Melf-trad".$endian."mips ";
+ 
+diff --git a/scripts/sstrip.sh b/scripts/sstrip.sh
+new file mode 100755
+index 0000000..49b973a
+--- /dev/null
++++ b/scripts/sstrip.sh
+@@ -0,0 +1,59 @@
++#!/bin/bash
++# sstrip.sh -- strip the section table of an elf file
++#
++# Copyright (C) 2010 Wu Zhangjin, wuzhangjin@gmail.com
++# Licensed under the GPLv2
++#
++# Since the section table is useless for the embedded device, it can be
++# stripped out.
++#
++# Note: Some bootloader may check the section table but most of the time, it
++# may be not really used, If it really need the section table, it may need the
++# decompressed kernel image.
++
++# Usage
++
++function usage
++{
++cat <<EOF
++
++	# sstrip.sh -- strip the section table of an elf file
++
++	# Input: elf file
++	# Output: truncated elf file without the section table
++	# Usage: sstrip.sh /path/to/image
++
++EOF
++}
++
++# Do some necessary check
++IMAGE=$1
++
++[ -z "${IMAGE}" ] && echo "$0 : No indicated file to be stripped" && usage && exit -1
++[ ! -f "${IMAGE}" ] && echo "$0 : ${IMAGE} : No such file" && exit -1
++FILE_TYPE=`dd if=${IMAGE} bs=1 skip=1 count=3 2>/dev/null`
++[ "xELF" != "x${FILE_TYPE}" ] && echo "$0: ${IMAGE} is not an ELF file" && exit -1
++
++[ "x${V}" == "x1" ] && orig_filesz=`wc -c ${IMAGE} | cut -d' ' -f1`
++
++# Get the offset of the section table, here get the end of the program section
++filesz=$((`${OBJDUMP} -p ${IMAGE} | grep -m1 filesz | tr -s ' ' | cut -d' ' -f3`))
++
++# Truncate it via the dd tool
++dd if=/dev/null bs=1 of=${IMAGE} seek=${filesz} 2>/dev/null
++
++# Clear the section table information in the ELF header
++# The last 6 bytes of the ELF header are the section table information
++echo -ne "\x00\x00\x00\x00\x00\x00" | dd of=${IMAGE} bs=1 seek=46 count=6 conv=notrunc 2>/dev/null
++
++# Debug
++if [ "x${V}" == "x1" ]; then
++	echo "----------------------------------------------------------------"
++	echo "Strip the section table at ${filesz} of ${IMAGE}"
++	echo "----------------------------------------------------------------"
++	echo "       sstrip: $0"
++	echo "      objdump: ${OBJDUMP}"
++	echo "original size: ${orig_filesz}"
++	echo "current  size: ${filesz}"
++	echo "reduced  size: $((${orig_filesz} - ${filesz}))"
++fi
diff --git a/kernels/linux-libre-lts-knock/PKGBUILD b/kernels/linux-libre-lts-knock/PKGBUILD
index 8860428b4..25b8e1689 100644
--- a/kernels/linux-libre-lts-knock/PKGBUILD
+++ b/kernels/linux-libre-lts-knock/PKGBUILD
@@ -42,7 +42,7 @@ source=("http://linux-libre.fsfla.org/pub/linux-libre/releases/${_pkgbasever}/li
         '0003-module-remove-MODULE_GENERIC_TABLE.patch'
         '0006-genksyms-fix-typeof-handling.patch'
         # loongson-community patch: http://linux-libre.fsfla.org/pub/linux-libre/lemote/gnewsense/pool/debuginfo/
-        '3.14.14-a410a5e2b7-loongson-community.patch')
+        '3.14.26-8475f027b4-loongson-community.patch')
 sha256sums=('477555c709b9407fe37dbd70d3331ff9dde1f9d874aba2741f138d07ae6f281b'
             'SKIP'
             'ac2ae79e3a50b2865d584f3f2cd6d5262f01255a8fdb804b4fb5ee52b5e78fae'
@@ -61,7 +61,7 @@ sha256sums=('477555c709b9407fe37dbd70d3331ff9dde1f9d874aba2741f138d07ae6f281b'
             '52dec83a8805a8642d74d764494acda863e0aa23e3d249e80d4b457e20a3fd29'
             '65d58f63215ee3c5f9c4fc6bce36fc5311a6c7dbdbe1ad29de40647b47ff9c0d'
             'cf2e7a2d00787f754028e7459688c2755a406e632ce48b60952fa4ff7ed6f4b7'
-            '3baf1279805edd561e80877a1baf32d98fe07220dc6e7cb4ced73ab531947bc4')
+            '7c2d1e257acce0ea6f260f3acf18e30e21c12a9a6b3d7d1d4097dafd287388e2')
 validpgpkeys=(
               '474402C8C582DAFBE389C427BCB7CF877E7D47A7' # Alexandre Oliva
               'F949CFBD140A6DD071E90B8CDC24396B6D451038' # Julian Kirsch
@@ -113,7 +113,7 @@ prepare() {
 
   # Adding loongson-community patch
   if [ "${CARCH}" == "mips64el" ]; then
-    patch -p1 -i ${srcdir}/3.14.14-a410a5e2b7-loongson-community.patch
+    patch -p1 -i ${srcdir}/3.14.26-8475f027b4-loongson-community.patch
   fi
 
   cat "${srcdir}/config.${CARCH}" > ./.config
-- 
cgit v1.2.3-2-g168b