mirror of
https://github.com/linux-thinkpad/tp_smapi.git
synced 2024-09-11 07:05:28 +03:00
import of tp-smapi version 0.22
This commit is contained in:
parent
987e971824
commit
e696acf8b6
29
CHANGES
29
CHANGES
@ -1,5 +1,34 @@
|
||||
Change history for tp_smapi:
|
||||
|
||||
0.22 2006-07-20
|
||||
----------------
|
||||
- hdaps: Added support for X60s (but X axis is still inverted).
|
||||
- tp_base: Wait until EC starts replying before considering a request
|
||||
successful (this improves stability and eliminates abnormal events.)
|
||||
- hdaps: Removed /sys/devices/platform/hdaps/{variance,temp2}. They actually
|
||||
show an older pending readout (if there is one), so it never makes sense
|
||||
to read them via the existing interface.
|
||||
- tp_base: Added many additional status and consistency checks on EC,
|
||||
to give early warning on any strange behavior.
|
||||
- tp_base: New constants and comments greatly clarify access to the EC.
|
||||
- hdaps: Convert all init code to use tp_base transactions. Mo more direct
|
||||
IO port access.
|
||||
- hdaps: Move some init code to new functions hdaps_set_power() and
|
||||
hdaps_set_ec_config(). These will be exposed as attributes in the future.
|
||||
- hdaps: If init failed, tell at which step it happened.
|
||||
- tp_base: Allow additional inputs and optional outputs to EC requests.
|
||||
- tp_base: Decrease timeout for reply to request.
|
||||
- Kconfig: Make TP_BASE an invisible tristate option
|
||||
- tp_base: Rename tp_controller_trylock() to tp_controller_try_lock().
|
||||
- tp_base: make tp_controller_lock() return an int; have it checked in
|
||||
tp_smapi and hdaps.
|
||||
- tp_base: Rewrote all printk()s using macros, and made debug=1 quieter.
|
||||
- Makefile: work even if $SHELL is not bash.
|
||||
- Makefile: Autopatch hdaps patch to work with 2.6.18-rc1
|
||||
|
||||
Many of the above changes were suggested or contributed by
|
||||
Henrique de Moraes Holschuh.
|
||||
|
||||
0.21 2006-06-21
|
||||
----------------
|
||||
- Compatibility with kernel 2.6.17.
|
||||
|
24
Makefile
24
Makefile
@ -8,6 +8,7 @@ PWD := $(shell pwd)
|
||||
IDIR := include/linux
|
||||
TP_DIR := drivers/firmware
|
||||
TP_MODULES := tp_base.o tp_smapi.o
|
||||
SHELL := /bin/bash
|
||||
|
||||
ifeq ($(HDAPS),1)
|
||||
TP_MODULES += hdaps.o
|
||||
@ -22,7 +23,7 @@ ifneq ($(shell [ -f $(KSRC)/include/linux/platform_device.h ] && echo 1),1)
|
||||
$(error This driver requires kernel 2.6.15 or newer.)
|
||||
endif
|
||||
|
||||
.PHONY: default clean modules load unload install patch check_hdaps
|
||||
.PHONY: default clean modules load unload install patch check_hdaps mk-hdaps.diff
|
||||
export TP_MODULES
|
||||
|
||||
#####################################################################
|
||||
@ -40,7 +41,7 @@ clean:
|
||||
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
|
||||
rm -fr .tmp_versions Modules.symvers
|
||||
rm -fr .tmp_versions Modules.symvers diff/hdaps.diff.tmp
|
||||
@if [ -f hdaps.c -a hdaps.c.flag -ot hdaps.c ]; then \
|
||||
echo 'WARNING: hdaps.c has changed since autogeneration, will not delete.'; \
|
||||
else \
|
||||
@ -75,12 +76,14 @@ endif
|
||||
depmod -a
|
||||
|
||||
# Match hdaps.c from kernel tree into local copy.
|
||||
# (First do a small change in our own patch if kernel 2.6.17.)
|
||||
# (First do a small change in our own patch if kernel != 2.6.17.x)
|
||||
hdaps.c: $(KSRC)/drivers/hwmon/hdaps.c diff/hdaps.diff
|
||||
cat $(PWD)/diff/hdaps.diff \
|
||||
| if grep -q 'ret = -ENODEV' $(KSRC)/drivers/hwmon/hdaps.c; then \
|
||||
perl -pe 'm/^ \t\tret = -ENXIO;/ && s/NXIO/NODEV/'; else cat; fi \
|
||||
| patch -d $(KSRC) -p1 -o $(PWD)/hdaps.c
|
||||
| if ! grep -q 'ret = -ENODEV' $(KSRC)/drivers/hwmon/hdaps.c; then \
|
||||
perl -0777 -pe 's/(laptop not found.*\n.*)ENODEV/$$1ENXIO/'; else cat; fi \
|
||||
| if grep -q 'Celsius' $(KSRC)/drivers/hwmon/hdaps.c; then \
|
||||
perl -0777 -pe 's/ celcius / Celsius /'; else cat; fi \
|
||||
| patch -d $(KSRC) -p1 -o $(PWD)/hdaps.c || { rm -v hdaps.c; exit 1; }
|
||||
@touch -r hdaps.c hdaps.c.flag
|
||||
|
||||
|
||||
@ -123,7 +126,7 @@ patch: hdaps.c
|
||||
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 \
|
||||
; fi &&\
|
||||
\
|
||||
{ diff -Nurp $(ORG) $(NEW) > patch \
|
||||
{ diff -dNurp $(ORG) $(NEW) > patch \
|
||||
|| [ $$? -lt 2 ]; } &&\
|
||||
{ diffstat patch; echo; echo; cat patch; } \
|
||||
> $(PWD)/${PATCH} &&\
|
||||
@ -131,6 +134,13 @@ patch: hdaps.c
|
||||
@echo -e "\nPatch file created:\n ${PATCH}"
|
||||
@echo -e "To apply, use:\n patch -p1 -d ${KSRC} < ${PATCH}"
|
||||
|
||||
#####################################################################
|
||||
# Tools for preparing a release. Ignore these.
|
||||
|
||||
mk-hdaps.diff: diff/hdaps.diff $(KSRC)/drivers/hwmon/hdaps.c
|
||||
{ head -2 diff/hdaps.diff; diff -up -U3 $(KSRC)/drivers/hwmon/hdaps.c hdaps.c | tail -n +3; } > diff/hdaps.diff.tmp || :
|
||||
mv diff/hdaps.diff.tmp diff/hdaps.diff
|
||||
|
||||
else
|
||||
#####################################################################
|
||||
# This part runs as a submake in kernel Makefile context:
|
||||
|
14
README
14
README
@ -1,4 +1,4 @@
|
||||
IBM ThinkPad SMAPI Driver v0.21
|
||||
IBM ThinkPad SMAPI Driver v0.22
|
||||
Author: Shem Multinymous <multinymous@gmail.com>
|
||||
Project: http://sourceforge.net/projects/tpctl
|
||||
Wiki: http://thinkwiki.org/wiki/tp_smapi
|
||||
@ -258,9 +258,9 @@ returned in the CPU's registers. It is not clear what is the relation between
|
||||
the two variants of SMAPI, though the assignment of error codes seems to be
|
||||
similar.
|
||||
|
||||
In addition to the above, battery information is accessible through IO ports
|
||||
0x1604-0x161F (these provide a virtual address space, which exposes both
|
||||
battery information and HDAPS accelerometer data). It's not clear which
|
||||
hardware component handles these ports; maybe it's another interface to the
|
||||
embedded controller (in addition to the above SMAPI protocol and the standard
|
||||
EC interface at ports 0x62,0x66).
|
||||
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:
|
||||
http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
|
||||
|
@ -1,21 +1,20 @@
|
||||
--- 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
|
||||
@@ -87,2 +87,11 @@
|
||||
@@ -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 "ThinkPad-specific hardware support"
|
||||
+ depends on X86
|
||||
+ default n
|
||||
+ help
|
||||
+ This provides coordination of access to ThinkPad-specific hardware.
|
||||
+
|
||||
+ If you have an IBM ThinkPad laptop, say Y or M here
|
||||
+ 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
|
||||
@@ -415,3 +415,3 @@
|
||||
@@ -437,6 +437,7 @@
|
||||
config SENSORS_HDAPS
|
||||
tristate "IBM Hard Drive Active Protection System (hdaps)"
|
||||
- depends on HWMON && INPUT && X86
|
||||
+ depends on HWMON && INPUT && X86 && TP_BASE
|
||||
depends on HWMON && INPUT && X86
|
||||
+ select TP_BASE
|
||||
default n
|
||||
help
|
||||
This driver provides support for the IBM Hard Drive Active Protection
|
||||
|
@ -1,15 +1,18 @@
|
||||
--- 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
|
||||
@@ -87,2 +87,13 @@
|
||||
@@ -88,4 +88,16 @@
|
||||
config TP_BASE
|
||||
tristate
|
||||
|
||||
+config TP_SMAPI
|
||||
+ tristate "ThinkPad SMAPI Support"
|
||||
+ depends on X86 && TP_BASE
|
||||
+ depends on X86
|
||||
+ select TP_BASE
|
||||
+ default n
|
||||
+ help
|
||||
+ This adds SMAPI support on IBM ThinkPads, mostly used for battery
|
||||
+ charge control. For more information about this driver see
|
||||
+ <http://www.thinkwiki.org/wiki/tp_smapi> .
|
||||
+ <http://www.thinkwiki.org/wiki/tp_smapi>.
|
||||
+
|
||||
+ If you have an IBM ThinkPad laptop, say Y or M here.
|
||||
+
|
||||
|
484
diff/hdaps.diff
484
diff/hdaps.diff
@ -1,12 +1,12 @@
|
||||
--- a/drivers/hwmon/hdaps.c 2006-01-01 00:00:00.000000000 +0000
|
||||
+++ b/drivers/hwmon/hdaps.c 2006-01-01 00:00:00.000000000 +0000
|
||||
@@ -33,74 +33,62 @@
|
||||
@@ -33,253 +33,256 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/dmi.h>
|
||||
-#include <asm/io.h>
|
||||
+#include <linux/tp_base.h>
|
||||
+#include <linux/jiffies.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
-#define HDAPS_LOW_PORT 0x1600 /* first port used by hdaps */
|
||||
-#define HDAPS_NR_PORTS 0x30 /* number of ports: 0x1600 - 0x162f */
|
||||
@ -22,14 +22,16 @@
|
||||
-#define HDAPS_PORT_KMACT 0x161d /* keyboard or mouse activity */
|
||||
-
|
||||
-#define STATE_FRESH 0x50 /* accelerometer data is fresh */
|
||||
+#define HDAPS_IDX_STATE 0x1 /* device state */
|
||||
+#define HDAPS_IDX_YPOS 0x2 /* y-axis position */
|
||||
+#define HDAPS_IDX_XPOS 0x4 /* x-axis position */
|
||||
+#define HDAPS_IDX_TEMP1 0x6 /* device temperature, in celcius */
|
||||
+#define HDAPS_IDX_YVAR 0x7 /* y-axis variance (what is this?) */
|
||||
+#define HDAPS_IDX_XVAR 0x9 /* x-axis variance (what is this?) */
|
||||
+#define HDAPS_IDX_TEMP2 0xb /* device temperature (again?) */
|
||||
+#define HDAPS_IDX_UNKNOWN 0xc /* what is this? */
|
||||
+#define HDAPS_IDX_READOUTS 0x1 /* readouts included in this read */
|
||||
+ /* First readout, if READOUTS>=1: */
|
||||
+#define HDAPS_IDX_YPOS1 0x2 /* y-axis position word */
|
||||
+#define HDAPS_IDX_XPOS1 0x4 /* x-axis position word */
|
||||
+#define HDAPS_IDX_TEMP1 0x6 /* device temperature in Celsius */
|
||||
+ /* Second readout, if READOUTS>=2: */
|
||||
+#define HDAPS_IDX_XPOS2 0x7 /* y-axis position word */
|
||||
+#define HDAPS_IDX_YPOS2 0x9 /* x-axis pisition word */
|
||||
+#define HDAPS_IDX_TEMP2 0xb /* device temperature in Celsius */
|
||||
+#define HDAPS_IDX_QUEUED 0xc /* Number of queued readouts left */
|
||||
+#define HDAPS_IDX_KMACT 0xd /* keyboard or mouse activity */
|
||||
|
||||
#define KEYBD_MASK 0x20 /* set if keyboard activity */
|
||||
@ -39,8 +41,8 @@
|
||||
|
||||
-#define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */
|
||||
-#define INIT_WAIT_MSECS 200 /* ... in 200ms increments */
|
||||
+#define STATE_HAVE_POS 0x01 /* have position data */
|
||||
+#define STATE_HAVE_VAR 0x02 /* have position and variance data */
|
||||
+#define EC_POLL_HZ 200 /* EC accelerometer sampling rate */
|
||||
+#define EC_FILTER_DEPTH 2 /* EC running average filter depth */
|
||||
+
|
||||
+#define READ_TIMEOUT_MSECS 100 /* wait this long for device read */
|
||||
+#define RETRY_MSECS 3 /* retry delay */
|
||||
@ -49,7 +51,7 @@
|
||||
#define HDAPS_INPUT_FUZZ 4 /* input event threshold */
|
||||
#define HDAPS_INPUT_FLAT 4
|
||||
|
||||
+#define KMACT_REMEMBER_PERIOD (HDAPS_POLL_PERIOD*2) /* k/m persistance */
|
||||
+#define KMACT_REMEMBER_PERIOD (HZ/10) /* k/m persistance */
|
||||
+
|
||||
static struct timer_list hdaps_timer;
|
||||
static struct platform_device *pdev;
|
||||
@ -58,19 +60,35 @@
|
||||
-static u8 km_activity;
|
||||
-static int rest_x;
|
||||
-static int rest_y;
|
||||
-
|
||||
-static DECLARE_MUTEX(hdaps_sem);
|
||||
+static unsigned int hdaps_force;
|
||||
+static int needs_calibration = 0;
|
||||
|
||||
-static DECLARE_MUTEX(hdaps_sem);
|
||||
-
|
||||
-/*
|
||||
- * __get_latch - Get the value from a given port. Callers must hold hdaps_sem.
|
||||
- */
|
||||
+/* Latest state read */
|
||||
+static int pos_x, pos_y; /* position */
|
||||
+static u8 temperature; /* temperature */
|
||||
+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;
|
||||
+
|
||||
+/* hdaps_read_row - read a row of data from the controller.
|
||||
+ * Also prefetches the next read, to reduce udelay busy-waiting.
|
||||
+ * If fast, do one quick attempt without retries.
|
||||
+ * Caller must hold controller lock.
|
||||
*/
|
||||
-static inline u8 __get_latch(u16 port)
|
||||
-{
|
||||
- return inb(port) & 0xff;
|
||||
-}
|
||||
-
|
||||
+static int hdaps_read_row(int fast, u8 *dataval) {
|
||||
+ int ret;
|
||||
+ struct tp_controller_row args, data;
|
||||
|
||||
-/*
|
||||
- * __check_latch - Check a port latch for a given value. Returns zero if the
|
||||
- * port contains the given value. Callers must hold hdaps_sem.
|
||||
@ -80,37 +98,46 @@
|
||||
- if (__get_latch(port) == val)
|
||||
- return 0;
|
||||
- return -EINVAL;
|
||||
-}
|
||||
+/* Latest state read */
|
||||
+static int pos_x, pos_y; /* position */
|
||||
+static int var_x, var_y; /* variance (what is this?) */
|
||||
+static int rest_x, rest_y; /* calibrated rest position */
|
||||
+static u8 temp1, temp2; /* temperatures */
|
||||
+
|
||||
+/* Last time we saw keyboard and mouse activity */
|
||||
+u64 last_keyboard_jiffies = INITIAL_JIFFIES;
|
||||
+u64 last_mouse_jiffies = INITIAL_JIFFIES;
|
||||
+ args.val[0x0] = 0x11;
|
||||
+ args.mask = 0x0001;
|
||||
+ data.mask = (1 << HDAPS_IDX_READOUTS) | (1 << HDAPS_IDX_KMACT) |
|
||||
+ (3 << HDAPS_IDX_YPOS1) | (3 << HDAPS_IDX_XPOS1) |
|
||||
+ (1 << HDAPS_IDX_TEMP1);
|
||||
+ if (fast)
|
||||
+ ret = tp_controller_try_read_row(&args, &data);
|
||||
+ else
|
||||
+ ret = tp_controller_read_row(&args, &data);
|
||||
+ memcpy(dataval, &data.val, TP_CONTROLLER_ROW_LEN);
|
||||
+ tp_controller_prefetch_row(&args);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
-/*
|
||||
- * __wait_latch - Wait up to 100us for a port latch to get a certain value,
|
||||
- * returning zero if the value is obtained. Callers must hold hdaps_sem.
|
||||
+ * __wait_latch - Wait for a port latch to get a certain value,
|
||||
+ * returning zero if the value is obtained. Callers must hold controller lock.
|
||||
+/* __hdaps_update - read current state and update global state variables.
|
||||
+ * Caller must hold controller lock.
|
||||
*/
|
||||
static int __wait_latch(u16 port, u8 val)
|
||||
-static int __wait_latch(u16 port, u8 val)
|
||||
+static int __hdaps_update(int fast)
|
||||
{
|
||||
unsigned int i;
|
||||
- unsigned int i;
|
||||
+ u8 row[TP_CONTROLLER_ROW_LEN];
|
||||
+ int ret;
|
||||
|
||||
- for (i = 0; i < 20; i++) {
|
||||
- if (!__check_latch(port, val))
|
||||
+ for (i = 0; i < 200; i++) {
|
||||
+ if (inb(port)==val)
|
||||
return 0;
|
||||
udelay(5);
|
||||
}
|
||||
@@ -108,82 +96,63 @@ static int __wait_latch(u16 port, u8 val
|
||||
return -EIO;
|
||||
}
|
||||
- return 0;
|
||||
- udelay(5);
|
||||
- }
|
||||
+ ret = hdaps_read_row(fast, row);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
- return -EIO;
|
||||
-}
|
||||
+ if (row[HDAPS_IDX_READOUTS]<1)
|
||||
+ return -EBUSY; /* no pending readout, try again later */
|
||||
|
||||
-/*
|
||||
- * __device_refresh - request a refresh from the accelerometer. Does not wait
|
||||
@ -122,7 +149,26 @@
|
||||
- if (inb(0x1604) != STATE_FRESH) {
|
||||
- outb(0x11, 0x1610);
|
||||
- outb(0x01, 0x161f);
|
||||
- }
|
||||
+ pos_x = *(s16*)(row+HDAPS_IDX_XPOS1) * (hdaps_invert?-1:1);
|
||||
+ pos_y = *(s16*)(row+HDAPS_IDX_YPOS1) * (hdaps_invert?-1:1);
|
||||
+
|
||||
+ /* Keyboard and mouse activity status is cleared as soon as it's read,
|
||||
+ * so applications will eat each other's events. Thus we remember any
|
||||
+ * event for KMACT_REMEMBER_PERIOD jiffies.
|
||||
+ */
|
||||
+ if (row[HDAPS_IDX_KMACT] & KEYBD_MASK)
|
||||
+ last_keyboard_jiffies = get_jiffies_64();
|
||||
+ if (row[HDAPS_IDX_KMACT] & MOUSE_MASK)
|
||||
+ last_mouse_jiffies = get_jiffies_64();
|
||||
+
|
||||
+ /* Temperatures */
|
||||
+ temperature = row[HDAPS_IDX_TEMP1];
|
||||
+
|
||||
+ if (needs_calibration) {
|
||||
+ rest_x = pos_x;
|
||||
+ rest_y = pos_y;
|
||||
+ needs_calibration = 0;
|
||||
}
|
||||
-}
|
||||
|
||||
-/*
|
||||
@ -134,79 +180,82 @@
|
||||
-{
|
||||
- __device_refresh();
|
||||
- return __wait_latch(0x1604, STATE_FRESH);
|
||||
-}
|
||||
-
|
||||
-/*
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
- * __device_complete - indicate to the accelerometer that we are done reading
|
||||
- * data, and then initiate an async refresh. Callers must hold hdaps_sem.
|
||||
+/* hdaps_read_row - read a row of data from the controller.
|
||||
+ * Also prefetches the next read, to reduce udelay busy-waiting.
|
||||
+ * If fast, do one quick attempt without retries.
|
||||
+ * Caller must hold controller lock.
|
||||
+ * hdaps_read_pair - reads the values from a pair of ports, placing the values
|
||||
+ * in the given pointers. Returns zero on success. Can sleep.
|
||||
+ * Retries until timeout if the accelerometer is not in ready status (common).
|
||||
+ * Does internal locking.
|
||||
*/
|
||||
-static inline void __device_complete(void)
|
||||
-{
|
||||
+static int hdaps_update(void)
|
||||
{
|
||||
- inb(0x161f);
|
||||
- inb(0x1604);
|
||||
- __device_refresh();
|
||||
+static int hdaps_read_row(int fast, u8* row) {
|
||||
+ int ret;
|
||||
+ if (fast)
|
||||
+ ret = tp_controller_try_read_row(0x11, 0x01, row);
|
||||
+ else
|
||||
+ ret = tp_controller_read_row(0x11, 0x01, row);
|
||||
+ tp_controller_prefetch_row(0x11, 0x01);
|
||||
+ int total, ret;
|
||||
+ for (total=READ_TIMEOUT_MSECS; total>0; total-=RETRY_MSECS) {
|
||||
+ ret = tp_controller_lock();
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ ret = __hdaps_update(0);
|
||||
+ tp_controller_unlock();
|
||||
+
|
||||
+ if (!ret)
|
||||
+ return 0;
|
||||
+ if (ret != -EBUSY)
|
||||
+ break;
|
||||
+ msleep(RETRY_MSECS);
|
||||
+ }
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
-/*
|
||||
/*
|
||||
- * hdaps_readb_one - reads a byte from a single I/O port, placing the value in
|
||||
- * the given pointer. Returns zero on success or a negative error on failure.
|
||||
- * Can sleep.
|
||||
+/* __hdaps_update - read current state and update global state variables.
|
||||
+ * Caller must hold controller lock.
|
||||
+ * hdaps_set_power - enable or disable power to the accelerometer.
|
||||
+ * Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
-static int hdaps_readb_one(unsigned int port, u8 *val)
|
||||
+static int __hdaps_update(int fast)
|
||||
{
|
||||
+ u8 row[TP_CONTROLLER_ROW_LEN];
|
||||
-{
|
||||
+static int hdaps_set_power(int on) {
|
||||
int ret;
|
||||
|
||||
-
|
||||
- down(&hdaps_sem);
|
||||
-
|
||||
- /* do a sync refresh -- we need to be sure that we read fresh data */
|
||||
- ret = __device_refresh_sync();
|
||||
+ ret = hdaps_read_row(fast, row);
|
||||
+ struct tp_controller_row args, data;
|
||||
+ args.val[0x0] = 0x14;
|
||||
+ args.val[0x1] = on ? 0x01 : 0x00;
|
||||
+ args.mask = 0x0003;
|
||||
+ data.mask = 0x8000;
|
||||
+ ret = tp_controller_read_row(&args, &data);
|
||||
if (ret)
|
||||
- goto out;
|
||||
-
|
||||
- *val = inb(port);
|
||||
- __device_complete();
|
||||
+ return ret;
|
||||
|
||||
-
|
||||
-out:
|
||||
- up(&hdaps_sem);
|
||||
- return ret;
|
||||
-}
|
||||
+ if (row[HDAPS_IDX_STATE]>=STATE_HAVE_POS) {
|
||||
+ pos_x = *(s16*)(row+HDAPS_IDX_XPOS) * (hdaps_invert?-1:1);
|
||||
+ pos_y = *(s16*)(row+HDAPS_IDX_YPOS) * (hdaps_invert?-1:1);
|
||||
+ } else
|
||||
+ return -EBUSY;
|
||||
+
|
||||
+ /* Don't insist on a "variance" readout; it's useless anyway. */
|
||||
+ if (row[HDAPS_IDX_STATE]>=STATE_HAVE_VAR) {
|
||||
+ var_x = *(s16*)(row+HDAPS_IDX_XVAR) * (hdaps_invert?-1:1);
|
||||
+ var_y = *(s16*)(row+HDAPS_IDX_YVAR) * (hdaps_invert?-1:1);
|
||||
+ }
|
||||
|
||||
-
|
||||
-/* __hdaps_read_pair - internal lockless helper for hdaps_read_pair(). */
|
||||
-static int __hdaps_read_pair(unsigned int port1, unsigned int port2,
|
||||
- int *x, int *y)
|
||||
-{
|
||||
- /* do a sync refresh -- we need to be sure that we read fresh data */
|
||||
- if (__device_refresh_sync())
|
||||
- return -EIO;
|
||||
+ return ret;
|
||||
+ if (data.val[0xF]!=0x00)
|
||||
return -EIO;
|
||||
-
|
||||
- *y = inw(port2);
|
||||
- *x = inw(port1);
|
||||
@ -217,70 +266,68 @@
|
||||
- if (hdaps_invert) {
|
||||
- *x = -*x;
|
||||
- *y = -*y;
|
||||
+ /* Keyboard and mouse activity status is cleared as soon as it's read,
|
||||
+ * so applications will eat each other's events. Thus we remember any
|
||||
+ * event for KMACT_REMEMBER_PERIOD jiffies.
|
||||
+ */
|
||||
+ if (row[HDAPS_IDX_KMACT] & KEYBD_MASK)
|
||||
+ last_keyboard_jiffies = get_jiffies_64();
|
||||
+ if (row[HDAPS_IDX_KMACT] & MOUSE_MASK)
|
||||
+ last_mouse_jiffies = get_jiffies_64();
|
||||
+
|
||||
+ /* Temperatures */
|
||||
+ temp1 = row[HDAPS_IDX_TEMP1];
|
||||
+ temp2 = row[HDAPS_IDX_TEMP2];
|
||||
+
|
||||
+ if (needs_calibration) {
|
||||
+ rest_x = pos_x;
|
||||
+ rest_y = pos_y;
|
||||
+ needs_calibration = 0;
|
||||
}
|
||||
|
||||
- }
|
||||
-
|
||||
return 0;
|
||||
@@ -192,16 +161,23 @@ static int __hdaps_read_pair(unsigned in
|
||||
}
|
||||
|
||||
/*
|
||||
* hdaps_read_pair - reads the values from a pair of ports, placing the values
|
||||
* in the given pointers. Returns zero on success. Can sleep.
|
||||
+ * Retries until timeout if the accelerometer is not in ready status (common).
|
||||
+ * Does internal locking.
|
||||
- * 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_set_ec_config - set accelerometer parameters.
|
||||
+ * freq - embedded controller sampling rate
|
||||
+ * depth - embedded controller running average filter depth
|
||||
+ * Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
-static int hdaps_read_pair(unsigned int port1, unsigned int port2,
|
||||
- int *val1, int *val2)
|
||||
+static int hdaps_update(void)
|
||||
{
|
||||
- int ret;
|
||||
-{
|
||||
+static int hdaps_set_ec_config(int freq, int depth) {
|
||||
int ret;
|
||||
-
|
||||
- down(&hdaps_sem);
|
||||
- ret = __hdaps_read_pair(port1, port2, val1, val2);
|
||||
- up(&hdaps_sem);
|
||||
+ int total, ret;
|
||||
+ for (total=READ_TIMEOUT_MSECS; total>0; total-=RETRY_MSECS) {
|
||||
+ tp_controller_lock();
|
||||
+ ret = __hdaps_update(0);
|
||||
+ tp_controller_unlock();
|
||||
|
||||
+ if (!ret)
|
||||
+ return 0;
|
||||
+ if (ret != -EBUSY)
|
||||
+ break;
|
||||
+ msleep(RETRY_MSECS);
|
||||
+ }
|
||||
return ret;
|
||||
-
|
||||
- return ret;
|
||||
+ struct tp_controller_row args, data;
|
||||
+ args.val[0x0] = 0x10;
|
||||
+ args.val[0x1] = (u8)freq;
|
||||
+ args.val[0x2] = (u8)(freq>>8);
|
||||
+ args.val[0x3] = depth;
|
||||
+ args.mask = 0x000F;
|
||||
+ data.mask = 0x8000;
|
||||
+ ret = tp_controller_read_row(&args, &data);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ if (data.val[0xF]!=0x00)
|
||||
+ return -EIO;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
@@ -211,30 +187,30 @@ static int hdaps_read_pair(unsigned int
|
||||
/*
|
||||
* hdaps_device_init - initialize the accelerometer. Returns zero on success
|
||||
* and negative error code on failure. Can sleep.
|
||||
*/
|
||||
+#define BAD_INIT(msg) do { err_msg = msg; goto bad; } while (0)
|
||||
static int hdaps_device_init(void)
|
||||
{
|
||||
- int total, ret = -ENXIO;
|
||||
-
|
||||
- down(&hdaps_sem);
|
||||
-
|
||||
+ int ret;
|
||||
+ struct tp_controller_row args, data;
|
||||
+ u8 status;
|
||||
+ const char *err_msg = "";
|
||||
|
||||
- outb(0x13, 0x1610);
|
||||
- outb(0x01, 0x161f);
|
||||
- if (__wait_latch(0x161f, 0x00))
|
||||
- goto out;
|
||||
-
|
||||
+ ret = tp_controller_lock();
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
- /*
|
||||
- * Most ThinkPads return 0x01.
|
||||
- *
|
||||
@ -292,43 +339,72 @@
|
||||
- if (__check_latch(0x1611, 0x03) &&
|
||||
- __check_latch(0x1611, 0x02) &&
|
||||
- __check_latch(0x1611, 0x01))
|
||||
+ int ret = -ENXIO;
|
||||
+ u8 row[TP_CONTROLLER_ROW_LEN];
|
||||
+ u8 status;
|
||||
+
|
||||
+ tp_controller_lock();
|
||||
+
|
||||
+ if (tp_controller_read_row(0x13, 0x01, row))
|
||||
+ goto out;
|
||||
+ if (row[0xf]!=0x00)
|
||||
+ goto out;
|
||||
+ status = row[1];
|
||||
- goto out;
|
||||
+ args.val[0x0] = 0x13; /* Get accelerometer info */
|
||||
+ args.mask=0x0001;
|
||||
+ data.mask=0x8002;
|
||||
+ if (tp_controller_read_row(&args, &data))
|
||||
+ BAD_INIT("read1");
|
||||
+ if (data.val[0xF]==0x80 || data.val[0x1]==0x00)
|
||||
+ BAD_INIT("accelerometer not available");
|
||||
+ if (data.val[0xF]!=0x00)
|
||||
+ BAD_INIT("check1");
|
||||
+ status = data.val[0x1];
|
||||
+
|
||||
+ if (status != 0x04 && /* Semi-invertex axes (ThinkPad T60) */
|
||||
+ if (status != 0x01 && /* Normal axes (ThinkPad T43) */
|
||||
+ status != 0x02 && /* Already initialized */
|
||||
+ status != 0x03 && /* Invertex axes (ThinkPad R50p, T41p, R42p) */
|
||||
+ status != 0x02 && /* Chip already initialized */
|
||||
+ status != 0x01 ) /* Normal axes */
|
||||
+ status != 0x04 && /* Swapped axes (ThinkPad T60) */
|
||||
+ status != 0x05 ) /* Inverted X axis (ThinkPad X60s) */
|
||||
+ {
|
||||
+ printk(KERN_ERR "hdaps: initial latch check bad (0x%02x).\n",
|
||||
+ status);
|
||||
goto out;
|
||||
+ BAD_INIT("latch");
|
||||
+ }
|
||||
|
||||
printk(KERN_DEBUG "hdaps: initial latch check good (0x%02x).\n",
|
||||
- __get_latch(0x1611));
|
||||
+ status);
|
||||
|
||||
outb(0x17, 0x1610);
|
||||
outb(0x81, 0x1611);
|
||||
@@ -259,27 +235,18 @@ static int hdaps_device_init(void)
|
||||
outb(0x01, 0x161f);
|
||||
if (__wait_latch(0x161f, 0x00))
|
||||
goto out;
|
||||
- outb(0x17, 0x1610);
|
||||
- outb(0x81, 0x1611);
|
||||
- outb(0x01, 0x161f);
|
||||
- if (__wait_latch(0x161f, 0x00))
|
||||
- goto out;
|
||||
- if (__wait_latch(0x1611, 0x00))
|
||||
- goto out;
|
||||
- if (__wait_latch(0x1612, 0x60))
|
||||
- goto out;
|
||||
- if (__wait_latch(0x1613, 0x00))
|
||||
- goto out;
|
||||
- outb(0x14, 0x1610);
|
||||
- outb(0x01, 0x1611);
|
||||
- outb(0x01, 0x161f);
|
||||
- if (__wait_latch(0x161f, 0x00))
|
||||
- goto out;
|
||||
- outb(0x10, 0x1610);
|
||||
- outb(0xc8, 0x1611);
|
||||
- outb(0x00, 0x1612);
|
||||
- outb(0x02, 0x1613);
|
||||
- outb(0x01, 0x161f);
|
||||
- if (__wait_latch(0x161f, 0x00))
|
||||
- goto out;
|
||||
- if (__device_refresh_sync())
|
||||
- goto out;
|
||||
- if (__wait_latch(0x1611, 0x00))
|
||||
- goto out;
|
||||
-
|
||||
+ args.val[0x0] = 0x17; /* Function unknown */
|
||||
+ args.val[0x1] = 0x81;
|
||||
+ args.mask = 0x0003;
|
||||
+ data.mask = 0x800E;
|
||||
+ if (tp_controller_read_row(&args, &data))
|
||||
+ BAD_INIT("read2");
|
||||
+ if (data.val[0x1]!=0x00 ||
|
||||
+ data.val[0x2]!=0x60 ||
|
||||
+ data.val[0x3]!=0x00 ||
|
||||
+ data.val[0xF]!=0x00)
|
||||
+ BAD_INIT("check2");
|
||||
|
||||
- /* we have done our dance, now let's wait for the applause */
|
||||
- for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
|
||||
- int x, y;
|
||||
@ -339,25 +415,36 @@
|
||||
- ret = 0;
|
||||
- break;
|
||||
- }
|
||||
+ tp_controller_invalidate();
|
||||
+ udelay(200);
|
||||
+ if (hdaps_set_power(1))
|
||||
+ BAD_INIT("hdaps_set_power");
|
||||
|
||||
- msleep(INIT_WAIT_MSECS);
|
||||
- }
|
||||
+ /* Just prefetch instead of reading, to avoid ~1sec delay on load */
|
||||
+ ret = tp_controller_prefetch_row(0x11, 0x01);
|
||||
+ goto good;
|
||||
+ if (hdaps_set_ec_config(EC_POLL_HZ, EC_FILTER_DEPTH))
|
||||
+ BAD_INIT("hdaps_set_ec_config");
|
||||
|
||||
out:
|
||||
-out:
|
||||
- up(&hdaps_sem);
|
||||
+ printk(KERN_ERR "hdaps: init failed!\n");
|
||||
+ tp_controller_invalidate();
|
||||
+ udelay(200);
|
||||
+
|
||||
+ /* Just prefetch instead of reading, to avoid ~1sec delay on load */
|
||||
+ args.val[0x0] = 0x11;
|
||||
+ args.mask = 0x0001;
|
||||
+ ret = tp_controller_prefetch_row(&args);
|
||||
+ if (ret)
|
||||
+ BAD_INIT("prefetch");
|
||||
+ goto good;
|
||||
+bad:
|
||||
+ ret = -ENXIO;
|
||||
+ printk(KERN_ERR "hdaps: init failed (%s)\n", err_msg);
|
||||
+good:
|
||||
+ tp_controller_invalidate();
|
||||
+ tp_controller_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -298,13 +265,25 @@ static int hdaps_probe(struct platform_d
|
||||
@@ -298,13 +301,25 @@ static int hdaps_probe(struct platform_d
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -384,7 +471,7 @@
|
||||
.resume = hdaps_resume,
|
||||
.driver = {
|
||||
.name = "hdaps",
|
||||
@@ -313,34 +292,42 @@ static struct platform_driver hdaps_driv
|
||||
@@ -313,34 +328,42 @@ static struct platform_driver hdaps_driv
|
||||
};
|
||||
|
||||
/*
|
||||
@ -411,7 +498,7 @@
|
||||
/* Cannot sleep. Try nonblockingly. If we fail, try again later. */
|
||||
- if (down_trylock(&hdaps_sem)) {
|
||||
- mod_timer(&hdaps_timer,jiffies + HDAPS_POLL_PERIOD);
|
||||
+ if (tp_controller_trylock())
|
||||
+ if (tp_controller_try_lock())
|
||||
+ goto keep_active;
|
||||
+
|
||||
+ ret = __hdaps_update(1); /* fast update, we're in softirq context */
|
||||
@ -428,38 +515,35 @@
|
||||
-
|
||||
- input_report_abs(hdaps_idev, ABS_X, x - rest_x);
|
||||
- input_report_abs(hdaps_idev, ABS_Y, y - rest_y);
|
||||
- input_sync(hdaps_idev);
|
||||
-
|
||||
+keep_active:
|
||||
+ /* Even if we failed now, pos_x,y may have been updated earlier: */
|
||||
+ input_report_abs(hdaps_idev, ABS_X, pos_x - rest_x);
|
||||
+ input_report_abs(hdaps_idev, ABS_Y, pos_y - rest_y);
|
||||
input_sync(hdaps_idev);
|
||||
-
|
||||
mod_timer(&hdaps_timer, jiffies + HDAPS_POLL_PERIOD);
|
||||
-
|
||||
-out:
|
||||
- up(&hdaps_sem);
|
||||
+ /* Even if we failed now, pos_x,y may have been updated earlier: */
|
||||
+ input_report_abs(hdaps_idev, ABS_X, pos_x - rest_x);
|
||||
+ input_report_abs(hdaps_idev, ABS_Y, pos_y - rest_y);
|
||||
+ input_sync(hdaps_idev);
|
||||
}
|
||||
|
||||
|
||||
@@ -349,65 +336,55 @@ out:
|
||||
@@ -349,65 +372,37 @@ out:
|
||||
static ssize_t hdaps_position_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
- int ret, x, y;
|
||||
-
|
||||
- ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y);
|
||||
+ int ret = hdaps_update();
|
||||
if (ret)
|
||||
return ret;
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- return sprintf(buf, "(%d,%d)\n", x, y);
|
||||
+ return sprintf(buf, "(%d,%d)\n", pos_x, pos_y);
|
||||
}
|
||||
|
||||
static ssize_t hdaps_variance_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
-}
|
||||
-
|
||||
-static ssize_t hdaps_variance_show(struct device *dev,
|
||||
- struct device_attribute *attr, char *buf)
|
||||
-{
|
||||
- int ret, x, y;
|
||||
-
|
||||
- ret = hdaps_read_pair(HDAPS_PORT_XVAR, HDAPS_PORT_YVAR, &x, &y);
|
||||
@ -468,26 +552,24 @@
|
||||
return ret;
|
||||
-
|
||||
- return sprintf(buf, "(%d,%d)\n", x, y);
|
||||
+ return sprintf(buf, "(%d,%d)\n", var_x, var_y);
|
||||
}
|
||||
|
||||
static ssize_t hdaps_temp1_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
-}
|
||||
-
|
||||
-static ssize_t hdaps_temp1_show(struct device *dev,
|
||||
- struct device_attribute *attr, char *buf)
|
||||
-{
|
||||
- u8 temp;
|
||||
- int ret;
|
||||
-
|
||||
- ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp);
|
||||
- if (ret < 0)
|
||||
+ int ret = hdaps_update();
|
||||
+ if (ret)
|
||||
return ret;
|
||||
- return ret;
|
||||
-
|
||||
- return sprintf(buf, "%u\n", temp);
|
||||
+ return sprintf(buf, "%u\n", temp1);
|
||||
+ return sprintf(buf, "(%d,%d)\n", pos_x, pos_y);
|
||||
}
|
||||
|
||||
static ssize_t hdaps_temp2_show(struct device *dev,
|
||||
-static ssize_t hdaps_temp2_show(struct device *dev,
|
||||
+static ssize_t hdaps_temperature_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
- u8 temp;
|
||||
@ -500,7 +582,7 @@
|
||||
return ret;
|
||||
-
|
||||
- return sprintf(buf, "%u\n", temp);
|
||||
+ return sprintf(buf, "%u\n", temp2);
|
||||
+ return sprintf(buf, "%u\n", temperature);
|
||||
}
|
||||
|
||||
static ssize_t hdaps_keyboard_activity_show(struct device *dev,
|
||||
@ -524,7 +606,7 @@
|
||||
}
|
||||
|
||||
static ssize_t hdaps_calibrate_show(struct device *dev,
|
||||
@@ -420,10 +397,7 @@ static ssize_t hdaps_calibrate_store(str
|
||||
@@ -420,10 +415,7 @@ static ssize_t hdaps_calibrate_store(str
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
@ -535,14 +617,36 @@
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -530,20 +504,18 @@ static int __init hdaps_init(void)
|
||||
@@ -449,9 +441,8 @@ static ssize_t hdaps_invert_store(struct
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL);
|
||||
-static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL);
|
||||
-static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL);
|
||||
-static DEVICE_ATTR(temp2, 0444, hdaps_temp2_show, NULL);
|
||||
+static DEVICE_ATTR(temp1, 0444, hdaps_temperature_show, NULL);
|
||||
+ /* named temp1 instead of temperature for backward compatibility */
|
||||
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);
|
||||
@@ -459,9 +450,7 @@ static DEVICE_ATTR(invert, 0644, hdaps_i
|
||||
|
||||
static struct attribute *hdaps_attributes[] = {
|
||||
&dev_attr_position.attr,
|
||||
- &dev_attr_variance.attr,
|
||||
&dev_attr_temp1.attr,
|
||||
- &dev_attr_temp2.attr,
|
||||
&dev_attr_keyboard_activity.attr,
|
||||
&dev_attr_mouse_activity.attr,
|
||||
&dev_attr_calibrate.attr,
|
||||
@@ -542,20 +531,18 @@ static int __init hdaps_init(void)
|
||||
{ .ident = NULL }
|
||||
};
|
||||
|
||||
- if (!dmi_check_system(hdaps_whitelist)) {
|
||||
+ if (!(dmi_check_system(hdaps_whitelist) || hdaps_force)) {
|
||||
printk(KERN_WARNING "hdaps: supported laptop not found!\n");
|
||||
ret = -ENXIO;
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -561,7 +665,7 @@
|
||||
|
||||
pdev = platform_device_register_simple("hdaps", -1, NULL, 0);
|
||||
if (IS_ERR(pdev)) {
|
||||
@@ -561,8 +533,8 @@ static int __init hdaps_init(void)
|
||||
@@ -573,8 +560,8 @@ static int __init hdaps_init(void)
|
||||
goto out_group;
|
||||
}
|
||||
|
||||
@ -572,7 +676,7 @@
|
||||
|
||||
/* initialize the input class */
|
||||
hdaps_idev->name = "hdaps";
|
||||
@@ -575,11 +547,7 @@ static int __init hdaps_init(void)
|
||||
@@ -587,11 +574,7 @@ static int __init hdaps_init(void)
|
||||
|
||||
input_register_device(hdaps_idev);
|
||||
|
||||
@ -585,7 +689,7 @@
|
||||
|
||||
printk(KERN_INFO "hdaps: driver successfully loaded.\n");
|
||||
return 0;
|
||||
@@ -590,8 +558,6 @@ out_device:
|
||||
@@ -602,8 +585,6 @@ out_device:
|
||||
platform_device_unregister(pdev);
|
||||
out_driver:
|
||||
platform_driver_unregister(&hdaps_driver);
|
||||
@ -594,7 +698,7 @@
|
||||
out:
|
||||
printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret);
|
||||
return ret;
|
||||
@@ -604,7 +570,6 @@ static void __exit hdaps_exit(void)
|
||||
@@ -616,7 +597,6 @@ static void __exit hdaps_exit(void)
|
||||
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
|
||||
platform_device_unregister(pdev);
|
||||
platform_driver_unregister(&hdaps_driver);
|
||||
@ -602,7 +706,7 @@
|
||||
|
||||
printk(KERN_INFO "hdaps: driver unloaded.\n");
|
||||
}
|
||||
@@ -615,6 +580,9 @@ module_exit(hdaps_exit);
|
||||
@@ -627,6 +607,9 @@ module_exit(hdaps_exit);
|
||||
module_param_named(invert, hdaps_invert, bool, 0);
|
||||
MODULE_PARM_DESC(invert, "invert data along each axis");
|
||||
|
||||
|
308
tp_base.c
308
tp_base.c
@ -1,13 +1,16 @@
|
||||
/*
|
||||
* tp_base.c - coordinate access to ThinkPad-specific hardware resources
|
||||
*
|
||||
* ThinkPad laptops have a controller, accessible at ports 0x1600-0x161F,
|
||||
* which provides system management services (currently known: battery
|
||||
* information and accelerometer readouts). This driver coordinates access
|
||||
* to the controller, and abstracts it to the extent possible.
|
||||
* 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:
|
||||
* "H8S/2104B Group Hardware Manual",
|
||||
* http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2005 Shem Multinymous <multinymous@gmail.com>
|
||||
* Copyright (C) 2006 Shem Multinymous <multinymous@gmail.com>
|
||||
*
|
||||
* 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
|
||||
@ -33,188 +36,260 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define TP_VERSION "0.21"
|
||||
#define TP_VERSION "0.22"
|
||||
|
||||
MODULE_AUTHOR("Shem Multinymous");
|
||||
MODULE_DESCRIPTION("ThinkPad embedded controller hardware access");
|
||||
MODULE_VERSION(TP_VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* IO ports used by embedded controller LPC channel 3: */
|
||||
#define TPC_BASE_PORT 0x1600
|
||||
#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_READ_RETRIES 100
|
||||
#define TPC_READ_UDELAY 5
|
||||
/* 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 */
|
||||
#define H8S_STR3_SWMF 0x10 /* Slave Write Mode Flag */
|
||||
#define H8S_STR3_MASK 0xF0 /* All bits we care about in STR3 */
|
||||
|
||||
/* Timeouts and retries */
|
||||
#define TPC_READ_RETRIES 150
|
||||
#define TPC_READ_NDELAY 500
|
||||
#define TPC_REQUEST_RETRIES 100
|
||||
#define TPC_REQUEST_NDELAY 10
|
||||
#define TPC_PREFETCH_TIMEOUT (HZ/10) /* invalidate prefetch after 0.1sec */
|
||||
#define TPC_PREFETCH_INVALID INITIAL_JIFFIES
|
||||
|
||||
static DECLARE_MUTEX(tp_controller_sem);
|
||||
|
||||
/* Module parameters: */
|
||||
static int tp_debug = 0;
|
||||
module_param_named(debug, tp_debug, int, 0600);
|
||||
MODULE_PARM_DESC(debug, "Debug level (0=off, 1=on)");
|
||||
|
||||
#define DPRINTK(fmt, args...) { if (tp_debug) printk(KERN_DEBUG "tp_base: " fmt, ## args); }
|
||||
/* A few macros for printk()ing: */
|
||||
#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) \
|
||||
MSG_FMT("%s: (0x%02x:0x%02x)->0x%02x", \
|
||||
msg, args->val[0x0], args->val[0xF], code)
|
||||
|
||||
static u64 prefetch_jiffies = TPC_PREFETCH_INVALID; /* time of prefetch */
|
||||
static u8 prefetch_arg1610, prefetch_arg161F; /* args of last prefetch */
|
||||
/* State of request prefetching: */
|
||||
static u8 prefetch_arg0, prefetch_argF; /* Args of last prefetch */
|
||||
static u64 prefetch_jiffies; /* time of prefetch, or: */
|
||||
#define TPC_PREFETCH_NONE INITIAL_JIFFIES /* - No prefetch */
|
||||
#define TPC_PREFETCH_JUNK INITIAL_JIFFIES+1 /* - Ignore prefetch */
|
||||
|
||||
/*** Functionality ***/
|
||||
/* Locking: */
|
||||
|
||||
void tp_controller_lock(void) {
|
||||
down(&tp_controller_sem);
|
||||
static DECLARE_MUTEX(tp_controller_mutex);
|
||||
|
||||
int tp_controller_lock(void)
|
||||
{
|
||||
int ret;
|
||||
ret = down_interruptible(&tp_controller_mutex);
|
||||
if (ret)
|
||||
DPRINTK("tp_controller mutex down interrupted: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_lock);
|
||||
|
||||
int tp_controller_trylock(void) {
|
||||
return down_trylock(&tp_controller_sem);
|
||||
int tp_controller_try_lock(void)
|
||||
{
|
||||
return down_trylock(&tp_controller_mutex);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_trylock);
|
||||
EXPORT_SYMBOL_GPL(tp_controller_try_lock);
|
||||
|
||||
void tp_controller_unlock(void) {
|
||||
up(&tp_controller_sem);
|
||||
void tp_controller_unlock(void)
|
||||
{
|
||||
up(&tp_controller_mutex);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_unlock);
|
||||
|
||||
/* Tell embedded controller to prepare a row */
|
||||
static int tp_controller_request_row(u8 arg1610, u8 arg161F) {
|
||||
u8 status;
|
||||
status = inb(0x1604);
|
||||
if (status&0x40) { /* readout data already pending? */
|
||||
inb(0x161F); /* marks end of previous transaction */
|
||||
DPRINTK("readout already pending (0x%02x,0x%02x)->0x%02x\n",
|
||||
arg1610, arg161F, status);
|
||||
static int tp_controller_request_row(struct tp_controller_row *args)
|
||||
{
|
||||
u8 str3;
|
||||
int i;
|
||||
|
||||
/* EC protocol requires write to TWR0 (function code): */
|
||||
if (!(args->mask & 0x0001)) {
|
||||
printk(KERN_ERR MSG_FMT("bad args->mask=0x%02x", args->mask));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* EC protocol requires write to TWR15. Default to 0x01: */
|
||||
if (!(args->mask & 0x8000))
|
||||
args->val[0xF] = 0x01;
|
||||
|
||||
/* Check initial STR3 status: */
|
||||
str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
|
||||
if (str3 & H8S_STR3_OBF3B) { /* data already pending */
|
||||
inb(TPC_TWR15_PORT); /* marks end of previous transaction */
|
||||
if (prefetch_jiffies == TPC_PREFETCH_NONE)
|
||||
printk(KERN_WARNING
|
||||
ARG_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));
|
||||
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));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* send 1st argument */
|
||||
outb(arg1610, 0x1610);
|
||||
status = inb(0x1604);
|
||||
if (status!=0x20) { /* not accepted? */
|
||||
DPRINTK("arg1610 not accepted (0x%02x,0x%02x)->0x%02x\n",
|
||||
arg1610, arg161F, status);
|
||||
if (tp_debug) { /* Someone else touching the EC? */
|
||||
static int dumps_left=20;
|
||||
if (dumps_left) { /* May occur often, don't flood. */
|
||||
printk("" "tp_base: arg1610 not accepted:\n");
|
||||
dump_stack();
|
||||
dumps_left--;
|
||||
}
|
||||
/* Send TWR0MW: */
|
||||
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));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Send TWR1 through TWR14: */
|
||||
for (i=1; i<TP_CONTROLLER_ROW_LEN-1; i++)
|
||||
if ((args->mask>>i)&1)
|
||||
outb(args->val[i], TPC_TWR0_PORT+i);
|
||||
|
||||
/* Send TWR15. This marks end of command. */
|
||||
outb(args->val[0xF], TPC_TWR15_PORT);
|
||||
|
||||
/* Wait until EC starts writing its reply (~60ns on average).
|
||||
* Releasing locks before this happens may cause an EC hang
|
||||
* due to firmware bug!
|
||||
*/
|
||||
for (i=0; i<TPC_REQUEST_RETRIES; ++i) {
|
||||
str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
|
||||
if (str3 & H8S_STR3_SWMF) /* EC started replying */
|
||||
return 0;
|
||||
else if (str3==(H8S_STR3_IBF3B|H8S_STR3_MWMF) ||
|
||||
str3==0x00) /* normal progress, wait it out */
|
||||
ndelay(TPC_REQUEST_NDELAY);
|
||||
else { /* weird EC status */
|
||||
printk(KERN_WARNING
|
||||
ARG_FMT("bad end STR3", str3));
|
||||
return -EIO;
|
||||
}
|
||||
return -EBUSY; /* the EC is handling a prior request */
|
||||
}
|
||||
|
||||
/* send 2nd argument */
|
||||
outb(arg161F, 0x161F);
|
||||
status = inb(0x1604);
|
||||
if (status==0x20) { /* not responding? */
|
||||
printk(KERN_WARNING
|
||||
"tp_base: 161F rejected (0x%02x,0x%02x)->0x%02x\n",
|
||||
arg1610, arg161F, status);
|
||||
return -EIO; /* this is abnormal */
|
||||
}
|
||||
|
||||
return 0;
|
||||
printk(KERN_WARNING ARG_FMT("EC is mysteriously silent", str3));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Read current row data from the controller, assuming it's already
|
||||
* requested.
|
||||
*/
|
||||
static int tp_controller_read_data(u8* buf) {
|
||||
static int tp_controller_read_data(struct tp_controller_row *data)
|
||||
{
|
||||
int i;
|
||||
u8 status = inb(0x1604);
|
||||
/* After writing to ports 0x1610 and 0x161F, the status register at
|
||||
* IO port 0x1604 assumes the sequence of values 0xA0, 0x00, 0x10 and
|
||||
* finally bit 0x40 goes up (usually 0x50) signalling data ready.
|
||||
* It takes about a dozen nanosecs total, with very high variance.
|
||||
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.
|
||||
* It takes about a few dozen nanosecs total, with very high variance.
|
||||
*/
|
||||
if (status==0xA0 || status==0x00 || status==0x10) {
|
||||
if (str3==(H8S_STR3_IBF3B|H8S_STR3_MWMF) ||
|
||||
str3==0x00 ||
|
||||
str3==H8S_STR3_SWMF )
|
||||
return -EBUSY; /* not ready yet */
|
||||
}
|
||||
if (!(status&0x40)) {
|
||||
printk(KERN_WARNING "tp_base: bad status (0x%02x) in read\n",
|
||||
status);
|
||||
/* Finally, it signals output buffer full: */
|
||||
if (str3 != (H8S_STR3_OBF3B|H8S_STR3_SWMF)) {
|
||||
printk(KERN_WARNING
|
||||
MSG_FMT("bad initial STR3 (0x%02x)", str3));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Data in IO ports 0x1610-0x161F. Reading 0x161F ends transaction. */
|
||||
for (i=0; i<TP_CONTROLLER_ROW_LEN; ++i)
|
||||
buf[i] = inb(0x1610+i);
|
||||
if (inb(0x1604)&0x40) /* readout still pending? */
|
||||
printk(KERN_WARNING "tp_base: data pending after read\n");
|
||||
/* Read first byte (signals start of read transactions): */
|
||||
data->val[0] = inb(TPC_TWR0_PORT);
|
||||
/* Optionally read 14 more bytes: */
|
||||
for (i=1; i<TP_CONTROLLER_ROW_LEN-1; ++i)
|
||||
if ((data->mask >> i)&1)
|
||||
data->val[i] = inb(TPC_TWR0_PORT+i);
|
||||
/* Read last byte from 0x161F (signals end of read transaction): */
|
||||
data->val[0xF] = inb(TPC_TWR15_PORT);
|
||||
|
||||
/* Readout still pending? */
|
||||
str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
|
||||
if (str3 & H8S_STR3_OBF3B)
|
||||
printk(KERN_WARNING
|
||||
MSG_FMT("OBF3B=1 after read (0x%02x)", str3));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Is the given row currently prefetched? */
|
||||
static int tp_controller_is_row_fetched(u8 arg1610, u8 arg161F) {
|
||||
return (prefetch_jiffies != TPC_PREFETCH_INVALID) &&
|
||||
(prefetch_arg1610 == arg1610) &&
|
||||
(prefetch_arg161F == arg161F) &&
|
||||
/* 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(struct tp_controller_row *args)
|
||||
{
|
||||
return (prefetch_jiffies != TPC_PREFETCH_NONE) &&
|
||||
(prefetch_jiffies != TPC_PREFETCH_JUNK) &&
|
||||
(prefetch_arg0 == args->val[0]) &&
|
||||
(prefetch_argF == args->val[0xF]) &&
|
||||
(get_jiffies_64() < prefetch_jiffies + TPC_PREFETCH_TIMEOUT);
|
||||
}
|
||||
|
||||
/* Read a row from the embedded controller */
|
||||
int tp_controller_read_row(u8 arg1610, u8 arg161F, u8* buf) {
|
||||
int tp_controller_read_row(struct tp_controller_row *args,
|
||||
struct tp_controller_row *data)
|
||||
{
|
||||
int retries, ret;
|
||||
if (tp_controller_is_row_fetched(arg1610,arg161F))
|
||||
|
||||
if (tp_controller_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(arg1610, arg161F);
|
||||
ret = tp_controller_request_row(args);
|
||||
if (!ret)
|
||||
goto read_row;
|
||||
if (ret != -EBUSY)
|
||||
break;
|
||||
udelay(TPC_READ_UDELAY);
|
||||
ndelay(TPC_READ_NDELAY);
|
||||
}
|
||||
printk(KERN_ERR
|
||||
"thinkpad controller read(0x%02x,0x%02x): failed requesting row\n",
|
||||
arg1610, arg161F);
|
||||
printk(KERN_ERR ARG_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(buf);
|
||||
ret = tp_controller_read_data(data);
|
||||
if (!ret)
|
||||
goto out;
|
||||
if (ret!=-EBUSY)
|
||||
break;
|
||||
udelay(TPC_READ_UDELAY);
|
||||
ndelay(TPC_READ_NDELAY);
|
||||
}
|
||||
|
||||
printk(KERN_ERR
|
||||
"thinkpad controller read(0x%02x,0x%02x): failed waiting for data\n",
|
||||
arg1610, arg161F);
|
||||
printk(KERN_ERR ARG_FMT("failed waiting for data", ret));
|
||||
|
||||
out:
|
||||
prefetch_jiffies = TPC_PREFETCH_INVALID;
|
||||
prefetch_jiffies = TPC_PREFETCH_JUNK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_read_row);
|
||||
|
||||
/* Read a prefetched row from the controller. Don't fetch, don't retry. */
|
||||
int tp_controller_try_read_row(u8 arg1610, u8 arg161F, u8* buf) {
|
||||
int tp_controller_try_read_row(struct tp_controller_row *args,
|
||||
struct tp_controller_row *data)
|
||||
{
|
||||
int ret;
|
||||
if (!tp_controller_is_row_fetched(arg1610,arg161F)) {
|
||||
DPRINTK("try_read_row(0x%02x,0x%02x): row not fetched\n",
|
||||
arg1610, arg161F);
|
||||
return -ENODATA;
|
||||
if (!tp_controller_is_row_fetched(args)) {
|
||||
ret = -ENODATA;
|
||||
} else {
|
||||
prefetch_jiffies = TPC_PREFETCH_INVALID; /* data eaten up */
|
||||
ret = tp_controller_read_data(buf);
|
||||
if (ret)
|
||||
DPRINTK("try_read_row(0x%02x,0x%02x): "
|
||||
"read failed with %d\n",
|
||||
arg1610, arg161F, ret);
|
||||
return ret;
|
||||
ret = tp_controller_read_data(data);
|
||||
if (!ret)
|
||||
prefetch_jiffies = TPC_PREFETCH_NONE; /* eaten up */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_try_read_row);
|
||||
@ -222,22 +297,25 @@ EXPORT_SYMBOL_GPL(tp_controller_try_read_row);
|
||||
/* Prefech a row from the controller. This is a one-shot prefetch
|
||||
* attempt, without retries or delays.
|
||||
*/
|
||||
int tp_controller_prefetch_row(u8 arg1610, u8 arg161F) {
|
||||
int ret = tp_controller_request_row(arg1610, arg161F);
|
||||
int tp_controller_prefetch_row(struct tp_controller_row *args)
|
||||
{
|
||||
int ret;
|
||||
ret = tp_controller_request_row(args);
|
||||
if (ret) {
|
||||
prefetch_jiffies = TPC_PREFETCH_INVALID;
|
||||
prefetch_jiffies = TPC_PREFETCH_JUNK;
|
||||
} else {
|
||||
prefetch_jiffies = get_jiffies_64();
|
||||
prefetch_arg1610 = arg1610;
|
||||
prefetch_arg161F = arg161F;
|
||||
prefetch_arg0 = args->val[0x0];
|
||||
prefetch_argF = args->val[0xF];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_prefetch_row);
|
||||
|
||||
void tp_controller_invalidate(void) {
|
||||
prefetch_jiffies = TPC_PREFETCH_INVALID;
|
||||
void tp_controller_invalidate(void)
|
||||
{
|
||||
prefetch_jiffies = TPC_PREFETCH_JUNK;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(tp_controller_invalidate);
|
||||
@ -253,7 +331,8 @@ EXPORT_SYMBOL_GPL(tp_controller_invalidate);
|
||||
} \
|
||||
}
|
||||
|
||||
static int is_thinkpad(void) {
|
||||
static int is_thinkpad(void)
|
||||
{
|
||||
struct dmi_system_id tp_whitelist[] = {
|
||||
TP_DMI_MATCH("LENOVO","ThinkPad"),
|
||||
TP_DMI_MATCH("IBM","ThinkPad"),
|
||||
@ -266,7 +345,8 @@ static int is_thinkpad(void) {
|
||||
|
||||
/*** Init and cleanup ***/
|
||||
|
||||
static int __init tp_base_init(void) {
|
||||
static int __init tp_base_init(void)
|
||||
{
|
||||
if (!is_thinkpad()) {
|
||||
printk(KERN_ERR "tp_base: not a ThinkPad!\n");
|
||||
return -ENODEV;
|
||||
@ -277,14 +357,16 @@ static int __init tp_base_init(void) {
|
||||
printk(KERN_ERR "tp_base: cannot claim ports %#x-%#x"
|
||||
" (conflict with old hdaps driver?)\n",
|
||||
TPC_BASE_PORT,
|
||||
TPC_BASE_PORT+TPC_NUM_PORTS -1);
|
||||
TPC_BASE_PORT + TPC_NUM_PORTS -1);
|
||||
return -ENXIO;
|
||||
}
|
||||
prefetch_jiffies = TPC_PREFETCH_JUNK;
|
||||
printk(KERN_INFO "tp_base: tp_base " TP_VERSION " loaded.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit tp_base_exit(void) {
|
||||
static void __exit tp_base_exit(void)
|
||||
{
|
||||
release_region(TPC_BASE_PORT, TPC_NUM_PORTS );
|
||||
printk(KERN_INFO "tp_base: unloaded.\n");
|
||||
}
|
||||
|
25
tp_base.h
25
tp_base.h
@ -31,15 +31,20 @@
|
||||
|
||||
#define TP_CONTROLLER_ROW_LEN 16
|
||||
|
||||
struct tp_controller_row {
|
||||
u8 val[TP_CONTROLLER_ROW_LEN];
|
||||
u16 mask;
|
||||
};
|
||||
|
||||
/* tp_controller_lock:
|
||||
* Get exclusive lock for accesing the controller.
|
||||
*/
|
||||
extern void tp_controller_lock(void);
|
||||
extern int tp_controller_lock(void);
|
||||
|
||||
/* tp_controller_trylock:
|
||||
* Likewise, but non-blocking. Returns 0 if acquired lock.
|
||||
*/
|
||||
extern int tp_controller_trylock(void);
|
||||
extern int tp_controller_try_lock(void);
|
||||
|
||||
/* tp_controller_unlock:
|
||||
* Release lock.
|
||||
@ -48,27 +53,37 @@ extern void tp_controller_unlock(void);
|
||||
|
||||
/* tp_controller_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).
|
||||
* 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.
|
||||
*/
|
||||
extern int tp_controller_read_row(u8 arg1610, u8 arg161F, u8* buf);
|
||||
extern int tp_controller_read_row(struct tp_controller_row *args,
|
||||
struct tp_controller_row *data);
|
||||
|
||||
/* tp_controller_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.
|
||||
* Returns -EBUSY is data not ready and -ENODATA if row not prefetched.
|
||||
* Caller must hold controller lock.
|
||||
*/
|
||||
extern int tp_controller_try_read_row(u8 arg1610, u8 arg161F, u8* buf);
|
||||
extern int tp_controller_try_read_row(struct tp_controller_row *args,
|
||||
struct tp_controller_row *mask);
|
||||
|
||||
/* tp_controller_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
|
||||
* good chance of succeeding if done neither too soon nor too late.
|
||||
* See tp_controller_read_row above for the meaning of the arguments.
|
||||
* Returns -EBUSY on transient error and -EIO on abnormal condition.
|
||||
* Caller must hold controller lock.
|
||||
*/
|
||||
extern int tp_controller_prefetch_row(u8 arg1610, u8 arg161F);
|
||||
extern int tp_controller_prefetch_row(struct tp_controller_row *args);
|
||||
|
||||
/* tp_controller_invalidate:
|
||||
* Invalidate the prefetched controller data.
|
||||
|
64
tp_smapi.c
64
tp_smapi.c
@ -10,11 +10,8 @@
|
||||
* embedded controller (via the tp_base module).
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2005 Shem Multinymous <multinymous@gmail.com>.
|
||||
* SMAPI access code based on the mwave driver by Mike Sullivan,
|
||||
* Copyright (C) 1999 IBM Corporation.
|
||||
* Module interface and DMI whitelist based on the hdaps module by
|
||||
* Robert Love and Jesper Juhl.
|
||||
* Copyright (C) 2006 Shem Multinymous <multinymous@gmail.com>.
|
||||
* SMAPI access code based on the mwave driver by Mike Sullivan.
|
||||
*
|
||||
* 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
|
||||
@ -44,7 +41,7 @@
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define TP_VERSION "0.21"
|
||||
#define TP_VERSION "0.22"
|
||||
#define TP_DESC "ThinkPad SMAPI Support"
|
||||
#define TP_DIR "smapi"
|
||||
|
||||
@ -133,7 +130,7 @@ static struct {u8 rc; char *msg; int ret;} smapi_rc[]=
|
||||
#define SMAPI_PORT2 0x4F /* fixed port, meaning unclear */
|
||||
static unsigned short smapi_port = 0; /* APM control port, normally 0xB2 */
|
||||
|
||||
static DECLARE_MUTEX(smapi_sem);
|
||||
static DECLARE_MUTEX(smapi_mutex);
|
||||
|
||||
#define SMAPI_MAX_RETRIES 10
|
||||
|
||||
@ -181,8 +178,12 @@ retry:
|
||||
DPRINTK("req_in: BX=%x CX=%x DI=%x SI=%x\n",
|
||||
inBX, inCX, inDI, inSI);
|
||||
|
||||
tp_controller_lock(); /* SMAPI and tp_base use different interfaces to
|
||||
* the same chip, so stay on the safe side. */
|
||||
/* SMAPI's SMI call and tp_base end up using the same interface
|
||||
* (ports 0x1600-0x161F).use different interfaces to the same chip,
|
||||
* they must be excluded from each other. */
|
||||
ret = tp_controller_lock();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"movl $0x00005380,%%eax\n\t"
|
||||
@ -269,11 +270,20 @@ static int smapi_write(u32 inBX, u32 inCX,
|
||||
*/
|
||||
|
||||
/* Lock controller and read row */
|
||||
static int tpc_read_row(u8 arg1610, u8 arg161F, u8* buf) {
|
||||
static int tpc_read_row(u8 arg0, int bat, u8* dataval) {
|
||||
int ret;
|
||||
tp_controller_lock();
|
||||
ret = tp_controller_read_row(arg1610, arg161F, buf);
|
||||
struct tp_controller_row args, data;
|
||||
|
||||
ret = tp_controller_lock();
|
||||
if (ret)
|
||||
return ret;
|
||||
args.val[0x0] = arg0;
|
||||
args.val[0xF] = (u8)bat;
|
||||
args.mask = 0x8001;
|
||||
data.mask = 0xFFFF;
|
||||
ret = tp_controller_read_row(&args, &data);
|
||||
tp_controller_unlock();
|
||||
memcpy(dataval, &data.val, TP_CONTROLLER_ROW_LEN);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -579,7 +589,7 @@ static int attr_get_bat(struct device_attribute *attr) {
|
||||
return container_of(attr, struct bat_device_attribute, dev_attr)->bat;
|
||||
}
|
||||
|
||||
static int show_tpc_bat_word(const char* fmt, u8 arg1610, int off,
|
||||
static int show_tpc_bat_word(const char* fmt, u8 arg0, int off,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
u8 row[TP_CONTROLLER_ROW_LEN];
|
||||
@ -587,13 +597,13 @@ static int show_tpc_bat_word(const char* fmt, u8 arg1610, int off,
|
||||
int ret;
|
||||
if (bat_has_extended_status(bat)!=1)
|
||||
return -ENXIO;
|
||||
ret = tpc_read_row(arg1610, bat, row);
|
||||
ret = tpc_read_row(arg0, bat, row);
|
||||
if (ret)
|
||||
return ret;
|
||||
return sprintf(buf, fmt, (int)*(u16*)(row+off));
|
||||
}
|
||||
|
||||
static int show_tpc_bat_str(u8 arg1610, int off, int maxlen,
|
||||
static int show_tpc_bat_str(u8 arg0, int off, int maxlen,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int bat = attr_get_bat(attr);
|
||||
@ -601,7 +611,7 @@ static int show_tpc_bat_str(u8 arg1610, int off, int maxlen,
|
||||
int ret;
|
||||
if (bat_has_extended_status(bat)!=1)
|
||||
return -ENXIO;
|
||||
ret = tpc_read_row(arg1610, bat, row);
|
||||
ret = tpc_read_row(arg0, bat, row);
|
||||
if (ret)
|
||||
return ret;
|
||||
strncpy(buf, (char*)row+off, maxlen);
|
||||
@ -610,7 +620,7 @@ static int show_tpc_bat_str(u8 arg1610, int off, int maxlen,
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
static int show_tpc_bat_power(u8 arg1610, int offV, int offA,
|
||||
static int show_tpc_bat_power(u8 arg0, int offV, int offA,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
u8 row[TP_CONTROLLER_ROW_LEN];
|
||||
@ -626,7 +636,7 @@ static int show_tpc_bat_power(u8 arg1610, int offV, int offA,
|
||||
return sprintf(buf, "%d mW\n", milliamp*millivolt/1000);
|
||||
}
|
||||
|
||||
static int show_tpc_bat_date(u8 arg1610, int off,
|
||||
static int show_tpc_bat_date(u8 arg0, int off,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
u8 row[TP_CONTROLLER_ROW_LEN];
|
||||
@ -636,7 +646,7 @@ static int show_tpc_bat_date(u8 arg1610, int off,
|
||||
int bat = attr_get_bat(attr);
|
||||
if (bat_has_extended_status(bat)!=1)
|
||||
return -ENXIO;
|
||||
ret = tpc_read_row(arg1610, bat, row);
|
||||
ret = tpc_read_row(arg0, bat, row);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -692,7 +702,7 @@ static int battery_start_charge_thresh_store(struct device *dev,
|
||||
if (thresh > MAX_THRESH_START) /* clamp down to MAX_THRESH_START */
|
||||
thresh = MAX_THRESH_START;
|
||||
|
||||
down(&smapi_sem);
|
||||
down(&smapi_mutex);
|
||||
ret = get_thresh(bat, THRESH_STOP, &other_thresh);
|
||||
if (ret!=-ENOSYS) {
|
||||
if (ret) /* other threshold is set? */
|
||||
@ -710,7 +720,7 @@ static int battery_start_charge_thresh_store(struct device *dev,
|
||||
}
|
||||
ret = set_thresh(bat, THRESH_START, thresh);
|
||||
out:
|
||||
up(&smapi_sem);
|
||||
up(&smapi_mutex);
|
||||
return count;
|
||||
|
||||
}
|
||||
@ -731,7 +741,7 @@ static int battery_stop_charge_thresh_store(struct device *dev,
|
||||
if (thresh<MIN_THRESH_STOP) /* clamp up to MIN_THRESH_STOP */
|
||||
thresh = MIN_THRESH_STOP;
|
||||
|
||||
down(&smapi_sem);
|
||||
down(&smapi_mutex);
|
||||
ret = get_thresh(bat, THRESH_START, &other_thresh);
|
||||
if (ret!=-ENOSYS) { /* other threshold exists? */
|
||||
if (ret)
|
||||
@ -750,7 +760,7 @@ static int battery_stop_charge_thresh_store(struct device *dev,
|
||||
}
|
||||
ret = set_thresh(bat, THRESH_STOP, thresh);
|
||||
out:
|
||||
up(&smapi_sem);
|
||||
up(&smapi_mutex);
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -960,18 +970,18 @@ static int battery_dump_show(
|
||||
int j;
|
||||
char* p = buf;
|
||||
int bat = attr_get_bat(attr);
|
||||
u8 arg1610;
|
||||
u8 arg0;
|
||||
u8 row[TP_CONTROLLER_ROW_LEN];
|
||||
int ret;
|
||||
|
||||
for (arg1610=1; arg1610<=8; ++arg1610) {
|
||||
ret = tpc_read_row(arg1610, bat, row);
|
||||
for (arg0=1; arg0<=8; ++arg0) {
|
||||
ret = tpc_read_row(arg0, bat, row);
|
||||
if (ret)
|
||||
return ret;
|
||||
for (j=0; j<TP_CONTROLLER_ROW_LEN; j++)
|
||||
p += sprintf(p, "%02x ", row[j]);
|
||||
p += sprintf(p, "\n");
|
||||
if (arg1610==1 && bat_has_extended_status(bat)!=1)
|
||||
if (arg0==1 && bat_has_extended_status(bat)!=1)
|
||||
goto done;
|
||||
if ( (p-buf)>PAGE_SIZE-256 ) {
|
||||
printk(TP_ERR "aps_dump_show: too much output!\n");
|
||||
|
Loading…
Reference in New Issue
Block a user