diff options
Diffstat (limited to 'kernels/linux-libre-lts-grsec/3.14.14-a410a5e2b7-loongson-community.patch')
-rw-r--r-- | kernels/linux-libre-lts-grsec/3.14.14-a410a5e2b7-loongson-community.patch | 10845 |
1 files changed, 0 insertions, 10845 deletions
diff --git a/kernels/linux-libre-lts-grsec/3.14.14-a410a5e2b7-loongson-community.patch b/kernels/linux-libre-lts-grsec/3.14.14-a410a5e2b7-loongson-community.patch deleted file mode 100644 index 8cd53fd61..000000000 --- a/kernels/linux-libre-lts-grsec/3.14.14-a410a5e2b7-loongson-community.patch +++ /dev/null @@ -1,10845 +0,0 @@ -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, ®_data); -+ -+ if (reg_data & 0x0400) { -+ /* Link status change */ -+ titan_ge_mdio_read(port_num, -+ TITAN_GE_MDIO_PHY_STATUS, ®_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 |