summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kernels/linux-libre-lts-knock/3.10.50-33c9ab76eb-loongson-community.patch14572
-rw-r--r--kernels/linux-libre-lts-knock/gnewsense-binutils-flag.patch28
-rw-r--r--kernels/linux-libre-rt/3.14.14-a410a5e2b7-loongson-community.patch10869
-rw-r--r--kernels/linux-libre-rt/gnewsense-binutils-flag.patch28
-rw-r--r--libre/linux-libre-grsec/3.15.7-2704e67ebc-loongson-community.patch11744
-rw-r--r--libre/linux-libre-grsec/gnewsense-binutils-flag.patch28
-rw-r--r--libre/linux-libre-lts/3.14.14-a410a5e2b7-loongson-community.patch10869
-rw-r--r--libre/linux-libre-lts/gnewsense-binutils-flag.patch28
-rw-r--r--libre/linux-libre/3.16-7981337ad0-loongson-community.patch11731
-rw-r--r--libre/linux-libre/gnewsense-binutils-flag.patch28
10 files changed, 59925 insertions, 0 deletions
diff --git a/kernels/linux-libre-lts-knock/3.10.50-33c9ab76eb-loongson-community.patch b/kernels/linux-libre-lts-knock/3.10.50-33c9ab76eb-loongson-community.patch
new file mode 100644
index 000000000..5ef0ad621
--- /dev/null
+++ b/kernels/linux-libre-lts-knock/3.10.50-33c9ab76eb-loongson-community.patch
@@ -0,0 +1,14572 @@
+diff --git a/Makefile b/Makefile
+index 8d891c6..c5f4411a 100644
+--- a/Makefile
++++ b/Makefile
+@@ -241,8 +241,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.
+@@ -573,7 +573,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 e53e2b4..86c2834 100644
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -270,7 +270,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.
+
+@@ -932,6 +932,60 @@ config CSRC_POWERTV
+ 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
+
+@@ -1537,6 +1591,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
+
+@@ -2131,7 +2194,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 5a43aa0..96f8852 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/Makefile b/arch/mips/Makefile
+index dd58a04..a24c24b 100644
+--- a/arch/mips/Makefile
++++ b/arch/mips/Makefile
+@@ -260,18 +260,19 @@ endif
+ # Other need ECOFF, so we build a 32-bit ELF binary for them which we then
+ # convert to ECOFF using elf2ecoff.
+ #
++quiet_cmd_32 = OBJCOPY $@
++ cmd_32 = $(OBJCOPY) -O $(32bit-bfd) $(OBJCOPYFLAGS) $< $@
+ vmlinux.32: vmlinux
+- $(OBJCOPY) -O $(32bit-bfd) $(OBJCOPYFLAGS) $< $@
+-
+-
+-#obj-$(CONFIG_KPROBES) += kprobes.o
++ $(call cmd,32)
+
+ #
+ # The 64-bit ELF tools are pretty broken so at this time we generate 64-bit
+ # ELF files from 32-bit files by conversion.
+ #
++quiet_cmd_64 = OBJCOPY $@
++ cmd_64 = $(OBJCOPY) -O $(64bit-bfd) $(OBJCOPYFLAGS) $< $@
+ vmlinux.64: vmlinux
+- $(OBJCOPY) -O $(64bit-bfd) $(OBJCOPYFLAGS) $< $@
++ $(call cmd,64)
+
+ all: $(all-y)
+
+diff --git a/arch/mips/bcm63xx/cpu.c b/arch/mips/bcm63xx/cpu.c
+index 79fe32d..5eb36f3 100644
+--- a/arch/mips/bcm63xx/cpu.c
++++ b/arch/mips/bcm63xx/cpu.c
+@@ -293,7 +293,7 @@ void __init bcm63xx_cpu_init(void)
+ /* soc registers location depends on cpu type */
+ chipid_reg = 0;
+
+- switch (c->cputype) {
++ switch (current_cpu_type()) {
+ case CPU_BMIPS3300:
+ if ((read_c0_prid() & 0xff00) != PRID_IMP_BMIPS3300_ALT)
+ __cpu_name[cpu] = "Broadcom BCM6338";
+diff --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile
+index bbaa1d4b..87af880 100644
+--- a/arch/mips/boot/compressed/Makefile
++++ b/arch/mips/boot/compressed/Makefile
+@@ -28,9 +28,10 @@ KBUILD_AFLAGS := $(LINUXINCLUDE) $(KBUILD_AFLAGS) -D__ASSEMBLY__ \
+ targets := head.o decompress.o dbg.o uart-16550.o uart-alchemy.o
+
+ # decompressor objects (linked with vmlinuz)
+-vmlinuzobjs-y := $(obj)/head.o $(obj)/decompress.o $(obj)/dbg.o
++vmlinuzobjs-y := $(obj)/head.o $(obj)/decompress.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
+@@ -71,9 +72,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
+@@ -86,14 +96,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 $@
+@@ -102,11 +112,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 2c95730..3f65441 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 8e6b07c..99ca111 100644
+--- a/arch/mips/boot/compressed/ld.script
++++ b/arch/mips/boot/compressed/ld.script
+@@ -46,5 +46,6 @@ SECTIONS
+ *(.reginfo)
+ *(.comment)
+ *(.note)
++ *(.gnu.attributes)
+ }
+ }
+diff --git a/arch/mips/configs/fuloong2e_defconfig b/arch/mips/configs/fuloong2e_defconfig
+index e5b73de..1e02d0b 100644
+--- a/arch/mips/configs/fuloong2e_defconfig
++++ b/arch/mips/configs/fuloong2e_defconfig
+@@ -12,7 +12,6 @@ CONFIG_BSD_PROCESS_ACCT=y
+ CONFIG_IKCONFIG=y
+ CONFIG_IKCONFIG_PROC=y
+ CONFIG_LOG_BUF_SHIFT=14
+-CONFIG_SYSFS_DEPRECATED_V2=y
+ CONFIG_NAMESPACES=y
+ CONFIG_USER_NS=y
+ CONFIG_PID_NS=y
+@@ -222,7 +221,8 @@ CONFIG_EXT4_FS_SECURITY=y
+ CONFIG_REISERFS_FS=m
+ CONFIG_AUTOFS_FS=y
+ CONFIG_AUTOFS4_FS=y
+-CONFIG_FUSE_FS=y
++CONFIG_FUSE_FS=m
++CONFIG_CUSE=m
+ CONFIG_ISO9660_FS=m
+ CONFIG_JOLIET=y
+ CONFIG_ZISOFS=y
+@@ -260,9 +260,9 @@ CONFIG_NLS_CODEPAGE_936=y
+ CONFIG_NLS_ISO8859_1=y
+ CONFIG_NLS_UTF8=y
+ # CONFIG_ENABLE_MUST_CHECK is not set
+-CONFIG_DEBUG_FS=y
+ # CONFIG_RCU_CPU_STALL_DETECTOR is not set
+ CONFIG_SYSCTL_SYSCALL_CHECK=y
++# CONFIG_EARLY_PRINTK is not set
+ CONFIG_CRYPTO_FIPS=y
+ CONFIG_CRYPTO_AUTHENC=m
+ CONFIG_CRYPTO_CCM=m
+diff --git a/arch/mips/configs/gdium_minimal_defconfig b/arch/mips/configs/gdium_minimal_defconfig
+new file mode 100644
+index 0000000..595b414
+--- /dev/null
++++ b/arch/mips/configs/gdium_minimal_defconfig
+@@ -0,0 +1,125 @@
++CONFIG_MACH_LOONGSON=y
++CONFIG_DEXXON_GDIUM=y
++CONFIG_64BIT=y
++CONFIG_NO_HZ=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_PREEMPT=y
++CONFIG_EXPERIMENTAL=y
++# CONFIG_LOCALVERSION_AUTO is not set
++CONFIG_KERNEL_LZMA=y
++CONFIG_SYSVIPC=y
++CONFIG_POSIX_MQUEUE=y
++CONFIG_BSD_PROCESS_ACCT=y
++CONFIG_BSD_PROCESS_ACCT_V3=y
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_LOG_BUF_SHIFT=15
++CONFIG_BLK_DEV_INITRD=y
++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
++CONFIG_EMBEDDED=y
++# CONFIG_BLK_DEV_BSG is not set
++# CONFIG_IOSCHED_DEADLINE is not set
++# CONFIG_IOSCHED_CFQ is not set
++CONFIG_PCI=y
++CONFIG_MIPS32_COMPAT=y
++CONFIG_MIPS32_O32=y
++CONFIG_MIPS32_N32=y
++CONFIG_NET=y
++CONFIG_PACKET=y
++CONFIG_UNIX=y
++CONFIG_INET=y
++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
++# CONFIG_INET_XFRM_MODE_TUNNEL is not set
++# CONFIG_INET_XFRM_MODE_BEET is not set
++# CONFIG_INET_LRO is not set
++# CONFIG_INET_DIAG is not set
++# CONFIG_IPV6 is not set
++# CONFIG_WIRELESS is not set
++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
++CONFIG_DEVTMPFS=y
++CONFIG_DEVTMPFS_MOUNT=y
++CONFIG_SCSI=y
++CONFIG_BLK_DEV_SD=y
++CONFIG_NETDEVICES=y
++CONFIG_NET_ETHERNET=y
++CONFIG_NET_PCI=y
++CONFIG_8139TOO=y
++CONFIG_R8169=y
++# CONFIG_NETDEV_10000 is not set
++# CONFIG_WLAN is not set
++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
++CONFIG_INPUT_EVDEV=y
++# CONFIG_MOUSE_PS2_ALPS is not set
++# CONFIG_MOUSE_PS2_LOGIPS2PP is not set
++# CONFIG_MOUSE_PS2_TRACKPOINT is not set
++# CONFIG_SERIO_SERPORT is not set
++# CONFIG_DEVKMEM is not set
++CONFIG_SERIAL_8250=y
++CONFIG_SERIAL_8250_CONSOLE=y
++CONFIG_LEGACY_PTY_COUNT=16
++# CONFIG_HW_RANDOM is not set
++CONFIG_I2C=y
++CONFIG_I2C_CHARDEV=y
++CONFIG_I2C_GPIO=y
++CONFIG_SENSORS_LM75=y
++CONFIG_MFD_SM501=y
++CONFIG_MFD_SM501_GPIO=y
++CONFIG_FB=y
++CONFIG_FB_SIS=y
++CONFIG_FB_SIS_300=y
++CONFIG_FB_SIS_315=y
++CONFIG_FB_SM501=y
++# CONFIG_VGA_CONSOLE is not set
++CONFIG_FRAMEBUFFER_CONSOLE=y
++CONFIG_HIDRAW=y
++CONFIG_USB_HIDDEV=y
++CONFIG_HID_DRAGONRISE=y
++CONFIG_DRAGONRISE_FF=y
++CONFIG_HID_GYRATION=y
++CONFIG_HID_TWINHAN=y
++CONFIG_HID_NTRIG=y
++CONFIG_HID_PANTHERLORD=y
++CONFIG_PANTHERLORD_FF=y
++CONFIG_HID_PETALYNX=y
++CONFIG_HID_SAMSUNG=y
++CONFIG_HID_SONY=y
++CONFIG_HID_SUNPLUS=y
++CONFIG_HID_GREENASIA=y
++CONFIG_GREENASIA_FF=y
++CONFIG_HID_SMARTJOYPLUS=y
++CONFIG_SMARTJOYPLUS_FF=y
++CONFIG_HID_TOPSEED=y
++CONFIG_HID_THRUSTMASTER=y
++CONFIG_THRUSTMASTER_FF=y
++CONFIG_HID_ZEROPLUS=y
++CONFIG_ZEROPLUS_FF=y
++CONFIG_USB=y
++# CONFIG_USB_DEVICE_CLASS is not set
++CONFIG_USB_DYNAMIC_MINORS=y
++CONFIG_USB_MON=y
++CONFIG_USB_EHCI_HCD=y
++CONFIG_USB_EHCI_ROOT_HUB_TT=y
++# CONFIG_USB_EHCI_TT_NEWSCHED is not set
++CONFIG_USB_OHCI_HCD=y
++CONFIG_USB_STORAGE=y
++CONFIG_USB_LIBUSUAL=y
++CONFIG_RTC_CLASS=y
++CONFIG_RTC_DRV_M41T80=y
++CONFIG_STAGING=y
++# CONFIG_STAGING_EXCLUDE_BUILD is not set
++CONFIG_FB_SM7XX=y
++# CONFIG_MIPS_PLATFORM_DEVICES is not set
++CONFIG_EXT2_FS=y
++CONFIG_EXT2_FS_XATTR=y
++CONFIG_EXT3_FS=y
++# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
++CONFIG_EXT4_FS=y
++CONFIG_TMPFS=y
++# CONFIG_MISC_FILESYSTEMS is not set
++# CONFIG_NETWORK_FILESYSTEMS is not set
++CONFIG_NLS_DEFAULT="utf8"
++CONFIG_NLS_ASCII=y
++CONFIG_NLS_UTF8=y
++CONFIG_FRAME_WARN=1024
++CONFIG_STRIP_ASM_SYMS=y
++# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+diff --git a/arch/mips/configs/gdium_small_defconfig b/arch/mips/configs/gdium_small_defconfig
+new file mode 100644
+index 0000000..8027d5d
+--- /dev/null
++++ b/arch/mips/configs/gdium_small_defconfig
+@@ -0,0 +1,149 @@
++CONFIG_MACH_LOONGSON=y
++CONFIG_DEXXON_GDIUM=y
++CONFIG_64BIT=y
++CONFIG_NO_HZ=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_PREEMPT=y
++CONFIG_EXPERIMENTAL=y
++# CONFIG_LOCALVERSION_AUTO is not set
++CONFIG_KERNEL_LZMA=y
++CONFIG_SYSVIPC=y
++CONFIG_POSIX_MQUEUE=y
++CONFIG_BSD_PROCESS_ACCT=y
++CONFIG_BSD_PROCESS_ACCT_V3=y
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_LOG_BUF_SHIFT=15
++CONFIG_NAMESPACES=y
++# CONFIG_UTS_NS is not set
++# CONFIG_IPC_NS is not set
++# CONFIG_USER_NS is not set
++# CONFIG_PID_NS is not set
++# CONFIG_NET_NS is not set
++CONFIG_BLK_DEV_INITRD=y
++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
++CONFIG_EMBEDDED=y
++CONFIG_MODULES=y
++CONFIG_MODULE_UNLOAD=y
++# CONFIG_BLK_DEV_BSG is not set
++# CONFIG_IOSCHED_DEADLINE is not set
++# CONFIG_IOSCHED_CFQ is not set
++CONFIG_PCI=y
++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
++CONFIG_MIPS32_COMPAT=y
++CONFIG_MIPS32_O32=y
++CONFIG_MIPS32_N32=y
++CONFIG_PM=y
++CONFIG_HIBERNATION=y
++CONFIG_PM_STD_PARTITION="/dev/sda4"
++CONFIG_CPU_FREQ=y
++CONFIG_CPU_FREQ_STAT_DETAILS=y
++CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y
++CONFIG_CPU_FREQ_GOV_POWERSAVE=y
++CONFIG_CPU_FREQ_GOV_USERSPACE=y
++CONFIG_CPU_FREQ_GOV_ONDEMAND=y
++CONFIG_LOONGSON2_CPUFREQ=m
++CONFIG_NET=y
++CONFIG_PACKET=y
++CONFIG_UNIX=y
++CONFIG_INET=y
++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
++# CONFIG_INET_XFRM_MODE_TUNNEL is not set
++# CONFIG_INET_XFRM_MODE_BEET is not set
++# CONFIG_INET_LRO is not set
++# CONFIG_INET_DIAG is not set
++# CONFIG_IPV6 is not set
++CONFIG_CFG80211=y
++# CONFIG_CFG80211_DEFAULT_PS is not set
++# CONFIG_CFG80211_WEXT is not set
++CONFIG_LIB80211=y
++CONFIG_MAC80211=y
++CONFIG_RFKILL=y
++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
++CONFIG_DEVTMPFS=y
++CONFIG_DEVTMPFS_MOUNT=y
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_SIZE=8192
++CONFIG_MISC_DEVICES=y
++# CONFIG_SCSI_PROC_FS is not set
++CONFIG_BLK_DEV_SD=y
++# CONFIG_SCSI_LOWLEVEL is not set
++CONFIG_ATA=y
++# CONFIG_SATA_PMP is not set
++CONFIG_PATA_AMD=y
++CONFIG_NETDEVICES=y
++CONFIG_NET_ETHERNET=y
++CONFIG_NET_PCI=y
++CONFIG_8139TOO=y
++CONFIG_R8169=y
++# CONFIG_NETDEV_10000 is not set
++CONFIG_RT2X00=m
++CONFIG_RT61PCI=m
++CONFIG_INPUT_FF_MEMLESS=y
++CONFIG_INPUT_EVDEV=y
++CONFIG_MOUSE_PS2=m
++# CONFIG_MOUSE_PS2_ALPS is not set
++# CONFIG_MOUSE_PS2_LOGIPS2PP is not set
++# CONFIG_MOUSE_PS2_TRACKPOINT is not set
++# CONFIG_SERIO_SERPORT is not set
++# CONFIG_DEVKMEM is not set
++CONFIG_SERIAL_8250=y
++CONFIG_SERIAL_8250_CONSOLE=y
++CONFIG_LEGACY_PTY_COUNT=16
++# CONFIG_HW_RANDOM is not set
++CONFIG_I2C=y
++CONFIG_I2C_CHARDEV=y
++CONFIG_I2C_GPIO=y
++CONFIG_HWMON=m
++CONFIG_SENSORS_LM75=m
++CONFIG_MFD_SM501=y
++CONFIG_MFD_SM501_GPIO=y
++CONFIG_FB=y
++CONFIG_FB_SIS=y
++CONFIG_FB_SIS_300=y
++CONFIG_FB_SIS_315=y
++CONFIG_FB_SM501=y
++CONFIG_BACKLIGHT_LCD_SUPPORT=y
++# CONFIG_BACKLIGHT_GENERIC is not set
++CONFIG_BACKLIGHT_PWM=m
++# CONFIG_VGA_CONSOLE is not set
++CONFIG_FRAMEBUFFER_CONSOLE=y
++CONFIG_SOUND=y
++CONFIG_SND=y
++CONFIG_SND_CS5535AUDIO=m
++CONFIG_HIDRAW=y
++CONFIG_USB_HIDDEV=y
++CONFIG_HID_GDIUM=y
++CONFIG_USB=y
++# CONFIG_USB_DEVICE_CLASS is not set
++CONFIG_USB_DYNAMIC_MINORS=y
++CONFIG_USB_MON=y
++CONFIG_USB_EHCI_HCD=y
++CONFIG_USB_EHCI_ROOT_HUB_TT=y
++# CONFIG_USB_EHCI_TT_NEWSCHED is not set
++CONFIG_USB_OHCI_HCD=y
++CONFIG_USB_STORAGE=y
++CONFIG_USB_LIBUSUAL=y
++CONFIG_RTC_CLASS=y
++CONFIG_RTC_DRV_M41T80=y
++CONFIG_RTC_DRV_M41T80_WDT=y
++CONFIG_STAGING=y
++# CONFIG_STAGING_EXCLUDE_BUILD is not set
++CONFIG_FB_SM7XX=y
++CONFIG_EXT2_FS=y
++CONFIG_EXT2_FS_XATTR=y
++CONFIG_EXT3_FS=y
++# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
++CONFIG_EXT4_FS=y
++CONFIG_FANOTIFY=y
++CONFIG_TMPFS=y
++# CONFIG_MISC_FILESYSTEMS is not set
++# CONFIG_NETWORK_FILESYSTEMS is not set
++CONFIG_NLS_DEFAULT="utf8"
++CONFIG_NLS_ASCII=y
++CONFIG_NLS_UTF8=y
++CONFIG_FRAME_WARN=1024
++CONFIG_STRIP_ASM_SYMS=y
++# CONFIG_RCU_CPU_STALL_DETECTOR is not set
++# CONFIG_EARLY_PRINTK is not set
+diff --git a/arch/mips/configs/lemote2f_defconfig b/arch/mips/configs/lemote2f_defconfig
+index 343bebc..ef8b4d13 100644
+--- a/arch/mips/configs/lemote2f_defconfig
++++ b/arch/mips/configs/lemote2f_defconfig
+@@ -1,27 +1,27 @@
+ CONFIG_MACH_LOONGSON=y
+ CONFIG_LEMOTE_MACH2F=y
+-CONFIG_CS5536_MFGPT=y
+ CONFIG_64BIT=y
++CONFIG_KSM=y
+ CONFIG_NO_HZ=y
+ CONFIG_HIGH_RES_TIMERS=y
+ CONFIG_PREEMPT=y
+-CONFIG_KEXEC=y
+-# CONFIG_SECCOMP is not set
+ CONFIG_EXPERIMENTAL=y
+ # CONFIG_LOCALVERSION_AUTO is not set
++CONFIG_KERNEL_LZMA=y
+ CONFIG_SYSVIPC=y
+ CONFIG_BSD_PROCESS_ACCT=y
+ CONFIG_BSD_PROCESS_ACCT_V3=y
++CONFIG_TASKSTATS=y
++CONFIG_TASK_DELAY_ACCT=y
++CONFIG_TASK_XACCT=y
++CONFIG_TASK_IO_ACCOUNTING=y
+ CONFIG_AUDIT=y
+ CONFIG_IKCONFIG=y
+ CONFIG_IKCONFIG_PROC=y
+ CONFIG_LOG_BUF_SHIFT=15
+-CONFIG_SYSFS_DEPRECATED_V2=y
++CONFIG_SCHED_AUTOGROUP=y
+ CONFIG_BLK_DEV_INITRD=y
+-CONFIG_RD_BZIP2=y
+-CONFIG_RD_LZMA=y
+ # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+-CONFIG_EXPERT=y
+ CONFIG_PROFILING=y
+ CONFIG_OPROFILE=m
+ CONFIG_MODULES=y
+@@ -34,20 +34,17 @@ CONFIG_BINFMT_MISC=m
+ CONFIG_MIPS32_COMPAT=y
+ CONFIG_MIPS32_O32=y
+ CONFIG_MIPS32_N32=y
+-CONFIG_PM=y
+ CONFIG_HIBERNATION=y
+-CONFIG_PM_STD_PARTITION="/dev/hda3"
++CONFIG_PM_STD_PARTITION="/dev/sda3"
+ CONFIG_PM_RUNTIME=y
+ CONFIG_CPU_FREQ=y
+ CONFIG_CPU_FREQ_DEBUG=y
+-CONFIG_CPU_FREQ_STAT=m
+ CONFIG_CPU_FREQ_STAT_DETAILS=y
+-CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+-CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+-CONFIG_CPU_FREQ_GOV_USERSPACE=m
+-CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
++CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y
++CONFIG_CPU_FREQ_GOV_POWERSAVE=y
++CONFIG_CPU_FREQ_GOV_USERSPACE=y
++CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+ CONFIG_LOONGSON2_CPUFREQ=m
+-CONFIG_NET=y
+ CONFIG_PACKET=y
+ CONFIG_UNIX=y
+ CONFIG_XFRM_USER=m
+@@ -59,7 +56,6 @@ CONFIG_IP_MULTIPLE_TABLES=y
+ CONFIG_IP_ROUTE_MULTIPATH=y
+ CONFIG_IP_ROUTE_VERBOSE=y
+ CONFIG_NET_IPIP=m
+-CONFIG_NET_IPGRE=m
+ CONFIG_IP_MROUTE=y
+ CONFIG_IP_PIMSM_V1=y
+ CONFIG_IP_PIMSM_V2=y
+@@ -79,12 +75,249 @@ CONFIG_IPV6_MULTIPLE_TABLES=y
+ CONFIG_IPV6_SUBTREES=y
+ CONFIG_NETWORK_SECMARK=y
+ CONFIG_NETFILTER=y
++CONFIG_NF_CONNTRACK=m
++CONFIG_NF_CT_PROTO_UDPLITE=m
++CONFIG_NF_CONNTRACK_AMANDA=m
++CONFIG_NF_CONNTRACK_FTP=m
++CONFIG_NF_CONNTRACK_H323=m
++CONFIG_NF_CONNTRACK_IRC=m
++CONFIG_NF_CONNTRACK_NETBIOS_NS=m
++CONFIG_NF_CONNTRACK_PPTP=m
++CONFIG_NF_CONNTRACK_SANE=m
++CONFIG_NF_CONNTRACK_SIP=m
++CONFIG_NF_CONNTRACK_TFTP=m
++CONFIG_NF_CT_NETLINK=m
++CONFIG_NETFILTER_TPROXY=m
++CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
++CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
++CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
++CONFIG_NETFILTER_XT_TARGET_CT=m
++CONFIG_NETFILTER_XT_TARGET_DSCP=m
++CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m
++CONFIG_NETFILTER_XT_TARGET_MARK=m
++CONFIG_NETFILTER_XT_TARGET_NFLOG=m
++CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
++CONFIG_NETFILTER_XT_TARGET_NOTRACK=m
++CONFIG_NETFILTER_XT_TARGET_TEE=m
++CONFIG_NETFILTER_XT_TARGET_TPROXY=m
++CONFIG_NETFILTER_XT_TARGET_TRACE=m
++CONFIG_NETFILTER_XT_TARGET_SECMARK=m
++CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
++CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m
++CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
++CONFIG_NETFILTER_XT_MATCH_COMMENT=m
++CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
++CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
++CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
++CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
++CONFIG_NETFILTER_XT_MATCH_CPU=m
++CONFIG_NETFILTER_XT_MATCH_DSCP=m
++CONFIG_NETFILTER_XT_MATCH_ESP=m
++CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
++CONFIG_NETFILTER_XT_MATCH_HELPER=m
++CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
++CONFIG_NETFILTER_XT_MATCH_IPVS=m
++CONFIG_NETFILTER_XT_MATCH_LENGTH=m
++CONFIG_NETFILTER_XT_MATCH_LIMIT=m
++CONFIG_NETFILTER_XT_MATCH_MAC=m
++CONFIG_NETFILTER_XT_MATCH_MARK=m
++CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
++CONFIG_NETFILTER_XT_MATCH_OSF=m
++CONFIG_NETFILTER_XT_MATCH_OWNER=m
++CONFIG_NETFILTER_XT_MATCH_POLICY=m
++CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
++CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
++CONFIG_NETFILTER_XT_MATCH_QUOTA=m
++CONFIG_NETFILTER_XT_MATCH_RATEEST=m
++CONFIG_NETFILTER_XT_MATCH_REALM=m
++CONFIG_NETFILTER_XT_MATCH_RECENT=m
++CONFIG_NETFILTER_XT_MATCH_SOCKET=m
++CONFIG_NETFILTER_XT_MATCH_STATE=m
++CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
++CONFIG_NETFILTER_XT_MATCH_STRING=m
++CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
++CONFIG_NETFILTER_XT_MATCH_TIME=m
++CONFIG_NETFILTER_XT_MATCH_U32=m
++CONFIG_IP_VS=m
++CONFIG_IP_VS_IPV6=y
++CONFIG_IP_VS_PROTO_TCP=y
++CONFIG_IP_VS_PROTO_UDP=y
++CONFIG_IP_VS_PROTO_ESP=y
++CONFIG_IP_VS_PROTO_AH=y
++CONFIG_IP_VS_PROTO_SCTP=y
++CONFIG_IP_VS_RR=m
++CONFIG_IP_VS_WRR=m
++CONFIG_IP_VS_LC=m
++CONFIG_IP_VS_WLC=m
++CONFIG_IP_VS_LBLC=m
++CONFIG_IP_VS_LBLCR=m
++CONFIG_IP_VS_DH=m
++CONFIG_IP_VS_SH=m
++CONFIG_IP_VS_SED=m
++CONFIG_IP_VS_NQ=m
++CONFIG_IP_VS_FTP=m
++CONFIG_NF_CONNTRACK_IPV4=m
++CONFIG_IP_NF_QUEUE=m
++CONFIG_IP_NF_IPTABLES=m
++CONFIG_IP_NF_MATCH_AH=m
++CONFIG_IP_NF_MATCH_ECN=m
++CONFIG_IP_NF_MATCH_TTL=m
++CONFIG_IP_NF_FILTER=m
++CONFIG_IP_NF_TARGET_REJECT=m
++CONFIG_IP_NF_TARGET_LOG=m
++CONFIG_IP_NF_TARGET_ULOG=m
++CONFIG_NF_NAT=m
++CONFIG_IP_NF_TARGET_MASQUERADE=m
++CONFIG_IP_NF_TARGET_NETMAP=m
++CONFIG_IP_NF_TARGET_REDIRECT=m
++CONFIG_IP_NF_MANGLE=m
++CONFIG_IP_NF_TARGET_CLUSTERIP=m
++CONFIG_IP_NF_TARGET_ECN=m
++CONFIG_IP_NF_TARGET_TTL=m
++CONFIG_IP_NF_RAW=m
++CONFIG_IP_NF_ARPTABLES=m
++CONFIG_IP_NF_ARPFILTER=m
++CONFIG_IP_NF_ARP_MANGLE=m
++CONFIG_NF_CONNTRACK_IPV6=m
++CONFIG_IP6_NF_QUEUE=m
++CONFIG_IP6_NF_IPTABLES=m
++CONFIG_IP6_NF_MATCH_AH=m
++CONFIG_IP6_NF_MATCH_EUI64=m
++CONFIG_IP6_NF_MATCH_FRAG=m
++CONFIG_IP6_NF_MATCH_OPTS=m
++CONFIG_IP6_NF_MATCH_HL=m
++CONFIG_IP6_NF_MATCH_IPV6HEADER=m
++CONFIG_IP6_NF_MATCH_MH=m
++CONFIG_IP6_NF_MATCH_RT=m
++CONFIG_IP6_NF_TARGET_HL=m
++CONFIG_IP6_NF_TARGET_LOG=m
++CONFIG_IP6_NF_FILTER=m
++CONFIG_IP6_NF_TARGET_REJECT=m
++CONFIG_IP6_NF_MANGLE=m
++CONFIG_IP6_NF_RAW=m
++CONFIG_BRIDGE_NF_EBTABLES=m
++CONFIG_BRIDGE_EBT_BROUTE=m
++CONFIG_BRIDGE_EBT_T_FILTER=m
++CONFIG_BRIDGE_EBT_T_NAT=m
++CONFIG_BRIDGE_EBT_802_3=m
++CONFIG_BRIDGE_EBT_AMONG=m
++CONFIG_BRIDGE_EBT_ARP=m
++CONFIG_BRIDGE_EBT_IP=m
++CONFIG_BRIDGE_EBT_IP6=m
++CONFIG_BRIDGE_EBT_LIMIT=m
++CONFIG_BRIDGE_EBT_MARK=m
++CONFIG_BRIDGE_EBT_PKTTYPE=m
++CONFIG_BRIDGE_EBT_STP=m
++CONFIG_BRIDGE_EBT_VLAN=m
++CONFIG_BRIDGE_EBT_ARPREPLY=m
++CONFIG_BRIDGE_EBT_DNAT=m
++CONFIG_BRIDGE_EBT_MARK_T=m
++CONFIG_BRIDGE_EBT_REDIRECT=m
++CONFIG_BRIDGE_EBT_SNAT=m
++CONFIG_BRIDGE_EBT_LOG=m
++CONFIG_BRIDGE_EBT_ULOG=m
++CONFIG_BRIDGE_EBT_NFLOG=m
++CONFIG_IP_DCCP=m
++CONFIG_IP_SCTP=m
++CONFIG_RDS=m
++CONFIG_RDS_TCP=m
++CONFIG_TIPC=m
++CONFIG_TIPC_ADVANCED=y
++CONFIG_ATM=m
++CONFIG_ATM_CLIP=m
++CONFIG_ATM_CLIP_NO_ICMP=y
++CONFIG_ATM_LANE=m
++CONFIG_ATM_MPOA=m
++CONFIG_ATM_BR2684=m
++CONFIG_ATM_BR2684_IPFILTER=y
++CONFIG_L2TP=m
++CONFIG_L2TP_DEBUGFS=m
++CONFIG_L2TP_V3=y
++CONFIG_L2TP_IP=m
++CONFIG_L2TP_ETH=m
+ CONFIG_BRIDGE=m
+ CONFIG_VLAN_8021Q=m
+-CONFIG_IPX=m
++CONFIG_ATALK=m
+ CONFIG_NET_SCHED=y
++CONFIG_NET_SCH_CBQ=m
++CONFIG_NET_SCH_HTB=m
++CONFIG_NET_SCH_HFSC=m
++CONFIG_NET_SCH_ATM=m
++CONFIG_NET_SCH_PRIO=m
++CONFIG_NET_SCH_MULTIQ=m
++CONFIG_NET_SCH_RED=m
++CONFIG_NET_SCH_SFQ=m
++CONFIG_NET_SCH_TEQL=m
++CONFIG_NET_SCH_TBF=m
++CONFIG_NET_SCH_GRED=m
++CONFIG_NET_SCH_DSMARK=m
++CONFIG_NET_SCH_NETEM=m
++CONFIG_NET_SCH_DRR=m
++CONFIG_NET_SCH_INGRESS=m
++CONFIG_NET_CLS_BASIC=m
++CONFIG_NET_CLS_TCINDEX=m
++CONFIG_NET_CLS_ROUTE4=m
++CONFIG_NET_CLS_FW=m
++CONFIG_NET_CLS_U32=m
++CONFIG_CLS_U32_PERF=y
++CONFIG_CLS_U32_MARK=y
++CONFIG_NET_CLS_RSVP=m
++CONFIG_NET_CLS_RSVP6=m
++CONFIG_NET_CLS_FLOW=m
+ CONFIG_NET_EMATCH=y
++CONFIG_NET_EMATCH_CMP=m
++CONFIG_NET_EMATCH_NBYTE=m
++CONFIG_NET_EMATCH_U32=m
++CONFIG_NET_EMATCH_META=m
++CONFIG_NET_EMATCH_TEXT=m
+ CONFIG_NET_CLS_ACT=y
++CONFIG_NET_ACT_POLICE=m
++CONFIG_NET_ACT_GACT=m
++CONFIG_GACT_PROB=y
++CONFIG_NET_ACT_MIRRED=m
++CONFIG_NET_ACT_IPT=m
++CONFIG_NET_ACT_NAT=m
++CONFIG_NET_ACT_PEDIT=m
++CONFIG_NET_ACT_SIMP=m
++CONFIG_NET_ACT_SKBEDIT=m
++CONFIG_NET_CLS_IND=y
++CONFIG_NET_PKTGEN=m
++CONFIG_CAN=m
++CONFIG_CAN_RAW=m
++CONFIG_CAN_BCM=m
++CONFIG_CAN_VCAN=m
++CONFIG_CAN_DEV=m
++CONFIG_CAN_CALC_BITTIMING=y
++CONFIG_CAN_SJA1000=m
++CONFIG_CAN_SJA1000_ISA=m
++CONFIG_CAN_SJA1000_PLATFORM=m
++CONFIG_CAN_EMS_PCI=m
++CONFIG_CAN_KVASER_PCI=m
++CONFIG_CAN_PLX_PCI=m
++CONFIG_CAN_EMS_USB=m
++CONFIG_CAN_ESD_USB2=m
++CONFIG_IRDA=m
++CONFIG_IRLAN=m
++CONFIG_IRCOMM=m
++CONFIG_IRTTY_SIR=m
++CONFIG_DONGLE=y
++CONFIG_ESI_DONGLE=m
++CONFIG_ACTISYS_DONGLE=m
++CONFIG_TEKRAM_DONGLE=m
++CONFIG_TOIM3232_DONGLE=m
++CONFIG_LITELINK_DONGLE=m
++CONFIG_MA600_DONGLE=m
++CONFIG_GIRBIL_DONGLE=m
++CONFIG_MCP2120_DONGLE=m
++CONFIG_OLD_BELKIN_DONGLE=m
++CONFIG_ACT200L_DONGLE=m
++CONFIG_KINGSUN_DONGLE=m
++CONFIG_KSDAZZLE_DONGLE=m
++CONFIG_KS959_DONGLE=m
++CONFIG_USB_IRDA=m
++CONFIG_SIGMATEL_FIR=m
++CONFIG_VLSI_FIR=m
++CONFIG_MCS_FIR=m
+ CONFIG_BT=m
+ CONFIG_BT_L2CAP=y
+ CONFIG_BT_SCO=y
+@@ -95,30 +328,114 @@ CONFIG_BT_BNEP_MC_FILTER=y
+ CONFIG_BT_BNEP_PROTO_FILTER=y
+ CONFIG_BT_HIDP=m
+ CONFIG_BT_HCIBTUSB=m
++CONFIG_BT_HCIBTSDIO=m
++CONFIG_BT_HCIUART=m
++CONFIG_BT_HCIUART_ATH3K=y
++CONFIG_BT_HCIBCM203X=m
++CONFIG_BT_HCIBPA10X=m
+ CONFIG_BT_HCIBFUSB=m
+ CONFIG_BT_HCIVHCI=m
++CONFIG_BT_MRVL=m
++CONFIG_BT_MRVL_SDIO=m
++CONFIG_BT_ATH3K=m
+ CONFIG_CFG80211=m
+ CONFIG_LIB80211=m
+ CONFIG_LIB80211_DEBUG=y
+ CONFIG_MAC80211=m
+-CONFIG_MAC80211_LEDS=y
++CONFIG_WIMAX=m
+ CONFIG_RFKILL=m
+-CONFIG_RFKILL_INPUT=y
++CONFIG_CAIF=m
+ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
++CONFIG_DEVTMPFS=y
++CONFIG_DEVTMPFS_MOUNT=y
+ CONFIG_BLK_DEV_LOOP=y
+ CONFIG_BLK_DEV_CRYPTOLOOP=m
+ CONFIG_BLK_DEV_RAM=y
+ CONFIG_BLK_DEV_RAM_SIZE=8192
+-# CONFIG_MISC_DEVICES is not set
+-CONFIG_IDE=y
+-CONFIG_IDE_TASK_IOCTL=y
+-# CONFIG_IDEPCI_PCIBUS_ORDER is not set
+-CONFIG_BLK_DEV_AMD74XX=y
+-CONFIG_SCSI=m
+-CONFIG_BLK_DEV_SD=m
++CONFIG_MISC_DEVICES=y
++CONFIG_EEPROM_AT24=m
++CONFIG_EEPROM_LEGACY=m
++CONFIG_BLK_DEV_SD=y
++CONFIG_CHR_DEV_ST=m
++CONFIG_CHR_DEV_OSST=m
++CONFIG_BLK_DEV_SR=m
++CONFIG_BLK_DEV_SR_VENDOR=y
+ CONFIG_CHR_DEV_SG=m
++CONFIG_CHR_DEV_SCH=m
+ CONFIG_SCSI_MULTI_LUN=y
+-# CONFIG_SCSI_LOWLEVEL is not set
++CONFIG_SCSI_CONSTANTS=y
++CONFIG_SCSI_LOGGING=y
++CONFIG_SCSI_SCAN_ASYNC=y
++CONFIG_SCSI_FC_TGT_ATTRS=y
++CONFIG_SCSI_SAS_ATA=y
++CONFIG_SCSI_SRP_ATTRS=m
++CONFIG_SCSI_SRP_TGT_ATTRS=y
++CONFIG_ISCSI_TCP=m
++CONFIG_SCSI_CXGB3_ISCSI=m
++CONFIG_SCSI_BNX2_ISCSI=m
++CONFIG_BE2ISCSI=m
++CONFIG_BLK_DEV_3W_XXXX_RAID=m
++CONFIG_SCSI_HPSA=m
++CONFIG_SCSI_3W_9XXX=m
++CONFIG_SCSI_3W_SAS=m
++CONFIG_SCSI_ACARD=m
++CONFIG_SCSI_AACRAID=m
++CONFIG_SCSI_AIC7XXX=m
++CONFIG_AIC7XXX_RESET_DELAY_MS=15000
++CONFIG_SCSI_AIC7XXX_OLD=m
++CONFIG_SCSI_AIC79XX=m
++CONFIG_AIC79XX_RESET_DELAY_MS=15000
++CONFIG_SCSI_AIC94XX=m
++# CONFIG_AIC94XX_DEBUG is not set
++CONFIG_SCSI_MVSAS=m
++# CONFIG_SCSI_MVSAS_DEBUG is not set
++CONFIG_MEGARAID_SAS=m
++CONFIG_SCSI_MPT2SAS=m
++CONFIG_SCSI_HPTIOP=m
++CONFIG_FCOE=m
++CONFIG_SCSI_STEX=m
++CONFIG_SCSI_QLA_FC=m
++CONFIG_SCSI_QLA_ISCSI=m
++CONFIG_SCSI_LPFC=m
++CONFIG_SCSI_PMCRAID=m
++CONFIG_SCSI_PM8001=m
++CONFIG_SCSI_SRP=m
++CONFIG_SCSI_BFA_FC=m
++CONFIG_SCSI_DH=m
++CONFIG_SCSI_DH_RDAC=m
++CONFIG_SCSI_DH_HP_SW=m
++CONFIG_SCSI_DH_EMC=m
++CONFIG_SCSI_DH_ALUA=m
++CONFIG_SCSI_OSD_INITIATOR=m
++CONFIG_SCSI_OSD_ULD=m
++CONFIG_ATA=y
++CONFIG_SATA_AHCI=m
++CONFIG_SATA_INIC162X=m
++CONFIG_SATA_SIL24=m
++CONFIG_PDC_ADMA=m
++CONFIG_SATA_QSTOR=m
++CONFIG_SATA_SX4=m
++CONFIG_ATA_PIIX=m
++CONFIG_SATA_MV=m
++CONFIG_SATA_NV=m
++CONFIG_SATA_PROMISE=m
++CONFIG_SATA_SIL=y
++CONFIG_SATA_SIS=m
++CONFIG_SATA_SVW=m
++CONFIG_SATA_ULI=m
++CONFIG_SATA_VIA=m
++CONFIG_SATA_VITESSE=m
++CONFIG_PATA_ARTOP=m
++CONFIG_PATA_ATP867X=m
++CONFIG_PATA_CMD64X=m
++CONFIG_PATA_CS5536=y
++CONFIG_PATA_IT821X=m
++CONFIG_PATA_JMICRON=m
++CONFIG_PATA_MARVELL=m
++CONFIG_PATA_RDC=m
++CONFIG_PATA_SCH=m
++CONFIG_PATA_TOSHIBA=m
++CONFIG_ATA_GENERIC=m
+ CONFIG_MD=y
+ CONFIG_BLK_DEV_MD=m
+ CONFIG_MD_LINEAR=m
+@@ -129,7 +446,6 @@ CONFIG_MD_RAID456=m
+ CONFIG_MD_MULTIPATH=m
+ CONFIG_MD_FAULTY=m
+ CONFIG_BLK_DEV_DM=m
+-CONFIG_DM_DEBUG=y
+ CONFIG_DM_CRYPT=m
+ CONFIG_DM_SNAPSHOT=m
+ CONFIG_DM_MIRROR=m
+@@ -140,55 +456,192 @@ CONFIG_DM_MULTIPATH_QL=m
+ CONFIG_DM_MULTIPATH_ST=m
+ CONFIG_DM_DELAY=m
+ CONFIG_DM_UEVENT=y
+-CONFIG_NETDEVICES=y
++CONFIG_FUSION=y
++CONFIG_FUSION_SPI=m
++CONFIG_FUSION_FC=m
++CONFIG_FUSION_SAS=m
++CONFIG_FUSION_MAX_SGE=40
++CONFIG_FUSION_CTL=m
+ CONFIG_DUMMY=m
++CONFIG_BONDING=m
++CONFIG_MACVLAN=m
+ CONFIG_TUN=m
+ CONFIG_VETH=m
++CONFIG_MII=y
+ CONFIG_NET_ETHERNET=y
+ CONFIG_NET_PCI=y
+-CONFIG_8139TOO=y
+-# CONFIG_8139TOO_PIO is not set
+-CONFIG_R8169=y
+-CONFIG_R8169_VLAN=y
+-# CONFIG_NETDEV_10000 is not set
+-CONFIG_USB_USBNET=m
++CONFIG_8139TOO=m
++CONFIG_R8169=m
++CONFIG_AT76C50X_USB=m
++CONFIG_USB_ZD1201=m
++CONFIG_USB_NET_RNDIS_WLAN=m
++CONFIG_RTL8187B=m
++CONFIG_ATH_COMMON=m
++CONFIG_AR9170_USB=m
++CONFIG_RT2X00=m
++CONFIG_RT2500USB=m
++CONFIG_RT73USB=m
++CONFIG_RT2800USB=m
++CONFIG_RT2800USB_RT35XX=y
++CONFIG_RT2800USB_UNKNOWN=y
++CONFIG_ZD1211RW=m
++CONFIG_USB_CATC=m
++CONFIG_USB_KAWETH=m
++CONFIG_USB_PEGASUS=m
++CONFIG_USB_RTL8150=m
+ CONFIG_USB_NET_CDC_EEM=m
++CONFIG_USB_NET_DM9601=m
++CONFIG_USB_NET_SMSC75XX=m
++CONFIG_USB_NET_SMSC95XX=m
++CONFIG_USB_NET_GL620A=m
++CONFIG_USB_NET_PLUSB=m
++CONFIG_USB_NET_MCS7830=m
++CONFIG_USB_ALI_M5632=y
++CONFIG_USB_AN2720=y
++CONFIG_USB_EPSON2888=y
++CONFIG_USB_KC2190=y
++CONFIG_USB_HSO=m
++CONFIG_USB_NET_INT51X1=m
++CONFIG_USB_IPHETH=m
++CONFIG_USB_SIERRA_NET=m
++CONFIG_WAN=y
++CONFIG_LANMEDIA=m
++CONFIG_HDLC=m
++CONFIG_HDLC_RAW=m
++CONFIG_HDLC_RAW_ETH=m
++CONFIG_HDLC_CISCO=m
++CONFIG_HDLC_FR=m
++CONFIG_HDLC_PPP=m
++CONFIG_PCI200SYN=m
++CONFIG_WANXL=m
++CONFIG_PC300TOO=m
++CONFIG_N2=m
++CONFIG_C101=m
++CONFIG_FARSYNC=m
++CONFIG_DSCC4=m
++CONFIG_DLCI=m
++CONFIG_SDLA=m
++CONFIG_ATM_DUMMY=m
++CONFIG_ATM_TCP=m
++CONFIG_ATM_LANAI=m
++CONFIG_ATM_ENI=m
++CONFIG_ATM_ENI_DEBUG=y
++CONFIG_CAIF_TTY=m
++CONFIG_CAIF_SPI_SLAVE=m
++CONFIG_PPP=m
++CONFIG_PPP_ASYNC=m
++CONFIG_PPP_SYNC_TTY=m
++CONFIG_PPP_DEFLATE=m
++CONFIG_PPP_BSDCOMP=m
++CONFIG_PPP_MPPE=m
++CONFIG_PPPOE=m
++CONFIG_PPPOATM=m
++CONFIG_PPPOL2TP=m
++CONFIG_SLIP=m
++CONFIG_SLIP_SMART=y
++CONFIG_SLIP_MODE_SLIP6=y
++CONFIG_NET_FC=y
+ CONFIG_NETCONSOLE=m
+-CONFIG_NETCONSOLE_DYNAMIC=y
+-CONFIG_INPUT_POLLDEV=m
++CONFIG_NETPOLL_TRAP=y
++CONFIG_VMXNET3=m
++CONFIG_PHONE=m
++CONFIG_PHONE_IXJ=m
+ CONFIG_INPUT_EVDEV=y
+-# CONFIG_MOUSE_PS2_ALPS is not set
+-# CONFIG_MOUSE_PS2_LOGIPS2PP is not set
+-# CONFIG_MOUSE_PS2_TRACKPOINT is not set
++CONFIG_KEYBOARD_LKKBD=m
++CONFIG_KEYBOARD_NEWTON=m
++CONFIG_KEYBOARD_OPENCORES=m
++CONFIG_KEYBOARD_STOWAWAY=m
++CONFIG_KEYBOARD_SUNKBD=m
++CONFIG_KEYBOARD_XTKBD=m
+ CONFIG_MOUSE_APPLETOUCH=m
+-# CONFIG_SERIO_SERPORT is not set
+-CONFIG_SERIAL_NONSTANDARD=y
++CONFIG_MOUSE_BCM5974=m
++CONFIG_INPUT_TABLET=y
++CONFIG_TABLET_USB_ACECAD=m
++CONFIG_TABLET_USB_AIPTEK=m
++CONFIG_TABLET_USB_GTCO=m
++CONFIG_TABLET_USB_KBTAB=m
++CONFIG_TABLET_USB_WACOM=m
++CONFIG_INPUT_TOUCHSCREEN=y
++CONFIG_TOUCHSCREEN_AD7879=m
++CONFIG_TOUCHSCREEN_DYNAPRO=m
++CONFIG_TOUCHSCREEN_HAMPSHIRE=m
++CONFIG_TOUCHSCREEN_FUJITSU=m
++CONFIG_TOUCHSCREEN_GUNZE=m
++CONFIG_TOUCHSCREEN_ELO=m
++CONFIG_TOUCHSCREEN_WACOM_W8001=m
++CONFIG_TOUCHSCREEN_MTOUCH=m
++CONFIG_TOUCHSCREEN_INEXIO=m
++CONFIG_TOUCHSCREEN_MK712=m
++CONFIG_TOUCHSCREEN_HTCPEN=m
++CONFIG_TOUCHSCREEN_PENMOUNT=m
++CONFIG_TOUCHSCREEN_TOUCHRIGHT=m
++CONFIG_TOUCHSCREEN_TOUCHWIN=m
++CONFIG_TOUCHSCREEN_WM97XX=m
++CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
++CONFIG_TOUCHSCREEN_TOUCHIT213=m
++CONFIG_INPUT_MISC=y
++CONFIG_INPUT_AD714X=m
++CONFIG_INPUT_ATI_REMOTE=m
++CONFIG_INPUT_ATI_REMOTE2=m
++CONFIG_INPUT_KEYSPAN_REMOTE=m
++CONFIG_INPUT_YEALINK=m
++CONFIG_INPUT_CM109=m
++CONFIG_INPUT_ADXL34X=m
++CONFIG_LEGACY_PTY_COUNT=16
++CONFIG_NOZOMI=m
++CONFIG_N_GSM=m
+ CONFIG_SERIAL_8250=m
+-# CONFIG_SERIAL_8250_PCI is not set
+ CONFIG_SERIAL_8250_NR_UARTS=16
+ CONFIG_SERIAL_8250_EXTENDED=y
+ CONFIG_SERIAL_8250_MANY_PORTS=y
+ CONFIG_SERIAL_8250_FOURPORT=y
+-CONFIG_LEGACY_PTY_COUNT=16
+ CONFIG_HW_RANDOM=y
+ CONFIG_RTC=y
++CONFIG_I2C=m
++CONFIG_I2C_CHARDEV=m
++CONFIG_I2C_MUX=m
++CONFIG_I2C_MUX_PCA954x=m
++CONFIG_I2C_OCORES=m
++CONFIG_I2C_PARPORT_LIGHT=m
++CONFIG_I2C_TINY_USB=m
++CONFIG_SCx200_ACB=m
++CONFIG_SPI=y
++CONFIG_W1=m
++CONFIG_W1_MASTER_DS2490=m
++CONFIG_W1_SLAVE_THERM=m
++CONFIG_W1_SLAVE_SMEM=m
++CONFIG_W1_SLAVE_DS2431=m
++CONFIG_W1_SLAVE_DS2433=m
++CONFIG_W1_SLAVE_DS2433_CRC=y
++CONFIG_W1_SLAVE_DS2760=m
++CONFIG_W1_SLAVE_BQ27000=m
+ CONFIG_THERMAL=y
++CONFIG_WATCHDOG=y
++CONFIG_SOFT_WATCHDOG=m
++CONFIG_USBPCWATCHDOG=m
+ CONFIG_MEDIA_SUPPORT=m
+ CONFIG_VIDEO_DEV=m
+-CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
++CONFIG_DVB_CORE=m
+ CONFIG_VIDEO_VIVI=m
+ CONFIG_USB_VIDEO_CLASS=m
+ CONFIG_USB_M5602=m
+ CONFIG_USB_STV06XX=m
++CONFIG_USB_GL860=m
++CONFIG_USB_GSPCA_BENQ=m
+ CONFIG_USB_GSPCA_CONEX=m
++CONFIG_USB_GSPCA_CPIA1=m
+ CONFIG_USB_GSPCA_ETOMS=m
+ CONFIG_USB_GSPCA_FINEPIX=m
++CONFIG_USB_GSPCA_JEILINJ=m
+ CONFIG_USB_GSPCA_MARS=m
+ CONFIG_USB_GSPCA_MR97310A=m
+ CONFIG_USB_GSPCA_OV519=m
+ CONFIG_USB_GSPCA_OV534=m
++CONFIG_USB_GSPCA_OV534_9=m
+ CONFIG_USB_GSPCA_PAC207=m
++CONFIG_USB_GSPCA_PAC7302=m
+ CONFIG_USB_GSPCA_PAC7311=m
++CONFIG_USB_GSPCA_SN9C2028=m
+ CONFIG_USB_GSPCA_SN9C20X=m
+ CONFIG_USB_GSPCA_SONIXB=m
+ CONFIG_USB_GSPCA_SONIXJ=m
+@@ -198,30 +651,72 @@ CONFIG_USB_GSPCA_SPCA505=m
+ CONFIG_USB_GSPCA_SPCA506=m
+ CONFIG_USB_GSPCA_SPCA508=m
+ CONFIG_USB_GSPCA_SPCA561=m
++CONFIG_USB_GSPCA_SPCA1528=m
+ CONFIG_USB_GSPCA_SQ905=m
+ CONFIG_USB_GSPCA_SQ905C=m
++CONFIG_USB_GSPCA_SQ930X=m
+ CONFIG_USB_GSPCA_STK014=m
++CONFIG_USB_GSPCA_STV0680=m
+ CONFIG_USB_GSPCA_SUNPLUS=m
+ CONFIG_USB_GSPCA_T613=m
+ CONFIG_USB_GSPCA_TV8532=m
+ CONFIG_USB_GSPCA_VC032X=m
+ CONFIG_USB_GSPCA_ZC3XX=m
++CONFIG_VIDEO_PVRUSB2=m
++CONFIG_VIDEO_PVRUSB2_DEBUGIFC=y
++CONFIG_VIDEO_HDPVR=m
++CONFIG_VIDEO_EM28XX=m
++CONFIG_VIDEO_EM28XX_ALSA=m
++CONFIG_VIDEO_CX231XX=m
++CONFIG_VIDEO_CX231XX_ALSA=m
++CONFIG_VIDEO_USBVISION=m
+ CONFIG_USB_ET61X251=m
+ CONFIG_USB_SN9C102=m
++CONFIG_USB_PWC=m
+ CONFIG_USB_ZR364XX=m
+ CONFIG_USB_STKWEBCAM=m
+ CONFIG_USB_S2255=m
+-# CONFIG_RADIO_ADAPTERS is not set
++CONFIG_USB_DSBR=m
++CONFIG_USB_MR800=m
++CONFIG_DVB_USB=m
++CONFIG_DVB_USB_A800=m
++CONFIG_DVB_USB_DIBUSB_MB=m
++CONFIG_DVB_USB_DIBUSB_MB_FAULTY=y
++CONFIG_DVB_USB_DIBUSB_MC=m
++CONFIG_DVB_USB_DIB0700=m
++CONFIG_DVB_USB_UMT_010=m
++CONFIG_DVB_USB_CXUSB=m
++CONFIG_DVB_USB_M920X=m
++CONFIG_DVB_USB_GL861=m
++CONFIG_DVB_USB_AU6610=m
++CONFIG_DVB_USB_DIGITV=m
++CONFIG_DVB_USB_VP7045=m
++CONFIG_DVB_USB_VP702X=m
++CONFIG_DVB_USB_GP8PSK=m
++CONFIG_DVB_USB_NOVA_T_USB2=m
++CONFIG_DVB_USB_TTUSB2=m
++CONFIG_DVB_USB_DTT200U=m
++CONFIG_DVB_USB_OPERA1=m
++CONFIG_DVB_USB_AF9005=m
++CONFIG_DVB_USB_DW2102=m
++CONFIG_DVB_USB_CINERGY_T2=m
++CONFIG_DVB_USB_ANYSEE=m
++CONFIG_DVB_USB_DTV5100=m
++CONFIG_DVB_USB_AF9015=m
++CONFIG_DVB_USB_CE6230=m
++CONFIG_DVB_USB_FRIIO=m
++CONFIG_DVB_USB_EC168=m
++CONFIG_DVB_USB_AZ6027=m
++CONFIG_DVB_TTUSB_BUDGET=m
++CONFIG_DVB_TTUSB_DEC=m
+ CONFIG_VIDEO_OUTPUT_CONTROL=y
+ CONFIG_FB=y
+ CONFIG_FIRMWARE_EDID=y
+-CONFIG_FB_MODE_HELPERS=y
+ CONFIG_FB_TILEBLITTING=y
+ CONFIG_FB_SIS=y
+ CONFIG_FB_SIS_300=y
+ CONFIG_FB_SIS_315=y
+-CONFIG_BACKLIGHT_LCD_SUPPORT=y
+-# CONFIG_LCD_CLASS_DEVICE is not set
++CONFIG_FB_UDL=m
+ CONFIG_BACKLIGHT_CLASS_DEVICE=y
+ CONFIG_BACKLIGHT_GENERIC=m
+ # CONFIG_VGA_CONSOLE is not set
+@@ -238,8 +733,6 @@ CONFIG_FONT_SUN8x16=y
+ CONFIG_FONT_SUN12x22=y
+ CONFIG_FONT_10x18=y
+ CONFIG_LOGO=y
+-# CONFIG_LOGO_LINUX_MONO is not set
+-# CONFIG_LOGO_LINUX_VGA16 is not set
+ CONFIG_SOUND=m
+ CONFIG_SND=m
+ CONFIG_SND_SEQUENCER=m
+@@ -255,30 +748,18 @@ CONFIG_SND_MPU401=m
+ CONFIG_SND_AC97_POWER_SAVE=y
+ CONFIG_SND_AC97_POWER_SAVE_DEFAULT=10
+ CONFIG_SND_CS5535AUDIO=m
+-# CONFIG_SND_MIPS is not set
+ CONFIG_SND_USB_AUDIO=m
+ CONFIG_SND_USB_CAIAQ=m
+ CONFIG_SND_USB_CAIAQ_INPUT=y
+ CONFIG_HIDRAW=y
++CONFIG_USB_HID=m
+ CONFIG_USB_HIDDEV=y
+-CONFIG_HID_A4TECH=m
+-CONFIG_HID_APPLE=m
+-CONFIG_HID_BELKIN=m
+-CONFIG_HID_CHERRY=m
+-CONFIG_HID_CHICONY=m
+-CONFIG_HID_CYPRESS=m
+ CONFIG_HID_DRAGONRISE=m
+ CONFIG_DRAGONRISE_FF=y
+-CONFIG_HID_EZKEY=m
+-CONFIG_HID_KYE=m
+ CONFIG_HID_GYRATION=m
+ CONFIG_HID_TWINHAN=m
+-CONFIG_HID_KENSINGTON=m
+-CONFIG_HID_LOGITECH=m
+ CONFIG_LOGITECH_FF=y
+ CONFIG_LOGIRUMBLEPAD2_FF=y
+-CONFIG_HID_MICROSOFT=m
+-CONFIG_HID_MONTEREY=m
+ CONFIG_HID_NTRIG=m
+ CONFIG_HID_PANTHERLORD=m
+ CONFIG_PANTHERLORD_FF=y
+@@ -293,18 +774,14 @@ CONFIG_SMARTJOYPLUS_FF=y
+ CONFIG_HID_TOPSEED=m
+ CONFIG_HID_THRUSTMASTER=m
+ CONFIG_THRUSTMASTER_FF=y
+-CONFIG_HID_WACOM=m
+ CONFIG_HID_ZEROPLUS=m
+ CONFIG_ZEROPLUS_FF=y
+ CONFIG_USB=y
+-CONFIG_USB_DEVICEFS=y
+-# CONFIG_USB_DEVICE_CLASS is not set
+ CONFIG_USB_DYNAMIC_MINORS=y
+ CONFIG_USB_OTG_WHITELIST=y
+ CONFIG_USB_MON=y
+ CONFIG_USB_EHCI_HCD=y
+ CONFIG_USB_EHCI_ROOT_HUB_TT=y
+-# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+ CONFIG_USB_OHCI_HCD=y
+ CONFIG_USB_UHCI_HCD=m
+ CONFIG_USB_WHCI_HCD=m
+@@ -312,7 +789,7 @@ CONFIG_USB_HWA_HCD=m
+ CONFIG_USB_ACM=m
+ CONFIG_USB_PRINTER=m
+ CONFIG_USB_WDM=m
+-CONFIG_USB_STORAGE=m
++CONFIG_USB_STORAGE=y
+ CONFIG_USB_STORAGE_DATAFAB=m
+ CONFIG_USB_STORAGE_FREECOM=m
+ CONFIG_USB_STORAGE_ISD200=m
+@@ -321,22 +798,123 @@ CONFIG_USB_STORAGE_SDDR09=m
+ CONFIG_USB_STORAGE_SDDR55=m
+ CONFIG_USB_STORAGE_JUMPSHOT=m
+ CONFIG_USB_STORAGE_ALAUDA=m
++CONFIG_USB_STORAGE_ONETOUCH=m
++CONFIG_USB_STORAGE_KARMA=m
++CONFIG_USB_STORAGE_CYPRESS_ATACB=m
+ CONFIG_USB_LIBUSUAL=y
++CONFIG_USB_MDC800=m
++CONFIG_USB_MICROTEK=m
+ CONFIG_USB_SERIAL=m
+ CONFIG_USB_SERIAL_GENERIC=y
++CONFIG_USB_SERIAL_AIRCABLE=m
++CONFIG_USB_SERIAL_ARK3116=m
++CONFIG_USB_SERIAL_BELKIN=m
++CONFIG_USB_SERIAL_CH341=m
++CONFIG_USB_SERIAL_WHITEHEAT=m
++CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
++CONFIG_USB_SERIAL_CP210X=m
++CONFIG_USB_SERIAL_CYPRESS_M8=m
++CONFIG_USB_SERIAL_EMPEG=m
++CONFIG_USB_SERIAL_FTDI_SIO=m
++CONFIG_USB_SERIAL_FUNSOFT=m
++CONFIG_USB_SERIAL_VISOR=m
++CONFIG_USB_SERIAL_IPAQ=m
++CONFIG_USB_SERIAL_IR=m
++CONFIG_USB_SERIAL_EDGEPORT=m
++CONFIG_USB_SERIAL_EDGEPORT_TI=m
++CONFIG_USB_SERIAL_GARMIN=m
++CONFIG_USB_SERIAL_IPW=m
++CONFIG_USB_SERIAL_IUU=m
++CONFIG_USB_SERIAL_KEYSPAN_PDA=m
++CONFIG_USB_SERIAL_KEYSPAN=m
++CONFIG_USB_SERIAL_KEYSPAN_MPR=y
++CONFIG_USB_SERIAL_KEYSPAN_USA28=y
++CONFIG_USB_SERIAL_KEYSPAN_USA28X=y
++CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y
++CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y
++CONFIG_USB_SERIAL_KEYSPAN_USA19=y
++CONFIG_USB_SERIAL_KEYSPAN_USA18X=y
++CONFIG_USB_SERIAL_KEYSPAN_USA19W=y
++CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y
++CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y
++CONFIG_USB_SERIAL_KEYSPAN_USA49W=y
++CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
++CONFIG_USB_SERIAL_KLSI=m
++CONFIG_USB_SERIAL_KOBIL_SCT=m
++CONFIG_USB_SERIAL_MCT_U232=m
++CONFIG_USB_SERIAL_MOS7720=m
++CONFIG_USB_SERIAL_MOS7840=m
++CONFIG_USB_SERIAL_MOTOROLA=m
++CONFIG_USB_SERIAL_NAVMAN=m
++CONFIG_USB_SERIAL_PL2303=m
++CONFIG_USB_SERIAL_OTI6858=m
++CONFIG_USB_SERIAL_QCAUX=m
++CONFIG_USB_SERIAL_QUALCOMM=m
++CONFIG_USB_SERIAL_SPCP8X5=m
++CONFIG_USB_SERIAL_HP4X=m
++CONFIG_USB_SERIAL_SAFE=m
++CONFIG_USB_SERIAL_SAFE_PADDED=y
++CONFIG_USB_SERIAL_SIEMENS_MPI=m
++CONFIG_USB_SERIAL_SIERRAWIRELESS=m
++CONFIG_USB_SERIAL_SYMBOL=m
++CONFIG_USB_SERIAL_TI=m
++CONFIG_USB_SERIAL_CYBERJACK=m
++CONFIG_USB_SERIAL_XIRCOM=m
++CONFIG_USB_SERIAL_OPTION=m
++CONFIG_USB_SERIAL_OMNINET=m
++CONFIG_USB_SERIAL_OPTICON=m
++CONFIG_USB_SERIAL_VIVOPAY_SERIAL=m
++CONFIG_USB_SERIAL_ZIO=m
++CONFIG_USB_SERIAL_SSU100=m
++CONFIG_USB_EMI62=m
++CONFIG_USB_EMI26=m
++CONFIG_USB_ADUTUX=m
++CONFIG_USB_SEVSEG=m
++CONFIG_USB_RIO500=m
++CONFIG_USB_LEGOTOWER=m
++CONFIG_USB_LCD=m
+ CONFIG_USB_LED=m
++CONFIG_USB_CYPRESS_CY7C63=m
++CONFIG_USB_CYTHERM=m
++CONFIG_USB_IDMOUSE=m
++CONFIG_USB_FTDI_ELAN=m
++CONFIG_USB_APPLEDISPLAY=m
++CONFIG_USB_SISUSBVGA=m
++CONFIG_USB_LD=m
++CONFIG_USB_TRANCEVIBRATOR=m
++CONFIG_USB_IOWARRIOR=m
++CONFIG_USB_ISIGHTFW=m
++CONFIG_USB_ATM=m
++CONFIG_USB_SPEEDTOUCH=m
++CONFIG_USB_CXACRU=m
++CONFIG_USB_UEAGLEATM=m
++CONFIG_USB_XUSBATM=m
+ CONFIG_USB_GADGET=m
+ CONFIG_USB_GADGET_M66592=y
++CONFIG_USB_ZERO=m
++CONFIG_USB_AUDIO=m
++CONFIG_USB_ETH=m
++CONFIG_USB_GADGETFS=m
++CONFIG_USB_FUNCTIONFS=m
++CONFIG_USB_MASS_STORAGE=m
++CONFIG_USB_G_SERIAL=m
++CONFIG_USB_MIDI_GADGET=m
++CONFIG_USB_G_PRINTER=m
++CONFIG_USB_CDC_COMPOSITE=m
++CONFIG_USB_G_MULTI=m
++CONFIG_USB_G_WEBCAM=m
++CONFIG_NOP_USB_XCEIV=m
+ CONFIG_MMC=m
+ CONFIG_LEDS_CLASS=y
+ CONFIG_STAGING=y
+ # CONFIG_STAGING_EXCLUDE_BUILD is not set
++CONFIG_PRISM2_USB=m
++CONFIG_R8187SE=m
++CONFIG_RTL8192E=m
++CONFIG_USB_SERIAL_QUATECH2=m
++CONFIG_USB_SERIAL_QUATECH_USB2=m
+ CONFIG_FB_SM7XX=y
+-CONFIG_EXT2_FS=m
+-CONFIG_EXT3_FS=y
+-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+-CONFIG_EXT3_FS_POSIX_ACL=y
+-CONFIG_EXT3_FS_SECURITY=y
++CONFIG_EASYCAP=m
+ CONFIG_EXT4_FS=y
+ CONFIG_REISERFS_FS=m
+ CONFIG_REISERFS_PROC_INFO=y
+@@ -346,16 +924,17 @@ CONFIG_JFS_POSIX_ACL=y
+ CONFIG_XFS_FS=m
+ CONFIG_XFS_QUOTA=y
+ CONFIG_XFS_POSIX_ACL=y
+-CONFIG_BTRFS_FS=m
+-CONFIG_QUOTA=y
+-CONFIG_QFMT_V2=m
+-CONFIG_AUTOFS_FS=m
++CONFIG_GFS2_FS=m
++CONFIG_BTRFS_FS=y
+ CONFIG_AUTOFS4_FS=m
++CONFIG_FUSE_FS=m
++CONFIG_CUSE=m
+ CONFIG_FSCACHE=m
+ CONFIG_CACHEFILES=m
+ CONFIG_ISO9660_FS=m
+ CONFIG_JOLIET=y
+ CONFIG_ZISOFS=y
++CONFIG_UDF_FS=m
+ CONFIG_MSDOS_FS=m
+ CONFIG_VFAT_FS=m
+ CONFIG_NTFS_FS=m
+@@ -366,6 +945,7 @@ CONFIG_CRAMFS=m
+ CONFIG_SQUASHFS=m
+ CONFIG_SQUASHFS_EMBEDDED=y
+ CONFIG_ROMFS_FS=m
++CONFIG_UFS_FS=m
+ CONFIG_NFS_FS=m
+ CONFIG_NFS_V3=y
+ CONFIG_NFS_V3_ACL=y
+@@ -411,15 +991,15 @@ CONFIG_NLS_ISO8859_15=m
+ CONFIG_NLS_KOI8_R=m
+ CONFIG_NLS_KOI8_U=m
+ CONFIG_NLS_UTF8=y
+-CONFIG_PRINTK_TIME=y
+ CONFIG_FRAME_WARN=1024
++CONFIG_MAGIC_SYSRQ=y
+ CONFIG_STRIP_ASM_SYMS=y
+ CONFIG_DEBUG_FS=y
+-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
++CONFIG_DEBUG_KERNEL=y
++CONFIG_TIMER_STATS=y
+ CONFIG_SYSCTL_SYSCALL_CHECK=y
+ CONFIG_KEYS=y
+ CONFIG_KEYS_DEBUG_PROC_KEYS=y
+-CONFIG_CRYPTO_FIPS=y
+ CONFIG_CRYPTO_NULL=m
+ CONFIG_CRYPTO_CRYPTD=m
+ CONFIG_CRYPTO_AUTHENC=m
+@@ -429,15 +1009,12 @@ CONFIG_CRYPTO_GCM=m
+ CONFIG_CRYPTO_LRW=m
+ CONFIG_CRYPTO_PCBC=m
+ CONFIG_CRYPTO_XTS=m
+-CONFIG_CRYPTO_HMAC=m
+ CONFIG_CRYPTO_XCBC=m
+-CONFIG_CRYPTO_MD4=m
+ CONFIG_CRYPTO_MICHAEL_MIC=m
+ CONFIG_CRYPTO_RMD128=m
+ CONFIG_CRYPTO_RMD160=m
+ CONFIG_CRYPTO_RMD256=m
+ CONFIG_CRYPTO_RMD320=m
+-CONFIG_CRYPTO_SHA1=m
+ CONFIG_CRYPTO_SHA256=m
+ CONFIG_CRYPTO_SHA512=m
+ CONFIG_CRYPTO_TGR192=m
+@@ -457,4 +1034,3 @@ CONFIG_CRYPTO_TWOFISH=m
+ CONFIG_CRYPTO_DEFLATE=m
+ CONFIG_CRYPTO_ZLIB=m
+ CONFIG_CRYPTO_LZO=m
+-CONFIG_CRC_T10DIF=y
+diff --git a/arch/mips/configs/lemote2f_minimal_defconfig b/arch/mips/configs/lemote2f_minimal_defconfig
+new file mode 100644
+index 0000000..82ad0bd4
+--- /dev/null
++++ b/arch/mips/configs/lemote2f_minimal_defconfig
+@@ -0,0 +1,122 @@
++CONFIG_MACH_LOONGSON=y
++CONFIG_LEMOTE_MACH2F=y
++CONFIG_64BIT=y
++CONFIG_NO_HZ=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_PREEMPT=y
++CONFIG_EXPERIMENTAL=y
++# CONFIG_LOCALVERSION_AUTO is not set
++CONFIG_KERNEL_LZMA=y
++CONFIG_SYSVIPC=y
++CONFIG_POSIX_MQUEUE=y
++CONFIG_BSD_PROCESS_ACCT=y
++CONFIG_BSD_PROCESS_ACCT_V3=y
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_LOG_BUF_SHIFT=15
++CONFIG_BLK_DEV_INITRD=y
++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
++# CONFIG_BLK_DEV_BSG is not set
++# CONFIG_IOSCHED_DEADLINE is not set
++# CONFIG_IOSCHED_CFQ is not set
++CONFIG_PCI=y
++CONFIG_MIPS32_COMPAT=y
++CONFIG_MIPS32_O32=y
++CONFIG_MIPS32_N32=y
++CONFIG_NET=y
++CONFIG_PACKET=y
++CONFIG_UNIX=y
++CONFIG_INET=y
++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
++# CONFIG_INET_XFRM_MODE_TUNNEL is not set
++# CONFIG_INET_XFRM_MODE_BEET is not set
++# CONFIG_INET_LRO is not set
++# CONFIG_INET_DIAG is not set
++# CONFIG_IPV6 is not set
++# CONFIG_WIRELESS is not set
++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
++CONFIG_DEVTMPFS=y
++CONFIG_DEVTMPFS_MOUNT=y
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_SIZE=8192
++# CONFIG_MISC_DEVICES is not set
++# CONFIG_SCSI_PROC_FS is not set
++CONFIG_BLK_DEV_SD=y
++# CONFIG_SCSI_LOWLEVEL is not set
++CONFIG_ATA=y
++# CONFIG_SATA_PMP is not set
++CONFIG_PATA_AMD=y
++CONFIG_NETDEVICES=y
++CONFIG_NET_ETHERNET=y
++CONFIG_NET_PCI=y
++CONFIG_8139TOO=y
++CONFIG_R8169=y
++# CONFIG_NETDEV_10000 is not set
++# CONFIG_WLAN is not set
++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
++CONFIG_INPUT_EVDEV=y
++# CONFIG_SERIO_SERPORT is not set
++# CONFIG_DEVKMEM is not set
++CONFIG_SERIAL_8250=y
++CONFIG_SERIAL_8250_CONSOLE=y
++CONFIG_LEGACY_PTY_COUNT=16
++# CONFIG_HW_RANDOM is not set
++# CONFIG_HWMON is not set
++# CONFIG_MFD_SUPPORT is not set
++CONFIG_FB=y
++CONFIG_FB_SIS=y
++CONFIG_FB_SIS_300=y
++CONFIG_FB_SIS_315=y
++# CONFIG_VGA_CONSOLE is not set
++CONFIG_FRAMEBUFFER_CONSOLE=y
++CONFIG_HIDRAW=y
++CONFIG_USB_HIDDEV=y
++CONFIG_HID_DRAGONRISE=y
++CONFIG_DRAGONRISE_FF=y
++CONFIG_HID_GYRATION=y
++CONFIG_HID_TWINHAN=y
++CONFIG_LOGITECH_FF=y
++CONFIG_LOGIRUMBLEPAD2_FF=y
++CONFIG_HID_NTRIG=y
++CONFIG_HID_PANTHERLORD=y
++CONFIG_PANTHERLORD_FF=y
++CONFIG_HID_PETALYNX=y
++CONFIG_HID_SAMSUNG=y
++CONFIG_HID_SONY=y
++CONFIG_HID_SUNPLUS=y
++CONFIG_HID_GREENASIA=y
++CONFIG_GREENASIA_FF=y
++CONFIG_HID_SMARTJOYPLUS=y
++CONFIG_SMARTJOYPLUS_FF=y
++CONFIG_HID_TOPSEED=y
++CONFIG_HID_THRUSTMASTER=y
++CONFIG_THRUSTMASTER_FF=y
++CONFIG_HID_ZEROPLUS=y
++CONFIG_ZEROPLUS_FF=y
++CONFIG_USB=y
++# CONFIG_USB_DEVICE_CLASS is not set
++CONFIG_USB_DYNAMIC_MINORS=y
++CONFIG_USB_MON=y
++CONFIG_USB_EHCI_HCD=y
++CONFIG_USB_EHCI_ROOT_HUB_TT=y
++# CONFIG_USB_EHCI_TT_NEWSCHED is not set
++CONFIG_USB_OHCI_HCD=y
++CONFIG_USB_STORAGE=y
++CONFIG_USB_LIBUSUAL=y
++CONFIG_RTC_CLASS=y
++CONFIG_RTC_DRV_CMOS=y
++CONFIG_STAGING=y
++# CONFIG_STAGING_EXCLUDE_BUILD is not set
++CONFIG_FB_SM7XX=y
++# CONFIG_MIPS_PLATFORM_DEVICES is not set
++CONFIG_EXT4_FS=y
++CONFIG_TMPFS=y
++# CONFIG_MISC_FILESYSTEMS is not set
++# CONFIG_NETWORK_FILESYSTEMS is not set
++CONFIG_NLS_DEFAULT="utf8"
++CONFIG_NLS_ASCII=y
++CONFIG_NLS_UTF8=y
++CONFIG_FRAME_WARN=1024
++CONFIG_STRIP_ASM_SYMS=y
++# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+diff --git a/arch/mips/configs/lemote2f_small_defconfig b/arch/mips/configs/lemote2f_small_defconfig
+new file mode 100644
+index 0000000..45a833e
+--- /dev/null
++++ b/arch/mips/configs/lemote2f_small_defconfig
+@@ -0,0 +1,151 @@
++CONFIG_MACH_LOONGSON=y
++CONFIG_LEMOTE_MACH2F=y
++CONFIG_64BIT=y
++CONFIG_NO_HZ=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_PREEMPT=y
++CONFIG_EXPERIMENTAL=y
++# CONFIG_LOCALVERSION_AUTO is not set
++CONFIG_KERNEL_LZMA=y
++CONFIG_SYSVIPC=y
++CONFIG_POSIX_MQUEUE=y
++CONFIG_BSD_PROCESS_ACCT=y
++CONFIG_BSD_PROCESS_ACCT_V3=y
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_LOG_BUF_SHIFT=15
++# CONFIG_UTS_NS is not set
++# CONFIG_IPC_NS is not set
++# CONFIG_USER_NS is not set
++# CONFIG_PID_NS is not set
++# CONFIG_NET_NS is not set
++CONFIG_BLK_DEV_INITRD=y
++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
++CONFIG_MODULES=y
++CONFIG_MODULE_UNLOAD=y
++# CONFIG_BLK_DEV_BSG is not set
++# CONFIG_IOSCHED_DEADLINE is not set
++# CONFIG_IOSCHED_CFQ is not set
++CONFIG_PCI=y
++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
++CONFIG_MIPS32_COMPAT=y
++CONFIG_MIPS32_O32=y
++CONFIG_MIPS32_N32=y
++CONFIG_PM=y
++CONFIG_HIBERNATION=y
++CONFIG_PM_STD_PARTITION="/dev/sda3"
++CONFIG_PM_RUNTIME=y
++CONFIG_CPU_FREQ=y
++CONFIG_CPU_FREQ_STAT_DETAILS=y
++CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y
++CONFIG_CPU_FREQ_GOV_POWERSAVE=y
++CONFIG_CPU_FREQ_GOV_USERSPACE=y
++CONFIG_CPU_FREQ_GOV_ONDEMAND=y
++CONFIG_LOONGSON2_CPUFREQ=m
++CONFIG_NET=y
++CONFIG_PACKET=y
++CONFIG_UNIX=y
++CONFIG_INET=y
++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
++# CONFIG_INET_XFRM_MODE_TUNNEL is not set
++# CONFIG_INET_XFRM_MODE_BEET is not set
++# CONFIG_INET_LRO is not set
++# CONFIG_INET_DIAG is not set
++# CONFIG_IPV6 is not set
++CONFIG_CFG80211=y
++# CONFIG_CFG80211_DEFAULT_PS is not set
++# CONFIG_CFG80211_WEXT is not set
++CONFIG_LIB80211=y
++CONFIG_MAC80211=y
++CONFIG_RFKILL=y
++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
++CONFIG_DEVTMPFS=y
++CONFIG_DEVTMPFS_MOUNT=y
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_SIZE=8192
++# CONFIG_SCSI_PROC_FS is not set
++CONFIG_BLK_DEV_SD=y
++# CONFIG_SCSI_LOWLEVEL is not set
++CONFIG_ATA=y
++# CONFIG_SATA_PMP is not set
++CONFIG_PATA_AMD=y
++CONFIG_NETDEVICES=y
++CONFIG_NET_ETHERNET=y
++CONFIG_NET_PCI=y
++CONFIG_8139TOO=y
++CONFIG_R8169=y
++# CONFIG_NETDEV_10000 is not set
++CONFIG_RTL8187B=m
++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
++CONFIG_INPUT_EVDEV=y
++# CONFIG_SERIO_SERPORT is not set
++# CONFIG_DEVKMEM is not set
++CONFIG_SERIAL_8250=y
++CONFIG_SERIAL_8250_CONSOLE=y
++CONFIG_LEGACY_PTY_COUNT=16
++# CONFIG_HW_RANDOM is not set
++CONFIG_HWMON=m
++# CONFIG_MFD_SUPPORT is not set
++CONFIG_FB=y
++CONFIG_FB_SIS=y
++CONFIG_FB_SIS_300=y
++CONFIG_FB_SIS_315=y
++CONFIG_LCD_CLASS_DEVICE=y
++CONFIG_LCD_PLATFORM=y
++CONFIG_BACKLIGHT_CLASS_DEVICE=y
++# CONFIG_BACKLIGHT_GENERIC is not set
++# CONFIG_VGA_CONSOLE is not set
++CONFIG_FRAMEBUFFER_CONSOLE=y
++CONFIG_SOUND=y
++CONFIG_SND=y
++CONFIG_SND_CS5535AUDIO=m
++CONFIG_HIDRAW=y
++CONFIG_USB_HIDDEV=y
++CONFIG_HID_DRAGONRISE=y
++CONFIG_DRAGONRISE_FF=y
++CONFIG_HID_GYRATION=y
++CONFIG_HID_TWINHAN=y
++CONFIG_LOGITECH_FF=y
++CONFIG_LOGIRUMBLEPAD2_FF=y
++CONFIG_HID_NTRIG=y
++CONFIG_HID_PANTHERLORD=y
++CONFIG_PANTHERLORD_FF=y
++CONFIG_HID_PETALYNX=y
++CONFIG_HID_SAMSUNG=y
++CONFIG_HID_SONY=y
++CONFIG_HID_SUNPLUS=y
++CONFIG_HID_GREENASIA=y
++CONFIG_GREENASIA_FF=y
++CONFIG_HID_SMARTJOYPLUS=y
++CONFIG_SMARTJOYPLUS_FF=y
++CONFIG_HID_TOPSEED=y
++CONFIG_HID_THRUSTMASTER=y
++CONFIG_THRUSTMASTER_FF=y
++CONFIG_HID_ZEROPLUS=y
++CONFIG_ZEROPLUS_FF=y
++CONFIG_USB=y
++# CONFIG_USB_DEVICE_CLASS is not set
++CONFIG_USB_DYNAMIC_MINORS=y
++CONFIG_USB_MON=y
++CONFIG_USB_EHCI_HCD=y
++CONFIG_USB_EHCI_ROOT_HUB_TT=y
++# CONFIG_USB_EHCI_TT_NEWSCHED is not set
++CONFIG_USB_OHCI_HCD=y
++CONFIG_USB_STORAGE=y
++CONFIG_USB_LIBUSUAL=y
++CONFIG_RTC_CLASS=y
++CONFIG_RTC_DRV_CMOS=y
++CONFIG_STAGING=y
++# CONFIG_STAGING_EXCLUDE_BUILD is not set
++CONFIG_FB_SM7XX=y
++CONFIG_EXT4_FS=y
++CONFIG_TMPFS=y
++# CONFIG_MISC_FILESYSTEMS is not set
++# CONFIG_NETWORK_FILESYSTEMS is not set
++CONFIG_NLS_DEFAULT="utf8"
++CONFIG_NLS_ASCII=y
++CONFIG_NLS_UTF8=y
++CONFIG_FRAME_WARN=1024
++CONFIG_STRIP_ASM_SYMS=y
++# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+diff --git a/arch/mips/configs/yeeloong_tiny_defconfig b/arch/mips/configs/yeeloong_tiny_defconfig
+new file mode 100644
+index 0000000..93e3e7b
+--- /dev/null
++++ b/arch/mips/configs/yeeloong_tiny_defconfig
+@@ -0,0 +1,74 @@
++CONFIG_MACH_LOONGSON=y
++CONFIG_LEMOTE_MACH2F=y
++CONFIG_HZ_1000=y
++CONFIG_PREEMPT=y
++# CONFIG_LOCALVERSION_AUTO is not set
++CONFIG_KERNEL_LZMA=y
++# CONFIG_SWAP is not set
++CONFIG_TINY_RCU=y
++CONFIG_LOG_BUF_SHIFT=15
++CONFIG_EMBEDDED=y
++# CONFIG_KALLSYMS is not set
++# CONFIG_HOTPLUG is not set
++# CONFIG_PRINTK is not set
++# CONFIG_BUG is not set
++# CONFIG_ELF_CORE is not set
++# CONFIG_PCSPKR_PLATFORM is not set
++# CONFIG_BASE_FULL is not set
++# CONFIG_FUTEX is not set
++# CONFIG_EPOLL is not set
++# CONFIG_SIGNALFD is not set
++# CONFIG_TIMERFD is not set
++# CONFIG_EVENTFD is not set
++# CONFIG_SHMEM is not set
++# CONFIG_AIO is not set
++# CONFIG_VM_EVENT_COUNTERS is not set
++# CONFIG_PCI_QUIRKS is not set
++CONFIG_SLOB=y
++# CONFIG_LBDAF is not set
++# CONFIG_BLK_DEV_BSG is not set
++# CONFIG_IOSCHED_DEADLINE is not set
++# CONFIG_IOSCHED_CFQ is not set
++CONFIG_PCI=y
++# CONFIG_FW_LOADER is not set
++# CONFIG_BLK_DEV is not set
++CONFIG_BLK_DEV_SD=y
++# CONFIG_SCSI_LOWLEVEL is not set
++CONFIG_ATA=y
++# CONFIG_ATA_VERBOSE_ERROR is not set
++# CONFIG_SATA_PMP is not set
++CONFIG_PATA_AMD=y
++# CONFIG_INPUT_MOUSEDEV is not set
++# CONFIG_INPUT_MOUSE is not set
++# CONFIG_SERIO_SERPORT is not set
++# CONFIG_CONSOLE_TRANSLATIONS is not set
++# CONFIG_DEVKMEM is not set
++# CONFIG_UNIX98_PTYS is not set
++# CONFIG_LEGACY_PTYS is not set
++# CONFIG_HW_RANDOM is not set
++# CONFIG_HWMON is not set
++# CONFIG_MFD_SUPPORT is not set
++# CONFIG_VGA_ARB is not set
++CONFIG_FB=y
++# CONFIG_VGA_CONSOLE is not set
++CONFIG_FRAMEBUFFER_CONSOLE=y
++# CONFIG_HID_SUPPORT is not set
++# CONFIG_USB_SUPPORT is not set
++CONFIG_RTC_CLASS=y
++CONFIG_RTC_DRV_CMOS=y
++CONFIG_STAGING=y
++# CONFIG_STAGING_EXCLUDE_BUILD is not set
++CONFIG_FB_SM7XX=y
++# CONFIG_MIPS_PLATFORM_DEVICES is not set
++CONFIG_EXT2_FS=y
++# CONFIG_FILE_LOCKING is not set
++# CONFIG_DNOTIFY is not set
++# CONFIG_INOTIFY_USER is not set
++# CONFIG_PROC_FS is not set
++# CONFIG_SYSFS is not set
++# CONFIG_MISC_FILESYSTEMS is not set
++# CONFIG_ENABLE_WARN_DEPRECATED is not set
++# CONFIG_ENABLE_MUST_CHECK is not set
++CONFIG_STRIP_ASM_SYMS=y
++# CONFIG_BKL is not set
++CONFIG_CRC16=y
+diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h
+index e5ec8fc..bcce9b0 100644
+--- a/arch/mips/include/asm/cpu-features.h
++++ b/arch/mips/include/asm/cpu-features.h
+@@ -13,6 +13,10 @@
+ #include <asm/cpu-info.h>
+ #include <cpu-feature-overrides.h>
+
++#ifndef current_cpu_prid
++#define current_cpu_prid() current_cpu_data.processor_id
++#endif
++
+ #ifndef current_cpu_type
+ #define current_cpu_type() current_cpu_data.cputype
+ #endif
+diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h
+index dd86ab2..5eb6c38 100644
+--- a/arch/mips/include/asm/cpu.h
++++ b/arch/mips/include/asm/cpu.h
+@@ -37,6 +37,8 @@
+ #define PRID_COMP_CAVIUM 0x0d0000
+ #define PRID_COMP_INGENIC 0xd00000
+
++#define PRID_COMP_MASK 0xff0000
++
+ /*
+ * Assigned values for the product ID register. In order to detect a
+ * certain CPU type exactly eventually additional registers may need to
+@@ -74,6 +76,7 @@
+ #define PRID_IMP_LOONGSON2 0x6300
+
+ #define PRID_IMP_UNKNOWN 0xff00
++#define PRID_IMP_MASK 0xff00
+
+ /*
+ * These are the PRID's for when 23:16 == PRID_COMP_MIPS
+@@ -202,6 +205,13 @@
+ #define PRID_REV_LOONGSON1B 0x0020
+ #define PRID_REV_LOONGSON2E 0x0002
+ #define PRID_REV_LOONGSON2F 0x0003
++#define PRID_REV_LOONGSON1B 0x0020
++
++#define cpu_prid_comp() (current_cpu_prid() & PRID_COMP_MASK)
++#define cpu_prid_imp() (current_cpu_prid() & PRID_IMP_MASK)
++#define cpu_prid_rev() (current_cpu_prid() & PRID_REV_MASK)
++
++#define cpu_prid_encode(comp, imp, rev) ((comp) | (imp) | (rev))
+
+ /*
+ * Older processors used to encode processor version and revision in two
+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/ftrace.h b/arch/mips/include/asm/ftrace.h
+index ce35c9a..4c02d4c 100644
+--- a/arch/mips/include/asm/ftrace.h
++++ b/arch/mips/include/asm/ftrace.h
+@@ -3,7 +3,7 @@
+ * License. See the file "COPYING" in the main directory of this archive for
+ * more details.
+ *
+- * Copyright (C) 2009 DSLab, Lanzhou University, China
++ * Copyright (C) 2009, 2010 DSLab, Lanzhou University, China
+ * Author: Wu Zhangjin <wuzhangjin@gmail.com>
+ */
+
+@@ -83,8 +83,8 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr)
+
+ struct dyn_arch_ftrace {
+ };
+-
+ #endif /* CONFIG_DYNAMIC_FTRACE */
++
+ #endif /* __ASSEMBLY__ */
+ #endif /* CONFIG_FUNCTION_TRACER */
+ #endif /* _ASM_MIPS_FTRACE_H */
+diff --git a/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h b/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h
+index c0f3ef4..223de04 100644
+--- a/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h
++++ b/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h
+@@ -16,6 +16,20 @@
+ #ifndef __ASM_MACH_LOONGSON_CPU_FEATURE_OVERRIDES_H
+ #define __ASM_MACH_LOONGSON_CPU_FEATURE_OVERRIDES_H
+
++#ifdef CONFIG_CPU_LOONGSON2
++#define cpu_prid_loongson2() \
++ cpu_prid_encode(PRID_COMP_LEGACY, PRID_IMP_LOONGSON2, 0)
++
++#ifdef CONFIG_CPU_LOONGSON2F
++#define current_cpu_prid() (cpu_prid_loongson2() | PRID_REV_LOONGSON2F)
++#else /* CONFIG_CPU_LOONGSON2E */
++#define current_cpu_prid() (cpu_prid_loongson2() | PRID_REV_LOONGSON2E)
++#endif
++
++#endif /* CONFIG_CPU_LOONGSON2 */
++
++#define current_cpu_type() CPU_LOONGSON2
++
+ #define cpu_dcache_line_size() 32
+ #define cpu_icache_line_size() 32
+ #define cpu_scache_line_size() 32
+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/ec_kb3310b.h b/arch/mips/include/asm/mach-loongson/ec_kb3310b.h
+new file mode 100644
+index 0000000..600ac10
+--- /dev/null
++++ b/arch/mips/include/asm/mach-loongson/ec_kb3310b.h
+@@ -0,0 +1,194 @@
++/*
++ * KB3310B Embedded Controller
++ *
++ * Copyright (C) 2008 Lemote Inc.
++ * Author: liujl <liujl@lemote.com>, 2008-03-14
++ * 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.
++ */
++
++#ifndef _EC_KB3310B_H
++#define _EC_KB3310B_H
++
++extern unsigned char ec_read(unsigned short addr);
++extern void ec_write(unsigned short addr, unsigned char val);
++extern int ec_query_seq(unsigned char cmd);
++extern int ec_query_event_num(void);
++extern int ec_get_event_num(void);
++
++typedef int (*sci_handler) (int status);
++extern sci_handler yeeloong_report_lid_status;
++
++#define ON 1
++#define OFF 0
++
++#define SCI_IRQ_NUM 0x0A
++
++/*
++ * The following registers are determined by the EC index configuration.
++ * 1, fill the PORT_HIGH as EC register high part.
++ * 2, fill the PORT_LOW as EC register low part.
++ * 3, fill the PORT_DATA as EC register write data or get the data from it.
++ */
++#define EC_IO_PORT_HIGH 0x0381
++#define EC_IO_PORT_LOW 0x0382
++#define EC_IO_PORT_DATA 0x0383
++
++/*
++ * EC delay time is 500us for register and status access
++ */
++#define EC_REG_DELAY 500 /* unit : us */
++#define EC_CMD_TIMEOUT 0x1000
++
++/*
++ * EC access port for SCI communication
++ */
++#define EC_CMD_PORT 0x66
++#define EC_STS_PORT 0x66
++#define EC_DAT_PORT 0x62
++#define CMD_INIT_IDLE_MODE 0xdd
++#define CMD_EXIT_IDLE_MODE 0xdf
++#define CMD_INIT_RESET_MODE 0xd8
++#define CMD_REBOOT_SYSTEM 0x8c
++#define CMD_GET_EVENT_NUM 0x84
++#define CMD_PROGRAM_PIECE 0xda
++
++/* temperature & fan registers */
++#define REG_TEMPERATURE_VALUE 0xF458
++#define REG_FAN_AUTO_MAN_SWITCH 0xF459
++#define BIT_FAN_AUTO 0
++#define BIT_FAN_MANUAL 1
++#define REG_FAN_CONTROL 0xF4D2
++#define BIT_FAN_CONTROL_ON (1 << 0)
++#define BIT_FAN_CONTROL_OFF (0 << 0)
++#define REG_FAN_STATUS 0xF4DA
++#define BIT_FAN_STATUS_ON (1 << 0)
++#define BIT_FAN_STATUS_OFF (0 << 0)
++#define REG_FAN_SPEED_HIGH 0xFE22
++#define REG_FAN_SPEED_LOW 0xFE23
++#define REG_FAN_SPEED_LEVEL 0xF4CC
++/* fan speed divider */
++#define FAN_SPEED_DIVIDER 480000 /* (60*1000*1000/62.5/2)*/
++
++/* battery registers */
++#define REG_BAT_DESIGN_CAP_HIGH 0xF77D
++#define REG_BAT_DESIGN_CAP_LOW 0xF77E
++#define REG_BAT_FULLCHG_CAP_HIGH 0xF780
++#define REG_BAT_FULLCHG_CAP_LOW 0xF781
++#define REG_BAT_DESIGN_VOL_HIGH 0xF782
++#define REG_BAT_DESIGN_VOL_LOW 0xF783
++#define REG_BAT_CURRENT_HIGH 0xF784
++#define REG_BAT_CURRENT_LOW 0xF785
++#define REG_BAT_VOLTAGE_HIGH 0xF786
++#define REG_BAT_VOLTAGE_LOW 0xF787
++#define REG_BAT_TEMPERATURE_HIGH 0xF788
++#define REG_BAT_TEMPERATURE_LOW 0xF789
++#define REG_BAT_RELATIVE_CAP_HIGH 0xF492
++#define REG_BAT_RELATIVE_CAP_LOW 0xF493
++#define REG_BAT_VENDOR 0xF4C4
++#define FLAG_BAT_VENDOR_SANYO 0x01
++#define FLAG_BAT_VENDOR_SIMPLO 0x02
++#define REG_BAT_CELL_COUNT 0xF4C6
++#define FLAG_BAT_CELL_3S1P 0x03
++#define FLAG_BAT_CELL_3S2P 0x06
++#define REG_BAT_CHARGE 0xF4A2
++#define FLAG_BAT_CHARGE_DISCHARGE 0x01
++#define FLAG_BAT_CHARGE_CHARGE 0x02
++#define FLAG_BAT_CHARGE_ACPOWER 0x00
++#define REG_BAT_STATUS 0xF4B0
++#define BIT_BAT_STATUS_LOW (1 << 5)
++#define BIT_BAT_STATUS_DESTROY (1 << 2)
++#define BIT_BAT_STATUS_FULL (1 << 1)
++#define BIT_BAT_STATUS_IN (1 << 0)
++#define REG_BAT_CHARGE_STATUS 0xF4B1
++#define BIT_BAT_CHARGE_STATUS_OVERTEMP (1 << 2)
++#define BIT_BAT_CHARGE_STATUS_PRECHG (1 << 1)
++#define REG_BAT_STATE 0xF482
++#define BIT_BAT_STATE_CHARGING (1 << 1)
++#define BIT_BAT_STATE_DISCHARGING (1 << 0)
++#define REG_BAT_POWER 0xF440
++#define BIT_BAT_POWER_S3 (1 << 2)
++#define BIT_BAT_POWER_ON (1 << 1)
++#define BIT_BAT_POWER_ACIN (1 << 0)
++
++/* Audio: rd/wr */
++#define REG_AUDIO_VOLUME 0xF46C
++#define REG_AUDIO_MUTE 0xF4E7
++#define REG_AUDIO_BEEP 0xF4D0
++/* USB port power or not: rd/wr */
++#define REG_USB0_FLAG 0xF461
++#define REG_USB1_FLAG 0xF462
++#define REG_USB2_FLAG 0xF463
++#define BIT_USB_FLAG_ON 1
++#define BIT_USB_FLAG_OFF 0
++/* LID */
++#define REG_LID_DETECT 0xF4BD
++#define BIT_LID_DETECT_ON 1
++#define BIT_LID_DETECT_OFF 0
++/* CRT */
++#define REG_CRT_DETECT 0xF4AD
++#define BIT_CRT_DETECT_PLUG 1
++#define BIT_CRT_DETECT_UNPLUG 0
++/* LCD backlight brightness adjust: 9 levels */
++#define REG_DISPLAY_BRIGHTNESS 0xF4F5
++/* Black screen Status */
++#define BIT_DISPLAY_LCD_ON 1
++#define BIT_DISPLAY_LCD_OFF 0
++/* LCD backlight control: off/restore */
++#define REG_BACKLIGHT_CTRL 0xF7BD
++#define BIT_BACKLIGHT_ON 1
++#define BIT_BACKLIGHT_OFF 0
++/* Reset the machine auto-clear: rd/wr */
++#define REG_RESET 0xF4EC
++#define BIT_RESET_ON 1
++/* Light the led: rd/wr */
++#define REG_LED 0xF4C8
++#define BIT_LED_RED_POWER (1 << 0)
++#define BIT_LED_ORANGE_POWER (1 << 1)
++#define BIT_LED_GREEN_CHARGE (1 << 2)
++#define BIT_LED_RED_CHARGE (1 << 3)
++#define BIT_LED_NUMLOCK (1 << 4)
++/* Test led mode, all led on/off */
++#define REG_LED_TEST 0xF4C2
++#define BIT_LED_TEST_IN 1
++#define BIT_LED_TEST_OUT 0
++/* Camera on/off */
++#define REG_CAMERA_STATUS 0xF46A
++#define BIT_CAMERA_STATUS_ON 1
++#define BIT_CAMERA_STATUS_OFF 0
++#define REG_CAMERA_CONTROL 0xF7B7
++#define BIT_CAMERA_CONTROL_OFF 0
++#define BIT_CAMERA_CONTROL_ON 1
++/* Wlan Status */
++#define REG_WLAN 0xF4FA
++#define BIT_WLAN_ON 1
++#define BIT_WLAN_OFF 0
++#define REG_DISPLAY_LCD 0xF79F
++
++/* SCI Event Number from EC */
++enum {
++ EVENT_LID = 0x23, /* Turn on/off LID */
++ EVENT_SWITCHVIDEOMODE, /* Fn+F3 for display switch */
++ EVENT_SLEEP, /* Fn+F1 for entering sleep mode */
++ EVENT_OVERTEMP, /* Over-temperature happened */
++ EVENT_CRT_DETECT, /* CRT is connected */
++ EVENT_CAMERA, /* Camera on/off */
++ EVENT_USB_OC2, /* USB2 Over Current occurred */
++ EVENT_USB_OC0, /* USB0 Over Current occurred */
++ EVENT_DISPLAYTOGGLE, /* Fn+F2, Turn on/off backlight */
++ EVENT_AUDIO_MUTE, /* Fn+F4, Mute on/off */
++ EVENT_DISPLAY_BRIGHTNESS,/* Fn+^/V, LCD backlight brightness adjust */
++ EVENT_AC_BAT, /* AC & Battery relative issue */
++ EVENT_AUDIO_VOLUME, /* Fn+<|>, Volume adjust */
++ EVENT_WLAN, /* Wlan on/off */
++};
++
++#define EVENT_START EVENT_LID
++#define EVENT_END EVENT_WLAN
++
++#endif /* !_EC_KB3310B_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..a596622 100644
+--- a/arch/mips/include/asm/mach-loongson/machine.h
++++ b/arch/mips/include/asm/mach-loongson/machine.h
+@@ -12,16 +12,16 @@
+ #define __ASM_MACH_LOONGSON_MACHINE_H
+
+ #ifdef CONFIG_LEMOTE_FULOONG2E
+-
+-#define LOONGSON_MACHTYPE MACH_LEMOTE_FL2E
+-
++ #define LOONGSON_MACHTYPE MACH_LEMOTE_FL2E
+ #endif
+
+ /* use fuloong2f as the default machine of LEMOTE_MACH2F */
+ #ifdef CONFIG_LEMOTE_MACH2F
++ #define LOONGSON_MACHTYPE MACH_LEMOTE_FL2F
++#endif
+
+-#define LOONGSON_MACHTYPE MACH_LEMOTE_FL2F
+-
++#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 6529704..bf6a701 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/mipsregs.h>
+
+ /*
+diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h
+index 0f4aec2..7c79b47 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
+@@ -134,7 +136,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
+ };
+
+ /*
+@@ -162,8 +164,8 @@ enum cop1_sdw_func {
+ */
+ enum cop1x_func {
+ lwxc1_op = 0x00, ldxc1_op = 0x01,
+- pfetch_op = 0x07, swxc1_op = 0x08,
+- sdxc1_op = 0x09, madd_s_op = 0x20,
++ swxc1_op = 0x08, sdxc1_op = 0x09,
++ 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/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
+index c6568bf4..ff54da8 100644
+--- a/arch/mips/kernel/cpu-probe.c
++++ b/arch/mips/kernel/cpu-probe.c
+@@ -53,16 +53,14 @@ __setup("nodsp", dsp_disable);
+
+ static inline void check_errata(void)
+ {
+- struct cpuinfo_mips *c = &current_cpu_data;
+-
+- switch (c->cputype) {
++ switch (current_cpu_type()) {
+ case CPU_34K:
+ /*
+ * Erratum "RPS May Cause Incorrect Instruction Execution"
+ * This code only handles VPE0, any SMP/SMTC/RTOS code
+ * making use of VPE1 will be responsable for that VPE.
+ */
+- if ((c->processor_id & PRID_REV_MASK) <= PRID_REV_34K_V1_0_2)
++ if (cpu_prid_rev() <= PRID_REV_34K_V1_0_2)
+ write_c0_config7(read_c0_config7() | MIPS_CONF7_RPS);
+ break;
+ default:
+@@ -328,7 +326,7 @@ static void __cpuinit decode_configs(struct cpuinfo_mips *c)
+
+ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
+ {
+- switch (c->processor_id & 0xff00) {
++ switch (cpu_prid_imp()) {
+ case PRID_IMP_R2000:
+ c->cputype = CPU_R2000;
+ __cpu_name[cpu] = "R2000";
+@@ -340,7 +338,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
+ c->tlbsize = 64;
+ break;
+ case PRID_IMP_R3000:
+- if ((c->processor_id & 0xff) == PRID_REV_R3000A) {
++ if (cpu_prid_rev() == PRID_REV_R3000A) {
+ if (cpu_has_confreg()) {
+ c->cputype = CPU_R3081E;
+ __cpu_name[cpu] = "R3081";
+@@ -361,7 +359,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
+ break;
+ case PRID_IMP_R4000:
+ if (read_c0_config() & CONF_SC) {
+- if ((c->processor_id & 0xff) >= PRID_REV_R4400) {
++ if (cpu_prid_rev() >= PRID_REV_R4400) {
+ c->cputype = CPU_R4400PC;
+ __cpu_name[cpu] = "R4400PC";
+ } else {
+@@ -369,7 +367,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
+ __cpu_name[cpu] = "R4000PC";
+ }
+ } else {
+- if ((c->processor_id & 0xff) >= PRID_REV_R4400) {
++ if (cpu_prid_rev() >= PRID_REV_R4400) {
+ c->cputype = CPU_R4400SC;
+ __cpu_name[cpu] = "R4400SC";
+ } else {
+@@ -398,7 +396,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
+ __cpu_name[cpu] = "NEC VR4121";
+ break;
+ case PRID_REV_VR4122:
+- if ((c->processor_id & 0xf) < 0x3) {
++ if ((current_cpu_prid() & 0xf) < 0x3) {
+ c->cputype = CPU_VR4122;
+ __cpu_name[cpu] = "NEC VR4122";
+ } else {
+@@ -407,7 +405,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
+ }
+ break;
+ case PRID_REV_VR4130:
+- if ((c->processor_id & 0xf) < 0x4) {
++ if ((current_cpu_prid() & 0xf) < 0x4) {
+ c->cputype = CPU_VR4131;
+ __cpu_name[cpu] = "NEC VR4131";
+ } else {
+@@ -458,12 +456,12 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
+ set_isa(c, MIPS_CPU_ISA_I);
+ c->options = MIPS_CPU_TLB | MIPS_CPU_TX39_CACHE;
+
+- if ((c->processor_id & 0xf0) == (PRID_REV_TX3927 & 0xf0)) {
++ if ((current_cpu_prid() & 0xf0) == (PRID_REV_TX3927 & 0xf0)) {
+ c->cputype = CPU_TX3927;
+ __cpu_name[cpu] = "TX3927";
+ c->tlbsize = 64;
+ } else {
+- switch (c->processor_id & 0xff) {
++ switch (cpu_prid_rev()) {
+ case PRID_REV_TX3912:
+ c->cputype = CPU_TX3912;
+ __cpu_name[cpu] = "TX3912";
+@@ -490,7 +488,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
+ __cpu_name[cpu] = "R49XX";
+ set_isa(c, MIPS_CPU_ISA_III);
+ c->options = R4K_OPTS | MIPS_CPU_LLSC;
+- if (!(c->processor_id & 0x08))
++ if (!(current_cpu_prid() & 0x08))
+ c->options |= MIPS_CPU_FPU | MIPS_CPU_32FPR;
+ c->tlbsize = 48;
+ break;
+@@ -649,7 +647,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
+ static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu)
+ {
+ decode_configs(c);
+- switch (c->processor_id & 0xff00) {
++ switch (cpu_prid_imp()) {
+ case PRID_IMP_4KC:
+ c->cputype = CPU_4KC;
+ __cpu_name[cpu] = "MIPS 4Kc";
+@@ -720,11 +718,11 @@ static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu)
+ static inline void cpu_probe_alchemy(struct cpuinfo_mips *c, unsigned int cpu)
+ {
+ decode_configs(c);
+- switch (c->processor_id & 0xff00) {
++ switch (cpu_prid_imp()) {
+ case PRID_IMP_AU1_REV1:
+ case PRID_IMP_AU1_REV2:
+ c->cputype = CPU_ALCHEMY;
+- switch ((c->processor_id >> 24) & 0xff) {
++ switch ((current_cpu_prid() >> 24) & 0xff) {
+ case 0:
+ __cpu_name[cpu] = "Au1000";
+ break;
+@@ -739,7 +737,7 @@ static inline void cpu_probe_alchemy(struct cpuinfo_mips *c, unsigned int cpu)
+ break;
+ case 4:
+ __cpu_name[cpu] = "Au1200";
+- if ((c->processor_id & 0xff) == 2)
++ if (cpu_prid_rev() == 2)
+ __cpu_name[cpu] = "Au1250";
+ break;
+ case 5:
+@@ -757,12 +755,12 @@ static inline void cpu_probe_sibyte(struct cpuinfo_mips *c, unsigned int cpu)
+ {
+ decode_configs(c);
+
+- switch (c->processor_id & 0xff00) {
++ switch (cpu_prid_imp()) {
+ case PRID_IMP_SB1:
+ c->cputype = CPU_SB1;
+ __cpu_name[cpu] = "SiByte SB1";
+ /* FPU in pass1 is known to have issues. */
+- if ((c->processor_id & 0xff) < 0x02)
++ if (cpu_prid_rev() < 0x02)
+ c->options &= ~(MIPS_CPU_FPU | MIPS_CPU_32FPR);
+ break;
+ case PRID_IMP_SB1A:
+@@ -775,7 +773,7 @@ static inline void cpu_probe_sibyte(struct cpuinfo_mips *c, unsigned int cpu)
+ static inline void cpu_probe_sandcraft(struct cpuinfo_mips *c, unsigned int cpu)
+ {
+ decode_configs(c);
+- switch (c->processor_id & 0xff00) {
++ switch (cpu_prid_imp()) {
+ case PRID_IMP_SR71000:
+ c->cputype = CPU_SR71000;
+ __cpu_name[cpu] = "Sandcraft SR71000";
+@@ -788,7 +786,7 @@ static inline void cpu_probe_sandcraft(struct cpuinfo_mips *c, unsigned int cpu)
+ static inline void cpu_probe_nxp(struct cpuinfo_mips *c, unsigned int cpu)
+ {
+ decode_configs(c);
+- switch (c->processor_id & 0xff00) {
++ switch (cpu_prid_imp()) {
+ case PRID_IMP_PR4450:
+ c->cputype = CPU_PR4450;
+ __cpu_name[cpu] = "Philips PR4450";
+@@ -800,7 +798,7 @@ static inline void cpu_probe_nxp(struct cpuinfo_mips *c, unsigned int cpu)
+ static inline void cpu_probe_broadcom(struct cpuinfo_mips *c, unsigned int cpu)
+ {
+ decode_configs(c);
+- switch (c->processor_id & 0xff00) {
++ switch (cpu_prid_imp()) {
+ case PRID_IMP_BMIPS32_REV4:
+ case PRID_IMP_BMIPS32_REV8:
+ c->cputype = CPU_BMIPS32;
+@@ -815,7 +813,7 @@ static inline void cpu_probe_broadcom(struct cpuinfo_mips *c, unsigned int cpu)
+ set_elf_platform(cpu, "bmips3300");
+ break;
+ case PRID_IMP_BMIPS43XX: {
+- int rev = c->processor_id & 0xff;
++ int rev = cpu_prid_rev();
+
+ if (rev >= PRID_REV_BMIPS4380_LO &&
+ rev <= PRID_REV_BMIPS4380_HI) {
+@@ -841,7 +839,7 @@ static inline void cpu_probe_broadcom(struct cpuinfo_mips *c, unsigned int cpu)
+ static inline void cpu_probe_cavium(struct cpuinfo_mips *c, unsigned int cpu)
+ {
+ decode_configs(c);
+- switch (c->processor_id & 0xff00) {
++ switch (cpu_prid_imp()) {
+ case PRID_IMP_CAVIUM_CN38XX:
+ case PRID_IMP_CAVIUM_CN31XX:
+ case PRID_IMP_CAVIUM_CN30XX:
+@@ -877,7 +875,7 @@ static inline void cpu_probe_ingenic(struct cpuinfo_mips *c, unsigned int cpu)
+ decode_configs(c);
+ /* JZRISC does not implement the CP0 counter. */
+ c->options &= ~MIPS_CPU_COUNTER;
+- switch (c->processor_id & 0xff00) {
++ switch (cpu_prid_imp()) {
+ case PRID_IMP_JZRISC:
+ c->cputype = CPU_JZRISC;
+ __cpu_name[cpu] = "Ingenic JZRISC";
+@@ -980,7 +978,7 @@ __cpuinit void cpu_probe(void)
+ c->cputype = CPU_UNKNOWN;
+
+ c->processor_id = read_c0_prid();
+- switch (c->processor_id & 0xff0000) {
++ switch (cpu_prid_comp()) {
+ case PRID_COMP_LEGACY:
+ cpu_probe_legacy(c, cpu);
+ break;
+@@ -1060,7 +1058,7 @@ __cpuinit void cpu_report(void)
+ struct cpuinfo_mips *c = &current_cpu_data;
+
+ printk(KERN_INFO "CPU revision is: %08x (%s)\n",
+- c->processor_id, cpu_name_string());
++ current_cpu_prid(), cpu_name_string());
+ if (c->options & MIPS_CPU_FPU)
+ printk(KERN_INFO "FPU revision is: %08x\n", c->fpu_id);
+ }
+diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
+index 74f485d..91688d1 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/spram.c b/arch/mips/kernel/spram.c
+index 6af08d8..4be9669 100644
+--- a/arch/mips/kernel/spram.c
++++ b/arch/mips/kernel/spram.c
+@@ -198,10 +198,9 @@ static __cpuinit void probe_spram(char *type,
+ }
+ void __cpuinit spram_config(void)
+ {
+- struct cpuinfo_mips *c = &current_cpu_data;
+ unsigned int config0;
+
+- switch (c->cputype) {
++ switch (current_cpu_type()) {
+ case CPU_24K:
+ case CPU_34K:
+ case CPU_74K:
+diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c
+index 9d686bf..a90822f 100644
+--- a/arch/mips/kernel/time.c
++++ b/arch/mips/kernel/time.c
+@@ -105,7 +105,7 @@ static __init int cpu_has_mfc0_count_bug(void)
+ * The published errata for the R4400 up to 3.0 say the CPU
+ * has the mfc0 from count bug.
+ */
+- if ((current_cpu_data.processor_id & 0xff) <= 0x30)
++ if (cpu_prid_rev() <= 0x30)
+ return 1;
+
+ /*
+@@ -119,6 +119,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();
+
+ if (!mips_clockevent_init() || !cpu_has_mfc0_count_bug())
+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 4c57b3e..703b058 100644
+--- a/arch/mips/loongson/common/Makefile
++++ b/arch/mips/loongson/common/Makefile
+@@ -4,6 +4,7 @@
+
+ obj-y += setup.o init.o cmdline.o env.o time.o reset.o irq.o \
+ pci.o bonito-irq.o mem.o machtype.o platform.o
++
+ obj-$(CONFIG_GPIOLIB) += gpio.o
+
+ #
+@@ -11,7 +12,6 @@ obj-$(CONFIG_GPIOLIB) += gpio.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_acc.c b/arch/mips/loongson/common/cs5536/cs5536_acc.c
+index ab4d6cc..2b0d31e 100644
+--- a/arch/mips/loongson/common/cs5536/cs5536_acc.c
++++ b/arch/mips/loongson/common/cs5536/cs5536_acc.c
+@@ -18,7 +18,7 @@
+
+ void pci_acc_write_reg(int reg, u32 value)
+ {
+- u32 hi = 0, lo = value;
++ u32 hi, lo;
+
+ switch (reg) {
+ case PCI_COMMAND:
+@@ -66,75 +66,73 @@ void pci_acc_write_reg(int reg, u32 value)
+ u32 pci_acc_read_reg(int reg)
+ {
+ u32 hi, lo;
+- u32 conf_data = 0;
++ u32 cfg = 0;
+
+ switch (reg) {
+ case PCI_VENDOR_ID:
+- conf_data =
+- CFG_PCI_VENDOR_ID(CS5536_ACC_DEVICE_ID, CS5536_VENDOR_ID);
++ cfg = CFG_PCI_VENDOR_ID(PCI_DEVICE_ID_AMD_CS5536_AUDIO,
++ PCI_VENDOR_ID_AMD);
+ break;
+ case PCI_COMMAND:
+ _rdmsr(GLIU_MSR_REG(GLIU_IOD_BM1), &hi, &lo);
+ if (((lo & 0xfff00000) || (hi & 0x000000ff))
+ && ((hi & 0xf0000000) == 0xa0000000))
+- conf_data |= PCI_COMMAND_IO;
++ cfg |= PCI_COMMAND_IO;
+ _rdmsr(GLIU_MSR_REG(GLIU_PAE), &hi, &lo);
+ if ((lo & 0x300) == 0x300)
+- conf_data |= PCI_COMMAND_MASTER;
++ cfg |= PCI_COMMAND_MASTER;
+ break;
+ case PCI_STATUS:
+- conf_data |= PCI_STATUS_66MHZ;
+- conf_data |= PCI_STATUS_FAST_BACK;
++ cfg |= PCI_STATUS_66MHZ;
++ cfg |= PCI_STATUS_FAST_BACK;
+ _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
+ if (lo & SB_PARE_ERR_FLAG)
+- conf_data |= PCI_STATUS_PARITY;
+- conf_data |= PCI_STATUS_DEVSEL_MEDIUM;
++ cfg |= PCI_STATUS_PARITY;
++ cfg |= PCI_STATUS_DEVSEL_MEDIUM;
+ break;
+ case PCI_CLASS_REVISION:
+ _rdmsr(ACC_MSR_REG(ACC_CAP), &hi, &lo);
+- conf_data = lo & 0x000000ff;
+- conf_data |= (CS5536_ACC_CLASS_CODE << 8);
++ cfg = lo & 0x000000ff;
++ cfg |= (CS5536_ACC_CLASS_CODE << 8);
+ break;
+ case PCI_CACHE_LINE_SIZE:
+- conf_data =
+- CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE,
+- PCI_NORMAL_LATENCY_TIMER);
++ cfg = CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE,
++ PCI_NORMAL_LATENCY_TIMER);
+ break;
+ case PCI_BAR0_REG:
+ _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
+ if (lo & SOFT_BAR_ACC_FLAG) {
+- conf_data = CS5536_ACC_RANGE |
++ cfg = CS5536_ACC_RANGE |
+ PCI_BASE_ADDRESS_SPACE_IO;
+ lo &= ~SOFT_BAR_ACC_FLAG;
+ _wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
+ } else {
+ _rdmsr(GLIU_MSR_REG(GLIU_IOD_BM1), &hi, &lo);
+- conf_data = (hi & 0x000000ff) << 12;
+- conf_data |= (lo & 0xfff00000) >> 20;
+- conf_data |= 0x01;
+- conf_data &= ~0x02;
++ cfg = (hi & 0x000000ff) << 12;
++ cfg |= (lo & 0xfff00000) >> 20;
++ cfg |= 0x01;
++ cfg &= ~0x02;
+ }
+ break;
+ case PCI_CARDBUS_CIS:
+- conf_data = PCI_CARDBUS_CIS_POINTER;
++ cfg = PCI_CARDBUS_CIS_POINTER;
+ break;
+ case PCI_SUBSYSTEM_VENDOR_ID:
+- conf_data =
+- CFG_PCI_VENDOR_ID(CS5536_ACC_SUB_ID, CS5536_SUB_VENDOR_ID);
++ cfg = CFG_PCI_VENDOR_ID(PCI_DEVICE_ID_AMD_CS5536_AUDIO,
++ PCI_VENDOR_ID_AMD);
+ break;
+ case PCI_ROM_ADDRESS:
+- conf_data = PCI_EXPANSION_ROM_BAR;
++ cfg = PCI_EXPANSION_ROM_BAR;
+ break;
+ case PCI_CAPABILITY_LIST:
+- conf_data = PCI_CAPLIST_USB_POINTER;
++ cfg = PCI_CAPLIST_USB_POINTER;
+ break;
+ case PCI_INTERRUPT_LINE:
+- conf_data =
+- CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_ACC_INTR);
++ cfg = CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_ACC_INTR);
+ break;
+ default:
+ break;
+ }
+
+- return conf_data;
++ return cfg;
+ }
+diff --git a/arch/mips/loongson/common/cs5536/cs5536_ehci.c b/arch/mips/loongson/common/cs5536/cs5536_ehci.c
+index ec2e360..3c2163a 100644
+--- a/arch/mips/loongson/common/cs5536/cs5536_ehci.c
++++ b/arch/mips/loongson/common/cs5536/cs5536_ehci.c
+@@ -18,7 +18,7 @@
+
+ void pci_ehci_write_reg(int reg, u32 value)
+ {
+- u32 hi = 0, lo = value;
++ u32 hi, lo;
+
+ switch (reg) {
+ case PCI_COMMAND:
+@@ -78,83 +78,78 @@ void pci_ehci_write_reg(int reg, u32 value)
+
+ u32 pci_ehci_read_reg(int reg)
+ {
+- u32 conf_data = 0;
++ u32 cfg = 0;
+ u32 hi, lo;
+
+ switch (reg) {
+ case PCI_VENDOR_ID:
+- conf_data =
+- CFG_PCI_VENDOR_ID(CS5536_EHCI_DEVICE_ID, CS5536_VENDOR_ID);
++ case PCI_SUBSYSTEM_VENDOR_ID:
++ cfg = CFG_PCI_VENDOR_ID(PCI_DEVICE_ID_AMD_CS5536_EHC,
++ PCI_VENDOR_ID_AMD);
+ break;
+ case PCI_COMMAND:
+ _rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
+ if (hi & PCI_COMMAND_MASTER)
+- conf_data |= PCI_COMMAND_MASTER;
++ cfg |= PCI_COMMAND_MASTER;
+ if (hi & PCI_COMMAND_MEMORY)
+- conf_data |= PCI_COMMAND_MEMORY;
++ cfg |= PCI_COMMAND_MEMORY;
+ break;
+ case PCI_STATUS:
+- conf_data |= PCI_STATUS_66MHZ;
+- conf_data |= PCI_STATUS_FAST_BACK;
++ cfg |= PCI_STATUS_66MHZ;
++ cfg |= PCI_STATUS_FAST_BACK;
+ _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
+ if (lo & SB_PARE_ERR_FLAG)
+- conf_data |= PCI_STATUS_PARITY;
+- conf_data |= PCI_STATUS_DEVSEL_MEDIUM;
++ cfg |= PCI_STATUS_PARITY;
++ cfg |= PCI_STATUS_DEVSEL_MEDIUM;
+ break;
+ case PCI_CLASS_REVISION:
+ _rdmsr(USB_MSR_REG(USB_CAP), &hi, &lo);
+- conf_data = lo & 0x000000ff;
+- conf_data |= (CS5536_EHCI_CLASS_CODE << 8);
++ cfg = lo & 0x000000ff;
++ cfg |= (CS5536_EHCI_CLASS_CODE << 8);
+ break;
+ case PCI_CACHE_LINE_SIZE:
+- conf_data =
+- CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE,
+- PCI_NORMAL_LATENCY_TIMER);
++ cfg = CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE,
++ PCI_NORMAL_LATENCY_TIMER);
+ break;
+ case PCI_BAR0_REG:
+ _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
+ if (lo & SOFT_BAR_EHCI_FLAG) {
+- conf_data = CS5536_EHCI_RANGE |
++ cfg = CS5536_EHCI_RANGE |
+ PCI_BASE_ADDRESS_SPACE_MEMORY;
+ lo &= ~SOFT_BAR_EHCI_FLAG;
+ _wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
+ } else {
+ _rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
+- conf_data = lo & 0xfffff000;
++ cfg = lo & 0xfffff000;
+ }
+ break;
+ case PCI_CARDBUS_CIS:
+- conf_data = PCI_CARDBUS_CIS_POINTER;
+- break;
+- case PCI_SUBSYSTEM_VENDOR_ID:
+- conf_data =
+- CFG_PCI_VENDOR_ID(CS5536_EHCI_SUB_ID, CS5536_SUB_VENDOR_ID);
++ cfg = PCI_CARDBUS_CIS_POINTER;
+ break;
+ case PCI_ROM_ADDRESS:
+- conf_data = PCI_EXPANSION_ROM_BAR;
++ cfg = PCI_EXPANSION_ROM_BAR;
+ break;
+ case PCI_CAPABILITY_LIST:
+- conf_data = PCI_CAPLIST_USB_POINTER;
++ cfg = PCI_CAPLIST_USB_POINTER;
+ break;
+ case PCI_INTERRUPT_LINE:
+- conf_data =
+- CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_USB_INTR);
++ cfg = CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_USB_INTR);
+ break;
+ case PCI_EHCI_LEGSMIEN_REG:
+ _rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
+- conf_data = (hi & 0x003f0000) >> 16;
++ cfg = (hi & 0x003f0000) >> 16;
+ break;
+ case PCI_EHCI_LEGSMISTS_REG:
+ _rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
+- conf_data = (hi & 0x3f000000) >> 24;
++ cfg = (hi & 0x3f000000) >> 24;
+ break;
+ case PCI_EHCI_FLADJ_REG:
+ _rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
+- conf_data = hi & 0x00003f00;
++ cfg = hi & 0x00003f00;
+ break;
+ default:
+ break;
+ }
+
+- return conf_data;
++ return cfg;
+ }
+diff --git a/arch/mips/loongson/common/cs5536/cs5536_ide.c b/arch/mips/loongson/common/cs5536/cs5536_ide.c
+index a73414d..4699e15 100644
+--- a/arch/mips/loongson/common/cs5536/cs5536_ide.c
++++ b/arch/mips/loongson/common/cs5536/cs5536_ide.c
+@@ -18,7 +18,7 @@
+
+ void pci_ide_write_reg(int reg, u32 value)
+ {
+- u32 hi = 0, lo = value;
++ u32 hi, lo;
+
+ switch (reg) {
+ case PCI_COMMAND:
+@@ -72,26 +72,16 @@ void pci_ide_write_reg(int reg, u32 value)
+ _wrmsr(IDE_MSR_REG(IDE_CFG), hi, lo);
+ }
+ break;
+- case PCI_IDE_DTC_REG:
+- _rdmsr(IDE_MSR_REG(IDE_DTC), &hi, &lo);
+- lo = value;
+- _wrmsr(IDE_MSR_REG(IDE_DTC), hi, lo);
+- break;
+- case PCI_IDE_CAST_REG:
+- _rdmsr(IDE_MSR_REG(IDE_CAST), &hi, &lo);
+- lo = value;
+- _wrmsr(IDE_MSR_REG(IDE_CAST), hi, lo);
+- break;
+- case PCI_IDE_ETC_REG:
+- _rdmsr(IDE_MSR_REG(IDE_ETC), &hi, &lo);
+- lo = value;
+- _wrmsr(IDE_MSR_REG(IDE_ETC), hi, lo);
+- break;
+- case PCI_IDE_PM_REG:
+- _rdmsr(IDE_MSR_REG(IDE_INTERNAL_PM), &hi, &lo);
+- lo = value;
+- _wrmsr(IDE_MSR_REG(IDE_INTERNAL_PM), hi, lo);
++#define SET_PCI_IDE_REG(r) \
++ case PCI_IDE_##r##_REG: \
++ _rdmsr(IDE_MSR_REG(IDE_##r), &hi, &lo); \
++ lo = value; \
++ _wrmsr(IDE_MSR_REG(IDE_##r), hi, lo); \
+ break;
++ SET_PCI_IDE_REG(DTC)
++ SET_PCI_IDE_REG(CAST)
++ SET_PCI_IDE_REG(ETC)
++ SET_PCI_IDE_REG(PM)
+ default:
+ break;
+ }
+@@ -99,94 +89,79 @@ void pci_ide_write_reg(int reg, u32 value)
+
+ u32 pci_ide_read_reg(int reg)
+ {
+- u32 conf_data = 0;
++ u32 cfg = 0;
+ u32 hi, lo;
+
+ switch (reg) {
+ case PCI_VENDOR_ID:
+- conf_data =
+- CFG_PCI_VENDOR_ID(CS5536_IDE_DEVICE_ID, CS5536_VENDOR_ID);
++ case PCI_SUBSYSTEM_VENDOR_ID:
++ cfg = CFG_PCI_VENDOR_ID(PCI_DEVICE_ID_AMD_CS5536_IDE,
++ PCI_VENDOR_ID_AMD);
+ break;
+ case PCI_COMMAND:
+ _rdmsr(IDE_MSR_REG(IDE_IO_BAR), &hi, &lo);
+ if (lo & 0xfffffff0)
+- conf_data |= PCI_COMMAND_IO;
++ cfg |= PCI_COMMAND_IO;
+ _rdmsr(GLIU_MSR_REG(GLIU_PAE), &hi, &lo);
+ if ((lo & 0x30) == 0x30)
+- conf_data |= PCI_COMMAND_MASTER;
++ cfg |= PCI_COMMAND_MASTER;
+ break;
+ case PCI_STATUS:
+- conf_data |= PCI_STATUS_66MHZ;
+- conf_data |= PCI_STATUS_FAST_BACK;
++ cfg |= PCI_STATUS_66MHZ;
++ cfg |= PCI_STATUS_FAST_BACK;
+ _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
+ if (lo & SB_PARE_ERR_FLAG)
+- conf_data |= PCI_STATUS_PARITY;
+- conf_data |= PCI_STATUS_DEVSEL_MEDIUM;
++ cfg |= PCI_STATUS_PARITY;
++ cfg |= PCI_STATUS_DEVSEL_MEDIUM;
+ break;
+ case PCI_CLASS_REVISION:
+ _rdmsr(IDE_MSR_REG(IDE_CAP), &hi, &lo);
+- conf_data = lo & 0x000000ff;
+- conf_data |= (CS5536_IDE_CLASS_CODE << 8);
++ cfg = lo & 0x000000ff;
++ cfg |= (CS5536_IDE_CLASS_CODE << 8);
+ break;
+ case PCI_CACHE_LINE_SIZE:
+ _rdmsr(SB_MSR_REG(SB_CTRL), &hi, &lo);
+ hi &= 0x000000f8;
+- conf_data = CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE, hi);
++ cfg = CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE, hi);
+ break;
+ case PCI_BAR4_REG:
+ _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
+ if (lo & SOFT_BAR_IDE_FLAG) {
+- conf_data = CS5536_IDE_RANGE |
++ cfg = CS5536_IDE_RANGE |
+ PCI_BASE_ADDRESS_SPACE_IO;
+ lo &= ~SOFT_BAR_IDE_FLAG;
+ _wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
+ } else {
+ _rdmsr(IDE_MSR_REG(IDE_IO_BAR), &hi, &lo);
+- conf_data = lo & 0xfffffff0;
+- conf_data |= 0x01;
+- conf_data &= ~0x02;
++ cfg = lo & 0xfffffff0;
++ cfg |= 0x01;
++ cfg &= ~0x02;
+ }
+ break;
+ case PCI_CARDBUS_CIS:
+- conf_data = PCI_CARDBUS_CIS_POINTER;
+- break;
+- case PCI_SUBSYSTEM_VENDOR_ID:
+- conf_data =
+- CFG_PCI_VENDOR_ID(CS5536_IDE_SUB_ID, CS5536_SUB_VENDOR_ID);
++ cfg = PCI_CARDBUS_CIS_POINTER;
+ break;
+ case PCI_ROM_ADDRESS:
+- conf_data = PCI_EXPANSION_ROM_BAR;
++ cfg = PCI_EXPANSION_ROM_BAR;
+ break;
+ case PCI_CAPABILITY_LIST:
+- conf_data = PCI_CAPLIST_POINTER;
++ cfg = PCI_CAPLIST_POINTER;
+ break;
+ case PCI_INTERRUPT_LINE:
+- conf_data =
+- CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_IDE_INTR);
+- break;
+- case PCI_IDE_CFG_REG:
+- _rdmsr(IDE_MSR_REG(IDE_CFG), &hi, &lo);
+- conf_data = lo;
+- break;
+- case PCI_IDE_DTC_REG:
+- _rdmsr(IDE_MSR_REG(IDE_DTC), &hi, &lo);
+- conf_data = lo;
+- break;
+- case PCI_IDE_CAST_REG:
+- _rdmsr(IDE_MSR_REG(IDE_CAST), &hi, &lo);
+- conf_data = lo;
+- break;
+- case PCI_IDE_ETC_REG:
+- _rdmsr(IDE_MSR_REG(IDE_ETC), &hi, &lo);
+- conf_data = lo;
++ cfg = CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_IDE_INTR);
+ break;
+- case PCI_IDE_PM_REG:
+- _rdmsr(IDE_MSR_REG(IDE_INTERNAL_PM), &hi, &lo);
+- conf_data = lo;
++#define GET_PCI_IDE_REG(r) \
++ case PCI_IDE_##r##_REG: \
++ _rdmsr(IDE_MSR_REG(IDE_##r), &hi, &cfg); \
+ break;
++ GET_PCI_IDE_REG(CFG)
++ GET_PCI_IDE_REG(DTC)
++ GET_PCI_IDE_REG(CAST)
++ GET_PCI_IDE_REG(ETC)
++ GET_PCI_IDE_REG(PM)
+ default:
+ break;
+ }
+
+- return conf_data;
++ return cfg;
+ }
+diff --git a/arch/mips/loongson/common/cs5536/cs5536_isa.c b/arch/mips/loongson/common/cs5536/cs5536_isa.c
+index a6eb2e8..f74aeb3 100644
+--- a/arch/mips/loongson/common/cs5536/cs5536_isa.c
++++ b/arch/mips/loongson/common/cs5536/cs5536_isa.c
+@@ -86,7 +86,7 @@ static void divil_lbar_disable(void)
+
+ void pci_isa_write_bar(int n, u32 value)
+ {
+- u32 hi = 0, lo = value;
++ u32 hi, lo;
+
+ if (value == PCI_BAR_RANGE_MASK) {
+ _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
+@@ -95,7 +95,7 @@ void pci_isa_write_bar(int n, u32 value)
+ } else if (value & 0x01) {
+ /* NATIVE reg */
+ hi = 0x0000f001;
+- lo &= bar_space_range[n];
++ lo = value & bar_space_range[n];
+ _wrmsr(divil_msr_reg[n], hi, lo);
+
+ /* RCONFx is 4bytes in units for I/O space */
+@@ -112,21 +112,21 @@ void pci_isa_write_bar(int n, u32 value)
+
+ u32 pci_isa_read_bar(int n)
+ {
+- u32 conf_data = 0;
++ u32 cfg = 0;
+ u32 hi, lo;
+
+ _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
+ if (lo & soft_bar_flag[n]) {
+- conf_data = bar_space_range[n] | PCI_BASE_ADDRESS_SPACE_IO;
++ cfg = bar_space_range[n] | PCI_BASE_ADDRESS_SPACE_IO;
+ lo &= ~soft_bar_flag[n];
+ _wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
+ } else {
+ _rdmsr(divil_msr_reg[n], &hi, &lo);
+- conf_data = lo & bar_space_range[n];
+- conf_data |= 0x01;
+- conf_data &= ~0x02;
++ cfg = lo & bar_space_range[n];
++ cfg |= 0x01;
++ cfg &= ~0x02;
+ }
+- return conf_data;
++ return cfg;
+ }
+
+ /*
+@@ -136,7 +136,7 @@ u32 pci_isa_read_bar(int n)
+ */
+ void pci_isa_write_reg(int reg, u32 value)
+ {
+- u32 hi = 0, lo = value;
++ u32 hi, lo;
+ u32 temp;
+
+ switch (reg) {
+@@ -230,45 +230,46 @@ void pci_isa_write_reg(int reg, u32 value)
+ */
+ u32 pci_isa_read_reg(int reg)
+ {
+- u32 conf_data = 0;
++ u32 cfg = 0;
+ u32 hi, lo;
+
+ switch (reg) {
+ case PCI_VENDOR_ID:
+- conf_data =
+- CFG_PCI_VENDOR_ID(CS5536_ISA_DEVICE_ID, CS5536_VENDOR_ID);
++ case PCI_SUBSYSTEM_VENDOR_ID:
++ cfg = CFG_PCI_VENDOR_ID(PCI_DEVICE_ID_AMD_CS5536_ISA,
++ PCI_VENDOR_ID_AMD);
+ break;
+ case PCI_COMMAND:
+ /* we just check the first LBAR for the IO enable bit, */
+ /* maybe we should changed later. */
+ _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_SMB), &hi, &lo);
+ if (hi & 0x01)
+- conf_data |= PCI_COMMAND_IO;
++ cfg |= PCI_COMMAND_IO;
+ break;
+ case PCI_STATUS:
+- conf_data |= PCI_STATUS_66MHZ;
+- conf_data |= PCI_STATUS_DEVSEL_MEDIUM;
+- conf_data |= PCI_STATUS_FAST_BACK;
++ cfg |= PCI_STATUS_66MHZ;
++ cfg |= PCI_STATUS_DEVSEL_MEDIUM;
++ cfg |= PCI_STATUS_FAST_BACK;
+
+ _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
+ if (lo & SB_TAS_ERR_FLAG)
+- conf_data |= PCI_STATUS_SIG_TARGET_ABORT;
++ cfg |= PCI_STATUS_SIG_TARGET_ABORT;
+ if (lo & SB_TAR_ERR_FLAG)
+- conf_data |= PCI_STATUS_REC_TARGET_ABORT;
++ cfg |= PCI_STATUS_REC_TARGET_ABORT;
+ if (lo & SB_MAR_ERR_FLAG)
+- conf_data |= PCI_STATUS_REC_MASTER_ABORT;
++ cfg |= PCI_STATUS_REC_MASTER_ABORT;
+ if (lo & SB_PARE_ERR_FLAG)
+- conf_data |= PCI_STATUS_DETECTED_PARITY;
++ cfg |= PCI_STATUS_DETECTED_PARITY;
+ break;
+ case PCI_CLASS_REVISION:
+ _rdmsr(GLCP_MSR_REG(GLCP_CHIP_REV_ID), &hi, &lo);
+- conf_data = lo & 0x000000ff;
+- conf_data |= (CS5536_ISA_CLASS_CODE << 8);
++ cfg = lo & 0x000000ff;
++ cfg |= (CS5536_ISA_CLASS_CODE << 8);
+ break;
+ case PCI_CACHE_LINE_SIZE:
+ _rdmsr(SB_MSR_REG(SB_CTRL), &hi, &lo);
+ hi &= 0x000000f8;
+- conf_data = CFG_PCI_CACHE_LINE_SIZE(PCI_BRIDGE_HEADER_TYPE, hi);
++ cfg = CFG_PCI_CACHE_LINE_SIZE(PCI_BRIDGE_HEADER_TYPE, hi);
+ break;
+ /*
+ * we only use the LBAR of DIVIL, no RCONF used.
+@@ -292,25 +293,21 @@ u32 pci_isa_read_reg(int reg)
+ return pci_isa_read_bar(5);
+ break;
+ case PCI_CARDBUS_CIS:
+- conf_data = PCI_CARDBUS_CIS_POINTER;
+- break;
+- case PCI_SUBSYSTEM_VENDOR_ID:
+- conf_data =
+- CFG_PCI_VENDOR_ID(CS5536_ISA_SUB_ID, CS5536_SUB_VENDOR_ID);
++ cfg = PCI_CARDBUS_CIS_POINTER;
+ break;
+ case PCI_ROM_ADDRESS:
+- conf_data = PCI_EXPANSION_ROM_BAR;
++ cfg = PCI_EXPANSION_ROM_BAR;
+ break;
+ case PCI_CAPABILITY_LIST:
+- conf_data = PCI_CAPLIST_POINTER;
++ cfg = PCI_CAPLIST_POINTER;
+ break;
+ case PCI_INTERRUPT_LINE:
+ /* no interrupt used here */
+- conf_data = CFG_PCI_INTERRUPT_LINE(0x00, 0x00);
++ cfg = CFG_PCI_INTERRUPT_LINE(0x00, 0x00);
+ break;
+ default:
+ break;
+ }
+
+- return conf_data;
++ return cfg;
+ }
+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/cs5536/cs5536_ohci.c b/arch/mips/loongson/common/cs5536/cs5536_ohci.c
+index f7c905e..a181193 100644
+--- a/arch/mips/loongson/common/cs5536/cs5536_ohci.c
++++ b/arch/mips/loongson/common/cs5536/cs5536_ohci.c
+@@ -18,7 +18,7 @@
+
+ void pci_ohci_write_reg(int reg, u32 value)
+ {
+- u32 hi = 0, lo = value;
++ u32 hi, lo;
+
+ switch (reg) {
+ case PCI_COMMAND:
+@@ -73,77 +73,72 @@ void pci_ohci_write_reg(int reg, u32 value)
+
+ u32 pci_ohci_read_reg(int reg)
+ {
+- u32 conf_data = 0;
++ u32 cfg = 0;
+ u32 hi, lo;
+
+ switch (reg) {
+ case PCI_VENDOR_ID:
+- conf_data =
+- CFG_PCI_VENDOR_ID(CS5536_OHCI_DEVICE_ID, CS5536_VENDOR_ID);
++ case PCI_SUBSYSTEM_VENDOR_ID:
++ cfg = CFG_PCI_VENDOR_ID(PCI_DEVICE_ID_AMD_CS5536_OHC,
++ PCI_VENDOR_ID_AMD);
+ break;
+ case PCI_COMMAND:
+ _rdmsr(USB_MSR_REG(USB_OHCI), &hi, &lo);
+ if (hi & PCI_COMMAND_MASTER)
+- conf_data |= PCI_COMMAND_MASTER;
++ cfg |= PCI_COMMAND_MASTER;
+ if (hi & PCI_COMMAND_MEMORY)
+- conf_data |= PCI_COMMAND_MEMORY;
++ cfg |= PCI_COMMAND_MEMORY;
+ break;
+ case PCI_STATUS:
+- conf_data |= PCI_STATUS_66MHZ;
+- conf_data |= PCI_STATUS_FAST_BACK;
++ cfg |= PCI_STATUS_66MHZ;
++ cfg |= PCI_STATUS_FAST_BACK;
+ _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
+ if (lo & SB_PARE_ERR_FLAG)
+- conf_data |= PCI_STATUS_PARITY;
+- conf_data |= PCI_STATUS_DEVSEL_MEDIUM;
++ cfg |= PCI_STATUS_PARITY;
++ cfg |= PCI_STATUS_DEVSEL_MEDIUM;
+ break;
+ case PCI_CLASS_REVISION:
+ _rdmsr(USB_MSR_REG(USB_CAP), &hi, &lo);
+- conf_data = lo & 0x000000ff;
+- conf_data |= (CS5536_OHCI_CLASS_CODE << 8);
++ cfg = lo & 0x000000ff;
++ cfg |= (CS5536_OHCI_CLASS_CODE << 8);
+ break;
+ case PCI_CACHE_LINE_SIZE:
+- conf_data =
+- CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE,
+- PCI_NORMAL_LATENCY_TIMER);
++ cfg = CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE,
++ PCI_NORMAL_LATENCY_TIMER);
+ break;
+ case PCI_BAR0_REG:
+ _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
+ if (lo & SOFT_BAR_OHCI_FLAG) {
+- conf_data = CS5536_OHCI_RANGE |
++ cfg = CS5536_OHCI_RANGE |
+ PCI_BASE_ADDRESS_SPACE_MEMORY;
+ lo &= ~SOFT_BAR_OHCI_FLAG;
+ _wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
+ } else {
+ _rdmsr(USB_MSR_REG(USB_OHCI), &hi, &lo);
+- conf_data = lo & 0xffffff00;
+- conf_data &= ~0x0000000f; /* 32bit mem */
++ cfg = lo & 0xffffff00;
++ cfg &= ~0x0000000f; /* 32bit mem */
+ }
+ break;
+ case PCI_CARDBUS_CIS:
+- conf_data = PCI_CARDBUS_CIS_POINTER;
+- break;
+- case PCI_SUBSYSTEM_VENDOR_ID:
+- conf_data =
+- CFG_PCI_VENDOR_ID(CS5536_OHCI_SUB_ID, CS5536_SUB_VENDOR_ID);
++ cfg = PCI_CARDBUS_CIS_POINTER;
+ break;
+ case PCI_ROM_ADDRESS:
+- conf_data = PCI_EXPANSION_ROM_BAR;
++ cfg = PCI_EXPANSION_ROM_BAR;
+ break;
+ case PCI_CAPABILITY_LIST:
+- conf_data = PCI_CAPLIST_USB_POINTER;
++ cfg = PCI_CAPLIST_USB_POINTER;
+ break;
+ case PCI_INTERRUPT_LINE:
+- conf_data =
+- CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_USB_INTR);
++ cfg = CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_USB_INTR);
+ break;
+ case PCI_OHCI_INT_REG:
+ _rdmsr(DIVIL_MSR_REG(PIC_YSEL_LOW), &hi, &lo);
+ if ((lo & 0x00000f00) == CS5536_USB_INTR)
+- conf_data = 1;
++ cfg = 1;
+ break;
+ default:
+ break;
+ }
+
+- return conf_data;
++ return cfg;
+ }
+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/env.c b/arch/mips/loongson/common/env.c
+index 0a18fcf..0e69c16 100644
+--- a/arch/mips/loongson/common/env.c
++++ b/arch/mips/loongson/common/env.c
+@@ -40,7 +40,6 @@ void __init prom_init_env(void)
+ /* pmon passes arguments in 32bit pointers */
+ int *_prom_envp;
+ unsigned long bus_clock;
+- unsigned int processor_id;
+ long l;
+
+ /* firmware arguments are initialized in head.S */
+@@ -60,8 +59,7 @@ void __init prom_init_env(void)
+ if (bus_clock == 0)
+ bus_clock = 66000000;
+ if (cpu_clock_freq == 0) {
+- processor_id = (&current_cpu_data)->processor_id;
+- switch (processor_id & PRID_REV_MASK) {
++ switch (cpu_prid_rev()) {
+ case PRID_REV_LOONGSON2E:
+ cpu_clock_freq = 533080000;
+ break;
+diff --git a/arch/mips/loongson/common/gpio.c b/arch/mips/loongson/common/gpio.c
+index 2186990..8a1f77c 100644
+--- a/arch/mips/loongson/common/gpio.c
++++ b/arch/mips/loongson/common/gpio.c
+@@ -19,7 +19,6 @@
+ #include <loongson.h>
+ #include <linux/gpio.h>
+
+-#define STLS2F_N_GPIO 4
+ #define STLS2F_GPIO_IN_OFFSET 16
+
+ static DEFINE_SPINLOCK(gpio_lock);
+@@ -29,7 +28,7 @@ int gpio_get_value(unsigned gpio)
+ u32 val;
+ u32 mask;
+
+- if (gpio >= STLS2F_N_GPIO)
++ if (gpio >= ARCH_NR_GPIOS)
+ return __gpio_get_value(gpio);
+
+ mask = 1 << (gpio + STLS2F_GPIO_IN_OFFSET);
+@@ -46,7 +45,7 @@ void gpio_set_value(unsigned gpio, int state)
+ u32 val;
+ u32 mask;
+
+- if (gpio >= STLS2F_N_GPIO) {
++ if (gpio >= ARCH_NR_GPIOS) {
+ __gpio_set_value(gpio, state);
+ return ;
+ }
+@@ -66,7 +65,7 @@ EXPORT_SYMBOL(gpio_set_value);
+
+ int gpio_cansleep(unsigned gpio)
+ {
+- if (gpio < STLS2F_N_GPIO)
++ if (gpio < ARCH_NR_GPIOS)
+ return 0;
+ else
+ return __gpio_cansleep(gpio);
+@@ -78,7 +77,7 @@ static int ls2f_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+ u32 temp;
+ u32 mask;
+
+- if (gpio >= STLS2F_N_GPIO)
++ if (gpio >= ARCH_NR_GPIOS)
+ return -EINVAL;
+
+ spin_lock(&gpio_lock);
+@@ -97,7 +96,7 @@ static int ls2f_gpio_direction_output(struct gpio_chip *chip,
+ u32 temp;
+ u32 mask;
+
+- if (gpio >= STLS2F_N_GPIO)
++ if (gpio >= ARCH_NR_GPIOS)
+ return -EINVAL;
+
+ gpio_set_value(gpio, level);
+@@ -129,7 +128,7 @@ static struct gpio_chip ls2f_chip = {
+ .direction_output = ls2f_gpio_direction_output,
+ .set = ls2f_gpio_set_value,
+ .base = 0,
+- .ngpio = STLS2F_N_GPIO,
++ .ngpio = ARCH_NR_GPIOS,
+ };
+
+ static int __init ls2f_gpio_setup(void)
+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/platform.c b/arch/mips/loongson/common/platform.c
+index 0ed3832..4fed18c 100644
+--- a/arch/mips/loongson/common/platform.c
++++ b/arch/mips/loongson/common/platform.c
+@@ -13,16 +13,14 @@
+ #include <linux/platform_device.h>
+
+ static struct platform_device loongson2_cpufreq_device = {
+- .name = "loongson2_cpufreq",
++ .name = "l2_cpufreq",
+ .id = -1,
+ };
+
+ static int __init loongson2_cpufreq_init(void)
+ {
+- struct cpuinfo_mips *c = &current_cpu_data;
+-
+ /* Only 2F revision and it's successors support CPUFreq */
+- if ((c->processor_id & PRID_REV_MASK) >= PRID_REV_LOONGSON2F)
++ if (cpu_prid_rev() >= PRID_REV_LOONGSON2F)
+ return platform_device_register(&loongson2_cpufreq_device);
+
+ return -ENODEV;
+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/ec_kb3310b.c b/arch/mips/loongson/lemote-2f/ec_kb3310b.c
+index 2b666d3..88ef558 100644
+--- a/arch/mips/loongson/lemote-2f/ec_kb3310b.c
++++ b/arch/mips/loongson/lemote-2f/ec_kb3310b.c
+@@ -14,7 +14,7 @@
+ #include <linux/spinlock.h>
+ #include <linux/delay.h>
+
+-#include "ec_kb3310b.h"
++#include <ec_kb3310b.h>
+
+ static DEFINE_SPINLOCK(index_access_lock);
+ static DEFINE_SPINLOCK(port_access_lock);
+@@ -76,12 +76,9 @@ int ec_query_seq(unsigned char cmd)
+ spin_unlock_irqrestore(&port_access_lock, flags);
+
+ if (timeout <= 0) {
+- printk(KERN_ERR "%s: deadable error : timeout...\n", __func__);
++ pr_err("%s: deadable error : timeout...\n", __func__);
+ ret = -EINVAL;
+- } else
+- printk(KERN_INFO
+- "(%x/%d)ec issued command %d status : 0x%x\n",
+- timeout, EC_CMD_TIMEOUT - timeout, cmd, status);
++ }
+
+ return ret;
+ }
+@@ -116,8 +113,7 @@ int ec_get_event_num(void)
+ udelay(EC_REG_DELAY);
+ }
+ if (timeout <= 0) {
+- pr_info("%s: get event number timeout.\n", __func__);
+-
++ pr_err("%s: get event number timeout.\n", __func__);
+ return -EINVAL;
+ }
+ value = inb(EC_DAT_PORT);
+diff --git a/arch/mips/loongson/lemote-2f/ec_kb3310b.h b/arch/mips/loongson/lemote-2f/ec_kb3310b.h
+deleted file mode 100644
+index 5a3f186..0000000
+--- a/arch/mips/loongson/lemote-2f/ec_kb3310b.h
++++ /dev/null
+@@ -1,188 +0,0 @@
+-/*
+- * KB3310B Embedded Controller
+- *
+- * Copyright (C) 2008 Lemote Inc.
+- * Author: liujl <liujl@lemote.com>, 2008-03-14
+- *
+- * 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 _EC_KB3310B_H
+-#define _EC_KB3310B_H
+-
+-extern unsigned char ec_read(unsigned short addr);
+-extern void ec_write(unsigned short addr, unsigned char val);
+-extern int ec_query_seq(unsigned char cmd);
+-extern int ec_query_event_num(void);
+-extern int ec_get_event_num(void);
+-
+-typedef int (*sci_handler) (int status);
+-extern sci_handler yeeloong_report_lid_status;
+-
+-#define SCI_IRQ_NUM 0x0A
+-
+-/*
+- * The following registers are determined by the EC index configuration.
+- * 1, fill the PORT_HIGH as EC register high part.
+- * 2, fill the PORT_LOW as EC register low part.
+- * 3, fill the PORT_DATA as EC register write data or get the data from it.
+- */
+-#define EC_IO_PORT_HIGH 0x0381
+-#define EC_IO_PORT_LOW 0x0382
+-#define EC_IO_PORT_DATA 0x0383
+-
+-/*
+- * EC delay time is 500us for register and status access
+- */
+-#define EC_REG_DELAY 500 /* unit : us */
+-#define EC_CMD_TIMEOUT 0x1000
+-
+-/*
+- * EC access port for SCI communication
+- */
+-#define EC_CMD_PORT 0x66
+-#define EC_STS_PORT 0x66
+-#define EC_DAT_PORT 0x62
+-#define CMD_INIT_IDLE_MODE 0xdd
+-#define CMD_EXIT_IDLE_MODE 0xdf
+-#define CMD_INIT_RESET_MODE 0xd8
+-#define CMD_REBOOT_SYSTEM 0x8c
+-#define CMD_GET_EVENT_NUM 0x84
+-#define CMD_PROGRAM_PIECE 0xda
+-
+-/* temperature & fan registers */
+-#define REG_TEMPERATURE_VALUE 0xF458
+-#define REG_FAN_AUTO_MAN_SWITCH 0xF459
+-#define BIT_FAN_AUTO 0
+-#define BIT_FAN_MANUAL 1
+-#define REG_FAN_CONTROL 0xF4D2
+-#define BIT_FAN_CONTROL_ON (1 << 0)
+-#define BIT_FAN_CONTROL_OFF (0 << 0)
+-#define REG_FAN_STATUS 0xF4DA
+-#define BIT_FAN_STATUS_ON (1 << 0)
+-#define BIT_FAN_STATUS_OFF (0 << 0)
+-#define REG_FAN_SPEED_HIGH 0xFE22
+-#define REG_FAN_SPEED_LOW 0xFE23
+-#define REG_FAN_SPEED_LEVEL 0xF4CC
+-/* fan speed divider */
+-#define FAN_SPEED_DIVIDER 480000 /* (60*1000*1000/62.5/2)*/
+-
+-/* battery registers */
+-#define REG_BAT_DESIGN_CAP_HIGH 0xF77D
+-#define REG_BAT_DESIGN_CAP_LOW 0xF77E
+-#define REG_BAT_FULLCHG_CAP_HIGH 0xF780
+-#define REG_BAT_FULLCHG_CAP_LOW 0xF781
+-#define REG_BAT_DESIGN_VOL_HIGH 0xF782
+-#define REG_BAT_DESIGN_VOL_LOW 0xF783
+-#define REG_BAT_CURRENT_HIGH 0xF784
+-#define REG_BAT_CURRENT_LOW 0xF785
+-#define REG_BAT_VOLTAGE_HIGH 0xF786
+-#define REG_BAT_VOLTAGE_LOW 0xF787
+-#define REG_BAT_TEMPERATURE_HIGH 0xF788
+-#define REG_BAT_TEMPERATURE_LOW 0xF789
+-#define REG_BAT_RELATIVE_CAP_HIGH 0xF492
+-#define REG_BAT_RELATIVE_CAP_LOW 0xF493
+-#define REG_BAT_VENDOR 0xF4C4
+-#define FLAG_BAT_VENDOR_SANYO 0x01
+-#define FLAG_BAT_VENDOR_SIMPLO 0x02
+-#define REG_BAT_CELL_COUNT 0xF4C6
+-#define FLAG_BAT_CELL_3S1P 0x03
+-#define FLAG_BAT_CELL_3S2P 0x06
+-#define REG_BAT_CHARGE 0xF4A2
+-#define FLAG_BAT_CHARGE_DISCHARGE 0x01
+-#define FLAG_BAT_CHARGE_CHARGE 0x02
+-#define FLAG_BAT_CHARGE_ACPOWER 0x00
+-#define REG_BAT_STATUS 0xF4B0
+-#define BIT_BAT_STATUS_LOW (1 << 5)
+-#define BIT_BAT_STATUS_DESTROY (1 << 2)
+-#define BIT_BAT_STATUS_FULL (1 << 1)
+-#define BIT_BAT_STATUS_IN (1 << 0)
+-#define REG_BAT_CHARGE_STATUS 0xF4B1
+-#define BIT_BAT_CHARGE_STATUS_OVERTEMP (1 << 2)
+-#define BIT_BAT_CHARGE_STATUS_PRECHG (1 << 1)
+-#define REG_BAT_STATE 0xF482
+-#define BIT_BAT_STATE_CHARGING (1 << 1)
+-#define BIT_BAT_STATE_DISCHARGING (1 << 0)
+-#define REG_BAT_POWER 0xF440
+-#define BIT_BAT_POWER_S3 (1 << 2)
+-#define BIT_BAT_POWER_ON (1 << 1)
+-#define BIT_BAT_POWER_ACIN (1 << 0)
+-
+-/* other registers */
+-/* Audio: rd/wr */
+-#define REG_AUDIO_VOLUME 0xF46C
+-#define REG_AUDIO_MUTE 0xF4E7
+-#define REG_AUDIO_BEEP 0xF4D0
+-/* USB port power or not: rd/wr */
+-#define REG_USB0_FLAG 0xF461
+-#define REG_USB1_FLAG 0xF462
+-#define REG_USB2_FLAG 0xF463
+-#define BIT_USB_FLAG_ON 1
+-#define BIT_USB_FLAG_OFF 0
+-/* LID */
+-#define REG_LID_DETECT 0xF4BD
+-#define BIT_LID_DETECT_ON 1
+-#define BIT_LID_DETECT_OFF 0
+-/* CRT */
+-#define REG_CRT_DETECT 0xF4AD
+-#define BIT_CRT_DETECT_PLUG 1
+-#define BIT_CRT_DETECT_UNPLUG 0
+-/* LCD backlight brightness adjust: 9 levels */
+-#define REG_DISPLAY_BRIGHTNESS 0xF4F5
+-/* Black screen Status */
+-#define BIT_DISPLAY_LCD_ON 1
+-#define BIT_DISPLAY_LCD_OFF 0
+-/* LCD backlight control: off/restore */
+-#define REG_BACKLIGHT_CTRL 0xF7BD
+-#define BIT_BACKLIGHT_ON 1
+-#define BIT_BACKLIGHT_OFF 0
+-/* Reset the machine auto-clear: rd/wr */
+-#define REG_RESET 0xF4EC
+-#define BIT_RESET_ON 1
+-/* Light the led: rd/wr */
+-#define REG_LED 0xF4C8
+-#define BIT_LED_RED_POWER (1 << 0)
+-#define BIT_LED_ORANGE_POWER (1 << 1)
+-#define BIT_LED_GREEN_CHARGE (1 << 2)
+-#define BIT_LED_RED_CHARGE (1 << 3)
+-#define BIT_LED_NUMLOCK (1 << 4)
+-/* Test led mode, all led on/off */
+-#define REG_LED_TEST 0xF4C2
+-#define BIT_LED_TEST_IN 1
+-#define BIT_LED_TEST_OUT 0
+-/* Camera on/off */
+-#define REG_CAMERA_STATUS 0xF46A
+-#define BIT_CAMERA_STATUS_ON 1
+-#define BIT_CAMERA_STATUS_OFF 0
+-#define REG_CAMERA_CONTROL 0xF7B7
+-#define BIT_CAMERA_CONTROL_OFF 0
+-#define BIT_CAMERA_CONTROL_ON 1
+-/* Wlan Status */
+-#define REG_WLAN 0xF4FA
+-#define BIT_WLAN_ON 1
+-#define BIT_WLAN_OFF 0
+-#define REG_DISPLAY_LCD 0xF79F
+-
+-/* SCI Event Number from EC */
+-enum {
+- EVENT_LID = 0x23, /* LID open/close */
+- EVENT_DISPLAY_TOGGLE, /* Fn+F3 for display switch */
+- EVENT_SLEEP, /* Fn+F1 for entering sleep mode */
+- EVENT_OVERTEMP, /* Over-temperature happened */
+- EVENT_CRT_DETECT, /* CRT is connected */
+- EVENT_CAMERA, /* Camera on/off */
+- EVENT_USB_OC2, /* USB2 Over Current occurred */
+- EVENT_USB_OC0, /* USB0 Over Current occurred */
+- EVENT_BLACK_SCREEN, /* Turn on/off backlight */
+- EVENT_AUDIO_MUTE, /* Mute on/off */
+- EVENT_DISPLAY_BRIGHTNESS,/* LCD backlight brightness adjust */
+- EVENT_AC_BAT, /* AC & Battery relative issue */
+- EVENT_AUDIO_VOLUME, /* Volume adjust */
+- EVENT_WLAN, /* Wlan on/off */
+- EVENT_END
+-};
+-
+-#endif /* !_EC_KB3310B_H */
+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..b82ab17 100644
+--- a/arch/mips/loongson/lemote-2f/pm.c
++++ b/arch/mips/loongson/lemote-2f/pm.c
+@@ -23,7 +23,7 @@
+ #include <loongson.h>
+
+ #include <cs5536/cs5536_mfgpt.h>
+-#include "ec_kb3310b.h"
++#include <ec_kb3310b.h>
+
+ #define I8042_KBD_IRQ 1
+ #define I8042_CTR_KBDINT 0x01
+@@ -88,7 +88,7 @@ EXPORT_SYMBOL(yeeloong_report_lid_status);
+ static void yeeloong_lid_update_task(struct work_struct *work)
+ {
+ if (yeeloong_report_lid_status)
+- yeeloong_report_lid_status(BIT_LID_DETECT_ON);
++ yeeloong_report_lid_status(ON);
+ }
+
+ int wakeup_loongson(void)
+@@ -100,7 +100,7 @@ int wakeup_loongson(void)
+ if (irq < 0)
+ return 0;
+
+- printk(KERN_INFO "%s: irq = %d\n", __func__, irq);
++ pr_debug("%s: irq = %d\n", __func__, irq);
+
+ if (irq == I8042_KBD_IRQ)
+ return 1;
+@@ -118,7 +118,7 @@ int wakeup_loongson(void)
+ /* check the LID status */
+ lid_status = ec_read(REG_LID_DETECT);
+ /* wakeup cpu when people open the LID */
+- if (lid_status == BIT_LID_DETECT_ON) {
++ if (lid_status == ON) {
+ /* If we call it directly here, the WARNING
+ * will be sent out by getnstimeofday
+ * via "WARN_ON(timekeeping_suspended);"
+@@ -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/loongson/lemote-2f/reset.c b/arch/mips/loongson/lemote-2f/reset.c
+index 90962a3..5d3d95b 100644
+--- a/arch/mips/loongson/lemote-2f/reset.c
++++ b/arch/mips/loongson/lemote-2f/reset.c
+@@ -20,15 +20,14 @@
+ #include <loongson.h>
+
+ #include <cs5536/cs5536.h>
+-#include "ec_kb3310b.h"
++#include <ec_kb3310b.h>
+
+ static void reset_cpu(void)
+ {
+ /*
+- * reset cpu to full speed, this is needed when enabling cpu frequency
+- * scalling
++ * reset cpu to full speed
+ */
+- LOONGSON_CHIPCFG0 |= 0x7;
++ LOONGSON_CHIPCFG0 |= 7;
+ }
+
+ /* reset support for fuloong2f */
+@@ -81,7 +80,7 @@ void ml2f_reboot(void)
+ reset_cpu();
+
+ /* sending an reset signal to EC(embedded controller) */
+- ec_write(REG_RESET, BIT_RESET_ON);
++ ec_write(REG_RESET, ON);
+ }
+
+ #define yl2f89_reboot ml2f_reboot
+diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
+index f03771900..85c2a15 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 */
+
+@@ -884,6 +899,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.
+@@ -1260,6 +1283,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:
+ return SIGILL;
+ }
+@@ -1338,6 +1370,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)
+ {
+@@ -1431,7 +1629,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ break;
+
+ default:
+- return SIGILL;
++ goto SIGILL_unless_prefx_op;
+ }
+ break;
+ }
+@@ -1501,19 +1699,17 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ goto copcsr;
+
+ default:
+- return SIGILL;
++ goto SIGILL_unless_prefx_op;
+ }
+ break;
+ }
+
+- case 0x7: /* 7 */
+- if (MIPSInst_FUNC(ir) != pfetch_op) {
+- return SIGILL;
+- }
+- /* ignore prefx operation */
+- break;
+-
+ default:
++ SIGILL_unless_prefx_op:
++ if (MIPSInst_FUNC(ir) == prefx_op) {
++ /* ignore prefx operation */
++ break;
++ }
+ return SIGILL;
+ }
+
+@@ -1534,7 +1730,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;
+@@ -1606,7 +1807,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:
+@@ -1793,7 +1994,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:{
+@@ -1904,6 +2105,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;
+
+@@ -1993,6 +2271,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/c-octeon.c b/arch/mips/mm/c-octeon.c
+index 8557fb5..8b568ef 100644
+--- a/arch/mips/mm/c-octeon.c
++++ b/arch/mips/mm/c-octeon.c
+@@ -188,7 +188,7 @@ static void __cpuinit probe_octeon(void)
+ struct cpuinfo_mips *c = &current_cpu_data;
+
+ config1 = read_c0_config1();
+- switch (c->cputype) {
++ switch (current_cpu_type()) {
+ case CPU_CAVIUM_OCTEON:
+ case CPU_CAVIUM_OCTEON_PLUS:
+ c->icache.linesz = 2 << ((config1 >> 19) & 7);
+diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
+index 21813be..d8ecfa6 100644
+--- a/arch/mips/mm/c-r4k.c
++++ b/arch/mips/mm/c-r4k.c
+@@ -809,7 +809,7 @@ static void __cpuinit probe_pcache(void)
+ unsigned long config1;
+ unsigned int lsize;
+
+- switch (c->cputype) {
++ switch (current_cpu_type()) {
+ case CPU_R4600: /* QED style two way caches? */
+ case CPU_R4700:
+ case CPU_R5000:
+@@ -897,10 +897,10 @@ static void __cpuinit probe_pcache(void)
+ write_c0_config(config & ~VR41_CONF_P4K);
+ case CPU_VR4131:
+ /* Workaround for cache instruction bug of VR4131 */
+- if (c->processor_id == 0x0c80U || c->processor_id == 0x0c81U ||
+- c->processor_id == 0x0c82U) {
++ if (current_cpu_prid() == 0x0c80U || current_cpu_prid() == 0x0c81U ||
++ current_cpu_prid() == 0x0c82U) {
+ config |= 0x00400000U;
+- if (c->processor_id == 0x0c80U)
++ if (current_cpu_prid() == 0x0c80U)
+ config |= VR41_CONF_BP;
+ write_c0_config(config);
+ } else
+@@ -1045,7 +1045,7 @@ static void __cpuinit probe_pcache(void)
+ * normally they'd suffer from aliases but magic in the hardware deals
+ * with that for us so we don't need to take care ourselves.
+ */
+- switch (c->cputype) {
++ switch (current_cpu_type()) {
+ case CPU_20KC:
+ case CPU_25KF:
+ case CPU_SB1:
+@@ -1078,7 +1078,7 @@ static void __cpuinit probe_pcache(void)
+ c->dcache.flags |= MIPS_CACHE_ALIASES;
+ }
+
+- switch (c->cputype) {
++ switch (current_cpu_type()) {
+ case CPU_20KC:
+ /*
+ * Some older 20Kc chips doesn't have the 'VI' bit in
+@@ -1207,7 +1207,7 @@ static void __cpuinit setup_scache(void)
+ * processors don't have a S-cache that would be relevant to the
+ * Linux memory management.
+ */
+- switch (c->cputype) {
++ switch (current_cpu_type()) {
+ case CPU_R4000SC:
+ case CPU_R4000MC:
+ case CPU_R4400SC:
+@@ -1384,9 +1384,8 @@ static void __cpuinit r4k_cache_error_setup(void)
+ {
+ extern char __weak except_vec2_generic;
+ extern char __weak except_vec2_sb1;
+- struct cpuinfo_mips *c = &current_cpu_data;
+
+- switch (c->cputype) {
++ switch (current_cpu_type()) {
+ case CPU_SB1:
+ case CPU_SB1A:
+ set_uncached_handler(0x100, &except_vec2_sb1, 0x80);
+diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c
+index 23129d10..27e33a4 100644
+--- a/arch/mips/mm/dma-default.c
++++ b/arch/mips/mm/dma-default.c
+@@ -335,7 +335,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/mm/page.c b/arch/mips/mm/page.c
+index 4eb8dcf..72207dc 100644
+--- a/arch/mips/mm/page.c
++++ b/arch/mips/mm/page.c
+@@ -164,7 +164,7 @@ static void __cpuinit set_prefetch_parameters(void)
+ * hints are broken.
+ */
+ if (current_cpu_type() == CPU_SB1 &&
+- (current_cpu_data.processor_id & 0xff) < 0x02) {
++ cpu_prid_rev() < 0x02) {
+ pref_src_mode = Pref_Load;
+ pref_dst_mode = Pref_Store;
+ } else {
+diff --git a/arch/mips/mm/sc-mips.c b/arch/mips/mm/sc-mips.c
+index df96da7..cceaaaf 100644
+--- a/arch/mips/mm/sc-mips.c
++++ b/arch/mips/mm/sc-mips.c
+@@ -71,7 +71,7 @@ static inline int mips_sc_is_activated(struct cpuinfo_mips *c)
+ unsigned int tmp;
+
+ /* Check the bypass bit (L2B) */
+- switch (c->cputype) {
++ switch (current_cpu_type()) {
+ case CPU_34K:
+ case CPU_74K:
+ case CPU_1004K:
+diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
+index afeef93..b81da41 100644
+--- a/arch/mips/mm/tlbex.c
++++ b/arch/mips/mm/tlbex.c
+@@ -138,7 +138,7 @@ static int scratchpad_offset(int i)
+ */
+ static int __cpuinit m4kc_tlbp_war(void)
+ {
+- return (current_cpu_data.processor_id & 0xffff00) ==
++ return (current_cpu_prid() & 0xffff00) ==
+ (PRID_COMP_MIPS | PRID_IMP_4KC);
+ }
+
+@@ -631,7 +631,7 @@ static void __cpuinit build_tlb_write_entry(u32 **p, struct uasm_label **l,
+
+ default:
+ panic("No TLB refill handler yet (CPU type: %d)",
+- current_cpu_data.cputype);
++ current_cpu_type());
+ break;
+ }
+ }
+diff --git a/arch/mips/oprofile/op_model_mipsxx.c b/arch/mips/oprofile/op_model_mipsxx.c
+index e4b1140..6ffce07 100644
+--- a/arch/mips/oprofile/op_model_mipsxx.c
++++ b/arch/mips/oprofile/op_model_mipsxx.c
+@@ -381,7 +381,7 @@ static int __init mipsxx_init(void)
+ break;
+
+ case CPU_R10000:
+- if ((current_cpu_data.processor_id & 0xff) == 0x20)
++ if (current_cpu_prid() == 0x20)
+ op_model_mipsxx_ops.cpu_type = "mips/r10000-v2.x";
+ else
+ op_model_mipsxx_ops.cpu_type = "mips/r10000";
+diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile
+index 2cb1d31..9491e3a 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
+ 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/pci/ops-loongson2.c b/arch/mips/pci/ops-loongson2.c
+index 98254af..6b0f694 100644
+--- a/arch/mips/pci/ops-loongson2.c
++++ b/arch/mips/pci/ops-loongson2.c
+@@ -22,6 +22,7 @@
+ #ifdef CONFIG_CS5536
+ #include <cs5536/cs5536_pci.h>
+ #include <cs5536/cs5536.h>
++#include <linux/export.h>
+ #endif
+
+ #define PCI_ACCESS_READ 0
+diff --git a/arch/mips/vr41xx/common/init.c b/arch/mips/vr41xx/common/init.c
+index 23916321..aca5c76 100644
+--- a/arch/mips/vr41xx/common/init.c
++++ b/arch/mips/vr41xx/common/init.c
+@@ -43,8 +43,8 @@ void __init plat_time_init(void)
+ vr41xx_calculate_clock_frequency();
+
+ tclock = vr41xx_get_tclock_frequency();
+- if (current_cpu_data.processor_id == PRID_VR4131_REV2_0 ||
+- current_cpu_data.processor_id == PRID_VR4131_REV2_1)
++ if (current_cpu_prid() == PRID_VR4131_REV2_0 ||
++ current_cpu_prid() == PRID_VR4131_REV2_1)
+ mips_hpt_frequency = tclock / 2;
+ else
+ mips_hpt_frequency = tclock / 4;
+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 bee88ee..fc9062f 100644
+--- a/drivers/hid/Kconfig
++++ b/drivers/hid/Kconfig
+@@ -748,6 +748,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 && GENERIC_HARDIRQS
+diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
+index 2065694..10b48f0 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 a1e431f..84d07bc 100644
+--- a/drivers/hid/hid-ids.h
++++ b/drivers/hid/hid-ids.h
+@@ -912,6 +912,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 d4fe13e..8c1daa0 100644
+--- a/drivers/i2c/busses/Kconfig
++++ b/drivers/i2c/busses/Kconfig
+@@ -946,7 +946,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 9816c23..f83fd8a2 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/Kconfig b/drivers/net/wireless/Kconfig
+index f8f0156..e6b8e8a 100644
+--- a/drivers/net/wireless/Kconfig
++++ b/drivers/net/wireless/Kconfig
+@@ -213,6 +213,91 @@ config USB_NET_RNDIS_WLAN
+
+ If you choose to build a module, it'll be called rndis_wlan.
+
++config RTL8180
++ tristate "Realtek 8180/8185 PCI support"
++ depends on MAC80211 && PCI && EXPERIMENTAL
++ select EEPROM_93CX6
++ ---help---
++ This is a driver for RTL8180 and RTL8185 based cards.
++ These are PCI based chips found in cards such as:
++
++ (RTL8185 802.11g)
++ A-Link WL54PC
++
++ (RTL8180 802.11b)
++ Belkin F5D6020 v3
++ Belkin F5D6020 v3
++ Dlink DWL-610
++ Dlink DWL-510
++ Netgear MA521
++ Level-One WPC-0101
++ Acer Aspire 1357 LMi
++ VCTnet PC-11B1
++ Ovislink AirLive WL-1120PCM
++ Mentor WL-PCI
++ Linksys WPC11 v4
++ TrendNET TEW-288PI
++ D-Link DWL-520 Rev D
++ Repotec RP-WP7126
++ TP-Link TL-WN250/251
++ Zonet ZEW1000
++ Longshine LCS-8031-R
++ HomeLine HLW-PCC200
++ GigaFast WF721-AEX
++ Planet WL-3553
++ Encore ENLWI-PCI1-NT
++ TrendNET TEW-266PC
++ Gigabyte GN-WLMR101
++ Siemens-fujitsu Amilo D1840W
++ Edimax EW-7126
++ PheeNet WL-11PCIR
++ Tonze PC-2100T
++ Planet WL-8303
++ Dlink DWL-650 v M1
++ Edimax EW-7106
++ Q-Tec 770WC
++ Topcom Skyr@cer 4011b
++ Roper FreeLan 802.11b (edition 2004)
++ Wistron Neweb Corp CB-200B
++ Pentagram HorNET
++ QTec 775WC
++ TwinMOS Booming B Series
++ Micronet SP906BB
++ Sweex LC700010
++ Surecom EP-9428
++ Safecom SWLCR-1100
++
++ Thanks to Realtek for their support!
++
++config RTL8187
++ tristate "Realtek 8187 and 8187B USB support"
++ depends on MAC80211 && USB && !LEMOTE_MACH2F
++ select EEPROM_93CX6
++ ---help---
++ This is a driver for RTL8187 and RTL8187B based cards.
++ These are USB based chips found in devices such as:
++
++ Netgear WG111v2
++ Level 1 WNC-0301USB
++ Micronet SP907GK V5
++ Encore ENUWI-G2
++ Trendnet TEW-424UB
++ ASUS P5B Deluxe/P5K Premium motherboards
++ Toshiba Satellite Pro series of laptops
++ Asus Wireless Link
++ Linksys WUSB54GC-EU v2
++ (v1 = rt73usb; v3 is rt2070-based,
++ use staging/rt3070 or try rt2800usb)
++
++ Thanks to Realtek for their support!
++
++# If possible, automatically enable LEDs for RTL8187.
++
++config RTL8187_LEDS
++ bool
++ depends on RTL8187 && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = RTL8187)
++ default y
++
+ source "drivers/net/wireless/rtl818x/Kconfig"
+
+ config ADM8211
+diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
+index 67156ef..940aaf7 100644
+--- a/drivers/net/wireless/Makefile
++++ b/drivers/net/wireless/Makefile
+@@ -24,7 +24,7 @@ obj-$(CONFIG_B43LEGACY) += b43legacy/
+ obj-$(CONFIG_ZD1211RW) += zd1211rw/
+ obj-$(CONFIG_RTL8180) += rtl818x/
+ obj-$(CONFIG_RTL8187) += rtl818x/
+-obj-$(CONFIG_RTLWIFI) += rtlwifi/
++obj-$(CONFIG_RTL8192CE) += rtlwifi/
+
+ # 16-bit wireless PCMCIA client drivers
+ obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
+diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c
+index f49220e..acbfed9 100644
+--- a/drivers/net/wireless/rtl818x/rtl8187/dev.c
++++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c
+@@ -1015,6 +1015,9 @@ static void rtl8187_stop(struct ieee80211_hw *dev)
+ u32 reg;
+
+ mutex_lock(&priv->conf_mutex);
++
++ priv->vif = NULL;
++
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
+
+ reg = rtl818x_ioread8(priv, &priv->map->CMD);
+@@ -1134,10 +1137,7 @@ exit:
+ static void rtl8187_remove_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+ {
+- struct rtl8187_priv *priv = dev->priv;
+- mutex_lock(&priv->conf_mutex);
+- priv->vif = NULL;
+- mutex_unlock(&priv->conf_mutex);
++ /* Nothing to do */
+ }
+
+ static int rtl8187_config(struct ieee80211_hw *dev, u32 changed)
+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 69616ae..20f4f61 100644
+--- a/drivers/platform/Kconfig
++++ b/drivers/platform/Kconfig
+@@ -4,4 +4,6 @@ endif
+ if GOLDFISH
+ source "drivers/platform/goldfish/Kconfig"
+ endif
+-
++if MIPS
++source "drivers/platform/mips/Kconfig"
++endif
+diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
+index 8a44a4c..f38b7e8 100644
+--- a/drivers/platform/Makefile
++++ b/drivers/platform/Makefile
+@@ -3,5 +3,6 @@
+ #
+
+ obj-$(CONFIG_X86) += x86/
++obj-$(CONFIG_MIPS) += mips/
+ obj-$(CONFIG_OLPC) += olpc/
+ obj-$(CONFIG_GOLDFISH) += goldfish/
+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..0eadf8e
+--- /dev/null
++++ b/drivers/platform/mips/Makefile
+@@ -0,0 +1,8 @@
++#
++# Makefile for MIPS Platform-Specific Drivers
++#
++
++obj-$(CONFIG_LEMOTE_YEELOONG2F) += yeeloong_laptop.o
++
++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..3db791e
+--- /dev/null
++++ b/drivers/platform/mips/yeeloong_laptop.c
+@@ -0,0 +1,1356 @@
++/*
++ * 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>
++
++/* 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_DISPLAYTOGGLE, { KEY_DISPLAYTOGGLE } }, /* Fn + F2 */
++ {KE_KEY, EVENT_SWITCHVIDEOMODE, { 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_DISPLAYTOGGLE] = {REG_DISPLAY_LCD, displaytoggle_handler},
++ [EVENT_DISPLAY_BRIGHTNESS] = {REG_DISPLAY_BRIGHTNESS, NULL},
++ [EVENT_LID] = {REG_LID_DETECT, NULL},
++ [EVENT_SWITCHVIDEOMODE] = {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 b983813..1d8b111 100644
+--- a/drivers/rtc/Kconfig
++++ b/drivers/rtc/Kconfig
+@@ -598,6 +598,7 @@ comment "Platform RTC drivers"
+ config RTC_DRV_CMOS
+ tristate "PC-style 'CMOS'"
+ depends on X86 || ALPHA || ARM || M32R || ATARI || 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/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
+index 6ae046b..4c46047 100644
+--- a/drivers/rtc/rtc-cmos.c
++++ b/drivers/rtc/rtc-cmos.c
+@@ -739,8 +739,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
+ /* FIXME:
+ * <asm-generic/rtc.h> doesn't know 12-hour mode either.
+ */
+- if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) {
+- dev_warn(dev, "only 24-hr supported\n");
++ if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) {
++ dev_dbg(dev, "only 24-hr supported\n");
+ retval = -ENXIO;
+ goto cleanup1;
+ }
+diff --git a/drivers/staging/sm7xxfb/sm7xxfb.c b/drivers/staging/sm7xxfb/sm7xxfb.c
+index 8add64b..df8d3fb 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 865946c..8b7434fdb 100644
+--- a/drivers/usb/host/ohci-hcd.c
++++ b/drivers/usb/host/ohci-hcd.c
+@@ -863,9 +863,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 9cfe3af..b93877d 100644
+--- a/drivers/usb/host/pci-quirks.c
++++ b/drivers/usb/host/pci-quirks.c
+@@ -353,6 +353,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.
+@@ -460,12 +461,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;
+@@ -532,7 +537,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) */
+@@ -705,6 +714,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
+@@ -849,6 +861,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.
+ *
+@@ -956,6 +969,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/drivers/video/Makefile b/drivers/video/Makefile
+index e8bae8d..ad1d873 100644
+--- a/drivers/video/Makefile
++++ b/drivers/video/Makefile
+@@ -141,7 +141,7 @@ obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o
+ obj-$(CONFIG_FB_SH_MOBILE_MERAM) += sh_mobile_meram.o
+ obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o
+ obj-$(CONFIG_FB_OMAP) += omap/
+-obj-y += omap2/
++obj-$(CONFIG_FB_OMAP2) += omap2/
+ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o
+ obj-$(CONFIG_FB_CARMINE) += carminefb.o
+ obj-$(CONFIG_FB_MB862XX) += mb862xx/
+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 fda0a7b..7b5b4e5 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
+@@ -168,6 +169,7 @@ static unsigned long __cpuinit calibrate_delay_direct(void)
+ #else
+ static unsigned long __cpuinit 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
+@@ -279,6 +281,7 @@ void __cpuinit 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) {
+@@ -289,7 +292,9 @@ void __cpuinit 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)
+ pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)\n",
+diff --git a/net/rfkill/core.c b/net/rfkill/core.c
+index 1cec5e4..f6e5eae 100644
+--- a/net/rfkill/core.c
++++ b/net/rfkill/core.c
+@@ -113,7 +113,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 858966a..537470e 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/gnewsense-binutils-flag.patch b/kernels/linux-libre-lts-knock/gnewsense-binutils-flag.patch
new file mode 100644
index 000000000..5f2d44f99
--- /dev/null
+++ b/kernels/linux-libre-lts-knock/gnewsense-binutils-flag.patch
@@ -0,0 +1,28 @@
+Our binutils somehow ended up with a -mfix-gs2f-kernel, rather than
+-mfix-ls2f-kernel. Cope with it.
+
+Index: arch/mips/loongson/Platform
+===================================================================
+--- arch/mips/loongson/Platform.orig 2010-10-25 19:09:49.000000000 +0000
++++ arch/mips/loongson/Platform 2010-10-25 19:10:44.000000000 +0000
+@@ -10,6 +10,12 @@
+ $(call cc-option,-march=loongson2f,-march=r4600)
+ # Enable the workarounds for Loongson2f
+ ifdef CONFIG_CPU_LOONGSON2F_WORKAROUNDS
++ ifneq ($(call as-option,-Wa$(comma)-mfix-gs2f-kernel,),)
++ cflags-$(CONFIG_CPU_NOP_WORKAROUNDS) += -Wa$(comma)-mfix-gs2f-kernel
++ ifneq ($(CONFIG_CPU_NOP_WORKAROUNDS),$(CONFIG_CPU_JUMP_WORKAROUNDS))
++ cflags-$(CONFIG_CPU_JUMP_WORKAROUNDS) += -Wa$(comma)-mfix-gs2f-kernel
++ endif
++ else
+ ifeq ($(call as-option,-Wa$(comma)-mfix-loongson2f-nop,),)
+ $(error only binutils >= 2.20.2 have needed option -mfix-loongson2f-nop)
+ else
+@@ -20,6 +26,7 @@
+ else
+ cflags-$(CONFIG_CPU_JUMP_WORKAROUNDS) += -Wa$(comma)-mfix-loongson2f-jump
+ endif
++ endif
+ endif
+
+ #
diff --git a/kernels/linux-libre-rt/3.14.14-a410a5e2b7-loongson-community.patch b/kernels/linux-libre-rt/3.14.14-a410a5e2b7-loongson-community.patch
new file mode 100644
index 000000000..c7a63c2e5
--- /dev/null
+++ b/kernels/linux-libre-rt/3.14.14-a410a5e2b7-loongson-community.patch
@@ -0,0 +1,10869 @@
+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-rt/gnewsense-binutils-flag.patch b/kernels/linux-libre-rt/gnewsense-binutils-flag.patch
new file mode 100644
index 000000000..5f2d44f99
--- /dev/null
+++ b/kernels/linux-libre-rt/gnewsense-binutils-flag.patch
@@ -0,0 +1,28 @@
+Our binutils somehow ended up with a -mfix-gs2f-kernel, rather than
+-mfix-ls2f-kernel. Cope with it.
+
+Index: arch/mips/loongson/Platform
+===================================================================
+--- arch/mips/loongson/Platform.orig 2010-10-25 19:09:49.000000000 +0000
++++ arch/mips/loongson/Platform 2010-10-25 19:10:44.000000000 +0000
+@@ -10,6 +10,12 @@
+ $(call cc-option,-march=loongson2f,-march=r4600)
+ # Enable the workarounds for Loongson2f
+ ifdef CONFIG_CPU_LOONGSON2F_WORKAROUNDS
++ ifneq ($(call as-option,-Wa$(comma)-mfix-gs2f-kernel,),)
++ cflags-$(CONFIG_CPU_NOP_WORKAROUNDS) += -Wa$(comma)-mfix-gs2f-kernel
++ ifneq ($(CONFIG_CPU_NOP_WORKAROUNDS),$(CONFIG_CPU_JUMP_WORKAROUNDS))
++ cflags-$(CONFIG_CPU_JUMP_WORKAROUNDS) += -Wa$(comma)-mfix-gs2f-kernel
++ endif
++ else
+ ifeq ($(call as-option,-Wa$(comma)-mfix-loongson2f-nop,),)
+ $(error only binutils >= 2.20.2 have needed option -mfix-loongson2f-nop)
+ else
+@@ -20,6 +26,7 @@
+ else
+ cflags-$(CONFIG_CPU_JUMP_WORKAROUNDS) += -Wa$(comma)-mfix-loongson2f-jump
+ endif
++ endif
+ endif
+
+ #
diff --git a/libre/linux-libre-grsec/3.15.7-2704e67ebc-loongson-community.patch b/libre/linux-libre-grsec/3.15.7-2704e67ebc-loongson-community.patch
new file mode 100644
index 000000000..0af02e4d4
--- /dev/null
+++ b/libre/linux-libre-grsec/3.15.7-2704e67ebc-loongson-community.patch
@@ -0,0 +1,11744 @@
+diff --git a/Makefile b/Makefile
+index 833f67f..354c3fe 100644
+--- a/Makefile
++++ b/Makefile
+@@ -245,8 +245,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
+
+ ifeq ($(shell $(HOSTCC) -v 2>&1 | grep -c "clang version"), 1)
+ HOSTCFLAGS += -Wno-unused-value -Wno-unused-parameter \
+@@ -597,7 +597,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 5cd695f..2ca9abb 100644
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -277,7 +277,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.
+
+@@ -882,6 +882,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
+ select MIPS_CM
+ bool
+@@ -1524,6 +1578,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
+
+@@ -2204,7 +2267,7 @@ config CPU_SUPPORTS_MSA
+
+ 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/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/mach-loongson/cs5536/cs5536.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
+index a0ee0cb..4e18add 100644
+--- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
++++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
+@@ -301,5 +301,40 @@ extern void _wrmsr(u32 msr, u32 hi, u32 lo);
+ /* GPIO : I/O SPACE; REG : 32BITS */
+ #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..50aafca 100644
+--- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
++++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
+@@ -28,8 +28,19 @@ static inline void __maybe_unused enable_mfgpt0_counter(void)
+ #define COMPARE ((MFGPT_TICK_RATE + HZ/2) / HZ)
+
+ #define MFGPT_BASE mfgpt_base
++#define MFGPT0_CMP1 (MFGPT_BASE + 0)
+ #define MFGPT0_CMP2 (MFGPT_BASE + 2)
+ #define MFGPT0_CNT (MFGPT_BASE + 4)
+ #define MFGPT0_SETUP (MFGPT_BASE + 6)
+
++#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/loongson.h b/arch/mips/include/asm/mach-loongson/loongson.h
+index f3fd1eb..3fdfe84 100644
+--- a/arch/mips/include/asm/mach-loongson/loongson.h
++++ b/arch/mips/include/asm/mach-loongson/loongson.h
+@@ -46,6 +46,12 @@ static inline void prom_init_uart_base(void)
+ #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);
+ extern void __init bonito_irq_init(void);
+diff --git a/arch/mips/include/asm/mach-loongson/machine.h b/arch/mips/include/asm/mach-loongson/machine.h
+index 1b1f592..2f6eb79 100644
+--- a/arch/mips/include/asm/mach-loongson/machine.h
++++ b/arch/mips/include/asm/mach-loongson/machine.h
+@@ -24,6 +24,12 @@
+
+ #endif
+
++#ifdef CONFIG_DEXXON_GDIUM
++
++#define LOONGSON_MACHTYPE MACH_DEXXON_GDIUM2F10
++
++#endif
++
+ #ifdef CONFIG_LEMOTE_MACH3A
+
+ #define LOONGSON_MACHTYPE MACH_LEMOTE_A1101
+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/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 3125797..bf4241d 100644
+--- a/arch/mips/include/uapi/asm/inst.h
++++ b/arch/mips/include/uapi/asm/inst.h
+@@ -63,6 +63,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
+@@ -142,7 +144,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
+ };
+
+ /*
+@@ -171,7 +173,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 f1343cc..1320561 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 603d79a..984bf73 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,14 +45,13 @@ 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.
+@@ -60,6 +59,31 @@ config LEMOTE_MACH2F
+ 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.
++
+ config LEMOTE_MACH3A
+ bool "Lemote Loongson 3A family machines"
+ select ARCH_SPARSEMEM_ENABLE
+@@ -133,6 +157,24 @@ 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.
++
+ config LEFI_FIRMWARE_INTERFACE
+ bool
+
+diff --git a/arch/mips/loongson/Makefile b/arch/mips/loongson/Makefile
+index 7429994..63214c8 100644
+--- a/arch/mips/loongson/Makefile
++++ b/arch/mips/loongson/Makefile
+@@ -17,6 +17,12 @@ 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/
++
++#
+ # All Loongson-3 family machines
+ #
+
+diff --git a/arch/mips/loongson/Platform b/arch/mips/loongson/Platform
+index 6205372..32dd006 100644
+--- a/arch/mips/loongson/Platform
++++ b/arch/mips/loongson/Platform
+@@ -30,4 +30,5 @@ 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
+ load-$(CONFIG_CPU_LOONGSON3) += 0xffffffff80200000
+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/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/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/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
+index 7b3c9ac..4ba73f2 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 */
+
+@@ -919,6 +934,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.
+@@ -1314,6 +1337,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;
+@@ -1393,6 +1425,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)
+ {
+@@ -1486,7 +1684,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ break;
+
+ default:
+- return SIGILL;
++ goto SIGILL_unless_prefx_op;
+ }
+ break;
+ }
+@@ -1556,7 +1754,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;
+ }
+@@ -1569,6 +1767,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;
+ }
+
+@@ -1589,7 +1792,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;
+@@ -1661,7 +1869,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:
+@@ -1848,7 +2056,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:{
+@@ -1959,6 +2167,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;
+
+@@ -2051,6 +2336,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/pci/Makefile b/arch/mips/pci/Makefile
+index d61138a..9083d6f 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_LEMOTE_MACH3A) += fixup-loongson3.o ops-loongson3.o
+ obj-$(CONFIG_MIPS_MALTA) += fixup-malta.o pci-malta.o
+ obj-$(CONFIG_PMC_MSP7120_GW) += 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/hid/Kconfig b/drivers/hid/Kconfig
+index 7af9d0b..925d8bb 100644
+--- a/drivers/hid/Kconfig
++++ b/drivers/hid/Kconfig
+@@ -797,6 +797,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 fc712dd..666bd76 100644
+--- a/drivers/hid/Makefile
++++ b/drivers/hid/Makefile
+@@ -116,6 +116,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 34bb220..4aae21f 100644
+--- a/drivers/hid/hid-ids.h
++++ b/drivers/hid/hid-ids.h
+@@ -968,6 +968,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/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 2e565f8..f142f42 100644
+--- a/drivers/rtc/Kconfig
++++ b/drivers/rtc/Kconfig
+@@ -661,6 +661,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/Kconfig b/drivers/staging/Kconfig
+index 22365f1..757d7ce 100644
+--- a/drivers/staging/Kconfig
++++ b/drivers/staging/Kconfig
+@@ -80,6 +80,8 @@ source "drivers/staging/wlags49_h2/Kconfig"
+
+ source "drivers/staging/wlags49_h25/Kconfig"
+
++source "drivers/staging/sm7xxfb/Kconfig"
++
+ source "drivers/staging/crystalhd/Kconfig"
+
+ source "drivers/staging/cxt1e1/Kconfig"
+diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
+index fbe84ed..585bfcef 100644
+--- a/drivers/staging/Makefile
++++ b/drivers/staging/Makefile
+@@ -34,6 +34,7 @@ obj-$(CONFIG_DX_SEP) += sep/
+ obj-$(CONFIG_IIO) += iio/
+ obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
+ obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
++obj-$(CONFIG_FB_SM7XX) += sm7xxfb/
+ obj-$(CONFIG_CRYSTALHD) += crystalhd/
+ obj-$(CONFIG_CXT1E1) += cxt1e1/
+ obj-$(CONFIG_FB_XGI) += xgifb/
+diff --git a/drivers/staging/sm7xxfb/Kconfig b/drivers/staging/sm7xxfb/Kconfig
+new file mode 100644
+index 0000000..e2922ae
+--- /dev/null
++++ b/drivers/staging/sm7xxfb/Kconfig
+@@ -0,0 +1,13 @@
++config FB_SM7XX
++ tristate "Silicon Motion SM7XX framebuffer support"
++ depends on FB && PCI
++ select FB_CFB_FILLRECT
++ select FB_CFB_COPYAREA
++ select FB_CFB_IMAGEBLIT
++ help
++ Frame buffer driver for the Silicon Motion SM710, SM712, SM721
++ and SM722 chips.
++
++ This driver is also available as a module. The module will be
++ called sm7xxfb. If you want to compile it as a module, say M
++ here and read <file:Documentation/kbuild/modules.txt>.
+diff --git a/drivers/staging/sm7xxfb/Makefile b/drivers/staging/sm7xxfb/Makefile
+new file mode 100644
+index 0000000..48f471c
+--- /dev/null
++++ b/drivers/staging/sm7xxfb/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_FB_SM7XX) += sm7xxfb.o
+diff --git a/drivers/staging/sm7xxfb/TODO b/drivers/staging/sm7xxfb/TODO
+new file mode 100644
+index 0000000..1fcead5
+--- /dev/null
++++ b/drivers/staging/sm7xxfb/TODO
+@@ -0,0 +1,9 @@
++TODO:
++- Dual head support
++- 2D acceleration support
++- use kernel coding style
++- refine the code and remove unused code
++- move it to drivers/video/sm7xxfb.c
++
++Please send any patches to Greg Kroah-Hartman <greg@kroah.com> and
++Teddy Wang <teddy.wang@siliconmotion.com.cn>.
+diff --git a/drivers/staging/sm7xxfb/sm7xx.h b/drivers/staging/sm7xxfb/sm7xx.h
+new file mode 100644
+index 0000000..8599861
+--- /dev/null
++++ b/drivers/staging/sm7xxfb/sm7xx.h
+@@ -0,0 +1,779 @@
++/*
++ * Silicon Motion SM712 frame buffer device
++ *
++ * Copyright (C) 2006 Silicon Motion Technology Corp.
++ * Authors: Ge Wang, gewang@siliconmotion.com
++ * Boyod boyod.yang@siliconmotion.com.cn
++ *
++ * Copyright (C) 2009 Lemote, Inc.
++ * Author: Wu Zhangjin, wuzhangjin@gmail.com
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ */
++
++#define NR_PALETTE 256
++
++#define FB_ACCEL_SMI_LYNX 88
++
++#define SCREEN_X_RES 1024
++#define SCREEN_Y_RES 600
++#define SCREEN_BPP 16
++
++/*Assume SM712 graphics chip has 4MB VRAM */
++#define SM712_VIDEOMEMORYSIZE 0x00400000
++/*Assume SM722 graphics chip has 8MB VRAM */
++#define SM722_VIDEOMEMORYSIZE 0x00800000
++
++#define dac_reg (0x3c8)
++#define dac_val (0x3c9)
++
++extern void __iomem *smtc_RegBaseAddress;
++#define smtc_mmiowb(dat, reg) writeb(dat, smtc_RegBaseAddress + reg)
++#define smtc_mmioww(dat, reg) writew(dat, smtc_RegBaseAddress + reg)
++#define smtc_mmiowl(dat, reg) writel(dat, smtc_RegBaseAddress + reg)
++
++#define smtc_mmiorb(reg) readb(smtc_RegBaseAddress + reg)
++#define smtc_mmiorw(reg) readw(smtc_RegBaseAddress + reg)
++#define smtc_mmiorl(reg) readl(smtc_RegBaseAddress + reg)
++
++#define SIZE_SR00_SR04 (0x04 - 0x00 + 1)
++#define SIZE_SR10_SR24 (0x24 - 0x10 + 1)
++#define SIZE_SR30_SR75 (0x75 - 0x30 + 1)
++#define SIZE_SR80_SR93 (0x93 - 0x80 + 1)
++#define SIZE_SRA0_SRAF (0xAF - 0xA0 + 1)
++#define SIZE_GR00_GR08 (0x08 - 0x00 + 1)
++#define SIZE_AR00_AR14 (0x14 - 0x00 + 1)
++#define SIZE_CR00_CR18 (0x18 - 0x00 + 1)
++#define SIZE_CR30_CR4D (0x4D - 0x30 + 1)
++#define SIZE_CR90_CRA7 (0xA7 - 0x90 + 1)
++#define SIZE_VPR (0x6C + 1)
++#define SIZE_DPR (0x44 + 1)
++
++static inline void smtc_crtcw(int reg, int val)
++{
++ smtc_mmiowb(reg, 0x3d4);
++ smtc_mmiowb(val, 0x3d5);
++}
++
++static inline unsigned int smtc_crtcr(int reg)
++{
++ smtc_mmiowb(reg, 0x3d4);
++ return smtc_mmiorb(0x3d5);
++}
++
++static inline void smtc_grphw(int reg, int val)
++{
++ smtc_mmiowb(reg, 0x3ce);
++ smtc_mmiowb(val, 0x3cf);
++}
++
++static inline unsigned int smtc_grphr(int reg)
++{
++ smtc_mmiowb(reg, 0x3ce);
++ return smtc_mmiorb(0x3cf);
++}
++
++static inline void smtc_attrw(int reg, int val)
++{
++ smtc_mmiorb(0x3da);
++ smtc_mmiowb(reg, 0x3c0);
++ smtc_mmiorb(0x3c1);
++ smtc_mmiowb(val, 0x3c0);
++}
++
++static inline void smtc_seqw(int reg, int val)
++{
++ smtc_mmiowb(reg, 0x3c4);
++ smtc_mmiowb(val, 0x3c5);
++}
++
++static inline unsigned int smtc_seqr(int reg)
++{
++ smtc_mmiowb(reg, 0x3c4);
++ return smtc_mmiorb(0x3c5);
++}
++
++/* The next structure holds all information relevant for a specific video mode.
++ */
++
++struct ModeInit {
++ int mmSizeX;
++ int mmSizeY;
++ int bpp;
++ int hz;
++ unsigned char Init_MISC;
++ unsigned char Init_SR00_SR04[SIZE_SR00_SR04];
++ unsigned char Init_SR10_SR24[SIZE_SR10_SR24];
++ unsigned char Init_SR30_SR75[SIZE_SR30_SR75];
++ unsigned char Init_SR80_SR93[SIZE_SR80_SR93];
++ unsigned char Init_SRA0_SRAF[SIZE_SRA0_SRAF];
++ unsigned char Init_GR00_GR08[SIZE_GR00_GR08];
++ unsigned char Init_AR00_AR14[SIZE_AR00_AR14];
++ unsigned char Init_CR00_CR18[SIZE_CR00_CR18];
++ unsigned char Init_CR30_CR4D[SIZE_CR30_CR4D];
++ unsigned char Init_CR90_CRA7[SIZE_CR90_CRA7];
++};
++
++/**********************************************************************
++ SM712 Mode table.
++ **********************************************************************/
++struct ModeInit VGAMode[] = {
++ {
++ /* mode#0: 640 x 480 16Bpp 60Hz */
++ 640, 480, 16, 60,
++ /* Init_MISC */
++ 0xE3,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x00, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
++ 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
++ 0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
++ 0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
++ 0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
++ 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
++ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
++ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
++ 0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
++ 0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
++ 0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
++ 0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
++ },
++ },
++ {
++ /* mode#1: 640 x 480 24Bpp 60Hz */
++ 640, 480, 24, 60,
++ /* Init_MISC */
++ 0xE3,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x00, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
++ 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
++ 0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
++ 0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
++ 0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
++ 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
++ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
++ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
++ 0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
++ 0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
++ 0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
++ 0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
++ },
++ },
++ {
++ /* mode#0: 640 x 480 32Bpp 60Hz */
++ 640, 480, 32, 60,
++ /* Init_MISC */
++ 0xE3,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x00, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
++ 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
++ 0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
++ 0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
++ 0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
++ 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
++ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
++ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
++ 0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
++ 0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
++ 0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
++ 0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
++ },
++ },
++
++ { /* mode#2: 800 x 600 16Bpp 60Hz */
++ 800, 600, 16, 60,
++ /* Init_MISC */
++ 0x2B,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x34, 0x03, 0x20, 0x09, 0xC0, 0x24, 0x24, 0x24,
++ 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x38, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x24, 0x24, 0x24,
++ 0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
++ 0x04, 0x55, 0x59, 0x24, 0x24, 0x00, 0x00, 0x24,
++ 0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
++ 0x02, 0x45, 0x30, 0x35, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0x00, 0x00, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x24,
++ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x24, 0x24,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
++ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
++ 0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
++ 0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
++ 0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
++ 0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
++ },
++ },
++ { /* mode#3: 800 x 600 24Bpp 60Hz */
++ 800, 600, 24, 60,
++ 0x2B,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x36, 0x03, 0x20, 0x09, 0xC0, 0x36, 0x36, 0x36,
++ 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x36, 0x36, 0x36,
++ 0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
++ 0x04, 0x55, 0x59, 0x36, 0x36, 0x00, 0x00, 0x36,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
++ 0x02, 0x45, 0x30, 0x30, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x36,
++ 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x36, 0x36,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
++ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
++ 0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
++ 0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
++ 0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
++ 0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
++ },
++ },
++ { /* mode#7: 800 x 600 32Bpp 60Hz */
++ 800, 600, 32, 60,
++ /* Init_MISC */
++ 0x2B,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x34, 0x03, 0x20, 0x09, 0xC0, 0x24, 0x24, 0x24,
++ 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x38, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x24, 0x24, 0x24,
++ 0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
++ 0x04, 0x55, 0x59, 0x24, 0x24, 0x00, 0x00, 0x24,
++ 0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
++ 0x02, 0x45, 0x30, 0x35, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0x00, 0x00, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x24,
++ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x24, 0x24,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
++ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
++ 0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
++ 0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
++ 0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
++ 0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
++ },
++ },
++ /* We use 1024x768 table to light 1024x600 panel for lemote */
++ { /* mode#4: 1024 x 600 16Bpp 60Hz */
++ 1024, 600, 16, 60,
++ /* Init_MISC */
++ 0xEB,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x00, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xC8, 0x40, 0x14, 0x60, 0x00, 0x0A, 0x17, 0x20,
++ 0x51, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x00, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x22, 0x03, 0x24, 0x09, 0xC0, 0x22, 0x22, 0x22,
++ 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x22, 0x22, 0x22,
++ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
++ 0x00, 0x60, 0x59, 0x22, 0x22, 0x00, 0x00, 0x22,
++ 0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x16, 0x02, 0x0D, 0x82, 0x09, 0x02,
++ 0x04, 0x45, 0x3F, 0x30, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
++ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
++ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
++ 0xA3, 0x7F, 0x00, 0x82, 0x0b, 0x6f, 0x57, 0x00,
++ 0x5c, 0x0f, 0xE0, 0xe0, 0x7F, 0x57,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
++ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
++ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
++ },
++ },
++ { /* mode#5: 1024 x 768 24Bpp 60Hz */
++ 1024, 768, 24, 60,
++ /* Init_MISC */
++ 0xEB,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
++ 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
++ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
++ 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x3B, 0x0D, 0x09, 0x02,
++ 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
++ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
++ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
++ 0xA3, 0x7F, 0x00, 0x86, 0x15, 0x24, 0xFF, 0x00,
++ 0x01, 0x07, 0xE5, 0x20, 0x7F, 0xFF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
++ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
++ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
++ },
++ },
++ { /* mode#4: 1024 x 768 32Bpp 60Hz */
++ 1024, 768, 32, 60,
++ /* Init_MISC */
++ 0xEB,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x32, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
++ 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
++ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
++ 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x3B, 0x0D, 0x09, 0x02,
++ 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
++ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
++ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
++ 0xA3, 0x7F, 0x00, 0x86, 0x15, 0x24, 0xFF, 0x00,
++ 0x01, 0x07, 0xE5, 0x20, 0x7F, 0xFF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
++ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
++ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
++ },
++ },
++ { /* mode#6: 320 x 240 16Bpp 60Hz */
++ 320, 240, 16, 60,
++ /* Init_MISC */
++ 0xEB,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x32, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
++ 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
++ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
++ 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x08, 0x43, 0x08, 0x43,
++ 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
++ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
++ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
++ 0x00, 0x00, 0x30, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
++ 0x2E, 0x27, 0x00, 0x2b, 0x0c, 0x0F, 0xEF, 0x00,
++ 0xFe, 0x0f, 0x01, 0xC0, 0x27, 0xEF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
++ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
++ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
++ },
++ },
++
++ { /* mode#8: 320 x 240 32Bpp 60Hz */
++ 320, 240, 32, 60,
++ /* Init_MISC */
++ 0xEB,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x32, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
++ 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
++ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
++ 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x08, 0x43, 0x08, 0x43,
++ 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
++ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
++ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
++ 0x00, 0x00, 0x30, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
++ 0x2E, 0x27, 0x00, 0x2b, 0x0c, 0x0F, 0xEF, 0x00,
++ 0xFe, 0x0f, 0x01, 0xC0, 0x27, 0xEF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
++ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
++ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
++ },
++ },
++};
++
++#define numVGAModes ARRAY_SIZE(VGAMode)
+diff --git a/drivers/staging/sm7xxfb/sm7xxfb.c b/drivers/staging/sm7xxfb/sm7xxfb.c
+new file mode 100644
+index 0000000..e40ce80
+--- /dev/null
++++ b/drivers/staging/sm7xxfb/sm7xxfb.c
+@@ -0,0 +1,1029 @@
++/*
++ * Silicon Motion SM7XX frame buffer device
++ *
++ * Copyright (C) 2006 Silicon Motion Technology Corp.
++ * Authors: Ge Wang, gewang@siliconmotion.com
++ * Boyod boyod.yang@siliconmotion.com.cn
++ *
++ * Copyright (C) 2009 Lemote, Inc.
++ * Author: Wu Zhangjin, wuzhangjin@gmail.com
++ *
++ * Copyright (C) 2011 Igalia, S.L.
++ * Author: Javier M. Mellid <jmunhoz@igalia.com>
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ *
++ * Framebuffer driver for Silicon Motion SM710, SM712, SM721 and SM722 chips
++ */
++
++#include <linux/io.h>
++#include <linux/fb.h>
++#include <linux/pci.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/module.h>
++#include <linux/console.h>
++#include <linux/screen_info.h>
++
++#ifdef CONFIG_PM
++#include <linux/pm.h>
++#endif
++
++#include "sm7xx.h"
++
++/*
++* Private structure
++*/
++struct smtcfb_info {
++ struct pci_dev *pdev;
++ struct fb_info fb;
++ u16 chip_id;
++ u8 chip_rev_id;
++
++ void __iomem *lfb; /* linear frame buffer */
++ void __iomem *dp_regs; /* drawing processor control regs */
++ void __iomem *vp_regs; /* video processor control regs */
++ void __iomem *cp_regs; /* capture processor control regs */
++ void __iomem *mmio; /* memory map IO port */
++
++ u_int width;
++ u_int height;
++ u_int hz;
++
++ u32 colreg[17];
++};
++
++void __iomem *smtc_RegBaseAddress; /* Memory Map IO starting address */
++
++static struct fb_var_screeninfo smtcfb_var = {
++ .xres = 1024,
++ .yres = 600,
++ .xres_virtual = 1024,
++ .yres_virtual = 600,
++ .bits_per_pixel = 16,
++ .red = {16, 8, 0},
++ .green = {8, 8, 0},
++ .blue = {0, 8, 0},
++ .activate = FB_ACTIVATE_NOW,
++ .height = -1,
++ .width = -1,
++ .vmode = FB_VMODE_NONINTERLACED,
++ .nonstd = 0,
++ .accel_flags = FB_ACCELF_TEXT,
++};
++
++static struct fb_fix_screeninfo smtcfb_fix = {
++ .id = "smXXXfb",
++ .type = FB_TYPE_PACKED_PIXELS,
++ .visual = FB_VISUAL_TRUECOLOR,
++ .line_length = 800 * 3,
++ .accel = FB_ACCEL_SMI_LYNX,
++ .type_aux = 0,
++ .xpanstep = 0,
++ .ypanstep = 0,
++ .ywrapstep = 0,
++};
++
++struct vesa_mode {
++ char index[6];
++ u16 lfb_width;
++ u16 lfb_height;
++ u16 lfb_depth;
++};
++
++static struct vesa_mode vesa_mode_table[] = {
++ {"0x301", 640, 480, 8},
++ {"0x303", 800, 600, 8},
++ {"0x305", 1024, 768, 8},
++ {"0x307", 1280, 1024, 8},
++
++ {"0x311", 640, 480, 16},
++ {"0x313", 800, 480, 16},
++ {"0x314", 800, 600, 16},
++ {"0x317", 1024, 768, 16},
++ {"0x31A", 1280, 1024, 16},
++
++ {"0x312", 640, 480, 24},
++ {"0x315", 800, 600, 24},
++ {"0x318", 1024, 768, 24},
++ {"0x31B", 1280, 1024, 24},
++};
++
++struct screen_info smtc_scr_info;
++
++/* process command line options, get vga parameter */
++static int __init sm7xx_vga_setup(char *options)
++{
++ int i;
++
++ if (!options || !*options)
++ return -EINVAL;
++
++ smtc_scr_info.lfb_width = 0;
++ smtc_scr_info.lfb_height = 0;
++ smtc_scr_info.lfb_depth = 0;
++
++ pr_debug("sm7xx_vga_setup = %s\n", options);
++
++ for (i = 0; i < ARRAY_SIZE(vesa_mode_table); i++) {
++ if (strstr(options, vesa_mode_table[i].index)) {
++ smtc_scr_info.lfb_width = vesa_mode_table[i].lfb_width;
++ smtc_scr_info.lfb_height =
++ vesa_mode_table[i].lfb_height;
++ smtc_scr_info.lfb_depth = vesa_mode_table[i].lfb_depth;
++ return 0;
++ }
++ }
++
++ return -1;
++}
++__setup("vga=", sm7xx_vga_setup);
++
++static void sm712_setpalette(int regno, unsigned red, unsigned green,
++ unsigned blue, struct fb_info *info)
++{
++ /* set bit 5:4 = 01 (write LCD RAM only) */
++ smtc_seqw(0x66, (smtc_seqr(0x66) & 0xC3) | 0x10);
++
++ smtc_mmiowb(regno, dac_reg);
++ smtc_mmiowb(red >> 10, dac_val);
++ smtc_mmiowb(green >> 10, dac_val);
++ smtc_mmiowb(blue >> 10, dac_val);
++}
++
++/* chan_to_field
++ *
++ * convert a colour value into a field position
++ *
++ * from pxafb.c
++ */
++
++static inline unsigned int chan_to_field(unsigned int chan,
++ struct fb_bitfield *bf)
++{
++ chan &= 0xffff;
++ chan >>= 16 - bf->length;
++ return chan << bf->offset;
++}
++
++static int smtc_blank(int blank_mode, struct fb_info *info)
++{
++ /* clear DPMS setting */
++ switch (blank_mode) {
++ case FB_BLANK_UNBLANK:
++ /* Screen On: HSync: On, VSync : On */
++ smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20)));
++ smtc_seqw(0x6a, 0x16);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x21, (smtc_seqr(0x21) & 0x77));
++ smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30)));
++ smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0)));
++ smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01));
++ smtc_seqw(0x31, (smtc_seqr(0x31) | 0x03));
++ break;
++ case FB_BLANK_NORMAL:
++ /* Screen Off: HSync: On, VSync : On Soft blank */
++ smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20)));
++ smtc_seqw(0x6a, 0x16);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30)));
++ smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0)));
++ smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01));
++ smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
++ break;
++ case FB_BLANK_VSYNC_SUSPEND:
++ /* Screen On: HSync: On, VSync : Off */
++ smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
++ smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
++ smtc_seqw(0x6a, 0x0c);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
++ smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x20));
++ smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0x20));
++ smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
++ smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
++ smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
++ break;
++ case FB_BLANK_HSYNC_SUSPEND:
++ /* Screen On: HSync: Off, VSync : On */
++ smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
++ smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
++ smtc_seqw(0x6a, 0x0c);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
++ smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x10));
++ smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8));
++ smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
++ smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
++ smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
++ break;
++ case FB_BLANK_POWERDOWN:
++ /* Screen On: HSync: Off, VSync : Off */
++ smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
++ smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
++ smtc_seqw(0x6a, 0x0c);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
++ smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x30));
++ smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8));
++ smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
++ smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
++ smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int smtc_setcolreg(unsigned regno, unsigned red, unsigned green,
++ unsigned blue, unsigned trans, struct fb_info *info)
++{
++ struct smtcfb_info *sfb;
++ u32 val;
++
++ sfb = info->par;
++
++ if (regno > 255)
++ return 1;
++
++ switch (sfb->fb.fix.visual) {
++ case FB_VISUAL_DIRECTCOLOR:
++ case FB_VISUAL_TRUECOLOR:
++ /*
++ * 16/32 bit true-colour, use pseudo-palette for 16 base color
++ */
++ if (regno < 16) {
++ if (sfb->fb.var.bits_per_pixel == 16) {
++ u32 *pal = sfb->fb.pseudo_palette;
++ val = chan_to_field(red, &sfb->fb.var.red);
++ val |= chan_to_field(green, &sfb->fb.var.green);
++ val |= chan_to_field(blue, &sfb->fb.var.blue);
++#ifdef __BIG_ENDIAN
++ pal[regno] =
++ ((red & 0xf800) >> 8) |
++ ((green & 0xe000) >> 13) |
++ ((green & 0x1c00) << 3) |
++ ((blue & 0xf800) >> 3);
++#else
++ pal[regno] = val;
++#endif
++ } else {
++ u32 *pal = sfb->fb.pseudo_palette;
++ val = chan_to_field(red, &sfb->fb.var.red);
++ val |= chan_to_field(green, &sfb->fb.var.green);
++ val |= chan_to_field(blue, &sfb->fb.var.blue);
++#ifdef __BIG_ENDIAN
++ val =
++ (val & 0xff00ff00 >> 8) |
++ (val & 0x00ff00ff << 8);
++#endif
++ pal[regno] = val;
++ }
++ }
++ break;
++
++ case FB_VISUAL_PSEUDOCOLOR:
++ /* color depth 8 bit */
++ sm712_setpalette(regno, red, green, blue, info);
++ break;
++
++ default:
++ return 1; /* unknown type */
++ }
++
++ return 0;
++
++}
++
++#ifdef __BIG_ENDIAN
++static ssize_t smtcfb_read(struct fb_info *info, char __user *buf, size_t
++ count, loff_t *ppos)
++{
++ unsigned long p = *ppos;
++
++ u32 *buffer, *dst;
++ u32 __iomem *src;
++ int c, i, cnt = 0, err = 0;
++ unsigned long total_size;
++
++ if (!info || !info->screen_base)
++ return -ENODEV;
++
++ if (info->state != FBINFO_STATE_RUNNING)
++ return -EPERM;
++
++ total_size = info->screen_size;
++
++ if (total_size == 0)
++ total_size = info->fix.smem_len;
++
++ if (p >= total_size)
++ return 0;
++
++ if (count >= total_size)
++ count = total_size;
++
++ if (count + p > total_size)
++ count = total_size - p;
++
++ buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL);
++ if (!buffer)
++ return -ENOMEM;
++
++ src = (u32 __iomem *) (info->screen_base + p);
++
++ if (info->fbops->fb_sync)
++ info->fbops->fb_sync(info);
++
++ while (count) {
++ c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
++ dst = buffer;
++ for (i = c >> 2; i--;) {
++ *dst = fb_readl(src++);
++ *dst =
++ (*dst & 0xff00ff00 >> 8) |
++ (*dst & 0x00ff00ff << 8);
++ dst++;
++ }
++ if (c & 3) {
++ u8 *dst8 = (u8 *) dst;
++ u8 __iomem *src8 = (u8 __iomem *) src;
++
++ for (i = c & 3; i--;) {
++ if (i & 1) {
++ *dst8++ = fb_readb(++src8);
++ } else {
++ *dst8++ = fb_readb(--src8);
++ src8 += 2;
++ }
++ }
++ src = (u32 __iomem *) src8;
++ }
++
++ if (copy_to_user(buf, buffer, c)) {
++ err = -EFAULT;
++ break;
++ }
++ *ppos += c;
++ buf += c;
++ cnt += c;
++ count -= c;
++ }
++
++ kfree(buffer);
++
++ return (err) ? err : cnt;
++}
++
++static ssize_t
++smtcfb_write(struct fb_info *info, const char __user *buf, size_t count,
++ loff_t *ppos)
++{
++ unsigned long p = *ppos;
++
++ u32 *buffer, *src;
++ u32 __iomem *dst;
++ int c, i, cnt = 0, err = 0;
++ unsigned long total_size;
++
++ if (!info || !info->screen_base)
++ return -ENODEV;
++
++ if (info->state != FBINFO_STATE_RUNNING)
++ return -EPERM;
++
++ total_size = info->screen_size;
++
++ if (total_size == 0)
++ total_size = info->fix.smem_len;
++
++ if (p > total_size)
++ return -EFBIG;
++
++ if (count > total_size) {
++ err = -EFBIG;
++ count = total_size;
++ }
++
++ if (count + p > total_size) {
++ if (!err)
++ err = -ENOSPC;
++
++ count = total_size - p;
++ }
++
++ buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL);
++ if (!buffer)
++ return -ENOMEM;
++
++ dst = (u32 __iomem *) (info->screen_base + p);
++
++ if (info->fbops->fb_sync)
++ info->fbops->fb_sync(info);
++
++ while (count) {
++ c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
++ src = buffer;
++
++ if (copy_from_user(src, buf, c)) {
++ err = -EFAULT;
++ break;
++ }
++
++ for (i = c >> 2; i--;) {
++ fb_writel((*src & 0xff00ff00 >> 8) |
++ (*src & 0x00ff00ff << 8), dst++);
++ src++;
++ }
++ if (c & 3) {
++ u8 *src8 = (u8 *) src;
++ u8 __iomem *dst8 = (u8 __iomem *) dst;
++
++ for (i = c & 3; i--;) {
++ if (i & 1) {
++ fb_writeb(*src8++, ++dst8);
++ } else {
++ fb_writeb(*src8++, --dst8);
++ dst8 += 2;
++ }
++ }
++ dst = (u32 __iomem *) dst8;
++ }
++
++ *ppos += c;
++ buf += c;
++ cnt += c;
++ count -= c;
++ }
++
++ kfree(buffer);
++
++ return (cnt) ? cnt : err;
++}
++#endif /* ! __BIG_ENDIAN */
++
++static void sm7xx_set_timing(struct smtcfb_info *sfb)
++{
++ int i = 0, j = 0;
++ u32 m_nScreenStride;
++
++ dev_dbg(&sfb->pdev->dev,
++ "sfb->width=%d sfb->height=%d "
++ "sfb->fb.var.bits_per_pixel=%d sfb->hz=%d\n",
++ sfb->width, sfb->height, sfb->fb.var.bits_per_pixel, sfb->hz);
++
++ for (j = 0; j < numVGAModes; j++) {
++ if (VGAMode[j].mmSizeX == sfb->width &&
++ VGAMode[j].mmSizeY == sfb->height &&
++ VGAMode[j].bpp == sfb->fb.var.bits_per_pixel &&
++ VGAMode[j].hz == sfb->hz) {
++
++ dev_dbg(&sfb->pdev->dev,
++ "VGAMode[j].mmSizeX=%d VGAMode[j].mmSizeY=%d "
++ "VGAMode[j].bpp=%d VGAMode[j].hz=%d\n",
++ VGAMode[j].mmSizeX, VGAMode[j].mmSizeY,
++ VGAMode[j].bpp, VGAMode[j].hz);
++
++ dev_dbg(&sfb->pdev->dev, "VGAMode index=%d\n", j);
++
++ smtc_mmiowb(0x0, 0x3c6);
++
++ smtc_seqw(0, 0x1);
++
++ smtc_mmiowb(VGAMode[j].Init_MISC, 0x3c2);
++
++ /* init SEQ register SR00 - SR04 */
++ for (i = 0; i < SIZE_SR00_SR04; i++)
++ smtc_seqw(i, VGAMode[j].Init_SR00_SR04[i]);
++
++ /* init SEQ register SR10 - SR24 */
++ for (i = 0; i < SIZE_SR10_SR24; i++)
++ smtc_seqw(i + 0x10,
++ VGAMode[j].Init_SR10_SR24[i]);
++
++ /* init SEQ register SR30 - SR75 */
++ for (i = 0; i < SIZE_SR30_SR75; i++)
++ if ((i + 0x30) != 0x62 &&
++ (i + 0x30) != 0x6a &&
++ (i + 0x30) != 0x6b)
++ smtc_seqw(i + 0x30,
++ VGAMode[j].Init_SR30_SR75[i]);
++
++ /* init SEQ register SR80 - SR93 */
++ for (i = 0; i < SIZE_SR80_SR93; i++)
++ smtc_seqw(i + 0x80,
++ VGAMode[j].Init_SR80_SR93[i]);
++
++ /* init SEQ register SRA0 - SRAF */
++ for (i = 0; i < SIZE_SRA0_SRAF; i++)
++ smtc_seqw(i + 0xa0,
++ VGAMode[j].Init_SRA0_SRAF[i]);
++
++ /* init Graphic register GR00 - GR08 */
++ for (i = 0; i < SIZE_GR00_GR08; i++)
++ smtc_grphw(i, VGAMode[j].Init_GR00_GR08[i]);
++
++ /* init Attribute register AR00 - AR14 */
++ for (i = 0; i < SIZE_AR00_AR14; i++)
++ smtc_attrw(i, VGAMode[j].Init_AR00_AR14[i]);
++
++ /* init CRTC register CR00 - CR18 */
++ for (i = 0; i < SIZE_CR00_CR18; i++)
++ smtc_crtcw(i, VGAMode[j].Init_CR00_CR18[i]);
++
++ /* init CRTC register CR30 - CR4D */
++ for (i = 0; i < SIZE_CR30_CR4D; i++)
++ smtc_crtcw(i + 0x30,
++ VGAMode[j].Init_CR30_CR4D[i]);
++
++ /* init CRTC register CR90 - CRA7 */
++ for (i = 0; i < SIZE_CR90_CRA7; i++)
++ smtc_crtcw(i + 0x90,
++ VGAMode[j].Init_CR90_CRA7[i]);
++ }
++ }
++ smtc_mmiowb(0x67, 0x3c2);
++
++ /* set VPR registers */
++ writel(0x0, sfb->vp_regs + 0x0C);
++ writel(0x0, sfb->vp_regs + 0x40);
++
++ /* set data width */
++ m_nScreenStride =
++ (sfb->width * sfb->fb.var.bits_per_pixel) / 64;
++ switch (sfb->fb.var.bits_per_pixel) {
++ case 8:
++ writel(0x0, sfb->vp_regs + 0x0);
++ break;
++ case 16:
++ writel(0x00020000, sfb->vp_regs + 0x0);
++ break;
++ case 24:
++ writel(0x00040000, sfb->vp_regs + 0x0);
++ break;
++ case 32:
++ writel(0x00030000, sfb->vp_regs + 0x0);
++ break;
++ }
++ writel((u32) (((m_nScreenStride + 2) << 16) | m_nScreenStride),
++ sfb->vp_regs + 0x10);
++
++}
++
++static void smtc_set_timing(struct smtcfb_info *sfb)
++{
++ switch (sfb->chip_id) {
++ case 0x710:
++ case 0x712:
++ case 0x720:
++ sm7xx_set_timing(sfb);
++ break;
++ }
++}
++
++static void smtcfb_setmode(struct smtcfb_info *sfb)
++{
++ switch (sfb->fb.var.bits_per_pixel) {
++ case 32:
++ sfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
++ sfb->fb.fix.line_length = sfb->fb.var.xres * 4;
++ sfb->fb.var.red.length = 8;
++ sfb->fb.var.green.length = 8;
++ sfb->fb.var.blue.length = 8;
++ sfb->fb.var.red.offset = 16;
++ sfb->fb.var.green.offset = 8;
++ sfb->fb.var.blue.offset = 0;
++ break;
++ case 24:
++ sfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
++ sfb->fb.fix.line_length = sfb->fb.var.xres * 3;
++ sfb->fb.var.red.length = 8;
++ sfb->fb.var.green.length = 8;
++ sfb->fb.var.blue.length = 8;
++ sfb->fb.var.red.offset = 16;
++ sfb->fb.var.green.offset = 8;
++ sfb->fb.var.blue.offset = 0;
++ break;
++ case 8:
++ sfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ sfb->fb.fix.line_length = sfb->fb.var.xres;
++ sfb->fb.var.red.length = 3;
++ sfb->fb.var.green.length = 3;
++ sfb->fb.var.blue.length = 2;
++ sfb->fb.var.red.offset = 5;
++ sfb->fb.var.green.offset = 2;
++ sfb->fb.var.blue.offset = 0;
++ break;
++ case 16:
++ default:
++ sfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
++ sfb->fb.fix.line_length = sfb->fb.var.xres * 2;
++ sfb->fb.var.red.length = 5;
++ sfb->fb.var.green.length = 6;
++ sfb->fb.var.blue.length = 5;
++ sfb->fb.var.red.offset = 11;
++ sfb->fb.var.green.offset = 5;
++ sfb->fb.var.blue.offset = 0;
++ break;
++ }
++
++ sfb->width = sfb->fb.var.xres;
++ sfb->height = sfb->fb.var.yres;
++ sfb->hz = 60;
++ smtc_set_timing(sfb);
++}
++
++static int smtc_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
++{
++ /* sanity checks */
++ if (var->xres_virtual < var->xres)
++ var->xres_virtual = var->xres;
++
++ if (var->yres_virtual < var->yres)
++ var->yres_virtual = var->yres;
++
++ /* set valid default bpp */
++ if ((var->bits_per_pixel != 8) && (var->bits_per_pixel != 16) &&
++ (var->bits_per_pixel != 24) && (var->bits_per_pixel != 32))
++ var->bits_per_pixel = 16;
++
++ return 0;
++}
++
++static int smtc_set_par(struct fb_info *info)
++{
++ smtcfb_setmode(info->par);
++
++ return 0;
++}
++
++static struct fb_ops smtcfb_ops = {
++ .owner = THIS_MODULE,
++ .fb_check_var = smtc_check_var,
++ .fb_set_par = smtc_set_par,
++ .fb_setcolreg = smtc_setcolreg,
++ .fb_blank = smtc_blank,
++ .fb_fillrect = cfb_fillrect,
++ .fb_imageblit = cfb_imageblit,
++ .fb_copyarea = cfb_copyarea,
++#ifdef __BIG_ENDIAN
++ .fb_read = smtcfb_read,
++ .fb_write = smtcfb_write,
++#endif
++};
++
++/*
++ * alloc struct smtcfb_info and assign default values
++ */
++static struct smtcfb_info *smtc_alloc_fb_info(struct pci_dev *pdev)
++{
++ struct smtcfb_info *sfb;
++
++ sfb = kzalloc(sizeof(*sfb), GFP_KERNEL);
++
++ if (!sfb)
++ return NULL;
++
++ sfb->pdev = pdev;
++
++ sfb->fb.flags = FBINFO_FLAG_DEFAULT;
++ sfb->fb.fbops = &smtcfb_ops;
++ sfb->fb.fix = smtcfb_fix;
++ sfb->fb.var = smtcfb_var;
++ sfb->fb.pseudo_palette = sfb->colreg;
++ sfb->fb.par = sfb;
++
++ return sfb;
++}
++
++/*
++ * free struct smtcfb_info
++ */
++static void smtc_free_fb_info(struct smtcfb_info *sfb)
++{
++ kfree(sfb);
++}
++
++/*
++ * Unmap in the memory mapped IO registers
++ */
++
++static void smtc_unmap_mmio(struct smtcfb_info *sfb)
++{
++ if (sfb && smtc_RegBaseAddress)
++ smtc_RegBaseAddress = NULL;
++}
++
++/*
++ * Map in the screen memory
++ */
++
++static int smtc_map_smem(struct smtcfb_info *sfb,
++ struct pci_dev *pdev, u_long smem_len)
++{
++
++ sfb->fb.fix.smem_start = pci_resource_start(pdev, 0);
++
++#ifdef __BIG_ENDIAN
++ if (sfb->fb.var.bits_per_pixel == 32)
++ sfb->fb.fix.smem_start += 0x800000;
++#endif
++
++ sfb->fb.fix.smem_len = smem_len;
++
++ sfb->fb.screen_base = sfb->lfb;
++
++ if (!sfb->fb.screen_base) {
++ dev_err(&pdev->dev,
++ "%s: unable to map screen memory\n", sfb->fb.fix.id);
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++/*
++ * Unmap in the screen memory
++ *
++ */
++static void smtc_unmap_smem(struct smtcfb_info *sfb)
++{
++ if (sfb && sfb->fb.screen_base) {
++ iounmap(sfb->fb.screen_base);
++ sfb->fb.screen_base = NULL;
++ }
++}
++
++/*
++ * We need to wake up the device and make sure its in linear memory mode.
++ */
++static inline void sm7xx_init_hw(void)
++{
++ outb_p(0x18, 0x3c4);
++ outb_p(0x11, 0x3c5);
++}
++
++static int smtcfb_pci_probe(struct pci_dev *pdev,
++ const struct pci_device_id *ent)
++{
++ struct smtcfb_info *sfb;
++ u_long smem_size = 0x00800000; /* default 8MB */
++ int err;
++ unsigned long mmio_base;
++
++ dev_info(&pdev->dev, "Silicon Motion display driver.");
++
++ err = pci_enable_device(pdev); /* enable SMTC chip */
++ if (err)
++ return err;
++
++ sprintf(smtcfb_fix.id, "sm%Xfb", ent->device);
++
++ sfb = smtc_alloc_fb_info(pdev);
++
++ if (!sfb) {
++ err = -ENOMEM;
++ goto failed_free;
++ }
++
++ sfb->chip_id = ent->device;
++
++ pci_set_drvdata(pdev, sfb);
++
++ sm7xx_init_hw();
++
++ /* get mode parameter from smtc_scr_info */
++ if (smtc_scr_info.lfb_width != 0) {
++ sfb->fb.var.xres = smtc_scr_info.lfb_width;
++ sfb->fb.var.yres = smtc_scr_info.lfb_height;
++ sfb->fb.var.bits_per_pixel = smtc_scr_info.lfb_depth;
++ } else {
++ /* default resolution 1024x600 16bit mode */
++ sfb->fb.var.xres = SCREEN_X_RES;
++ sfb->fb.var.yres = SCREEN_Y_RES;
++ sfb->fb.var.bits_per_pixel = SCREEN_BPP;
++ }
++
++#ifdef __BIG_ENDIAN
++ if (sfb->fb.var.bits_per_pixel == 24)
++ sfb->fb.var.bits_per_pixel = (smtc_scr_info.lfb_depth = 32);
++#endif
++ /* Map address and memory detection */
++ mmio_base = pci_resource_start(pdev, 0);
++ pci_read_config_byte(pdev, PCI_REVISION_ID, &sfb->chip_rev_id);
++
++ switch (sfb->chip_id) {
++ case 0x710:
++ case 0x712:
++ sfb->fb.fix.mmio_start = mmio_base + 0x00400000;
++ sfb->fb.fix.mmio_len = 0x00400000;
++ smem_size = SM712_VIDEOMEMORYSIZE;
++#ifdef __BIG_ENDIAN
++ sfb->lfb = ioremap(mmio_base, 0x00c00000);
++#else
++ sfb->lfb = ioremap(mmio_base, 0x00800000);
++#endif
++ sfb->mmio = (smtc_RegBaseAddress =
++ sfb->lfb + 0x00700000);
++ sfb->dp_regs = sfb->lfb + 0x00408000;
++ sfb->vp_regs = sfb->lfb + 0x0040c000;
++#ifdef __BIG_ENDIAN
++ if (sfb->fb.var.bits_per_pixel == 32) {
++ sfb->lfb += 0x800000;
++ dev_info(&pdev->dev, "sfb->lfb=%p", sfb->lfb);
++ }
++#endif
++ if (!smtc_RegBaseAddress) {
++ dev_err(&pdev->dev,
++ "%s: unable to map memory mapped IO!",
++ sfb->fb.fix.id);
++ err = -ENOMEM;
++ goto failed_fb;
++ }
++
++ /* set MCLK = 14.31818 * (0x16 / 0x2) */
++ smtc_seqw(0x6a, 0x16);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x62, 0x3e);
++ /* enable PCI burst */
++ smtc_seqw(0x17, 0x20);
++ /* enable word swap */
++#ifdef __BIG_ENDIAN
++ if (sfb->fb.var.bits_per_pixel == 32)
++ smtc_seqw(0x17, 0x30);
++#endif
++ break;
++ case 0x720:
++ sfb->fb.fix.mmio_start = mmio_base;
++ sfb->fb.fix.mmio_len = 0x00200000;
++ smem_size = SM722_VIDEOMEMORYSIZE;
++ sfb->dp_regs = ioremap(mmio_base, 0x00a00000);
++ sfb->lfb = sfb->dp_regs + 0x00200000;
++ sfb->mmio = (smtc_RegBaseAddress =
++ sfb->dp_regs + 0x000c0000);
++ sfb->vp_regs = sfb->dp_regs + 0x800;
++
++ smtc_seqw(0x62, 0xff);
++ smtc_seqw(0x6a, 0x0d);
++ smtc_seqw(0x6b, 0x02);
++ break;
++ default:
++ dev_err(&pdev->dev,
++ "No valid Silicon Motion display chip was detected!");
++
++ goto failed_fb;
++ }
++
++ /* can support 32 bpp */
++ if (15 == sfb->fb.var.bits_per_pixel)
++ sfb->fb.var.bits_per_pixel = 16;
++
++ sfb->fb.var.xres_virtual = sfb->fb.var.xres;
++ sfb->fb.var.yres_virtual = sfb->fb.var.yres;
++ err = smtc_map_smem(sfb, pdev, smem_size);
++ if (err)
++ goto failed;
++
++ smtcfb_setmode(sfb);
++
++ err = register_framebuffer(&sfb->fb);
++ if (err < 0)
++ goto failed;
++
++ dev_info(&pdev->dev,
++ "Silicon Motion SM%X Rev%X primary display mode %dx%d-%d Init Complete.",
++ sfb->chip_id, sfb->chip_rev_id, sfb->fb.var.xres,
++ sfb->fb.var.yres, sfb->fb.var.bits_per_pixel);
++
++ return 0;
++
++failed:
++ dev_err(&pdev->dev, "Silicon Motion, Inc. primary display init fail.");
++
++ smtc_unmap_smem(sfb);
++ smtc_unmap_mmio(sfb);
++failed_fb:
++ smtc_free_fb_info(sfb);
++
++failed_free:
++ pci_disable_device(pdev);
++
++ return err;
++}
++
++/*
++ * 0x710 (LynxEM)
++ * 0x712 (LynxEM+)
++ * 0x720 (Lynx3DM, Lynx3DM+)
++ */
++static const struct pci_device_id smtcfb_pci_table[] = {
++ { PCI_DEVICE(0x126f, 0x710), },
++ { PCI_DEVICE(0x126f, 0x712), },
++ { PCI_DEVICE(0x126f, 0x720), },
++ {0,}
++};
++
++static void smtcfb_pci_remove(struct pci_dev *pdev)
++{
++ struct smtcfb_info *sfb;
++
++ sfb = pci_get_drvdata(pdev);
++ smtc_unmap_smem(sfb);
++ smtc_unmap_mmio(sfb);
++ unregister_framebuffer(&sfb->fb);
++ smtc_free_fb_info(sfb);
++}
++
++#ifdef CONFIG_PM
++static int smtcfb_pci_suspend(struct device *device)
++{
++ struct pci_dev *pdev = to_pci_dev(device);
++ struct smtcfb_info *sfb;
++
++ sfb = pci_get_drvdata(pdev);
++
++ /* set the hw in sleep mode use external clock and self memory refresh
++ * so that we can turn off internal PLLs later on
++ */
++ smtc_seqw(0x20, (smtc_seqr(0x20) | 0xc0));
++ smtc_seqw(0x69, (smtc_seqr(0x69) & 0xf7));
++
++ console_lock();
++ fb_set_suspend(&sfb->fb, 1);
++ console_unlock();
++
++ /* additionally turn off all function blocks including internal PLLs */
++ smtc_seqw(0x21, 0xff);
++
++ return 0;
++}
++
++static int smtcfb_pci_resume(struct device *device)
++{
++ struct pci_dev *pdev = to_pci_dev(device);
++ struct smtcfb_info *sfb;
++
++ sfb = pci_get_drvdata(pdev);
++
++ /* reinit hardware */
++ sm7xx_init_hw();
++ switch (sfb->chip_id) {
++ case 0x710:
++ case 0x712:
++ /* set MCLK = 14.31818 * (0x16 / 0x2) */
++ smtc_seqw(0x6a, 0x16);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x62, 0x3e);
++ /* enable PCI burst */
++ smtc_seqw(0x17, 0x20);
++#ifdef __BIG_ENDIAN
++ if (sfb->fb.var.bits_per_pixel == 32)
++ smtc_seqw(0x17, 0x30);
++#endif
++ break;
++ case 0x720:
++ smtc_seqw(0x62, 0xff);
++ smtc_seqw(0x6a, 0x0d);
++ smtc_seqw(0x6b, 0x02);
++ break;
++ }
++
++ smtc_seqw(0x34, (smtc_seqr(0x34) | 0xc0));
++ smtc_seqw(0x33, ((smtc_seqr(0x33) | 0x08) & 0xfb));
++
++ smtcfb_setmode(sfb);
++
++ console_lock();
++ fb_set_suspend(&sfb->fb, 0);
++ console_unlock();
++
++ return 0;
++}
++
++static SIMPLE_DEV_PM_OPS(sm7xx_pm_ops, smtcfb_pci_suspend, smtcfb_pci_resume);
++#define SM7XX_PM_OPS (&sm7xx_pm_ops)
++
++#else /* !CONFIG_PM */
++
++#define SM7XX_PM_OPS NULL
++
++#endif /* !CONFIG_PM */
++
++static struct pci_driver smtcfb_driver = {
++ .name = "smtcfb",
++ .id_table = smtcfb_pci_table,
++ .probe = smtcfb_pci_probe,
++ .remove = smtcfb_pci_remove,
++ .driver.pm = SM7XX_PM_OPS,
++};
++
++module_pci_driver(smtcfb_driver);
++
++MODULE_AUTHOR("Siliconmotion ");
++MODULE_DESCRIPTION("Framebuffer driver for SMI Graphic Cards");
++MODULE_LICENSE("GPL");
+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/drivers/video/Kconfig b/drivers/video/Kconfig
+index c7b4f0f..7e67f27 100644
+--- a/drivers/video/Kconfig
++++ b/drivers/video/Kconfig
+@@ -35,6 +35,12 @@ config VGASTATE
+ tristate
+ default n
+
++config VIDEO_OUTPUT_CONTROL
++ tristate "Lowlevel video output switch controls"
++ help
++ This framework adds support for low-level control of the video
++ output switch.
++
+ config VIDEOMODE_HELPERS
+ bool
+
+diff --git a/drivers/video/Makefile b/drivers/video/Makefile
+index 9ad3c17..3d869d9 100644
+--- a/drivers/video/Makefile
++++ b/drivers/video/Makefile
+@@ -7,6 +7,8 @@ obj-y += backlight/
+
+ obj-y += fbdev/
+
++#video output switch sysfs driver
++obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o
+ obj-$(CONFIG_VIDEOMODE_HELPERS) += display_timing.o videomode.o
+ ifeq ($(CONFIG_OF),y)
+ obj-$(CONFIG_VIDEOMODE_HELPERS) += of_display_timing.o of_videomode.o
+diff --git a/drivers/video/output.c b/drivers/video/output.c
+new file mode 100644
+index 0000000..1446c49
+--- /dev/null
++++ b/drivers/video/output.c
+@@ -0,0 +1,133 @@
++/*
++ * output.c - Display Output Switch driver
++ *
++ * Copyright (C) 2006 Luming Yu <luming.yu@intel.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.
++ *
++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ */
++#include <linux/module.h>
++#include <linux/video_output.h>
++#include <linux/slab.h>
++#include <linux/err.h>
++#include <linux/ctype.h>
++
++
++MODULE_DESCRIPTION("Display Output Switcher Lowlevel Control Abstraction");
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Luming Yu <luming.yu@intel.com>");
++
++static ssize_t state_show(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ ssize_t ret_size = 0;
++ struct output_device *od = to_output_device(dev);
++ if (od->props)
++ ret_size = sprintf(buf,"%.8x\n",od->props->get_status(od));
++ return ret_size;
++}
++
++static ssize_t state_store(struct device *dev, struct device_attribute *attr,
++ const char *buf,size_t count)
++{
++ char *endp;
++ struct output_device *od = to_output_device(dev);
++ int request_state = simple_strtoul(buf,&endp,0);
++ size_t size = endp - buf;
++
++ if (isspace(*endp))
++ size++;
++ if (size != count)
++ return -EINVAL;
++
++ if (od->props) {
++ od->request_state = request_state;
++ od->props->set_state(od);
++ }
++ return count;
++}
++static DEVICE_ATTR_RW(state);
++
++static void video_output_release(struct device *dev)
++{
++ struct output_device *od = to_output_device(dev);
++ kfree(od);
++}
++
++static struct attribute *video_output_attrs[] = {
++ &dev_attr_state.attr,
++ NULL,
++};
++ATTRIBUTE_GROUPS(video_output);
++
++static struct class video_output_class = {
++ .name = "video_output",
++ .dev_release = video_output_release,
++ .dev_groups = video_output_groups,
++};
++
++struct output_device *video_output_register(const char *name,
++ struct device *dev,
++ void *devdata,
++ struct output_properties *op)
++{
++ struct output_device *new_dev;
++ int ret_code = 0;
++
++ new_dev = kzalloc(sizeof(struct output_device),GFP_KERNEL);
++ if (!new_dev) {
++ ret_code = -ENOMEM;
++ goto error_return;
++ }
++ new_dev->props = op;
++ new_dev->dev.class = &video_output_class;
++ new_dev->dev.parent = dev;
++ dev_set_name(&new_dev->dev, "%s", name);
++ dev_set_drvdata(&new_dev->dev, devdata);
++ ret_code = device_register(&new_dev->dev);
++ if (ret_code) {
++ kfree(new_dev);
++ goto error_return;
++ }
++ return new_dev;
++
++error_return:
++ return ERR_PTR(ret_code);
++}
++EXPORT_SYMBOL(video_output_register);
++
++void video_output_unregister(struct output_device *dev)
++{
++ if (!dev)
++ return;
++ device_unregister(&dev->dev);
++}
++EXPORT_SYMBOL(video_output_unregister);
++
++static void __exit video_output_class_exit(void)
++{
++ class_unregister(&video_output_class);
++}
++
++static int __init video_output_class_init(void)
++{
++ return class_register(&video_output_class);
++}
++
++postcore_initcall(video_output_class_init);
++module_exit(video_output_class_exit);
+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/include/linux/video_output.h b/include/linux/video_output.h
+new file mode 100644
+index 0000000..ed5cdeb
+--- /dev/null
++++ b/include/linux/video_output.h
+@@ -0,0 +1,57 @@
++/*
++ *
++ * Copyright (C) 2006 Luming Yu <luming.yu@intel.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.
++ *
++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ */
++#ifndef _LINUX_VIDEO_OUTPUT_H
++#define _LINUX_VIDEO_OUTPUT_H
++#include <linux/device.h>
++#include <linux/err.h>
++struct output_device;
++struct output_properties {
++ int (*set_state)(struct output_device *);
++ int (*get_status)(struct output_device *);
++};
++struct output_device {
++ int request_state;
++ struct output_properties *props;
++ struct device dev;
++};
++#define to_output_device(obj) container_of(obj, struct output_device, dev)
++#if defined(CONFIG_VIDEO_OUTPUT_CONTROL) || defined(CONFIG_VIDEO_OUTPUT_CONTROL_MODULE)
++struct output_device *video_output_register(const char *name,
++ struct device *dev,
++ void *devdata,
++ struct output_properties *op);
++void video_output_unregister(struct output_device *dev);
++#else
++static struct output_device *video_output_register(const char *name,
++ struct device *dev,
++ void *devdata,
++ struct output_properties *op)
++{
++ return ERR_PTR(-ENODEV);
++}
++static void video_output_unregister(struct output_device *dev)
++{
++ return;
++}
++#endif
++#endif
+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 b3b16c0..e0a91100 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/libre/linux-libre-grsec/gnewsense-binutils-flag.patch b/libre/linux-libre-grsec/gnewsense-binutils-flag.patch
new file mode 100644
index 000000000..5f2d44f99
--- /dev/null
+++ b/libre/linux-libre-grsec/gnewsense-binutils-flag.patch
@@ -0,0 +1,28 @@
+Our binutils somehow ended up with a -mfix-gs2f-kernel, rather than
+-mfix-ls2f-kernel. Cope with it.
+
+Index: arch/mips/loongson/Platform
+===================================================================
+--- arch/mips/loongson/Platform.orig 2010-10-25 19:09:49.000000000 +0000
++++ arch/mips/loongson/Platform 2010-10-25 19:10:44.000000000 +0000
+@@ -10,6 +10,12 @@
+ $(call cc-option,-march=loongson2f,-march=r4600)
+ # Enable the workarounds for Loongson2f
+ ifdef CONFIG_CPU_LOONGSON2F_WORKAROUNDS
++ ifneq ($(call as-option,-Wa$(comma)-mfix-gs2f-kernel,),)
++ cflags-$(CONFIG_CPU_NOP_WORKAROUNDS) += -Wa$(comma)-mfix-gs2f-kernel
++ ifneq ($(CONFIG_CPU_NOP_WORKAROUNDS),$(CONFIG_CPU_JUMP_WORKAROUNDS))
++ cflags-$(CONFIG_CPU_JUMP_WORKAROUNDS) += -Wa$(comma)-mfix-gs2f-kernel
++ endif
++ else
+ ifeq ($(call as-option,-Wa$(comma)-mfix-loongson2f-nop,),)
+ $(error only binutils >= 2.20.2 have needed option -mfix-loongson2f-nop)
+ else
+@@ -20,6 +26,7 @@
+ else
+ cflags-$(CONFIG_CPU_JUMP_WORKAROUNDS) += -Wa$(comma)-mfix-loongson2f-jump
+ endif
++ endif
+ endif
+
+ #
diff --git a/libre/linux-libre-lts/3.14.14-a410a5e2b7-loongson-community.patch b/libre/linux-libre-lts/3.14.14-a410a5e2b7-loongson-community.patch
new file mode 100644
index 000000000..c7a63c2e5
--- /dev/null
+++ b/libre/linux-libre-lts/3.14.14-a410a5e2b7-loongson-community.patch
@@ -0,0 +1,10869 @@
+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/libre/linux-libre-lts/gnewsense-binutils-flag.patch b/libre/linux-libre-lts/gnewsense-binutils-flag.patch
new file mode 100644
index 000000000..5f2d44f99
--- /dev/null
+++ b/libre/linux-libre-lts/gnewsense-binutils-flag.patch
@@ -0,0 +1,28 @@
+Our binutils somehow ended up with a -mfix-gs2f-kernel, rather than
+-mfix-ls2f-kernel. Cope with it.
+
+Index: arch/mips/loongson/Platform
+===================================================================
+--- arch/mips/loongson/Platform.orig 2010-10-25 19:09:49.000000000 +0000
++++ arch/mips/loongson/Platform 2010-10-25 19:10:44.000000000 +0000
+@@ -10,6 +10,12 @@
+ $(call cc-option,-march=loongson2f,-march=r4600)
+ # Enable the workarounds for Loongson2f
+ ifdef CONFIG_CPU_LOONGSON2F_WORKAROUNDS
++ ifneq ($(call as-option,-Wa$(comma)-mfix-gs2f-kernel,),)
++ cflags-$(CONFIG_CPU_NOP_WORKAROUNDS) += -Wa$(comma)-mfix-gs2f-kernel
++ ifneq ($(CONFIG_CPU_NOP_WORKAROUNDS),$(CONFIG_CPU_JUMP_WORKAROUNDS))
++ cflags-$(CONFIG_CPU_JUMP_WORKAROUNDS) += -Wa$(comma)-mfix-gs2f-kernel
++ endif
++ else
+ ifeq ($(call as-option,-Wa$(comma)-mfix-loongson2f-nop,),)
+ $(error only binutils >= 2.20.2 have needed option -mfix-loongson2f-nop)
+ else
+@@ -20,6 +26,7 @@
+ else
+ cflags-$(CONFIG_CPU_JUMP_WORKAROUNDS) += -Wa$(comma)-mfix-loongson2f-jump
+ endif
++ endif
+ endif
+
+ #
diff --git a/libre/linux-libre/3.16-7981337ad0-loongson-community.patch b/libre/linux-libre/3.16-7981337ad0-loongson-community.patch
new file mode 100644
index 000000000..0c6be8023
--- /dev/null
+++ b/libre/linux-libre/3.16-7981337ad0-loongson-community.patch
@@ -0,0 +1,11731 @@
+diff --git a/Makefile b/Makefile
+index d0901b4..d6f54c7 100644
+--- a/Makefile
++++ b/Makefile
+@@ -303,8 +303,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
+
+ ifeq ($(shell $(HOSTCC) -v 2>&1 | grep -c "clang version"), 1)
+ HOSTCFLAGS += -Wno-unused-value -Wno-unused-parameter \
+@@ -618,7 +618,7 @@ KBUILD_CFLAGS += $(call cc-option,-fno-delete-null-pointer-checks,)
+ ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
+ KBUILD_CFLAGS += -Os $(call cc-disable-warning,maybe-uninitialized,)
+ else
+-KBUILD_CFLAGS += -O2
++KBUILD_CFLAGS += -O3
+ endif
+
+ ifdef CONFIG_READABLE_ASM
+diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
+index 4e238e6..4e71740 100644
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -285,7 +285,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.
+
+@@ -919,6 +919,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
+ select MIPS_CM
+ bool
+@@ -1557,6 +1611,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
+
+@@ -2201,7 +2264,7 @@ config CPU_SUPPORTS_MSA
+
+ 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/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/mach-loongson/cs5536/cs5536.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
+index a0ee0cb..4e18add 100644
+--- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
++++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
+@@ -301,5 +301,40 @@ extern void _wrmsr(u32 msr, u32 hi, u32 lo);
+ /* GPIO : I/O SPACE; REG : 32BITS */
+ #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..50aafca 100644
+--- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
++++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
+@@ -28,8 +28,19 @@ static inline void __maybe_unused enable_mfgpt0_counter(void)
+ #define COMPARE ((MFGPT_TICK_RATE + HZ/2) / HZ)
+
+ #define MFGPT_BASE mfgpt_base
++#define MFGPT0_CMP1 (MFGPT_BASE + 0)
+ #define MFGPT0_CMP2 (MFGPT_BASE + 2)
+ #define MFGPT0_CNT (MFGPT_BASE + 4)
+ #define MFGPT0_SETUP (MFGPT_BASE + 6)
+
++#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/loongson.h b/arch/mips/include/asm/mach-loongson/loongson.h
+index f3fd1eb..3fdfe84 100644
+--- a/arch/mips/include/asm/mach-loongson/loongson.h
++++ b/arch/mips/include/asm/mach-loongson/loongson.h
+@@ -46,6 +46,12 @@ static inline void prom_init_uart_base(void)
+ #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);
+ extern void __init bonito_irq_init(void);
+diff --git a/arch/mips/include/asm/mach-loongson/machine.h b/arch/mips/include/asm/mach-loongson/machine.h
+index 1b1f592..2f6eb79 100644
+--- a/arch/mips/include/asm/mach-loongson/machine.h
++++ b/arch/mips/include/asm/mach-loongson/machine.h
+@@ -24,6 +24,12 @@
+
+ #endif
+
++#ifdef CONFIG_DEXXON_GDIUM
++
++#define LOONGSON_MACHTYPE MACH_DEXXON_GDIUM2F10
++
++#endif
++
+ #ifdef CONFIG_LEMOTE_MACH3A
+
+ #define LOONGSON_MACHTYPE MACH_LEMOTE_A1101
+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/timex.h b/arch/mips/include/asm/timex.h
+index b05bb70..44c9a69 100644
+--- a/arch/mips/include/asm/timex.h
++++ b/arch/mips/include/asm/timex.h
+@@ -11,6 +11,10 @@
+
+ #ifdef __KERNEL__
+
++#ifdef CONFIG_CSRC_R4K
++#define ARCH_HAS_PREPARED_LPJ
++#endif
++
+ #include <linux/compiler.h>
+
+ #include <asm/cpu.h>
+diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h
+index 4bfdb9d..f57d892 100644
+--- a/arch/mips/include/uapi/asm/inst.h
++++ b/arch/mips/include/uapi/asm/inst.h
+@@ -65,6 +65,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
+@@ -146,7 +148,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
+ };
+
+ /*
+@@ -175,7 +177,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 f1343cc..1320561 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 8d01709..9cd25da 100644
+--- a/arch/mips/kernel/time.c
++++ b/arch/mips/kernel/time.c
+@@ -119,6 +119,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 e6a86cc..df57920 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,14 +45,13 @@ 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.
+@@ -60,6 +59,31 @@ config LEMOTE_MACH2F
+ 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.
++
+ config LEMOTE_MACH3A
+ bool "Lemote Loongson 3A family machines"
+ select ARCH_SPARSEMEM_ENABLE
+@@ -134,6 +158,24 @@ 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.
++
+ config LEFI_FIRMWARE_INTERFACE
+ bool
+
+diff --git a/arch/mips/loongson/Makefile b/arch/mips/loongson/Makefile
+index 7429994..63214c8 100644
+--- a/arch/mips/loongson/Makefile
++++ b/arch/mips/loongson/Makefile
+@@ -17,6 +17,12 @@ 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/
++
++#
+ # All Loongson-3 family machines
+ #
+
+diff --git a/arch/mips/loongson/Platform b/arch/mips/loongson/Platform
+index 6205372..32dd006 100644
+--- a/arch/mips/loongson/Platform
++++ b/arch/mips/loongson/Platform
+@@ -30,4 +30,5 @@ 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
+ load-$(CONFIG_CPU_LOONGSON3) += 0xffffffff80200000
+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/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/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/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
+index 736c17a..d6b6cac 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.
+@@ -59,6 +62,11 @@ 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 *);
+
++#ifdef CONFIG_MACH_LOONGSON
++static int loongson_spec2_emu(struct pt_regs *,
++ struct mips_fpu_struct *, mips_instruction, void *__user *);
++#endif
++
+ /* Control registers */
+
+ #define FPCREG_RID 0 /* $0 = revision id */
+@@ -694,6 +702,14 @@ do { \
+ #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.
+@@ -1087,6 +1103,16 @@ emul:
+ xcp->regs[MIPSInst_RD(ir)] =
+ xcp->regs[MIPSInst_RS(ir)];
+ break;
++
++#ifdef CONFIG_MACH_LOONGSON
++ case spec2_op:{
++ int sig = loongson_spec2_emu(xcp, ctx, ir, fault_addr);
++ if (sig)
++ return sig;
++ break;
++ }
++#endif
++
+ default:
+ sigill:
+ return SIGILL;
+@@ -1164,6 +1190,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 CONFIG_MACH_LOONGSON
++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 {
++ union ieee754dp d;
++ struct {
++ union ieee754sp s;
++ union 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:{
++ union ieee754sp(*handler) (union ieee754sp, union ieee754sp, union ieee754sp);
++ union 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:{
++ union ieee754dp(*handler) (union ieee754dp, union ieee754dp, union ieee754dp);
++ union 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:{
++ union ieee754sp(*handler) (union ieee754sp, union ieee754sp, union ieee754sp);
++ struct _ieee754_csr ieee754_csr_save;
++ union ieee754sp fd1, fs1, ft1;
++ union 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)
+ {
+@@ -1265,7 +1457,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ break;
+
+ default:
+- return SIGILL;
++ goto SIGILL_unless_prefx_op;
+ }
+ break;
+ }
+@@ -1335,7 +1527,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;
+ }
+@@ -1348,6 +1540,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;
+ }
+
+@@ -1369,7 +1566,12 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
+ unsigned cond;
+ union {
+ union ieee754dp d;
+- union ieee754sp s;
++ struct {
++ union ieee754sp s;
++#ifdef CONFIG_MACH_LOONGSON
++ union ieee754sp s2; /* for Loongson paired singles */
++#endif
++ };
+ int w;
+ s64 l;
+ } rv; /* resulting value */
+@@ -1466,7 +1668,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:
+@@ -1663,7 +1865,7 @@ copcsr:
+ case fmov_op:
+ /* an easy one */
+ DPFROMREG(rv.d, MIPSInst_FS(ir));
+- goto copcsr;
++ break;
+
+ /* binary op on handler */
+ dcopbop:
+@@ -1780,6 +1982,83 @@ dcopuop:
+ break;
+ }
+
++#ifdef CONFIG_MACH_LOONGSON
++ case ps_fmt:{ /* 6 */
++ /* Support for Loongson paired single fp instructions */
++ union {
++ union ieee754sp(*b) (union ieee754sp, union ieee754sp);
++ union ieee754sp(*u) (union 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;
++ union ieee754sp fs1, ft1;
++ union 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;
++ union ieee754sp fs1;
++ union 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 l_fmt:
+
+ if (!cpu_has_mips_3_4_5 && !cpu_has_mips64)
+@@ -1851,6 +2130,11 @@ dcopuop:
+
+ DITOREG(rv.l, MIPSInst_FD(ir));
+ break;
++#ifdef CONFIG_MACH_LOONGSON
++ case ps_fmt:
++ PSPTOREG(rv.s, rv.s2, MIPSInst_FD(ir));
++ break;
++#endif
+ default:
+ return SIGILL;
+ }
+diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile
+index ff8a553..a747755 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_LEMOTE_MACH3A) += fixup-loongson3.o ops-loongson3.o
+ obj-$(CONFIG_MIPS_MALTA) += fixup-malta.o pci-malta.o
+ obj-$(CONFIG_PMC_MSP7120_GW) += 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/hid/Kconfig b/drivers/hid/Kconfig
+index 5e79c6a..3f08f0d 100644
+--- a/drivers/hid/Kconfig
++++ b/drivers/hid/Kconfig
+@@ -808,6 +808,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 && HAS_IOMEM
+diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
+index a6fa6ba..cf8793ef 100644
+--- a/drivers/hid/Makefile
++++ b/drivers/hid/Makefile
+@@ -117,6 +117,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 48b66bb..f5b5e46 100644
+--- a/drivers/hid/hid-ids.h
++++ b/drivers/hid/hid-ids.h
+@@ -980,6 +980,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/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 81e6d09..929365e 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 0754f5c7..ff7475d 100644
+--- a/drivers/rtc/Kconfig
++++ b/drivers/rtc/Kconfig
+@@ -680,6 +680,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/Kconfig b/drivers/staging/Kconfig
+index 4f38fc0..f57367c 100644
+--- a/drivers/staging/Kconfig
++++ b/drivers/staging/Kconfig
+@@ -80,6 +80,8 @@ source "drivers/staging/wlags49_h2/Kconfig"
+
+ source "drivers/staging/wlags49_h25/Kconfig"
+
++source "drivers/staging/sm7xxfb/Kconfig"
++
+ source "drivers/staging/crystalhd/Kconfig"
+
+ source "drivers/staging/cxt1e1/Kconfig"
+diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
+index 1e97ad2..65a1ff0 100644
+--- a/drivers/staging/Makefile
++++ b/drivers/staging/Makefile
+@@ -34,6 +34,7 @@ obj-$(CONFIG_DX_SEP) += sep/
+ obj-$(CONFIG_IIO) += iio/
+ obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
+ obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
++obj-$(CONFIG_FB_SM7XX) += sm7xxfb/
+ obj-$(CONFIG_CRYSTALHD) += crystalhd/
+ obj-$(CONFIG_CXT1E1) += cxt1e1/
+ obj-$(CONFIG_FB_XGI) += xgifb/
+diff --git a/drivers/staging/sm7xxfb/Kconfig b/drivers/staging/sm7xxfb/Kconfig
+new file mode 100644
+index 0000000..e2922ae
+--- /dev/null
++++ b/drivers/staging/sm7xxfb/Kconfig
+@@ -0,0 +1,13 @@
++config FB_SM7XX
++ tristate "Silicon Motion SM7XX framebuffer support"
++ depends on FB && PCI
++ select FB_CFB_FILLRECT
++ select FB_CFB_COPYAREA
++ select FB_CFB_IMAGEBLIT
++ help
++ Frame buffer driver for the Silicon Motion SM710, SM712, SM721
++ and SM722 chips.
++
++ This driver is also available as a module. The module will be
++ called sm7xxfb. If you want to compile it as a module, say M
++ here and read <file:Documentation/kbuild/modules.txt>.
+diff --git a/drivers/staging/sm7xxfb/Makefile b/drivers/staging/sm7xxfb/Makefile
+new file mode 100644
+index 0000000..48f471c
+--- /dev/null
++++ b/drivers/staging/sm7xxfb/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_FB_SM7XX) += sm7xxfb.o
+diff --git a/drivers/staging/sm7xxfb/TODO b/drivers/staging/sm7xxfb/TODO
+new file mode 100644
+index 0000000..1fcead5
+--- /dev/null
++++ b/drivers/staging/sm7xxfb/TODO
+@@ -0,0 +1,9 @@
++TODO:
++- Dual head support
++- 2D acceleration support
++- use kernel coding style
++- refine the code and remove unused code
++- move it to drivers/video/sm7xxfb.c
++
++Please send any patches to Greg Kroah-Hartman <greg@kroah.com> and
++Teddy Wang <teddy.wang@siliconmotion.com.cn>.
+diff --git a/drivers/staging/sm7xxfb/sm7xx.h b/drivers/staging/sm7xxfb/sm7xx.h
+new file mode 100644
+index 0000000..8599861
+--- /dev/null
++++ b/drivers/staging/sm7xxfb/sm7xx.h
+@@ -0,0 +1,779 @@
++/*
++ * Silicon Motion SM712 frame buffer device
++ *
++ * Copyright (C) 2006 Silicon Motion Technology Corp.
++ * Authors: Ge Wang, gewang@siliconmotion.com
++ * Boyod boyod.yang@siliconmotion.com.cn
++ *
++ * Copyright (C) 2009 Lemote, Inc.
++ * Author: Wu Zhangjin, wuzhangjin@gmail.com
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ */
++
++#define NR_PALETTE 256
++
++#define FB_ACCEL_SMI_LYNX 88
++
++#define SCREEN_X_RES 1024
++#define SCREEN_Y_RES 600
++#define SCREEN_BPP 16
++
++/*Assume SM712 graphics chip has 4MB VRAM */
++#define SM712_VIDEOMEMORYSIZE 0x00400000
++/*Assume SM722 graphics chip has 8MB VRAM */
++#define SM722_VIDEOMEMORYSIZE 0x00800000
++
++#define dac_reg (0x3c8)
++#define dac_val (0x3c9)
++
++extern void __iomem *smtc_RegBaseAddress;
++#define smtc_mmiowb(dat, reg) writeb(dat, smtc_RegBaseAddress + reg)
++#define smtc_mmioww(dat, reg) writew(dat, smtc_RegBaseAddress + reg)
++#define smtc_mmiowl(dat, reg) writel(dat, smtc_RegBaseAddress + reg)
++
++#define smtc_mmiorb(reg) readb(smtc_RegBaseAddress + reg)
++#define smtc_mmiorw(reg) readw(smtc_RegBaseAddress + reg)
++#define smtc_mmiorl(reg) readl(smtc_RegBaseAddress + reg)
++
++#define SIZE_SR00_SR04 (0x04 - 0x00 + 1)
++#define SIZE_SR10_SR24 (0x24 - 0x10 + 1)
++#define SIZE_SR30_SR75 (0x75 - 0x30 + 1)
++#define SIZE_SR80_SR93 (0x93 - 0x80 + 1)
++#define SIZE_SRA0_SRAF (0xAF - 0xA0 + 1)
++#define SIZE_GR00_GR08 (0x08 - 0x00 + 1)
++#define SIZE_AR00_AR14 (0x14 - 0x00 + 1)
++#define SIZE_CR00_CR18 (0x18 - 0x00 + 1)
++#define SIZE_CR30_CR4D (0x4D - 0x30 + 1)
++#define SIZE_CR90_CRA7 (0xA7 - 0x90 + 1)
++#define SIZE_VPR (0x6C + 1)
++#define SIZE_DPR (0x44 + 1)
++
++static inline void smtc_crtcw(int reg, int val)
++{
++ smtc_mmiowb(reg, 0x3d4);
++ smtc_mmiowb(val, 0x3d5);
++}
++
++static inline unsigned int smtc_crtcr(int reg)
++{
++ smtc_mmiowb(reg, 0x3d4);
++ return smtc_mmiorb(0x3d5);
++}
++
++static inline void smtc_grphw(int reg, int val)
++{
++ smtc_mmiowb(reg, 0x3ce);
++ smtc_mmiowb(val, 0x3cf);
++}
++
++static inline unsigned int smtc_grphr(int reg)
++{
++ smtc_mmiowb(reg, 0x3ce);
++ return smtc_mmiorb(0x3cf);
++}
++
++static inline void smtc_attrw(int reg, int val)
++{
++ smtc_mmiorb(0x3da);
++ smtc_mmiowb(reg, 0x3c0);
++ smtc_mmiorb(0x3c1);
++ smtc_mmiowb(val, 0x3c0);
++}
++
++static inline void smtc_seqw(int reg, int val)
++{
++ smtc_mmiowb(reg, 0x3c4);
++ smtc_mmiowb(val, 0x3c5);
++}
++
++static inline unsigned int smtc_seqr(int reg)
++{
++ smtc_mmiowb(reg, 0x3c4);
++ return smtc_mmiorb(0x3c5);
++}
++
++/* The next structure holds all information relevant for a specific video mode.
++ */
++
++struct ModeInit {
++ int mmSizeX;
++ int mmSizeY;
++ int bpp;
++ int hz;
++ unsigned char Init_MISC;
++ unsigned char Init_SR00_SR04[SIZE_SR00_SR04];
++ unsigned char Init_SR10_SR24[SIZE_SR10_SR24];
++ unsigned char Init_SR30_SR75[SIZE_SR30_SR75];
++ unsigned char Init_SR80_SR93[SIZE_SR80_SR93];
++ unsigned char Init_SRA0_SRAF[SIZE_SRA0_SRAF];
++ unsigned char Init_GR00_GR08[SIZE_GR00_GR08];
++ unsigned char Init_AR00_AR14[SIZE_AR00_AR14];
++ unsigned char Init_CR00_CR18[SIZE_CR00_CR18];
++ unsigned char Init_CR30_CR4D[SIZE_CR30_CR4D];
++ unsigned char Init_CR90_CRA7[SIZE_CR90_CRA7];
++};
++
++/**********************************************************************
++ SM712 Mode table.
++ **********************************************************************/
++struct ModeInit VGAMode[] = {
++ {
++ /* mode#0: 640 x 480 16Bpp 60Hz */
++ 640, 480, 16, 60,
++ /* Init_MISC */
++ 0xE3,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x00, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
++ 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
++ 0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
++ 0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
++ 0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
++ 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
++ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
++ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
++ 0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
++ 0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
++ 0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
++ 0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
++ },
++ },
++ {
++ /* mode#1: 640 x 480 24Bpp 60Hz */
++ 640, 480, 24, 60,
++ /* Init_MISC */
++ 0xE3,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x00, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
++ 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
++ 0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
++ 0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
++ 0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
++ 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
++ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
++ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
++ 0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
++ 0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
++ 0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
++ 0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
++ },
++ },
++ {
++ /* mode#0: 640 x 480 32Bpp 60Hz */
++ 640, 480, 32, 60,
++ /* Init_MISC */
++ 0xE3,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x00, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
++ 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
++ 0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
++ 0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
++ 0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
++ 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
++ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
++ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
++ 0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
++ 0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
++ 0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
++ 0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
++ },
++ },
++
++ { /* mode#2: 800 x 600 16Bpp 60Hz */
++ 800, 600, 16, 60,
++ /* Init_MISC */
++ 0x2B,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x34, 0x03, 0x20, 0x09, 0xC0, 0x24, 0x24, 0x24,
++ 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x38, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x24, 0x24, 0x24,
++ 0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
++ 0x04, 0x55, 0x59, 0x24, 0x24, 0x00, 0x00, 0x24,
++ 0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
++ 0x02, 0x45, 0x30, 0x35, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0x00, 0x00, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x24,
++ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x24, 0x24,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
++ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
++ 0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
++ 0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
++ 0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
++ 0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
++ },
++ },
++ { /* mode#3: 800 x 600 24Bpp 60Hz */
++ 800, 600, 24, 60,
++ 0x2B,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x36, 0x03, 0x20, 0x09, 0xC0, 0x36, 0x36, 0x36,
++ 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x36, 0x36, 0x36,
++ 0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
++ 0x04, 0x55, 0x59, 0x36, 0x36, 0x00, 0x00, 0x36,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
++ 0x02, 0x45, 0x30, 0x30, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x36,
++ 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x36, 0x36,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
++ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
++ 0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
++ 0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
++ 0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
++ 0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
++ },
++ },
++ { /* mode#7: 800 x 600 32Bpp 60Hz */
++ 800, 600, 32, 60,
++ /* Init_MISC */
++ 0x2B,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x34, 0x03, 0x20, 0x09, 0xC0, 0x24, 0x24, 0x24,
++ 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x38, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x24, 0x24, 0x24,
++ 0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
++ 0x04, 0x55, 0x59, 0x24, 0x24, 0x00, 0x00, 0x24,
++ 0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
++ 0x02, 0x45, 0x30, 0x35, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0x00, 0x00, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x24,
++ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x24, 0x24,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
++ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
++ 0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
++ 0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
++ 0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
++ 0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
++ },
++ },
++ /* We use 1024x768 table to light 1024x600 panel for lemote */
++ { /* mode#4: 1024 x 600 16Bpp 60Hz */
++ 1024, 600, 16, 60,
++ /* Init_MISC */
++ 0xEB,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x00, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xC8, 0x40, 0x14, 0x60, 0x00, 0x0A, 0x17, 0x20,
++ 0x51, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x00, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x22, 0x03, 0x24, 0x09, 0xC0, 0x22, 0x22, 0x22,
++ 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x22, 0x22, 0x22,
++ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
++ 0x00, 0x60, 0x59, 0x22, 0x22, 0x00, 0x00, 0x22,
++ 0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x16, 0x02, 0x0D, 0x82, 0x09, 0x02,
++ 0x04, 0x45, 0x3F, 0x30, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
++ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
++ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
++ 0xA3, 0x7F, 0x00, 0x82, 0x0b, 0x6f, 0x57, 0x00,
++ 0x5c, 0x0f, 0xE0, 0xe0, 0x7F, 0x57,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
++ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
++ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
++ },
++ },
++ { /* mode#5: 1024 x 768 24Bpp 60Hz */
++ 1024, 768, 24, 60,
++ /* Init_MISC */
++ 0xEB,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x30, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
++ 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
++ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
++ 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x3B, 0x0D, 0x09, 0x02,
++ 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
++ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
++ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
++ 0xA3, 0x7F, 0x00, 0x86, 0x15, 0x24, 0xFF, 0x00,
++ 0x01, 0x07, 0xE5, 0x20, 0x7F, 0xFF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
++ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
++ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
++ },
++ },
++ { /* mode#4: 1024 x 768 32Bpp 60Hz */
++ 1024, 768, 32, 60,
++ /* Init_MISC */
++ 0xEB,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x32, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
++ 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
++ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
++ 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x3B, 0x0D, 0x09, 0x02,
++ 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
++ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
++ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
++ 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
++ 0xA3, 0x7F, 0x00, 0x86, 0x15, 0x24, 0xFF, 0x00,
++ 0x01, 0x07, 0xE5, 0x20, 0x7F, 0xFF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
++ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
++ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
++ },
++ },
++ { /* mode#6: 320 x 240 16Bpp 60Hz */
++ 320, 240, 16, 60,
++ /* Init_MISC */
++ 0xEB,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x32, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
++ 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
++ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
++ 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x08, 0x43, 0x08, 0x43,
++ 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
++ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
++ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
++ 0x00, 0x00, 0x30, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
++ 0x2E, 0x27, 0x00, 0x2b, 0x0c, 0x0F, 0xEF, 0x00,
++ 0xFe, 0x0f, 0x01, 0xC0, 0x27, 0xEF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
++ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
++ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
++ },
++ },
++
++ { /* mode#8: 320 x 240 32Bpp 60Hz */
++ 320, 240, 32, 60,
++ /* Init_MISC */
++ 0xEB,
++ { /* Init_SR0_SR4 */
++ 0x03, 0x01, 0x0F, 0x03, 0x0E,
++ },
++ { /* Init_SR10_SR24 */
++ 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
++ 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0xC4, 0x32, 0x02, 0x01, 0x01,
++ },
++ { /* Init_SR30_SR75 */
++ 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
++ 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
++ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
++ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
++ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
++ 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
++ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
++ 0x50, 0x03, 0x74, 0x14, 0x08, 0x43, 0x08, 0x43,
++ 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
++ },
++ { /* Init_SR80_SR93 */
++ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
++ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
++ 0x00, 0x00, 0x00, 0x00,
++ },
++ { /* Init_SRA0_SRAF */
++ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
++ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
++ },
++ { /* Init_GR00_GR08 */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
++ 0xFF,
++ },
++ { /* Init_AR00_AR14 */
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
++ 0x41, 0x00, 0x0F, 0x00, 0x00,
++ },
++ { /* Init_CR00_CR18 */
++ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
++ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
++ 0xFF,
++ },
++ { /* Init_CR30_CR4D */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
++ 0x00, 0x00, 0x30, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
++ 0x2E, 0x27, 0x00, 0x2b, 0x0c, 0x0F, 0xEF, 0x00,
++ 0xFe, 0x0f, 0x01, 0xC0, 0x27, 0xEF,
++ },
++ { /* Init_CR90_CRA7 */
++ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
++ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
++ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
++ },
++ },
++};
++
++#define numVGAModes ARRAY_SIZE(VGAMode)
+diff --git a/drivers/staging/sm7xxfb/sm7xxfb.c b/drivers/staging/sm7xxfb/sm7xxfb.c
+new file mode 100644
+index 0000000..e40ce80
+--- /dev/null
++++ b/drivers/staging/sm7xxfb/sm7xxfb.c
+@@ -0,0 +1,1029 @@
++/*
++ * Silicon Motion SM7XX frame buffer device
++ *
++ * Copyright (C) 2006 Silicon Motion Technology Corp.
++ * Authors: Ge Wang, gewang@siliconmotion.com
++ * Boyod boyod.yang@siliconmotion.com.cn
++ *
++ * Copyright (C) 2009 Lemote, Inc.
++ * Author: Wu Zhangjin, wuzhangjin@gmail.com
++ *
++ * Copyright (C) 2011 Igalia, S.L.
++ * Author: Javier M. Mellid <jmunhoz@igalia.com>
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ *
++ * Framebuffer driver for Silicon Motion SM710, SM712, SM721 and SM722 chips
++ */
++
++#include <linux/io.h>
++#include <linux/fb.h>
++#include <linux/pci.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/module.h>
++#include <linux/console.h>
++#include <linux/screen_info.h>
++
++#ifdef CONFIG_PM
++#include <linux/pm.h>
++#endif
++
++#include "sm7xx.h"
++
++/*
++* Private structure
++*/
++struct smtcfb_info {
++ struct pci_dev *pdev;
++ struct fb_info fb;
++ u16 chip_id;
++ u8 chip_rev_id;
++
++ void __iomem *lfb; /* linear frame buffer */
++ void __iomem *dp_regs; /* drawing processor control regs */
++ void __iomem *vp_regs; /* video processor control regs */
++ void __iomem *cp_regs; /* capture processor control regs */
++ void __iomem *mmio; /* memory map IO port */
++
++ u_int width;
++ u_int height;
++ u_int hz;
++
++ u32 colreg[17];
++};
++
++void __iomem *smtc_RegBaseAddress; /* Memory Map IO starting address */
++
++static struct fb_var_screeninfo smtcfb_var = {
++ .xres = 1024,
++ .yres = 600,
++ .xres_virtual = 1024,
++ .yres_virtual = 600,
++ .bits_per_pixel = 16,
++ .red = {16, 8, 0},
++ .green = {8, 8, 0},
++ .blue = {0, 8, 0},
++ .activate = FB_ACTIVATE_NOW,
++ .height = -1,
++ .width = -1,
++ .vmode = FB_VMODE_NONINTERLACED,
++ .nonstd = 0,
++ .accel_flags = FB_ACCELF_TEXT,
++};
++
++static struct fb_fix_screeninfo smtcfb_fix = {
++ .id = "smXXXfb",
++ .type = FB_TYPE_PACKED_PIXELS,
++ .visual = FB_VISUAL_TRUECOLOR,
++ .line_length = 800 * 3,
++ .accel = FB_ACCEL_SMI_LYNX,
++ .type_aux = 0,
++ .xpanstep = 0,
++ .ypanstep = 0,
++ .ywrapstep = 0,
++};
++
++struct vesa_mode {
++ char index[6];
++ u16 lfb_width;
++ u16 lfb_height;
++ u16 lfb_depth;
++};
++
++static struct vesa_mode vesa_mode_table[] = {
++ {"0x301", 640, 480, 8},
++ {"0x303", 800, 600, 8},
++ {"0x305", 1024, 768, 8},
++ {"0x307", 1280, 1024, 8},
++
++ {"0x311", 640, 480, 16},
++ {"0x313", 800, 480, 16},
++ {"0x314", 800, 600, 16},
++ {"0x317", 1024, 768, 16},
++ {"0x31A", 1280, 1024, 16},
++
++ {"0x312", 640, 480, 24},
++ {"0x315", 800, 600, 24},
++ {"0x318", 1024, 768, 24},
++ {"0x31B", 1280, 1024, 24},
++};
++
++struct screen_info smtc_scr_info;
++
++/* process command line options, get vga parameter */
++static int __init sm7xx_vga_setup(char *options)
++{
++ int i;
++
++ if (!options || !*options)
++ return -EINVAL;
++
++ smtc_scr_info.lfb_width = 0;
++ smtc_scr_info.lfb_height = 0;
++ smtc_scr_info.lfb_depth = 0;
++
++ pr_debug("sm7xx_vga_setup = %s\n", options);
++
++ for (i = 0; i < ARRAY_SIZE(vesa_mode_table); i++) {
++ if (strstr(options, vesa_mode_table[i].index)) {
++ smtc_scr_info.lfb_width = vesa_mode_table[i].lfb_width;
++ smtc_scr_info.lfb_height =
++ vesa_mode_table[i].lfb_height;
++ smtc_scr_info.lfb_depth = vesa_mode_table[i].lfb_depth;
++ return 0;
++ }
++ }
++
++ return -1;
++}
++__setup("vga=", sm7xx_vga_setup);
++
++static void sm712_setpalette(int regno, unsigned red, unsigned green,
++ unsigned blue, struct fb_info *info)
++{
++ /* set bit 5:4 = 01 (write LCD RAM only) */
++ smtc_seqw(0x66, (smtc_seqr(0x66) & 0xC3) | 0x10);
++
++ smtc_mmiowb(regno, dac_reg);
++ smtc_mmiowb(red >> 10, dac_val);
++ smtc_mmiowb(green >> 10, dac_val);
++ smtc_mmiowb(blue >> 10, dac_val);
++}
++
++/* chan_to_field
++ *
++ * convert a colour value into a field position
++ *
++ * from pxafb.c
++ */
++
++static inline unsigned int chan_to_field(unsigned int chan,
++ struct fb_bitfield *bf)
++{
++ chan &= 0xffff;
++ chan >>= 16 - bf->length;
++ return chan << bf->offset;
++}
++
++static int smtc_blank(int blank_mode, struct fb_info *info)
++{
++ /* clear DPMS setting */
++ switch (blank_mode) {
++ case FB_BLANK_UNBLANK:
++ /* Screen On: HSync: On, VSync : On */
++ smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20)));
++ smtc_seqw(0x6a, 0x16);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x21, (smtc_seqr(0x21) & 0x77));
++ smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30)));
++ smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0)));
++ smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01));
++ smtc_seqw(0x31, (smtc_seqr(0x31) | 0x03));
++ break;
++ case FB_BLANK_NORMAL:
++ /* Screen Off: HSync: On, VSync : On Soft blank */
++ smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20)));
++ smtc_seqw(0x6a, 0x16);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30)));
++ smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0)));
++ smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01));
++ smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
++ break;
++ case FB_BLANK_VSYNC_SUSPEND:
++ /* Screen On: HSync: On, VSync : Off */
++ smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
++ smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
++ smtc_seqw(0x6a, 0x0c);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
++ smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x20));
++ smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0x20));
++ smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
++ smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
++ smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
++ break;
++ case FB_BLANK_HSYNC_SUSPEND:
++ /* Screen On: HSync: Off, VSync : On */
++ smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
++ smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
++ smtc_seqw(0x6a, 0x0c);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
++ smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x10));
++ smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8));
++ smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
++ smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
++ smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
++ break;
++ case FB_BLANK_POWERDOWN:
++ /* Screen On: HSync: Off, VSync : Off */
++ smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
++ smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
++ smtc_seqw(0x6a, 0x0c);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
++ smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x30));
++ smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8));
++ smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
++ smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
++ smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int smtc_setcolreg(unsigned regno, unsigned red, unsigned green,
++ unsigned blue, unsigned trans, struct fb_info *info)
++{
++ struct smtcfb_info *sfb;
++ u32 val;
++
++ sfb = info->par;
++
++ if (regno > 255)
++ return 1;
++
++ switch (sfb->fb.fix.visual) {
++ case FB_VISUAL_DIRECTCOLOR:
++ case FB_VISUAL_TRUECOLOR:
++ /*
++ * 16/32 bit true-colour, use pseudo-palette for 16 base color
++ */
++ if (regno < 16) {
++ if (sfb->fb.var.bits_per_pixel == 16) {
++ u32 *pal = sfb->fb.pseudo_palette;
++ val = chan_to_field(red, &sfb->fb.var.red);
++ val |= chan_to_field(green, &sfb->fb.var.green);
++ val |= chan_to_field(blue, &sfb->fb.var.blue);
++#ifdef __BIG_ENDIAN
++ pal[regno] =
++ ((red & 0xf800) >> 8) |
++ ((green & 0xe000) >> 13) |
++ ((green & 0x1c00) << 3) |
++ ((blue & 0xf800) >> 3);
++#else
++ pal[regno] = val;
++#endif
++ } else {
++ u32 *pal = sfb->fb.pseudo_palette;
++ val = chan_to_field(red, &sfb->fb.var.red);
++ val |= chan_to_field(green, &sfb->fb.var.green);
++ val |= chan_to_field(blue, &sfb->fb.var.blue);
++#ifdef __BIG_ENDIAN
++ val =
++ (val & 0xff00ff00 >> 8) |
++ (val & 0x00ff00ff << 8);
++#endif
++ pal[regno] = val;
++ }
++ }
++ break;
++
++ case FB_VISUAL_PSEUDOCOLOR:
++ /* color depth 8 bit */
++ sm712_setpalette(regno, red, green, blue, info);
++ break;
++
++ default:
++ return 1; /* unknown type */
++ }
++
++ return 0;
++
++}
++
++#ifdef __BIG_ENDIAN
++static ssize_t smtcfb_read(struct fb_info *info, char __user *buf, size_t
++ count, loff_t *ppos)
++{
++ unsigned long p = *ppos;
++
++ u32 *buffer, *dst;
++ u32 __iomem *src;
++ int c, i, cnt = 0, err = 0;
++ unsigned long total_size;
++
++ if (!info || !info->screen_base)
++ return -ENODEV;
++
++ if (info->state != FBINFO_STATE_RUNNING)
++ return -EPERM;
++
++ total_size = info->screen_size;
++
++ if (total_size == 0)
++ total_size = info->fix.smem_len;
++
++ if (p >= total_size)
++ return 0;
++
++ if (count >= total_size)
++ count = total_size;
++
++ if (count + p > total_size)
++ count = total_size - p;
++
++ buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL);
++ if (!buffer)
++ return -ENOMEM;
++
++ src = (u32 __iomem *) (info->screen_base + p);
++
++ if (info->fbops->fb_sync)
++ info->fbops->fb_sync(info);
++
++ while (count) {
++ c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
++ dst = buffer;
++ for (i = c >> 2; i--;) {
++ *dst = fb_readl(src++);
++ *dst =
++ (*dst & 0xff00ff00 >> 8) |
++ (*dst & 0x00ff00ff << 8);
++ dst++;
++ }
++ if (c & 3) {
++ u8 *dst8 = (u8 *) dst;
++ u8 __iomem *src8 = (u8 __iomem *) src;
++
++ for (i = c & 3; i--;) {
++ if (i & 1) {
++ *dst8++ = fb_readb(++src8);
++ } else {
++ *dst8++ = fb_readb(--src8);
++ src8 += 2;
++ }
++ }
++ src = (u32 __iomem *) src8;
++ }
++
++ if (copy_to_user(buf, buffer, c)) {
++ err = -EFAULT;
++ break;
++ }
++ *ppos += c;
++ buf += c;
++ cnt += c;
++ count -= c;
++ }
++
++ kfree(buffer);
++
++ return (err) ? err : cnt;
++}
++
++static ssize_t
++smtcfb_write(struct fb_info *info, const char __user *buf, size_t count,
++ loff_t *ppos)
++{
++ unsigned long p = *ppos;
++
++ u32 *buffer, *src;
++ u32 __iomem *dst;
++ int c, i, cnt = 0, err = 0;
++ unsigned long total_size;
++
++ if (!info || !info->screen_base)
++ return -ENODEV;
++
++ if (info->state != FBINFO_STATE_RUNNING)
++ return -EPERM;
++
++ total_size = info->screen_size;
++
++ if (total_size == 0)
++ total_size = info->fix.smem_len;
++
++ if (p > total_size)
++ return -EFBIG;
++
++ if (count > total_size) {
++ err = -EFBIG;
++ count = total_size;
++ }
++
++ if (count + p > total_size) {
++ if (!err)
++ err = -ENOSPC;
++
++ count = total_size - p;
++ }
++
++ buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL);
++ if (!buffer)
++ return -ENOMEM;
++
++ dst = (u32 __iomem *) (info->screen_base + p);
++
++ if (info->fbops->fb_sync)
++ info->fbops->fb_sync(info);
++
++ while (count) {
++ c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
++ src = buffer;
++
++ if (copy_from_user(src, buf, c)) {
++ err = -EFAULT;
++ break;
++ }
++
++ for (i = c >> 2; i--;) {
++ fb_writel((*src & 0xff00ff00 >> 8) |
++ (*src & 0x00ff00ff << 8), dst++);
++ src++;
++ }
++ if (c & 3) {
++ u8 *src8 = (u8 *) src;
++ u8 __iomem *dst8 = (u8 __iomem *) dst;
++
++ for (i = c & 3; i--;) {
++ if (i & 1) {
++ fb_writeb(*src8++, ++dst8);
++ } else {
++ fb_writeb(*src8++, --dst8);
++ dst8 += 2;
++ }
++ }
++ dst = (u32 __iomem *) dst8;
++ }
++
++ *ppos += c;
++ buf += c;
++ cnt += c;
++ count -= c;
++ }
++
++ kfree(buffer);
++
++ return (cnt) ? cnt : err;
++}
++#endif /* ! __BIG_ENDIAN */
++
++static void sm7xx_set_timing(struct smtcfb_info *sfb)
++{
++ int i = 0, j = 0;
++ u32 m_nScreenStride;
++
++ dev_dbg(&sfb->pdev->dev,
++ "sfb->width=%d sfb->height=%d "
++ "sfb->fb.var.bits_per_pixel=%d sfb->hz=%d\n",
++ sfb->width, sfb->height, sfb->fb.var.bits_per_pixel, sfb->hz);
++
++ for (j = 0; j < numVGAModes; j++) {
++ if (VGAMode[j].mmSizeX == sfb->width &&
++ VGAMode[j].mmSizeY == sfb->height &&
++ VGAMode[j].bpp == sfb->fb.var.bits_per_pixel &&
++ VGAMode[j].hz == sfb->hz) {
++
++ dev_dbg(&sfb->pdev->dev,
++ "VGAMode[j].mmSizeX=%d VGAMode[j].mmSizeY=%d "
++ "VGAMode[j].bpp=%d VGAMode[j].hz=%d\n",
++ VGAMode[j].mmSizeX, VGAMode[j].mmSizeY,
++ VGAMode[j].bpp, VGAMode[j].hz);
++
++ dev_dbg(&sfb->pdev->dev, "VGAMode index=%d\n", j);
++
++ smtc_mmiowb(0x0, 0x3c6);
++
++ smtc_seqw(0, 0x1);
++
++ smtc_mmiowb(VGAMode[j].Init_MISC, 0x3c2);
++
++ /* init SEQ register SR00 - SR04 */
++ for (i = 0; i < SIZE_SR00_SR04; i++)
++ smtc_seqw(i, VGAMode[j].Init_SR00_SR04[i]);
++
++ /* init SEQ register SR10 - SR24 */
++ for (i = 0; i < SIZE_SR10_SR24; i++)
++ smtc_seqw(i + 0x10,
++ VGAMode[j].Init_SR10_SR24[i]);
++
++ /* init SEQ register SR30 - SR75 */
++ for (i = 0; i < SIZE_SR30_SR75; i++)
++ if ((i + 0x30) != 0x62 &&
++ (i + 0x30) != 0x6a &&
++ (i + 0x30) != 0x6b)
++ smtc_seqw(i + 0x30,
++ VGAMode[j].Init_SR30_SR75[i]);
++
++ /* init SEQ register SR80 - SR93 */
++ for (i = 0; i < SIZE_SR80_SR93; i++)
++ smtc_seqw(i + 0x80,
++ VGAMode[j].Init_SR80_SR93[i]);
++
++ /* init SEQ register SRA0 - SRAF */
++ for (i = 0; i < SIZE_SRA0_SRAF; i++)
++ smtc_seqw(i + 0xa0,
++ VGAMode[j].Init_SRA0_SRAF[i]);
++
++ /* init Graphic register GR00 - GR08 */
++ for (i = 0; i < SIZE_GR00_GR08; i++)
++ smtc_grphw(i, VGAMode[j].Init_GR00_GR08[i]);
++
++ /* init Attribute register AR00 - AR14 */
++ for (i = 0; i < SIZE_AR00_AR14; i++)
++ smtc_attrw(i, VGAMode[j].Init_AR00_AR14[i]);
++
++ /* init CRTC register CR00 - CR18 */
++ for (i = 0; i < SIZE_CR00_CR18; i++)
++ smtc_crtcw(i, VGAMode[j].Init_CR00_CR18[i]);
++
++ /* init CRTC register CR30 - CR4D */
++ for (i = 0; i < SIZE_CR30_CR4D; i++)
++ smtc_crtcw(i + 0x30,
++ VGAMode[j].Init_CR30_CR4D[i]);
++
++ /* init CRTC register CR90 - CRA7 */
++ for (i = 0; i < SIZE_CR90_CRA7; i++)
++ smtc_crtcw(i + 0x90,
++ VGAMode[j].Init_CR90_CRA7[i]);
++ }
++ }
++ smtc_mmiowb(0x67, 0x3c2);
++
++ /* set VPR registers */
++ writel(0x0, sfb->vp_regs + 0x0C);
++ writel(0x0, sfb->vp_regs + 0x40);
++
++ /* set data width */
++ m_nScreenStride =
++ (sfb->width * sfb->fb.var.bits_per_pixel) / 64;
++ switch (sfb->fb.var.bits_per_pixel) {
++ case 8:
++ writel(0x0, sfb->vp_regs + 0x0);
++ break;
++ case 16:
++ writel(0x00020000, sfb->vp_regs + 0x0);
++ break;
++ case 24:
++ writel(0x00040000, sfb->vp_regs + 0x0);
++ break;
++ case 32:
++ writel(0x00030000, sfb->vp_regs + 0x0);
++ break;
++ }
++ writel((u32) (((m_nScreenStride + 2) << 16) | m_nScreenStride),
++ sfb->vp_regs + 0x10);
++
++}
++
++static void smtc_set_timing(struct smtcfb_info *sfb)
++{
++ switch (sfb->chip_id) {
++ case 0x710:
++ case 0x712:
++ case 0x720:
++ sm7xx_set_timing(sfb);
++ break;
++ }
++}
++
++static void smtcfb_setmode(struct smtcfb_info *sfb)
++{
++ switch (sfb->fb.var.bits_per_pixel) {
++ case 32:
++ sfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
++ sfb->fb.fix.line_length = sfb->fb.var.xres * 4;
++ sfb->fb.var.red.length = 8;
++ sfb->fb.var.green.length = 8;
++ sfb->fb.var.blue.length = 8;
++ sfb->fb.var.red.offset = 16;
++ sfb->fb.var.green.offset = 8;
++ sfb->fb.var.blue.offset = 0;
++ break;
++ case 24:
++ sfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
++ sfb->fb.fix.line_length = sfb->fb.var.xres * 3;
++ sfb->fb.var.red.length = 8;
++ sfb->fb.var.green.length = 8;
++ sfb->fb.var.blue.length = 8;
++ sfb->fb.var.red.offset = 16;
++ sfb->fb.var.green.offset = 8;
++ sfb->fb.var.blue.offset = 0;
++ break;
++ case 8:
++ sfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ sfb->fb.fix.line_length = sfb->fb.var.xres;
++ sfb->fb.var.red.length = 3;
++ sfb->fb.var.green.length = 3;
++ sfb->fb.var.blue.length = 2;
++ sfb->fb.var.red.offset = 5;
++ sfb->fb.var.green.offset = 2;
++ sfb->fb.var.blue.offset = 0;
++ break;
++ case 16:
++ default:
++ sfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
++ sfb->fb.fix.line_length = sfb->fb.var.xres * 2;
++ sfb->fb.var.red.length = 5;
++ sfb->fb.var.green.length = 6;
++ sfb->fb.var.blue.length = 5;
++ sfb->fb.var.red.offset = 11;
++ sfb->fb.var.green.offset = 5;
++ sfb->fb.var.blue.offset = 0;
++ break;
++ }
++
++ sfb->width = sfb->fb.var.xres;
++ sfb->height = sfb->fb.var.yres;
++ sfb->hz = 60;
++ smtc_set_timing(sfb);
++}
++
++static int smtc_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
++{
++ /* sanity checks */
++ if (var->xres_virtual < var->xres)
++ var->xres_virtual = var->xres;
++
++ if (var->yres_virtual < var->yres)
++ var->yres_virtual = var->yres;
++
++ /* set valid default bpp */
++ if ((var->bits_per_pixel != 8) && (var->bits_per_pixel != 16) &&
++ (var->bits_per_pixel != 24) && (var->bits_per_pixel != 32))
++ var->bits_per_pixel = 16;
++
++ return 0;
++}
++
++static int smtc_set_par(struct fb_info *info)
++{
++ smtcfb_setmode(info->par);
++
++ return 0;
++}
++
++static struct fb_ops smtcfb_ops = {
++ .owner = THIS_MODULE,
++ .fb_check_var = smtc_check_var,
++ .fb_set_par = smtc_set_par,
++ .fb_setcolreg = smtc_setcolreg,
++ .fb_blank = smtc_blank,
++ .fb_fillrect = cfb_fillrect,
++ .fb_imageblit = cfb_imageblit,
++ .fb_copyarea = cfb_copyarea,
++#ifdef __BIG_ENDIAN
++ .fb_read = smtcfb_read,
++ .fb_write = smtcfb_write,
++#endif
++};
++
++/*
++ * alloc struct smtcfb_info and assign default values
++ */
++static struct smtcfb_info *smtc_alloc_fb_info(struct pci_dev *pdev)
++{
++ struct smtcfb_info *sfb;
++
++ sfb = kzalloc(sizeof(*sfb), GFP_KERNEL);
++
++ if (!sfb)
++ return NULL;
++
++ sfb->pdev = pdev;
++
++ sfb->fb.flags = FBINFO_FLAG_DEFAULT;
++ sfb->fb.fbops = &smtcfb_ops;
++ sfb->fb.fix = smtcfb_fix;
++ sfb->fb.var = smtcfb_var;
++ sfb->fb.pseudo_palette = sfb->colreg;
++ sfb->fb.par = sfb;
++
++ return sfb;
++}
++
++/*
++ * free struct smtcfb_info
++ */
++static void smtc_free_fb_info(struct smtcfb_info *sfb)
++{
++ kfree(sfb);
++}
++
++/*
++ * Unmap in the memory mapped IO registers
++ */
++
++static void smtc_unmap_mmio(struct smtcfb_info *sfb)
++{
++ if (sfb && smtc_RegBaseAddress)
++ smtc_RegBaseAddress = NULL;
++}
++
++/*
++ * Map in the screen memory
++ */
++
++static int smtc_map_smem(struct smtcfb_info *sfb,
++ struct pci_dev *pdev, u_long smem_len)
++{
++
++ sfb->fb.fix.smem_start = pci_resource_start(pdev, 0);
++
++#ifdef __BIG_ENDIAN
++ if (sfb->fb.var.bits_per_pixel == 32)
++ sfb->fb.fix.smem_start += 0x800000;
++#endif
++
++ sfb->fb.fix.smem_len = smem_len;
++
++ sfb->fb.screen_base = sfb->lfb;
++
++ if (!sfb->fb.screen_base) {
++ dev_err(&pdev->dev,
++ "%s: unable to map screen memory\n", sfb->fb.fix.id);
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++/*
++ * Unmap in the screen memory
++ *
++ */
++static void smtc_unmap_smem(struct smtcfb_info *sfb)
++{
++ if (sfb && sfb->fb.screen_base) {
++ iounmap(sfb->fb.screen_base);
++ sfb->fb.screen_base = NULL;
++ }
++}
++
++/*
++ * We need to wake up the device and make sure its in linear memory mode.
++ */
++static inline void sm7xx_init_hw(void)
++{
++ outb_p(0x18, 0x3c4);
++ outb_p(0x11, 0x3c5);
++}
++
++static int smtcfb_pci_probe(struct pci_dev *pdev,
++ const struct pci_device_id *ent)
++{
++ struct smtcfb_info *sfb;
++ u_long smem_size = 0x00800000; /* default 8MB */
++ int err;
++ unsigned long mmio_base;
++
++ dev_info(&pdev->dev, "Silicon Motion display driver.");
++
++ err = pci_enable_device(pdev); /* enable SMTC chip */
++ if (err)
++ return err;
++
++ sprintf(smtcfb_fix.id, "sm%Xfb", ent->device);
++
++ sfb = smtc_alloc_fb_info(pdev);
++
++ if (!sfb) {
++ err = -ENOMEM;
++ goto failed_free;
++ }
++
++ sfb->chip_id = ent->device;
++
++ pci_set_drvdata(pdev, sfb);
++
++ sm7xx_init_hw();
++
++ /* get mode parameter from smtc_scr_info */
++ if (smtc_scr_info.lfb_width != 0) {
++ sfb->fb.var.xres = smtc_scr_info.lfb_width;
++ sfb->fb.var.yres = smtc_scr_info.lfb_height;
++ sfb->fb.var.bits_per_pixel = smtc_scr_info.lfb_depth;
++ } else {
++ /* default resolution 1024x600 16bit mode */
++ sfb->fb.var.xres = SCREEN_X_RES;
++ sfb->fb.var.yres = SCREEN_Y_RES;
++ sfb->fb.var.bits_per_pixel = SCREEN_BPP;
++ }
++
++#ifdef __BIG_ENDIAN
++ if (sfb->fb.var.bits_per_pixel == 24)
++ sfb->fb.var.bits_per_pixel = (smtc_scr_info.lfb_depth = 32);
++#endif
++ /* Map address and memory detection */
++ mmio_base = pci_resource_start(pdev, 0);
++ pci_read_config_byte(pdev, PCI_REVISION_ID, &sfb->chip_rev_id);
++
++ switch (sfb->chip_id) {
++ case 0x710:
++ case 0x712:
++ sfb->fb.fix.mmio_start = mmio_base + 0x00400000;
++ sfb->fb.fix.mmio_len = 0x00400000;
++ smem_size = SM712_VIDEOMEMORYSIZE;
++#ifdef __BIG_ENDIAN
++ sfb->lfb = ioremap(mmio_base, 0x00c00000);
++#else
++ sfb->lfb = ioremap(mmio_base, 0x00800000);
++#endif
++ sfb->mmio = (smtc_RegBaseAddress =
++ sfb->lfb + 0x00700000);
++ sfb->dp_regs = sfb->lfb + 0x00408000;
++ sfb->vp_regs = sfb->lfb + 0x0040c000;
++#ifdef __BIG_ENDIAN
++ if (sfb->fb.var.bits_per_pixel == 32) {
++ sfb->lfb += 0x800000;
++ dev_info(&pdev->dev, "sfb->lfb=%p", sfb->lfb);
++ }
++#endif
++ if (!smtc_RegBaseAddress) {
++ dev_err(&pdev->dev,
++ "%s: unable to map memory mapped IO!",
++ sfb->fb.fix.id);
++ err = -ENOMEM;
++ goto failed_fb;
++ }
++
++ /* set MCLK = 14.31818 * (0x16 / 0x2) */
++ smtc_seqw(0x6a, 0x16);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x62, 0x3e);
++ /* enable PCI burst */
++ smtc_seqw(0x17, 0x20);
++ /* enable word swap */
++#ifdef __BIG_ENDIAN
++ if (sfb->fb.var.bits_per_pixel == 32)
++ smtc_seqw(0x17, 0x30);
++#endif
++ break;
++ case 0x720:
++ sfb->fb.fix.mmio_start = mmio_base;
++ sfb->fb.fix.mmio_len = 0x00200000;
++ smem_size = SM722_VIDEOMEMORYSIZE;
++ sfb->dp_regs = ioremap(mmio_base, 0x00a00000);
++ sfb->lfb = sfb->dp_regs + 0x00200000;
++ sfb->mmio = (smtc_RegBaseAddress =
++ sfb->dp_regs + 0x000c0000);
++ sfb->vp_regs = sfb->dp_regs + 0x800;
++
++ smtc_seqw(0x62, 0xff);
++ smtc_seqw(0x6a, 0x0d);
++ smtc_seqw(0x6b, 0x02);
++ break;
++ default:
++ dev_err(&pdev->dev,
++ "No valid Silicon Motion display chip was detected!");
++
++ goto failed_fb;
++ }
++
++ /* can support 32 bpp */
++ if (15 == sfb->fb.var.bits_per_pixel)
++ sfb->fb.var.bits_per_pixel = 16;
++
++ sfb->fb.var.xres_virtual = sfb->fb.var.xres;
++ sfb->fb.var.yres_virtual = sfb->fb.var.yres;
++ err = smtc_map_smem(sfb, pdev, smem_size);
++ if (err)
++ goto failed;
++
++ smtcfb_setmode(sfb);
++
++ err = register_framebuffer(&sfb->fb);
++ if (err < 0)
++ goto failed;
++
++ dev_info(&pdev->dev,
++ "Silicon Motion SM%X Rev%X primary display mode %dx%d-%d Init Complete.",
++ sfb->chip_id, sfb->chip_rev_id, sfb->fb.var.xres,
++ sfb->fb.var.yres, sfb->fb.var.bits_per_pixel);
++
++ return 0;
++
++failed:
++ dev_err(&pdev->dev, "Silicon Motion, Inc. primary display init fail.");
++
++ smtc_unmap_smem(sfb);
++ smtc_unmap_mmio(sfb);
++failed_fb:
++ smtc_free_fb_info(sfb);
++
++failed_free:
++ pci_disable_device(pdev);
++
++ return err;
++}
++
++/*
++ * 0x710 (LynxEM)
++ * 0x712 (LynxEM+)
++ * 0x720 (Lynx3DM, Lynx3DM+)
++ */
++static const struct pci_device_id smtcfb_pci_table[] = {
++ { PCI_DEVICE(0x126f, 0x710), },
++ { PCI_DEVICE(0x126f, 0x712), },
++ { PCI_DEVICE(0x126f, 0x720), },
++ {0,}
++};
++
++static void smtcfb_pci_remove(struct pci_dev *pdev)
++{
++ struct smtcfb_info *sfb;
++
++ sfb = pci_get_drvdata(pdev);
++ smtc_unmap_smem(sfb);
++ smtc_unmap_mmio(sfb);
++ unregister_framebuffer(&sfb->fb);
++ smtc_free_fb_info(sfb);
++}
++
++#ifdef CONFIG_PM
++static int smtcfb_pci_suspend(struct device *device)
++{
++ struct pci_dev *pdev = to_pci_dev(device);
++ struct smtcfb_info *sfb;
++
++ sfb = pci_get_drvdata(pdev);
++
++ /* set the hw in sleep mode use external clock and self memory refresh
++ * so that we can turn off internal PLLs later on
++ */
++ smtc_seqw(0x20, (smtc_seqr(0x20) | 0xc0));
++ smtc_seqw(0x69, (smtc_seqr(0x69) & 0xf7));
++
++ console_lock();
++ fb_set_suspend(&sfb->fb, 1);
++ console_unlock();
++
++ /* additionally turn off all function blocks including internal PLLs */
++ smtc_seqw(0x21, 0xff);
++
++ return 0;
++}
++
++static int smtcfb_pci_resume(struct device *device)
++{
++ struct pci_dev *pdev = to_pci_dev(device);
++ struct smtcfb_info *sfb;
++
++ sfb = pci_get_drvdata(pdev);
++
++ /* reinit hardware */
++ sm7xx_init_hw();
++ switch (sfb->chip_id) {
++ case 0x710:
++ case 0x712:
++ /* set MCLK = 14.31818 * (0x16 / 0x2) */
++ smtc_seqw(0x6a, 0x16);
++ smtc_seqw(0x6b, 0x02);
++ smtc_seqw(0x62, 0x3e);
++ /* enable PCI burst */
++ smtc_seqw(0x17, 0x20);
++#ifdef __BIG_ENDIAN
++ if (sfb->fb.var.bits_per_pixel == 32)
++ smtc_seqw(0x17, 0x30);
++#endif
++ break;
++ case 0x720:
++ smtc_seqw(0x62, 0xff);
++ smtc_seqw(0x6a, 0x0d);
++ smtc_seqw(0x6b, 0x02);
++ break;
++ }
++
++ smtc_seqw(0x34, (smtc_seqr(0x34) | 0xc0));
++ smtc_seqw(0x33, ((smtc_seqr(0x33) | 0x08) & 0xfb));
++
++ smtcfb_setmode(sfb);
++
++ console_lock();
++ fb_set_suspend(&sfb->fb, 0);
++ console_unlock();
++
++ return 0;
++}
++
++static SIMPLE_DEV_PM_OPS(sm7xx_pm_ops, smtcfb_pci_suspend, smtcfb_pci_resume);
++#define SM7XX_PM_OPS (&sm7xx_pm_ops)
++
++#else /* !CONFIG_PM */
++
++#define SM7XX_PM_OPS NULL
++
++#endif /* !CONFIG_PM */
++
++static struct pci_driver smtcfb_driver = {
++ .name = "smtcfb",
++ .id_table = smtcfb_pci_table,
++ .probe = smtcfb_pci_probe,
++ .remove = smtcfb_pci_remove,
++ .driver.pm = SM7XX_PM_OPS,
++};
++
++module_pci_driver(smtcfb_driver);
++
++MODULE_AUTHOR("Siliconmotion ");
++MODULE_DESCRIPTION("Framebuffer driver for SMI Graphic Cards");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
+index f98d03f..09cf729 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 a968894..0bfa2cb 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/drivers/video/Kconfig b/drivers/video/Kconfig
+index 8bf495f..f6a15b6 100644
+--- a/drivers/video/Kconfig
++++ b/drivers/video/Kconfig
+@@ -36,6 +36,12 @@ config VGASTATE
+ tristate
+ default n
+
++config VIDEO_OUTPUT_CONTROL
++ tristate "Lowlevel video output switch controls"
++ help
++ This framework adds support for low-level control of the video
++ output switch.
++
+ config VIDEOMODE_HELPERS
+ bool
+
+diff --git a/drivers/video/Makefile b/drivers/video/Makefile
+index 9ad3c17..3d869d9 100644
+--- a/drivers/video/Makefile
++++ b/drivers/video/Makefile
+@@ -7,6 +7,8 @@ obj-y += backlight/
+
+ obj-y += fbdev/
+
++#video output switch sysfs driver
++obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o
+ obj-$(CONFIG_VIDEOMODE_HELPERS) += display_timing.o videomode.o
+ ifeq ($(CONFIG_OF),y)
+ obj-$(CONFIG_VIDEOMODE_HELPERS) += of_display_timing.o of_videomode.o
+diff --git a/drivers/video/output.c b/drivers/video/output.c
+new file mode 100644
+index 0000000..1446c49
+--- /dev/null
++++ b/drivers/video/output.c
+@@ -0,0 +1,133 @@
++/*
++ * output.c - Display Output Switch driver
++ *
++ * Copyright (C) 2006 Luming Yu <luming.yu@intel.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.
++ *
++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ */
++#include <linux/module.h>
++#include <linux/video_output.h>
++#include <linux/slab.h>
++#include <linux/err.h>
++#include <linux/ctype.h>
++
++
++MODULE_DESCRIPTION("Display Output Switcher Lowlevel Control Abstraction");
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Luming Yu <luming.yu@intel.com>");
++
++static ssize_t state_show(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ ssize_t ret_size = 0;
++ struct output_device *od = to_output_device(dev);
++ if (od->props)
++ ret_size = sprintf(buf,"%.8x\n",od->props->get_status(od));
++ return ret_size;
++}
++
++static ssize_t state_store(struct device *dev, struct device_attribute *attr,
++ const char *buf,size_t count)
++{
++ char *endp;
++ struct output_device *od = to_output_device(dev);
++ int request_state = simple_strtoul(buf,&endp,0);
++ size_t size = endp - buf;
++
++ if (isspace(*endp))
++ size++;
++ if (size != count)
++ return -EINVAL;
++
++ if (od->props) {
++ od->request_state = request_state;
++ od->props->set_state(od);
++ }
++ return count;
++}
++static DEVICE_ATTR_RW(state);
++
++static void video_output_release(struct device *dev)
++{
++ struct output_device *od = to_output_device(dev);
++ kfree(od);
++}
++
++static struct attribute *video_output_attrs[] = {
++ &dev_attr_state.attr,
++ NULL,
++};
++ATTRIBUTE_GROUPS(video_output);
++
++static struct class video_output_class = {
++ .name = "video_output",
++ .dev_release = video_output_release,
++ .dev_groups = video_output_groups,
++};
++
++struct output_device *video_output_register(const char *name,
++ struct device *dev,
++ void *devdata,
++ struct output_properties *op)
++{
++ struct output_device *new_dev;
++ int ret_code = 0;
++
++ new_dev = kzalloc(sizeof(struct output_device),GFP_KERNEL);
++ if (!new_dev) {
++ ret_code = -ENOMEM;
++ goto error_return;
++ }
++ new_dev->props = op;
++ new_dev->dev.class = &video_output_class;
++ new_dev->dev.parent = dev;
++ dev_set_name(&new_dev->dev, "%s", name);
++ dev_set_drvdata(&new_dev->dev, devdata);
++ ret_code = device_register(&new_dev->dev);
++ if (ret_code) {
++ kfree(new_dev);
++ goto error_return;
++ }
++ return new_dev;
++
++error_return:
++ return ERR_PTR(ret_code);
++}
++EXPORT_SYMBOL(video_output_register);
++
++void video_output_unregister(struct output_device *dev)
++{
++ if (!dev)
++ return;
++ device_unregister(&dev->dev);
++}
++EXPORT_SYMBOL(video_output_unregister);
++
++static void __exit video_output_class_exit(void)
++{
++ class_unregister(&video_output_class);
++}
++
++static int __init video_output_class_init(void)
++{
++ return class_register(&video_output_class);
++}
++
++postcore_initcall(video_output_class_init);
++module_exit(video_output_class_exit);
+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/include/linux/video_output.h b/include/linux/video_output.h
+new file mode 100644
+index 0000000..ed5cdeb
+--- /dev/null
++++ b/include/linux/video_output.h
+@@ -0,0 +1,57 @@
++/*
++ *
++ * Copyright (C) 2006 Luming Yu <luming.yu@intel.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.
++ *
++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ */
++#ifndef _LINUX_VIDEO_OUTPUT_H
++#define _LINUX_VIDEO_OUTPUT_H
++#include <linux/device.h>
++#include <linux/err.h>
++struct output_device;
++struct output_properties {
++ int (*set_state)(struct output_device *);
++ int (*get_status)(struct output_device *);
++};
++struct output_device {
++ int request_state;
++ struct output_properties *props;
++ struct device dev;
++};
++#define to_output_device(obj) container_of(obj, struct output_device, dev)
++#if defined(CONFIG_VIDEO_OUTPUT_CONTROL) || defined(CONFIG_VIDEO_OUTPUT_CONTROL_MODULE)
++struct output_device *video_output_register(const char *name,
++ struct device *dev,
++ void *devdata,
++ struct output_properties *op);
++void video_output_unregister(struct output_device *dev);
++#else
++static struct output_device *video_output_register(const char *name,
++ struct device *dev,
++ void *devdata,
++ struct output_properties *op)
++{
++ return ERR_PTR(-ENODEV);
++}
++static void video_output_unregister(struct output_device *dev)
++{
++ return;
++}
++#endif
++#endif
+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 b3b16c0..e0a91100 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 397b6b8..fee5bc1 100755
+--- a/scripts/recordmcount.pl
++++ b/scripts/recordmcount.pl
+@@ -312,14 +312,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
+@@ -331,19 +350,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/libre/linux-libre/gnewsense-binutils-flag.patch b/libre/linux-libre/gnewsense-binutils-flag.patch
new file mode 100644
index 000000000..5f2d44f99
--- /dev/null
+++ b/libre/linux-libre/gnewsense-binutils-flag.patch
@@ -0,0 +1,28 @@
+Our binutils somehow ended up with a -mfix-gs2f-kernel, rather than
+-mfix-ls2f-kernel. Cope with it.
+
+Index: arch/mips/loongson/Platform
+===================================================================
+--- arch/mips/loongson/Platform.orig 2010-10-25 19:09:49.000000000 +0000
++++ arch/mips/loongson/Platform 2010-10-25 19:10:44.000000000 +0000
+@@ -10,6 +10,12 @@
+ $(call cc-option,-march=loongson2f,-march=r4600)
+ # Enable the workarounds for Loongson2f
+ ifdef CONFIG_CPU_LOONGSON2F_WORKAROUNDS
++ ifneq ($(call as-option,-Wa$(comma)-mfix-gs2f-kernel,),)
++ cflags-$(CONFIG_CPU_NOP_WORKAROUNDS) += -Wa$(comma)-mfix-gs2f-kernel
++ ifneq ($(CONFIG_CPU_NOP_WORKAROUNDS),$(CONFIG_CPU_JUMP_WORKAROUNDS))
++ cflags-$(CONFIG_CPU_JUMP_WORKAROUNDS) += -Wa$(comma)-mfix-gs2f-kernel
++ endif
++ else
+ ifeq ($(call as-option,-Wa$(comma)-mfix-loongson2f-nop,),)
+ $(error only binutils >= 2.20.2 have needed option -mfix-loongson2f-nop)
+ else
+@@ -20,6 +26,7 @@
+ else
+ cflags-$(CONFIG_CPU_JUMP_WORKAROUNDS) += -Wa$(comma)-mfix-loongson2f-jump
+ endif
++ endif
+ endif
+
+ #