From: Wu Zhangjin <[email protected]>
This patchset adds platform specific drivers for YeeLoong netbook. including
the backlight, battery, hwmon, video output, suspend and hotkey(input)
subdrivers. These drivers provide standard interfaces to the user-space
applications to manage the related devices:
Subdrivers Applicatioins.
- backlight
/sys/class/backlight/ kpowersave, gnome-power-manager
- battery
/proc/apm kpowersave, gnome-power-manager
- hwmon
/sys/class/hwmon/ lm-sensors, sensors-applet...
- video output
/sys/class/video_output ?
- hotkey
/sys/class/input/ gnome-settings-daemon ?
- platform(suspend)
/sys/power/state kpowersave, gnome-power-manager
Changes from v6:
- Move the whole stuff back to drivers/platform/mips/
It's very difficult to find a good place to put it in, so, just did
what the folks have done under drivers/platform/x86/
- Rebase the hotkey driver on the sparse keymap library from Dmitry
Torokhov.
- Load this module automatically
Register a platform device, bind it with this module.
- Fixup of battery subdriver
Ensure apm_get_power_status is NULL when exit.
Wu Zhangjin (8):
MIPS: add subdirectory for platform extension drivers
Loongson: YeeLoong: add platform driver
Loongson: YeeLoong: add backlight driver
Loongson: YeeLoong: add battery driver
Loongson: YeeLoong: add hardware monitoring driver
Loongson: YeeLoong: add video output driver
Loongson: YeeLoong: add suspend support
Loongson: YeeLoong: add input/hotkey driver
arch/mips/include/asm/mach-loongson/ec_kb3310b.h | 191 ++++
arch/mips/include/asm/mach-loongson/loongson.h | 6 +
arch/mips/loongson/common/cmdline.c | 8 +
arch/mips/loongson/lemote-2f/Makefile | 2 +-
arch/mips/loongson/lemote-2f/ec_kb3310b.c | 12 +-
arch/mips/loongson/lemote-2f/ec_kb3310b.h | 188 ----
arch/mips/loongson/lemote-2f/platform.c | 40 +
arch/mips/loongson/lemote-2f/pm.c | 4 +-
arch/mips/loongson/lemote-2f/reset.c | 2 +-
drivers/platform/Kconfig | 4 +
drivers/platform/Makefile | 1 +
drivers/platform/mips/Kconfig | 33 +
drivers/platform/mips/Makefile | 5 +
drivers/platform/mips/yeeloong_laptop.c | 1067 ++++++++++++++++++++++
14 files changed, 1363 insertions(+), 200 deletions(-)
create mode 100644 arch/mips/include/asm/mach-loongson/ec_kb3310b.h
delete mode 100644 arch/mips/loongson/lemote-2f/ec_kb3310b.h
create mode 100644 arch/mips/loongson/lemote-2f/platform.c
create mode 100644 drivers/platform/mips/Kconfig
create mode 100644 drivers/platform/mips/Makefile
create mode 100644 drivers/platform/mips/yeeloong_laptop.c
On Fri 2009-12-04 21:34:17, Wu Zhangjin wrote:
> From: Wu Zhangjin <[email protected]>
>
> 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
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
On Fri 2009-12-04 21:36:43, Wu Zhangjin wrote:
> From: Wu Zhangjin <[email protected]>
>
> 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
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
From: Wu Zhangjin <[email protected]>
It is really hard to split the platform specific subdrivers into
different subsystems, which will generate lots of duplicated source
code, break the whole support into several pieces and also will make the
users be difficult to choose the suitable subdrivers in different
places.
So, I did like the forks have done under drivers/platform/x86, created
the drivers/platform/mips/ for putting the future MIPS netbook/laptop/pc
extension drivers in.
Signed-off-by: Wu Zhangjin <[email protected]>
---
drivers/platform/Kconfig | 4 ++++
drivers/platform/mips/Kconfig | 18 ++++++++++++++++++
2 files changed, 22 insertions(+), 0 deletions(-)
create mode 100644 drivers/platform/mips/Kconfig
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index 9652c3f..2319c0b 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -3,3 +3,7 @@
if X86
source "drivers/platform/x86/Kconfig"
endif
+
+if MIPS
+source "drivers/platform/mips/Kconfig"
+endif
diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
new file mode 100644
index 0000000..2f77693
--- /dev/null
+++ b/drivers/platform/mips/Kconfig
@@ -0,0 +1,18 @@
+#
+# MIPS Platform Specific Drivers
+#
+
+menuconfig MIPS_PLATFORM_DEVICES
+ bool "MIPS Platform Specific Device Drivers"
+ default y
+ help
+ Say Y here to get to see options for device drivers of various
+ MIPS platforms, including vendor-specific netbook/laptop/pc extension
+ drivers. This option alone does not add any kernel code.
+
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if MIPS_PLATFORM_DEVICES
+
+
+endif # MIPS_PLATFORM_DEVICES
--
1.6.2.1
From: Wu Zhangjin <[email protected]>
This patch adds platform driver for YeeLoong, Currently, This driver is
"empty", the subdrivers will be added in the coming patches.
Signed-off-by: Wu Zhangjin <[email protected]>
---
arch/mips/include/asm/mach-loongson/ec_kb3310b.h | 191 ++++++++++++++++++++++
arch/mips/loongson/lemote-2f/Makefile | 2 +-
arch/mips/loongson/lemote-2f/ec_kb3310b.c | 12 +-
arch/mips/loongson/lemote-2f/ec_kb3310b.h | 188 ---------------------
arch/mips/loongson/lemote-2f/platform.c | 40 +++++
arch/mips/loongson/lemote-2f/pm.c | 4 +-
arch/mips/loongson/lemote-2f/reset.c | 2 +-
drivers/platform/Makefile | 1 +
drivers/platform/mips/Kconfig | 9 +
drivers/platform/mips/Makefile | 5 +
drivers/platform/mips/yeeloong_laptop.c | 60 +++++++
11 files changed, 314 insertions(+), 200 deletions(-)
create mode 100644 arch/mips/include/asm/mach-loongson/ec_kb3310b.h
delete mode 100644 arch/mips/loongson/lemote-2f/ec_kb3310b.h
create mode 100644 arch/mips/loongson/lemote-2f/platform.c
create mode 100644 drivers/platform/mips/Makefile
create mode 100644 drivers/platform/mips/yeeloong_laptop.c
diff --git a/arch/mips/include/asm/mach-loongson/ec_kb3310b.h b/arch/mips/include/asm/mach-loongson/ec_kb3310b.h
new file mode 100644
index 0000000..4fccb5d
--- /dev/null
+++ b/arch/mips/include/asm/mach-loongson/ec_kb3310b.h
@@ -0,0 +1,191 @@
+/*
+ * KB3310B Embedded Controller
+ *
+ * Copyright (C) 2008 Lemote Inc.
+ * Author: liujl <[email protected]>, 2008-03-14
+ * Copyright (C) 2009 Lemote Inc.
+ * Author: Wu Zhangjin <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _EC_KB3310B_H
+#define _EC_KB3310B_H
+
+extern unsigned char ec_read(unsigned short addr);
+extern void ec_write(unsigned short addr, unsigned char val);
+extern int ec_query_seq(unsigned char cmd);
+extern int ec_query_event_num(void);
+extern int ec_get_event_num(void);
+
+typedef int (*sci_handler) (int status);
+extern sci_handler yeeloong_report_lid_status;
+
+#define SCI_IRQ_NUM 0x0A
+
+/*
+ * The following registers are determined by the EC index configuration.
+ * 1, fill the PORT_HIGH as EC register high part.
+ * 2, fill the PORT_LOW as EC register low part.
+ * 3, fill the PORT_DATA as EC register write data or get the data from it.
+ */
+#define EC_IO_PORT_HIGH 0x0381
+#define EC_IO_PORT_LOW 0x0382
+#define EC_IO_PORT_DATA 0x0383
+
+/*
+ * EC delay time is 500us for register and status access
+ */
+#define EC_REG_DELAY 500 /* unit : us */
+#define EC_CMD_TIMEOUT 0x1000
+
+/*
+ * EC access port for SCI communication
+ */
+#define EC_CMD_PORT 0x66
+#define EC_STS_PORT 0x66
+#define EC_DAT_PORT 0x62
+#define CMD_INIT_IDLE_MODE 0xdd
+#define CMD_EXIT_IDLE_MODE 0xdf
+#define CMD_INIT_RESET_MODE 0xd8
+#define CMD_REBOOT_SYSTEM 0x8c
+#define CMD_GET_EVENT_NUM 0x84
+#define CMD_PROGRAM_PIECE 0xda
+
+/* Temperature & Fan registers */
+#define REG_TEMPERATURE_VALUE 0xF458
+#define REG_FAN_AUTO_MAN_SWITCH 0xF459
+#define BIT_FAN_AUTO 0
+#define BIT_FAN_MANUAL 1
+#define REG_FAN_CONTROL 0xF4D2
+#define BIT_FAN_CONTROL_ON (1 << 0)
+#define BIT_FAN_CONTROL_OFF (0 << 0)
+#define REG_FAN_STATUS 0xF4DA
+#define BIT_FAN_STATUS_ON (1 << 0)
+#define BIT_FAN_STATUS_OFF (0 << 0)
+#define REG_FAN_SPEED_HIGH 0xFE22
+#define REG_FAN_SPEED_LOW 0xFE23
+#define REG_FAN_SPEED_LEVEL 0xF4CC
+/* Fan speed divider */
+#define FAN_SPEED_DIVIDER 480000 /* (60*1000*1000/62.5/2)*/
+
+/* Battery registers */
+#define REG_BAT_DESIGN_CAP_HIGH 0xF77D
+#define REG_BAT_DESIGN_CAP_LOW 0xF77E
+#define REG_BAT_FULLCHG_CAP_HIGH 0xF780
+#define REG_BAT_FULLCHG_CAP_LOW 0xF781
+#define REG_BAT_DESIGN_VOL_HIGH 0xF782
+#define REG_BAT_DESIGN_VOL_LOW 0xF783
+#define REG_BAT_CURRENT_HIGH 0xF784
+#define REG_BAT_CURRENT_LOW 0xF785
+#define REG_BAT_VOLTAGE_HIGH 0xF786
+#define REG_BAT_VOLTAGE_LOW 0xF787
+#define REG_BAT_TEMPERATURE_HIGH 0xF788
+#define REG_BAT_TEMPERATURE_LOW 0xF789
+#define REG_BAT_RELATIVE_CAP_HIGH 0xF492
+#define REG_BAT_RELATIVE_CAP_LOW 0xF493
+#define REG_BAT_VENDOR 0xF4C4
+#define FLAG_BAT_VENDOR_SANYO 0x01
+#define FLAG_BAT_VENDOR_SIMPLO 0x02
+#define REG_BAT_CELL_COUNT 0xF4C6
+#define FLAG_BAT_CELL_3S1P 0x03
+#define FLAG_BAT_CELL_3S2P 0x06
+#define REG_BAT_CHARGE 0xF4A2
+#define FLAG_BAT_CHARGE_DISCHARGE 0x01
+#define FLAG_BAT_CHARGE_CHARGE 0x02
+#define FLAG_BAT_CHARGE_ACPOWER 0x00
+#define REG_BAT_STATUS 0xF4B0
+#define BIT_BAT_STATUS_LOW (1 << 5)
+#define BIT_BAT_STATUS_DESTROY (1 << 2)
+#define BIT_BAT_STATUS_FULL (1 << 1)
+#define BIT_BAT_STATUS_IN (1 << 0)
+#define REG_BAT_CHARGE_STATUS 0xF4B1
+#define BIT_BAT_CHARGE_STATUS_OVERTEMP (1 << 2)
+#define BIT_BAT_CHARGE_STATUS_PRECHG (1 << 1)
+#define REG_BAT_STATE 0xF482
+#define BIT_BAT_STATE_CHARGING (1 << 1)
+#define BIT_BAT_STATE_DISCHARGING (1 << 0)
+#define REG_BAT_POWER 0xF440
+#define BIT_BAT_POWER_S3 (1 << 2)
+#define BIT_BAT_POWER_ON (1 << 1)
+#define BIT_BAT_POWER_ACIN (1 << 0)
+
+/* Audio: rd/wr */
+#define REG_AUDIO_VOLUME 0xF46C
+#define REG_AUDIO_MUTE 0xF4E7
+#define REG_AUDIO_BEEP 0xF4D0
+/* USB port power or not: rd/wr */
+#define REG_USB0_FLAG 0xF461
+#define REG_USB1_FLAG 0xF462
+#define REG_USB2_FLAG 0xF463
+#define BIT_USB_FLAG_ON 1
+#define BIT_USB_FLAG_OFF 0
+/* LID */
+#define REG_LID_DETECT 0xF4BD
+#define BIT_LID_DETECT_ON 1
+#define BIT_LID_DETECT_OFF 0
+/* CRT */
+#define REG_CRT_DETECT 0xF4AD
+#define BIT_CRT_DETECT_PLUG 1
+#define BIT_CRT_DETECT_UNPLUG 0
+/* LCD backlight brightness adjust: 9 levels */
+#define REG_DISPLAY_BRIGHTNESS 0xF4F5
+/* Black screen Status */
+#define BIT_DISPLAY_LCD_ON 1
+#define BIT_DISPLAY_LCD_OFF 0
+/* LCD backlight control: off/restore */
+#define REG_BACKLIGHT_CTRL 0xF7BD
+#define BIT_BACKLIGHT_ON 1
+#define BIT_BACKLIGHT_OFF 0
+/* Reset the machine auto-clear: rd/wr */
+#define REG_RESET 0xF4EC
+#define BIT_RESET_ON 1
+/* Light the led: rd/wr */
+#define REG_LED 0xF4C8
+#define BIT_LED_RED_POWER (1 << 0)
+#define BIT_LED_ORANGE_POWER (1 << 1)
+#define BIT_LED_GREEN_CHARGE (1 << 2)
+#define BIT_LED_RED_CHARGE (1 << 3)
+#define BIT_LED_NUMLOCK (1 << 4)
+/* Test led mode, all led on/off */
+#define REG_LED_TEST 0xF4C2
+#define BIT_LED_TEST_IN 1
+#define BIT_LED_TEST_OUT 0
+/* Camera on/off */
+#define REG_CAMERA_STATUS 0xF46A
+#define BIT_CAMERA_STATUS_ON 1
+#define BIT_CAMERA_STATUS_OFF 0
+#define REG_CAMERA_CONTROL 0xF7B7
+#define BIT_CAMERA_CONTROL_OFF 0
+#define BIT_CAMERA_CONTROL_ON 1
+/* Wlan Status */
+#define REG_WLAN 0xF4FA
+#define BIT_WLAN_ON 1
+#define BIT_WLAN_OFF 0
+#define REG_DISPLAY_LCD 0xF79F
+
+/* SCI Event Number from EC */
+enum {
+ EVENT_LID = 0x23, /* Turn on/off LID */
+ EVENT_DISPLAY_TOGGLE, /* Fn+F3 for display switch */
+ EVENT_SLEEP, /* Fn+F1 for entering sleep mode */
+ EVENT_OVERTEMP, /* Over-temperature happened */
+ EVENT_CRT_DETECT, /* CRT is connected */
+ EVENT_CAMERA, /* Camera on/off */
+ EVENT_USB_OC2, /* USB2 Over Current occurred */
+ EVENT_USB_OC0, /* USB0 Over Current occurred */
+ EVENT_BLACK_SCREEN, /* Turn on/off backlight */
+ EVENT_AUDIO_MUTE, /* Mute on/off */
+ EVENT_DISPLAY_BRIGHTNESS,/* LCD backlight brightness adjust */
+ EVENT_AC_BAT, /* AC & Battery relative issue */
+ EVENT_AUDIO_VOLUME, /* Volume adjust */
+ EVENT_WLAN, /* Wlan on/off */
+};
+
+#define EVENT_START EVENT_LID
+#define EVENT_END EVENT_WLAN
+
+#endif /* !_EC_KB3310B_H */
diff --git a/arch/mips/loongson/lemote-2f/Makefile b/arch/mips/loongson/lemote-2f/Makefile
index 4d84b27..470156e 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 ec_kb3310b.o
+obj-y += irq.o reset.o ec_kb3310b.o platform.o
#
# Suspend Support
diff --git a/arch/mips/loongson/lemote-2f/ec_kb3310b.c b/arch/mips/loongson/lemote-2f/ec_kb3310b.c
index 4d84111..734d2d0 100644
--- a/arch/mips/loongson/lemote-2f/ec_kb3310b.c
+++ b/arch/mips/loongson/lemote-2f/ec_kb3310b.c
@@ -14,7 +14,7 @@
#include <linux/spinlock.h>
#include <linux/delay.h>
-#include "ec_kb3310b.h"
+#include <ec_kb3310b.h>
static DEFINE_SPINLOCK(index_access_lock);
static DEFINE_SPINLOCK(port_access_lock);
@@ -76,12 +76,9 @@ int ec_query_seq(unsigned char cmd)
}
if (timeout <= 0) {
- printk(KERN_ERR "%s: deadable error : timeout...\n", __func__);
+ pr_err("%s: deadable error : timeout...\n", __func__);
ret = -EINVAL;
- } else
- printk(KERN_INFO
- "(%x/%d)ec issued command %d status : 0x%x\n",
- timeout, EC_CMD_TIMEOUT - timeout, cmd, status);
+ }
spin_unlock_irqrestore(&port_access_lock, flags);
@@ -118,8 +115,7 @@ int ec_get_event_num(void)
udelay(EC_REG_DELAY);
}
if (timeout <= 0) {
- pr_info("%s: get event number timeout.\n", __func__);
-
+ pr_err("%s: get event number timeout.\n", __func__);
return -EINVAL;
}
value = inb(EC_DAT_PORT);
diff --git a/arch/mips/loongson/lemote-2f/ec_kb3310b.h b/arch/mips/loongson/lemote-2f/ec_kb3310b.h
deleted file mode 100644
index 1595a21..0000000
--- a/arch/mips/loongson/lemote-2f/ec_kb3310b.h
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * KB3310B Embedded Controller
- *
- * Copyright (C) 2008 Lemote Inc.
- * Author: liujl <[email protected]>, 2008-03-14
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef _EC_KB3310B_H
-#define _EC_KB3310B_H
-
-extern unsigned char ec_read(unsigned short addr);
-extern void ec_write(unsigned short addr, unsigned char val);
-extern int ec_query_seq(unsigned char cmd);
-extern int ec_query_event_num(void);
-extern int ec_get_event_num(void);
-
-typedef int (*sci_handler) (int status);
-extern sci_handler yeeloong_report_lid_status;
-
-#define SCI_IRQ_NUM 0x0A
-
-/*
- * The following registers are determined by the EC index configuration.
- * 1, fill the PORT_HIGH as EC register high part.
- * 2, fill the PORT_LOW as EC register low part.
- * 3, fill the PORT_DATA as EC register write data or get the data from it.
- */
-#define EC_IO_PORT_HIGH 0x0381
-#define EC_IO_PORT_LOW 0x0382
-#define EC_IO_PORT_DATA 0x0383
-
-/*
- * EC delay time is 500us for register and status access
- */
-#define EC_REG_DELAY 500 /* unit : us */
-#define EC_CMD_TIMEOUT 0x1000
-
-/*
- * EC access port for SCI communication
- */
-#define EC_CMD_PORT 0x66
-#define EC_STS_PORT 0x66
-#define EC_DAT_PORT 0x62
-#define CMD_INIT_IDLE_MODE 0xdd
-#define CMD_EXIT_IDLE_MODE 0xdf
-#define CMD_INIT_RESET_MODE 0xd8
-#define CMD_REBOOT_SYSTEM 0x8c
-#define CMD_GET_EVENT_NUM 0x84
-#define CMD_PROGRAM_PIECE 0xda
-
-/* temperature & fan registers */
-#define REG_TEMPERATURE_VALUE 0xF458
-#define REG_FAN_AUTO_MAN_SWITCH 0xF459
-#define BIT_FAN_AUTO 0
-#define BIT_FAN_MANUAL 1
-#define REG_FAN_CONTROL 0xF4D2
-#define BIT_FAN_CONTROL_ON (1 << 0)
-#define BIT_FAN_CONTROL_OFF (0 << 0)
-#define REG_FAN_STATUS 0xF4DA
-#define BIT_FAN_STATUS_ON (1 << 0)
-#define BIT_FAN_STATUS_OFF (0 << 0)
-#define REG_FAN_SPEED_HIGH 0xFE22
-#define REG_FAN_SPEED_LOW 0xFE23
-#define REG_FAN_SPEED_LEVEL 0xF4CC
-/* fan speed divider */
-#define FAN_SPEED_DIVIDER 480000 /* (60*1000*1000/62.5/2)*/
-
-/* battery registers */
-#define REG_BAT_DESIGN_CAP_HIGH 0xF77D
-#define REG_BAT_DESIGN_CAP_LOW 0xF77E
-#define REG_BAT_FULLCHG_CAP_HIGH 0xF780
-#define REG_BAT_FULLCHG_CAP_LOW 0xF781
-#define REG_BAT_DESIGN_VOL_HIGH 0xF782
-#define REG_BAT_DESIGN_VOL_LOW 0xF783
-#define REG_BAT_CURRENT_HIGH 0xF784
-#define REG_BAT_CURRENT_LOW 0xF785
-#define REG_BAT_VOLTAGE_HIGH 0xF786
-#define REG_BAT_VOLTAGE_LOW 0xF787
-#define REG_BAT_TEMPERATURE_HIGH 0xF788
-#define REG_BAT_TEMPERATURE_LOW 0xF789
-#define REG_BAT_RELATIVE_CAP_HIGH 0xF492
-#define REG_BAT_RELATIVE_CAP_LOW 0xF493
-#define REG_BAT_VENDOR 0xF4C4
-#define FLAG_BAT_VENDOR_SANYO 0x01
-#define FLAG_BAT_VENDOR_SIMPLO 0x02
-#define REG_BAT_CELL_COUNT 0xF4C6
-#define FLAG_BAT_CELL_3S1P 0x03
-#define FLAG_BAT_CELL_3S2P 0x06
-#define REG_BAT_CHARGE 0xF4A2
-#define FLAG_BAT_CHARGE_DISCHARGE 0x01
-#define FLAG_BAT_CHARGE_CHARGE 0x02
-#define FLAG_BAT_CHARGE_ACPOWER 0x00
-#define REG_BAT_STATUS 0xF4B0
-#define BIT_BAT_STATUS_LOW (1 << 5)
-#define BIT_BAT_STATUS_DESTROY (1 << 2)
-#define BIT_BAT_STATUS_FULL (1 << 1)
-#define BIT_BAT_STATUS_IN (1 << 0)
-#define REG_BAT_CHARGE_STATUS 0xF4B1
-#define BIT_BAT_CHARGE_STATUS_OVERTEMP (1 << 2)
-#define BIT_BAT_CHARGE_STATUS_PRECHG (1 << 1)
-#define REG_BAT_STATE 0xF482
-#define BIT_BAT_STATE_CHARGING (1 << 1)
-#define BIT_BAT_STATE_DISCHARGING (1 << 0)
-#define REG_BAT_POWER 0xF440
-#define BIT_BAT_POWER_S3 (1 << 2)
-#define BIT_BAT_POWER_ON (1 << 1)
-#define BIT_BAT_POWER_ACIN (1 << 0)
-
-/* other registers */
-/* Audio: rd/wr */
-#define REG_AUDIO_VOLUME 0xF46C
-#define REG_AUDIO_MUTE 0xF4E7
-#define REG_AUDIO_BEEP 0xF4D0
-/* USB port power or not: rd/wr */
-#define REG_USB0_FLAG 0xF461
-#define REG_USB1_FLAG 0xF462
-#define REG_USB2_FLAG 0xF463
-#define BIT_USB_FLAG_ON 1
-#define BIT_USB_FLAG_OFF 0
-/* LID */
-#define REG_LID_DETECT 0xF4BD
-#define BIT_LID_DETECT_ON 1
-#define BIT_LID_DETECT_OFF 0
-/* CRT */
-#define REG_CRT_DETECT 0xF4AD
-#define BIT_CRT_DETECT_PLUG 1
-#define BIT_CRT_DETECT_UNPLUG 0
-/* LCD backlight brightness adjust: 9 levels */
-#define REG_DISPLAY_BRIGHTNESS 0xF4F5
-/* Black screen Status */
-#define BIT_DISPLAY_LCD_ON 1
-#define BIT_DISPLAY_LCD_OFF 0
-/* LCD backlight control: off/restore */
-#define REG_BACKLIGHT_CTRL 0xF7BD
-#define BIT_BACKLIGHT_ON 1
-#define BIT_BACKLIGHT_OFF 0
-/* Reset the machine auto-clear: rd/wr */
-#define REG_RESET 0xF4EC
-#define BIT_RESET_ON 1
-/* Light the led: rd/wr */
-#define REG_LED 0xF4C8
-#define BIT_LED_RED_POWER (1 << 0)
-#define BIT_LED_ORANGE_POWER (1 << 1)
-#define BIT_LED_GREEN_CHARGE (1 << 2)
-#define BIT_LED_RED_CHARGE (1 << 3)
-#define BIT_LED_NUMLOCK (1 << 4)
-/* Test led mode, all led on/off */
-#define REG_LED_TEST 0xF4C2
-#define BIT_LED_TEST_IN 1
-#define BIT_LED_TEST_OUT 0
-/* Camera on/off */
-#define REG_CAMERA_STATUS 0xF46A
-#define BIT_CAMERA_STATUS_ON 1
-#define BIT_CAMERA_STATUS_OFF 0
-#define REG_CAMERA_CONTROL 0xF7B7
-#define BIT_CAMERA_CONTROL_OFF 0
-#define BIT_CAMERA_CONTROL_ON 1
-/* Wlan Status */
-#define REG_WLAN 0xF4FA
-#define BIT_WLAN_ON 1
-#define BIT_WLAN_OFF 0
-#define REG_DISPLAY_LCD 0xF79F
-
-/* SCI Event Number from EC */
-enum {
- EVENT_LID = 0x23, /* LID open/close */
- EVENT_DISPLAY_TOGGLE, /* Fn+F3 for display switch */
- EVENT_SLEEP, /* Fn+F1 for entering sleep mode */
- EVENT_OVERTEMP, /* Over-temperature happened */
- EVENT_CRT_DETECT, /* CRT is connected */
- EVENT_CAMERA, /* Camera on/off */
- EVENT_USB_OC2, /* USB2 Over Current occurred */
- EVENT_USB_OC0, /* USB0 Over Current occurred */
- EVENT_BLACK_SCREEN, /* Turn on/off backlight */
- EVENT_AUDIO_MUTE, /* Mute on/off */
- EVENT_DISPLAY_BRIGHTNESS,/* LCD backlight brightness adjust */
- EVENT_AC_BAT, /* AC & Battery relative issue */
- EVENT_AUDIO_VOLUME, /* Volume adjust */
- EVENT_WLAN, /* Wlan on/off */
- EVENT_END
-};
-
-#endif /* !_EC_KB3310B_H */
diff --git a/arch/mips/loongson/lemote-2f/platform.c b/arch/mips/loongson/lemote-2f/platform.c
new file mode 100644
index 0000000..0e7b27d
--- /dev/null
+++ b/arch/mips/loongson/lemote-2f/platform.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 Lemote Inc.
+ * Author: Wu Zhangjin, [email protected]
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+#include <asm/bootinfo.h>
+
+static struct platform_device yeeloong_pdev = {
+ .name = "yeeloong_laptop",
+ .id = -1,
+};
+
+static int __init lemote2f_platform_init(void)
+{
+ struct platform_device *pdev = NULL;
+
+ switch (mips_machtype) {
+ case MACH_LEMOTE_YL2F89:
+ pdev = &yeeloong_pdev;
+ break;
+ default:
+ break;
+
+ }
+
+ if (pdev != NULL)
+ return platform_device_register(pdev);
+
+ return -ENODEV;
+}
+
+arch_initcall(lemote2f_platform_init);
diff --git a/arch/mips/loongson/lemote-2f/pm.c b/arch/mips/loongson/lemote-2f/pm.c
index d7af2e6..0d5698a 100644
--- a/arch/mips/loongson/lemote-2f/pm.c
+++ b/arch/mips/loongson/lemote-2f/pm.c
@@ -23,7 +23,7 @@
#include <loongson.h>
#include <cs5536/cs5536_mfgpt.h>
-#include "ec_kb3310b.h"
+#include <ec_kb3310b.h>
#define I8042_KBD_IRQ 1
#define I8042_CTR_KBDINT 0x01
@@ -100,7 +100,7 @@ int wakeup_loongson(void)
if (irq < 0)
return 0;
- printk(KERN_INFO "%s: irq = %d\n", __func__, irq);
+ pr_info("%s: irq = %d\n", __func__, irq);
if (irq == I8042_KBD_IRQ)
return 1;
diff --git a/arch/mips/loongson/lemote-2f/reset.c b/arch/mips/loongson/lemote-2f/reset.c
index 51d1a60..4627659 100644
--- a/arch/mips/loongson/lemote-2f/reset.c
+++ b/arch/mips/loongson/lemote-2f/reset.c
@@ -20,7 +20,7 @@
#include <loongson.h>
#include <cs5536/cs5536.h>
-#include "ec_kb3310b.h"
+#include <ec_kb3310b.h>
static void reset_cpu(void)
{
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index 782953a..8bdc97c 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_X86) += x86/
+obj-$(CONFIG_MIPS) += mips/
diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
index 2f77693..76f0250 100644
--- a/drivers/platform/mips/Kconfig
+++ b/drivers/platform/mips/Kconfig
@@ -14,5 +14,14 @@ menuconfig MIPS_PLATFORM_DEVICES
if MIPS_PLATFORM_DEVICES
+config LEMOTE_YEELOONG2F
+ tristate "Lemote YeeLoong Laptop"
+ depends on LEMOTE_MACH2F
+ default m
+ help
+ YeeLoong netbook is a mini laptop made by Lemote, which is basically
+ compatible to FuLoong2F mini PC, but it has an extra Embedded
+ Controller(kb3310b) for battery, hotkey, backlight, temperature and
+ fan management.
endif # MIPS_PLATFORM_DEVICES
diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile
new file mode 100644
index 0000000..506f920
--- /dev/null
+++ b/drivers/platform/mips/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for MIPS Platform-Specific Drivers
+#
+
+obj-$(CONFIG_LEMOTE_YEELOONG2F) += yeeloong_laptop.o
diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c
new file mode 100644
index 0000000..85fc7ed
--- /dev/null
+++ b/drivers/platform/mips/yeeloong_laptop.c
@@ -0,0 +1,60 @@
+/*
+ * Driver for YeeLoong laptop extras
+ *
+ * Copyright (C) 2009 Lemote Inc.
+ * Author: Wu Zhangjin <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+static struct platform_device_id platform_device_ids[] = {
+ {
+ .name = "yeeloong_laptop",
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(platform, platform_device_ids);
+
+static struct platform_driver platform_driver = {
+ .driver = {
+ .name = "yeeloong_laptop",
+ .owner = THIS_MODULE,
+ },
+ .id_table = platform_device_ids,
+};
+
+static int __init yeeloong_init(void)
+{
+ int ret;
+
+ pr_info("Load YeeLoong Laptop Platform Specific Driver.\n");
+
+ /* Register platform stuff */
+ ret = platform_driver_register(&platform_driver);
+ if (ret) {
+ pr_err("Fail to register yeeloong platform driver.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit yeeloong_exit(void)
+{
+ platform_driver_unregister(&platform_driver);
+
+ pr_info("Unload YeeLoong Platform Specific Driver.\n");
+}
+
+module_init(yeeloong_init);
+module_exit(yeeloong_exit);
+
+MODULE_AUTHOR("Wu Zhangjin <[email protected]>");
+MODULE_DESCRIPTION("YeeLoong laptop driver");
+MODULE_LICENSE("GPL");
--
1.6.2.1
From: Wu Zhangjin <[email protected]>
This patch adds YeeLoong Backlight Driver, it provides standard
interface(/sys/class/backlight/) for user-space applications(e.g.
kpowersave, gnome-power-manager) to control the brightness of the
backlight.
Signed-off-by: Wu Zhangjin <[email protected]>
---
drivers/platform/mips/Kconfig | 1 +
drivers/platform/mips/yeeloong_laptop.c | 79 +++++++++++++++++++++++++++++++
2 files changed, 80 insertions(+), 0 deletions(-)
diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
index 76f0250..f7a3705 100644
--- a/drivers/platform/mips/Kconfig
+++ b/drivers/platform/mips/Kconfig
@@ -17,6 +17,7 @@ if MIPS_PLATFORM_DEVICES
config LEMOTE_YEELOONG2F
tristate "Lemote YeeLoong Laptop"
depends on LEMOTE_MACH2F
+ select BACKLIGHT_CLASS_DEVICE
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 85fc7ed..fbc4ebb 100644
--- a/drivers/platform/mips/yeeloong_laptop.c
+++ b/drivers/platform/mips/yeeloong_laptop.c
@@ -11,6 +11,77 @@
#include <linux/err.h>
#include <linux/platform_device.h>
+#include <linux/backlight.h> /* for backlight subdriver */
+#include <linux/fb.h>
+
+#include <ec_kb3310b.h>
+
+/* 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 to modify the brightness when 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(void)
+{
+ int ret;
+
+ yeeloong_backlight_dev = backlight_device_register("backlight0", NULL,
+ 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;
+ }
+}
static struct platform_device_id platform_device_ids[] = {
{
@@ -42,11 +113,19 @@ static int __init yeeloong_init(void)
return ret;
}
+ ret = yeeloong_backlight_init();
+ if (ret) {
+ pr_err("Fail to register yeeloong backlight driver.\n");
+ yeeloong_backlight_exit();
+ return ret;
+ }
+
return 0;
}
static void __exit yeeloong_exit(void)
{
+ yeeloong_backlight_exit();
platform_driver_unregister(&platform_driver);
pr_info("Unload YeeLoong Platform Specific Driver.\n");
--
1.6.2.1
From: Wu Zhangjin <[email protected]>
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 <[email protected]>
---
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
From: Wu Zhangjin <[email protected]>
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 <[email protected]>
---
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
From: Wu Zhangjin <[email protected]>
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 <[email protected]>
---
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
From: Wu Zhangjin <[email protected]>
This patch add support to suspend the yeeloong platform specific
devices(LCD, CRT, USB...).
Acked-by: Rafael J. Wysocki <[email protected]>
Acked-by: Pavel Machek <[email protected]>
Signed-off-by: Wu Zhangjin <[email protected]>
---
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
From: Wu Zhangjin <[email protected]>
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 <[email protected]>
---
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
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 <[email protected]>
> >
> > 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
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!
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 <[email protected]>
> >
> > 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
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
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 <[email protected]>
>
> 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 <[email protected]>
> ---
> 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();
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 <[email protected]>
>
> 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 <[email protected]>
> ---
> 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);
>