import of tp-smapi version 0.28

This commit is contained in:
Shem Multinymous 2006-08-05 04:31:02 +02:00 committed by Evgeni Golov
parent ee4b50c735
commit 0fb18e4301
7 changed files with 742 additions and 816 deletions

15
CHANGES
View File

@ -1,5 +1,20 @@
Change history for tp_smapi:
0.28 2006-08-16
---------------------
Visible changes:
- thinkpad_ec: Removed 'debug' module parameter, it no longer affected much.
- tp_smapi: Remove optical drive speed control (formerly #ifdefed out).
Internal changes:
- hdaps: Removed __init from hdaps_check_ec(). This fixes crashes on resume
(Thanks, Igor!).
- hdaps: Moved axis transformation to dedicated function transform_axes().
- tp_smapi: Cleaned up printk() output, changed macros.
- tp_smapi: Added "E" to SMAPI parameter names (BX->EBX) - they're 32-bit.
- tp_Smapi: Removed verbose printks()s on bad sysfs args.
- A lot of small cleanups.
0.27 2006-08-05
---------------------
Visible changes:

View File

@ -46,7 +46,7 @@ clean:
load: check_hdaps unload modules
@( [ `id -u` == 0 ] || { echo "Must be root to load modules"; exit 1; } )
{ insmod ./thinkpad_ec.ko debug=$(DEBUG) &&\
{ insmod ./thinkpad_ec.ko &&\
insmod ./tp_smapi.ko debug=$(DEBUG) &&\
$(LOAD_HDAPS); }; :
@echo -e '\nRecent dmesg output:' ; dmesg | tail -10
@ -140,7 +140,6 @@ patch:
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) -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 \

30
README
View File

@ -1,4 +1,4 @@
tp_smapi version 0.27
tp_smapi version 0.28
IBM ThinkPad hardware functions driver
Author: Shem Multinymous <multinymous@gmail.com>
@ -51,25 +51,23 @@ 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 thinkpad_ec and tp_smapi with debug
dmesg output enabled.
Append "DEBUG=1" to "make load" to load tp_smapi with debug=1.
The original kernel tree is never modified by any these commands.
The /lib/modules directory is modified only by "make install".
If your kernel doesn't have dmi-decode-and-save-oem-string-information
applied (see below) then all make commands except "make patch" will need
to invoke "dmidecode" using "sudo", to get information about your
hardware at compile time.
Module parameters
-----------------
thinkpad_ec module:
debug=1 enables verbose dmesg output.
tp_smapi module:
debug=1 enables verbose dmesg output.
hdaps module (modified version of driver from mainline kernel):
force=1 forces loading on ThinkPad models that are not whitelisted.
Usage
-----
@ -155,20 +153,6 @@ This performs raw SMAPI calls. It uses a bad interface that cannot handle
multiple simultaneous access. Don't touch it, it's for development only.
Setting and getting CD-ROM speed:
# echo 0 > /sys/devices/platform/smapi/cd_speed # slow
# echo 1 > /sys/devices/platform/smapi/cd_speed # medium
# echo 2 > /sys/devices/platform/smapi/cd_speed # fast
# cat /sys/devices/platform/smapi/cd_speed
NOTE: cd_speed is disabled by default ("#define PROVIDE_CD_SPEED" to enable),
since changing the CD speed when the CD kernel is also accessing the CD
(e.g., reading a file) will hang your computer. You can get the same
functionality in a safer using combination of "hdparm -E" or "eject -x" for
CD and speedcontrol (http://safari.iki.fi/speedcontrol.c) for DVD.
Model-specific status
---------------------

View File

@ -1,98 +0,0 @@
--- orig/tp_smapi.c 2006-01-07 20:39:28.000000000 +0000
+++ no_cd_speed/tp_smapi.c 2006-01-07 21:05:56.000000000 +0000
@@ -66,2 +65,0 @@
-/* #define PROVIDE_CD_SPEED */ /* evil - see README */
-
@@ -560,48 +557,0 @@
-#ifdef PROVIDE_CD_SPEED
-
-static int get_cd_speed(int *speed) {
- const char* msg;
- u32 bx, dx, di;
- int ret = smapi_request(SMAPI_GET_CDROM_STATUS, 0, 0, 0,
- &bx, NULL, &dx, &di, NULL, &msg);
- if (ret) {
- printk(TP_NOTICE "cannot get cd speed: %s\n", msg);
- return ret;
- }
- if (dx==0x78 && di==0x1e) {
- *speed = 2;
- } else if (dx==0x0f && di==0x00) {
- *speed = 0;
- } else {
- *speed = 1;
- }
- /* what does bx&80 mean? */
- return 0;
-}
-
-static int set_cd_speed(int speed) {
- const char* msg;
- int ret, dummy;
- short int cx, di;
- ret = get_cd_speed(&dummy); /* verify read before writing */
- if (ret)
- return ret;
- if (speed==0) {
- cx=0x0f; di=0x00;
- } else if (speed==1) {
- cx=0x1e; di=0x04;
- } else {
- cx=0x78; di=0x1e;
- }
- ret = smapi_write(SMAPI_SET_CDROM_STATUS, cx, di, 0, &msg);
- if (ret)
- printk(TP_NOTICE "cannot set cd speed to %d: %s\n",
- speed, msg);
- else
- printk(TP_INFO "cd speed set to level %d\n",
- speed);
- return ret;
-}
-
-#endif /* PROVIDE_CD_SPEED */
-
@@ -1135,35 +1084,0 @@
-#ifdef PROVIDE_CD_SPEED
-/*********************************************************************
- * Optical drive speed setting. This should be done by the ATAPI
- * driver instead, and may get in the ATAPI driver's way.
- */
-
-static int show_cd_speed(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int speed;
- int ret = get_cd_speed(&speed);
- if (ret)
- return ret;
- return sprintf(buf, "%d\n", speed);
-}
-
-static int store_cd_speed(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- int ret;
- int speed;
- if (sscanf(buf, "%d", &speed)<1 || speed<0 || speed>2) {
- printk(TP_ERR "cd_speed: must be between 0 and 2\n");
- return -EINVAL;
- }
- ret = set_cd_speed(speed);
- if (ret)
- return ret;
- return count;
-}
-
-#endif /* PROVIDE_CD_SPEED */
-
-
@@ -1235,3 +1149,0 @@
-#ifdef PROVIDE_CD_SPEED
-static DEVICE_ATTR(cd_speed, 0644, show_cd_speed, store_cd_speed);
-#endif
@@ -1243,3 +1154,0 @@
-#ifdef PROVIDE_CD_SPEED
- &dev_attr_cd_speed.attr,
-#endif

172
hdaps.c
View File

@ -65,14 +65,14 @@ static struct timer_list hdaps_timer;
static struct platform_device *pdev;
static struct input_dev *hdaps_idev;
static unsigned int hdaps_invert;
static int needs_calibration = 0;
static int needs_calibration;
/* Configuration: */
static int sampling_rate = 50; /* Sampling rate */
static int oversampling_ratio = 5; /* Ratio between our sampling rate and
static int oversampling_ratio = 5; /* Ratio between our sampling rate and
* EC accelerometer sampling rate */
static int running_avg_filter_order = 2; /* EC running average filter order */
static int fake_data_mode = 0; /* Enable EC fake data mode? */
static int fake_data_mode; /* Enable EC fake data mode? */
/* Latest state readout: */
static int pos_x, pos_y; /* position */
@ -84,10 +84,21 @@ static int rest_x, rest_y; /* calibrated rest position */
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.
* If fast!=0, do one quick attempt without retries.
* Caller must hold controller lock.
/* Some models require an axis transformation to the standard reprsentation */
static void transform_axes(int *x, int *y)
{
if (hdaps_invert) {
*x = -*x;
*y = -*y;
}
}
/**
* __hdaps_update - query current state, with locks already acquired
* @fast: if nonzero, do one quick attempt without retries.
*
* Query current accelerometer state and update global state variables.
* Also prefetches the next query. Caller must hold controller lock.
*/
static int __hdaps_update(int fast)
{
@ -113,12 +124,13 @@ static int __hdaps_update(int fast)
return -EIO;
}
if (data.val[EC_ACCEL_IDX_READOUTS]<1)
if (data.val[EC_ACCEL_IDX_READOUTS] < 1)
return -EBUSY; /* no pending readout, try again later */
/* Parse position data: */
pos_x = *(s16*)(data.val+EC_ACCEL_IDX_XPOS1) * (hdaps_invert?-1:1);
pos_y = *(s16*)(data.val+EC_ACCEL_IDX_YPOS1) * (hdaps_invert?-1:1);
pos_x = *(s16*)(data.val+EC_ACCEL_IDX_XPOS1);
pos_y = *(s16*)(data.val+EC_ACCEL_IDX_YPOS1);
transform_axes(&pos_x, &pos_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
@ -141,8 +153,11 @@ static int __hdaps_update(int fast)
return 0;
}
/* hdaps_update - read current state and update global state variables.
* Also prefetches the next read, to reduce udelay busy-waiting.
/**
* hdaps_update - acquire locks and query current state
*
* Query current accelerometer state and update global state variables.
* Also prefetches the next query.
* Retries until timeout if the accelerometer is not in ready status (common).
* Does its own locking.
*/
@ -151,7 +166,7 @@ static int hdaps_update(void)
int total, ret;
if (!stale_readout) /* already updated recently? */
return 0;
for (total=READ_TIMEOUT_MSECS; total>0; total-=RETRY_MSECS) {
for (total=0; total<READ_TIMEOUT_MSECS; total+=RETRY_MSECS) {
ret = thinkpad_ec_lock();
if (ret)
return ret;
@ -167,12 +182,13 @@ static int hdaps_update(void)
return ret;
}
/*
/**
* 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_set_power(int on) {
struct thinkpad_ec_row args =
static int hdaps_set_power(int on)
{
struct thinkpad_ec_row args =
{ .mask=0x0003, .val={0x14, on?0x01:0x00} };
struct thinkpad_ec_row data = { .mask = 0x8000 };
int ret = thinkpad_ec_read_row(&args, &data);
@ -183,13 +199,14 @@ static int hdaps_set_power(int on) {
return 0;
}
/*
* hdaps_set_fake_data_mode - enable or disable EC test mode, which fakes
* accelerometer data using an incrementing counter.
/**
* hdaps_set_fake_data_mode - enable or disable EC test mode
* EC test mode fakes accelerometer data using an incrementing counter.
* Returns zero on success and negative error code on failure. Can sleep.
*/
static int hdaps_set_fake_data_mode(int on) {
struct thinkpad_ec_row args =
static int hdaps_set_fake_data_mode(int on)
{
struct thinkpad_ec_row args =
{ .mask=0x0007, .val={0x17, 0x83, on?0x01:0x00} };
struct thinkpad_ec_row data = { .mask = 0x8000 };
int ret = thinkpad_ec_read_row(&args, &data);
@ -204,15 +221,16 @@ static int hdaps_set_fake_data_mode(int on) {
return 0;
}
/*
/**
* hdaps_set_ec_config - set accelerometer parameters.
* ec_rate - embedded controller sampling rate
* ( sampling_rate * oversampling_ratio )
* order - embedded controller running average filter order
* @ec_rate: embedded controller sampling rate
* @order: embedded controller running average filter order
* (Normally we have @ec_rate = sampling_rate * oversampling_ratio.)
* Returns zero on success and negative error code on failure. Can sleep.
*/
static int hdaps_set_ec_config(int ec_rate, int order) {
struct thinkpad_ec_row args = { .mask=0x000F,
static int hdaps_set_ec_config(int ec_rate, int order)
{
struct thinkpad_ec_row args = { .mask=0x000F,
.val={0x10, (u8)ec_rate, (u8)(ec_rate>>8), order} };
struct thinkpad_ec_row data = { .mask = 0x8000 };
int ret = thinkpad_ec_read_row(&args, &data);
@ -236,14 +254,15 @@ static int hdaps_set_ec_config(int ec_rate, int order) {
return 0;
}
/*
/**
* hdaps_get_ec_config - get accelerometer parameters.
* ec_rate - embedded controller sampling rate
* order - embedded controller running average filter order
* @ec_rate: embedded controller sampling rate
* @order: embedded controller running average filter 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 thinkpad_ec_row args =
static int hdaps_get_ec_config(int *ec_rate, int *order)
{
const struct thinkpad_ec_row args =
{ .mask=0x0003, .val={0x17, 0x82} };
struct thinkpad_ec_row data = { .mask = 0x801F };
int ret = thinkpad_ec_read_row(&args, &data);
@ -260,18 +279,19 @@ static int hdaps_get_ec_config(int *ec_rate, int *order) {
return 0;
}
/*
/**
* hdaps_get_ec_mode - get EC accelerometer mode
* Returns zero on success and negative error code on failure. Can sleep.
*/
static int hdaps_get_ec_mode(u8 *mode) {
static int hdaps_get_ec_mode(u8 *mode)
{
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) {
printk(KERN_WARNING
printk(KERN_WARNING
"accelerometer not implemented (0x%02x)\n",
data.val[0xF]);
return -EIO;
@ -280,13 +300,14 @@ static int hdaps_get_ec_mode(u8 *mode) {
return 0;
}
/*
/**
* hdaps_check_ec - checks something about the EC.
* Follows the clean-room spec for HDAPS; we don't know what it means.
* Returns zero on success and negative error code on failure. Can sleep.
*/
static int __init hdaps_check_ec(u8 *mode) {
const struct thinkpad_ec_row args =
static int hdaps_check_ec()
{
const struct thinkpad_ec_row args =
{ .mask=0x0003, .val={0x17, 0x81} };
struct thinkpad_ec_row data = { .mask = 0x800E };
int ret = thinkpad_ec_read_row(&args, &data);
@ -298,11 +319,14 @@ static int __init hdaps_check_ec(u8 *mode) {
return 0;
}
/*
* hdaps_device_init - initialize the accelerometer. Returns zero on success
* and negative error code on failure. Can sleep.
/**
* hdaps_device_init - initialize the accelerometer.
*
* Call several embedded controller functions to test and initialize the
* accelerometer.
* Returns zero on success and negative error code on failure. Can sleep.
*/
#define ABORT_INIT(msg) { printk(KERN_ERR "hdaps init: %s\n", msg); goto bad; }
#define ABORT_INIT(msg) printk(KERN_ERR "hdaps init failed at: %s\n", msg)
static int hdaps_device_init(void)
{
int ret;
@ -313,23 +337,24 @@ static int hdaps_device_init(void)
return ret;
if (hdaps_get_ec_mode(&mode))
ABORT_INIT("hdaps_get_ec_mode failed");
{ ABORT_INIT("hdaps_get_ec_mode failed"); goto bad; }
printk(KERN_DEBUG "hdaps: initial mode latch is 0x%02x\n", mode);
if (mode==0x00)
ABORT_INIT("accelerometer not available");
{ ABORT_INIT("accelerometer not available"); goto bad; }
if (hdaps_check_ec(&mode))
ABORT_INIT("hdaps_check_ec failed");
if (hdaps_check_ec())
{ ABORT_INIT("hdaps_check_ec failed"); goto bad; }
if (hdaps_set_power(1))
ABORT_INIT("hdaps_set_power failed");
{ ABORT_INIT("hdaps_set_power failed"); goto bad; }
if (hdaps_set_ec_config(sampling_rate*oversampling_ratio,
running_avg_filter_order))
ABORT_INIT("hdaps_set_ec_config failed");
{ ABORT_INIT("hdaps_set_ec_config failed"); goto bad; }
if (hdaps_set_fake_data_mode(fake_data_mode))
ABORT_INIT("hdaps_set_fake_data_mode failed");
{ ABORT_INIT("hdaps_set_fake_data_mode failed"); goto bad; }
thinkpad_ec_invalidate();
udelay(200);
@ -337,7 +362,7 @@ static int hdaps_device_init(void)
/* Just prefetch instead of reading, to avoid ~1sec delay on load */
ret = thinkpad_ec_prefetch_row(&ec_accel_args);
if (ret)
ABORT_INIT("initial prefetch failed");
{ ABORT_INIT("initial prefetch failed"); goto bad; }
goto good;
bad:
thinkpad_ec_invalidate();
@ -348,14 +373,22 @@ good:
return ret;
}
/*
* hdaps_device_shutdown - power off the accelerometer. Can sleep.
/**
* hdaps_device_shutdown - power off the accelerometer
* Returns nonzero on failure. Can sleep.
*/
static void hdaps_device_shutdown(void) {
if (hdaps_set_power(0))
static int hdaps_device_shutdown(void)
{
int ret;
ret = hdaps_set_power(0);
if (ret) {
printk(KERN_WARNING "hdaps: cannot power off\n");
if (hdaps_set_ec_config(0, 1))
return ret;
}
ret = hdaps_set_ec_config(0, 1);
if (ret)
printk(KERN_WARNING "hdaps: cannot stop EC sampling\n");
return ret;
}
/* Device model stuff */
@ -376,7 +409,7 @@ static int hdaps_suspend(struct platform_device *dev, pm_message_t state)
{
/* Don't do hdaps polls until resume re-initializes the sensor. */
del_timer_sync(&hdaps_timer);
hdaps_device_shutdown();
hdaps_device_shutdown(); /* ignore errors, effect is negligible */
return 0;
}
@ -399,8 +432,9 @@ 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)
{
@ -426,8 +460,8 @@ static void hdaps_mousedev_poll(unsigned long unused)
thinkpad_ec_unlock();
/* Any of "successful", "not yet ready" and "not prefetched"? */
if (ret!=0 && ret!=-EBUSY && ret!=-ENODATA) {
printk(KERN_ERR
"hdaps: poll failed, disabling update timer\n");
printk(KERN_ERR
"hdaps: poll failed, disabling updates\n");
return;
}
@ -467,7 +501,7 @@ static ssize_t hdaps_keyboard_activity_show(struct device *dev,
int ret = hdaps_update();
if (ret)
return ret;
return sprintf(buf, "%u\n",
return sprintf(buf, "%u\n",
get_jiffies_64() < last_keyboard_jiffies + KMACT_REMEMBER_PERIOD);
}
@ -478,7 +512,7 @@ static ssize_t hdaps_mouse_activity_show(struct device *dev,
int ret = hdaps_update();
if (ret)
return ret;
return sprintf(buf, "%u\n",
return sprintf(buf, "%u\n",
get_jiffies_64() < last_mouse_jiffies + KMACT_REMEMBER_PERIOD);
}
@ -529,7 +563,7 @@ static ssize_t hdaps_sampling_rate_store(
{
int rate, ret;
if (sscanf(buf, "%d", &rate) != 1 || rate>HZ || rate<0) {
printk(KERN_WARNING
printk(KERN_WARNING
"must have 0<input_sampling_rate<=HZ=%d\n", HZ);
return -EINVAL;
}
@ -558,7 +592,7 @@ static ssize_t hdaps_oversampling_ratio_store(
int ratio, ret;
if (sscanf(buf, "%d", &ratio) != 1 || ratio<1)
return -EINVAL;
ret = hdaps_set_ec_config(sampling_rate*ratio,
ret = hdaps_set_ec_config(sampling_rate*ratio,
running_avg_filter_order);
if (ret)
return ret;
@ -595,11 +629,8 @@ static ssize_t hdaps_fake_data_mode_store(struct device *dev,
const char *buf, size_t count)
{
int on, ret;
if (sscanf(buf, "%d", &on) != 1 || on<0 || on>1) {
printk(KERN_WARNING
"fake_data should be 0 or 1\n");
if (sscanf(buf, "%d", &on) != 1 || on<0 || on>1)
return -EINVAL;
}
ret = hdaps_set_fake_data_mode(on);
if (ret)
return ret;
@ -656,7 +687,7 @@ static struct attribute_group hdaps_attribute_group = {
static int hdaps_dmi_match_invert(struct dmi_system_id *id)
{
hdaps_invert = 1;
printk(KERN_INFO "hdaps: %s detected, inverting axes\n",
printk(KERN_INFO "hdaps: %s detected, inverting axes\n",
id->ident);
return 1;
}
@ -736,6 +767,7 @@ out_device:
platform_device_unregister(pdev);
out_driver:
platform_driver_unregister(&hdaps_driver);
hdaps_device_shutdown();
out:
printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret);
return ret;
@ -745,7 +777,7 @@ static void __exit hdaps_exit(void)
{
del_timer_sync(&hdaps_timer);
input_unregister_device(hdaps_idev);
hdaps_device_shutdown();
hdaps_device_shutdown(); /* ignore errors, effect is negligible */
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
platform_device_unregister(pdev);
platform_driver_unregister(&hdaps_driver);

View File

@ -1,9 +1,9 @@
/*
* 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
* 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.
* For information about the LPC protocol and terminology, see:
@ -36,7 +36,7 @@
#include <linux/jiffies.h>
#include <asm/io.h>
#define TP_VERSION "0.27"
#define TP_VERSION "0.28"
MODULE_AUTHOR("Shem Multinymous");
MODULE_DESCRIPTION("ThinkPad embedded controller hardware access");
@ -65,14 +65,7 @@ MODULE_LICENSE("GPL");
#define TPC_REQUEST_NDELAY 10
#define TPC_PREFETCH_TIMEOUT (HZ/10) /* invalidate prefetch after 0.1sec */
/* 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)");
/* 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...) \
"thinkpad_ec: %s: " fmt "\n", __func__, ## args
#define REQ_FMT(msg, code) \
@ -89,36 +82,42 @@ static u64 prefetch_jiffies; /* time of prefetch, or: */
static DECLARE_MUTEX(thinkpad_ec_mutex);
/* thinkpad_ec_lock:
* Get exclusive lock for accesing the controller. May sleep.
* Returns 0 iff lock acquired .
/**
* thinkpad_ec_lock - get lock on the ThinkPad EC
*
* Get exclusive lock for accesing the ThinkPad embedded controller LPC3
* interface. Returns 0 iff lock acquired.
*/
int thinkpad_ec_lock(void)
int thinkpad_ec_lock(void)
{
int ret;
ret = down_interruptible(&thinkpad_ec_mutex);
if (ret)
DPRINTK("tp_controller mutex down interrupted: %d\n", ret);
return ret;
}
EXPORT_SYMBOL_GPL(thinkpad_ec_lock);
EXPORT_SYMBOL_GPL(thinkpad_ec_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 .
/**
* thinkpad_ec_try_lock - try getting lock on the ThinkPad EC
*
* Try getting an exclusive lock for accesing the ThinkPad embedded
* controller LPC3. Returns immediately if lock is not available; neither
* blocks nor sleeps. Returns 0 iff lock acquired .
*/
int thinkpad_ec_try_lock(void)
int thinkpad_ec_try_lock(void)
{
return down_trylock(&thinkpad_ec_mutex);
}
EXPORT_SYMBOL_GPL(thinkpad_ec_try_lock);
EXPORT_SYMBOL_GPL(thinkpad_ec_try_lock);
/* thinkpad_ec_unlock:
* Release a previously acquired controller lock.
/**
* thinkpad_ec_unlock - release lock on ThinkPad EC
*
* Release a previously acquired exclusive lock on the ThinkPad ebmedded
* controller LPC3 interface.
*/
void thinkpad_ec_unlock(void)
void thinkpad_ec_unlock(void)
{
up(&thinkpad_ec_mutex);
}
@ -126,7 +125,7 @@ void thinkpad_ec_unlock(void)
EXPORT_SYMBOL_GPL(thinkpad_ec_unlock);
/* Tell embedded controller to prepare a row */
static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args)
static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args)
{
u8 str3;
int i;
@ -175,7 +174,7 @@ static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args)
* Releasing locks before this happens may cause an EC hang
* due to firmware bug!
*/
for (i=0; i<TPC_REQUEST_RETRIES; ++i) {
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;
@ -193,7 +192,7 @@ static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args)
}
/* Read current row data from the controller, assuming it's already
* requested.
* requested.
*/
static int thinkpad_ec_read_data(struct thinkpad_ec_row *data)
{
@ -213,16 +212,16 @@ static int thinkpad_ec_read_data(struct thinkpad_ec_row *data)
MSG_FMT("bad initial STR3 (0x%02x)", str3));
return -EIO;
}
/* 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)
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)
@ -231,7 +230,7 @@ static int thinkpad_ec_read_data(struct thinkpad_ec_row *data)
return 0;
}
/* Is the given row currently prefetched?
/* Is the given row currently prefetched?
* To keep things simple we compare only the first and last args;
* in practice this suffices .*/
static int thinkpad_ec_is_row_fetched(const struct thinkpad_ec_row *args)
@ -243,16 +242,22 @@ static int thinkpad_ec_is_row_fetched(const struct thinkpad_ec_row *args)
(get_jiffies_64() < prefetch_jiffies + TPC_PREFETCH_TIMEOUT);
}
/* 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[], 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).
/**
* thinkpad_ec_read_row - request and read data from ThinkPad EC
* @args Input register arguments
* @data Output register values
*
* Read a data row from the ThinkPad embedded controller LPC3 interface.
* Does fetching and retrying if needed. The row args are specified by
* 16 byte arguments, some of which may be missing (but the first is
* mandatory). These are given in @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->mask. 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.
* Caller must hold controller lock.
*/
int thinkpad_ec_read_row(const struct thinkpad_ec_row *args,
struct thinkpad_ec_row *data)
@ -294,11 +299,18 @@ out:
EXPORT_SYMBOL_GPL(thinkpad_ec_read_row);
/* thinkpad_ec_try_read_row:
* Try read a prefetched row from the controller. Don't fetch or retry.
* See thinkpad_ec_read_row above for the meaning of the arguments.
/**
* thinkpad_ec_try_read_row - try reading prefetched data from ThinkPad EC
* @args Input register arguments
* @data Output register values
*
* Try reading a data row from the ThinkPad embedded controller LPC3
* interface, if this raw was recently prefetched using
* thinkpad_ec_prefetch_row(). Does not fetch, retry or block.
* The parameters have the same meaning as in thinkpad_ec_read_row().
*
* Returns -EBUSY is data not ready and -ENODATA if row not prefetched.
* Caller must hold controller lock.
* Caller must hold controller lock.
*/
int thinkpad_ec_try_read_row(const struct thinkpad_ec_row *args,
struct thinkpad_ec_row *data)
@ -316,12 +328,17 @@ int thinkpad_ec_try_read_row(const struct thinkpad_ec_row *args,
EXPORT_SYMBOL_GPL(thinkpad_ec_try_read_row);
/* thinkpad_ec_prefetch_row:
* Prefetch data row from the controller. A subsequent call to
* 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 thinkpad_ec_read_row above for the meaning of the arguments.
/**
* thinkpad_ec_prefetch_row - prefetch data from ThinkPad EC
* @args Input register arguments
*
* Prefetch a data row from the ThinkPad embedded controller LCP3
* interface. A subsequent call to 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
* thinkpad_ec_read_row() for the meaning of @args.
*
* Returns -EBUSY on transient error and -EIO on abnormal condition.
* Caller must hold controller lock.
*/
@ -341,12 +358,15 @@ int thinkpad_ec_prefetch_row(const struct thinkpad_ec_row *args)
EXPORT_SYMBOL_GPL(thinkpad_ec_prefetch_row);
/* thinkpad_ec_invalidate:
* Invalidate the prefetched controller data.
/**
* thinkpad_ec_invalidate - invalidate prefetched ThinkPad EC data
*
* Invalidate the data prefetched via thinkpad_ec_prefetch_row() from the
* ThinkPad embedded controller LPC3 interface.
* Must be called before unlocking by any code that accesses the controller
* ports directly.
*/
void thinkpad_ec_invalidate(void)
void thinkpad_ec_invalidate(void)
{
prefetch_jiffies = TPC_PREFETCH_JUNK;
}
@ -361,7 +381,8 @@ EXPORT_SYMBOL_GPL(thinkpad_ec_invalidate);
* 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 thinkpad_ec_test(void) {
static int thinkpad_ec_test(void)
{
int ret;
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} };
@ -374,8 +395,9 @@ static int thinkpad_ec_test(void) {
return ret;
}
/* Search all DMI device names for a given type for a substrng */
static int __init dmi_find_substring(int type, const char *substr) {
/* Search all DMI device names of a given type for a substring */
static int __init dmi_find_substring(int type, const char *substr)
{
struct dmi_device *dev = NULL;
while ((dev = dmi_find_device(type, NULL, dev))) {
if (strstr(dev->name, substr))
@ -393,7 +415,7 @@ static int __init dmi_find_substring(int type, const char *substr) {
}
/* Check DMI for existence of ThinkPad embedded controller */
static int __init check_dmi_for_ec(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[] = {
@ -417,13 +439,12 @@ static int __init check_dmi_for_ec(void)
static int __init thinkpad_ec_init(void)
{
if (!check_dmi_for_ec()) {
printk(KERN_ERR "thinkpad_ec: no ThinkPad embedded controller!\n");
printk(KERN_WARNING "thinkpad_ec: no ThinkPad embedded controller!\n");
return -ENODEV;
}
if (!request_region(TPC_BASE_PORT, TPC_NUM_PORTS,
"thinkpad_ec"))
{
"thinkpad_ec")) {
printk(KERN_ERR "thinkpad_ec: cannot claim io ports %#x-%#x\n",
TPC_BASE_PORT,
TPC_BASE_PORT + TPC_NUM_PORTS -1);
@ -431,7 +452,7 @@ static int __init thinkpad_ec_init(void)
}
prefetch_jiffies = TPC_PREFETCH_JUNK;
if (thinkpad_ec_test()) {
printk(KERN_INFO "thinkpad_ec: initial ec test failed\n");
printk(KERN_ERR "thinkpad_ec: initial ec test failed\n");
release_region(TPC_BASE_PORT, TPC_NUM_PORTS);
return -ENXIO;
}

1095
tp_smapi.c

File diff suppressed because it is too large Load Diff