Since Ralf havn't appear for a long time, I resend this patch.
Btw: Why linux-mips wiki and patchwork down for so long time?
To operate EC from platform driver, this head file need able to be include
from anywhere. This patch just move ec_kb3310b.h to include dir and
clean up ec_kb3310b.h.
Signed-off-by: Jiaxun Yang <[email protected]>
---
arch/mips/include/asm/mach-loongson64/ec_kb3310b.h | 170 +++++++++++++++++++
arch/mips/loongson64/lemote-2f/ec_kb3310b.c | 2 +-
arch/mips/loongson64/lemote-2f/ec_kb3310b.h | 188 ---------------------
arch/mips/loongson64/lemote-2f/pm.c | 4 +-
arch/mips/loongson64/lemote-2f/reset.c | 4 +-
5 files changed, 175 insertions(+), 193 deletions(-)
create mode 100644 arch/mips/include/asm/mach-loongson64/ec_kb3310b.h
delete mode 100644 arch/mips/loongson64/lemote-2f/ec_kb3310b.h
diff --git a/arch/mips/include/asm/mach-loongson64/ec_kb3310b.h b/arch/mips/include/asm/mach-loongson64/ec_kb3310b.h
new file mode 100644
index 000000000000..2e8690532ea5
--- /dev/null
+++ b/arch/mips/include/asm/mach-loongson64/ec_kb3310b.h
@@ -0,0 +1,170 @@
+/*
+ * 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 ON 1
+#define OFF 0
+
+#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 REG_FAN_STATUS 0xF4DA
+#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 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
+/* LID */
+#define REG_LID_DETECT 0xF4BD
+/* CRT */
+#define REG_CRT_DETECT 0xF4AD
+/* LCD backlight brightness adjust: 9 levels */
+#define REG_DISPLAY_BRIGHTNESS 0xF4F5
+/* LCD backlight control: off/restore */
+#define REG_BACKLIGHT_CTRL 0xF7BD
+/* Reset the machine auto-clear: rd/wr */
+#define REG_RESET 0xF4EC
+/* 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 REG_CAMERA_CONTROL 0xF7B7
+/* Wlan Status */
+#define REG_WLAN 0xF4FA
+#define REG_DISPLAY_LCD 0xF79F
+
+/* SCI Event Number from EC */
+enum {
+ EVENT_LID = 0x23, /* Turn on/off LID */
+ EVENT_SWITCHVIDEOMODE, /* 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_DISPLAYTOGGLE, /* Fn+F2, Turn on/off backlight */
+ EVENT_AUDIO_MUTE, /* Fn+F4, Mute on/off */
+ EVENT_DISPLAY_BRIGHTNESS,/* Fn+^/V, LCD backlight brightness adjust */
+ EVENT_AC_BAT, /* AC & Battery relative issue */
+ EVENT_AUDIO_VOLUME, /* Fn+<|>, 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/loongson64/lemote-2f/ec_kb3310b.c b/arch/mips/loongson64/lemote-2f/ec_kb3310b.c
index 321822997e76..6e416d55b42a 100644
--- a/arch/mips/loongson64/lemote-2f/ec_kb3310b.c
+++ b/arch/mips/loongson64/lemote-2f/ec_kb3310b.c
@@ -15,7 +15,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);
diff --git a/arch/mips/loongson64/lemote-2f/ec_kb3310b.h b/arch/mips/loongson64/lemote-2f/ec_kb3310b.h
deleted file mode 100644
index 5a3f1860d4d2..000000000000
--- a/arch/mips/loongson64/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/loongson64/lemote-2f/pm.c b/arch/mips/loongson64/lemote-2f/pm.c
index 6859e934862d..0768739155f6 100644
--- a/arch/mips/loongson64/lemote-2f/pm.c
+++ b/arch/mips/loongson64/lemote-2f/pm.c
@@ -88,7 +88,7 @@ EXPORT_SYMBOL(yeeloong_report_lid_status);
static void yeeloong_lid_update_task(struct work_struct *work)
{
if (yeeloong_report_lid_status)
- yeeloong_report_lid_status(BIT_LID_DETECT_ON);
+ yeeloong_report_lid_status(ON);
}
int wakeup_loongson(void)
@@ -118,7 +118,7 @@ int wakeup_loongson(void)
/* check the LID status */
lid_status = ec_read(REG_LID_DETECT);
/* wakeup cpu when people open the LID */
- if (lid_status == BIT_LID_DETECT_ON) {
+ if (lid_status == ON) {
/* If we call it directly here, the WARNING
* will be sent out by getnstimeofday
* via "WARN_ON(timekeeping_suspended);"
diff --git a/arch/mips/loongson64/lemote-2f/reset.c b/arch/mips/loongson64/lemote-2f/reset.c
index a26ca7fcd7e0..2b72b197c51d 100644
--- a/arch/mips/loongson64/lemote-2f/reset.c
+++ b/arch/mips/loongson64/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)
{
@@ -81,7 +81,7 @@ static void ml2f_reboot(void)
reset_cpu();
/* sending an reset signal to EC(embedded controller) */
- ec_write(REG_RESET, BIT_RESET_ON);
+ ec_write(REG_RESET, ON);
}
#define yl2f89_reboot ml2f_reboot
--
2.15.0
This patch just add pdev during boot to load the platform driver
Signed-off-by: Jiaxun Yang <[email protected]>
---
arch/mips/loongson64/lemote-2f/Makefile | 2 +-
arch/mips/loongson64/lemote-2f/platform.c | 27 +++++++++++++++++++++++++++
2 files changed, 28 insertions(+), 1 deletion(-)
create mode 100644 arch/mips/loongson64/lemote-2f/platform.c
diff --git a/arch/mips/loongson64/lemote-2f/Makefile b/arch/mips/loongson64/lemote-2f/Makefile
index 08b8abcbfef5..31c90737b98c 100644
--- a/arch/mips/loongson64/lemote-2f/Makefile
+++ b/arch/mips/loongson64/lemote-2f/Makefile
@@ -2,7 +2,7 @@
# Makefile for lemote loongson2f family machines
#
-obj-y += clock.o machtype.o irq.o reset.o ec_kb3310b.o
+obj-y += clock.o machtype.o irq.o reset.o ec_kb3310b.o platform.o
#
# Suspend Support
diff --git a/arch/mips/loongson64/lemote-2f/platform.c b/arch/mips/loongson64/lemote-2f/platform.c
new file mode 100644
index 000000000000..46922f730a64
--- /dev/null
+++ b/arch/mips/loongson64/lemote-2f/platform.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 Jiaxun Yang.
+ * Author: Jiaxun Yang, [email protected]
+
+ * 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 int __init lemote2f_platform_init(void)
+{
+ if (mips_machtype != MACH_LEMOTE_YL2F89)
+ return -ENODEV;
+
+ return platform_device_register_simple("yeeloong_laptop", -1, NULL, 0);
+}
+
+arch_initcall(lemote2f_platform_init);
\ No newline at end of file
--
2.15.0
Yeeloong is a laptop with a MIPS Loongson 2F processor, AMD CS5536
chipset, and KB3310B controller.
This yeeloong_laptop module enables access to sensors, battery,
video camera switch, external video connector event, and some
additional buttons.
This driver was orginally from linux-loongson-community. I Just do
some clean up and port to mainline kernel tree.
Signed-off-by: Jiaxun Yang <[email protected]>
---
drivers/platform/mips/Kconfig | 19 +
drivers/platform/mips/Makefile | 3 +
drivers/platform/mips/yeeloong_laptop.c | 1142 +++++++++++++++++++++++++++++++
3 files changed, 1164 insertions(+)
create mode 100644 drivers/platform/mips/yeeloong_laptop.c
diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig
index b3ae30a4c67b..acd27e36710b 100644
--- a/drivers/platform/mips/Kconfig
+++ b/drivers/platform/mips/Kconfig
@@ -23,4 +23,23 @@ config CPU_HWMON
help
Loongson-3A/3B CPU Hwmon (temperature sensor) driver.
+config LEMOTE_YEELOONG2F
+ tristate "Lemote YeeLoong Laptop"
+ depends on LEMOTE_MACH2F
+ select BACKLIGHT_LCD_SUPPORT
+ select LCD_CLASS_DEVICE
+ select BACKLIGHT_CLASS_DEVICE
+ select POWER_SUPPLY
+ select HWMON
+ select INPUT
+ select INPUT_MISC
+ select INPUT_SPARSEKMAP
+ select INPUT_EVDEV
+ 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
index 8dfd03924c37..b3172b081a2f 100644
--- a/drivers/platform/mips/Makefile
+++ b/drivers/platform/mips/Makefile
@@ -1 +1,4 @@
obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o
+
+obj-$(CONFIG_LEMOTE_YEELOONG2F) += yeeloong_laptop.o
+CFLAGS_yeeloong_laptop.o = -I$(srctree)/arch/mips/loongson/lemote-2f
diff --git a/drivers/platform/mips/yeeloong_laptop.c b/drivers/platform/mips/yeeloong_laptop.c
new file mode 100644
index 000000000000..61353c3e894c
--- /dev/null
+++ b/drivers/platform/mips/yeeloong_laptop.c
@@ -0,0 +1,1142 @@
+/*
+ * Driver for YeeLoong laptop extras
+ *
+ * Copyright (C) 2017 Jiaxun Yang.
+ * Author: Jiaxun Yang <[email protected]>
+ *
+ * Copyright (C) 2009 Lemote Inc.
+ * Author: Wu Zhangjin <[email protected]>, Liu Junliang <[email protected]>
+ *
+ * Fixes: Petr Pisar <[email protected]>, 2012, 2013, 2014, 2015.
+ *
+ * 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>
+#include <linux/backlight.h> /* for backlight subdriver */
+#include <linux/fb.h>
+#include <linux/hwmon.h> /* for hwmon subdriver */
+#include <linux/hwmon-sysfs.h>
+#include <linux/kernel.h> /* for clamp_val() */
+#include <linux/input.h> /* for hotkey subdriver */
+#include <linux/input/sparse-keymap.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/power_supply.h> /* for AC & Battery subdriver */
+#include <linux/module.h> /* For MODULE_DEVICE_TABLE() */
+
+#include <asm/bootinfo.h>
+
+#include <cs5536/cs5536.h>
+
+#include <loongson.h> /* for loongson_cmdline */
+#include <ec_kb3310b.h>
+
+/* common function */
+#define EC_VER_LEN 64
+
+static int ec_version_before(char *version)
+{
+ char *p, ec_ver[EC_VER_LEN];
+
+ p = strstr(loongson_cmdline, "EC_VER=");
+ if (!p)
+ memset(ec_ver, 0, EC_VER_LEN);
+ else {
+ strncpy(ec_ver, p, EC_VER_LEN);
+ p = strstr(ec_ver, " ");
+ if (p)
+ *p = '\0';
+ }
+
+ return (strncasecmp(ec_ver, version, 64) < 0);
+}
+
+/* backlight subdriver */
+#define 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;
+
+ level = clamp_val(level, 0, MAX_BRIGHTNESS);
+
+ /* Avoid to modify the brightness when EC is tuning it */
+ if (old_level != level) {
+ current_level = ec_read(REG_DISPLAY_BRIGHTNESS);
+ if (old_level == current_level)
+ ec_write(REG_DISPLAY_BRIGHTNESS, level);
+ old_level = level;
+ }
+
+ return 0;
+}
+
+static int yeeloong_get_brightness(struct backlight_device *bd)
+{
+ return ec_read(REG_DISPLAY_BRIGHTNESS);
+}
+
+const struct backlight_ops backlight_ops = {
+ .get_brightness = yeeloong_get_brightness,
+ .update_status = yeeloong_set_brightness,
+};
+
+static struct backlight_device *yeeloong_backlight_dev;
+
+static int yeeloong_backlight_init(void)
+{
+ int ret;
+ struct backlight_properties props;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = MAX_BRIGHTNESS;
+ yeeloong_backlight_dev = backlight_device_register("backlight0", NULL,
+ NULL, &backlight_ops, &props);
+
+ if (IS_ERR(yeeloong_backlight_dev)) {
+ ret = PTR_ERR(yeeloong_backlight_dev);
+ yeeloong_backlight_dev = NULL;
+ return ret;
+ }
+
+ yeeloong_backlight_dev->props.brightness =
+ yeeloong_get_brightness(yeeloong_backlight_dev);
+ backlight_update_status(yeeloong_backlight_dev);
+
+ return 0;
+}
+
+static void yeeloong_backlight_exit(void)
+{
+ if (yeeloong_backlight_dev) {
+ backlight_device_unregister(yeeloong_backlight_dev);
+ yeeloong_backlight_dev = NULL;
+ }
+}
+
+/* AC & Battery subdriver */
+
+static struct power_supply *yeeloong_ac, *yeeloong_bat;
+
+#define RET (val->intval)
+
+static inline bool is_ac_in(void)
+{
+ return !!(ec_read(REG_BAT_POWER) & BIT_BAT_POWER_ACIN);
+}
+
+static int yeeloong_get_ac_props(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ RET = is_ac_in();
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static enum power_supply_property yeeloong_ac_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc yeeloong_ac_desc = {
+ .name = "yeeloong-ac",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .properties = yeeloong_ac_props,
+ .num_properties = ARRAY_SIZE(yeeloong_ac_props),
+ .get_property = yeeloong_get_ac_props,
+};
+
+#define BAT_CAP_CRITICAL 5
+#define BAT_CAP_HIGH 95
+
+#define get_bat_info(type) \
+ ((ec_read(REG_BAT_##type##_HIGH) << 8) | \
+ (ec_read(REG_BAT_##type##_LOW)))
+
+static inline bool is_bat_in(void)
+{
+ return !!(ec_read(REG_BAT_STATUS) & BIT_BAT_STATUS_IN);
+}
+
+static inline int get_bat_status(void)
+{
+ return ec_read(REG_BAT_STATUS);
+}
+
+static int get_battery_temp(void)
+{
+ int value;
+
+ value = get_bat_info(TEMPERATURE);
+
+ return value * 1000;
+}
+
+static int get_battery_current(void)
+{
+ s16 value;
+
+ value = get_bat_info(CURRENT);
+
+ return -value;
+}
+
+static int get_battery_voltage(void)
+{
+ int value;
+
+ value = get_bat_info(VOLTAGE);
+
+ return value;
+}
+
+static inline char *get_manufacturer(void)
+{
+ return (ec_read(REG_BAT_VENDOR) == FLAG_BAT_VENDOR_SANYO) ? "SANYO" :
+ "SIMPLO";
+}
+
+static int yeeloong_get_bat_props(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ switch (psp) {
+ /* Fixed information */
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ /* mV -> µV */
+ RET = get_bat_info(DESIGN_VOL) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ /* mAh->µAh */
+ RET = get_bat_info(DESIGN_CAP) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ /* µAh */
+ RET = get_bat_info(FULLCHG_CAP) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = get_manufacturer();
+ break;
+ /* Dynamic information */
+ case POWER_SUPPLY_PROP_PRESENT:
+ RET = is_bat_in();
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ /* mA -> µA */
+ RET = is_bat_in() ? get_battery_current() * 1000 : 0;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ /* mV -> µV */
+ RET = is_bat_in() ? get_battery_voltage() * 1000 : 0;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ /* Celcius */
+ RET = is_bat_in() ? get_battery_temp() : 0;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ RET = is_bat_in() ? get_bat_info(RELATIVE_CAP) : 0;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+ {
+ int status;
+
+ if (!is_bat_in()) {
+ RET = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+ break;
+ }
+
+ status = get_bat_status();
+ RET = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+
+ if (unlikely(status & BIT_BAT_STATUS_DESTROY)) {
+ RET = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+ break;
+ }
+
+ if (status & BIT_BAT_STATUS_LOW)
+ RET = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+ else if (status & BIT_BAT_STATUS_FULL)
+ RET = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+ else {
+ int curr_cap;
+
+ curr_cap = get_bat_info(RELATIVE_CAP);
+
+ if (curr_cap >= BAT_CAP_HIGH)
+ RET = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
+ else if (curr_cap <= BAT_CAP_CRITICAL)
+ RET = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+ }
+
+ } break;
+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
+ /* seconds */
+ RET = is_bat_in() ?
+ (get_bat_info(RELATIVE_CAP) - 3) * 54 + 142
+ : 0;
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ {
+ int charge = ec_read(REG_BAT_CHARGE);
+
+ if (charge & FLAG_BAT_CHARGE_DISCHARGE)
+ RET = POWER_SUPPLY_STATUS_DISCHARGING;
+ else if (charge & FLAG_BAT_CHARGE_CHARGE)
+ RET = POWER_SUPPLY_STATUS_CHARGING;
+ else
+ RET = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ {
+ int status;
+
+ if (!is_bat_in()) {
+ RET = POWER_SUPPLY_HEALTH_UNKNOWN;
+ break;
+ }
+
+ status = get_bat_status();
+
+ RET = POWER_SUPPLY_HEALTH_GOOD;
+ if (status & (BIT_BAT_STATUS_DESTROY |
+ BIT_BAT_STATUS_LOW))
+ RET = POWER_SUPPLY_HEALTH_DEAD;
+ if (ec_read(REG_BAT_CHARGE_STATUS) &
+ BIT_BAT_CHARGE_STATUS_OVERTEMP)
+ RET = POWER_SUPPLY_HEALTH_OVERHEAT;
+ }
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW: /* 1/100(%)*1000 µAh */
+ RET = get_bat_info(RELATIVE_CAP) *
+ get_bat_info(FULLCHG_CAP) * 10;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+#undef RET
+
+static enum power_supply_property yeeloong_bat_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static const struct power_supply_desc yeeloong_bat_desc = {
+ .name = "yeeloong-bat",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = yeeloong_bat_props,
+ .num_properties = ARRAY_SIZE(yeeloong_bat_props),
+ .get_property = yeeloong_get_bat_props,
+};
+
+static int ac_bat_initialized;
+
+static int yeeloong_bat_init(void)
+{
+ yeeloong_ac = power_supply_register(NULL, &yeeloong_ac_desc, NULL);
+ if (IS_ERR(yeeloong_ac))
+ return PTR_ERR(yeeloong_ac);
+ yeeloong_bat = power_supply_register(NULL, &yeeloong_bat_desc, NULL);
+ if (IS_ERR(yeeloong_bat)) {
+ power_supply_unregister(yeeloong_ac);
+ return PTR_ERR(yeeloong_bat);
+ }
+ ac_bat_initialized = 1;
+
+ return 0;
+}
+
+static void yeeloong_bat_exit(void)
+{
+ if (ac_bat_initialized) {
+ ac_bat_initialized = 0;
+
+ power_supply_unregister(yeeloong_ac);
+ power_supply_unregister(yeeloong_bat);
+ }
+}
+/* hwmon subdriver */
+
+#define MIN_FAN_SPEED 0
+#define MAX_FAN_SPEED 3
+
+static int get_fan_pwm_enable(void)
+{
+ int level, mode;
+
+ level = ec_read(REG_FAN_SPEED_LEVEL);
+ mode = ec_read(REG_FAN_AUTO_MAN_SWITCH);
+
+ if (level == MAX_FAN_SPEED && mode == BIT_FAN_MANUAL)
+ mode = 0;
+ else if (mode == BIT_FAN_MANUAL)
+ mode = 1;
+ else
+ mode = 2;
+
+ return mode;
+}
+
+static void set_fan_pwm_enable(int mode)
+{
+ switch (mode) {
+ case 0:
+ /* fullspeed */
+ ec_write(REG_FAN_AUTO_MAN_SWITCH, BIT_FAN_MANUAL);
+ ec_write(REG_FAN_SPEED_LEVEL, MAX_FAN_SPEED);
+ break;
+ case 1:
+ ec_write(REG_FAN_AUTO_MAN_SWITCH, BIT_FAN_MANUAL);
+ break;
+ case 2:
+ ec_write(REG_FAN_AUTO_MAN_SWITCH, BIT_FAN_AUTO);
+ break;
+ default:
+ break;
+ }
+}
+
+static int get_fan_pwm(void)
+{
+ return ec_read(REG_FAN_SPEED_LEVEL);
+}
+
+static void set_fan_pwm(int value)
+{
+ int mode;
+
+ mode = ec_read(REG_FAN_AUTO_MAN_SWITCH);
+ if (mode != BIT_FAN_MANUAL)
+ return;
+
+ value = clamp_val(value, 0, 3);
+
+ /* We must ensure the fan is on */
+ if (value > 0)
+ ec_write(REG_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)
+{
+ s8 value;
+
+ value = ec_read(REG_TEMPERATURE_VALUE);
+
+ return value * 1000;
+}
+
+static int get_cpu_temp_max(void)
+{
+ return 60 * 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 ssize_t store_sys_hwmon(void (*set) (int), const char *buf, size_t count)
+{
+ int ret;
+ unsigned long value;
+
+ if (!count)
+ return 0;
+
+ ret = kstrtoul(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ set(value);
+
+ return count;
+}
+
+static ssize_t show_sys_hwmon(int (*get) (void), char *buf)
+{
+ return sprintf(buf, "%d\n", get());
+}
+
+#define CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
+ static ssize_t show_##_name(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+ return show_sys_hwmon(_set, buf); \
+ } \
+ static ssize_t store_##_name(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+ { \
+ return store_sys_hwmon(_get, buf, count); \
+ } \
+ static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0)
+
+CREATE_SENSOR_ATTR(fan1_input, 0444, get_fan_rpm, NULL);
+CREATE_SENSOR_ATTR(pwm1, 0444 | 0644, get_fan_pwm, set_fan_pwm);
+CREATE_SENSOR_ATTR(pwm1_enable, 0444 | 0644, get_fan_pwm_enable,
+ set_fan_pwm_enable);
+CREATE_SENSOR_ATTR(temp1_input, 0444, get_cpu_temp, NULL);
+CREATE_SENSOR_ATTR(temp1_max, 0444, get_cpu_temp_max, NULL);
+CREATE_SENSOR_ATTR(temp2_input, 0444, get_battery_temp, NULL);
+CREATE_SENSOR_ATTR(temp2_max_alarm, 0444, get_battery_temp_alarm, NULL);
+CREATE_SENSOR_ATTR(curr1_input, 0444, get_battery_current, NULL);
+CREATE_SENSOR_ATTR(in1_input, 0444, 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, 0444, show_name, NULL, 0);
+
+static struct attribute *hwmon_attributes[] = {
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_name.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group hwmon_attribute_group = {
+ .attrs = hwmon_attributes
+};
+
+static struct device *yeeloong_hwmon_dev;
+
+static int yeeloong_hwmon_init(void)
+{
+ int ret;
+
+ yeeloong_hwmon_dev = hwmon_device_register(NULL);
+ if (IS_ERR(yeeloong_hwmon_dev)) {
+ 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(2);
+
+ return 0;
+}
+
+static void yeeloong_hwmon_exit(void)
+{
+ if (yeeloong_hwmon_dev) {
+ sysfs_remove_group(&yeeloong_hwmon_dev->kobj,
+ &hwmon_attribute_group);
+ hwmon_device_unregister(yeeloong_hwmon_dev);
+ yeeloong_hwmon_dev = NULL;
+ }
+}
+
+/* video output controller */
+
+
+#define LCD 0
+#define CRT 1
+
+static void display_vo_set(int display, int on)
+{
+ int addr;
+ unsigned long value;
+
+ addr = (display == LCD) ? 0x31 : 0x21;
+
+ outb(addr, 0x3c4);
+ value = inb(0x3c5);
+
+ if (display == LCD)
+ value |= (on ? 0x03 : 0x02);
+ else {
+ if (on)
+ clear_bit(7, &value);
+ else
+ set_bit(7, &value);
+ }
+
+ outb(addr, 0x3c4);
+ outb(value, 0x3c5);
+}
+
+
+
+/* hotkey subdriver */
+
+static struct input_dev *yeeloong_hotkey_dev;
+
+static const struct key_entry yeeloong_keymap[] = {
+ {KE_SW, EVENT_LID, {SW_LID} },
+ /* Fn + ESC */
+ {KE_KEY, EVENT_CAMERA, {KEY_CAMERA} },
+ /* Fn + F1 */
+ {KE_KEY, EVENT_SLEEP, {KEY_SLEEP} },
+ /* Fn + F2 */
+ {KE_KEY, EVENT_DISPLAYTOGGLE, {KEY_DISPLAYTOGGLE} },
+ /* Fn + F3 */
+ {KE_KEY, EVENT_SWITCHVIDEOMODE, {KEY_SWITCHVIDEOMODE} },
+ /* Fn + F4 */
+ {KE_KEY, EVENT_AUDIO_MUTE, {KEY_MUTE} },
+ /* Fn + F5 */
+ {KE_KEY, EVENT_WLAN, {KEY_WLAN} },
+ /* Fn + up */
+ {KE_KEY, EVENT_DISPLAY_BRIGHTNESS, {KEY_BRIGHTNESSUP} },
+ /* Fn + down */
+ {KE_KEY, EVENT_DISPLAY_BRIGHTNESS, {KEY_BRIGHTNESSDOWN} },
+ /* Fn + right */
+ {KE_KEY, EVENT_AUDIO_VOLUME, {KEY_VOLUMEUP} },
+ /* Fn + left */
+ {KE_KEY, EVENT_AUDIO_VOLUME, {KEY_VOLUMEDOWN} },
+ {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 == 0) || (status < old_brightness_status))
+ ke++;
+ old_brightness_status = status;
+ break;
+ case EVENT_AUDIO_VOLUME:
+ if ((status == 0) || (status < old_volume_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 void yeeloong_vo_set(int lcd_status, int crt_status)
+{
+ display_vo_set(LCD, lcd_status);
+ display_vo_set(CRT, crt_status);
+}
+
+static int crt_detect_handler(int status)
+{
+ if (status)
+ yeeloong_vo_set(ON, ON);
+ else
+ yeeloong_vo_set(ON, OFF);
+
+ return status;
+}
+
+static int displaytoggle_handler(int status)
+{
+ /* EC(>=PQ1D26) does this job for us, we can not do it again,
+ * otherwise, the brightness will not resume to the normal level!
+ */
+ if (ec_version_before("EC_VER=PQ1D26"))
+ display_vo_set(LCD, status);
+
+ return status;
+}
+
+static int switchvideomode_handler(int status)
+{
+ static int video_output_status;
+
+ /* Only enable switch video output button
+ * when CRT is connected
+ */
+ if (ec_read(REG_CRT_DETECT) == OFF)
+ 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_vo_set(ON, ON);
+ break;
+ case 2:
+ yeeloong_vo_set(OFF, ON);
+ break;
+ case 3:
+ yeeloong_vo_set(OFF, OFF);
+ break;
+ case 4:
+ yeeloong_vo_set(ON, OFF);
+ break;
+ default:
+ /* Ensure LCD is on */
+ display_vo_set(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;
+}
+
+static int ac_bat_handler(int status)
+{
+ if (ac_bat_initialized) {
+ power_supply_changed(yeeloong_ac);
+ power_supply_changed(yeeloong_bat);
+ }
+ return status;
+}
+
+static void do_event_action(int event)
+{
+ sci_handler handler;
+ int reg, status;
+ struct key_entry *ke;
+
+ reg = 0;
+ handler = NULL;
+ status = 0;
+
+ switch (event) {
+ case EVENT_LID:
+ reg = REG_LID_DETECT;
+ break;
+ case EVENT_SWITCHVIDEOMODE:
+ handler = switchvideomode_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_DISPLAYTOGGLE:
+ reg = REG_DISPLAY_LCD;
+ handler = displaytoggle_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_AC_BAT:
+ handler = ac_bat_handler;
+ break;
+ default:
+ break;
+ }
+
+ if (reg != 0)
+ status = ec_read(reg);
+
+ if (handler != NULL)
+ status = handler(status);
+
+ pr_debug("%s: event: %d status: %d\n", __func__, event, status);
+
+ /* Report current key to user-space */
+ ke = get_event_key_entry(event, status);
+ 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 (irq != SCI_IRQ_NUM)
+ 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 */
+ mdelay(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
+ */
+
+ /* input enable */
+ outl(0x00000800, (gpio_base | 0xA0));
+ /* revert the input */
+ outl(0x00000800, (gpio_base | 0xA4));
+ /* event-int enable */
+ outl(0x00000800, (gpio_base | 0xB8));
+
+ return 0;
+}
+
+static int yeeloong_hotkey_init(void)
+{
+ int ret;
+
+ ret = sci_irq_init();
+ if (ret)
+ return -EFAULT;
+
+ ret = request_threaded_irq(SCI_IRQ_NUM, NULL, &sci_irq_handler,
+ IRQF_ONESHOT, "sci", NULL);
+ if (ret)
+ return -EFAULT;
+
+ yeeloong_hotkey_dev = input_allocate_device();
+
+ if (!yeeloong_hotkey_dev) {
+ free_irq(SCI_IRQ_NUM, NULL);
+ 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) {
+ input_free_device(yeeloong_hotkey_dev);
+ return ret;
+ }
+
+ /* Update the current status of LID */
+ report_lid_switch(ON);
+
+#ifdef CONFIG_LOONGSON_SUSPEND
+ /* Install the real yeeloong_report_lid_status for pm.c */
+ yeeloong_report_lid_status = report_lid_switch;
+#endif
+
+ return 0;
+}
+
+static void yeeloong_hotkey_exit(void)
+{
+ /* Free irq */
+ free_irq(SCI_IRQ_NUM, NULL);
+
+#ifdef CONFIG_LOONGSON_SUSPEND
+ /* Uninstall yeeloong_report_lid_status for pm.c */
+ if (yeeloong_report_lid_status == report_lid_switch)
+ yeeloong_report_lid_status = NULL;
+#endif
+
+ if (yeeloong_hotkey_dev) {
+ input_unregister_device(yeeloong_hotkey_dev);
+ yeeloong_hotkey_dev = NULL;
+ }
+}
+
+#ifdef CONFIG_PM
+static void usb_ports_set(int status)
+{
+ status = !!status;
+
+ ec_write(REG_USB0_FLAG, status);
+ ec_write(REG_USB1_FLAG, status);
+ ec_write(REG_USB2_FLAG, status);
+}
+
+static int yeeloong_suspend(struct device *dev)
+
+{
+ if (ec_version_before("EC_VER=PQ1D27"))
+ display_vo_set(LCD, OFF);
+ display_vo_set(CRT, OFF);
+ usb_ports_set(OFF);
+
+ return 0;
+}
+
+static int yeeloong_resume(struct device *dev)
+{
+ int ret;
+
+ if (ec_version_before("EC_VER=PQ1D27"))
+ display_vo_set(LCD, ON);
+ display_vo_set(CRT, ON);
+ usb_ports_set(ON);
+
+ ret = sci_irq_init();
+ if (ret)
+ return -EFAULT;
+
+ return 0;
+}
+
+static const SIMPLE_DEV_PM_OPS(yeeloong_pm_ops, yeeloong_suspend,
+ yeeloong_resume);
+#endif
+
+static struct platform_device_id platform_device_ids[] = {
+ {
+ .name = "yeeloong_laptop",
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(platform, platform_device_ids);
+
+static struct platform_driver platform_driver = {
+ .driver = {
+ .name = "yeeloong_laptop",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &yeeloong_pm_ops,
+#endif
+ },
+ .id_table = platform_device_ids,
+};
+
+static int __init yeeloong_init(void)
+{
+ int ret;
+
+ if (mips_machtype != MACH_LEMOTE_YL2F89) {
+ pr_err("Unsupported system.\n");
+ return -ENODEV;
+ }
+
+ 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;
+ }
+
+ ret = yeeloong_backlight_init();
+ if (ret) {
+ pr_err("Fail to register yeeloong backlight driver.\n");
+ yeeloong_backlight_exit();
+ return ret;
+ }
+
+ ret = yeeloong_bat_init();
+ if (ret) {
+ pr_err("Fail to register yeeloong battery driver.\n");
+ yeeloong_bat_exit();
+ return ret;
+ }
+
+ ret = yeeloong_hwmon_init();
+ if (ret) {
+ pr_err("Fail to register yeeloong hwmon driver.\n");
+ yeeloong_hwmon_exit();
+ 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_hwmon_exit();
+ yeeloong_bat_exit();
+ yeeloong_backlight_exit();
+ 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]>; Liu Junliang <[email protected]>");
+MODULE_DESCRIPTION("YeeLoong laptop driver");
+MODULE_LICENSE("GPL");
--
2.15.0
Since lemote-2f/marchtype.c need to get cmdline from loongson.h
this patch simply copy kernel command line from arcs_cmdline
to fix that issue
Signed-off-by: Jiaxun Yang <[email protected]>
---
arch/mips/include/asm/mach-loongson64/loongson.h | 6 ++++++
arch/mips/loongson64/common/cmdline.c | 7 +++++++
2 files changed, 13 insertions(+)
diff --git a/arch/mips/include/asm/mach-loongson64/loongson.h b/arch/mips/include/asm/mach-loongson64/loongson.h
index c68c0cc879c6..1edf3a484e6a 100644
--- a/arch/mips/include/asm/mach-loongson64/loongson.h
+++ b/arch/mips/include/asm/mach-loongson64/loongson.h
@@ -45,6 +45,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/loongson64/common/cmdline.c b/arch/mips/loongson64/common/cmdline.c
index 01fbed137028..49e172184e15 100644
--- a/arch/mips/loongson64/common/cmdline.c
+++ b/arch/mips/loongson64/common/cmdline.c
@@ -21,6 +21,11 @@
#include <loongson.h>
+/* the kernel command line copied from arcs_cmdline */
+#include <linux/export.h>
+char loongson_cmdline[COMMAND_LINE_SIZE];
+EXPORT_SYMBOL(loongson_cmdline);
+
void __init prom_init_cmdline(void)
{
int prom_argc;
@@ -45,4 +50,6 @@ void __init prom_init_cmdline(void)
}
prom_init_machtype();
+ /* copy arcs_cmdline into loongson_cmdline */
+ strncpy(loongson_cmdline, arcs_cmdline, COMMAND_LINE_SIZE);
}
--
2.15.0
Hi Jiaxun,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on linus/master]
[also build test ERROR on v4.15-rc2 next-20171206]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Jiaxun-Yang/MIPS-Lonngson64-Copy-kernel-command-line-from-arcs_cmdline/20171206-065745
config: mips-lemote2f_defconfig (attached as .config)
compiler: mips64el-linux-gnuabi64-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=mips
All errors (new ones prefixed by >>):
arch/mips/loongson64/lemote-2f/platform.c: In function 'lemote2f_platform_init':
>> arch/mips/loongson64/lemote-2f/platform.c:24:9: error: return makes integer from pointer without a cast [-Werror=int-conversion]
return platform_device_register_simple("yeeloong_laptop", -1, NULL, 0);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
vim +24 arch/mips/loongson64/lemote-2f/platform.c
18
19 static int __init lemote2f_platform_init(void)
20 {
21 if (mips_machtype != MACH_LEMOTE_YL2F89)
22 return -ENODEV;
23
> 24 return platform_device_register_simple("yeeloong_laptop", -1, NULL, 0);
25 }
26
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation