MACH_DEXXON_GDIUM2F10,
        MACH_LEMOTE_NAS,
        MACH_LEMOTE_LL2F,
-       MACH_LEMOTE_A1004,
-       MACH_LEMOTE_A1101,
-       MACH_LEMOTE_A1201,
-       MACH_LEMOTE_A1205,
+       MACH_LOONGSON_GENERIC,
        MACH_LOONGSON_END
 };
 
 
 #define VIDEO_ROM              7
 #define ADAPTER_ROM            8
 #define ACPI_TABLE             9
-#define MAX_MEMORY_TYPE                10
+#define SMBIOS_TABLE           10
+#define MAX_MEMORY_TYPE                11
 
 #define LOONGSON3_BOOT_MEM_MAP_MAX 128
 struct efi_memory_map_loongson {
        u32 nr_cpus;
 } __packed;
 
+#define MAX_UARTS 64
+struct uart_device {
+       u32 iotype; /* see include/linux/serial_core.h */
+       u32 uartclk;
+       u32 int_offset;
+       u64 uart_base;
+} __packed;
+
+#define MAX_SENSORS 64
+#define SENSOR_TEMPER  0x00000001
+#define SENSOR_VOLTAGE 0x00000002
+#define SENSOR_FAN     0x00000004
+struct sensor_device {
+       char name[32];  /* a formal name */
+       char label[64]; /* a flexible description */
+       u32 type;       /* SENSOR_* */
+       u32 id;         /* instance id of a sensor-class */
+       u32 fan_policy; /* see loongson_hwmon.h */
+       u32 fan_percent;/* only for constant speed policy */
+       u64 base_addr;  /* base address of device registers */
+} __packed;
+
 struct system_loongson {
        u16 vers;     /* version of system_loongson */
        u32 ccnuma_smp; /* 0: no numa; 1: has numa */
        u32 sing_double_channel; /* 1:single; 2:double */
+       u32 nr_uarts;
+       struct uart_device uarts[MAX_UARTS];
+       u32 nr_sensors;
+       struct sensor_device sensors[MAX_SENSORS];
+       char has_ec;
+       char ec_name[32];
+       u64 ec_base_addr;
+       char has_tcm;
+       char tcm_name[32];
+       u64 tcm_base_addr;
+       u64 workarounds; /* see workarounds.h */
 } __packed;
 
 struct irq_source_routing_table {
        u64 suspend_addr;
        u64 vgabios_addr;
        u32 dma_mask_bits;
+       char ecname[32];
+       u32 nr_uarts;
+       struct uart_device uarts[MAX_UARTS];
+       u32 nr_sensors;
+       struct sensor_device sensors[MAX_SENSORS];
+       u64 workarounds;
 };
 
 extern struct efi_memory_map_loongson *loongson_memmap;
 extern struct loongson_system_configuration loongson_sysconf;
-extern int cpuhotplug_workaround;
+
 #endif
 
 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 unsigned long _loongson_uart_base[], loongson_uart_base[];
 extern void prom_init_loongson_uart_base(void);
 #endif
 
 
--- /dev/null
+#ifndef __LOONGSON_HWMON_H_
+#define __LOONGSON_HWMON_H_
+
+#include <linux/types.h>
+
+#define MIN_TEMP       0
+#define MAX_TEMP       255
+#define NOT_VALID_TEMP 999
+
+typedef int (*get_temp_fun)(int);
+extern int loongson3_cpu_temp(int);
+
+/* 0:Max speed, 1:Manual, 2:Auto */
+enum fan_control_mode {
+       FAN_FULL_MODE = 0,
+       FAN_MANUAL_MODE = 1,
+       FAN_AUTO_MODE = 2,
+       FAN_MODE_END
+};
+
+struct temp_range {
+       u8 low;
+       u8 high;
+       u8 level;
+};
+
+#define CONSTANT_SPEED_POLICY  0  /* at constent speed */
+#define STEP_SPEED_POLICY      1  /* use up/down arrays to describe policy */
+#define KERNEL_HELPER_POLICY   2  /* kernel as a helper to fan control */
+
+#define MAX_STEP_NUM   16
+#define MAX_FAN_LEVEL  255
+
+/* loongson_fan_policy works when fan work at FAN_AUTO_MODE */
+struct loongson_fan_policy {
+       u8      type;
+
+       /* percent only used when type is CONSTANT_SPEED_POLICY */
+       u8      percent;
+
+       /* period between two check. (Unit: S) */
+       u8      adjust_period;
+
+       /* fan adjust usually depend on a temprature input */
+       get_temp_fun    depend_temp;
+
+       /* up_step/down_step used when type is STEP_SPEED_POLICY */
+       u8      up_step_num;
+       u8      down_step_num;
+       struct temp_range up_step[MAX_STEP_NUM];
+       struct temp_range down_step[MAX_STEP_NUM];
+       struct delayed_work work;
+};
+
+#endif /* __LOONGSON_HWMON_H_*/
 
 
 #ifdef CONFIG_LOONGSON_MACH3X
 
