All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] Lemote-2f: Add Platform Specific Support
@ 2009-11-19 17:32 Wu Zhangjin
  2009-11-19 17:33 ` [PATCH 1/5] [loongson] LynLoong2F: " Wu Zhangjin
                   ` (4 more replies)
  0 siblings, 5 replies; 13+ messages in thread
From: Wu Zhangjin @ 2009-11-19 17:32 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips, zhangfx, yanh, huhb, Wu Zhangjin

This patchset add the platform specific support for LynLoong2F(ALLINONE) PC and
YeeLoong2F netbook. and also, a cleanup and update for YeeLoong2F is added.

  o [loongson] LynLoong2F: Add Platform Specific Support
    Add the backlight and suspend support for LynLoong2F.
  o [loongson] yeeloong2f: add basic ec operations
    [loongson] yeeloong2f: add platform specific support
    Add the support for YeeLoong2F's Embedded Controller and cleanup the reset
    logic with the ec operations. the following yeeloong2f patches need this ec support.
  o [loongson] yeeloong2f: add platform specific support
    Add the backlight,hwmon,hotkey,thermal,battery,suspend support for YeeLoong2F.
  o [loongson] yeeloong2f: add LID open event as the wakeup event
    Setup a new wakeup interrupt for YeeLoong2F to allow user wakeup the
    netbook from suspend mode via opening the netbook.

All of the above updates have been put into this branch:

git://dev.lemote.com/rt4ls.git linux-loongson/dev/for-upstream

Thanks & Best Regards,
     Wu Zhangjin

Wu Zhangjin (5):
  [loongson] LynLoong2F: Add Platform Specific Support
  [loongson] yeeloong2f: add basic ec operations
  [loongson] yeeloong2f: add LID open event as the wakeup event
  [loongson] yeeloong2f: cleanup the reset logic with ec_write function
  [loongson] yeeloong2f: add platform specific support

 .../mips/include/asm/mach-loongson/cs5536/cs5536.h |   35 +
 .../asm/mach-loongson/cs5536/cs5536_mfgpt.h        |    5 +
 arch/mips/include/asm/mach-loongson/loongson.h     |    1 +
 arch/mips/kernel/setup.c                           |    1 +
 arch/mips/loongson/Kconfig                         |   53 +
 arch/mips/loongson/common/cmdline.c                |   10 +
 arch/mips/loongson/lemote-2f/Makefile              |    8 +-
 arch/mips/loongson/lemote-2f/ec_kb3310b.c          |  132 ++
 arch/mips/loongson/lemote-2f/ec_kb3310b.h          |  197 +++
 arch/mips/loongson/lemote-2f/irq.c                 |    4 +-
 arch/mips/loongson/lemote-2f/lynloong_pc.c         |  609 +++++++++
 arch/mips/loongson/lemote-2f/pm.c                  |   72 +
 arch/mips/loongson/lemote-2f/reset.c               |   21 +-
 arch/mips/loongson/lemote-2f/yeeloong_laptop.c     | 1354 ++++++++++++++++++++
 14 files changed, 2481 insertions(+), 21 deletions(-)
 create mode 100644 arch/mips/loongson/lemote-2f/ec_kb3310b.c
 create mode 100644 arch/mips/loongson/lemote-2f/ec_kb3310b.h
 create mode 100644 arch/mips/loongson/lemote-2f/lynloong_pc.c
 create mode 100644 arch/mips/loongson/lemote-2f/yeeloong_laptop.c

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/5] [loongson] LynLoong2F: Add Platform Specific Support
  2009-11-19 17:32 [PATCH 0/5] Lemote-2f: Add Platform Specific Support Wu Zhangjin
@ 2009-11-19 17:33 ` Wu Zhangjin
  2009-11-19 17:33 ` [PATCH 2/5] [loongson] yeeloong2f: add basic ec operations Wu Zhangjin
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 13+ messages in thread
From: Wu Zhangjin @ 2009-11-19 17:33 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips, zhangfx, yanh, huhb, Richard Purdie, Wu Zhangjin

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(e.g. suspend/resume the display, the
external clocks.).

This patch adds the backlight subdriver and platform specific suspend
support for LynLoong(ALLINONE) PC. And also, a kernel command line
argument "video=sisfb:1360x768-16@60" is appended for the size-fixed
display.

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 .../mips/include/asm/mach-loongson/cs5536/cs5536.h |   35 ++
 .../asm/mach-loongson/cs5536/cs5536_mfgpt.h        |    5 +
 arch/mips/loongson/Kconfig                         |   35 ++
 arch/mips/loongson/common/cmdline.c                |   10 +
 arch/mips/loongson/lemote-2f/Makefile              |    5 +
 arch/mips/loongson/lemote-2f/lynloong_pc.c         |  609 ++++++++++++++++++++
 6 files changed, 699 insertions(+), 0 deletions(-)
 create mode 100644 arch/mips/loongson/lemote-2f/lynloong_pc.c

diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
index 021f77c..1cf86f3 100644
--- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
+++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536.h
@@ -301,5 +301,40 @@ extern void _wrmsr(u32 msr, u32 hi, u32 lo);
 /* GPIO : I/O SPACE; REG : 32BITS */
 #define	GPIOL_OUT_VAL		0x00
 #define	GPIOL_OUT_EN		0x04
+#define	GPIOL_OUT_AUX1_SEL	0x10
+/* SMB : I/O SPACE, REG : 8BITS WIDTH */
+#define	SMB_SDA			0x00
+#define	SMB_STS			0x01
+#define	SMB_STS_SLVSTP		(1 << 7)
+#define	SMB_STS_SDAST		(1 << 6)
+#define	SMB_STS_BER		(1 << 5)
+#define	SMB_STS_NEGACK		(1 << 4)
+#define	SMB_STS_STASTR		(1 << 3)
+#define	SMB_STS_NMATCH		(1 << 2)
+#define	SMB_STS_MASTER		(1 << 1)
+#define	SMB_STS_XMIT		(1 << 0)
+#define	SMB_CTRL_STS		0x02
+#define	SMB_CSTS_TGSTL		(1 << 5)
+#define	SMB_CSTS_TSDA		(1 << 4)
+#define	SMB_CSTS_GCMTCH		(1 << 3)
+#define	SMB_CSTS_MATCH		(1 << 2)
+#define	SMB_CSTS_BB		(1 << 1)
+#define	SMB_CSTS_BUSY		(1 << 0)
+#define	SMB_CTRL1		0x03
+#define	SMB_CTRL1_STASTRE	(1 << 7)
+#define	SMB_CTRL1_NMINTE	(1 << 6)
+#define	SMB_CTRL1_GCMEN		(1 << 5)
+#define	SMB_CTRL1_ACK		(1 << 4)
+#define	SMB_CTRL1_RSVD		(1 << 3)
+#define	SMB_CTRL1_INTEN		(1 << 2)
+#define	SMB_CTRL1_STOP		(1 << 1)
+#define	SMB_CTRL1_START		(1 << 0)
+#define	SMB_ADDR		0x04
+#define	SMB_ADDR_SAEN		(1 << 7)
+#define	SMB_CONTROLLER_ADDR	(0xef << 0)
+#define	SMB_CTRL2		0x05
+#define	SMB_FREQ		(0x20 << 1)
+#define	SMB_ENABLE		(0x01 << 0)
+#define	SMB_CTRL3		0x06
 
 #endif				/* _CS5536_H */
diff --git a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
index 4b493d6..cac04ee 100644
--- a/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
+++ b/arch/mips/include/asm/mach-loongson/cs5536/cs5536_mfgpt.h
@@ -32,4 +32,9 @@ static inline void __maybe_unused enable_mfgpt0_counter(void)
 #define MFGPT0_CNT	(MFGPT_BASE + 4)
 #define MFGPT0_SETUP	(MFGPT_BASE + 6)
 
+#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/loongson/Kconfig b/arch/mips/loongson/Kconfig
index 7a86987..1041336 100644
--- a/arch/mips/loongson/Kconfig
+++ b/arch/mips/loongson/Kconfig
@@ -78,3 +78,38 @@ config LOONGSON_SUSPEND
 	bool
 	default y
 	depends on CPU_SUPPORTS_CPUFREQ && SUSPEND
+
+#
+# Loongson Platform Specific Drivers
+#
+
+comment "Loongson Platform Specific Drivers"
+
+menuconfig LOONGSON_PLATFORM_DEVICES
+	bool "Loongson Platform Specific Drivers"
+	default y
+	---help---
+	  Say Y here to get to see options for device drivers for various
+	  loongson platforms, including vendor-specific 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 LOONGSON_PLATFORM_DEVICES
+
+config LEMOTE_LYNLOONG2F_PDEV
+	tristate "Lemote LynLoong(Allinone) Platform Specific Driver"
+	depends on LEMOTE_MACH2F
+	select THERMAL
+	select BACKLIGHT_CLASS_DEVICE
+	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.
+
+	  This driver adds the lynloong specific backlight driver and platform
+	  driver(mainly the suspend support).
+
+endif # LOONGSON_PLATFORM_DEVICES
diff --git a/arch/mips/loongson/common/cmdline.c b/arch/mips/loongson/common/cmdline.c
index 7ad47f2..13ce9b1 100644
--- a/arch/mips/loongson/common/cmdline.c
+++ b/arch/mips/loongson/common/cmdline.c
@@ -51,4 +51,14 @@ void __init prom_init_cmdline(void)
 		strcat(arcs_cmdline, " root=/dev/hda1");
 
 	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;
+	default:
+		break;
+	}
 }
diff --git a/arch/mips/loongson/lemote-2f/Makefile b/arch/mips/loongson/lemote-2f/Makefile
index 5add7b2..ad9e287 100644
--- a/arch/mips/loongson/lemote-2f/Makefile
+++ b/arch/mips/loongson/lemote-2f/Makefile
@@ -9,3 +9,8 @@ obj-y += irq.o reset.o
 #
 
 obj-$(CONFIG_LOONGSON_SUSPEND) += pm.o
