Linux laptop Discussion Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH v7 4/8] Loongson: YeeLoong: add battery driver
  2009-12-04 13:34 ` [PATCH v7 4/8] Loongson: YeeLoong: add battery driver Wu Zhangjin
@ 2009-12-04  8:04   ` Pavel Machek
  2009-12-04 14:56     ` Wu Zhangjin
  2009-12-08  6:57   ` Wu Zhangjin
  1 sibling, 1 reply; 15+ messages in thread
From: Pavel Machek @ 2009-12-04  8:04 UTC (permalink / raw)
  To: Wu Zhangjin
  Cc: Ralf Baechle, akpm, linux-mips, linux-kernel, Dmitry Torokhov,
	Rafael J. Wysocki, zhangfx, Stephen Rothwell, linux-laptop

On Fri 2009-12-04 21:34:17, Wu Zhangjin wrote:
> From: Wu Zhangjin <wuzhangjin@gmail.com>
> 
> This patch adds APM emulated Battery Driver, it provides standard
> interface(/proc/apm) for user-space applications(e.g. kpowersave,
> gnome-power-manager) to manage the battery.

It would be nicer if this went to drivers/power, and used its
interface -- providing not only percentage but also other values.

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [PATCH v7 5/8] Loongson: YeeLoong: add hardware monitoring driver
  2009-12-04 13:35 ` [PATCH v7 5/8] Loongson: YeeLoong: add hardware monitoring driver Wu Zhangjin
@ 2009-12-04  8:08   ` Pavel Machek
  2009-12-04 16:29     ` Wu Zhangjin
  0 siblings, 1 reply; 15+ messages in thread
From: Pavel Machek @ 2009-12-04  8:08 UTC (permalink / raw)
  To: Wu Zhangjin
  Cc: Ralf Baechle, akpm, linux-mips, linux-kernel, Dmitry Torokhov,
	Rafael J. Wysocki, zhangfx, linux-laptop

Hi!

> +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;

wtf?

Maybe value should be 's8'?

> +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;

Another version of  pair-complement conversion; this one is broken --
off by 1.

> +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;
> +}

We have strict_strtoul for a reason...

								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html


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

* Re: [PATCH v7 6/8] Loongson: YeeLoong: add video output driver
  2009-12-04 13:36 ` [PATCH v7 6/8] Loongson: YeeLoong: add video output driver Wu Zhangjin
@ 2009-12-04  8:11   ` Pavel Machek
  2009-12-04 15:04     ` Wu Zhangjin
  2009-12-08  6:54   ` Wu Zhangjin
  1 sibling, 1 reply; 15+ messages in thread
From: Pavel Machek @ 2009-12-04  8:11 UTC (permalink / raw)
  To: Wu Zhangjin
  Cc: Ralf Baechle, akpm, linux-mips, linux-kernel, Dmitry Torokhov,
	Rafael J. Wysocki, zhangfx, linux-laptop, luming.yu

On Fri 2009-12-04 21:36:43, Wu Zhangjin wrote:
> From: Wu Zhangjin <wuzhangjin@gmail.com>
> 
> This patch adds Video Output Driver, it provides standard
> interface(/sys/class/video_output) to turn on/off the video output of
> LCD, CRT.
> 

> diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
> index 9c8385c..4a89c01 100644
> --- a/drivers/platform/mips/Kconfig
> +++ b/drivers/platform/mips/Kconfig
> @@ -21,6 +21,7 @@ config LEMOTE_YEELOONG2F
>  	select SYS_SUPPORTS_APM_EMULATION
>  	select APM_EMULATION
>  	select HWMON
> +	select VIDEO_OUTPUT_CONTROL
>  	default m
>  	help
>  	  YeeLoong netbook is a mini laptop made by Lemote, which is basically

default m is evil.

> +	if (status == BIT_DISPLAY_LCD_ON) {
> +		/* Turn on LCD */
> +		outb(0x31, 0x3c4);
> +		value = inb(0x3c5);
> +		value = (value & 0xf8) | 0x03;
> +		outb(0x31, 0x3c4);
> +		outb(value, 0x3c5);
> +		/* Turn on backlight */
> +		ec_write(REG_BACKLIGHT_CTRL, BIT_BACKLIGHT_ON);
> +	} else {
> +		/* Turn off backlight */
> +		ec_write(REG_BACKLIGHT_CTRL, BIT_BACKLIGHT_OFF);
> +		/* Turn off LCD */
> +		outb(0x31, 0x3c4);
> +		value = inb(0x3c5);
> +		value = (value & 0xf8) | 0x02;
> +		outb(0x31, 0x3c4);
> +		outb(value, 0x3c5);
> +	}

IIRC this is opencoded in suspend support; should that get common
helpers?

> +	if (status == BIT_CRT_DETECT_PLUG) {
> +		if (ec_read(REG_CRT_DETECT) == BIT_CRT_DETECT_PLUG) {
> +			/* Turn on CRT */
> +			outb(0x21, 0x3c4);
> +			value = inb(0x3c5);
> +			value &= ~(1 << 7);
> +			outb(0x21, 0x3c4);
> +			outb(value, 0x3c5);
> +		}
> +	} else {
> +		/* Turn off CRT */
> +		outb(0x21, 0x3c4);
> +		value = inb(0x3c5);
> +		value |= (1 << 7);
> +		outb(0x21, 0x3c4);
> +		outb(value, 0x3c5);
> +	}

This looks suspiciously similar to one another and to code
above. Perhaps some more helpers?

								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [PATCH v7 8/8] Loongson: YeeLoong: add input/hotkey driver
  2009-12-04 13:39 ` [PATCH v7 8/8] Loongson: YeeLoong: add input/hotkey driver Wu Zhangjin
@ 2009-12-04  8:14   ` Pavel Machek
  2009-12-04 14:57     ` Wu Zhangjin
  0 siblings, 1 reply; 15+ messages in thread
From: Pavel Machek @ 2009-12-04  8:14 UTC (permalink / raw)
  To: Wu Zhangjin
  Cc: Ralf Baechle, akpm, linux-mips, linux-kernel, Dmitry Torokhov,
	Rafael J. Wysocki, zhangfx, linux-laptop, linux-input

Hi!


> +#define NO_REG		0
> +#define NO_HANDLER	NULL

Don't obfuscate code like that.

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* [PATCH v7 4/8] Loongson: YeeLoong: add battery driver
       [not found] <cover.1259932036.git.wuzhangjin@gmail.com>
@ 2009-12-04 13:34 ` Wu Zhangjin
  2009-12-04  8:04   ` Pavel Machek
  2009-12-08  6:57   ` Wu Zhangjin
  2009-12-04 13:35 ` [PATCH v7 5/8] Loongson: YeeLoong: add hardware monitoring driver Wu Zhangjin
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 15+ messages in thread
From: Wu Zhangjin @ 2009-12-04 13:34 UTC (permalink / raw)
  To: Ralf Baechle, akpm, linux-mips, linux-kernel
  Cc: Dmitry Torokhov, Pavel Machek, Rafael J. Wysocki, zhangfx,
	Stephen Rothwell, linux-laptop, Wu Zhangjin

From: Wu Zhangjin <wuzhangjin@gmail.com>