-#define LOONGSON_MACHTYPE MACH_LEMOTE_A1101
+#define LOONGSON_MACHTYPE MACH_LOONGSON_GENERIC
 
 #endif /* CONFIG_LOONGSON_MACH3X */
 
 
--- /dev/null
+#ifndef __ASM_MACH_LOONGSON_WORKAROUNDS_H_
+#define __ASM_MACH_LOONGSON_WORKAROUNDS_H_
+
+#define WORKAROUND_CPUFREQ     0x00000001
+#define WORKAROUND_CPUHOTPLUG  0x00000002
+
+#endif
 
        int timeout;
        unsigned char *uart_base;
 
-       uart_base = (unsigned char *)_loongson_uart_base;
+       uart_base = (unsigned char *)_loongson_uart_base[0];
        timeout = 1024;
 
        while (((serial_in(uart_base, UART_LSR) & UART_LSR_THRE) == 0) &&
 
 #include <asm/bootinfo.h>
 #include <loongson.h>
 #include <boot_param.h>
+#include <workarounds.h>
 
 u32 cpu_clock_freq;
 EXPORT_SYMBOL(cpu_clock_freq);
 u64 loongson_freqctrl[MAX_PACKAGES];
 
 unsigned long long smp_group[4];
-int cpuhotplug_workaround = 0;
 
 #define parse_even_earlier(res, option, p)                             \
 do {                                                                   \
 #else
        struct boot_params *boot_p;
        struct loongson_params *loongson_p;
+       struct system_loongson *esys;
        struct efi_cpuinfo_loongson *ecpu;
        struct irq_source_routing_table *eirq_source;
 
        boot_p = (struct boot_params *)fw_arg2;
        loongson_p = &(boot_p->efi.smbios.lp);
 
+       esys = (struct system_loongson *)
+               ((u64)loongson_p + loongson_p->system_offset);
        ecpu = (struct efi_cpuinfo_loongson *)
                ((u64)loongson_p + loongson_p->cpu_offset);
        eirq_source = (struct irq_source_routing_table *)
                loongson_chipcfg[2] = 0x900020001fe00180;
                loongson_chipcfg[3] = 0x900030001fe00180;
                loongson_sysconf.ht_control_base = 0x90000EFDFB000000;
+               loongson_sysconf.workarounds = WORKAROUND_CPUFREQ;
        } else if (ecpu->cputype == Loongson_3B) {
                loongson_sysconf.cores_per_node = 4; /* One chip has 2 nodes */
                loongson_sysconf.cores_per_package = 8;
                loongson_freqctrl[2] = 0x900040001fe001d0;
                loongson_freqctrl[3] = 0x900060001fe001d0;
                loongson_sysconf.ht_control_base = 0x90001EFDFB000000;
-               cpuhotplug_workaround = 1;
+               loongson_sysconf.workarounds = WORKAROUND_CPUHOTPLUG;
        } else {
                loongson_sysconf.cores_per_node = 1;
                loongson_sysconf.cores_per_package = 1;
        pr_debug("Shutdown Addr: %llx, Restart Addr: %llx, VBIOS Addr: %llx\n",
                loongson_sysconf.poweroff_addr, loongson_sysconf.restart_addr,
                loongson_sysconf.vgabios_addr);
+
+       memset(loongson_sysconf.ecname, 0, 32);
+       if (esys->has_ec)
+               memcpy(loongson_sysconf.ecname, esys->ec_name, 32);
+       loongson_sysconf.workarounds |= esys->workarounds;
+
+       loongson_sysconf.nr_uarts = esys->nr_uarts;
+       if (esys->nr_uarts < 1 || esys->nr_uarts > MAX_UARTS)
+               loongson_sysconf.nr_uarts = 1;
+       memcpy(loongson_sysconf.uarts, esys->uarts,
+               sizeof(struct uart_device) * loongson_sysconf.nr_uarts);
+
+       loongson_sysconf.nr_sensors = esys->nr_sensors;
+       if (loongson_sysconf.nr_sensors > MAX_SENSORS)
+               loongson_sysconf.nr_sensors = 0;
+       if (loongson_sysconf.nr_sensors)
+               memcpy(loongson_sysconf.sensors, esys->sensors,
+                       sizeof(struct sensor_device) * loongson_sysconf.nr_sensors);
 #endif
        if (cpu_clock_freq == 0) {
                processor_id = (¤t_cpu_data)->processor_id;
 
        [MACH_DEXXON_GDIUM2F10]         "dexxon-gdium-2f",
        [MACH_LEMOTE_NAS]               "lemote-nas-2f",
        [MACH_LEMOTE_LL2F]              "lemote-lynloong-2f",
-       [MACH_LEMOTE_A1004]             "lemote-3a-notebook-a1004",
-       [MACH_LEMOTE_A1101]             "lemote-3a-itx-a1101",
-       [MACH_LEMOTE_A1201]             "lemote-2gq-notebook-a1201",
-       [MACH_LEMOTE_A1205]             "lemote-2gq-aio-a1205",
+       [MACH_LOONGSON_GENERIC]         "generic-loongson-machine",
        [MACH_LOONGSON_END]             NULL,
 };
 
 
        .regshift       = 0,                                    \
 }
 
-static struct plat_serial8250_port uart8250_data[][2] = {
+static struct plat_serial8250_port uart8250_data[][MAX_UARTS + 1] = {
        [MACH_LOONGSON_UNKNOWN]         {},
        [MACH_LEMOTE_FL2E]              {PORT(4, 1843200), {} },
        [MACH_LEMOTE_FL2F]              {PORT(3, 1843200), {} },
        [MACH_DEXXON_GDIUM2F10]         {PORT_M(3, 3686400), {} },
        [MACH_LEMOTE_NAS]               {PORT_M(3, 3686400), {} },
        [MACH_LEMOTE_LL2F]              {PORT(3, 1843200), {} },
-       [MACH_LEMOTE_A1004]             {PORT_M(2, 33177600), {} },
-       [MACH_LEMOTE_A1101]             {PORT_M(2, 25000000), {} },
-       [MACH_LEMOTE_A1201]             {PORT_M(2, 25000000), {} },
-       [MACH_LEMOTE_A1205]             {PORT_M(2, 25000000), {} },
+       [MACH_LOONGSON_GENERIC]         {PORT_M(2, 25000000), {} },
        [MACH_LOONGSON_END]             {},
 };
 
 
 static int __init serial_init(void)
 {
+       int i;
        unsigned char iotype;
 
        iotype = uart8250_data[mips_machtype][0].iotype;
 
-       if (UPIO_MEM == iotype)
+       if (UPIO_MEM == iotype) {
+               uart8250_data[mips_machtype][0].mapbase =
+                       loongson_uart_base[0];
                uart8250_data[mips_machtype][0].membase =
-                       (void __iomem *)_loongson_uart_base;
+                       (void __iomem *)_loongson_uart_base[0];
+       }
        else if (UPIO_PORT == iotype)
                uart8250_data[mips_machtype][0].iobase =
-                   loongson_uart_base - LOONGSON_PCIIO_BASE;
+                       loongson_uart_base[0] - LOONGSON_PCIIO_BASE;
 
+       if (loongson_sysconf.uarts[0].uartclk)
+               uart8250_data[mips_machtype][0].uartclk =
+                       loongson_sysconf.uarts[0].uartclk;
+
+       for (i = 1; i < loongson_sysconf.nr_uarts; i++) {
+               iotype = loongson_sysconf.uarts[i].iotype;
+               uart8250_data[mips_machtype][i].iotype = iotype;
+               loongson_uart_base[i] = loongson_sysconf.uarts[i].uart_base;
+
+               if (UPIO_MEM == iotype) {
+                       uart8250_data[mips_machtype][i].irq =
+                               MIPS_CPU_IRQ_BASE + loongson_sysconf.uarts[i].int_offset;
+                       uart8250_data[mips_machtype][i].mapbase =
+                               loongson_uart_base[i];
+                       uart8250_data[mips_machtype][i].membase =
+                               ioremap_nocache(loongson_uart_base[i], 8);
+               } else if (UPIO_PORT == iotype) {
+                       uart8250_data[mips_machtype][i].irq =
+                               loongson_sysconf.uarts[i].int_offset;
+                       uart8250_data[mips_machtype][i].iobase =
+                               loongson_uart_base[i] - LOONGSON_PCIIO_BASE;
+               }
+
+               uart8250_data[mips_machtype][i].uartclk =
+                       loongson_sysconf.uarts[i].uartclk;
+               uart8250_data[mips_machtype][i].flags =
+                       UPF_BOOT_AUTOCONF | UPF_SKIP_TEST;
+       }
+
+       memset(&uart8250_data[mips_machtype][loongson_sysconf.nr_uarts],
+                       0, sizeof(struct plat_serial8250_port));
        uart8250_device.dev.platform_data = uart8250_data[mips_machtype];
 
        return platform_device_register(&uart8250_device);
 
 
 #include <loongson.h>
 
-/* ioremapped */
-unsigned long _loongson_uart_base;
-EXPORT_SYMBOL(_loongson_uart_base);
 /* raw */
-unsigned long loongson_uart_base;
+unsigned long loongson_uart_base[MAX_UARTS] = {};
+/* ioremapped */
+unsigned long _loongson_uart_base[MAX_UARTS] = {};
+
 EXPORT_SYMBOL(loongson_uart_base);
+EXPORT_SYMBOL(_loongson_uart_base);
 
 void prom_init_loongson_uart_base(void)
 {
        switch (mips_machtype) {
+       case MACH_LOONGSON_GENERIC:
+               /* The CPU provided serial port (CPU) */
+               loongson_uart_base[0] = LOONGSON_REG_BASE + 0x1e0;
+               break;
        case MACH_LEMOTE_FL2E:
-               loongson_uart_base = LOONGSON_PCIIO_BASE + 0x3f8;
+               loongson_uart_base[0] = LOONGSON_PCIIO_BASE + 0x3f8;
                break;
        case MACH_LEMOTE_FL2F:
        case MACH_LEMOTE_LL2F:
-               loongson_uart_base = LOONGSON_PCIIO_BASE + 0x2f8;
+               loongson_uart_base[0] = LOONGSON_PCIIO_BASE + 0x2f8;
                break;
        case MACH_LEMOTE_ML2F7:
        case MACH_LEMOTE_YL2F89:
        case MACH_LEMOTE_NAS:
        default:
                /* The CPU provided serial port (LPC) */
-               loongson_uart_base = LOONGSON_LIO1_BASE + 0x3f8;
-               break;
-       case MACH_LEMOTE_A1004:
-       case MACH_LEMOTE_A1101:
-       case MACH_LEMOTE_A1201:
-       case MACH_LEMOTE_A1205:
-               /* The CPU provided serial port (CPU) */
-               loongson_uart_base = LOONGSON_REG_BASE + 0x1e0;
+               loongson_uart_base[0] = LOONGSON_LIO1_BASE + 0x3f8;
                break;
        }
 
-       _loongson_uart_base =
-               (unsigned long)ioremap_nocache(loongson_uart_base, 8);
+       _loongson_uart_base[0] =
+               (unsigned long)ioremap_nocache(loongson_uart_base[0], 8);
 }
 
 #
 # Makefile for Loongson-3 family machines
 #
-obj-y                  += irq.o cop2-ex.o
+obj-y                  += irq.o cop2-ex.o platform.o
 
 obj-$(CONFIG_SMP)      += smp.o
 
 
--- /dev/null
+/*
+ * Copyright (C) 2009 Lemote Inc.
+ * Author: Wu Zhangjin, wuzhangjin@gmail.com
+ *         Xiang Yu, xiangy@lemote.com
+ *         Chen Huacai, chenhc@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/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <asm/bootinfo.h>
+#include <boot_param.h>
+#include <loongson_hwmon.h>
+#include <workarounds.h>
+
+static int __init loongson3_platform_init(void)
+{
+       int i;
+       struct platform_device *pdev;
+
+       if (loongson_sysconf.ecname[0] != '\0')
+               platform_device_register_simple(loongson_sysconf.ecname, -1, NULL, 0);
+
+       for (i = 0; i < loongson_sysconf.nr_sensors; i++) {
+               if (loongson_sysconf.sensors[i].type > SENSOR_FAN)
+                       continue;
+
+               pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
+               pdev->name = loongson_sysconf.sensors[i].name;
+               pdev->id = loongson_sysconf.sensors[i].id;
+               pdev->dev.platform_data = &loongson_sysconf.sensors[i];
+               platform_device_register(pdev);
+       }
+
+       return 0;
+}
+
+arch_initcall(loongson3_platform_init);
 
 #include <asm/tlbflush.h>
 #include <asm/cacheflush.h>
 #include <loongson.h>
+#include <workarounds.h>
 
 #include "smp.h"
 
        if (loongson_sysconf.cputype == Loongson_3A) {
                LOONGSON_CHIPCFG(package_id) &= ~(1 << (12 + core_id));
        } else if (loongson_sysconf.cputype == Loongson_3B) {
-               if (!cpuhotplug_workaround)
+               if (!(loongson_sysconf.workarounds & WORKAROUND_CPUHOTPLUG))
                        LOONGSON_FREQCTRL(package_id) &= ~(1 << (core_id * 4 + 3));
        }
 }
        if (loongson_sysconf.cputype == Loongson_3A) {
                LOONGSON_CHIPCFG(package_id) |= 1 << (12 + core_id);
        } else if (loongson_sysconf.cputype == Loongson_3B) {
-               if (!cpuhotplug_workaround)
+               if (!(loongson_sysconf.workarounds & WORKAROUND_CPUHOTPLUG))
                        LOONGSON_FREQCTRL(package_id) |= 1 << (core_id * 4 + 3);
        }
 }