mirror of
https://github.com/linux-thinkpad/tp_smapi.git
synced 2024-10-26 07:34:36 +03:00
import of tp-smapi version 0.27
This commit is contained in:
parent
840646759a
commit
ee4b50c735
17
CHANGES
17
CHANGES
@ -1,5 +1,22 @@
|
||||
Change history for tp_smapi:
|
||||
|
||||
0.27 2006-08-05
|
||||
---------------------
|
||||
Visible changes:
|
||||
- Renamed tp_base.* to thinkpad_ec.*.
|
||||
NOTE: If you have manual modprobe commands or configuration mentioning
|
||||
tp_base, you'll need to change them.
|
||||
- hdaps: All HDAPS-equippedThinkPads should be now be automatically
|
||||
recognized. Whitelisting is needed only for changing the axis
|
||||
configuration.
|
||||
- hdaps: Removed 'force' module parameter, it's no longer needed.
|
||||
|
||||
Internal changes:
|
||||
- Renamed all tp_controller_* functions to thinkpad_ec_*.
|
||||
- Small change to invalidation in hdaps_device_init().
|
||||
- Separate Kconfig changes into separate patches.
|
||||
- Clean up generated patch format a bit.
|
||||
|
||||
0.26 2006-08-04
|
||||
---------------------
|
||||
- The modified hdaps.c is now included as a full file (based on linux git)
|
||||
|
40
Makefile
40
Makefile
@ -7,7 +7,7 @@ MOD_DIR := /lib/modules/$(KVER)/kernel
|
||||
PWD := $(shell pwd)
|
||||
IDIR := include/linux
|
||||
TP_DIR := drivers/firmware
|
||||
TP_MODULES := tp_base.o tp_smapi.o
|
||||
TP_MODULES := thinkpad_ec.o tp_smapi.o
|
||||
SHELL := /bin/bash
|
||||
|
||||
ifeq ($(HDAPS),1)
|
||||
@ -31,13 +31,13 @@ export TP_MODULES
|
||||
|
||||
default: modules
|
||||
|
||||
# Build the modules tp_base.ko, tp_smapi.ko and (if HDAPS=1) hdaps.ko
|
||||
# Build the modules thinkpad_ec.ko, tp_smapi.ko and (if HDAPS=1) hdaps.ko
|
||||
modules: $(KSRC) dmi_ec_oem_string.h $(patsubst %.o,%.c,$(TP_MODULES))
|
||||
$(MAKE) -C $(KSRC) M=$(PWD) modules
|
||||
|
||||
clean:
|
||||
rm -f tp_smapi.mod.* tp_smapi.o tp_smapi.ko .tp_smapi.*.cmd
|
||||
rm -f tp_base.mod.* tp_base.o tp_base.ko .tp_base.*.cmd
|
||||
rm -f thinkpad_ec.mod.* thinkpad_ec.o thinkpad_ec.ko .thinkpad_ec.*.cmd
|
||||
rm -f hdaps.mod.* hdaps.o hdaps.ko .hdaps.*.cmd
|
||||
rm -f *~ diff/*~ *.orig diff/*.orig *.rej diff/*.rej
|
||||
rm -f tp_smapi-*-for-*.patch
|
||||
@ -46,7 +46,7 @@ clean:
|
||||
|
||||
load: check_hdaps unload modules
|
||||
@( [ `id -u` == 0 ] || { echo "Must be root to load modules"; exit 1; } )
|
||||
{ insmod ./tp_base.ko debug=$(DEBUG) &&\
|
||||
{ insmod ./thinkpad_ec.ko debug=$(DEBUG) &&\
|
||||
insmod ./tp_smapi.ko debug=$(DEBUG) &&\
|
||||
$(LOAD_HDAPS); }; :
|
||||
@echo -e '\nRecent dmesg output:' ; dmesg | tail -10
|
||||
@ -55,7 +55,8 @@ unload:
|
||||
@( [ `id -u` == 0 ] || { echo "Must be root to unload modules"; exit 1; } )
|
||||
if lsmod | grep -q '^hdaps '; then rmmod hdaps; fi
|
||||
if lsmod | grep -q '^tp_smapi '; then rmmod tp_smapi; fi
|
||||
if lsmod | grep -q '^tp_base '; then rmmod tp_base; fi
|
||||
if lsmod | grep -q '^thinkpad_ec '; then rmmod thinkpad_ec; fi
|
||||
if lsmod | grep -q '^tp_base '; then rmmod tp_base; fi # old thinkpad_ec
|
||||
|
||||
check_hdaps:
|
||||
ifneq ($(HDAPS),1)
|
||||
@ -67,9 +68,11 @@ endif
|
||||
|
||||
install: modules
|
||||
@( [ `id -u` == 0 ] || { echo "Must be root to install modules"; exit 1; } )
|
||||
rm -f $(MOD_DIR)/$(TP_DIR)/{tp_base,tp_smapi}.ko
|
||||
rm -f $(MOD_DIR)/$(TP_DIR)/{thinkpad_ec,tp_smapi,tp_base}.ko
|
||||
rm -f $(MOD_DIR)/extra/{thinkpad_ec,tp_smapi,tp_base}.ko
|
||||
ifeq ($(HDAPS),1)
|
||||
rm -f $(MOD_DIR)/drivers/hwmon/hdaps.ko
|
||||
rm -f $(MOD_DIR)/extra/hdaps.ko
|
||||
endif
|
||||
$(MAKE) -C $(KSRC) M=$(PWD) modules_install
|
||||
depmod -a
|
||||
@ -92,7 +95,7 @@ dmi_ec_oem_string.h: $(KSRC)/include/linux/dmi.h
|
||||
echo '/* This is machine-specific DMI information. */' > $@
|
||||
echo '#define DMI_EC_OEM_STRING_KLUDGE "$(DMI_EC_OEM_STRING)"' >> $@
|
||||
else
|
||||
NO_DMI_KLUDGE_DIFF=$(PWD)/diff/tp_base-no-dmi-kludge.diff
|
||||
NO_DMI_KLUDGE_DIFF=$(PWD)/diff/thinkpad_ec-no-dmi-kludge.diff
|
||||
GEN_DMI_DECODE_PATCH=cat /dev/null
|
||||
dmi_ec_oem_string.h: $(KSRC)/include/linux/dmi.h
|
||||
echo '/* Intentionally empty. You have proper DMI OEM Strings. */' > $@
|
||||
@ -102,12 +105,13 @@ endif
|
||||
# Generate a stand-alone kernel patch
|
||||
|
||||
TP_VER := ${shell sed -ne 's/^\#define TP_VERSION \"\(.*\)\"/\1/gp' tp_smapi.c}
|
||||
ORG := linux-$(KVER)-orig
|
||||
NEW := linux-$(KVER)-patched
|
||||
ORG := a
|
||||
NEW := b
|
||||
PATCH := tp_smapi-$(TP_VER)-for-$(KVER).patch
|
||||
|
||||
BASE_IN_PATCH := 1
|
||||
SMAPI_IN_PATCH := 1
|
||||
HDAPS_IN_PATCH := 1
|
||||
|
||||
patch:
|
||||
TMPDIR=`mktemp -d /tmp/tp_smapi-patch.XXXXXX` &&\
|
||||
@ -121,18 +125,22 @@ patch:
|
||||
cp -r $(ORG) $(NEW) &&\
|
||||
\
|
||||
if [ "$(BASE_IN_PATCH)" == 1 ]; then \
|
||||
patch --no-backup-if-mismatch -s -d $(NEW) -i $(PWD)/diff/Kconfig-tp_base.diff -p1 &&\
|
||||
cp $(PWD)/tp_base.c $(NEW)/$(TP_DIR)/tp_base.c &&\
|
||||
cp $(PWD)/tp_base.h $(NEW)/$(IDIR)/tp_base.h &&\
|
||||
patch --no-backup-if-mismatch -s -d $(NEW) -i $(PWD)/diff/Kconfig-thinkpad_ec.diff -p1 &&\
|
||||
cp $(PWD)/thinkpad_ec.c $(NEW)/$(TP_DIR)/thinkpad_ec.c &&\
|
||||
cp $(PWD)/thinkpad_ec.h $(NEW)/$(IDIR)/thinkpad_ec.h &&\
|
||||
patch --no-backup-if-mismatch -s -d $(NEW)/$(TP_DIR) -i $(PWD)/diff/thinkpad_ec-no-dmi-kludge.diff -p1 &&\
|
||||
sed -i -e '$$aobj-$$(CONFIG_THINKPAD_EC) += thinkpad_ec.o' $(NEW)/$(TP_DIR)/Makefile \
|
||||
; fi &&\
|
||||
\
|
||||
if [ "$(HDAPS_IN_PATCH)" == 1 ]; then \
|
||||
cp $(PWD)/hdaps.c $(NEW)/drivers/hwmon/ &&\
|
||||
sed -i -e '$$aobj-$$(CONFIG_TP_BASE) += tp_base.o' $(NEW)/$(TP_DIR)/Makefile \
|
||||
patch --no-backup-if-mismatch -s -d $(NEW) -i $(PWD)/diff/Kconfig-hdaps-select-thinkpad_ec -p1 \
|
||||
; fi &&\
|
||||
\
|
||||
if [ "$(SMAPI_IN_PATCH)" == 1 ]; then \
|
||||
sed -i -e '$$aobj-$$(CONFIG_TP_SMAPI) += tp_smapi.o' $(NEW)/$(TP_DIR)/Makefile &&\
|
||||
cp $(PWD)/tp_smapi.c $(NEW)/$(TP_DIR)/tp_smapi.c &&\
|
||||
patch --no-backup-if-mismatch -s -d $(NEW)/$(TP_DIR) -i $(PWD)/diff/tp_smapi-no_cd.diff -p1 &&\
|
||||
patch --no-backup-if-mismatch -s -d $(NEW)/$(TP_DIR) -i $(PWD)/diff/tp_base-no-dmi-kludge.diff -p1 &&\
|
||||
patch --no-backup-if-mismatch -s -d $(NEW) -i $(PWD)/diff/Kconfig-tp_smapi.diff -p1 &&\
|
||||
mkdir -p $(NEW)/Documentation &&\
|
||||
perl -0777 -pe 's/\n(Installation\n---+|Conflict with HDAPS\n---+|Files in this package\n---+|Setting and getting CD-ROM speed:\n).*?\n(?=[^\n]*\n-----)/\n/gs' $(PWD)/README > $(NEW)/Documentation/tp_smapi.txt \
|
||||
@ -141,7 +149,7 @@ patch:
|
||||
{ diff -dNurp $(ORG) $(NEW) > patch \
|
||||
|| [ $$? -lt 2 ]; } &&\
|
||||
$(GEN_DMI_DECODE_PATCH) >> patch &&\
|
||||
{ diffstat patch; echo; echo; cat patch; } \
|
||||
{ echo "Generated for $(KVER) in $(KSRC)"; echo; diffstat patch; echo; echo; cat patch; } \
|
||||
> $(PWD)/${PATCH} &&\
|
||||
rm -r $$TMPDIR
|
||||
@echo -e "\nPatch file created:\n ${PATCH}"
|
||||
@ -152,7 +160,7 @@ patch:
|
||||
|
||||
set-version:
|
||||
perl -i -pe 's/^(tp_smapi version ).*/$${1}$(VER)/' README
|
||||
perl -i -pe 's/^(#define TP_VERSION ").*/$${1}$(VER)"/' tp_base.c tp_smapi.c
|
||||
perl -i -pe 's/^(#define TP_VERSION ").*/$${1}$(VER)"/' thinkpad_ec.c tp_smapi.c
|
||||
|
||||
else
|
||||
#####################################################################
|
||||
|
16
README
16
README
@ -1,4 +1,4 @@
|
||||
tp_smapi version 0.26
|
||||
tp_smapi version 0.27
|
||||
IBM ThinkPad hardware functions driver
|
||||
|
||||
Author: Shem Multinymous <multinymous@gmail.com>
|
||||
@ -51,7 +51,7 @@ To prepare a stand-alone patch against another kernel, follow this example:
|
||||
To delete all autogenerated files:
|
||||
# make clean
|
||||
|
||||
Append "DEBUG=1" to "make load" to load tp_base and tp_smapi with debug
|
||||
Append "DEBUG=1" to "make load" to load thinkpad_ec and tp_smapi with debug
|
||||
dmesg output enabled.
|
||||
|
||||
The original kernel tree is never modified by any these commands.
|
||||
@ -61,7 +61,7 @@ The /lib/modules directory is modified only by "make install".
|
||||
Module parameters
|
||||
-----------------
|
||||
|
||||
tp_base module:
|
||||
thinkpad_ec module:
|
||||
debug=1 enables verbose dmesg output.
|
||||
|
||||
tp_smapi module:
|
||||
@ -217,7 +217,7 @@ Bug reporting
|
||||
|
||||
Mail <multinymous@gmail.com>. Please include:
|
||||
* Details about your model,
|
||||
* Relevant "dmesg" output. Make sure tp_base and tp_smapi are loaded with
|
||||
* Relevant "dmesg" output. Make sure thinkpad_ec and tp_smapi are loaded with
|
||||
the "debug=1" parameter (e.g., use "make load HDAPS=1 DEBUG=1").
|
||||
* Output of "dmidecode | grep -C5 Product"
|
||||
* Does the failed functionality works under Windows?
|
||||
@ -234,12 +234,12 @@ Makefile
|
||||
Makefile (see "Installation" above).
|
||||
tp_smapi.c
|
||||
tp_smapi driver module (main code).
|
||||
tp_base.*
|
||||
tp_base driver module (coordinates hardware access between tp_smapi and
|
||||
thinkpad_ec.*
|
||||
thinkpad_ec driver module (coordinates hardware access between tp_smapi and
|
||||
hdaps)
|
||||
hdaps.c
|
||||
Modified version of hdaps.c driver from mainline kernel, patched to use
|
||||
tp_base and several other improvements.
|
||||
thinkpad_ec and several other improvements.
|
||||
diff/dmi-decode-and-save-oem-string-information
|
||||
A patch from the kernel -mm series, needed for hardware detection.
|
||||
If you don't have it, "make load" and "make install" will use a workaround
|
||||
@ -298,5 +298,5 @@ In addition, the embedded controller on ThinkPad laptops has a non-standard
|
||||
interface at IO ports 0x1600-0x161F (mapped to LCP channel 3 of the H8S chip).
|
||||
The interface provides various system management services (currently known:
|
||||
battery information and accelerometer readouts). For more information see the
|
||||
tp_base modul and the H8S hardware documentation:
|
||||
thinkpad_ec modul and the H8S hardware documentation:
|
||||
http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
|
||||
|
10
diff/Kconfig-hdaps-select-thinkpad_ec
Normal file
10
diff/Kconfig-hdaps-select-thinkpad_ec
Normal file
@ -0,0 +1,10 @@
|
||||
--- a/drivers/hwmon/Kconfig
|
||||
+++ b/drivers/hwmon/Kconfig
|
||||
@@ -494,6 +494,7 @@
|
||||
config SENSORS_HDAPS
|
||||
tristate "IBM Hard Drive Active Protection System (hdaps)"
|
||||
depends on HWMON && INPUT && X86
|
||||
+ select THINKPAD_EC
|
||||
default n
|
||||
help
|
||||
This driver provides support for the IBM Hard Drive Active Protection
|
10
diff/Kconfig-thinkpad_ec.diff
Normal file
10
diff/Kconfig-thinkpad_ec.diff
Normal file
@ -0,0 +1,10 @@
|
||||
--- a/drivers/firmware/Kconfig
|
||||
+++ b/drivers/firmware/Kconfig
|
||||
@@ -85,4 +85,7 @@
|
||||
Say Y or M here to enable the driver for use by Dell systems
|
||||
management software such as Dell OpenManage.
|
||||
|
||||
+config THINKPAD_EC
|
||||
+ tristate
|
||||
+
|
||||
endmenu
|
@ -1,20 +0,0 @@
|
||||
--- linux-2.6.15-rc5-orig/drivers/firmware/Kconfig 2005-12-13 19:13:57.000000000 +0200
|
||||
+++ linux-2.6.15-rc5-patched/drivers/firmware/Kconfig 2005-12-21 00:35:13.000000000 +0200
|
||||
@@ -85,4 +85,7 @@
|
||||
Say Y or M here to enable the driver for use by Dell systems
|
||||
management software such as Dell OpenManage.
|
||||
|
||||
+config TP_BASE
|
||||
+ tristate
|
||||
+
|
||||
endmenu
|
||||
--- linux-2.6.15-rc5-orig/drivers/hwmon/Kconfig 2005-10-28 02:02:08.000000000 +0200
|
||||
+++ linux-2.6.15-rc5-patched/drivers/hwmon/Kconfig 2005-12-21 00:42:22.000000000 +0200
|
||||
@@ -437,6 +437,7 @@
|
||||
config SENSORS_HDAPS
|
||||
tristate "IBM Hard Drive Active Protection System (hdaps)"
|
||||
depends on HWMON && INPUT && X86
|
||||
+ select TP_BASE
|
||||
default n
|
||||
help
|
||||
This driver provides support for the IBM Hard Drive Active Protection
|
@ -1,13 +1,13 @@
|
||||
--- linux-2.6.15-rc5-orig/drivers/firmware/Kconfig 2005-12-13 19:13:57.000000000 +0200
|
||||
+++ linux-2.6.15-rc5-patched/drivers/firmware/Kconfig 2005-12-21 00:35:13.000000000 +0200
|
||||
@@ -88,4 +88,16 @@
|
||||
config TP_BASE
|
||||
config THINKPAD_EC
|
||||
tristate
|
||||
|
||||
+config TP_SMAPI
|
||||
+ tristate "ThinkPad SMAPI Support"
|
||||
+ depends on X86
|
||||
+ select TP_BASE
|
||||
+ select THINKPAD_EC
|
||||
+ default n
|
||||
+ help
|
||||
+ This adds SMAPI support on IBM ThinkPads, mostly used for battery
|
||||
|
@ -1,5 +1,5 @@
|
||||
--- orig/tp_base.c 2006-08-02 00:00:00.000000000 +0000
|
||||
+++ no-dmi-kludge/tp_base.c 2006-08-02 00:00:00.000000000 +0000
|
||||
--- orig/thinkpad_ec.c 2006-08-02 00:00:00.000000000 +0000
|
||||
+++ no-dmi-kludge/thinkpad_ec.c 2006-08-02 00:00:00.000000000 +0000
|
||||
@@ -402,9 +402,4 @@ static int __init check_for_embedded_con
|
||||
};
|
||||
-#include "dmi_ec_oem_string.h"
|
@ -59,7 +59,7 @@
|
||||
- * driver instead, and may get in the ATAPI driver's way.
|
||||
- */
|
||||
-
|
||||
-static int cd_speed_show(struct device *dev,
|
||||
-static int show_cd_speed(struct device *dev,
|
||||
- struct device_attribute *attr, char *buf)
|
||||
-{
|
||||
- int speed;
|
||||
@ -69,7 +69,7 @@
|
||||
- return sprintf(buf, "%d\n", speed);
|
||||
-}
|
||||
-
|
||||
-static int cd_speed_store(struct device *dev,
|
||||
-static int store_cd_speed(struct device *dev,
|
||||
- struct device_attribute *attr,
|
||||
- const char *buf, size_t count)
|
||||
-{
|
||||
@ -90,7 +90,7 @@
|
||||
-
|
||||
@@ -1235,3 +1149,0 @@
|
||||
-#ifdef PROVIDE_CD_SPEED
|
||||
-static DEVICE_ATTR(cd_speed, 0644, cd_speed_show, cd_speed_store);
|
||||
-static DEVICE_ATTR(cd_speed, 0644, show_cd_speed, store_cd_speed);
|
||||
-#endif
|
||||
@@ -1243,3 +1154,0 @@
|
||||
-#ifdef PROVIDE_CD_SPEED
|
||||
|
163
hdaps.c
163
hdaps.c
@ -33,11 +33,10 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/tp_base.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/thinkpad_ec.h>
|
||||
|
||||
/* Embedded controller accelerometer read command and its result: */
|
||||
static const struct tp_controller_row ec_accel_args =
|
||||
static const struct thinkpad_ec_row ec_accel_args =
|
||||
{ .mask=0x0001, .val={0x11} };
|
||||
#define EC_ACCEL_IDX_READOUTS 0x1 /* readouts included in this read */
|
||||
/* First readout, if READOUTS>=1: */
|
||||
@ -60,14 +59,12 @@ static const struct tp_controller_row ec_accel_args =
|
||||
|
||||
#define HDAPS_INPUT_FUZZ 4 /* input event threshold */
|
||||
#define HDAPS_INPUT_FLAT 4
|
||||
|
||||
#define KMACT_REMEMBER_PERIOD (HZ/10) /* keyboard/mouse persistance */
|
||||
|
||||
static struct timer_list hdaps_timer;
|
||||
static struct platform_device *pdev;
|
||||
static struct input_dev *hdaps_idev;
|
||||
static unsigned int hdaps_invert;
|
||||
static unsigned int hdaps_force;
|
||||
static int needs_calibration = 0;
|
||||
|
||||
/* Configuration: */
|
||||
@ -80,12 +77,12 @@ static int fake_data_mode = 0; /* Enable EC fake data mode? */
|
||||
/* Latest state readout: */
|
||||
static int pos_x, pos_y; /* position */
|
||||
static int temperature; /* temperature */
|
||||
static int stale_readout = 1; /* last read failed */
|
||||
static int stale_readout = 1; /* last read invalid */
|
||||
static int rest_x, rest_y; /* calibrated rest position */
|
||||
|
||||
/* Last time we saw keyboard and mouse activity: */
|
||||
u64 last_keyboard_jiffies = INITIAL_JIFFIES;
|
||||
u64 last_mouse_jiffies = INITIAL_JIFFIES;
|
||||
static u64 last_keyboard_jiffies = INITIAL_JIFFIES;
|
||||
static u64 last_mouse_jiffies = INITIAL_JIFFIES;
|
||||
|
||||
/* __hdaps_update - read current state and update global state variables.
|
||||
* Also prefetches the next read, to reduce udelay busy-waiting.
|
||||
@ -95,17 +92,17 @@ u64 last_mouse_jiffies = INITIAL_JIFFIES;
|
||||
static int __hdaps_update(int fast)
|
||||
{
|
||||
/* Read data: */
|
||||
struct tp_controller_row data;
|
||||
struct thinkpad_ec_row data;
|
||||
int ret;
|
||||
|
||||
data.mask = (1 << EC_ACCEL_IDX_READOUTS) | (1 << EC_ACCEL_IDX_KMACT) |
|
||||
(3 << EC_ACCEL_IDX_YPOS1) | (3 << EC_ACCEL_IDX_XPOS1) |
|
||||
(1 << EC_ACCEL_IDX_TEMP1) | (1 << EC_ACCEL_IDX_RETVAL);
|
||||
if (fast)
|
||||
ret = tp_controller_try_read_row(&ec_accel_args, &data);
|
||||
ret = thinkpad_ec_try_read_row(&ec_accel_args, &data);
|
||||
else
|
||||
ret = tp_controller_read_row(&ec_accel_args, &data);
|
||||
tp_controller_prefetch_row(&ec_accel_args); /* Prefetch even if error */
|
||||
ret = thinkpad_ec_read_row(&ec_accel_args, &data);
|
||||
thinkpad_ec_prefetch_row(&ec_accel_args); /* Prefetch even if error */
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -144,11 +141,10 @@ static int __hdaps_update(int fast)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* hdaps_read_pair - reads the values from a pair of ports, placing the values
|
||||
* in the given pointers. Returns zero on success. Can sleep.
|
||||
/* hdaps_update - read current state and update global state variables.
|
||||
* Also prefetches the next read, to reduce udelay busy-waiting.
|
||||
* Retries until timeout if the accelerometer is not in ready status (common).
|
||||
* Does internal locking.
|
||||
* Does its own locking.
|
||||
*/
|
||||
static int hdaps_update(void)
|
||||
{
|
||||
@ -156,11 +152,11 @@ static int hdaps_update(void)
|
||||
if (!stale_readout) /* already updated recently? */
|
||||
return 0;
|
||||
for (total=READ_TIMEOUT_MSECS; total>0; total-=RETRY_MSECS) {
|
||||
ret = tp_controller_lock();
|
||||
ret = thinkpad_ec_lock();
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = __hdaps_update(0);
|
||||
tp_controller_unlock();
|
||||
thinkpad_ec_unlock();
|
||||
|
||||
if (!ret)
|
||||
return 0;
|
||||
@ -176,10 +172,10 @@ static int hdaps_update(void)
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
static int hdaps_set_power(int on) {
|
||||
struct tp_controller_row args =
|
||||
struct thinkpad_ec_row args =
|
||||
{ .mask=0x0003, .val={0x14, on?0x01:0x00} };
|
||||
struct tp_controller_row data = { .mask = 0x8000 };
|
||||
int ret = tp_controller_read_row(&args, &data);
|
||||
struct thinkpad_ec_row data = { .mask = 0x8000 };
|
||||
int ret = thinkpad_ec_read_row(&args, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data.val[0xF]!=0x00)
|
||||
@ -193,10 +189,10 @@ static int hdaps_set_power(int on) {
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
static int hdaps_set_fake_data_mode(int on) {
|
||||
struct tp_controller_row args =
|
||||
struct thinkpad_ec_row args =
|
||||
{ .mask=0x0007, .val={0x17, 0x83, on?0x01:0x00} };
|
||||
struct tp_controller_row data = { .mask = 0x8000 };
|
||||
int ret = tp_controller_read_row(&args, &data);
|
||||
struct thinkpad_ec_row data = { .mask = 0x8000 };
|
||||
int ret = thinkpad_ec_read_row(&args, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data.val[0xF]!=0x00) {
|
||||
@ -216,10 +212,10 @@ static int hdaps_set_fake_data_mode(int on) {
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
static int hdaps_set_ec_config(int ec_rate, int order) {
|
||||
struct tp_controller_row args = { .mask=0x000F,
|
||||
struct thinkpad_ec_row args = { .mask=0x000F,
|
||||
.val={0x10, (u8)ec_rate, (u8)(ec_rate>>8), order} };
|
||||
struct tp_controller_row data = { .mask = 0x8000 };
|
||||
int ret = tp_controller_read_row(&args, &data);
|
||||
struct thinkpad_ec_row data = { .mask = 0x8000 };
|
||||
int ret = thinkpad_ec_read_row(&args, &data);
|
||||
printk(KERN_DEBUG "hdaps: setting ec_rate=%d, filter_order=%d\n",
|
||||
ec_rate, order);
|
||||
if (ret)
|
||||
@ -247,10 +243,10 @@ static int hdaps_set_ec_config(int ec_rate, int order) {
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
static int hdaps_get_ec_config(int *ec_rate, int *order) {
|
||||
const struct tp_controller_row args =
|
||||
const struct thinkpad_ec_row args =
|
||||
{ .mask=0x0003, .val={0x17, 0x82} };
|
||||
struct tp_controller_row data = { .mask = 0x801F };
|
||||
int ret = tp_controller_read_row(&args, &data);
|
||||
struct thinkpad_ec_row data = { .mask = 0x801F };
|
||||
int ret = thinkpad_ec_read_row(&args, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data.val[0xF]!=0x00)
|
||||
@ -269,9 +265,9 @@ static int hdaps_get_ec_config(int *ec_rate, int *order) {
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
static int hdaps_get_ec_mode(u8 *mode) {
|
||||
const struct tp_controller_row args = { .mask=0x0001, .val={0x13} };
|
||||
struct tp_controller_row data = { .mask = 0x8002 };
|
||||
int ret = tp_controller_read_row(&args, &data);
|
||||
const struct thinkpad_ec_row args = { .mask=0x0001, .val={0x13} };
|
||||
struct thinkpad_ec_row data = { .mask = 0x8002 };
|
||||
int ret = thinkpad_ec_read_row(&args, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data.val[0xF]!=0x00) {
|
||||
@ -290,10 +286,10 @@ static int hdaps_get_ec_mode(u8 *mode) {
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
static int __init hdaps_check_ec(u8 *mode) {
|
||||
const struct tp_controller_row args =
|
||||
const struct thinkpad_ec_row args =
|
||||
{ .mask=0x0003, .val={0x17, 0x81} };
|
||||
struct tp_controller_row data = { .mask = 0x800E };
|
||||
int ret = tp_controller_read_row(&args, &data);
|
||||
struct thinkpad_ec_row data = { .mask = 0x800E };
|
||||
int ret = thinkpad_ec_read_row(&args, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data.val[0x1]!=0x00 || data.val[0x2]!=0x60 ||
|
||||
@ -312,7 +308,7 @@ static int hdaps_device_init(void)
|
||||
int ret;
|
||||
u8 mode;
|
||||
|
||||
ret = tp_controller_lock();
|
||||
ret = thinkpad_ec_lock();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -335,19 +331,20 @@ static int hdaps_device_init(void)
|
||||
if (hdaps_set_fake_data_mode(fake_data_mode))
|
||||
ABORT_INIT("hdaps_set_fake_data_mode failed");
|
||||
|
||||
tp_controller_invalidate();
|
||||
thinkpad_ec_invalidate();
|
||||
udelay(200);
|
||||
|
||||
/* Just prefetch instead of reading, to avoid ~1sec delay on load */
|
||||
ret = tp_controller_prefetch_row(&ec_accel_args);
|
||||
ret = thinkpad_ec_prefetch_row(&ec_accel_args);
|
||||
if (ret)
|
||||
ABORT_INIT("initial prefetch failed");
|
||||
goto good;
|
||||
bad:
|
||||
thinkpad_ec_invalidate();
|
||||
ret = -ENXIO;
|
||||
good:
|
||||
tp_controller_invalidate();
|
||||
tp_controller_unlock();
|
||||
stale_readout = 1;
|
||||
thinkpad_ec_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -377,7 +374,7 @@ static int hdaps_probe(struct platform_device *dev)
|
||||
|
||||
static int hdaps_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
/* Don't do mouse polls until resume re-initializes the sensor. */
|
||||
/* Don't do hdaps polls until resume re-initializes the sensor. */
|
||||
del_timer_sync(&hdaps_timer);
|
||||
hdaps_device_shutdown();
|
||||
return 0;
|
||||
@ -403,8 +400,7 @@ static struct platform_driver hdaps_driver = {
|
||||
};
|
||||
|
||||
/*
|
||||
* hdaps_calibrate - Set our "resting" values.
|
||||
* Does its own locking.
|
||||
* hdaps_calibrate - Set our "resting" values. Does its own locking.
|
||||
*/
|
||||
static void hdaps_calibrate(void)
|
||||
{
|
||||
@ -423,11 +419,11 @@ static void hdaps_mousedev_poll(unsigned long unused)
|
||||
stale_readout = 1;
|
||||
|
||||
/* Cannot sleep. Try nonblockingly. If we fail, try again later. */
|
||||
if (tp_controller_try_lock())
|
||||
if (thinkpad_ec_try_lock())
|
||||
goto keep_active;
|
||||
|
||||
ret = __hdaps_update(1); /* fast update, we're in softirq context */
|
||||
tp_controller_unlock();
|
||||
thinkpad_ec_unlock();
|
||||
/* Any of "successful", "not yet ready" and "not prefetched"? */
|
||||
if (ret!=0 && ret!=-EBUSY && ret!=-ENODATA) {
|
||||
printk(KERN_ERR
|
||||
@ -455,7 +451,7 @@ static ssize_t hdaps_position_show(struct device *dev,
|
||||
return sprintf(buf, "(%d,%d)\n", pos_x, pos_y);
|
||||
}
|
||||
|
||||
static ssize_t hdaps_temperature_show(struct device *dev,
|
||||
static ssize_t hdaps_temp1_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret = hdaps_update();
|
||||
@ -618,8 +614,8 @@ static ssize_t hdaps_fake_data_mode_show(
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL);
|
||||
static DEVICE_ATTR(temp1, 0444, hdaps_temperature_show, NULL);
|
||||
/* named temp1 instead of temperature for backward compatibility */
|
||||
static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL);
|
||||
/* "temp1" instead of "temperature" is hwmon convention */
|
||||
static DEVICE_ATTR(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL);
|
||||
static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL);
|
||||
static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store);
|
||||
@ -656,79 +652,41 @@ static struct attribute_group hdaps_attribute_group = {
|
||||
|
||||
/* Module stuff */
|
||||
|
||||
/* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */
|
||||
static int hdaps_dmi_match(struct dmi_system_id *id)
|
||||
{
|
||||
printk(KERN_INFO "hdaps: %s detected.\n", id->ident);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* hdaps_dmi_match_invert - found an inverted match. */
|
||||
static int hdaps_dmi_match_invert(struct dmi_system_id *id)
|
||||
{
|
||||
hdaps_invert = 1;
|
||||
printk(KERN_INFO "hdaps: inverting axis readings.\n");
|
||||
return hdaps_dmi_match(id);
|
||||
printk(KERN_INFO "hdaps: %s detected, inverting axes\n",
|
||||
id->ident);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define HDAPS_DMI_MATCH_NORMAL(model) { \
|
||||
.ident = "IBM " model, \
|
||||
.callback = hdaps_dmi_match, \
|
||||
.matches = { \
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), \
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, model) \
|
||||
} \
|
||||
}
|
||||
|
||||
#define HDAPS_DMI_MATCH_INVERT(model) { \
|
||||
.ident = "IBM " model, \
|
||||
#define HDAPS_DMI_MATCH_INVERT(vendor,model) { \
|
||||
.ident = vendor " " model, \
|
||||
.callback = hdaps_dmi_match_invert, \
|
||||
.matches = { \
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), \
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, model) \
|
||||
} \
|
||||
}
|
||||
|
||||
#define HDAPS_DMI_MATCH_LENOVO(model) { \
|
||||
.ident = "Lenovo " model, \
|
||||
.callback = hdaps_dmi_match_invert, \
|
||||
.matches = { \
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), \
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, model) \
|
||||
} \
|
||||
}
|
||||
|
||||
static int __init hdaps_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
|
||||
/* List of models with abnormal axis configuration.
|
||||
Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
|
||||
"ThinkPad T42p", so the order of the entries matters */
|
||||
struct dmi_system_id hdaps_whitelist[] = {
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad H"),
|
||||
HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad R52"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad H"), /* R52 (1846AQG) */
|
||||
HDAPS_DMI_MATCH_INVERT("ThinkPad T41p"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad T41"),
|
||||
HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad T42"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"),
|
||||
HDAPS_DMI_MATCH_LENOVO("ThinkPad T60p"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad X41"),
|
||||
HDAPS_DMI_MATCH_LENOVO("ThinkPad X60"),
|
||||
HDAPS_DMI_MATCH_NORMAL("ThinkPad Z60m"),
|
||||
HDAPS_DMI_MATCH_INVERT("IBM","ThinkPad R50p"),
|
||||
HDAPS_DMI_MATCH_INVERT("IBM","ThinkPad T41p"),
|
||||
HDAPS_DMI_MATCH_INVERT("IBM","ThinkPad T42p"),
|
||||
HDAPS_DMI_MATCH_INVERT("LENOVO","ThinkPad T60p"),
|
||||
HDAPS_DMI_MATCH_INVERT("LENOVO","ThinkPad X60"),
|
||||
{ .ident = NULL }
|
||||
};
|
||||
|
||||
if (!(dmi_check_system(hdaps_whitelist) || hdaps_force)) {
|
||||
printk(KERN_WARNING "hdaps: supported laptop not found!\n");
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
dmi_check_system(hdaps_whitelist); /* default to normal axes */
|
||||
|
||||
/* Init timer before platform_driver_register, in case of suspend */
|
||||
init_timer(&hdaps_timer);
|
||||
@ -801,9 +759,6 @@ module_exit(hdaps_exit);
|
||||
module_param_named(invert, hdaps_invert, bool, 0);
|
||||
MODULE_PARM_DESC(invert, "invert data along each axis");
|
||||
|
||||
module_param_named(force, hdaps_force, bool, 0);
|
||||
MODULE_PARM_DESC(force, "force loading on non whitelisted laptops");
|
||||
|
||||
MODULE_AUTHOR("Robert Love");
|
||||
MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
1
include/linux/thinkpad_ec.h
Symbolic link
1
include/linux/thinkpad_ec.h
Symbolic link
@ -0,0 +1 @@
|
||||
../../thinkpad_ec.h
|
@ -1 +0,0 @@
|
||||
../../tp_base.h
|
@ -1,12 +1,12 @@
|
||||
/*
|
||||
* tp_base.c - coordinate access to ThinkPad-specific hardware resources
|
||||
* thinkpad_ec.c - coordinate access to ThinkPad-specific hardware resources
|
||||
*
|
||||
* The embedded controller on ThinkPad laptops has a non-standard interface
|
||||
* at IO ports 0x1600-0x161F (mapped to LCP channel 3 of the H8S chip).
|
||||
* The interface provides various system management services (currently
|
||||
* known: battery information and accelerometer readouts). This driver
|
||||
* provides access and mutual exclusion for the EC interface.
|
||||
* H8S hardware documentation and terminology is used in this file:
|
||||
* For information about the LPC protocol and terminology, see:
|
||||
* "H8S/2104B Group Hardware Manual",
|
||||
* http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
|
||||
*
|
||||
@ -32,11 +32,11 @@
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/tp_base.h>
|
||||
#include <linux/thinkpad_ec.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define TP_VERSION "0.26"
|
||||
#define TP_VERSION "0.27"
|
||||
|
||||
MODULE_AUTHOR("Shem Multinymous");
|
||||
MODULE_DESCRIPTION("ThinkPad embedded controller hardware access");
|
||||
@ -48,10 +48,10 @@ MODULE_LICENSE("GPL");
|
||||
#define TPC_NUM_PORTS 0x20
|
||||
#define TPC_STR3_PORT 0x1604 /* Reads H8S EC register STR3 */
|
||||
#define TPC_TWR0_PORT 0x1610 /* Mapped to H8S EC register TWR0MW/SW */
|
||||
#define TPC_TWR15_PORT 0x161F /* Mapped to H8S EC register TWR15 */
|
||||
/* port 1610+i is mapped to H8S reg TWRi for 0<i<16 */
|
||||
#define TPC_TWR15_PORT 0x161F /* Mapped to H8S EC register TWR15. */
|
||||
/* (and port TPC_TWR0_PORT+i is mapped to H8S reg TWRi for 0<i<16) */
|
||||
|
||||
/* H8S STR3 status flags (see H8S/2104B Group Hardware Manual p.549) */
|
||||
/* H8S STR3 status flags (see "H8S/2104B Group Hardware Manual" p.549) */
|
||||
#define H8S_STR3_IBF3B 0x80 /* Bidi. Data Register Input Buffer Full */
|
||||
#define H8S_STR3_OBF3B 0x40 /* Bidi. Data Register Output Buffer Full */
|
||||
#define H8S_STR3_MWMF 0x20 /* Master Write Mode Flag */
|
||||
@ -74,8 +74,8 @@ MODULE_PARM_DESC(debug, "Debug level (0=off, 1=on)");
|
||||
#define DPRINTK(fmt, args...) \
|
||||
do { if (tp_debug) printk(KERN_DEBUG fmt, ## args); } while (0)
|
||||
#define MSG_FMT(fmt, args...) \
|
||||
"tp_base: %s: " fmt "\n", __func__, ## args
|
||||
#define ARG_FMT(msg,code) \
|
||||
"thinkpad_ec: %s: " fmt "\n", __func__, ## args
|
||||
#define REQ_FMT(msg, code) \
|
||||
MSG_FMT("%s: (0x%02x:0x%02x)->0x%02x", \
|
||||
msg, args->val[0x0], args->val[0xF], code)
|
||||
|
||||
@ -87,46 +87,46 @@ static u64 prefetch_jiffies; /* time of prefetch, or: */
|
||||
|
||||
/* Locking: */
|
||||
|
||||
static DECLARE_MUTEX(tp_controller_mutex);
|
||||
static DECLARE_MUTEX(thinkpad_ec_mutex);
|
||||
|
||||
/* tp_controller_lock:
|
||||
/* thinkpad_ec_lock:
|
||||
* Get exclusive lock for accesing the controller. May sleep.
|
||||
* Returns 0 iff lock acquired .
|
||||
*/
|
||||
int tp_controller_lock(void)
|
||||
int thinkpad_ec_lock(void)
|
||||
{
|
||||
int ret;
|
||||
ret = down_interruptible(&tp_controller_mutex);
|
||||
ret = down_interruptible(&thinkpad_ec_mutex);
|
||||
if (ret)
|
||||
DPRINTK("tp_controller mutex down interrupted: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_lock);
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_lock);
|
||||
|
||||
/* tp_controller_try_lock:
|
||||
/* thinkpad_ec_try_lock:
|
||||
* Get exclusive lock for accesing the controller but only if it's available.
|
||||
* Does not block, does not sleep. Returns 0 iff lock acquired .
|
||||
*/
|
||||
int tp_controller_try_lock(void)
|
||||
int thinkpad_ec_try_lock(void)
|
||||
{
|
||||
return down_trylock(&tp_controller_mutex);
|
||||
return down_trylock(&thinkpad_ec_mutex);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_try_lock);
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_try_lock);
|
||||
|
||||
/* tp_controller_unlock:
|
||||
/* thinkpad_ec_unlock:
|
||||
* Release a previously acquired controller lock.
|
||||
*/
|
||||
void tp_controller_unlock(void)
|
||||
void thinkpad_ec_unlock(void)
|
||||
{
|
||||
up(&tp_controller_mutex);
|
||||
up(&thinkpad_ec_mutex);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_unlock);
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_unlock);
|
||||
|
||||
/* Tell embedded controller to prepare a row */
|
||||
static int tp_controller_request_row(const struct tp_controller_row *args)
|
||||
static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args)
|
||||
{
|
||||
u8 str3;
|
||||
int i;
|
||||
@ -143,15 +143,15 @@ static int tp_controller_request_row(const struct tp_controller_row *args)
|
||||
inb(TPC_TWR15_PORT); /* marks end of previous transaction */
|
||||
if (prefetch_jiffies == TPC_PREFETCH_NONE)
|
||||
printk(KERN_WARNING
|
||||
ARG_FMT("readout already pending", str3));
|
||||
REQ_FMT("readout already pending", str3));
|
||||
return -EBUSY; /* EC will be ready in a few usecs */
|
||||
} else if (str3 == H8S_STR3_SWMF) { /* busy with previous request */
|
||||
if (prefetch_jiffies == TPC_PREFETCH_NONE)
|
||||
printk(KERN_WARNING
|
||||
ARG_FMT("EC handles previous request", str3));
|
||||
REQ_FMT("EC handles previous request", str3));
|
||||
return -EBUSY; /* data will be pending in a few usecs */
|
||||
} else if (str3 != 0x00) { /* unexpected status? */
|
||||
printk(KERN_WARNING ARG_FMT("bad initial STR3", str3));
|
||||
printk(KERN_WARNING REQ_FMT("bad initial STR3", str3));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ static int tp_controller_request_row(const struct tp_controller_row *args)
|
||||
outb(args->val[0], TPC_TWR0_PORT);
|
||||
str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
|
||||
if (str3 != H8S_STR3_MWMF) { /* not accepted? */
|
||||
printk(KERN_WARNING ARG_FMT("arg0 rejected", str3));
|
||||
printk(KERN_WARNING REQ_FMT("arg0 rejected", str3));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -184,30 +184,30 @@ static int tp_controller_request_row(const struct tp_controller_row *args)
|
||||
ndelay(TPC_REQUEST_NDELAY);
|
||||
else { /* weird EC status */
|
||||
printk(KERN_WARNING
|
||||
ARG_FMT("bad end STR3", str3));
|
||||
REQ_FMT("bad end STR3", str3));
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
printk(KERN_WARNING ARG_FMT("EC is mysteriously silent", str3));
|
||||
printk(KERN_WARNING REQ_FMT("EC is mysteriously silent", str3));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Read current row data from the controller, assuming it's already
|
||||
/* Read current row data from the controller, assuming it's already
|
||||
* requested.
|
||||
*/
|
||||
static int tp_controller_read_data(struct tp_controller_row *data)
|
||||
static int thinkpad_ec_read_data(struct thinkpad_ec_row *data)
|
||||
{
|
||||
int i;
|
||||
u8 str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
|
||||
/* Once we make a request, STR3 assumes the following sequence of
|
||||
* values as it reads the request and writes its data.
|
||||
/* Once we make a request, STR3 assumes the sequence of values listed
|
||||
* in the following 'if' as it reads the request and writes its data.
|
||||
* It takes about a few dozen nanosecs total, with very high variance.
|
||||
*/
|
||||
if (str3==(H8S_STR3_IBF3B|H8S_STR3_MWMF) ||
|
||||
str3==0x00 ||
|
||||
str3==0x00 || /* the 0x00 is indistinguishable from idle EC! */
|
||||
str3==H8S_STR3_SWMF )
|
||||
return -EBUSY; /* not ready yet */
|
||||
/* Finally, it signals output buffer full: */
|
||||
/* Finally, the EC signals output buffer full: */
|
||||
if (str3 != (H8S_STR3_OBF3B|H8S_STR3_SWMF)) {
|
||||
printk(KERN_WARNING
|
||||
MSG_FMT("bad initial STR3 (0x%02x)", str3));
|
||||
@ -234,7 +234,7 @@ static int tp_controller_read_data(struct tp_controller_row *data)
|
||||
/* Is the given row currently prefetched?
|
||||
* To keep things simple we compare only the first and last args;
|
||||
* in practice this suffices .*/
|
||||
static int tp_controller_is_row_fetched(const struct tp_controller_row *args)
|
||||
static int thinkpad_ec_is_row_fetched(const struct thinkpad_ec_row *args)
|
||||
{
|
||||
return (prefetch_jiffies != TPC_PREFETCH_NONE) &&
|
||||
(prefetch_jiffies != TPC_PREFETCH_JUNK) &&
|
||||
@ -243,41 +243,41 @@ static int tp_controller_is_row_fetched(const struct tp_controller_row *args)
|
||||
(get_jiffies_64() < prefetch_jiffies + TPC_PREFETCH_TIMEOUT);
|
||||
}
|
||||
|
||||
/* tp_controller_read_row:
|
||||
/* thinkpad_ec_read_row:
|
||||
* Read a data row from the controller, fetching and retrying if needed.
|
||||
* The row args are specified by 16 byte arguments, some of which may be
|
||||
* missing (but the first and last are mandatory). These are given in
|
||||
* args->val[], args->val[i] is used iff (args->mask>>i)&1).
|
||||
* args->val[], where args->val[i] is used iff (args->mask>>i)&1).
|
||||
* The rows's data is stored in data->val[], but is only guaranteed to be
|
||||
* valid for indices corresponding to set bit in data->maska. That is,
|
||||
* if (data->mask>>i)&1==0 then data->val[i] may not be filled (to save time).
|
||||
* Returns -EBUSY on transient error and -EIO on abnormal condition.
|
||||
* Caller must hold controller lock.
|
||||
*/
|
||||
int tp_controller_read_row(const struct tp_controller_row *args,
|
||||
struct tp_controller_row *data)
|
||||
int thinkpad_ec_read_row(const struct thinkpad_ec_row *args,
|
||||
struct thinkpad_ec_row *data)
|
||||
{
|
||||
int retries, ret;
|
||||
|
||||
if (tp_controller_is_row_fetched(args))
|
||||
if (thinkpad_ec_is_row_fetched(args))
|
||||
goto read_row; /* already requested */
|
||||
|
||||
/* Request the row */
|
||||
for (retries=0; retries<TPC_READ_RETRIES; ++retries) {
|
||||
ret = tp_controller_request_row(args);
|
||||
ret = thinkpad_ec_request_row(args);
|
||||
if (!ret)
|
||||
goto read_row;
|
||||
if (ret != -EBUSY)
|
||||
break;
|
||||
ndelay(TPC_READ_NDELAY);
|
||||
}
|
||||
printk(KERN_ERR ARG_FMT("failed requesting row", ret));
|
||||
printk(KERN_ERR REQ_FMT("failed requesting row", ret));
|
||||
goto out;
|
||||
|
||||
read_row:
|
||||
/* Read the row's data */
|
||||
for (retries=0; retries<TPC_READ_RETRIES; ++retries) {
|
||||
ret = tp_controller_read_data(data);
|
||||
ret = thinkpad_ec_read_data(data);
|
||||
if (!ret)
|
||||
goto out;
|
||||
if (ret!=-EBUSY)
|
||||
@ -285,50 +285,50 @@ read_row:
|
||||
ndelay(TPC_READ_NDELAY);
|
||||
}
|
||||
|
||||
printk(KERN_ERR ARG_FMT("failed waiting for data", ret));
|
||||
printk(KERN_ERR REQ_FMT("failed waiting for data", ret));
|
||||
|
||||
out:
|
||||
prefetch_jiffies = TPC_PREFETCH_JUNK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_read_row);
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_read_row);
|
||||
|
||||
/* tp_controller_try_read_row:
|
||||
/* thinkpad_ec_try_read_row:
|
||||
* Try read a prefetched row from the controller. Don't fetch or retry.
|
||||
* See tp_controller_read_row above for the meaning of the arguments.
|
||||
* See thinkpad_ec_read_row above for the meaning of the arguments.
|
||||
* Returns -EBUSY is data not ready and -ENODATA if row not prefetched.
|
||||
* Caller must hold controller lock.
|
||||
*/
|
||||
int tp_controller_try_read_row(const struct tp_controller_row *args,
|
||||
struct tp_controller_row *data)
|
||||
int thinkpad_ec_try_read_row(const struct thinkpad_ec_row *args,
|
||||
struct thinkpad_ec_row *data)
|
||||
{
|
||||
int ret;
|
||||
if (!tp_controller_is_row_fetched(args)) {
|
||||
if (!thinkpad_ec_is_row_fetched(args)) {
|
||||
ret = -ENODATA;
|
||||
} else {
|
||||
ret = tp_controller_read_data(data);
|
||||
ret = thinkpad_ec_read_data(data);
|
||||
if (!ret)
|
||||
prefetch_jiffies = TPC_PREFETCH_NONE; /* eaten up */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_try_read_row);
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_try_read_row);
|
||||
|
||||
/* tp_controller_prefetch_row:
|
||||
/* thinkpad_ec_prefetch_row:
|
||||
* Prefetch data row from the controller. A subsequent call to
|
||||
* tp_controller_read_row() with the same arguments will be faster,
|
||||
* and a subsequent call to tp_controller_try_read_row stands a
|
||||
* thinkpad_ec_read_row() with the same arguments will be faster,
|
||||
* and a subsequent call to thinkpad_ec_try_read_row stands a
|
||||
* good chance of succeeding if done neither too soon nor too late.
|
||||
* See tp_controller_read_row above for the meaning of the arguments.
|
||||
* See thinkpad_ec_read_row above for the meaning of the arguments.
|
||||
* Returns -EBUSY on transient error and -EIO on abnormal condition.
|
||||
* Caller must hold controller lock.
|
||||
*/
|
||||
int tp_controller_prefetch_row(const struct tp_controller_row *args)
|
||||
int thinkpad_ec_prefetch_row(const struct thinkpad_ec_row *args)
|
||||
{
|
||||
int ret;
|
||||
ret = tp_controller_request_row(args);
|
||||
ret = thinkpad_ec_request_row(args);
|
||||
if (ret) {
|
||||
prefetch_jiffies = TPC_PREFETCH_JUNK;
|
||||
} else {
|
||||
@ -339,47 +339,41 @@ int tp_controller_prefetch_row(const struct tp_controller_row *args)
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_prefetch_row);
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_prefetch_row);
|
||||
|
||||
/* tp_controller_invalidate:
|
||||
/* thinkpad_ec_invalidate:
|
||||
* Invalidate the prefetched controller data.
|
||||
* Must be called before unlocking by any code that accesses the controller
|
||||
* ports directly.
|
||||
*/
|
||||
void tp_controller_invalidate(void)
|
||||
void thinkpad_ec_invalidate(void)
|
||||
{
|
||||
prefetch_jiffies = TPC_PREFETCH_JUNK;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_invalidate);
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_invalidate);
|
||||
|
||||
/* tp_controller_test:
|
||||
* Ensure the EC LPC3 channel really exists on this machine by making
|
||||
|
||||
/*** Checking for EC hardware ***/
|
||||
|
||||
/* thinkpad_ec_test:
|
||||
* Ensure the EC LPC3 channel really works on this machine by making
|
||||
* an arbitrary harmless EC request and seeing if the EC follows protocol.
|
||||
* This test writes to IO ports, so execute only after checking DMI.
|
||||
*/
|
||||
static int tp_controller_test(void) {
|
||||
static int thinkpad_ec_test(void) {
|
||||
int ret;
|
||||
const struct tp_controller_row args = /* battery 0 basic status */
|
||||
const struct thinkpad_ec_row args = /* battery 0 basic status */
|
||||
{ .mask=0x8001, .val={0x01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x00} };
|
||||
struct tp_controller_row data = { .mask = 0x0000 };
|
||||
ret = tp_controller_lock();
|
||||
struct thinkpad_ec_row data = { .mask = 0x0000 };
|
||||
ret = thinkpad_ec_lock();
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = tp_controller_read_row(&args, &data);
|
||||
tp_controller_unlock();
|
||||
ret = thinkpad_ec_read_row(&args, &data);
|
||||
thinkpad_ec_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*** Model whitelist ***/
|
||||
|
||||
#define TP_DMI_MATCH(vendor,model) { \
|
||||
.ident = vendor " " model, \
|
||||
.matches = { \
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, model) \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Search all DMI device names for a given type for a substrng */
|
||||
static int __init dmi_find_substring(int type, const char *substr) {
|
||||
struct dmi_device *dev = NULL;
|
||||
@ -390,8 +384,16 @@ static int __init dmi_find_substring(int type, const char *substr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TP_DMI_MATCH(vendor,model) { \
|
||||
.ident = vendor " " model, \
|
||||
.matches = { \
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, model) \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Check DMI for existence of ThinkPad embedded controller */
|
||||
static int __init check_for_embedded_controller(void)
|
||||
static int __init check_dmi_for_ec(void)
|
||||
{
|
||||
/* A few old models that have a good EC but don't report it in DMI */
|
||||
struct dmi_system_id tp_whitelist[] = {
|
||||
@ -412,36 +414,36 @@ static int __init check_for_embedded_controller(void)
|
||||
|
||||
/*** Init and cleanup ***/
|
||||
|
||||
static int __init tp_base_init(void)
|
||||
static int __init thinkpad_ec_init(void)
|
||||
{
|
||||
if (!check_for_embedded_controller()) {
|
||||
printk(KERN_ERR "tp_base: no ThinkPad embedded controller!\n");
|
||||
if (!check_dmi_for_ec()) {
|
||||
printk(KERN_ERR "thinkpad_ec: no ThinkPad embedded controller!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!request_region(TPC_BASE_PORT, TPC_NUM_PORTS,
|
||||
"thinkpad_ec"))
|
||||
{
|
||||
printk(KERN_ERR "tp_base: cannot claim io ports %#x-%#x\n",
|
||||
printk(KERN_ERR "thinkpad_ec: cannot claim io ports %#x-%#x\n",
|
||||
TPC_BASE_PORT,
|
||||
TPC_BASE_PORT + TPC_NUM_PORTS -1);
|
||||
return -ENXIO;
|
||||
}
|
||||
prefetch_jiffies = TPC_PREFETCH_JUNK;
|
||||
if (tp_controller_test()) {
|
||||
printk(KERN_INFO "tp_base: init test failed\n");
|
||||
if (thinkpad_ec_test()) {
|
||||
printk(KERN_INFO "thinkpad_ec: initial ec test failed\n");
|
||||
release_region(TPC_BASE_PORT, TPC_NUM_PORTS);
|
||||
return -ENXIO;
|
||||
}
|
||||
printk(KERN_INFO "tp_base: tp_base " TP_VERSION " loaded.\n");
|
||||
printk(KERN_INFO "thinkpad_ec: thinkpad_ec " TP_VERSION " loaded.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit tp_base_exit(void)
|
||||
static void __exit thinkpad_ec_exit(void)
|
||||
{
|
||||
release_region(TPC_BASE_PORT, TPC_NUM_PORTS);
|
||||
printk(KERN_INFO "tp_base: unloaded.\n");
|
||||
printk(KERN_INFO "thinkpad_ec: unloaded.\n");
|
||||
}
|
||||
|
||||
module_init(tp_base_init);
|
||||
module_exit(tp_base_exit);
|
||||
module_init(thinkpad_ec_init);
|
||||
module_exit(thinkpad_ec_exit);
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* tp_base.h - interface to the ThinkPad embedded controller LPC3 functions
|
||||
* thinkpad_ec.h - interface to ThinkPad embedded controller LPC3 functions
|
||||
*
|
||||
* Copyright (C) 2005 Shem Multinymous <multinymous@gmail.com>
|
||||
*
|
||||
@ -18,29 +18,30 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _TP_BASE_H
|
||||
#define _TP_BASE_H
|
||||
#ifndef _THINKPAD_EC_H
|
||||
#define _THINKPAD_EC_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#define TP_CONTROLLER_ROW_LEN 16
|
||||
|
||||
struct tp_controller_row {
|
||||
/* EC transactions input and output (possibly partial) vectors of 16 bytes. */
|
||||
struct thinkpad_ec_row {
|
||||
u16 mask; /* bitmap of which entries of val[] are meaningful */
|
||||
u8 val[TP_CONTROLLER_ROW_LEN];
|
||||
};
|
||||
|
||||
extern int tp_controller_lock(void);
|
||||
extern int tp_controller_try_lock(void);
|
||||
extern void tp_controller_unlock(void);
|
||||
extern int thinkpad_ec_lock(void);
|
||||
extern int thinkpad_ec_try_lock(void);
|
||||
extern void thinkpad_ec_unlock(void);
|
||||
|
||||
extern int tp_controller_read_row(const struct tp_controller_row *args,
|
||||
struct tp_controller_row *data);
|
||||
extern int tp_controller_try_read_row(const struct tp_controller_row *args,
|
||||
struct tp_controller_row *mask);
|
||||
extern int tp_controller_prefetch_row(const struct tp_controller_row *args);
|
||||
extern void tp_controller_invalidate(void);
|
||||
extern int thinkpad_ec_read_row(const struct thinkpad_ec_row *args,
|
||||
struct thinkpad_ec_row *data);
|
||||
extern int thinkpad_ec_try_read_row(const struct thinkpad_ec_row *args,
|
||||
struct thinkpad_ec_row *mask);
|
||||
extern int thinkpad_ec_prefetch_row(const struct thinkpad_ec_row *args);
|
||||
extern void thinkpad_ec_invalidate(void);
|
||||
|
||||
|
||||
#endif /* __KERNEL */
|
||||
#endif /* _TP_BASE_H */
|
||||
#endif /* _THINKPAD_EC_H */
|
27
tp_smapi.c
27
tp_smapi.c
@ -7,7 +7,7 @@
|
||||
* to the APM control port 0xB2. Older models use a different interface;
|
||||
* for those, try the "thinkpad" module.
|
||||
* It also exposes battery status information, obtained from the ThinkPad
|
||||
* embedded controller (via the tp_base module).
|
||||
* embedded controller (via the thinkpad_ec module).
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2006 Shem Multinymous <multinymous@gmail.com>.
|
||||
@ -36,12 +36,12 @@
|
||||
#include <linux/mc146818rtc.h> /* CMOS defines */
|
||||
#include <linux/delay.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/tp_base.h>
|
||||
#include <linux/thinkpad_ec.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define TP_VERSION "0.26"
|
||||
#define TP_VERSION "0.27"
|
||||
#define TP_DESC "ThinkPad SMAPI Support"
|
||||
#define TP_DIR "smapi"
|
||||
|
||||
@ -175,10 +175,9 @@ static int smapi_request(u32 inBX, u32 inCX,
|
||||
DPRINTK("req_in: BX=%x CX=%x DI=%x SI=%x\n",
|
||||
inBX, inCX, inDI, inSI);
|
||||
|
||||
/* SMAPI's SMBIOS call and tp_base end up using use different
|
||||
* interfaces to the same chip, so play it safe (necessary?):
|
||||
*/
|
||||
ret = tp_controller_lock();
|
||||
/* SMAPI's SMBIOS call and thinkpad_ec end up using use
|
||||
* different interfaces to the same chip, so play it safe. */
|
||||
ret = thinkpad_ec_lock();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -209,8 +208,8 @@ static int smapi_request(u32 inBX, u32 inCX,
|
||||
:"%eax", "%ebx", "%ecx", "%edx", "%edi",
|
||||
"%esi");
|
||||
|
||||
tp_controller_invalidate();
|
||||
tp_controller_unlock();
|
||||
thinkpad_ec_invalidate();
|
||||
thinkpad_ec_unlock();
|
||||
|
||||
/* Don't let the next SMAPI access happen too quickly,
|
||||
* may case problems. (We're hold smapi_mutex). */
|
||||
@ -261,15 +260,15 @@ static int smapi_write(u32 inBX, u32 inCX,
|
||||
*/
|
||||
static int tpc_read_row(u8 arg0, int bat, u8 j, u8* dataval) {
|
||||
int ret;
|
||||
const struct tp_controller_row args = { .mask=0xFFFF,
|
||||
const struct thinkpad_ec_row args = { .mask=0xFFFF,
|
||||
.val={arg0, j,j,j,j,j,j,j,j,j,j,j,j,j,j, (u8)bat} };
|
||||
struct tp_controller_row data = { .mask = 0xFFFF };
|
||||
struct thinkpad_ec_row data = { .mask = 0xFFFF };
|
||||
|
||||
ret = tp_controller_lock();
|
||||
ret = thinkpad_ec_lock();
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = tp_controller_read_row(&args, &data);
|
||||
tp_controller_unlock();
|
||||
ret = thinkpad_ec_read_row(&args, &data);
|
||||
thinkpad_ec_unlock();
|
||||
memcpy(dataval, &data.val, TP_CONTROLLER_ROW_LEN);
|
||||
return ret;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user