This patch adds APM emulated Battery Driver, it provides standard
interface(/proc/apm) for user-space applications(e.g. kpowersave,
gnome-power-manager) to manage the battery.

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 drivers/platform/mips/Kconfig           |    2 +
 drivers/platform/mips/yeeloong_laptop.c |  104 +++++++++++++++++++++++++++++++
 2 files changed, 106 insertions(+), 0 deletions(-)

diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
index f7a3705..0c6b5ad 100644
--- a/drivers/platform/mips/Kconfig
+++ b/drivers/platform/mips/Kconfig
@@ -18,6 +18,8 @@ config LEMOTE_YEELOONG2F
 	tristate "Lemote YeeLoong Laptop"
 	depends on LEMOTE_MACH2F
 	select BACKLIGHT_CLASS_DEVICE
+	select SYS_SUPPORTS_APM_EMULATION
+	select APM_EMULATION
 	default m
 	help
 	  YeeLoong netbook is a mini laptop made by Lemote, which is basically
diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c
index fbc4ebb..729e368 100644
--- a/drivers/platform/mips/yeeloong_laptop.c
+++ b/drivers/platform/mips/yeeloong_laptop.c
@@ -13,6 +13,7 @@
 #include <linux/platform_device.h>
 #include <linux/backlight.h>	/* for backlight subdriver */
 #include <linux/fb.h>
+#include <linux/apm-emulation.h>/* for battery subdriver */
 
 #include <ec_kb3310b.h>
 
@@ -83,6 +84,106 @@ static void yeeloong_backlight_exit(void)
 	}
 }
 