+
+#
+# Platform Drivers
+#
+obj-$(CONFIG_LEMOTE_LYNLOONG2F_PDEV) += lynloong_pc.o
diff --git a/arch/mips/loongson/lemote-2f/lynloong_pc.c b/arch/mips/loongson/lemote-2f/lynloong_pc.c
new file mode 100644
index 0000000..5e84288
--- /dev/null
+++ b/arch/mips/loongson/lemote-2f/lynloong_pc.c
@@ -0,0 +1,609 @@
+/*
+ *  Driver for LynLoong pc extras
+ *
+ *  Copyright (C) 2009 Lemote Inc.
+ *  Author: Xiang Yu <xiangy@lemote.com>
+ *          Wu Zhangjin <wuzj@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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/suspend.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/thermal.h>
+
+#include <asm/bootinfo.h>
+
+#include <loongson.h>
+
+#include <cs5536/cs5536.h>
+#include <cs5536/cs5536_pci.h>
+#include <cs5536/cs5536_mfgpt.h>
+
+static u32 gpio_base, smb_base, mfgpt_base;
+
+/* gpio operations */
+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 uint level;
+
+/* tune the brightness */
+static void setup_mfgpt2(void)
+{
+	/* 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);
+}
+
+static int lynloong_set_brightness(struct backlight_device *bd)
+{
+	uint i;
+
+	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;
+
+	if (level == 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);
+	}
+
+	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 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);
+
+	printk(KERN_INFO "exit from LingLoong Backlight Driver");
+}
+
+static int __init lynloong_backlight_init(struct device *dev)
+{
+	int ret;
+
+	/* select for mfgpt */
+	set_gpio_reg_high(7, GPIOL_OUT_AUX1_SEL);
+	/* enable brightness controlling */
+	set_gpio_output_high(7);
+
+	lynloong_backlight_dev =
+	    backlight_device_register("backlight0", dev, NULL,
+				      &backlight_ops);
+
+	if (IS_ERR(lynloong_backlight_dev)) {
+		ret = PTR_ERR(lynloong_backlight_dev);
+		return ret;
+	}
+
+	lynloong_backlight_dev->props.max_brightness = MAX_BRIGHTNESS;
+	lynloong_backlight_dev->props.brightness = DEFAULT_BRIGHTNESS;
+	backlight_update_status(lynloong_backlight_dev);
+
+	return 0;
+}
+
+/* Thermal cooling devices subdriver */
+
+static int video_get_max_state(struct thermal_cooling_device *cdev, unsigned
+			       long *state)
+{
+	*state = MAX_BRIGHTNESS;
+	return 0;
+}
+
+static int video_get_cur_state(struct thermal_cooling_device *cdev, unsigned
+			       long *state)
+{
+	static struct backlight_device *bd;
+
+	bd = (struct backlight_device *)cdev->devdata;
+
+	*state = lynloong_get_brightness(bd);
+
+	return 0;
+}
+
+static int video_set_cur_state(struct thermal_cooling_device *cdev, unsigned
+			       long state)
+{
+	static struct backlight_device *bd;
+
+	bd = (struct backlight_device *)cdev->devdata;
+
+	lynloong_backlight_dev->props.brightness = state;
+	backlight_update_status(bd);
+
+	return 0;
+}
+
+static struct thermal_cooling_device_ops video_cooling_ops = {
+	.get_max_state = video_get_max_state,
+	.get_cur_state = video_get_cur_state,
+	.set_cur_state = video_set_cur_state,
+};
+
+static struct thermal_cooling_device *lynloong_thermal_cdev;
+
+/* TODO: register cpu as the cooling devices */
+static int lynloong_thermal_init(struct device *dev)
+{
+	int ret;
+
+	if (!dev)
+		return -1;
+
+	lynloong_thermal_cdev = thermal_cooling_device_register("LCD", dev,
+			&video_cooling_ops);
+
+	if (IS_ERR(lynloong_thermal_cdev)) {
+		ret = PTR_ERR(lynloong_thermal_cdev);
+		return ret;
+	}
+
+	ret = sysfs_create_link(&dev->kobj,
+				&lynloong_thermal_cdev->device.kobj,
+				"thermal_cooling");
+	if (ret) {
+		printk(KERN_ERR "Create sysfs link\n");
+		return ret;
+	}
+	ret = sysfs_create_link(&lynloong_thermal_cdev->device.kobj,
+				&dev->kobj, "device");
+	if (ret) {
+		printk(KERN_ERR "Create sysfs link\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void lynloong_thermal_exit(struct device *dev)
+{
+	if (lynloong_thermal_cdev) {
+		if (dev)
+			sysfs_remove_link(&dev->kobj, "thermal_cooling");
+		sysfs_remove_link(&lynloong_thermal_cdev->device.kobj,
+				  "device");
+		thermal_cooling_device_unregister(lynloong_thermal_cdev);
+		lynloong_thermal_cdev = NULL;
+	}
+}
+
+/* platform subdriver */
+
+/* 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 struct platform_device *lynloong_pdev;
+
+static int __maybe_unused lynloong_suspend(struct platform_device *pdev,
+		pm_message_t state)
+{
+	int i;
+
+	printk(KERN_INFO "lynloong specific suspend\n");
+
+	/* disable AMP */
+	set_gpio_output_high(6);
+	/* disable the brightness control */
+	set_gpio_output_low(7);
+	/* disable the backlight output */
+	set_gpio_output_low(11);
+
+	/* stop the clocks of some devices */
+	stop_clocks();
+
+	/* decrease the external clock frequency */
+	decrease_clk_freq();
+
+	/* turn off the LCD */
+	for (i = 0; i < 0x600; i++)
+		delay();
+	set_gpio_output_high(8);
+
+	return 0;
+}
+
+static int __maybe_unused lynloong_resume(struct platform_device *pdev)
+{
+	int i;
+
+	printk(KERN_INFO "lynloong specific resume\n");
+
+	/* turn on the LCD */
+	set_gpio_output_low(8);
+	for (i = 0; i < 0x1000; i++)
+		delay();
+
+	/* resume clock frequency, enable the relative clocks */
+	resume_clk_freq();
+	enable_clocks();
+
+	/* enable the backlight output */
+	set_gpio_output_high(11);
+	/* enable the brightness control */
+	set_gpio_output_high(7);
+	/* enable AMP */
+	set_gpio_output_low(6);
+
+	return 0;
+}
+
+static struct platform_driver platform_driver = {
+	.driver = {
+		   .name = "lynloong-pc",
+		   .owner = THIS_MODULE,
+		   },
+#ifdef CONFIG_PM
+	.suspend = lynloong_suspend,
+	.resume = lynloong_resume,
+#endif
+};
+
+static ssize_t lynloong_pdev_name_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "lynloong pc\n");
+}
+
+static struct device_attribute dev_attr_lynloong_pdev_name =
+__ATTR(name, S_IRUGO, lynloong_pdev_name_show, NULL);
+
+static int lynloong_pdev_init(void)
+{
+	int ret;
+
+	/* Register platform stuff */
+	ret = platform_driver_register(&platform_driver);
+	if (ret)
+		return ret;
+
+	lynloong_pdev = platform_device_alloc("lynloong-laptop", -1);
+	if (!lynloong_pdev) {
+		ret = -ENOMEM;
+		platform_driver_unregister(&platform_driver);
+		return ret;
+	}
+
+	ret = platform_device_add(lynloong_pdev);
+	if (ret) {
+		platform_device_put(lynloong_pdev);
+		return ret;
+	}
+
+	if (IS_ERR(lynloong_pdev)) {
+		ret = PTR_ERR(lynloong_pdev);
+		lynloong_pdev = NULL;
+		printk(KERN_INFO "unable to register platform device\n");
+		return ret;
+	}
+
+	ret = device_create_file(&lynloong_pdev->dev,
+				 &dev_attr_lynloong_pdev_name);
+	if (ret) {
+		printk(KERN_INFO "unable to create sysfs device attributes\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void lynloong_pdev_exit(void)
+{
+	if (lynloong_pdev) {
+		platform_device_unregister(lynloong_pdev);
+		lynloong_pdev = NULL;
+		platform_driver_unregister(&platform_driver);
+	}
+}
+
+static int __init lynloong_init(void)
+{
+	int ret;
+	u32 hi;
+
+	if (mips_machtype != MACH_LEMOTE_LL2F) {
+		printk(KERN_INFO "This Driver is for LynLoong(Allinone) PC, You"
+				"can not use it on the other Machines\n");
+		return -EFAULT;
+	}
+
+	/* 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);
+	/* get smb base */
+	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_SMB), &hi, &smb_base);
+
+	ret = lynloong_pdev_init();
+	if (ret) {
+		lynloong_pdev_exit();
+		printk(KERN_INFO "init lynloong platform driver failure\n");
+		return ret;
+	}
+
+	ret = lynloong_backlight_init(&lynloong_pdev->dev);
+	if (ret) {
+		lynloong_backlight_exit();
+		printk(KERN_INFO "init lynloong backlight driver failure\n");
+		return ret;
+	}
+	ret = lynloong_thermal_init(&lynloong_backlight_dev->dev);
+	if (ret) {
+		lynloong_thermal_exit(&lynloong_backlight_dev->dev);
+		printk(KERN_INFO
+		       "init lynloong thermal cooling device failure\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __exit lynloong_exit(void)
+{
+	lynloong_pdev_exit();
+	lynloong_thermal_exit(&lynloong_backlight_dev->dev);
+	lynloong_backlight_exit();
+}
+
+module_init(lynloong_init);
+module_exit(lynloong_exit);
+
+MODULE_AUTHOR("Xiang Yu <xiangy@lemote.com>; Wu Zhangjin <wuzj@lemote.com>");
+MODULE_DESCRIPTION("LynLoong Platform Specific Driver");
+MODULE_LICENSE("GPL");
-- 
1.6.2.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/5] [loongson] yeeloong2f: add basic ec operations
  2009-11-19 17:32 [PATCH 0/5] Lemote-2f: Add Platform Specific Support Wu Zhangjin
  2009-11-19 17:33 ` [PATCH 1/5] [loongson] LynLoong2F: " Wu Zhangjin
@ 2009-11-19 17:33 ` Wu Zhangjin
  2009-11-19 17:34 ` [PATCH 3/5] [loongson] yeeloong2f: add LID open event as the wakeup event Wu Zhangjin
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 13+ messages in thread
From: Wu Zhangjin @ 2009-11-19 17:33 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips, zhangfx, yanh, huhb, liujl, huangw, Wu Zhangjin

YeeLoong2F has a kb3310b Embedded Controller. This patch add the basic
EC operations for future relative drivers or board support.

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 arch/mips/loongson/lemote-2f/Makefile     |    2 +-
 arch/mips/loongson/lemote-2f/ec_kb3310b.c |  132 +++++++++++++++++++++++++++++
 arch/mips/loongson/lemote-2f/ec_kb3310b.h |   47 ++++++++++
 3 files changed, 180 insertions(+), 1 deletions(-)
 create mode 100644 arch/mips/loongson/lemote-2f/ec_kb3310b.c
 create mode 100644 arch/mips/loongson/lemote-2f/ec_kb3310b.h

diff --git a/arch/mips/loongson/lemote-2f/Makefile b/arch/mips/loongson/lemote-2f/Makefile
index ad9e287..81a97d0 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 += irq.o reset.o
+obj-y += irq.o reset.o ec_kb3310b.o
 
 #
 # Suspend Support
diff --git a/arch/mips/loongson/lemote-2f/ec_kb3310b.c b/arch/mips/loongson/lemote-2f/ec_kb3310b.c
new file mode 100644
index 0000000..1ca38a8
--- /dev/null
+++ b/arch/mips/loongson/lemote-2f/ec_kb3310b.c
@@ -0,0 +1,132 @@
+/*
+ * EC(Embedded Controller) KB3310B basic support for YeeLoong2F netbook
+ *
+ *  Copyright (C) 2008 Lemote Inc.
+ *  Author: liujl <liujl@lemote.com>, 2008-04-20
+ *
+ * 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/delay.h>
+
+#include "ec_kb3310b.h"
+
+/* this spinlock is dedicated for ec_read & ec_write function */
+DEFINE_SPINLOCK(index_access_lock);
+/* this spinlock is dedicated for 62&66 ports access */
+DEFINE_SPINLOCK(port_access_lock);
+
+/* read a byte from EC registers throught index-io */
+unsigned char ec_read(unsigned short addr)
+{
+	unsigned char value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&index_access_lock, flags);
+	outb((addr & 0xff00) >> 8, EC_IO_PORT_HIGH);
+	outb((addr & 0x00ff), EC_IO_PORT_LOW);
+	value = inb(EC_IO_PORT_DATA);
+	spin_unlock_irqrestore(&index_access_lock, flags);
+
+	return value;
+}
+EXPORT_SYMBOL_GPL(ec_read);
+
+/* write a byte to EC registers throught index-io */
+void ec_write(unsigned short addr, unsigned char val)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&index_access_lock, flags);
+	outb((addr & 0xff00) >> 8, EC_IO_PORT_HIGH);
+	outb((addr & 0x00ff), EC_IO_PORT_LOW);
+	outb(val, EC_IO_PORT_DATA);
+	/*  flush the write action */
+	inb(EC_IO_PORT_DATA);
+	spin_unlock_irqrestore(&index_access_lock, flags);
+
+	return;
+}
+EXPORT_SYMBOL_GPL(ec_write);
+
+/*
+ * this function is used for ec command writing and corresponding status query
+ */
+int ec_query_seq(unsigned char cmd)
+{
+	int timeout;
+	unsigned char status;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&port_access_lock, flags);
+
+	/* make chip goto reset mode */
+	udelay(EC_REG_DELAY);
+	outb(cmd, EC_CMD_PORT);
+	udelay(EC_REG_DELAY);
+
+	/* check if the command is received by ec */
+	timeout = EC_CMD_TIMEOUT;
+	status = inb(EC_STS_PORT);
+	while (timeout-- && (status & (1 << 1))) {
+		status = inb(EC_STS_PORT);
+		udelay(EC_REG_DELAY);
+	}
+
+	if (timeout <= 0) {
+		printk(KERN_ERR "%s: deadable error : timeout...\n", __func__);
+		ret = -EINVAL;
+	} else
+		printk(KERN_INFO
+			   "(%x/%d)ec issued command %d status : 0x%x\n",
+			   timeout, EC_CMD_TIMEOUT - timeout, cmd, status);
+
+	spin_unlock_irqrestore(&port_access_lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ec_query_seq);
+
+/*
+ * using query command to ec to get the proper event number
+ */
+int ec_query_event_num(void)
+{
+	return ec_query_seq(CMD_GET_EVENT_NUM);
+}
+EXPORT_SYMBOL(ec_query_event_num);
+
+/*
+ * get event number from ec
+ *
+ * NOTE : this routine must follow the query_event_num function in the
+ * interrupt
+ */
+int ec_get_event_num(void)
+{
+	int timeout = 100;
+	unsigned char value;
+	unsigned char status;
+
+	udelay(EC_REG_DELAY);
+	status = inb(EC_STS_PORT);
+	udelay(EC_REG_DELAY);
+	while (timeout-- && !(status & (1 << 0))) {
+		status = inb(EC_STS_PORT);
+		udelay(EC_REG_DELAY);
+	}
+	if (timeout <= 0) {
+		printk(KERN_INFO "%s: get event number timeout.\n", __func__);
+		return -EINVAL;
+	}
+	value = inb(EC_DAT_PORT);
+	udelay(EC_REG_DELAY);
+
+	return value;
+}
+EXPORT_SYMBOL(ec_get_event_num);
diff --git a/arch/mips/loongson/lemote-2f/ec_kb3310b.h b/arch/mips/loongson/lemote-2f/ec_kb3310b.h
new file mode 100644
index 0000000..4d29953
--- /dev/null
+++ b/arch/mips/loongson/lemote-2f/ec_kb3310b.h
@@ -0,0 +1,47 @@
+/*
+ * EC(Embedded Controller) KB3310B header file
+ *
+ *  Copyright (C) 2008 Lemote Inc. & Insititute of Computing Technology
+ *  Author: liujl <liujl@lemote.com>, 2008-03-14
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _EC_KB3310B_H
+#define _EC_KB3310B_H
+
+extern unsigned char ec_read(unsigned short addr);
+extern void ec_write(unsigned short addr, unsigned char val);
+extern int ec_query_seq(unsigned char cmd);
+extern int ec_query_event_num(void);
+extern int ec_get_event_num(void);
+
+/*
+ * The following registers are determined by the EC index configuration.
+ * 1, fill the PORT_HIGH as EC register high part.
+ * 2, fill the PORT_LOW as EC register low part.
+ * 3, fill the PORT_DATA as EC register write data or get the data from it.
+ */
+#define	EC_IO_PORT_HIGH	0x0381
+#define	EC_IO_PORT_LOW	0x0382
+#define	EC_IO_PORT_DATA	0x0383
+
+/* ec delay time 500us for register and status access */
+#define	EC_REG_DELAY	500	/* unit : us */
+#define	EC_CMD_TIMEOUT		0x1000
+
+/* EC access port for sci communication */
+#define	EC_CMD_PORT		0x66
+#define	EC_STS_PORT		0x66
+#define	EC_DAT_PORT		0x62
+#define	CMD_INIT_IDLE_MODE	0xdd
+#define	CMD_EXIT_IDLE_MODE	0xdf
+#define	CMD_INIT_RESET_MODE	0xd8
+#define	CMD_REBOOT_SYSTEM	0x8c
+#define	CMD_GET_EVENT_NUM	0x84
+#define	CMD_PROGRAM_PIECE	0xda
+
+#endif /* !_EC_KB3310B_H */
-- 
1.6.2.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/5] [loongson] yeeloong2f: add LID open event as the wakeup event
  2009-11-19 17:32 [PATCH 0/5] Lemote-2f: Add Platform Specific Support Wu Zhangjin
  2009-11-19 17:33 ` [PATCH 1/5] [loongson] LynLoong2F: " Wu Zhangjin
  2009-11-19 17:33 ` [PATCH 2/5] [loongson] yeeloong2f: add basic ec operations Wu Zhangjin
@ 2009-11-19 17:34 ` Wu Zhangjin
  2009-11-19 17:34 ` [PATCH 4/5] [loongson] yeeloong2f: cleanup the reset logic with ec_write function Wu Zhangjin
  2009-11-19 17:39   ` [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform Wu Zhangjin
  4 siblings, 0 replies; 13+ messages in thread
From: Wu Zhangjin @ 2009-11-19 17:34 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips, zhangfx, yanh, huhb, Wu Zhangjin

Yeeloong2F netbook has an embedded controller(kb3310b) to manage the LID
action. when the LID is closed/opened, a relative SCI interrupt take
place, and the relative event is saved to a relative EC register.

This patch make the LID open action can wake up the processor from wait
mode if it in the suspend mode.

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 arch/mips/include/asm/mach-loongson/loongson.h |    1 +
 arch/mips/loongson/lemote-2f/irq.c             |    4 +-
 arch/mips/loongson/lemote-2f/pm.c              |   72 ++++++++++++++++++++++++
 3 files changed, 76 insertions(+), 1 deletions(-)

diff --git a/arch/mips/include/asm/mach-loongson/loongson.h b/arch/mips/include/asm/mach-loongson/loongson.h
index daf7041..71ce760 100644
--- a/arch/mips/include/asm/mach-loongson/loongson.h
+++ b/arch/mips/include/asm/mach-loongson/loongson.h
@@ -41,6 +41,7 @@ extern void __init bonito_irq_init(void);
 extern void __init set_irq_trigger_mode(void);
 extern void __init mach_init_irq(void);
 extern void mach_irq_dispatch(unsigned int pending);
+extern int mach_i8259_irq(void);
 
 /* We need this in some places... */
 #define delay()	({		\
diff --git a/arch/mips/loongson/lemote-2f/irq.c b/arch/mips/loongson/lemote-2f/irq.c
index 50e7bb6..77d32f9 100644
--- a/arch/mips/loongson/lemote-2f/irq.c
+++ b/arch/mips/loongson/lemote-2f/irq.c
@@ -9,6 +9,7 @@
  */
 
 #include <linux/interrupt.h>
+#include <linux/module.h>
 
 #include <asm/irq_cpu.h>
 #include <asm/i8259.h>
@@ -30,7 +31,7 @@
  * The generic i8259_irq() make the kernel hang on booting.  Since we cannot
  * get the irq via the IRR directly, we access the ISR instead.
  */
-static inline int mach_i8259_irq(void)
+int mach_i8259_irq(void)
 {
 	int irq, isr;
 
@@ -60,6 +61,7 @@ static inline int mach_i8259_irq(void)
 
 	return irq;
 }
+EXPORT_SYMBOL(mach_i8259_irq);
 
 static void i8259_irqdispatch(void)
 {
diff --git a/arch/mips/loongson/lemote-2f/pm.c b/arch/mips/loongson/lemote-2f/pm.c
index 8090d05..7202177 100644
--- a/arch/mips/loongson/lemote-2f/pm.c
+++ b/arch/mips/loongson/lemote-2f/pm.c
@@ -14,6 +14,7 @@
 #include <linux/interrupt.h>
 #include <linux/pm.h>
 #include <linux/i8042.h>
+#include <linux/module.h>
 
 #include <asm/i8259.h>
 #include <asm/mipsregs.h>
@@ -21,6 +22,8 @@
 
 #include <loongson.h>
 
+#include "ec_kb3310b.h"
+
 #define I8042_KBD_IRQ		1
 #define I8042_CTR_KBDINT	0x01
 #define I8042_CTR_KBDDIS	0x10
@@ -52,6 +55,9 @@ static int i8042_enable_kbd_port(void)
 /*
  * The i8042 is connnected to i8259A
  */
+#define PIC_CASCADE_IR 2	/* cascade irq num of i8259A */
+
+/* i8042 is connnectted to i8259A */
 void setup_wakeup_events(void)
 {
 	int irq_mask;
@@ -65,9 +71,75 @@ void setup_wakeup_events(void)
 
 		/* enable keyboard port */
 		i8042_enable_kbd_port();
+
+		/* wakeup cpu via sci with relative lid openning event */
+		outb(irq_mask & ~(1 << PIC_CASCADE_IR), PIC_MASTER_IMR);
+		inb(PIC_MASTER_IMR);
+		outb(0xff & ~(1 << (SCI_IRQ_NUM - 8)), PIC_SLAVE_IMR);
+		inb(PIC_SLAVE_IMR);
+
 		break;
 
 	default:
 		break;
 	}
 }
+
+static struct delayed_work lid_task;
+static int initialized;
+/* yeeloong_report_lid_status will be implemented in yeeloong_laptop.c */
+sci_handler yeeloong_report_lid_status;
+EXPORT_SYMBOL(yeeloong_report_lid_status);
+static void yeeloong_lid_update_task(struct work_struct *work)
+{
+	if (yeeloong_report_lid_status)
+		yeeloong_report_lid_status(BIT_LID_DETECT_ON);
+}
+
+int wakeup_loongson(void)
+{
+	int irq;
+
+	/* query the interrupt number */
+	irq = mach_i8259_irq();
+	if (irq < 0)
+		return 0;
+
+	printk(KERN_INFO "irq = %d\n", irq);
+
+	if (irq == I8042_KBD_IRQ)
+		return 1;
+	else if (irq == SCI_IRQ_NUM) {
+		int ret, sci_event;
+		/* query the event number */
+		ret = ec_query_seq(CMD_GET_EVENT_NUM);
+		if (ret < 0)
+			return 0;
+		sci_event = ec_get_event_num();
+		if (sci_event < 0)
+			return 0;
+		if (sci_event == EVENT_LID) {
+			int lid_status;
+			/* check the LID status */
+			lid_status = ec_read(REG_LID_DETECT);
+			/* wakeup cpu when people open the LID */
+			if (lid_status == BIT_LID_DETECT_ON) {
+				/* if we call it directly here, the WARNING
+				 * will happen at line 98 of
+				 * kerenl/time/timekeeping.c (getnstimeofday)
+				 * via "WARN_ON(timekeeping_suspended);" because,
+				 * currently, we are in the suspend status
+				 */
+				if (initialized == 0) {
+					/* init the rfkill work task */
+					INIT_DELAYED_WORK(&lid_task, yeeloong_lid_update_task);
+					initialized = 1;
+				}
+				schedule_delayed_work(&lid_task, 1);
+				return 1;
+			}
+		}
+	}
+
+	return 0;
+}
-- 
1.6.2.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/5] [loongson] yeeloong2f: cleanup the reset logic with ec_write function
  2009-11-19 17:32 [PATCH 0/5] Lemote-2f: Add Platform Specific Support Wu Zhangjin
                   ` (2 preceding siblings ...)
  2009-11-19 17:34 ` [PATCH 3/5] [loongson] yeeloong2f: add LID open event as the wakeup event Wu Zhangjin
@ 2009-11-19 17:34 ` Wu Zhangjin
  2009-11-19 17:39   ` [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform Wu Zhangjin
  4 siblings, 0 replies; 13+ messages in thread
From: Wu Zhangjin @ 2009-11-19 17:34 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips, zhangfx, yanh, huhb, Wu Zhangjin

A commen ec_read/ec_write function is defined in "[loongson] yeeloong2f:
add basic ec operations", So, we can use it here to cleanup the reset
logic of yeeloong2f netbook.

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 arch/mips/loongson/lemote-2f/reset.c |   21 ++-------------------
 1 files changed, 2 insertions(+), 19 deletions(-)

diff --git a/arch/mips/loongson/lemote-2f/reset.c b/arch/mips/loongson/lemote-2f/reset.c
index 44bb984..51d1a60 100644
--- a/arch/mips/loongson/lemote-2f/reset.c
+++ b/arch/mips/loongson/lemote-2f/reset.c
@@ -20,6 +20,7 @@
 #include <loongson.h>
 
 #include <cs5536/cs5536.h>
+#include "ec_kb3310b.h"
 
 static void reset_cpu(void)
 {
@@ -75,30 +76,12 @@ static void fl2f_shutdown(void)
 
 /* reset support for yeeloong2f and mengloong2f notebook */
 
-/*
- * The following registers are determined by the EC index configuration.
- * 1. fill the PORT_HIGH as EC register high part.
- * 2. fill the PORT_LOW as EC register low part.
- * 3. fill the PORT_DATA as EC register write data or get the data from it.
- */
-
-#define	EC_IO_PORT_HIGH	0x0381
-#define	EC_IO_PORT_LOW	0x0382
-#define	EC_IO_PORT_DATA	0x0383
-#define	REG_RESET_HIGH	0xF4	/* reset the machine auto-clear : rd/wr */
-#define REG_RESET_LOW	0xEC
-#define	BIT_RESET_ON	(1 << 0)
-
 void ml2f_reboot(void)
 {
 	reset_cpu();
 
 	/* sending an reset signal to EC(embedded controller) */
-	outb(REG_RESET_HIGH, EC_IO_PORT_HIGH);
-	outb(REG_RESET_LOW, EC_IO_PORT_LOW);
-	mmiowb();
-	outb(BIT_RESET_ON, EC_IO_PORT_DATA);
-	mmiowb();
+	ec_write(REG_RESET, BIT_RESET_ON);
 }
 
 #define yl2f89_reboot ml2f_reboot
-- 
1.6.2.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 5/5] [loongson] yeeloong2f: add platform specific support
  2009-11-19 17:32 [PATCH 0/5] Lemote-2f: Add Platform Specific Support Wu Zhangjin
@ 2009-11-19 17:39   ` Wu Zhangjin
  2009-11-19 17:33 ` [PATCH 2/5] [loongson] yeeloong2f: add basic ec operations Wu Zhangjin
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 13+ messages in thread
From: Wu Zhangjin @ 2009-11-19 17:39 UTC (permalink / raw)
  To: Ralf Baechle
  Cc: linux-mips, zhangfx, yanh, huhb, Richard Purdie, lm-sensors,
	Jamey Hicks, Wu Zhangjin

This patch added the following subdrivers: backlight, hotkey, video
output, thermal cooling, hwmon and battery.

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 arch/mips/kernel/setup.c                       |    1 +
 arch/mips/loongson/Kconfig                     |   18 +
 arch/mips/loongson/lemote-2f/Makefile          |    1 +
 arch/mips/loongson/lemote-2f/ec_kb3310b.h      |  150 +++
 arch/mips/loongson/lemote-2f/yeeloong_laptop.c | 1354 ++++++++++++++++++++++++
 5 files changed, 1524 insertions(+), 0 deletions(-)
 create mode 100644 arch/mips/loongson/lemote-2f/yeeloong_laptop.c

diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
index bd55f71..b1e1272 100644
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -60,6 +60,7 @@ struct boot_mem_map boot_mem_map;
 
 static char command_line[COMMAND_LINE_SIZE];
        char arcs_cmdline[COMMAND_LINE_SIZE] = CONFIG_CMDLINE;
+EXPORT_SYMBOL(arcs_cmdline);
 
 /*
  * mips_io_port_base is the begin of the address space to which x86 style
diff --git a/arch/mips/loongson/Kconfig b/arch/mips/loongson/Kconfig
index 1041336..34a94b0 100644
--- a/arch/mips/loongson/Kconfig
+++ b/arch/mips/loongson/Kconfig
@@ -112,4 +112,22 @@ config LEMOTE_LYNLOONG2F_PDEV
 	  This driver adds the lynloong specific backlight driver and platform
 	  driver(mainly the suspend support).
 
+config LEMOTE_YEELOONG2F_PDEV
+	tristate "Lemote YeeLoong Platform Specific Driver"
+	depends on LEMOTE_MACH2F
+	default m
+	select INPUT_EVDEV
+	select HWMON
+	select VIDEO_OUTPUT_CONTROL
+	select THERMAL
+	select BACKLIGHT_CLASS_DEVICE
+	select SYS_SUPPORTS_APM_EMULATION
+	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.
+
+	  This driver adds the YeeLoong Platform Specific support.
+
 endif # LOONGSON_PLATFORM_DEVICES
diff --git a/arch/mips/loongson/lemote-2f/Makefile b/arch/mips/loongson/lemote-2f/Makefile
index 81a97d0..4e8bf5d 100644
--- a/arch/mips/loongson/lemote-2f/Makefile
+++ b/arch/mips/loongson/lemote-2f/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_LOONGSON_SUSPEND) += pm.o
 # Platform Drivers
 #
 obj-$(CONFIG_LEMOTE_LYNLOONG2F_PDEV) += lynloong_pc.o
+obj-$(CONFIG_LEMOTE_YEELOONG2F_PDEV) += yeeloong_laptop.o
diff --git a/arch/mips/loongson/lemote-2f/ec_kb3310b.h b/arch/mips/loongson/lemote-2f/ec_kb3310b.h
index 4d29953..00530e3 100644
--- a/arch/mips/loongson/lemote-2f/ec_kb3310b.h
+++ b/arch/mips/loongson/lemote-2f/ec_kb3310b.h
@@ -19,6 +19,13 @@ extern int ec_query_seq(unsigned char cmd);
 extern int ec_query_event_num(void);
 extern int ec_get_event_num(void);
 
+typedef int (*sci_handler) (int status);
+extern sci_handler yeeloong_report_lid_status;
+extern int yeeloong_install_sci_handler(int event, sci_handler handler);
+extern int yeeloong_uninstall_sci_handler(int event, sci_handler handler);
+
+#define SCI_IRQ_NUM 0x0A
+
 /*
  * The following registers are determined by the EC index configuration.
  * 1, fill the PORT_HIGH as EC register high part.
@@ -44,4 +51,147 @@ extern int ec_get_event_num(void);
 #define	CMD_GET_EVENT_NUM	0x84
 #define	CMD_PROGRAM_PIECE	0xda
 
+/* temperature & fan registers */
+#define	REG_TEMPERATURE_VALUE	0xF458	/*  current temperature value */
+#define	REG_FAN_AUTO_MAN_SWITCH 0xF459  /*  fan auto/manual control switch */
+#define	BIT_FAN_AUTO		0
+#define	BIT_FAN_MANUAL		1
+#define	REG_FAN_CONTROL		0xF4D2	/*  fan control */
+#define	BIT_FAN_CONTROL_ON	(1 << 0)
+#define	BIT_FAN_CONTROL_OFF	(0 << 0)
+#define	REG_FAN_STATUS		0xF4DA	/*  fan status */
+#define	BIT_FAN_STATUS_ON	(1 << 0)
+#define	BIT_FAN_STATUS_OFF	(0 << 0)
+#define	REG_FAN_SPEED_HIGH	0xFE22	/*  fan speed high byte */
+#define	REG_FAN_SPEED_LOW	0xFE23	/*  fan speed low byte */
+#define	REG_FAN_SPEED_LEVEL	0xF4CC	/*  fan speed level, from 1 to 5 */
+/* fan speed divider */
+#define	FAN_SPEED_DIVIDER	480000	/* (60*1000*1000/62.5/2)*/
+
+/* battery registers */
+#define	REG_BAT_DESIGN_CAP_HIGH		0xF77D
+#define	REG_BAT_DESIGN_CAP_LOW		0xF77E
+#define	REG_BAT_FULLCHG_CAP_HIGH	0xF780
+#define	REG_BAT_FULLCHG_CAP_LOW		0xF781
+#define	REG_BAT_DESIGN_VOL_HIGH		0xF782
+#define	REG_BAT_DESIGN_VOL_LOW		0xF783
+#define	REG_BAT_CURRENT_HIGH		0xF784
+#define	REG_BAT_CURRENT_LOW		0xF785
+#define	REG_BAT_VOLTAGE_HIGH		0xF786
+#define	REG_BAT_VOLTAGE_LOW		0xF787
+#define	REG_BAT_TEMPERATURE_HIGH	0xF788
+#define	REG_BAT_TEMPERATURE_LOW		0xF789
+#define	REG_BAT_RELATIVE_CAP_HIGH	0xF492
+#define	REG_BAT_RELATIVE_CAP_LOW	0xF493
+#define	REG_BAT_VENDOR			0xF4C4
+#define	FLAG_BAT_VENDOR_SANYO		0x01
+#define	FLAG_BAT_VENDOR_SIMPLO		0x02
+#define	REG_BAT_CELL_COUNT		0xF4C6
+#define	FLAG_BAT_CELL_3S1P		0x03
+#define	FLAG_BAT_CELL_3S2P		0x06
+#define	REG_BAT_CHARGE			0xF4A2
+#define	FLAG_BAT_CHARGE_DISCHARGE	0x01
+#define	FLAG_BAT_CHARGE_CHARGE		0x02
+#define	FLAG_BAT_CHARGE_ACPOWER		0x00
+#define	REG_BAT_STATUS			0xF4B0
+#define	BIT_BAT_STATUS_LOW		(1 << 5)
+#define	BIT_BAT_STATUS_DESTROY		(1 << 2)
+#define	BIT_BAT_STATUS_FULL		(1 << 1)
+#define	BIT_BAT_STATUS_IN		(1 << 0)
+#define	REG_BAT_CHARGE_STATUS		0xF4B1
+#define	BIT_BAT_CHARGE_STATUS_OVERTEMP	(1 << 2)
+#define	BIT_BAT_CHARGE_STATUS_PRECHG	(1 << 1)
+#define	REG_BAT_STATE			0xF482
+#define	BIT_BAT_STATE_CHARGING		(1 << 1)
+#define	BIT_BAT_STATE_DISCHARGING	(1 << 0)
+#define	REG_BAT_POWER			0xF440
+#define	BIT_BAT_POWER_S3		(1 << 2)
+#define	BIT_BAT_POWER_ON		(1 << 1)
+#define	BIT_BAT_POWER_ACIN		(1 << 0)
+
+/* other registers */
+/* Audio: rd/wr */
+#define	REG_AUDIO_VOLUME	0xF46C  /* volume level */
+#define	REG_AUDIO_MUTE		0xF4E7
+#define	REG_AUDIO_BEEP		0xF4D0
+/* USB port power or not: rd/wr */
+#define	REG_USB0_FLAG		0xF461
+#define	REG_USB1_FLAG		0xF462
+#define	REG_USB2_FLAG		0xF463
+#define	BIT_USB_FLAG_ON		1
+#define	BIT_USB_FLAG_OFF	0
+/* LID */
+#define	REG_LID_DETECT		0xF4BD
+#define	BIT_LID_DETECT_ON	1
+#define	BIT_LID_DETECT_OFF	0
+/* CRT */
+#define	REG_CRT_DETECT		0xF4AD
+#define	BIT_CRT_DETECT_PLUG	1
+#define	BIT_CRT_DETECT_UNPLUG	0
+/* LCD backlight brightness adjust: 9 levels */
+#define	REG_DISPLAY_BRIGHTNESS	0xF4F5
+/* Black screen Status */
+#define	BIT_DISPLAY_LCD_ON	1
+#define	BIT_DISPLAY_LCD_OFF	0
+/* LCD backlight control: off/restore */
+#define	REG_BACKLIGHT_CTRL	0xF7BD
+#define	BIT_BACKLIGHT_ON	1
+#define	BIT_BACKLIGHT_OFF	0
+/* Reset the machine auto-clear: rd/wr */
+#define	REG_RESET		0xF4EC
+#define	BIT_RESET_ON		1
+#define	BIT_RESET_OFF		0
+/* Light the led: rd/wr */
+#define	REG_LED			0xF4C8
+#define	BIT_LED_RED_POWER	(1 << 0)
+#define	BIT_LED_ORANGE_POWER	(1 << 1)
+#define	BIT_LED_GREEN_CHARGE	(1 << 2)
+#define	BIT_LED_RED_CHARGE	(1 << 3)
+#define	BIT_LED_NUMLOCK		(1 << 4)
+/* Test led mode, all led on/off */
+#define	REG_LED_TEST		0xF4C2
+#define	BIT_LED_TEST_IN		1
+#define	BIT_LED_TEST_OUT	0
+/* Camera on/off */
+#define	REG_CAMERA_STATUS	0xF46A
+#define	BIT_CAMERA_STATUS_ON	1
+#define	BIT_CAMERA_STATUS_OFF	0
+#define	REG_CAMERA_CONTROL	0xF7B7
+#define	BIT_CAMERA_CONTROL_OFF	0
+#define	BIT_CAMERA_CONTROL_ON	1
+/* Wlan Status */
+#define	REG_WLAN		0xF4FA
+#define	BIT_WLAN_ON		1
+#define	BIT_WLAN_OFF		0
+#define	REG_DISPLAY_LCD		0xF79F
+
+/* SCI Event Number from EC */
+enum {
+	EVENT_LID = 0x23,	/*  press the lid or not */
+	EVENT_DISPLAY_TOGGLE,	/*  Fn+F3 for display switch */
+	EVENT_SLEEP,		/*  Fn+F1 for entering sleep mode */
+	EVENT_OVERTEMP,		/*  Over-temperature happened */
+	EVENT_CRT_DETECT,	/*  CRT is connected */
+	EVENT_CAMERA,		/*  Camera is on or off */
+	EVENT_USB_OC2,		/*  USB2 Over Current occurred */
+	EVENT_USB_OC0,		/*  USB0 Over Current occurred */
+	EVENT_BLACK_SCREEN,	/*  Black screen is on or off */
+	EVENT_AUDIO_MUTE,	/*  Mute is on or off */
+	EVENT_DISPLAY_BRIGHTNESS,/*  LCD backlight brightness adjust */
+	EVENT_AC_BAT,		/*  ac & battery relative issue */
+	EVENT_AUDIO_VOLUME,	/*  Volume adjust */
+	EVENT_WLAN,		/*  Wlan is on or off */
+	EVENT_END
+};
+
+enum {
+	BIT_AC_BAT_BAT_IN = 0,
+	BIT_AC_BAT_AC_IN,
+	BIT_AC_BAT_INIT_CAP,
+	BIT_AC_BAT_CHARGE_MODE,
+	BIT_AC_BAT_STOP_CHARGE,
+	BIT_AC_BAT_BAT_LOW,
+	BIT_AC_BAT_BAT_FULL
+};
+
 #endif /* !_EC_KB3310B_H */
diff --git a/arch/mips/loongson/lemote-2f/yeeloong_laptop.c b/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
new file mode 100644
index 0000000..ff74f7f
--- /dev/null
+++ b/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
@@ -0,0 +1,1354 @@
+/*
+ *  Driver for YeeLoong laptop extras
+ *
+ *  Copyright (C) 2009 Lemote Inc.
+ *  Author: Wu Zhangjin <wuzj@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/module.h>
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/video_output.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+#include <linux/apm-emulation.h>
+
+#include <asm/bootinfo.h>	/* for arcs_cmdline */
+
+#include <loongson.h>
+
+#include <cs5536/cs5536.h>
+#include "ec_kb3310b.h"
+
+#define DRIVER_VERSION "0.1"
+
+/* backlight subdriver */
+#define MAX_BRIGHTNESS 8
+
+static int yeeloong_set_brightness(struct backlight_device *bd)
+{
+	unsigned int level, current_level;
+	static unsigned int old_level;
+
+	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 < 0)
+		level = 0;
+
+	/* avoid tune the brightness when the EC is tuning it */
+	current_level = ec_read(REG_DISPLAY_BRIGHTNESS);
+	if ((old_level == current_level) && (old_level != level))
+		ec_write(REG_DISPLAY_BRIGHTNESS, level);
+	old_level = level;
+
+	return 0;
+}
+
+static int yeeloong_get_brightness(struct backlight_device *bd)
+{
+	return (int)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(struct device *dev)
+{
+	int ret;
+
+	yeeloong_backlight_dev = backlight_device_register("backlight0", dev,
+			NULL, &backlight_ops);
+
+	if (IS_ERR(yeeloong_backlight_dev)) {
+		ret = PTR_ERR(yeeloong_backlight_dev);
+		yeeloong_backlight_dev = NULL;
+		return ret;
+	}
+
+	yeeloong_backlight_dev->props.max_brightness = MAX_BRIGHTNESS;
+	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;
+	}
+}
+
+/* hwmon subdriver */
+
+/* pwm(auto/manual) enable or not */
+static int get_fan_pwm_enable(void)
+{
+	/* This get the fan control method: auto or manual */
+	return ec_read(REG_FAN_AUTO_MAN_SWITCH);
+}
+
+static void set_fan_pwm_enable(int manual)
+{
+	ec_write(REG_FAN_AUTO_MAN_SWITCH, !!manual);
+}
+
+static int get_fan_pwm(void)
+{
+	/* fan speed level */
+	return ec_read(REG_FAN_SPEED_LEVEL);
+}
+
+static void set_fan_pwm(int value)
+{
+	int status;
+
+	value = SENSORS_LIMIT(value, 0, 3);
+
+	/* if value is not ZERO, we should ensure it is on */
+	if (value != 0) {
+		status = ec_read(REG_FAN_STATUS);
+		if (status == 0)
+			ec_write(REG_FAN_CONTROL, BIT_FAN_CONTROL_ON);
+	}
+	/* 0xf4cc is for writing */
+	ec_write(REG_FAN_SPEED_LEVEL, value);
+}
+
+static int get_fan_rpm(void)
+{
+	int value = 0;
+
+	value = FAN_SPEED_DIVIDER /
+	    (((ec_read(REG_FAN_SPEED_HIGH) & 0x0f) << 8) |
+	     ec_read(REG_FAN_SPEED_LOW));
+
+	return value;
+}
+
+static int get_cpu_temp(void)
+{
+	int value;
+
+	value = ec_read(REG_TEMPERATURE_VALUE);
+
+	if (value & (1 << 7))
+		value = (value & 0x7f) - 128;
+	else
+		value = value & 0xff;
+
+	return value * 1000;
+}
+
+static int get_battery_temp(void)
+{
+	int value;
+
+	value = (ec_read(REG_BAT_TEMPERATURE_HIGH) << 8) |
+		(ec_read(REG_BAT_TEMPERATURE_LOW));
+
+	return value * 1000;
+}
+
+static int get_battery_current(void)
+{
+	int value;
+
+	value = (ec_read(REG_BAT_CURRENT_HIGH) << 8) |
+		(ec_read(REG_BAT_CURRENT_LOW));
+
+	if (value & 0x8000)
+		value = 0xffff - value;
+
+	return value;
+}
+
+static int get_battery_voltage(void)
+{
+	int value;
+
+	value = (ec_read(REG_BAT_VOLTAGE_HIGH) << 8) |
+		(ec_read(REG_BAT_VOLTAGE_LOW));
+
+	return value;
+}
+
+
+static int parse_arg(const char *buf, unsigned long count, int *val)
+{
+	if (!count)
+		return 0;
+	if (sscanf(buf, "%i", val) != 1)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t store_sys_hwmon(void (*set) (int), const char *buf, size_t count)
+{
+	int rv, value;
+
+	rv = parse_arg(buf, count, &value);
+	if (rv > 0)
+		set(value);
+	return rv;
+}
+
+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(temp2_input, S_IRUGO, get_battery_temp, NULL);
+CREATE_SENSOR_ATTR(curr1_input, S_IRUGO, get_battery_current, NULL);
+CREATE_SENSOR_ATTR(in1_input, S_IRUGO, get_battery_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_temp2_input.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
+};
+
+struct device *yeeloong_hwmon_dev;
+
+static int yeeloong_hwmon_init(struct device *dev)
+{
+	int ret;
+
+	yeeloong_hwmon_dev = hwmon_device_register(dev);
+	if (IS_ERR(yeeloong_hwmon_dev)) {
+		printk(KERN_INFO "Could not register yeeloong hwmon device\n");
+		yeeloong_hwmon_dev = NULL;
+		return PTR_ERR(yeeloong_hwmon_dev);
+	}
+	ret = sysfs_create_group(&yeeloong_hwmon_dev->kobj,
+				 &hwmon_attribute_group);
+	if (ret) {
+		sysfs_remove_group(&yeeloong_hwmon_dev->kobj,
+				   &hwmon_attribute_group);
+		hwmon_device_unregister(yeeloong_hwmon_dev);
+		yeeloong_hwmon_dev = NULL;
+	}
+	/* ensure fan is set to auto mode */
+	set_fan_pwm_enable(BIT_FAN_AUTO);
+
+	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 */
+
+static int lcd_video_output_get(struct output_device *od)
+{
+	return ec_read(REG_DISPLAY_LCD);
+}
+
+static int lcd_video_output_set(struct output_device *od)
+{
+	unsigned long status = od->request_state;
+	int value;
+
+	if (status == BIT_DISPLAY_LCD_ON) {
+		/* open LCD */
+		outb(0x31, 0x3c4);
+		value = inb(0x3c5);
+		value = (value & 0xf8) | 0x03;
+		outb(0x31, 0x3c4);
+		outb(value, 0x3c5);
+		/* open backlight */
+		ec_write(REG_BACKLIGHT_CTRL, BIT_BACKLIGHT_ON);
+	} else {
+		/* close backlight */
+		ec_write(REG_BACKLIGHT_CTRL, BIT_BACKLIGHT_OFF);
+		/* close LCD */
+		outb(0x31, 0x3c4);
+		value = inb(0x3c5);
+		value = (value & 0xf8) | 0x02;
+		outb(0x31, 0x3c4);
+		outb(value, 0x3c5);
+	}
+
+	return 0;
+}
+
+static struct output_properties lcd_output_properties = {
+	.set_state = lcd_video_output_set,
+	.get_status = lcd_video_output_get,
+};
+
+static int crt_video_output_get(struct output_device *od)
+{
+	return ec_read(REG_CRT_DETECT);
+}
+
+static int crt_video_output_set(struct output_device *od)
+{
+	unsigned long status = od->request_state;
+	int value;
+
+	if (status == BIT_CRT_DETECT_PLUG) {
+		if (ec_read(REG_CRT_DETECT) == BIT_CRT_DETECT_PLUG) {
+			/* open CRT */
+			outb(0x21, 0x3c4);
+			value = inb(0x3c5);
+			value &= ~(1 << 7);
+			outb(0x21, 0x3c4);
+			outb(value, 0x3c5);
+		}
+	} else {
+		/* close CRT */
+		outb(0x21, 0x3c4);
+		value = inb(0x3c5);
+		value |= (1 << 7);
+		outb(0x21, 0x3c4);
+		outb(value, 0x3c5);
+	}
+
+	return 0;
+}
+
+static struct output_properties crt_output_properties = {
+	.set_state = crt_video_output_set,
+	.get_status = crt_video_output_get,
+};
+
+struct output_device *lcd_output_dev, *crt_output_dev;
+
+static void lcd_vo_set(int status)
+{
+	lcd_output_dev->request_state = status;
+	lcd_video_output_set(lcd_output_dev);
+}
+
+static void crt_vo_set(int status)
+{
+	crt_output_dev->request_state = status;
+	crt_video_output_set(crt_output_dev);
+}
+
+static int crt_detect_handler(int status)
+{
+	if (status == BIT_CRT_DETECT_PLUG) {
+		crt_vo_set(BIT_CRT_DETECT_PLUG);
+		lcd_vo_set(BIT_DISPLAY_LCD_OFF);
+	} else {
+		lcd_vo_set(BIT_DISPLAY_LCD_ON);
+		crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+	}
+	return status;
+}
+
+static int black_screen_handler(int status)
+{
+	char *p = NULL, *ec_ver = NULL;
+
+	ec_ver = strstr(arcs_cmdline, "EC_VER=");
+	if (ec_ver) {
+		p = strstr(ec_ver, " ");
+		if (p)
+			*p = '\0';
+	}
+
+	/* Seems EC(>=PQ1D26) does this job for us, we can not do it again,
+	 * otherwise, the brightness will not resume! */
+	if ((ec_ver == NULL) || strncasecmp(ec_ver, "EC_VER=PQ1D26", 64) < 0)
+		lcd_vo_set(status);
+
+	return status;
+}
+
+static int display_toggle_handler(int status)
+{
+	static int video_output_status;
+
+	/* only enable switch video output button
+	 * when CRT is connected */
+	if (ec_read(REG_CRT_DETECT) == BIT_CRT_DETECT_UNPLUG)
+		return 0;
+	/* 0. no CRT connected: LCD on, CRT off
+	 * 1. BOTH on
+	 * 2. LCD off, CRT on
+	 * 3. BOTH off
+	 * 4. LCD on, CRT off
+	 */
+	video_output_status++;
+	if (video_output_status > 4)
+		video_output_status = 1;
+
+	switch (video_output_status) {
+	case 1:
+		lcd_vo_set(BIT_DISPLAY_LCD_ON);
+		crt_vo_set(BIT_CRT_DETECT_PLUG);
+		break;
+	case 2:
+		lcd_vo_set(BIT_DISPLAY_LCD_OFF);
+		crt_vo_set(BIT_CRT_DETECT_PLUG);
+		break;
+	case 3:
+		lcd_vo_set(BIT_DISPLAY_LCD_OFF);
+		crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+		break;
+	case 4:
+		lcd_vo_set(BIT_DISPLAY_LCD_ON);
+		crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+		break;
+	default:
+		/* ensure LCD is on */
+		lcd_vo_set(BIT_DISPLAY_LCD_ON);
+		break;
+	}
+	return video_output_status;
+}
+
+static int yeeloong_vo_init(struct device *dev)
+{
+	int ret;
+
+	/* register video output device: lcd, crt */
+	lcd_output_dev = video_output_register("LCD", dev, 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 */
+	lcd_vo_set(1);
+
+	crt_output_dev = video_output_register("CRT", dev, NULL,
+			&crt_output_properties);
+
+	if (IS_ERR(crt_output_dev)) {
+		ret = PTR_ERR(crt_output_dev);
+		crt_output_dev = NULL;
+		return ret;
+	}
+	/* close CRT by default, and will be enabled
+	 * when the CRT connectting event reported by SCI */
+	crt_vo_set(0);
+
+	/* install event handlers */
+	yeeloong_install_sci_handler(EVENT_CRT_DETECT,
+					   crt_detect_handler);
+	yeeloong_install_sci_handler(EVENT_BLACK_SCREEN,
+					   black_screen_handler);
+	yeeloong_install_sci_handler(EVENT_DISPLAY_TOGGLE,
+					   display_toggle_handler);
+
+	return 0;
+}
+
+static void yeeloong_vo_exit(void)
+{
+	/* uninstall event handlers */
+	yeeloong_uninstall_sci_handler(EVENT_CRT_DETECT,
+					   crt_detect_handler);
+	yeeloong_uninstall_sci_handler(EVENT_BLACK_SCREEN,
+					   black_screen_handler);
+	yeeloong_uninstall_sci_handler(EVENT_DISPLAY_TOGGLE,
+					   display_toggle_handler);
+
+	if (lcd_output_dev) {
+		video_output_unregister(lcd_output_dev);
+		lcd_output_dev = NULL;
+	}
+	if (crt_output_dev) {
+		video_output_unregister(crt_output_dev);
+		crt_output_dev = NULL;
+	}
+}
+
+/* Thermal cooling devices subdriver */
+
+static int video_get_max_state(struct thermal_cooling_device *cdev, unsigned
+			       long *state)
+{
+	*state = MAX_BRIGHTNESS;
+	return 0;
+}
+
+static int video_get_cur_state(struct thermal_cooling_device *cdev, unsigned
+			       long *state)
+{
+	static struct backlight_device *bd;
+
+	bd = (struct backlight_device *)cdev->devdata;
+
+	*state = yeeloong_get_brightness(bd);
+
+	return 0;
+}
+
+static int video_set_cur_state(struct thermal_cooling_device *cdev, unsigned
+			       long state)
+{
+	static struct backlight_device *bd;
+
+	bd = (struct backlight_device *)cdev->devdata;
+
+	yeeloong_backlight_dev->props.brightness = state;
+	backlight_update_status(bd);
+
+	return 0;
+}
+
+static struct thermal_cooling_device_ops video_cooling_ops = {
+	.get_max_state = video_get_max_state,
+	.get_cur_state = video_get_cur_state,
+	.set_cur_state = video_set_cur_state,
+};
+
+static struct thermal_cooling_device *yeeloong_thermal_cdev;
+
+/* TODO: register fan, cpu as the cooling devices */
+static int yeeloong_thermal_init(struct device *dev)
+{
+	int ret;
+
+	if (!dev)
+		return -1;
+
+	yeeloong_thermal_cdev = thermal_cooling_device_register("LCD", dev,
+			&video_cooling_ops);
+
+	if (IS_ERR(yeeloong_thermal_cdev)) {
+		ret = PTR_ERR(yeeloong_thermal_cdev);
+		return ret;
+	}
+
+	ret = sysfs_create_link(&dev->kobj,
+				&yeeloong_thermal_cdev->device.kobj,
+				"thermal_cooling");
+	if (ret) {
+		printk(KERN_ERR "Create sysfs link\n");
+		return ret;
+	}
+	ret = sysfs_create_link(&yeeloong_thermal_cdev->device.kobj,
+				&dev->kobj, "device");
+	if (ret) {
+		printk(KERN_ERR "Create sysfs link\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void yeeloong_thermal_exit(struct device *dev)
+{
+	if (yeeloong_thermal_cdev) {
+		if (dev)
+			sysfs_remove_link(&dev->kobj, "thermal_cooling");
+		sysfs_remove_link(&yeeloong_thermal_cdev->device.kobj,
+				  "device");
+		thermal_cooling_device_unregister(yeeloong_thermal_cdev);
+		yeeloong_thermal_cdev = NULL;
+	}
+}
+
+/* hotkey input subdriver */
+
+static struct input_dev *yeeloong_hotkey_dev;
+static int event, status;
+
+struct key_entry {
+	char type;		/* See KE_* below */
+	int event;		/* event from SCI */
+	u16 keycode;		/* KEY_* or SW_* */
+};
+
+enum { KE_KEY, KE_SW, KE_END };
+
+static struct key_entry yeeloong_keymap[] = {
+	{KE_SW, EVENT_LID, SW_LID},
+	/* SW_VIDEOOUT_INSERT? not included in hald-addon-input! */
+	{KE_KEY, EVENT_CRT_DETECT, KEY_PROG1},
+	/* Seems battery subdriver should report it */
+	{KE_KEY, EVENT_OVERTEMP, KEY_PROG2},
+	/*{KE_KEY, EVENT_AC_BAT, KEY_BATTERY},*/
+	{KE_KEY, EVENT_CAMERA, KEY_CAMERA},	/* Fn + ESC */
+	{KE_KEY, EVENT_SLEEP, KEY_SLEEP},	/* Fn + F1 */
+	/* Seems not clear? not included in hald-addon-input! */
+	{KE_KEY, EVENT_BLACK_SCREEN, KEY_PROG3},	/* 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 yeeloong_lid_update_status(int status)
+{
+	input_report_switch(yeeloong_hotkey_dev, SW_LID, !status);
+	input_sync(yeeloong_hotkey_dev);
+
+	return status;
+}
+
+static void yeeloong_hotkey_update_status(int key)
+{
+	input_report_key(yeeloong_hotkey_dev, key, 1);
+	input_sync(yeeloong_hotkey_dev);
+	input_report_key(yeeloong_hotkey_dev, key, 0);
+	input_sync(yeeloong_hotkey_dev);
+}
+
+static int get_event_keycode(void)
+{
+	struct key_entry *key;
+
+	for (key = yeeloong_keymap; key->type != KE_END; key++) {
+		if (key->event != event)
+			continue;
+		else {
+			if (EVENT_DISPLAY_BRIGHTNESS == event) {
+				static int old_brightness_status = -1;
+				/* current status > old one, means up */
+				if ((status < old_brightness_status)
+				    || (0 == status))
+					key++;
+				old_brightness_status = status;
+			} else if (EVENT_AUDIO_VOLUME == event) {
+				static int old_volume_status = -1;
+				if ((status < old_volume_status)
+				    || (0 == status))
+					key++;
+				old_volume_status = status;
+			}
+			break;
+		}
+	}
+	return key->keycode;
+}
+
+void yeeloong_report_key(void)
+{
+	int keycode;
+
+	keycode = get_event_keycode();
+
+	if (keycode == SW_LID)
+		yeeloong_lid_update_status(status);
+	else
+		yeeloong_hotkey_update_status(keycode);
+}
+
+enum { NO_REG, MUL_REG, REG_END };
+
+int event_reg[15] = {
+	REG_LID_DETECT,		/*  press the lid or not */
+	NO_REG,			/*  Fn+F3 for display switch */
+	NO_REG,			/*  Fn+F1 for entering sleep mode */
+	MUL_REG,		/*  Over-temperature happened */
+	REG_CRT_DETECT,		/*  CRT is connected */
+	REG_CAMERA_STATUS,	/*  Camera is on or off */
+	REG_USB2_FLAG,		/*  USB2 Over Current occurred */
+	REG_USB0_FLAG,		/*  USB0 Over Current occurred */
+	REG_DISPLAY_LCD,	/*  Black screen is on or off */
+	REG_AUDIO_MUTE,		/*  Mute is on or off */
+	REG_DISPLAY_BRIGHTNESS,	/*  LCD backlight brightness adjust */
+	NO_REG,			/*  ac & battery relative issue */
+	REG_AUDIO_VOLUME,	/*  Volume adjust */
+	REG_WLAN,		/*  Wlan is on or off */
+	REG_END
+};
+
+static int ec_get_event_status(void)
+{
+	int reg;
+
+	reg = event_reg[event - EVENT_LID];
+
+	if (reg == NO_REG)
+		return 1;
+	else if (reg == MUL_REG) {
+		if (event == EVENT_OVERTEMP) {
+			return (ec_read(REG_BAT_CHARGE_STATUS) &
+				BIT_BAT_CHARGE_STATUS_OVERTEMP) >> 2;
+		}
+	} else if (reg != REG_END)
+		return ec_read(reg);
+
+	return -1;
+}
+
+static sci_handler event_handler[15];
+
+int yeeloong_install_sci_handler(int event, sci_handler handler)
+{
+	if (event_handler[event - EVENT_LID] != NULL) {
+		printk(KERN_INFO "There is a handler installed for event: %d\n",
+		       event);
+		return -1;
+	}
+	event_handler[event - EVENT_LID] = handler;
+
+	return 0;
+}
+EXPORT_SYMBOL(yeeloong_install_sci_handler);
+
+int yeeloong_uninstall_sci_handler(int event, sci_handler handler)
+{
+	if (event_handler[event - EVENT_LID] == NULL) {
+		printk(KERN_INFO "There is no handler installed for event: %d\n",
+		       event);
+		return -1;
+	}
+	if (event_handler[event - EVENT_LID] != handler) {
+		printk(KERN_INFO "You can not uninstall the handler installed by others\n");
+		return -1;
+	}
+	event_handler[event - EVENT_LID] = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL(yeeloong_uninstall_sci_handler);
+
+static void yeeloong_event_action(void)
+{
+	sci_handler handler;
+
+	handler = event_handler[event - EVENT_LID];
+
+	if (handler == NULL)
+		return;
+
+	if (event == EVENT_CAMERA)
+		status = handler(3);
+	else
+		status = handler(status);
+}
+
+/*
+ * sci 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;
+
+	if (SCI_IRQ_NUM != irq) {
+		printk(KERN_ERR "%s: spurious irq.\n", __func__);
+		return IRQ_NONE;
+	}
+
+	/* query the event number */
+	ret = ec_query_event_num();
+	if (ret < 0) {
+		printk(KERN_ERR "%s: return: %d\n", __func__, ret);
+		return IRQ_NONE;
+	}
+
+	event = ec_get_event_num();
+	if (event < 0) {
+		printk(KERN_ERR "%s: return: %d\n", __func__, event);
+		return IRQ_NONE;
+	}
+
+	printk(KERN_INFO "sci event number: 0x%x\n", event);
+
+	/* parse the event number and wake the queue */
+	if ((event != 0x00) && (event != 0xff)) {
+		/* get status of current event */
+		status = ec_get_event_status();
+		printk(KERN_INFO "%s: status: %d\n", __func__, status);
+		if (status == -1)
+			return IRQ_NONE;
+		/* execute relative actions */
+		yeeloong_event_action();
+		/* report current key */
+		yeeloong_report_key();
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+ * config and init some msr and gpio register properly.
+ */
+static int sci_irq_init(void)
+{
+	u32 hi, lo;
+	u32 gpio_base;
+	int ret = 0;
+	unsigned long flags;
+
+	/* 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) {
+		printk(KERN_ERR "%s: failed.\n", __func__);
+		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;
+}
+
+struct irqaction sci_irqaction = {
+	.handler = sci_irq_handler,
+	.name = "sci",
+	.flags = IRQF_SHARED,
+};
+
+static int setup_sci(void)
+{
+	sci_irq_init();
+
+	setup_irq(SCI_IRQ_NUM, &sci_irqaction);
+
+	return 0;
+}
+
+static ssize_t
+ignore_store(struct device *dev,
+	     struct device_attribute *attr, const char *buf, size_t count)
+{
+	return count;
+}
+
+static ssize_t
+show_hotkeystate(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d %d\n", event, status);
+}
+
+static DEVICE_ATTR(state, 0444, show_hotkeystate, ignore_store);
+
+static struct attribute *hotkey_attributes[] = {
+	&dev_attr_state.attr,
+	NULL
+};
+
+static struct attribute_group hotkey_attribute_group = {
+	.attrs = hotkey_attributes
+};
+
+static int camera_set(int status)
+{
+	int value;
+	static int camera_status;
+
+	if (status == 2)
+		/* resume the old camera status */
+		camera_set(camera_status);
+	else if (status == 3) {
+		/* revert the camera status */
+		value = ec_read(REG_CAMERA_CONTROL);
+		ec_write(REG_CAMERA_CONTROL, value | (1 << 1));
+	} else {		/* status == 0 or status == 1 */
+		status = !!status;
+		camera_status = ec_read(REG_CAMERA_STATUS);
+		if (status != camera_status)
+			camera_set(3);
+	}
+	return ec_read(REG_CAMERA_STATUS);
+}
+
+#define I8042_STATUS_REG	0x64
+#define I8042_DATA_REG		0x60
+#define i8042_read_status() inb(I8042_STATUS_REG)
+#define i8042_read_data() inb(I8042_DATA_REG)
+#define I8042_STR_OBF		0x01
+#define I8042_BUFFER_SIZE	16
+
+static void i8042_flush(void)
+{
+	int i;
+
+	while ((i8042_read_status() & I8042_STR_OBF)
+		&& (i < I8042_BUFFER_SIZE)) {
+		udelay(50);
+		i8042_read_data();
+		i++;
+	}
+}
+
+static int yeeloong_hotkey_init(struct device *dev)
+{
+	int ret;
+	struct key_entry *key;
+
+	/* flush the buffer of keyboard */
+	i8042_flush();
+
+	/* setup the system control interface */
+	setup_sci();
+
+	yeeloong_hotkey_dev = input_allocate_device();
+
+	if (!yeeloong_hotkey_dev)
+		return -ENOMEM;
+
+	yeeloong_hotkey_dev->name = "HotKeys";
+	yeeloong_hotkey_dev->phys = "button/input0";
+	yeeloong_hotkey_dev->id.bustype = BUS_HOST;
+	yeeloong_hotkey_dev->dev.parent = dev;
+
+	for (key = yeeloong_keymap; key->type != KE_END; key++) {
+		switch (key->type) {
+		case KE_KEY:
+			set_bit(EV_KEY, yeeloong_hotkey_dev->evbit);
+			set_bit(key->keycode, yeeloong_hotkey_dev->keybit);
+			break;
+		case KE_SW:
+			set_bit(EV_SW, yeeloong_hotkey_dev->evbit);
+			set_bit(key->keycode, yeeloong_hotkey_dev->swbit);
+			break;
+		}
+	}
+
+	ret = input_register_device(yeeloong_hotkey_dev);
+	if (ret) {
+		input_free_device(yeeloong_hotkey_dev);
+		return ret;
+	}
+
+	ret = sysfs_create_group(&yeeloong_hotkey_dev->dev.kobj,
+				 &hotkey_attribute_group);
+	if (ret) {
+		sysfs_remove_group(&yeeloong_hotkey_dev->dev.kobj,
+				   &hotkey_attribute_group);
+		input_unregister_device(yeeloong_hotkey_dev);
+		yeeloong_hotkey_dev = NULL;
+	}
+	/* update the current status of lid */
+	yeeloong_lid_update_status(BIT_LID_DETECT_ON);
+
+	/* install the real yeeloong_report_lid_status for pm.c */
+	yeeloong_report_lid_status = yeeloong_lid_update_status;
+
+	/* install event handler */
+	yeeloong_install_sci_handler(EVENT_CAMERA, camera_set);
+
+	return 0;
+}
+
+static void yeeloong_hotkey_exit(void)
+{
+	/* free irq */
+	remove_irq(SCI_IRQ_NUM, &sci_irqaction);
+
+	/* uninstall the real yeeloong_report_lid_status for pm.c */
+	yeeloong_report_lid_status = NULL;
+
+	/* uninstall event handler */
+	yeeloong_uninstall_sci_handler(EVENT_CAMERA, camera_set);
+
+	if (yeeloong_hotkey_dev) {
+		sysfs_remove_group(&yeeloong_hotkey_dev->dev.kobj,
+				   &hotkey_attribute_group);
+		input_unregister_device(yeeloong_hotkey_dev);
+		yeeloong_hotkey_dev = NULL;
+	}
+}
+
+/* battery subdriver: APM emulated support */
+
+static void get_fixed_battery_info(void)
+{
+	int design_cap, full_charged_cap, design_vol, vendor, cell_count;
+
+	design_cap = (ec_read(REG_BAT_DESIGN_CAP_HIGH) << 8)
+	    | ec_read(REG_BAT_DESIGN_CAP_LOW);
+	full_charged_cap = (ec_read(REG_BAT_FULLCHG_CAP_HIGH) << 8)
+	    | ec_read(REG_BAT_FULLCHG_CAP_LOW);
+	design_vol = (ec_read(REG_BAT_DESIGN_VOL_HIGH) << 8)
+	    | ec_read(REG_BAT_DESIGN_VOL_LOW);
+	vendor = ec_read(REG_BAT_VENDOR);
+	cell_count = ec_read(REG_BAT_CELL_COUNT);
+
+	if (vendor != 0) {
+		printk(KERN_INFO
+		       "battery vendor(%s), cells count(%d), "
+		       "with designed capacity(%d),designed voltage(%d),"
+		       " full charged capacity(%d)\n",
+		       (vendor ==
+			FLAG_BAT_VENDOR_SANYO) ? "SANYO" : "SIMPLO",
+		       (cell_count == FLAG_BAT_CELL_3S1P) ? 3 : 6,
+		       design_cap, design_vol,
+		       full_charged_cap);
+	}
+}
+
+#define APM_CRITICAL		5
+
+static void yeeloong_apm_get_power_status(struct apm_power_info *info)
+{
+	unsigned char bat_status;
+
+	info->battery_status = APM_BATTERY_STATUS_UNKNOWN;
+	info->battery_flag = APM_BATTERY_FLAG_UNKNOWN;
+	info->units = APM_UNITS_MINS;
+
+	info->battery_life = (ec_read(REG_BAT_RELATIVE_CAP_HIGH) << 8) |
+		(ec_read(REG_BAT_RELATIVE_CAP_LOW));
+
+	info->ac_line_status = (ec_read(REG_BAT_POWER) & BIT_BAT_POWER_ACIN) ?
+		APM_AC_ONLINE : APM_AC_OFFLINE;
+
+	bat_status = ec_read(REG_BAT_STATUS);
+
+	if (!(bat_status & BIT_BAT_STATUS_IN)) {
+		/* no battery inserted */
+		info->battery_status = APM_BATTERY_STATUS_NOT_PRESENT;
+		info->battery_flag = APM_BATTERY_FLAG_NOT_PRESENT;
+		info->time = 0x00;
+		return;
+	}
+
+	/* adapter inserted */
+	if (info->ac_line_status == APM_AC_ONLINE) {
+		if (!(bat_status & BIT_BAT_STATUS_FULL)) {
+			/* battery is not fully charged */
+			info->battery_status = APM_BATTERY_STATUS_CHARGING;
+			info->battery_flag = APM_BATTERY_FLAG_CHARGING;
+		} else {
+			/* if the battery is fully charged */
+			info->battery_status = APM_BATTERY_STATUS_HIGH;
+			info->battery_flag = APM_BATTERY_FLAG_HIGH;
+			info->battery_life = 100;
+		}
+	} else {
+		/* battery is too low */
+		if (bat_status & BIT_BAT_STATUS_LOW) {
+			info->battery_status = APM_BATTERY_STATUS_LOW;
+			info->battery_flag = APM_BATTERY_FLAG_LOW;
+			if (info->battery_life <= APM_CRITICAL) {
+				/* we should power off the system now */
+				info->battery_status =
+					APM_BATTERY_STATUS_CRITICAL;
+				info->battery_flag = APM_BATTERY_FLAG_CRITICAL;
+			}
+		} else {
+			/* assume the battery is high enough. */
+			info->battery_status = APM_BATTERY_STATUS_HIGH;
+			info->battery_flag = APM_BATTERY_FLAG_HIGH;
+		}
+	}
+	info->time = ((info->battery_life - 3) * 54 + 142) / 60;
+}
+
+static int yeeloong_apm_init(void)
+{
+	/* print fixed information of battery */
+	get_fixed_battery_info();
+
+	apm_get_power_status = yeeloong_apm_get_power_status;
+
+	return 0;
+}
+
+static void yeeloong_apm_exit(void)
+{
+}
+
+/* platform subdriver */
+static struct platform_device *yeeloong_pdev;
+
+static void __maybe_unused 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 __maybe_unused yeeloong_suspend(struct platform_device *pdev,
+		pm_message_t state)
+{
+	printk(KERN_INFO "yeeloong specific suspend\n");
+
+	/* close LCD */
+	lcd_vo_set(BIT_DISPLAY_LCD_OFF);
+	/* close CRT */
+	crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+	/* power off camera */
+	camera_set(BIT_CAMERA_CONTROL_OFF);
+	/* poweroff three usb ports */
+	usb_ports_set(BIT_USB_FLAG_OFF);
+	/* minimize the speed of FAN */
+	set_fan_pwm_enable(BIT_FAN_MANUAL);
+	set_fan_pwm(1);
+
+	return 0;
+}
+
+static int __maybe_unused yeeloong_resume(struct platform_device *pdev)
+{
+	printk(KERN_INFO "yeeloong specific resume\n");
+
+	/* resume the status of lcd & crt */
+	lcd_vo_set(BIT_DISPLAY_LCD_ON);
+	crt_vo_set(BIT_CRT_DETECT_PLUG);
+
+	/* power on three usb ports */
+	usb_ports_set(BIT_USB_FLAG_ON);
+
+	/* resume the camera status */
+	camera_set(2);
+
+	/* resume fan to auto mode */
+	set_fan_pwm_enable(BIT_FAN_AUTO);
+
+	return 0;
+}
+
+static struct platform_driver platform_driver = {
+	.driver = {
+		   .name = "yeeloong-laptop",
+		   .owner = THIS_MODULE,
+		   },
+#ifdef CONFIG_PM
+	.suspend = yeeloong_suspend,
+	.resume = yeeloong_resume,
+#endif
+};
+
+static ssize_t yeeloong_pdev_name_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "yeeloong laptop\n");
+}
+
+static struct device_attribute dev_attr_yeeloong_pdev_name =
+__ATTR(name, S_IRUGO, yeeloong_pdev_name_show, NULL);
+
+static int yeeloong_pdev_init(void)
+{
+	int ret;
+
+	/* Register platform stuff */
+	ret = platform_driver_register(&platform_driver);
+	if (ret)
+		return ret;
+
+	yeeloong_pdev = platform_device_alloc("yeeloong-laptop", -1);
+	if (!yeeloong_pdev) {
+		ret = -ENOMEM;
+		platform_driver_unregister(&platform_driver);
+		return ret;
+	}
+
+	ret = platform_device_add(yeeloong_pdev);
+	if (ret) {
+		platform_device_put(yeeloong_pdev);
+		return ret;
+	}
+
+	if (IS_ERR(yeeloong_pdev)) {
+		ret = PTR_ERR(yeeloong_pdev);
+		yeeloong_pdev = NULL;
+		printk(KERN_INFO "unable to register platform device\n");
+		return ret;
+	}
+
+	ret = device_create_file(&yeeloong_pdev->dev,
+				 &dev_attr_yeeloong_pdev_name);
+	if (ret) {
+		printk(KERN_INFO "unable to create sysfs device attributes\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void yeeloong_pdev_exit(void)
+{
+	if (yeeloong_pdev) {
+		platform_device_unregister(yeeloong_pdev);
+		yeeloong_pdev = NULL;
+		platform_driver_unregister(&platform_driver);
+	}
+}
+
+static int __init yeeloong_init(void)
+{
+	int ret;
+
+	if (mips_machtype != MACH_LEMOTE_YL2F89) {
+		printk(KERN_INFO "This Driver is for YeeLoong netbook, You"
+				"can not use it on the other Machines\n");
+		return -EFAULT;
+	}
+
+	printk(KERN_INFO "YeeLoong Platform Specific Drivers %s loaded\n",
+			DRIVER_VERSION);
+
+	ret = yeeloong_pdev_init();
+	if (ret) {
+		yeeloong_pdev_exit();
+		printk(KERN_INFO "init yeeloong platform driver failure\n");
+		return ret;
+	}
+	ret = yeeloong_hotkey_init(&yeeloong_pdev->dev);
+	if (ret) {
+		yeeloong_hotkey_exit();
+		printk(KERN_INFO "init yeeloong hotkey driver failure\n");
+		return ret;
+	}
+	ret = yeeloong_apm_init();
+	if (ret) {
+		yeeloong_apm_exit();
+		printk(KERN_INFO "init yeeloong apm driver failure\n");
+		return ret;
+	}
+	ret = yeeloong_backlight_init(&yeeloong_pdev->dev);
+	if (ret) {
+		yeeloong_backlight_exit();
+		printk(KERN_INFO "init yeeloong backlight driver failure\n");
+		return ret;
+	}
+	ret = yeeloong_thermal_init(&yeeloong_backlight_dev->dev);
+	if (ret) {
+		yeeloong_thermal_exit(&yeeloong_backlight_dev->dev);
+		printk(KERN_INFO
+		       "Fail to init yeeloong thermal cooling device.\n");
+		return ret;
+	}
+	ret = yeeloong_hwmon_init(&yeeloong_pdev->dev);
+	if (ret) {
+		yeeloong_hwmon_exit();
+		printk(KERN_INFO "init yeeloong hwmon driver failure\n");
+		return ret;
+	}
+	ret = yeeloong_vo_init(&yeeloong_pdev->dev);
+	if (ret) {
+		yeeloong_vo_exit();
+		printk(KERN_INFO "init yeeloong video output driver failure\n");
+		return ret;
+	}
+	return 0;
+}
+
+static void __exit yeeloong_exit(void)
+{
+	yeeloong_vo_exit();
+	yeeloong_hwmon_exit();
+	yeeloong_thermal_exit(&yeeloong_backlight_dev->dev);
+	yeeloong_backlight_exit();
+	yeeloong_apm_exit();
+	yeeloong_hotkey_exit();
+	yeeloong_pdev_exit();
+
+	printk(KERN_INFO "YeeLoong Platform Specific Drivers %s unloaded\n",
+			DRIVER_VERSION);
+}
+
+module_init(yeeloong_init);
+module_exit(yeeloong_exit);
+
+MODULE_AUTHOR("Wu Zhangjin <wuzj@lemote.com>");
+MODULE_DESCRIPTION("YeeLoong laptop driver");
+MODULE_LICENSE("GPL");
-- 
1.6.2.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform
@ 2009-11-19 17:39   ` Wu Zhangjin
  0 siblings, 0 replies; 13+ messages in thread
From: Wu Zhangjin @ 2009-11-19 17:39 UTC (permalink / raw)
  To: Ralf Baechle
  Cc: linux-mips, zhangfx, yanh, huhb, Richard Purdie, lm-sensors,
	Jamey Hicks, Wu Zhangjin

This patch added the following subdrivers: backlight, hotkey, video
output, thermal cooling, hwmon and battery.

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 arch/mips/kernel/setup.c                       |    1 +
 arch/mips/loongson/Kconfig                     |   18 +
 arch/mips/loongson/lemote-2f/Makefile          |    1 +
 arch/mips/loongson/lemote-2f/ec_kb3310b.h      |  150 +++
 arch/mips/loongson/lemote-2f/yeeloong_laptop.c | 1354 ++++++++++++++++++++++++
 5 files changed, 1524 insertions(+), 0 deletions(-)
 create mode 100644 arch/mips/loongson/lemote-2f/yeeloong_laptop.c

diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
index bd55f71..b1e1272 100644
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -60,6 +60,7 @@ struct boot_mem_map boot_mem_map;
 
 static char command_line[COMMAND_LINE_SIZE];
        char arcs_cmdline[COMMAND_LINE_SIZE] = CONFIG_CMDLINE;
+EXPORT_SYMBOL(arcs_cmdline);
 
 /*
  * mips_io_port_base is the begin of the address space to which x86 style
diff --git a/arch/mips/loongson/Kconfig b/arch/mips/loongson/Kconfig
index 1041336..34a94b0 100644
--- a/arch/mips/loongson/Kconfig
+++ b/arch/mips/loongson/Kconfig
@@ -112,4 +112,22 @@ config LEMOTE_LYNLOONG2F_PDEV
 	  This driver adds the lynloong specific backlight driver and platform
 	  driver(mainly the suspend support).
 
+config LEMOTE_YEELOONG2F_PDEV
+	tristate "Lemote YeeLoong Platform Specific Driver"
+	depends on LEMOTE_MACH2F
+	default m
+	select INPUT_EVDEV
+	select HWMON
+	select VIDEO_OUTPUT_CONTROL
+	select THERMAL
+	select BACKLIGHT_CLASS_DEVICE
+	select SYS_SUPPORTS_APM_EMULATION
+	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.
+
+	  This driver adds the YeeLoong Platform Specific support.
+
 endif # LOONGSON_PLATFORM_DEVICES
diff --git a/arch/mips/loongson/lemote-2f/Makefile b/arch/mips/loongson/lemote-2f/Makefile
index 81a97d0..4e8bf5d 100644
--- a/arch/mips/loongson/lemote-2f/Makefile
+++ b/arch/mips/loongson/lemote-2f/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_LOONGSON_SUSPEND) += pm.o
 # Platform Drivers
 #
 obj-$(CONFIG_LEMOTE_LYNLOONG2F_PDEV) += lynloong_pc.o
+obj-$(CONFIG_LEMOTE_YEELOONG2F_PDEV) += yeeloong_laptop.o
diff --git a/arch/mips/loongson/lemote-2f/ec_kb3310b.h b/arch/mips/loongson/lemote-2f/ec_kb3310b.h
index 4d29953..00530e3 100644
--- a/arch/mips/loongson/lemote-2f/ec_kb3310b.h
+++ b/arch/mips/loongson/lemote-2f/ec_kb3310b.h
@@ -19,6 +19,13 @@ extern int ec_query_seq(unsigned char cmd);
 extern int ec_query_event_num(void);
 extern int ec_get_event_num(void);
 
+typedef int (*sci_handler) (int status);
+extern sci_handler yeeloong_report_lid_status;
+extern int yeeloong_install_sci_handler(int event, sci_handler handler);
+extern int yeeloong_uninstall_sci_handler(int event, sci_handler handler);
+
+#define SCI_IRQ_NUM 0x0A
+
 /*
  * The following registers are determined by the EC index configuration.
  * 1, fill the PORT_HIGH as EC register high part.
@@ -44,4 +51,147 @@ extern int ec_get_event_num(void);
 #define	CMD_GET_EVENT_NUM	0x84
 #define	CMD_PROGRAM_PIECE	0xda
 
+/* temperature & fan registers */
+#define	REG_TEMPERATURE_VALUE	0xF458	/*  current temperature value */
+#define	REG_FAN_AUTO_MAN_SWITCH 0xF459  /*  fan auto/manual control switch */
+#define	BIT_FAN_AUTO		0
+#define	BIT_FAN_MANUAL		1
+#define	REG_FAN_CONTROL		0xF4D2	/*  fan control */
+#define	BIT_FAN_CONTROL_ON	(1 << 0)
+#define	BIT_FAN_CONTROL_OFF	(0 << 0)
+#define	REG_FAN_STATUS		0xF4DA	/*  fan status */
+#define	BIT_FAN_STATUS_ON	(1 << 0)
+#define	BIT_FAN_STATUS_OFF	(0 << 0)
+#define	REG_FAN_SPEED_HIGH	0xFE22	/*  fan speed high byte */
+#define	REG_FAN_SPEED_LOW	0xFE23	/*  fan speed low byte */
+#define	REG_FAN_SPEED_LEVEL	0xF4CC	/*  fan speed level, from 1 to 5 */
+/* fan speed divider */
+#define	FAN_SPEED_DIVIDER	480000	/* (60*1000*1000/62.5/2)*/
+
+/* battery registers */
+#define	REG_BAT_DESIGN_CAP_HIGH		0xF77D
+#define	REG_BAT_DESIGN_CAP_LOW		0xF77E
+#define	REG_BAT_FULLCHG_CAP_HIGH	0xF780
+#define	REG_BAT_FULLCHG_CAP_LOW		0xF781
+#define	REG_BAT_DESIGN_VOL_HIGH		0xF782
+#define	REG_BAT_DESIGN_VOL_LOW		0xF783
+#define	REG_BAT_CURRENT_HIGH		0xF784
+#define	REG_BAT_CURRENT_LOW		0xF785
+#define	REG_BAT_VOLTAGE_HIGH		0xF786
+#define	REG_BAT_VOLTAGE_LOW		0xF787
+#define	REG_BAT_TEMPERATURE_HIGH	0xF788
+#define	REG_BAT_TEMPERATURE_LOW		0xF789
+#define	REG_BAT_RELATIVE_CAP_HIGH	0xF492
+#define	REG_BAT_RELATIVE_CAP_LOW	0xF493
+#define	REG_BAT_VENDOR			0xF4C4
+#define	FLAG_BAT_VENDOR_SANYO		0x01
+#define	FLAG_BAT_VENDOR_SIMPLO		0x02
+#define	REG_BAT_CELL_COUNT		0xF4C6
+#define	FLAG_BAT_CELL_3S1P		0x03
+#define	FLAG_BAT_CELL_3S2P		0x06
+#define	REG_BAT_CHARGE			0xF4A2
+#define	FLAG_BAT_CHARGE_DISCHARGE	0x01
+#define	FLAG_BAT_CHARGE_CHARGE		0x02
+#define	FLAG_BAT_CHARGE_ACPOWER		0x00
+#define	REG_BAT_STATUS			0xF4B0
+#define	BIT_BAT_STATUS_LOW		(1 << 5)
+#define	BIT_BAT_STATUS_DESTROY		(1 << 2)
+#define	BIT_BAT_STATUS_FULL		(1 << 1)
+#define	BIT_BAT_STATUS_IN		(1 << 0)
+#define	REG_BAT_CHARGE_STATUS		0xF4B1
+#define	BIT_BAT_CHARGE_STATUS_OVERTEMP	(1 << 2)
+#define	BIT_BAT_CHARGE_STATUS_PRECHG	(1 << 1)
+#define	REG_BAT_STATE			0xF482
+#define	BIT_BAT_STATE_CHARGING		(1 << 1)
+#define	BIT_BAT_STATE_DISCHARGING	(1 << 0)
+#define	REG_BAT_POWER			0xF440
+#define	BIT_BAT_POWER_S3		(1 << 2)
+#define	BIT_BAT_POWER_ON		(1 << 1)
+#define	BIT_BAT_POWER_ACIN		(1 << 0)
+
+/* other registers */
+/* Audio: rd/wr */
+#define	REG_AUDIO_VOLUME	0xF46C  /* volume level */
+#define	REG_AUDIO_MUTE		0xF4E7
+#define	REG_AUDIO_BEEP		0xF4D0
+/* USB port power or not: rd/wr */
+#define	REG_USB0_FLAG		0xF461
+#define	REG_USB1_FLAG		0xF462
+#define	REG_USB2_FLAG		0xF463
+#define	BIT_USB_FLAG_ON		1
+#define	BIT_USB_FLAG_OFF	0
+/* LID */
+#define	REG_LID_DETECT		0xF4BD
+#define	BIT_LID_DETECT_ON	1
+#define	BIT_LID_DETECT_OFF	0
+/* CRT */
+#define	REG_CRT_DETECT		0xF4AD
+#define	BIT_CRT_DETECT_PLUG	1
+#define	BIT_CRT_DETECT_UNPLUG	0
+/* LCD backlight brightness adjust: 9 levels */
+#define	REG_DISPLAY_BRIGHTNESS	0xF4F5
+/* Black screen Status */
+#define	BIT_DISPLAY_LCD_ON	1
+#define	BIT_DISPLAY_LCD_OFF	0
+/* LCD backlight control: off/restore */
+#define	REG_BACKLIGHT_CTRL	0xF7BD
+#define	BIT_BACKLIGHT_ON	1
+#define	BIT_BACKLIGHT_OFF	0
+/* Reset the machine auto-clear: rd/wr */
+#define	REG_RESET		0xF4EC
+#define	BIT_RESET_ON		1
+#define	BIT_RESET_OFF		0
+/* Light the led: rd/wr */
+#define	REG_LED			0xF4C8
+#define	BIT_LED_RED_POWER	(1 << 0)
+#define	BIT_LED_ORANGE_POWER	(1 << 1)
+#define	BIT_LED_GREEN_CHARGE	(1 << 2)
+#define	BIT_LED_RED_CHARGE	(1 << 3)
+#define	BIT_LED_NUMLOCK		(1 << 4)
+/* Test led mode, all led on/off */
+#define	REG_LED_TEST		0xF4C2
+#define	BIT_LED_TEST_IN		1
+#define	BIT_LED_TEST_OUT	0
+/* Camera on/off */
+#define	REG_CAMERA_STATUS	0xF46A
+#define	BIT_CAMERA_STATUS_ON	1
+#define	BIT_CAMERA_STATUS_OFF	0
+#define	REG_CAMERA_CONTROL	0xF7B7
+#define	BIT_CAMERA_CONTROL_OFF	0
+#define	BIT_CAMERA_CONTROL_ON	1
+/* Wlan Status */
+#define	REG_WLAN		0xF4FA
+#define	BIT_WLAN_ON		1
+#define	BIT_WLAN_OFF		0
+#define	REG_DISPLAY_LCD		0xF79F
+
+/* SCI Event Number from EC */
+enum {
+	EVENT_LID = 0x23,	/*  press the lid or not */
+	EVENT_DISPLAY_TOGGLE,	/*  Fn+F3 for display switch */
+	EVENT_SLEEP,		/*  Fn+F1 for entering sleep mode */
+	EVENT_OVERTEMP,		/*  Over-temperature happened */
+	EVENT_CRT_DETECT,	/*  CRT is connected */
+	EVENT_CAMERA,		/*  Camera is on or off */
+	EVENT_USB_OC2,		/*  USB2 Over Current occurred */
+	EVENT_USB_OC0,		/*  USB0 Over Current occurred */
+	EVENT_BLACK_SCREEN,	/*  Black screen is on or off */
+	EVENT_AUDIO_MUTE,	/*  Mute is on or off */
+	EVENT_DISPLAY_BRIGHTNESS,/*  LCD backlight brightness adjust */
+	EVENT_AC_BAT,		/*  ac & battery relative issue */
+	EVENT_AUDIO_VOLUME,	/*  Volume adjust */
+	EVENT_WLAN,		/*  Wlan is on or off */
+	EVENT_END
+};
+
+enum {
+	BIT_AC_BAT_BAT_IN = 0,
+	BIT_AC_BAT_AC_IN,
+	BIT_AC_BAT_INIT_CAP,
+	BIT_AC_BAT_CHARGE_MODE,
+	BIT_AC_BAT_STOP_CHARGE,
+	BIT_AC_BAT_BAT_LOW,
+	BIT_AC_BAT_BAT_FULL
+};
+
 #endif /* !_EC_KB3310B_H */
diff --git a/arch/mips/loongson/lemote-2f/yeeloong_laptop.c b/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
new file mode 100644
index 0000000..ff74f7f
--- /dev/null
+++ b/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
@@ -0,0 +1,1354 @@
+/*
+ *  Driver for YeeLoong laptop extras
+ *
+ *  Copyright (C) 2009 Lemote Inc.
+ *  Author: Wu Zhangjin <wuzj@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/module.h>
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/video_output.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+#include <linux/apm-emulation.h>
+
+#include <asm/bootinfo.h>	/* for arcs_cmdline */
+
+#include <loongson.h>
+
+#include <cs5536/cs5536.h>
+#include "ec_kb3310b.h"
+
+#define DRIVER_VERSION "0.1"
+
+/* backlight subdriver */
+#define MAX_BRIGHTNESS 8
+
+static int yeeloong_set_brightness(struct backlight_device *bd)
+{
+	unsigned int level, current_level;
+	static unsigned int old_level;
+
+	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 < 0)
+		level = 0;
+
+	/* avoid tune the brightness when the EC is tuning it */
+	current_level = ec_read(REG_DISPLAY_BRIGHTNESS);
+	if ((old_level = current_level) && (old_level != level))
+		ec_write(REG_DISPLAY_BRIGHTNESS, level);
+	old_level = level;
+
+	return 0;
+}
+
+static int yeeloong_get_brightness(struct backlight_device *bd)
+{
+	return (int)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(struct device *dev)
+{
+	int ret;
+
+	yeeloong_backlight_dev = backlight_device_register("backlight0", dev,
+			NULL, &backlight_ops);
+
+	if (IS_ERR(yeeloong_backlight_dev)) {
+		ret = PTR_ERR(yeeloong_backlight_dev);
+		yeeloong_backlight_dev = NULL;
+		return ret;
+	}
+
+	yeeloong_backlight_dev->props.max_brightness = MAX_BRIGHTNESS;
+	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;
+	}
+}
+
+/* hwmon subdriver */
+
+/* pwm(auto/manual) enable or not */
+static int get_fan_pwm_enable(void)
+{
+	/* This get the fan control method: auto or manual */
+	return ec_read(REG_FAN_AUTO_MAN_SWITCH);
+}
+
+static void set_fan_pwm_enable(int manual)
+{
+	ec_write(REG_FAN_AUTO_MAN_SWITCH, !!manual);
+}
+
+static int get_fan_pwm(void)
+{
+	/* fan speed level */
+	return ec_read(REG_FAN_SPEED_LEVEL);
+}
+
+static void set_fan_pwm(int value)
+{
+	int status;
+
+	value = SENSORS_LIMIT(value, 0, 3);
+
+	/* if value is not ZERO, we should ensure it is on */
+	if (value != 0) {
+		status = ec_read(REG_FAN_STATUS);
+		if (status = 0)
+			ec_write(REG_FAN_CONTROL, BIT_FAN_CONTROL_ON);
+	}
+	/* 0xf4cc is for writing */
+	ec_write(REG_FAN_SPEED_LEVEL, value);
+}
+
+static int get_fan_rpm(void)
+{
+	int value = 0;
+
+	value = FAN_SPEED_DIVIDER /
+	    (((ec_read(REG_FAN_SPEED_HIGH) & 0x0f) << 8) |
+	     ec_read(REG_FAN_SPEED_LOW));
+
+	return value;
+}
+
+static int get_cpu_temp(void)
+{
+	int value;
+
+	value = ec_read(REG_TEMPERATURE_VALUE);
+
+	if (value & (1 << 7))
+		value = (value & 0x7f) - 128;
+	else
+		value = value & 0xff;
+
+	return value * 1000;
+}
+
+static int get_battery_temp(void)
+{
+	int value;
+
+	value = (ec_read(REG_BAT_TEMPERATURE_HIGH) << 8) |
+		(ec_read(REG_BAT_TEMPERATURE_LOW));
+
+	return value * 1000;
+}
+
+static int get_battery_current(void)
+{
+	int value;
+
+	value = (ec_read(REG_BAT_CURRENT_HIGH) << 8) |
+		(ec_read(REG_BAT_CURRENT_LOW));
+
+	if (value & 0x8000)
+		value = 0xffff - value;
+
+	return value;
+}
+
+static int get_battery_voltage(void)
+{
+	int value;
+
+	value = (ec_read(REG_BAT_VOLTAGE_HIGH) << 8) |
+		(ec_read(REG_BAT_VOLTAGE_LOW));
+
+	return value;
+}
+
+
+static int parse_arg(const char *buf, unsigned long count, int *val)
+{
+	if (!count)
+		return 0;
+	if (sscanf(buf, "%i", val) != 1)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t store_sys_hwmon(void (*set) (int), const char *buf, size_t count)
+{
+	int rv, value;
+
+	rv = parse_arg(buf, count, &value);
+	if (rv > 0)
+		set(value);
+	return rv;
+}
+
+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(temp2_input, S_IRUGO, get_battery_temp, NULL);
+CREATE_SENSOR_ATTR(curr1_input, S_IRUGO, get_battery_current, NULL);
+CREATE_SENSOR_ATTR(in1_input, S_IRUGO, get_battery_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_temp2_input.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
+};
+
+struct device *yeeloong_hwmon_dev;
+
+static int yeeloong_hwmon_init(struct device *dev)
+{
+	int ret;
+
+	yeeloong_hwmon_dev = hwmon_device_register(dev);
+	if (IS_ERR(yeeloong_hwmon_dev)) {
+		printk(KERN_INFO "Could not register yeeloong hwmon device\n");
+		yeeloong_hwmon_dev = NULL;
+		return PTR_ERR(yeeloong_hwmon_dev);
+	}
+	ret = sysfs_create_group(&yeeloong_hwmon_dev->kobj,
+				 &hwmon_attribute_group);
+	if (ret) {
+		sysfs_remove_group(&yeeloong_hwmon_dev->kobj,
+				   &hwmon_attribute_group);
+		hwmon_device_unregister(yeeloong_hwmon_dev);
+		yeeloong_hwmon_dev = NULL;
+	}
+	/* ensure fan is set to auto mode */
+	set_fan_pwm_enable(BIT_FAN_AUTO);
+
+	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 */
+
+static int lcd_video_output_get(struct output_device *od)
+{
+	return ec_read(REG_DISPLAY_LCD);
+}
+
+static int lcd_video_output_set(struct output_device *od)
+{
+	unsigned long status = od->request_state;
+	int value;
+
+	if (status = BIT_DISPLAY_LCD_ON) {
+		/* open LCD */
+		outb(0x31, 0x3c4);
+		value = inb(0x3c5);
+		value = (value & 0xf8) | 0x03;
+		outb(0x31, 0x3c4);
+		outb(value, 0x3c5);
+		/* open backlight */
+		ec_write(REG_BACKLIGHT_CTRL, BIT_BACKLIGHT_ON);
+	} else {
+		/* close backlight */
+		ec_write(REG_BACKLIGHT_CTRL, BIT_BACKLIGHT_OFF);
+		/* close LCD */
+		outb(0x31, 0x3c4);
+		value = inb(0x3c5);
+		value = (value & 0xf8) | 0x02;
+		outb(0x31, 0x3c4);
+		outb(value, 0x3c5);
+	}
+
+	return 0;
+}
+
+static struct output_properties lcd_output_properties = {
+	.set_state = lcd_video_output_set,
+	.get_status = lcd_video_output_get,
+};
+
+static int crt_video_output_get(struct output_device *od)
+{
+	return ec_read(REG_CRT_DETECT);
+}
+
+static int crt_video_output_set(struct output_device *od)
+{
+	unsigned long status = od->request_state;
+	int value;
+
+	if (status = BIT_CRT_DETECT_PLUG) {
+		if (ec_read(REG_CRT_DETECT) = BIT_CRT_DETECT_PLUG) {
+			/* open CRT */
+			outb(0x21, 0x3c4);
+			value = inb(0x3c5);
+			value &= ~(1 << 7);
+			outb(0x21, 0x3c4);
+			outb(value, 0x3c5);
+		}
+	} else {
+		/* close CRT */
+		outb(0x21, 0x3c4);
+		value = inb(0x3c5);
+		value |= (1 << 7);
+		outb(0x21, 0x3c4);
+		outb(value, 0x3c5);
+	}
+
+	return 0;
+}
+
+static struct output_properties crt_output_properties = {
+	.set_state = crt_video_output_set,
+	.get_status = crt_video_output_get,
+};
+
+struct output_device *lcd_output_dev, *crt_output_dev;
+
+static void lcd_vo_set(int status)
+{
+	lcd_output_dev->request_state = status;
+	lcd_video_output_set(lcd_output_dev);
+}
+
+static void crt_vo_set(int status)
+{
+	crt_output_dev->request_state = status;
+	crt_video_output_set(crt_output_dev);
+}
+
+static int crt_detect_handler(int status)
+{
+	if (status = BIT_CRT_DETECT_PLUG) {
+		crt_vo_set(BIT_CRT_DETECT_PLUG);
+		lcd_vo_set(BIT_DISPLAY_LCD_OFF);
+	} else {
+		lcd_vo_set(BIT_DISPLAY_LCD_ON);
+		crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+	}
+	return status;
+}
+
+static int black_screen_handler(int status)
+{
+	char *p = NULL, *ec_ver = NULL;
+
+	ec_ver = strstr(arcs_cmdline, "EC_VER=");
+	if (ec_ver) {
+		p = strstr(ec_ver, " ");
+		if (p)
+			*p = '\0';
+	}
+
+	/* Seems EC(>=PQ1D26) does this job for us, we can not do it again,
+	 * otherwise, the brightness will not resume! */
+	if ((ec_ver = NULL) || strncasecmp(ec_ver, "EC_VER=PQ1D26", 64) < 0)
+		lcd_vo_set(status);
+
+	return status;
+}
+
+static int display_toggle_handler(int status)
+{
+	static int video_output_status;
+
+	/* only enable switch video output button
+	 * when CRT is connected */
+	if (ec_read(REG_CRT_DETECT) = BIT_CRT_DETECT_UNPLUG)
+		return 0;
+	/* 0. no CRT connected: LCD on, CRT off
+	 * 1. BOTH on
+	 * 2. LCD off, CRT on
+	 * 3. BOTH off
+	 * 4. LCD on, CRT off
+	 */
+	video_output_status++;
+	if (video_output_status > 4)
+		video_output_status = 1;
+
+	switch (video_output_status) {
+	case 1:
+		lcd_vo_set(BIT_DISPLAY_LCD_ON);
+		crt_vo_set(BIT_CRT_DETECT_PLUG);
+		break;
+	case 2:
+		lcd_vo_set(BIT_DISPLAY_LCD_OFF);
+		crt_vo_set(BIT_CRT_DETECT_PLUG);
+		break;
+	case 3:
+		lcd_vo_set(BIT_DISPLAY_LCD_OFF);
+		crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+		break;
+	case 4:
+		lcd_vo_set(BIT_DISPLAY_LCD_ON);
+		crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+		break;
+	default:
+		/* ensure LCD is on */
+		lcd_vo_set(BIT_DISPLAY_LCD_ON);
+		break;
+	}
+	return video_output_status;
+}
+
+static int yeeloong_vo_init(struct device *dev)
+{
+	int ret;
+
+	/* register video output device: lcd, crt */
+	lcd_output_dev = video_output_register("LCD", dev, 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 */
+	lcd_vo_set(1);
+
+	crt_output_dev = video_output_register("CRT", dev, NULL,
+			&crt_output_properties);
+
+	if (IS_ERR(crt_output_dev)) {
+		ret = PTR_ERR(crt_output_dev);
+		crt_output_dev = NULL;
+		return ret;
+	}
+	/* close CRT by default, and will be enabled
+	 * when the CRT connectting event reported by SCI */
+	crt_vo_set(0);
+
+	/* install event handlers */
+	yeeloong_install_sci_handler(EVENT_CRT_DETECT,
+					   crt_detect_handler);
+	yeeloong_install_sci_handler(EVENT_BLACK_SCREEN,
+					   black_screen_handler);
+	yeeloong_install_sci_handler(EVENT_DISPLAY_TOGGLE,
+					   display_toggle_handler);
+
+	return 0;
+}
+
+static void yeeloong_vo_exit(void)
+{
+	/* uninstall event handlers */
+	yeeloong_uninstall_sci_handler(EVENT_CRT_DETECT,
+					   crt_detect_handler);
+	yeeloong_uninstall_sci_handler(EVENT_BLACK_SCREEN,
+					   black_screen_handler);
+	yeeloong_uninstall_sci_handler(EVENT_DISPLAY_TOGGLE,
+					   display_toggle_handler);
+
+	if (lcd_output_dev) {
+		video_output_unregister(lcd_output_dev);
+		lcd_output_dev = NULL;
+	}
+	if (crt_output_dev) {
+		video_output_unregister(crt_output_dev);
+		crt_output_dev = NULL;
+	}
+}
+
+/* Thermal cooling devices subdriver */
+
+static int video_get_max_state(struct thermal_cooling_device *cdev, unsigned
+			       long *state)
+{
+	*state = MAX_BRIGHTNESS;
+	return 0;
+}
+
+static int video_get_cur_state(struct thermal_cooling_device *cdev, unsigned
+			       long *state)
+{
+	static struct backlight_device *bd;
+
+	bd = (struct backlight_device *)cdev->devdata;
+
+	*state = yeeloong_get_brightness(bd);
+
+	return 0;
+}
+
+static int video_set_cur_state(struct thermal_cooling_device *cdev, unsigned
+			       long state)
+{
+	static struct backlight_device *bd;
+
+	bd = (struct backlight_device *)cdev->devdata;
+
+	yeeloong_backlight_dev->props.brightness = state;
+	backlight_update_status(bd);
+
+	return 0;
+}
+
+static struct thermal_cooling_device_ops video_cooling_ops = {
+	.get_max_state = video_get_max_state,
+	.get_cur_state = video_get_cur_state,
+	.set_cur_state = video_set_cur_state,
+};
+
+static struct thermal_cooling_device *yeeloong_thermal_cdev;
+
+/* TODO: register fan, cpu as the cooling devices */
+static int yeeloong_thermal_init(struct device *dev)
+{
+	int ret;
+
+	if (!dev)
+		return -1;
+
+	yeeloong_thermal_cdev = thermal_cooling_device_register("LCD", dev,
+			&video_cooling_ops);
+
+	if (IS_ERR(yeeloong_thermal_cdev)) {
+		ret = PTR_ERR(yeeloong_thermal_cdev);
+		return ret;
+	}
+
+	ret = sysfs_create_link(&dev->kobj,
+				&yeeloong_thermal_cdev->device.kobj,
+				"thermal_cooling");
+	if (ret) {
+		printk(KERN_ERR "Create sysfs link\n");
+		return ret;
+	}
+	ret = sysfs_create_link(&yeeloong_thermal_cdev->device.kobj,
+				&dev->kobj, "device");
+	if (ret) {
+		printk(KERN_ERR "Create sysfs link\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void yeeloong_thermal_exit(struct device *dev)
+{
+	if (yeeloong_thermal_cdev) {
+		if (dev)
+			sysfs_remove_link(&dev->kobj, "thermal_cooling");
+		sysfs_remove_link(&yeeloong_thermal_cdev->device.kobj,
+				  "device");
+		thermal_cooling_device_unregister(yeeloong_thermal_cdev);
+		yeeloong_thermal_cdev = NULL;
+	}
+}
+
+/* hotkey input subdriver */
+
+static struct input_dev *yeeloong_hotkey_dev;
+static int event, status;
+
+struct key_entry {
+	char type;		/* See KE_* below */
+	int event;		/* event from SCI */
+	u16 keycode;		/* KEY_* or SW_* */
+};
+
+enum { KE_KEY, KE_SW, KE_END };
+
+static struct key_entry yeeloong_keymap[] = {
+	{KE_SW, EVENT_LID, SW_LID},
+	/* SW_VIDEOOUT_INSERT? not included in hald-addon-input! */
+	{KE_KEY, EVENT_CRT_DETECT, KEY_PROG1},
+	/* Seems battery subdriver should report it */
+	{KE_KEY, EVENT_OVERTEMP, KEY_PROG2},
+	/*{KE_KEY, EVENT_AC_BAT, KEY_BATTERY},*/
+	{KE_KEY, EVENT_CAMERA, KEY_CAMERA},	/* Fn + ESC */
+	{KE_KEY, EVENT_SLEEP, KEY_SLEEP},	/* Fn + F1 */
+	/* Seems not clear? not included in hald-addon-input! */
+	{KE_KEY, EVENT_BLACK_SCREEN, KEY_PROG3},	/* 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 yeeloong_lid_update_status(int status)
+{
+	input_report_switch(yeeloong_hotkey_dev, SW_LID, !status);
+	input_sync(yeeloong_hotkey_dev);
+
+	return status;
+}
+
+static void yeeloong_hotkey_update_status(int key)
+{
+	input_report_key(yeeloong_hotkey_dev, key, 1);
+	input_sync(yeeloong_hotkey_dev);
+	input_report_key(yeeloong_hotkey_dev, key, 0);
+	input_sync(yeeloong_hotkey_dev);
+}
+
+static int get_event_keycode(void)
+{
+	struct key_entry *key;
+
+	for (key = yeeloong_keymap; key->type != KE_END; key++) {
+		if (key->event != event)
+			continue;
+		else {
+			if (EVENT_DISPLAY_BRIGHTNESS = event) {
+				static int old_brightness_status = -1;
+				/* current status > old one, means up */
+				if ((status < old_brightness_status)
+				    || (0 = status))
+					key++;
+				old_brightness_status = status;
+			} else if (EVENT_AUDIO_VOLUME = event) {
+				static int old_volume_status = -1;
+				if ((status < old_volume_status)
+				    || (0 = status))
+					key++;
+				old_volume_status = status;
+			}
+			break;
+		}
+	}
+	return key->keycode;
+}
+
+void yeeloong_report_key(void)
+{
+	int keycode;
+
+	keycode = get_event_keycode();
+
+	if (keycode = SW_LID)
+		yeeloong_lid_update_status(status);
+	else
+		yeeloong_hotkey_update_status(keycode);
+}
+
+enum { NO_REG, MUL_REG, REG_END };
+
+int event_reg[15] = {
+	REG_LID_DETECT,		/*  press the lid or not */
+	NO_REG,			/*  Fn+F3 for display switch */
+	NO_REG,			/*  Fn+F1 for entering sleep mode */
+	MUL_REG,		/*  Over-temperature happened */
+	REG_CRT_DETECT,		/*  CRT is connected */
+	REG_CAMERA_STATUS,	/*  Camera is on or off */
+	REG_USB2_FLAG,		/*  USB2 Over Current occurred */
+	REG_USB0_FLAG,		/*  USB0 Over Current occurred */
+	REG_DISPLAY_LCD,	/*  Black screen is on or off */
+	REG_AUDIO_MUTE,		/*  Mute is on or off */
+	REG_DISPLAY_BRIGHTNESS,	/*  LCD backlight brightness adjust */
+	NO_REG,			/*  ac & battery relative issue */
+	REG_AUDIO_VOLUME,	/*  Volume adjust */
+	REG_WLAN,		/*  Wlan is on or off */
+	REG_END
+};
+
+static int ec_get_event_status(void)
+{
+	int reg;
+
+	reg = event_reg[event - EVENT_LID];
+
+	if (reg = NO_REG)
+		return 1;
+	else if (reg = MUL_REG) {
+		if (event = EVENT_OVERTEMP) {
+			return (ec_read(REG_BAT_CHARGE_STATUS) &
+				BIT_BAT_CHARGE_STATUS_OVERTEMP) >> 2;
+		}
+	} else if (reg != REG_END)
+		return ec_read(reg);
+
+	return -1;
+}
+
+static sci_handler event_handler[15];
+
+int yeeloong_install_sci_handler(int event, sci_handler handler)
+{
+	if (event_handler[event - EVENT_LID] != NULL) {
+		printk(KERN_INFO "There is a handler installed for event: %d\n",
+		       event);
+		return -1;
+	}
+	event_handler[event - EVENT_LID] = handler;
+
+	return 0;
+}
+EXPORT_SYMBOL(yeeloong_install_sci_handler);
+
+int yeeloong_uninstall_sci_handler(int event, sci_handler handler)
+{
+	if (event_handler[event - EVENT_LID] = NULL) {
+		printk(KERN_INFO "There is no handler installed for event: %d\n",
+		       event);
+		return -1;
+	}
+	if (event_handler[event - EVENT_LID] != handler) {
+		printk(KERN_INFO "You can not uninstall the handler installed by others\n");
+		return -1;
+	}
+	event_handler[event - EVENT_LID] = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL(yeeloong_uninstall_sci_handler);
+
+static void yeeloong_event_action(void)
+{
+	sci_handler handler;
+
+	handler = event_handler[event - EVENT_LID];
+
+	if (handler = NULL)
+		return;
+
+	if (event = EVENT_CAMERA)
+		status = handler(3);
+	else
+		status = handler(status);
+}
+
+/*
+ * sci 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;
+
+	if (SCI_IRQ_NUM != irq) {
+		printk(KERN_ERR "%s: spurious irq.\n", __func__);
+		return IRQ_NONE;
+	}
+
+	/* query the event number */
+	ret = ec_query_event_num();
+	if (ret < 0) {
+		printk(KERN_ERR "%s: return: %d\n", __func__, ret);
+		return IRQ_NONE;
+	}
+
+	event = ec_get_event_num();
+	if (event < 0) {
+		printk(KERN_ERR "%s: return: %d\n", __func__, event);
+		return IRQ_NONE;
+	}
+
+	printk(KERN_INFO "sci event number: 0x%x\n", event);
+
+	/* parse the event number and wake the queue */
+	if ((event != 0x00) && (event != 0xff)) {
+		/* get status of current event */
+		status = ec_get_event_status();
+		printk(KERN_INFO "%s: status: %d\n", __func__, status);
+		if (status = -1)
+			return IRQ_NONE;
+		/* execute relative actions */
+		yeeloong_event_action();
+		/* report current key */
+		yeeloong_report_key();
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+ * config and init some msr and gpio register properly.
+ */
+static int sci_irq_init(void)
+{
+	u32 hi, lo;
+	u32 gpio_base;
+	int ret = 0;
+	unsigned long flags;
+
+	/* 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) {
+		printk(KERN_ERR "%s: failed.\n", __func__);
+		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;
+}
+
+struct irqaction sci_irqaction = {
+	.handler = sci_irq_handler,
+	.name = "sci",
+	.flags = IRQF_SHARED,
+};
+
+static int setup_sci(void)
+{
+	sci_irq_init();
+
+	setup_irq(SCI_IRQ_NUM, &sci_irqaction);
+
+	return 0;
+}
+
+static ssize_t
+ignore_store(struct device *dev,
+	     struct device_attribute *attr, const char *buf, size_t count)
+{
+	return count;
+}
+
+static ssize_t
+show_hotkeystate(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d %d\n", event, status);
+}
+
+static DEVICE_ATTR(state, 0444, show_hotkeystate, ignore_store);
+
+static struct attribute *hotkey_attributes[] = {
+	&dev_attr_state.attr,
+	NULL
+};
+
+static struct attribute_group hotkey_attribute_group = {
+	.attrs = hotkey_attributes
+};
+
+static int camera_set(int status)
+{
+	int value;
+	static int camera_status;
+
+	if (status = 2)
+		/* resume the old camera status */
+		camera_set(camera_status);
+	else if (status = 3) {
+		/* revert the camera status */
+		value = ec_read(REG_CAMERA_CONTROL);
+		ec_write(REG_CAMERA_CONTROL, value | (1 << 1));
+	} else {		/* status = 0 or status = 1 */
+		status = !!status;
+		camera_status = ec_read(REG_CAMERA_STATUS);
+		if (status != camera_status)
+			camera_set(3);
+	}
+	return ec_read(REG_CAMERA_STATUS);
+}
+
+#define I8042_STATUS_REG	0x64
+#define I8042_DATA_REG		0x60
+#define i8042_read_status() inb(I8042_STATUS_REG)
+#define i8042_read_data() inb(I8042_DATA_REG)
+#define I8042_STR_OBF		0x01
+#define I8042_BUFFER_SIZE	16
+
+static void i8042_flush(void)
+{
+	int i;
+
+	while ((i8042_read_status() & I8042_STR_OBF)
+		&& (i < I8042_BUFFER_SIZE)) {
+		udelay(50);
+		i8042_read_data();
+		i++;
+	}
+}
+
+static int yeeloong_hotkey_init(struct device *dev)
+{
+	int ret;
+	struct key_entry *key;
+
+	/* flush the buffer of keyboard */
+	i8042_flush();
+
+	/* setup the system control interface */
+	setup_sci();
+
+	yeeloong_hotkey_dev = input_allocate_device();
+
+	if (!yeeloong_hotkey_dev)
+		return -ENOMEM;
+
+	yeeloong_hotkey_dev->name = "HotKeys";
+	yeeloong_hotkey_dev->phys = "button/input0";
+	yeeloong_hotkey_dev->id.bustype = BUS_HOST;
+	yeeloong_hotkey_dev->dev.parent = dev;
+
+	for (key = yeeloong_keymap; key->type != KE_END; key++) {
+		switch (key->type) {
+		case KE_KEY:
+			set_bit(EV_KEY, yeeloong_hotkey_dev->evbit);
+			set_bit(key->keycode, yeeloong_hotkey_dev->keybit);
+			break;
+		case KE_SW:
+			set_bit(EV_SW, yeeloong_hotkey_dev->evbit);
+			set_bit(key->keycode, yeeloong_hotkey_dev->swbit);
+			break;
+		}
+	}
+
+	ret = input_register_device(yeeloong_hotkey_dev);
+	if (ret) {
+		input_free_device(yeeloong_hotkey_dev);
+		return ret;
+	}
+
+	ret = sysfs_create_group(&yeeloong_hotkey_dev->dev.kobj,
+				 &hotkey_attribute_group);
+	if (ret) {
+		sysfs_remove_group(&yeeloong_hotkey_dev->dev.kobj,
+				   &hotkey_attribute_group);
+		input_unregister_device(yeeloong_hotkey_dev);
+		yeeloong_hotkey_dev = NULL;
+	}
+	/* update the current status of lid */
+	yeeloong_lid_update_status(BIT_LID_DETECT_ON);
+
+	/* install the real yeeloong_report_lid_status for pm.c */
+	yeeloong_report_lid_status = yeeloong_lid_update_status;
+
+	/* install event handler */
+	yeeloong_install_sci_handler(EVENT_CAMERA, camera_set);
+
+	return 0;
+}
+
+static void yeeloong_hotkey_exit(void)
+{
+	/* free irq */
+	remove_irq(SCI_IRQ_NUM, &sci_irqaction);
+
+	/* uninstall the real yeeloong_report_lid_status for pm.c */
+	yeeloong_report_lid_status = NULL;
+
+	/* uninstall event handler */
+	yeeloong_uninstall_sci_handler(EVENT_CAMERA, camera_set);
+
+	if (yeeloong_hotkey_dev) {
+		sysfs_remove_group(&yeeloong_hotkey_dev->dev.kobj,
+				   &hotkey_attribute_group);
+		input_unregister_device(yeeloong_hotkey_dev);
+		yeeloong_hotkey_dev = NULL;
+	}
+}
+
+/* battery subdriver: APM emulated support */
+
+static void get_fixed_battery_info(void)
+{
+	int design_cap, full_charged_cap, design_vol, vendor, cell_count;
+
+	design_cap = (ec_read(REG_BAT_DESIGN_CAP_HIGH) << 8)
+	    | ec_read(REG_BAT_DESIGN_CAP_LOW);
+	full_charged_cap = (ec_read(REG_BAT_FULLCHG_CAP_HIGH) << 8)
+	    | ec_read(REG_BAT_FULLCHG_CAP_LOW);
+	design_vol = (ec_read(REG_BAT_DESIGN_VOL_HIGH) << 8)
+	    | ec_read(REG_BAT_DESIGN_VOL_LOW);
+	vendor = ec_read(REG_BAT_VENDOR);
+	cell_count = ec_read(REG_BAT_CELL_COUNT);
+
+	if (vendor != 0) {
+		printk(KERN_INFO
+		       "battery vendor(%s), cells count(%d), "
+		       "with designed capacity(%d),designed voltage(%d),"
+		       " full charged capacity(%d)\n",
+		       (vendor =
+			FLAG_BAT_VENDOR_SANYO) ? "SANYO" : "SIMPLO",
+		       (cell_count = FLAG_BAT_CELL_3S1P) ? 3 : 6,
+		       design_cap, design_vol,
+		       full_charged_cap);
+	}
+}
+
+#define APM_CRITICAL		5
+
+static void yeeloong_apm_get_power_status(struct apm_power_info *info)
+{
+	unsigned char bat_status;
+
+	info->battery_status = APM_BATTERY_STATUS_UNKNOWN;
+	info->battery_flag = APM_BATTERY_FLAG_UNKNOWN;
+	info->units = APM_UNITS_MINS;
+
+	info->battery_life = (ec_read(REG_BAT_RELATIVE_CAP_HIGH) << 8) |
+		(ec_read(REG_BAT_RELATIVE_CAP_LOW));
+
+	info->ac_line_status = (ec_read(REG_BAT_POWER) & BIT_BAT_POWER_ACIN) ?
+		APM_AC_ONLINE : APM_AC_OFFLINE;
+
+	bat_status = ec_read(REG_BAT_STATUS);
+
+	if (!(bat_status & BIT_BAT_STATUS_IN)) {
+		/* no battery inserted */
+		info->battery_status = APM_BATTERY_STATUS_NOT_PRESENT;
+		info->battery_flag = APM_BATTERY_FLAG_NOT_PRESENT;
+		info->time = 0x00;
+		return;
+	}
+
+	/* adapter inserted */
+	if (info->ac_line_status = APM_AC_ONLINE) {
+		if (!(bat_status & BIT_BAT_STATUS_FULL)) {
+			/* battery is not fully charged */
+			info->battery_status = APM_BATTERY_STATUS_CHARGING;
+			info->battery_flag = APM_BATTERY_FLAG_CHARGING;
+		} else {
+			/* if the battery is fully charged */
+			info->battery_status = APM_BATTERY_STATUS_HIGH;
+			info->battery_flag = APM_BATTERY_FLAG_HIGH;
+			info->battery_life = 100;
+		}
+	} else {
+		/* battery is too low */
+		if (bat_status & BIT_BAT_STATUS_LOW) {
+			info->battery_status = APM_BATTERY_STATUS_LOW;
+			info->battery_flag = APM_BATTERY_FLAG_LOW;
+			if (info->battery_life <= APM_CRITICAL) {
+				/* we should power off the system now */
+				info->battery_status +					APM_BATTERY_STATUS_CRITICAL;
+				info->battery_flag = APM_BATTERY_FLAG_CRITICAL;
+			}
+		} else {
+			/* assume the battery is high enough. */
+			info->battery_status = APM_BATTERY_STATUS_HIGH;
+			info->battery_flag = APM_BATTERY_FLAG_HIGH;
+		}
+	}
+	info->time = ((info->battery_life - 3) * 54 + 142) / 60;
+}
+
+static int yeeloong_apm_init(void)
+{
+	/* print fixed information of battery */
+	get_fixed_battery_info();
+
+	apm_get_power_status = yeeloong_apm_get_power_status;
+
+	return 0;
+}
+
+static void yeeloong_apm_exit(void)
+{
+}
+
+/* platform subdriver */
+static struct platform_device *yeeloong_pdev;
+
+static void __maybe_unused 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 __maybe_unused yeeloong_suspend(struct platform_device *pdev,
+		pm_message_t state)
+{
+	printk(KERN_INFO "yeeloong specific suspend\n");
+
+	/* close LCD */
+	lcd_vo_set(BIT_DISPLAY_LCD_OFF);
+	/* close CRT */
+	crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+	/* power off camera */
+	camera_set(BIT_CAMERA_CONTROL_OFF);
+	/* poweroff three usb ports */
+	usb_ports_set(BIT_USB_FLAG_OFF);
+	/* minimize the speed of FAN */
+	set_fan_pwm_enable(BIT_FAN_MANUAL);
+	set_fan_pwm(1);
+
+	return 0;
+}
+
+static int __maybe_unused yeeloong_resume(struct platform_device *pdev)
+{
+	printk(KERN_INFO "yeeloong specific resume\n");
+
+	/* resume the status of lcd & crt */
+	lcd_vo_set(BIT_DISPLAY_LCD_ON);
+	crt_vo_set(BIT_CRT_DETECT_PLUG);
+
+	/* power on three usb ports */
+	usb_ports_set(BIT_USB_FLAG_ON);
+
+	/* resume the camera status */
+	camera_set(2);
+
+	/* resume fan to auto mode */
+	set_fan_pwm_enable(BIT_FAN_AUTO);
+
+	return 0;
+}
+
+static struct platform_driver platform_driver = {
+	.driver = {
+		   .name = "yeeloong-laptop",
+		   .owner = THIS_MODULE,
+		   },
+#ifdef CONFIG_PM
+	.suspend = yeeloong_suspend,
+	.resume = yeeloong_resume,
+#endif
+};
+
+static ssize_t yeeloong_pdev_name_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "yeeloong laptop\n");
+}
+
+static struct device_attribute dev_attr_yeeloong_pdev_name +__ATTR(name, S_IRUGO, yeeloong_pdev_name_show, NULL);
+
+static int yeeloong_pdev_init(void)
+{
+	int ret;
+
+	/* Register platform stuff */
+	ret = platform_driver_register(&platform_driver);
+	if (ret)
+		return ret;
+
+	yeeloong_pdev = platform_device_alloc("yeeloong-laptop", -1);
+	if (!yeeloong_pdev) {
+		ret = -ENOMEM;
+		platform_driver_unregister(&platform_driver);
+		return ret;
+	}
+
+	ret = platform_device_add(yeeloong_pdev);
+	if (ret) {
+		platform_device_put(yeeloong_pdev);
+		return ret;
+	}
+
+	if (IS_ERR(yeeloong_pdev)) {
+		ret = PTR_ERR(yeeloong_pdev);
+		yeeloong_pdev = NULL;
+		printk(KERN_INFO "unable to register platform device\n");
+		return ret;
+	}
+
+	ret = device_create_file(&yeeloong_pdev->dev,
+				 &dev_attr_yeeloong_pdev_name);
+	if (ret) {
+		printk(KERN_INFO "unable to create sysfs device attributes\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void yeeloong_pdev_exit(void)
+{
+	if (yeeloong_pdev) {
+		platform_device_unregister(yeeloong_pdev);
+		yeeloong_pdev = NULL;
+		platform_driver_unregister(&platform_driver);
+	}
+}
+
+static int __init yeeloong_init(void)
+{
+	int ret;
+
+	if (mips_machtype != MACH_LEMOTE_YL2F89) {
+		printk(KERN_INFO "This Driver is for YeeLoong netbook, You"
+				"can not use it on the other Machines\n");
+		return -EFAULT;
+	}
+
+	printk(KERN_INFO "YeeLoong Platform Specific Drivers %s loaded\n",
+			DRIVER_VERSION);
+
+	ret = yeeloong_pdev_init();
+	if (ret) {
+		yeeloong_pdev_exit();
+		printk(KERN_INFO "init yeeloong platform driver failure\n");
+		return ret;
+	}
+	ret = yeeloong_hotkey_init(&yeeloong_pdev->dev);
+	if (ret) {
+		yeeloong_hotkey_exit();
+		printk(KERN_INFO "init yeeloong hotkey driver failure\n");
+		return ret;
+	}
+	ret = yeeloong_apm_init();
+	if (ret) {
+		yeeloong_apm_exit();
+		printk(KERN_INFO "init yeeloong apm driver failure\n");
+		return ret;
+	}
+	ret = yeeloong_backlight_init(&yeeloong_pdev->dev);
+	if (ret) {
+		yeeloong_backlight_exit();
+		printk(KERN_INFO "init yeeloong backlight driver failure\n");
+		return ret;
+	}
+	ret = yeeloong_thermal_init(&yeeloong_backlight_dev->dev);
+	if (ret) {
+		yeeloong_thermal_exit(&yeeloong_backlight_dev->dev);
+		printk(KERN_INFO
+		       "Fail to init yeeloong thermal cooling device.\n");
+		return ret;
+	}
+	ret = yeeloong_hwmon_init(&yeeloong_pdev->dev);
+	if (ret) {
+		yeeloong_hwmon_exit();
+		printk(KERN_INFO "init yeeloong hwmon driver failure\n");
+		return ret;
+	}
+	ret = yeeloong_vo_init(&yeeloong_pdev->dev);
+	if (ret) {
+		yeeloong_vo_exit();
+		printk(KERN_INFO "init yeeloong video output driver failure\n");
+		return ret;
+	}
+	return 0;
+}
+
+static void __exit yeeloong_exit(void)
+{
+	yeeloong_vo_exit();
+	yeeloong_hwmon_exit();
+	yeeloong_thermal_exit(&yeeloong_backlight_dev->dev);
+	yeeloong_backlight_exit();
+	yeeloong_apm_exit();
+	yeeloong_hotkey_exit();
+	yeeloong_pdev_exit();
+
+	printk(KERN_INFO "YeeLoong Platform Specific Drivers %s unloaded\n",
+			DRIVER_VERSION);
+}
+
+module_init(yeeloong_init);
+module_exit(yeeloong_exit);
+
+MODULE_AUTHOR("Wu Zhangjin <wuzj@lemote.com>");
+MODULE_DESCRIPTION("YeeLoong laptop driver");
+MODULE_LICENSE("GPL");
-- 
1.6.2.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] [loongson] yeeloong2f: add platform specific support
  2009-11-19 17:39   ` [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform Wu Zhangjin
@ 2009-11-19 18:02     ` Wu Zhangjin
  -1 siblings, 0 replies; 13+ messages in thread
From: Wu Zhangjin @ 2009-11-19 18:02 UTC (permalink / raw)
  To: Ralf Baechle
  Cc: linux-mips, zhangfx, yanh, huhb, Richard Purdie, lm-sensors, Jamey Hicks

Sorry, This patch can not be compiled without CONFIG_PM, this patch is
needed to fix it:

We need to wrap the source code relative to CONFIG_PM, otherwise, it
will fail in compiling.

This patch is needed to "[PATCH 5/5] [loongson] yeeloong2f: add platform
specific support".

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 arch/mips/loongson/lemote-2f/yeeloong_laptop.c |   12 ++++++++----
 1 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
b/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
index ff74f7f..46c3847 100644
--- a/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
+++ b/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
@@ -1025,9 +1025,10 @@ static int yeeloong_hotkey_init(struct device
*dev)
 	/* update the current status of lid */
 	yeeloong_lid_update_status(BIT_LID_DETECT_ON);
 
+#ifdef CONFIG_SUSPEND
 	/* install the real yeeloong_report_lid_status for pm.c */
 	yeeloong_report_lid_status = yeeloong_lid_update_status;
-
+#endif
 	/* install event handler */
 	yeeloong_install_sci_handler(EVENT_CAMERA, camera_set);
 
@@ -1039,9 +1040,10 @@ static void yeeloong_hotkey_exit(void)
 	/* free irq */
 	remove_irq(SCI_IRQ_NUM, &sci_irqaction);
 
+#ifdef CONFIG_SUSPEND
 	/* uninstall the real yeeloong_report_lid_status for pm.c */
 	yeeloong_report_lid_status = NULL;
-
+#endif
 	/* uninstall event handler */
 	yeeloong_uninstall_sci_handler(EVENT_CAMERA, camera_set);
 
@@ -1083,7 +1085,8 @@ static void get_fixed_battery_info(void)
 
 #define APM_CRITICAL		5
 
-static void yeeloong_apm_get_power_status(struct apm_power_info *info)
+static void __maybe_unused yeeloong_apm_get_power_status(struct
apm_power_info
+		*info)
 {
 	unsigned char bat_status;
 
@@ -1144,8 +1147,9 @@ static int yeeloong_apm_init(void)
 	/* print fixed information of battery */
 	get_fixed_battery_info();
 
+#ifdef APM_EMULATION
 	apm_get_power_status = yeeloong_apm_get_power_status;
-
+#endif
 	return 0;
 }
 
-- 
1.6.2.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform
@ 2009-11-19 18:02     ` Wu Zhangjin
  0 siblings, 0 replies; 13+ messages in thread
From: Wu Zhangjin @ 2009-11-19 18:02 UTC (permalink / raw)
  To: Ralf Baechle
  Cc: linux-mips, zhangfx, yanh, huhb, Richard Purdie, lm-sensors, Jamey Hicks

Sorry, This patch can not be compiled without CONFIG_PM, this patch is
needed to fix it:

We need to wrap the source code relative to CONFIG_PM, otherwise, it
will fail in compiling.

This patch is needed to "[PATCH 5/5] [loongson] yeeloong2f: add platform
specific support".

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 arch/mips/loongson/lemote-2f/yeeloong_laptop.c |   12 ++++++++----
 1 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
b/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
index ff74f7f..46c3847 100644
--- a/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
+++ b/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
@@ -1025,9 +1025,10 @@ static int yeeloong_hotkey_init(struct device
*dev)
 	/* update the current status of lid */
 	yeeloong_lid_update_status(BIT_LID_DETECT_ON);
 
+#ifdef CONFIG_SUSPEND
 	/* install the real yeeloong_report_lid_status for pm.c */
 	yeeloong_report_lid_status = yeeloong_lid_update_status;
-
+#endif
 	/* install event handler */
 	yeeloong_install_sci_handler(EVENT_CAMERA, camera_set);
 
@@ -1039,9 +1040,10 @@ static void yeeloong_hotkey_exit(void)
 	/* free irq */
 	remove_irq(SCI_IRQ_NUM, &sci_irqaction);
 
+#ifdef CONFIG_SUSPEND
 	/* uninstall the real yeeloong_report_lid_status for pm.c */
 	yeeloong_report_lid_status = NULL;
-
+#endif
 	/* uninstall event handler */
 	yeeloong_uninstall_sci_handler(EVENT_CAMERA, camera_set);
 
@@ -1083,7 +1085,8 @@ static void get_fixed_battery_info(void)
 
 #define APM_CRITICAL		5
 
-static void yeeloong_apm_get_power_status(struct apm_power_info *info)
+static void __maybe_unused yeeloong_apm_get_power_status(struct
apm_power_info
+		*info)
 {
 	unsigned char bat_status;
 
@@ -1144,8 +1147,9 @@ static int yeeloong_apm_init(void)
 	/* print fixed information of battery */
 	get_fixed_battery_info();
 
+#ifdef APM_EMULATION
 	apm_get_power_status = yeeloong_apm_get_power_status;
-
+#endif
 	return 0;
 }
 
-- 
1.6.2.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] [loongson] yeeloong2f: add platform specific support
  2009-11-19 18:02     ` [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform Wu Zhangjin
@ 2009-11-19 21:42       ` Sergei Shtylyov
  -1 siblings, 0 replies; 13+ messages in thread
From: Sergei Shtylyov @ 2009-11-19 21:42 UTC (permalink / raw)
  To: wuzhangjin
  Cc: Ralf Baechle, linux-mips, zhangfx, yanh, huhb, Richard Purdie,
	lm-sensors, Jamey Hicks

Hello.

Wu Zhangjin wrote:

> Sorry, This patch can not be compiled without CONFIG_PM, this patch is
> needed to fix it:
>
> We need to wrap the source code relative to CONFIG_PM, otherwise, it
> will fail in compiling.
>
> This patch is needed to "[PATCH 5/5] [loongson] yeeloong2f: add platform
> specific support".
>
> Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
> ---
>  arch/mips/loongson/lemote-2f/yeeloong_laptop.c |   12 ++++++++----
>  1 files changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
> b/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
> index ff74f7f..46c3847 100644
> --- a/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
> +++ b/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
> @@ -1025,9 +1025,10 @@ static int yeeloong_hotkey_init(struct device
> *dev)
>  	/* update the current status of lid */
>  	yeeloong_lid_update_status(BIT_LID_DETECT_ON);
>  
> +#ifdef CONFIG_SUSPEND
>  	/* install the real yeeloong_report_lid_status for pm.c */
>  	yeeloong_report_lid_status = yeeloong_lid_update_status;
> -
> +#endif
>  	/* install event handler */
>  	yeeloong_install_sci_handler(EVENT_CAMERA, camera_set);
>  
> @@ -1039,9 +1040,10 @@ static void yeeloong_hotkey_exit(void)
>  	/* free irq */
>  	remove_irq(SCI_IRQ_NUM, &sci_irqaction);
>  
> +#ifdef CONFIG_SUSPEND
>  	/* uninstall the real yeeloong_report_lid_status for pm.c */
>  	yeeloong_report_lid_status = NULL;
> -
> +#endif
>  	/* uninstall event handler */
>  	yeeloong_uninstall_sci_handler(EVENT_CAMERA, camera_set);
>  
> @@ -1083,7 +1085,8 @@ static void get_fixed_battery_info(void)
>  
>  #define APM_CRITICAL		5
>  
> -static void yeeloong_apm_get_power_status(struct apm_power_info *info)
> +static void __maybe_unused yeeloong_apm_get_power_status(struct
> apm_power_info
> +		*info)
>   

   Your patch is line-wrapped.

WBR, Sergei

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform
@ 2009-11-19 21:42       ` Sergei Shtylyov
  0 siblings, 0 replies; 13+ messages in thread
From: Sergei Shtylyov @ 2009-11-19 21:42 UTC (permalink / raw)
  To: wuzhangjin
  Cc: Ralf Baechle, linux-mips, zhangfx, yanh, huhb, Richard Purdie,
	lm-sensors, Jamey Hicks

Hello.

Wu Zhangjin wrote:

> Sorry, This patch can not be compiled without CONFIG_PM, this patch is
> needed to fix it:
>
> We need to wrap the source code relative to CONFIG_PM, otherwise, it
> will fail in compiling.
>
> This patch is needed to "[PATCH 5/5] [loongson] yeeloong2f: add platform
> specific support".
>
> Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
> ---
>  arch/mips/loongson/lemote-2f/yeeloong_laptop.c |   12 ++++++++----
>  1 files changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
> b/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
> index ff74f7f..46c3847 100644
> --- a/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
> +++ b/arch/mips/loongson/lemote-2f/yeeloong_laptop.c
> @@ -1025,9 +1025,10 @@ static int yeeloong_hotkey_init(struct device
> *dev)
>  	/* update the current status of lid */
>  	yeeloong_lid_update_status(BIT_LID_DETECT_ON);
>  
> +#ifdef CONFIG_SUSPEND
>  	/* install the real yeeloong_report_lid_status for pm.c */
>  	yeeloong_report_lid_status = yeeloong_lid_update_status;
> -
> +#endif
>  	/* install event handler */
>  	yeeloong_install_sci_handler(EVENT_CAMERA, camera_set);
>  
> @@ -1039,9 +1040,10 @@ static void yeeloong_hotkey_exit(void)
>  	/* free irq */
>  	remove_irq(SCI_IRQ_NUM, &sci_irqaction);
>  
> +#ifdef CONFIG_SUSPEND
>  	/* uninstall the real yeeloong_report_lid_status for pm.c */
>  	yeeloong_report_lid_status = NULL;
> -
> +#endif
>  	/* uninstall event handler */
>  	yeeloong_uninstall_sci_handler(EVENT_CAMERA, camera_set);
>  
> @@ -1083,7 +1085,8 @@ static void get_fixed_battery_info(void)
>  
>  #define APM_CRITICAL		5
>  
> -static void yeeloong_apm_get_power_status(struct apm_power_info *info)
> +static void __maybe_unused yeeloong_apm_get_power_status(struct
> apm_power_info
> +		*info)
>   

   Your patch is line-wrapped.

WBR, Sergei



_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] [loongson] yeeloong2f: add platform specific support
  2009-11-19 21:42       ` [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform Sergei Shtylyov
@ 2009-11-20  1:08         ` Wu Zhangjin
  -1 siblings, 0 replies; 13+ messages in thread
From: Wu Zhangjin @ 2009-11-20  1:08 UTC (permalink / raw)
  To: Sergei Shtylyov
  Cc: Ralf Baechle, linux-mips, zhangfx, yanh, huhb, Richard Purdie,
	lm-sensors, Jamey Hicks

On Fri, 2009-11-20 at 00:42 +0300, Sergei Shtylyov wrote:
[...]
> >  
> > -static void yeeloong_apm_get_power_status(struct apm_power_info *info)
> > +static void __maybe_unused yeeloong_apm_get_power_status(struct
> > apm_power_info
> > +		*info)
> >   
> 
>    Your patch is line-wrapped.
> 
Yes, I found it, thanks! I have merged this one into "[PATCH v1]
[loongson] yeeloong2f: add platform specific support".

Best Regards,
	Wu Zhangjin

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform
@ 2009-11-20  1:08         ` Wu Zhangjin
  0 siblings, 0 replies; 13+ messages in thread
From: Wu Zhangjin @ 2009-11-20  1:08 UTC (permalink / raw)
  To: Sergei Shtylyov
  Cc: Ralf Baechle, linux-mips, zhangfx, yanh, huhb, Richard Purdie,
	lm-sensors, Jamey Hicks

On Fri, 2009-11-20 at 00:42 +0300, Sergei Shtylyov wrote:
[...]
> >  
> > -static void yeeloong_apm_get_power_status(struct apm_power_info *info)
> > +static void __maybe_unused yeeloong_apm_get_power_status(struct
> > apm_power_info
> > +		*info)
> >   
> 
>    Your patch is line-wrapped.
> 
Yes, I found it, thanks! I have merged this one into "[PATCH v1]
[loongson] yeeloong2f: add platform specific support".

Best Regards,
	Wu Zhangjin


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2009-11-20  1:09 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-11-19 17:32 [PATCH 0/5] Lemote-2f: Add Platform Specific Support Wu Zhangjin
2009-11-19 17:33 ` [PATCH 1/5] [loongson] LynLoong2F: " Wu Zhangjin
2009-11-19 17:33 ` [PATCH 2/5] [loongson] yeeloong2f: add basic ec operations Wu Zhangjin
2009-11-19 17:34 ` [PATCH 3/5] [loongson] yeeloong2f: add LID open event as the wakeup event Wu Zhangjin
2009-11-19 17:34 ` [PATCH 4/5] [loongson] yeeloong2f: cleanup the reset logic with ec_write function Wu Zhangjin
2009-11-19 17:39 ` [PATCH 5/5] [loongson] yeeloong2f: add platform specific support Wu Zhangjin
2009-11-19 17:39   ` [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform Wu Zhangjin
2009-11-19 18:02   ` [PATCH 5/5] [loongson] yeeloong2f: add platform specific support Wu Zhangjin
2009-11-19 18:02     ` [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform Wu Zhangjin
2009-11-19 21:42     ` [PATCH 5/5] [loongson] yeeloong2f: add platform specific support Sergei Shtylyov
2009-11-19 21:42       ` [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform Sergei Shtylyov
2009-11-20  1:08       ` [PATCH 5/5] [loongson] yeeloong2f: add platform specific support Wu Zhangjin
2009-11-20  1:08         ` [lm-sensors] [PATCH 5/5] [loongson] yeeloong2f: add platform Wu Zhangjin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.