+/* battery subdriver */
+
+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) {
+		pr_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 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 {
+			/* 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_battery_init(void)
+{
+	get_fixed_battery_info();
+
+	apm_get_power_status = get_power_status;
+
+	return 0;
+}
+
+static void yeeloong_battery_exit(void)
+{
+	if (apm_get_power_status == get_power_status)
+		apm_get_power_status = NULL;
+}
+
 static struct platform_device_id platform_device_ids[] = {
 	{
 		.name = "yeeloong_laptop",
@@ -120,11 +221,14 @@ static int __init yeeloong_init(void)
 		return ret;
 	}
 
+	yeeloong_battery_init();
+
 	return 0;
 }
 
 static void __exit yeeloong_exit(void)
 {
+	yeeloong_battery_exit();
 	yeeloong_backlight_exit();
 	platform_driver_unregister(&platform_driver);
 
-- 
1.6.2.1


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

* [PATCH v7 5/8] Loongson: YeeLoong: add hardware monitoring driver
       [not found] <cover.1259932036.git.wuzhangjin@gmail.com>
  2009-12-04 13:34 ` [PATCH v7 4/8] Loongson: YeeLoong: add battery driver Wu Zhangjin
@ 2009-12-04 13:35 ` Wu Zhangjin
  2009-12-04  8:08   ` Pavel Machek
  2009-12-04 13:36 ` [PATCH v7 6/8] Loongson: YeeLoong: add video output driver Wu Zhangjin
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 15+ messages in thread
From: Wu Zhangjin @ 2009-12-04 13:35 UTC (permalink / raw)
  To: Ralf Baechle, akpm, linux-mips, linux-kernel
  Cc: Dmitry Torokhov, Pavel Machek, Rafael J. Wysocki, zhangfx,
	linux-laptop, Wu Zhangjin

From: Wu Zhangjin <wuzhangjin@gmail.com>

This patch adds hardware monitoring driver, it provides standard
interface(/sys/class/hwmon/) for lm-sensors/sensors-applet to monitor
the temperatures of CPU and battery, the PWM of fan, the current,
voltage of battery.

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 drivers/platform/mips/Kconfig           |    1 +
 drivers/platform/mips/yeeloong_laptop.c |  221 +++++++++++++++++++++++++++++++
 2 files changed, 222 insertions(+), 0 deletions(-)

diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
index 0c6b5ad..9c8385c 100644
--- a/drivers/platform/mips/Kconfig
+++ b/drivers/platform/mips/Kconfig
@@ -20,6 +20,7 @@ config LEMOTE_YEELOONG2F
 	select BACKLIGHT_CLASS_DEVICE
 	select SYS_SUPPORTS_APM_EMULATION
 	select APM_EMULATION
+	select HWMON
 	default m
 	help
 	  YeeLoong netbook is a mini laptop made by Lemote, which is basically
diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c
index 729e368..644aaa7 100644
--- a/drivers/platform/mips/yeeloong_laptop.c
+++ b/drivers/platform/mips/yeeloong_laptop.c
@@ -14,6 +14,8 @@
 #include <linux/backlight.h>	/* for backlight subdriver */
 #include <linux/fb.h>
 #include <linux/apm-emulation.h>/* for battery subdriver */
+#include <linux/hwmon.h>	/* for hwmon subdriver */
+#include <linux/hwmon-sysfs.h>
 
 #include <ec_kb3310b.h>
 
@@ -184,6 +186,217 @@ static void yeeloong_battery_exit(void)
 		apm_get_power_status = NULL;
 }
 
+/* hwmon subdriver */
+
+/* pwm(auto/manual) enable or not */
+static int get_fan_pwm_enable(void)
+{
+	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)
+{
+	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);
+	}
+	ec_write(REG_FAN_SPEED_LEVEL, value);
+}
+
+static int get_fan_rpm(void)
+{
+	int value;
+
+	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_temp_alarm(void)
+{
+	int status;
+
+	status = (ec_read(REG_BAT_CHARGE_STATUS) &
+			BIT_BAT_CHARGE_STATUS_OVERTEMP);
+
+	return !!status;
+}
+
+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(temp2_max_alarm, S_IRUGO, get_battery_temp_alarm, 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_temp2_max_alarm.dev_attr.attr,
+	&sensor_dev_attr_curr1_input.dev_attr.attr,
+	&sensor_dev_attr_in1_input.dev_attr.attr,
+	&sensor_dev_attr_name.dev_attr.attr,
+	NULL
+};
+
+static struct attribute_group hwmon_attribute_group = {
+	.attrs = hwmon_attributes
+};
+
+static struct device *yeeloong_hwmon_dev;
+
+static int yeeloong_hwmon_init(void)
+{
+	int ret;
+
+	yeeloong_hwmon_dev = hwmon_device_register(NULL);
+	if (IS_ERR(yeeloong_hwmon_dev)) {
+		pr_err("Fail to 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) {
+		hwmon_device_unregister(yeeloong_hwmon_dev);
+		yeeloong_hwmon_dev = NULL;
+		return ret;
+	}
+	/* 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;
+	}
+}
+
 static struct platform_device_id platform_device_ids[] = {
 	{
 		.name = "yeeloong_laptop",
@@ -223,11 +436,19 @@ static int __init yeeloong_init(void)
 
 	yeeloong_battery_init();
 
+	ret = yeeloong_hwmon_init();
+	if (ret) {
+		pr_err("Fail to register yeeloong hwmon driver.\n");
+		yeeloong_hwmon_exit();
+		return ret;
+	}
+
 	return 0;
 }
 
 static void __exit yeeloong_exit(void)
 {
+	yeeloong_hwmon_exit();
 	yeeloong_battery_exit();
 	yeeloong_backlight_exit();
 	platform_driver_unregister(&platform_driver);
-- 
1.6.2.1

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

* [PATCH v7 6/8] Loongson: YeeLoong: add video output driver
       [not found] <cover.1259932036.git.wuzhangjin@gmail.com>
  2009-12-04 13:34 ` [PATCH v7 4/8] Loongson: YeeLoong: add battery driver Wu Zhangjin
  2009-12-04 13:35 ` [PATCH v7 5/8] Loongson: YeeLoong: add hardware monitoring driver Wu Zhangjin
@ 2009-12-04 13:36 ` Wu Zhangjin
  2009-12-04  8:11   ` Pavel Machek
  2009-12-08  6:54   ` Wu Zhangjin
  2009-12-04 13:37 ` [PATCH v7 7/8] Loongson: YeeLoong: add suspend support Wu Zhangjin
  2009-12-04 13:39 ` [PATCH v7 8/8] Loongson: YeeLoong: add input/hotkey driver Wu Zhangjin
  4 siblings, 2 replies; 15+ messages in thread
From: Wu Zhangjin @ 2009-12-04 13:36 UTC (permalink / raw)
  To: Ralf Baechle, akpm, linux-mips, linux-kernel
  Cc: Dmitry Torokhov, Pavel Machek, Rafael J. Wysocki, zhangfx,
	linux-laptop, luming.yu, Wu Zhangjin

From: Wu Zhangjin <wuzhangjin@gmail.com>

This patch adds Video Output Driver, it provides standard
interface(/sys/class/video_output) to turn on/off the video output of
LCD, CRT.

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 drivers/platform/mips/Kconfig           |    1 +
 drivers/platform/mips/yeeloong_laptop.c |  147 +++++++++++++++++++++++++++++++
 2 files changed, 148 insertions(+), 0 deletions(-)

diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
index 9c8385c..4a89c01 100644
--- a/drivers/platform/mips/Kconfig
+++ b/drivers/platform/mips/Kconfig
@@ -21,6 +21,7 @@ config LEMOTE_YEELOONG2F
 	select SYS_SUPPORTS_APM_EMULATION
 	select APM_EMULATION
 	select HWMON
+	select VIDEO_OUTPUT_CONTROL
 	default m
 	help
 	  YeeLoong netbook is a mini laptop made by Lemote, which is basically
diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c
index 644aaa7..8378926 100644
--- a/drivers/platform/mips/yeeloong_laptop.c
+++ b/drivers/platform/mips/yeeloong_laptop.c
@@ -16,6 +16,7 @@
 #include <linux/apm-emulation.h>/* for battery subdriver */
 #include <linux/hwmon.h>	/* for hwmon subdriver */
 #include <linux/hwmon-sysfs.h>
+#include <linux/video_output.h>	/* for video output subdriver */
 
 #include <ec_kb3310b.h>
 
@@ -397,6 +398,144 @@ static void yeeloong_hwmon_exit(void)
 	}
 }
 
+/* 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)
+{
+	int value;
+	unsigned long status;
+
+	status = !!od->request_state;
+
+	if (status == BIT_DISPLAY_LCD_ON) {
+		/* Turn on LCD */
+		outb(0x31, 0x3c4);
+		value = inb(0x3c5);
+		value = (value & 0xf8) | 0x03;
+		outb(0x31, 0x3c4);
+		outb(value, 0x3c5);
+		/* Turn on backlight */
+		ec_write(REG_BACKLIGHT_CTRL, BIT_BACKLIGHT_ON);
+	} else {
+		/* Turn off backlight */
+		ec_write(REG_BACKLIGHT_CTRL, BIT_BACKLIGHT_OFF);
+		/* Turn off 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)
+{
+	int value;
+	unsigned long status;
+
+	status = !!od->request_state;
+
+	if (status == BIT_CRT_DETECT_PLUG) {
+		if (ec_read(REG_CRT_DETECT) == BIT_CRT_DETECT_PLUG) {
+			/* Turn on CRT */
+			outb(0x21, 0x3c4);
+			value = inb(0x3c5);
+			value &= ~(1 << 7);
+			outb(0x21, 0x3c4);
+			outb(value, 0x3c5);
+		}
+	} else {
+		/* Turn off 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,
+};
+
+static struct output_device *lcd_output_dev, *crt_output_dev;
+
+static void yeeloong_lcd_vo_set(int status)
+{
+	lcd_output_dev->request_state = status;
+	lcd_video_output_set(lcd_output_dev);
+}
+
+static void yeeloong_crt_vo_set(int status)
+{
+	crt_output_dev->request_state = status;
+	crt_video_output_set(crt_output_dev);
+}
+
+static int yeeloong_vo_init(void)
+{
+	int ret;
+
+	/* Register video output device: lcd, crt */
+	lcd_output_dev = video_output_register("LCD", NULL, NULL,
+			&lcd_output_properties);
+
+	if (IS_ERR(lcd_output_dev)) {
+		ret = PTR_ERR(lcd_output_dev);
+		lcd_output_dev = NULL;
+		return ret;
+	}
+	/* Ensure LCD is on by default */
+	yeeloong_lcd_vo_set(BIT_DISPLAY_LCD_ON);
+
+	crt_output_dev = video_output_register("CRT", NULL, NULL,
+			&crt_output_properties);
+
+	if (IS_ERR(crt_output_dev)) {
+		ret = PTR_ERR(crt_output_dev);
+		crt_output_dev = NULL;
+		return ret;
+	}
+
+	/* Turn off CRT by default, and will be enabled when the CRT
+	 * connectting event reported by SCI */
+	yeeloong_crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+
+	return 0;
+}
+
+static void yeeloong_vo_exit(void)
+{
+	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;
+	}
+}
+
 static struct platform_device_id platform_device_ids[] = {
 	{
 		.name = "yeeloong_laptop",
@@ -443,11 +582,19 @@ static int __init yeeloong_init(void)
 		return ret;
 	}
 
+	ret = yeeloong_vo_init();
+	if (ret) {
+		pr_err("Fail to register yeeloong video output driver.\n");
+		yeeloong_vo_exit();
+		return ret;
+	}
+
 	return 0;
 }
 
 static void __exit yeeloong_exit(void)
 {
+	yeeloong_vo_exit();
 	yeeloong_hwmon_exit();
 	yeeloong_battery_exit();
 	yeeloong_backlight_exit();
-- 
1.6.2.1


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

* [PATCH v7 7/8] Loongson: YeeLoong: add suspend support
       [not found] <cover.1259932036.git.wuzhangjin@gmail.com>
                   ` (2 preceding siblings ...)
  2009-12-04 13:36 ` [PATCH v7 6/8] Loongson: YeeLoong: add video output driver Wu Zhangjin
@ 2009-12-04 13:37 ` Wu Zhangjin
  2009-12-04 13:39 ` [PATCH v7 8/8] Loongson: YeeLoong: add input/hotkey driver Wu Zhangjin
  4 siblings, 0 replies; 15+ messages in thread
From: Wu Zhangjin @ 2009-12-04 13:37 UTC (permalink / raw)
  To: Ralf Baechle, akpm, linux-mips, linux-kernel
  Cc: Dmitry Torokhov, Pavel Machek, Rafael J. Wysocki, zhangfx,
	linux-laptop, Wu Zhangjin

From: Wu Zhangjin <wuzhangjin@gmail.com>

This patch add support to suspend the yeeloong platform specific
devices(LCD, CRT, USB...).

Acked-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 drivers/platform/mips/yeeloong_laptop.c |   41 +++++++++++++++++++++++++++++++
 1 files changed, 41 insertions(+), 0 deletions(-)

diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c
index 8378926..d31824b 100644
--- a/drivers/platform/mips/yeeloong_laptop.c
+++ b/drivers/platform/mips/yeeloong_laptop.c
@@ -536,6 +536,45 @@ static void yeeloong_vo_exit(void)
 	}
 }
 
+#ifdef CONFIG_LOONGSON_SUSPEND
+static void usb_ports_set(int status)
+{
+	status = !!status;
+
+	ec_write(REG_USB0_FLAG, status);
+	ec_write(REG_USB1_FLAG, status);
+	ec_write(REG_USB2_FLAG, status);
+}
+
+static int yeeloong_suspend(struct platform_device *dev, pm_message_t state)
+
+{
+	/* Turn off LCD */
+	yeeloong_lcd_vo_set(BIT_DISPLAY_LCD_OFF);
+	/* Turn off CRT */
+	yeeloong_crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+	/* Poweroff three usb ports */
+	usb_ports_set(BIT_USB_FLAG_OFF);
+
+	return 0;
+}
+
+static int yeeloong_resume(struct platform_device *pdev)
+{
+	/* Resume the status of lcd & crt */
+	yeeloong_lcd_vo_set(BIT_DISPLAY_LCD_ON);
+	yeeloong_crt_vo_set(BIT_CRT_DETECT_PLUG);
+
+	/* Poweron three usb ports */
+	usb_ports_set(BIT_USB_FLAG_ON);
+
+	return 0;
+}
+#else	/* !CONFIG_LOONGSON_SUSPEND */
+#define yeeloong_suspend NULL
+#define yeeloong_resume NULL
+#endif
+
 static struct platform_device_id platform_device_ids[] = {
 	{
 		.name = "yeeloong_laptop",
@@ -551,6 +590,8 @@ static struct platform_driver platform_driver = {
 		   .owner = THIS_MODULE,
 		   },
 	.id_table = platform_device_ids,
+	.suspend = yeeloong_suspend,
+	.resume = yeeloong_resume,
 };
 
 static int __init yeeloong_init(void)
-- 
1.6.2.1


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

* [PATCH v7 8/8] Loongson: YeeLoong: add input/hotkey driver
       [not found] <cover.1259932036.git.wuzhangjin@gmail.com>
                   ` (3 preceding siblings ...)
  2009-12-04 13:37 ` [PATCH v7 7/8] Loongson: YeeLoong: add suspend support Wu Zhangjin
@ 2009-12-04 13:39 ` Wu Zhangjin
  2009-12-04  8:14   ` Pavel Machek
  4 siblings, 1 reply; 15+ messages in thread
From: Wu Zhangjin @ 2009-12-04 13:39 UTC (permalink / raw)
  To: Ralf Baechle, akpm, linux-mips, linux-kernel
  Cc: Dmitry Torokhov, Pavel Machek, Rafael J. Wysocki, zhangfx,
	linux-laptop, linux-input, Wu Zhangjin

From: Wu Zhangjin <wuzhangjin@gmail.com>

This patch adds Hotkey Driver, which will do relative actions for The
hotkey event(/sys/class/input) and report the corresponding input keys
to the user-space applications.

Changes from the old revision:
	- Rebase on the sparse keymap library from Dmitry Torokhov.

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
 arch/mips/include/asm/mach-loongson/loongson.h |    6 +
 arch/mips/loongson/common/cmdline.c            |    8 +
 drivers/platform/mips/Kconfig                  |    1 +
 drivers/platform/mips/yeeloong_laptop.c        |  415 ++++++++++++++++++++++++
 4 files changed, 430 insertions(+), 0 deletions(-)

diff --git a/arch/mips/include/asm/mach-loongson/loongson.h b/arch/mips/include/asm/mach-loongson/loongson.h
index ee8bc83..13e208e 100644
--- a/arch/mips/include/asm/mach-loongson/loongson.h
+++ b/arch/mips/include/asm/mach-loongson/loongson.h
@@ -43,6 +43,12 @@ static inline void prom_init_uart_base(void)
 #endif
 }
 
+/*
+ * Copy kernel command line from arcs_cmdline
+ */
+#include <asm/setup.h>
+extern char loongson_cmdline[COMMAND_LINE_SIZE];
+
 /* irq operation functions */
 extern void bonito_irqdispatch(void);
 extern void __init bonito_irq_init(void);
diff --git a/arch/mips/loongson/common/cmdline.c b/arch/mips/loongson/common/cmdline.c
index 7ad47f2..617faee 100644
--- a/arch/mips/loongson/common/cmdline.c
+++ b/arch/mips/loongson/common/cmdline.c
@@ -17,6 +17,7 @@
  * Free Software Foundation;  either version 2 of the  License, or (at your
  * option) any later version.
  */
+#include <linux/module.h>
 #include <asm/bootinfo.h>
 
 #include <loongson.h>
@@ -25,6 +26,10 @@ int prom_argc;
 /* pmon passes arguments in 32bit pointers */
 int *_prom_argv;
 
+/* the kernel command line copied from arcs_cmdline */
+char loongson_cmdline[COMMAND_LINE_SIZE];
+EXPORT_SYMBOL(loongson_cmdline);
+
 void __init prom_init_cmdline(void)
 {
 	int i;
@@ -51,4 +56,7 @@ void __init prom_init_cmdline(void)
 		strcat(arcs_cmdline, " root=/dev/hda1");
 
 	prom_init_machtype();
+
+	/* copy arcs_cmdline into loongson_cmdline */
+	strncpy(loongson_cmdline, arcs_cmdline, COMMAND_LINE_SIZE);
 }
diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
index 4a89c01..7c81c4c 100644
--- a/drivers/platform/mips/Kconfig
+++ b/drivers/platform/mips/Kconfig
@@ -22,6 +22,7 @@ config LEMOTE_YEELOONG2F
 	select APM_EMULATION
 	select HWMON
 	select VIDEO_OUTPUT_CONTROL
+	select INPUT_SPARSEKMAP
 	default m
 	help
 	  YeeLoong netbook is a mini laptop made by Lemote, which is basically
diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c
index d31824b..d8cd94b 100644
--- a/drivers/platform/mips/yeeloong_laptop.c
+++ b/drivers/platform/mips/yeeloong_laptop.c
@@ -17,7 +17,14 @@
 #include <linux/hwmon.h>	/* for hwmon subdriver */
 #include <linux/hwmon-sysfs.h>
 #include <linux/video_output.h>	/* for video output subdriver */
+#include <linux/input.h>	/* for hotkey subdriver */
+#include <linux/input/sparse-keymap.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
 
+#include <cs5536/cs5536.h>
+
+#include <loongson.h>		/* for loongson_cmdline */
 #include <ec_kb3310b.h>
 
 /* backlight subdriver */
@@ -536,6 +543,406 @@ static void yeeloong_vo_exit(void)
 	}
 }
 
+/* hotkey subdriver */
+
+static struct input_dev *yeeloong_hotkey_dev;
+
+static const struct key_entry yeeloong_keymap[] = {
+	{KE_SW, EVENT_LID, { SW_LID } },
+	{KE_KEY, EVENT_CAMERA, { KEY_CAMERA } }, /* Fn + ESC */
+	{KE_KEY, EVENT_SLEEP, { KEY_SLEEP } }, /* Fn + F1 */
+	{KE_KEY, EVENT_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 struct key_entry *get_event_key_entry(int event, int status)
+{
+	struct key_entry *ke;
+	static int old_brightness_status = -1;
+	static int old_volume_status = -1;
+
+	ke = sparse_keymap_entry_from_scancode(yeeloong_hotkey_dev, event);
+	if (!ke)
+		return NULL;
+
+	switch (event) {
+	case EVENT_DISPLAY_BRIGHTNESS:
+		/* current status > old one, means up */
+		if ((status < old_brightness_status) || (0 == status))
+			ke++;
+		old_brightness_status = status;
+		break;
+	case EVENT_AUDIO_VOLUME:
+		if ((status < old_volume_status) || (0 == status))
+			ke++;
+		old_volume_status = status;
+		break;
+	default:
+		break;
+	}
+
+	return ke;
+}
+
+static int report_lid_switch(int status)
+{
+	input_report_switch(yeeloong_hotkey_dev, SW_LID, !status);
+	input_sync(yeeloong_hotkey_dev);
+
+	return status;
+}
+
+static int crt_detect_handler(int status)
+{
+	if (status == BIT_CRT_DETECT_PLUG) {
+		yeeloong_crt_vo_set(BIT_CRT_DETECT_PLUG);
+		yeeloong_lcd_vo_set(BIT_DISPLAY_LCD_OFF);
+	} else {
+		yeeloong_lcd_vo_set(BIT_DISPLAY_LCD_ON);
+		yeeloong_crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+	}
+	return status;
+}
+
+#define EC_VER_LEN 64
+
+static int black_screen_handler(int status)
+{
+	char *p, ec_ver[EC_VER_LEN];
+
+	p = strstr(loongson_cmdline, "EC_VER=");
+	if (!p)
+		memset(ec_ver, 0, EC_VER_LEN);
+	else {
+		strncpy(ec_ver, p, EC_VER_LEN);
+		p = strstr(ec_ver, " ");
+		if (p)
+			*p = '\0';
+	}
+
+	/* Seems EC(>=PQ1D26) does this job for us, we can not do it again,
+	 * otherwise, the brightness will not resume to the normal level! */
+	if (strncasecmp(ec_ver, "EC_VER=PQ1D26", 64) < 0)
+		yeeloong_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:
+		yeeloong_lcd_vo_set(BIT_DISPLAY_LCD_ON);
+		yeeloong_crt_vo_set(BIT_CRT_DETECT_PLUG);
+		break;
+	case 2:
+		yeeloong_lcd_vo_set(BIT_DISPLAY_LCD_OFF);
+		yeeloong_crt_vo_set(BIT_CRT_DETECT_PLUG);
+		break;
+	case 3:
+		yeeloong_lcd_vo_set(BIT_DISPLAY_LCD_OFF);
+		yeeloong_crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+		break;
+	case 4:
+		yeeloong_lcd_vo_set(BIT_DISPLAY_LCD_ON);
+		yeeloong_crt_vo_set(BIT_CRT_DETECT_UNPLUG);
+		break;
+	default:
+		/* Ensure LCD is on */
+		yeeloong_lcd_vo_set(BIT_DISPLAY_LCD_ON);
+		break;
+	}
+	return video_output_status;
+}
+
+static int camera_handler(int status)
+{
+	int value;
+
+	value = ec_read(REG_CAMERA_CONTROL);
+	ec_write(REG_CAMERA_CONTROL, value | (1 << 1));
+
+	return status;
+}
+
+static int usb2_handler(int status)
+{
+	pr_emerg("USB2 Over Current occurred\n");
+
+	return status;
+}
+
+static int usb0_handler(int status)
+{
+	pr_emerg("USB0 Over Current occurred\n");
+
+	return status;
+}
+
+/* yeeloong_wifi_handler may be implemented in the wifi driver */
+sci_handler yeeloong_wifi_handler;
+EXPORT_SYMBOL(yeeloong_wifi_handler);
+
+#define NO_REG		0
+#define NO_HANDLER	NULL
+/* 2 maybe used to indicate the key as a switch button, such as EVENT_WLAN */
+#define NO_STATUS	2
+
+static void do_event_action(int event)
+{
+	sci_handler handler;
+	int reg, status;
+	struct key_entry *ke;
+
+	reg = NO_REG;
+	handler = NO_HANDLER;
+	status = NO_STATUS;
+
+	switch (event) {
+	case EVENT_LID:
+		reg = REG_LID_DETECT;
+		break;
+	case EVENT_DISPLAY_TOGGLE:
+		handler = display_toggle_handler;
+		break;
+	case EVENT_CRT_DETECT:
+		reg = REG_CRT_DETECT;
+		handler = crt_detect_handler;
+		break;
+	case EVENT_CAMERA:
+		reg = REG_CAMERA_STATUS;
+		handler = camera_handler;
+		break;
+	case EVENT_USB_OC2:
+		reg = REG_USB2_FLAG;
+		handler = usb2_handler;
+		break;
+	case EVENT_USB_OC0:
+		reg = REG_USB0_FLAG;
+		handler = usb0_handler;
+		break;
+	case EVENT_BLACK_SCREEN:
+		reg = REG_DISPLAY_LCD;
+		handler = black_screen_handler;
+		break;
+	case EVENT_AUDIO_MUTE:
+		reg = REG_AUDIO_MUTE;
+		break;
+	case EVENT_DISPLAY_BRIGHTNESS:
+		reg = REG_DISPLAY_BRIGHTNESS;
+		break;
+	case EVENT_AUDIO_VOLUME:
+		reg = REG_AUDIO_VOLUME;
+		break;
+	case EVENT_WLAN:
+		handler = yeeloong_wifi_handler;
+		break;
+	default:
+		break;
+	}
+
+	if (reg != NO_REG)
+		status = ec_read(reg);
+
+	if (handler != NO_HANDLER)
+		status = handler(status);
+
+	pr_info("%s: event: %d status: %d\n", __func__, event, status);
+
+	/* Report current key to user-space */
+	ke = get_event_key_entry(event, status);
+	if (ke) {
+		if (ke->keycode == SW_LID)
+			report_lid_switch(status);
+		else
+			sparse_keymap_report_entry(yeeloong_hotkey_dev, ke, 1,
+					true);
+	}
+}
+
+/*
+ * SCI(system control interrupt) main interrupt routine
+ *
+ * We will do the query and get event number together so the interrupt routine
+ * should be longer than 120us now at least 3ms elpase for it.
+ */
+static irqreturn_t sci_irq_handler(int irq, void *dev_id)
+{
+	int ret, event;
+
+	if (SCI_IRQ_NUM != irq)
+		return IRQ_NONE;
+
+	/* Query the event number */
+	ret = ec_query_event_num();
+	if (ret < 0)
+		return IRQ_NONE;
+
+	event = ec_get_event_num();
+	if (event < EVENT_START || event > EVENT_END)
+		return IRQ_NONE;
+
+	/* Execute corresponding actions */
+	do_event_action(event);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Config and init some msr and gpio register properly.
+ */
+static int sci_irq_init(void)
+{
+	u32 hi, lo;
+	u32 gpio_base;
+	unsigned long flags;
+	int ret;
+
+	/* Get gpio base */
+	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &lo);
+	gpio_base = lo & 0xff00;
+
+	/* Filter the former kb3310 interrupt for security */
+	ret = ec_query_event_num();
+	if (ret)
+		return ret;
+
+	/* For filtering next number interrupt */
+	udelay(10000);
+
+	/* Set gpio native registers and msrs for GPIO27 SCI EVENT PIN
+	 * gpio :
+	 *      input, pull-up, no-invert, event-count and value 0,
+	 *      no-filter, no edge mode
+	 *      gpio27 map to Virtual gpio0
+	 * msr :
+	 *      no primary and lpc
+	 *      Unrestricted Z input to IG10 from Virtual gpio 0.
+	 */
+	local_irq_save(flags);
+	_rdmsr(0x80000024, &hi, &lo);
+	lo &= ~(1 << 10);
+	_wrmsr(0x80000024, hi, lo);
+	_rdmsr(0x80000025, &hi, &lo);
+	lo &= ~(1 << 10);
+	_wrmsr(0x80000025, hi, lo);
+	_rdmsr(0x80000023, &hi, &lo);
+	lo |= (0x0a << 0);
+	_wrmsr(0x80000023, hi, lo);
+	local_irq_restore(flags);
+
+	/* Set gpio27 as sci interrupt
+	 *
+	 * input, pull-up, no-fliter, no-negedge, invert
+	 * the sci event is just about 120us
+	 */
+	asm(".set noreorder\n");
+	/*  input enable */
+	outl(0x00000800, (gpio_base | 0xA0));
+	/*  revert the input */
+	outl(0x00000800, (gpio_base | 0xA4));
+	/*  event-int enable */
+	outl(0x00000800, (gpio_base | 0xB8));
+	asm(".set reorder\n");
+
+	return 0;
+}
+
+static struct irqaction sci_irqaction = {
+	.handler = sci_irq_handler,
+	.name = "sci",
+	.flags = IRQF_SHARED,
+};
+
+static int yeeloong_hotkey_init(void)
+{
+	int ret;
+
+	ret = sci_irq_init();
+	if (ret)
+		return -EFAULT;
+
+	ret = setup_irq(SCI_IRQ_NUM, &sci_irqaction);
+	if (ret)
+		return -EFAULT;
+
+	yeeloong_hotkey_dev = input_allocate_device();
+
+	if (!yeeloong_hotkey_dev) {
+		remove_irq(SCI_IRQ_NUM, &sci_irqaction);
+		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 = NULL;
+
+	ret = sparse_keymap_setup(yeeloong_hotkey_dev, yeeloong_keymap, NULL);
+	if (ret) {
+		pr_err("Fail to setup input device keymap\n");
+		input_free_device(yeeloong_hotkey_dev);
+		return ret;
+	}
+
+	ret = input_register_device(yeeloong_hotkey_dev);
+	if (ret) {
+		sparse_keymap_free(yeeloong_hotkey_dev);
+		input_free_device(yeeloong_hotkey_dev);
+		return ret;
+	}
+
+	/* Update the current status of LID */
+	report_lid_switch(BIT_LID_DETECT_ON);
+
+#ifdef CONFIG_LOONGSON_SUSPEND
+	/* Install the real yeeloong_report_lid_status for pm.c */
+	yeeloong_report_lid_status = report_lid_switch;
+#endif
+
+	return 0;
+}
+
+static void yeeloong_hotkey_exit(void)
+{
+	/* Free irq */
+	remove_irq(SCI_IRQ_NUM, &sci_irqaction);
+
+#ifdef CONFIG_LOONGSON_SUSPEND
+	/* Uninstall yeeloong_report_lid_status for pm.c */
+	if (yeeloong_report_lid_status == report_lid_switch)
+		yeeloong_report_lid_status = NULL;
+#endif
+
+	if (yeeloong_hotkey_dev) {
+		sparse_keymap_free(yeeloong_hotkey_dev);
+		input_unregister_device(yeeloong_hotkey_dev);
+		yeeloong_hotkey_dev = NULL;
+	}
+}
+
 #ifdef CONFIG_LOONGSON_SUSPEND
 static void usb_ports_set(int status)
 {
@@ -630,11 +1037,19 @@ static int __init yeeloong_init(void)
 		return ret;
 	}
 
+	ret = yeeloong_hotkey_init();
+	if (ret) {
+		pr_err("Fail to register yeeloong hotkey driver.\n");
+		yeeloong_hotkey_exit();
+		return ret;
+	}
+
 	return 0;
 }
 
 static void __exit yeeloong_exit(void)
 {
+	yeeloong_hotkey_exit();
 	yeeloong_vo_exit();
 	yeeloong_hwmon_exit();
 	yeeloong_battery_exit();
-- 
1.6.2.1


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

* Re: [PATCH v7 4/8] Loongson: YeeLoong: add battery driver
  2009-12-04  8:04   ` Pavel Machek
@ 2009-12-04 14:56     ` Wu Zhangjin
  0 siblings, 0 replies; 15+ messages in thread
From: Wu Zhangjin @ 2009-12-04 14:56 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Ralf Baechle, akpm, linux-mips, linux-kernel, Dmitry Torokhov,
	Rafael J. Wysocki, zhangfx, Stephen Rothwell, linux-laptop,
	Hongbing Hu

On Fri, 2009-12-04 at 09:04 +0100, Pavel Machek wrote:
> On Fri 2009-12-04 21:34:17, Wu Zhangjin wrote:
> > From: Wu Zhangjin <wuzhangjin@gmail.com>
> > 
> > This patch adds APM emulated Battery Driver, it provides standard
> > interface(/proc/apm) for user-space applications(e.g. kpowersave,
> > gnome-power-manager) to manage the battery.
> 
> It would be nicer if this went to drivers/power, and used its
> interface -- providing not only percentage but also other values.

There is a version basic on the power supply interface, but it is buggy.

Regards,
	Wu Zhangjin



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

* Re: [PATCH v7 8/8] Loongson: YeeLoong: add input/hotkey driver
  2009-12-04  8:14   ` Pavel Machek
@ 2009-12-04 14:57     ` Wu Zhangjin
  0 siblings, 0 replies; 15+ messages in thread
From: Wu Zhangjin @ 2009-12-04 14:57 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Ralf Baechle, akpm, linux-mips, linux-kernel, Dmitry Torokhov,
	Rafael J. Wysocki, zhangfx, linux-laptop, linux-input

On Fri, 2009-12-04 at 09:14 +0100, Pavel Machek wrote:
> Hi!
> 
> 
> > +#define NO_REG		0
> > +#define NO_HANDLER	NULL
> 
> Don't obfuscate code like that.

Will remove it later, thanks!



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

* Re: [PATCH v7 6/8] Loongson: YeeLoong: add video output driver
  2009-12-04  8:11   ` Pavel Machek
@ 2009-12-04 15:04     ` Wu Zhangjin
  0 siblings, 0 replies; 15+ messages in thread
From: Wu Zhangjin @ 2009-12-04 15:04 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Ralf Baechle, akpm, linux-mips, linux-kernel, Dmitry Torokhov,
	Rafael J. Wysocki, zhangfx, linux-laptop, luming.yu

On Fri, 2009-12-04 at 09:11 +0100, Pavel Machek wrote:
> On Fri 2009-12-04 21:36:43, Wu Zhangjin wrote:
> > From: Wu Zhangjin <wuzhangjin@gmail.com>
> > 
> > This patch adds Video Output Driver, it provides standard
> > interface(/sys/class/video_output) to turn on/off the video output of
> > LCD, CRT.
> > 
> 
> > diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
> > index 9c8385c..4a89c01 100644
> > --- a/drivers/platform/mips/Kconfig
> > +++ b/drivers/platform/mips/Kconfig
> > @@ -21,6 +21,7 @@ config LEMOTE_YEELOONG2F
> >  	select SYS_SUPPORTS_APM_EMULATION
> >  	select APM_EMULATION
> >  	select HWMON
> > +	select VIDEO_OUTPUT_CONTROL
> >  	default m
> >  	help
> >  	  YeeLoong netbook is a mini laptop made by Lemote, which is basically
> 
> default m is evil.
> 

why? this module can be loaded automatically, so, I let it be a module
by default.

> > +	if (status == BIT_DISPLAY_LCD_ON) {
> > +		/* Turn on LCD */
> > +		outb(0x31, 0x3c4);
> > +		value = inb(0x3c5);
> > +		value = (value & 0xf8) | 0x03;
> > +		outb(0x31, 0x3c4);
> > +		outb(value, 0x3c5);
> > +		/* Turn on backlight */
> > +		ec_write(REG_BACKLIGHT_CTRL, BIT_BACKLIGHT_ON);
> > +	} else {
> > +		/* Turn off backlight */
> > +		ec_write(REG_BACKLIGHT_CTRL, BIT_BACKLIGHT_OFF);
> > +		/* Turn off LCD */
> > +		outb(0x31, 0x3c4);
> > +		value = inb(0x3c5);
> > +		value = (value & 0xf8) | 0x02;
> > +		outb(0x31, 0x3c4);
> > +		outb(value, 0x3c5);
> > +	}
> 
> IIRC this is opencoded in suspend support; should that get common
> helpers?
> 
> > +	if (status == BIT_CRT_DETECT_PLUG) {
> > +		if (ec_read(REG_CRT_DETECT) == BIT_CRT_DETECT_PLUG) {
> > +			/* Turn on CRT */
> > +			outb(0x21, 0x3c4);
> > +			value = inb(0x3c5);
> > +			value &= ~(1 << 7);
> > +			outb(0x21, 0x3c4);
> > +			outb(value, 0x3c5);
> > +		}
> > +	} else {
> > +		/* Turn off CRT */
> > +		outb(0x21, 0x3c4);
> > +		value = inb(0x3c5);
> > +		value |= (1 << 7);
> > +		outb(0x21, 0x3c4);
> > +		outb(value, 0x3c5);
> > +	}
> 
> This looks suspiciously similar to one another and to code
> above. Perhaps some more helpers?
> 

Yes, lots of duplicated source code above, thanks!

Regards,
	Wu Zhangjin


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

* Re: [PATCH v7 5/8] Loongson: YeeLoong: add hardware monitoring driver
  2009-12-04  8:08   ` Pavel Machek
@ 2009-12-04 16:29     ` Wu Zhangjin
  0 siblings, 0 replies; 15+ messages in thread
From: Wu Zhangjin @ 2009-12-04 16:29 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Ralf Baechle, akpm, linux-mips, linux-kernel, Dmitry Torokhov,
	Rafael J. Wysocki, zhangfx, linux-laptop

On Fri, 2009-12-04 at 09:08 +0100, Pavel Machek wrote:
> Hi!
> 
> > +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;
> 
> wtf?
> 
> Maybe value should be 's8'?
> 
> > +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;
> 
> Another version of  pair-complement conversion; this one is broken --
> off by 1.
> 
> > +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;
> > +}
> 
> We have strict_strtoul for a reason...
> 

Done, thanks!

Regards,
	Wu Zhangjin

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

* Re: [PATCH v7 6/8] Loongson: YeeLoong: add video output driver
  2009-12-04 13:36 ` [PATCH v7 6/8] Loongson: YeeLoong: add video output driver Wu Zhangjin
  2009-12-04  8:11   ` Pavel Machek
@ 2009-12-08  6:54   ` Wu Zhangjin
  1 sibling, 0 replies; 15+ messages in thread
From: Wu Zhangjin @ 2009-12-08  6:54 UTC (permalink / raw)
  To: Ralf Baechle
  Cc: akpm, linux-mips, linux-kernel, Dmitry Torokhov, Pavel Machek,
	Rafael J. Wysocki, zhangfx, linux-laptop, luming.yu

Hi, Pavel

Can I get your Acked-by: for this patch?

Thanks!
	Wu Zhangjin

On Fri, 2009-12-04 at 21:36 +0800, Wu Zhangjin wrote:
> From: Wu Zhangjin <wuzhangjin@gmail.com>
> 
> This patch adds Video Output Driver, it provides standard
> interface(/sys/class/video_output) to turn on/off the video output of
> LCD, CRT.
> 
> Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
> ---
>  drivers/platform/mips/Kconfig           |    1 +
>  drivers/platform/mips/yeeloong_laptop.c |  147 +++++++++++++++++++++++++++++++
>  2 files changed, 148 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
> index 9c8385c..4a89c01 100644
> --- a/drivers/platform/mips/Kconfig
> +++ b/drivers/platform/mips/Kconfig
> @@ -21,6 +21,7 @@ config LEMOTE_YEELOONG2F
>  	select SYS_SUPPORTS_APM_EMULATION
>  	select APM_EMULATION
>  	select HWMON
> +	select VIDEO_OUTPUT_CONTROL
>  	default m
>  	help
>  	  YeeLoong netbook is a mini laptop made by Lemote, which is basically
> diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c
> index 644aaa7..8378926 100644
> --- a/drivers/platform/mips/yeeloong_laptop.c
> +++ b/drivers/platform/mips/yeeloong_laptop.c
> @@ -16,6 +16,7 @@
>  #include <linux/apm-emulation.h>/* for battery subdriver */
>  #include <linux/hwmon.h>	/* for hwmon subdriver */
>  #include <linux/hwmon-sysfs.h>
> +#include <linux/video_output.h>	/* for video output subdriver */
>  
>  #include <ec_kb3310b.h>
>  
> @@ -397,6 +398,144 @@ static void yeeloong_hwmon_exit(void)
>  	}
>  }
>  
> +/* 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)
> +{
> +	int value;
> +	unsigned long status;
> +
> +	status = !!od->request_state;
> +
> +	if (status == BIT_DISPLAY_LCD_ON) {
> +		/* Turn on LCD */
> +		outb(0x31, 0x3c4);
> +		value = inb(0x3c5);
> +		value = (value & 0xf8) | 0x03;
> +		outb(0x31, 0x3c4);
> +		outb(value, 0x3c5);
> +		/* Turn on backlight */
> +		ec_write(REG_BACKLIGHT_CTRL, BIT_BACKLIGHT_ON);
> +	} else {
> +		/* Turn off backlight */
> +		ec_write(REG_BACKLIGHT_CTRL, BIT_BACKLIGHT_OFF);
> +		/* Turn off 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)
> +{
> +	int value;
> +	unsigned long status;
> +
> +	status = !!od->request_state;
> +
> +	if (status == BIT_CRT_DETECT_PLUG) {
> +		if (ec_read(REG_CRT_DETECT) == BIT_CRT_DETECT_PLUG) {
> +			/* Turn on CRT */
> +			outb(0x21, 0x3c4);
> +			value = inb(0x3c5);
> +			value &= ~(1 << 7);
> +			outb(0x21, 0x3c4);
> +			outb(value, 0x3c5);
> +		}
> +	} else {
> +		/* Turn off 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,
> +};
> +
> +static struct output_device *lcd_output_dev, *crt_output_dev;
> +
> +static void yeeloong_lcd_vo_set(int status)
> +{
> +	lcd_output_dev->request_state = status;
> +	lcd_video_output_set(lcd_output_dev);
> +}
> +
> +static void yeeloong_crt_vo_set(int status)
> +{
> +	crt_output_dev->request_state = status;
> +	crt_video_output_set(crt_output_dev);
> +}
> +
> +static int yeeloong_vo_init(void)
> +{
> +	int ret;
> +
> +	/* Register video output device: lcd, crt */
> +	lcd_output_dev = video_output_register("LCD", NULL, NULL,
> +			&lcd_output_properties);
> +
> +	if (IS_ERR(lcd_output_dev)) {
> +		ret = PTR_ERR(lcd_output_dev);
> +		lcd_output_dev = NULL;
> +		return ret;
> +	}
> +	/* Ensure LCD is on by default */
> +	yeeloong_lcd_vo_set(BIT_DISPLAY_LCD_ON);
> +
> +	crt_output_dev = video_output_register("CRT", NULL, NULL,
> +			&crt_output_properties);
> +
> +	if (IS_ERR(crt_output_dev)) {
> +		ret = PTR_ERR(crt_output_dev);
> +		crt_output_dev = NULL;
> +		return ret;
> +	}
> +
> +	/* Turn off CRT by default, and will be enabled when the CRT
> +	 * connectting event reported by SCI */
> +	yeeloong_crt_vo_set(BIT_CRT_DETECT_UNPLUG);
> +
> +	return 0;
> +}
> +
> +static void yeeloong_vo_exit(void)
> +{
> +	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;
> +	}
> +}
> +
>  static struct platform_device_id platform_device_ids[] = {
>  	{
>  		.name = "yeeloong_laptop",
> @@ -443,11 +582,19 @@ static int __init yeeloong_init(void)
>  		return ret;
>  	}
>  
> +	ret = yeeloong_vo_init();
> +	if (ret) {
> +		pr_err("Fail to register yeeloong video output driver.\n");
> +		yeeloong_vo_exit();
> +		return ret;
> +	}
> +
>  	return 0;
>  }
>  
>  static void __exit yeeloong_exit(void)
>  {
> +	yeeloong_vo_exit();
>  	yeeloong_hwmon_exit();
>  	yeeloong_battery_exit();
>  	yeeloong_backlight_exit();



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

* Re: [PATCH v7 4/8] Loongson: YeeLoong: add battery driver
  2009-12-04 13:34 ` [PATCH v7 4/8] Loongson: YeeLoong: add battery driver Wu Zhangjin
  2009-12-04  8:04   ` Pavel Machek
@ 2009-12-08  6:57   ` Wu Zhangjin
  1 sibling, 0 replies; 15+ messages in thread
From: Wu Zhangjin @ 2009-12-08  6:57 UTC (permalink / raw)
  To: Pavel Machek
  Cc: akpm, linux-mips, linux-kernel, Dmitry Torokhov, Pavel Machek,
	Rafael J. Wysocki, zhangfx, Stephen Rothwell, linux-laptop,
	Ralf Baechle

Hi, Pavel

Seems you have reviewed this patch, can I get your Acked-by:?

Thanks!
	Wu Zhangjin

On Fri, 2009-12-04 at 21:34 +0800, Wu Zhangjin wrote:
> From: Wu Zhangjin <wuzhangjin@gmail.com>
> 
> This patch adds APM emulated Battery Driver, it provides standard
> interface(/proc/apm) for user-space applications(e.g. kpowersave,
> gnome-power-manager) to manage the battery.
> 
> Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
> ---
>  drivers/platform/mips/Kconfig           |    2 +
>  drivers/platform/mips/yeeloong_laptop.c |  104 +++++++++++++++++++++++++++++++
>  2 files changed, 106 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
> index f7a3705..0c6b5ad 100644
> --- a/drivers/platform/mips/Kconfig
> +++ b/drivers/platform/mips/Kconfig
> @@ -18,6 +18,8 @@ config LEMOTE_YEELOONG2F
>  	tristate "Lemote YeeLoong Laptop"
>  	depends on LEMOTE_MACH2F
>  	select BACKLIGHT_CLASS_DEVICE
> +	select SYS_SUPPORTS_APM_EMULATION
> +	select APM_EMULATION
>  	default m
>  	help
>  	  YeeLoong netbook is a mini laptop made by Lemote, which is basically
> diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c
> index fbc4ebb..729e368 100644
> --- a/drivers/platform/mips/yeeloong_laptop.c
> +++ b/drivers/platform/mips/yeeloong_laptop.c
> @@ -13,6 +13,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/backlight.h>	/* for backlight subdriver */
>  #include <linux/fb.h>
> +#include <linux/apm-emulation.h>/* for battery subdriver */
>  
>  #include <ec_kb3310b.h>
>  
> @@ -83,6 +84,106 @@ static void yeeloong_backlight_exit(void)
>  	}
>  }
>  
> +/* battery subdriver */
> +
> +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) {
> +		pr_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 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 {
> +			/* 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_battery_init(void)
> +{
> +	get_fixed_battery_info();
> +
> +	apm_get_power_status = get_power_status;
> +
> +	return 0;
> +}
> +
> +static void yeeloong_battery_exit(void)
> +{
> +	if (apm_get_power_status == get_power_status)
> +		apm_get_power_status = NULL;
> +}
> +
>  static struct platform_device_id platform_device_ids[] = {
>  	{
>  		.name = "yeeloong_laptop",
> @@ -120,11 +221,14 @@ static int __init yeeloong_init(void)
>  		return ret;
>  	}
>  
> +	yeeloong_battery_init();
> +
>  	return 0;
>  }
>  
>  static void __exit yeeloong_exit(void)
>  {
> +	yeeloong_battery_exit();
>  	yeeloong_backlight_exit();
>  	platform_driver_unregister(&platform_driver);
>  


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

end of thread, other threads:[~2009-12-08  6:57 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <cover.1259932036.git.wuzhangjin@gmail.com>
2009-12-04 13:34 ` [PATCH v7 4/8] Loongson: YeeLoong: add battery driver Wu Zhangjin
2009-12-04  8:04   ` Pavel Machek
2009-12-04 14:56     ` Wu Zhangjin
2009-12-08  6:57   ` Wu Zhangjin
2009-12-04 13:35 ` [PATCH v7 5/8] Loongson: YeeLoong: add hardware monitoring driver Wu Zhangjin
2009-12-04  8:08   ` Pavel Machek
2009-12-04 16:29     ` Wu Zhangjin
2009-12-04 13:36 ` [PATCH v7 6/8] Loongson: YeeLoong: add video output driver Wu Zhangjin
2009-12-04  8:11   ` Pavel Machek
2009-12-04 15:04     ` Wu Zhangjin
2009-12-08  6:54   ` Wu Zhangjin
2009-12-04 13:37 ` [PATCH v7 7/8] Loongson: YeeLoong: add suspend support Wu Zhangjin
2009-12-04 13:39 ` [PATCH v7 8/8] Loongson: YeeLoong: add input/hotkey driver Wu Zhangjin
2009-12-04  8:14   ` Pavel Machek
2009-12-04 14:57     ` Wu Zhangjin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox