linux-tkg/linux-tkg-patches/5.4/0003-glitched-base.patch

4558 lines
149 KiB
Diff

Merge linux-tkg packages into a single package, add Void Linux and Clang/LLVM support (#63) * Add Clang/LLVM and Void Linux support * merge packages, update .gitignore * Stage 2 of merge * Stage 3 of merge * Stage 4 of merge * Stage 5 of mrege, almost done * Complete merge of linux-tkg, sync with master (undead PDS 5.8) * Complete merge of linux-tkg, sync with master (undead PDS 5.8) * Forgot to add all the patches * Fix prompt, add config_hardened files * Fix some stuff for Void * Merge linux-tkg README, add Void Linux info * typo * Fix broken MuQSS on Void at least, Fix CPU opts patch apply * update to 5.8.8, 5.4.64, block LLVM build with kernel 5.7 because it seems to segfault at linking vmlinux, fix RC building on Void Linux * update to 5.8.8, 5.4.64. seems making a confdir variable wasn't necessary, revert it. sync with master. * remove variables for messages in favor of defined functions in the Void template * resync Linux58-tkg back to master * Clear patches is the same file for every version * glitched base is the same file for 5.8 and 5.9, fix wrong version in kernel selection for 5.8, also just use * since it's the same .-. * merge some patches that are the same together, fix building 5.4 BMQ * BMQ and PDS ondemand are the same file, fix missing space on kernel version selection * add Clang makedepends to PKGBUILD, add missing compiler option in customization.cfg, make GCC recommended * Add kernel selection to install.sh * Somehow this wasn't edited * Add optional clang deps to install.sh * Update gitignore to just ignore linux-5.x instead of commenting it out * Missing fi * forgot to sync back install.sh... * generalize desktop profile configs, uncomment fsync and bcachefs in customization.cfg, add Project C /PDS and Undead PDS to README, credit plug, add kernel version selection to customization.cfg, fallback for compiler selection * Fix compiler selection error out on Void (for some reason xbps-src always runs the else statement) also avoid conflict with _compiler, rename it to _compiler_opt for the actual options in the make command * oops * Fix Void extra config not appearing, missing indent for _configfile, missed PKGBUILD in other commit... * remove globbing in install.sh * Move loading of external configuration to the beginning of _tkg_initscript * Change MuQSS note in README * 5.4.68, 5.8.12, 5.9-rc7 * Update Project C patchset to v5.8-r3 * 5.8 bcachefs * LLVM=1 was required to be used on make commands otherwise it would reset config, we don't need to know if gcc was used to build a kernel (since llvm is appended to llvm builds), remove the compiler_name if not using llvm, make kernel versions variables in prepare * _compileropt does not need to be llvm specific * add fix for AMD GPU DCN3.0 build with clang in glitched base 5.9 https://lore.kernel.org/amd-gfx/4b5927dd-9f2d-40f9-ef63-007cf7b65f37@amd.com/T/#t * Revert "add fix for AMD GPU DCN3.0 build with clang in glitched base 5.9" This reverts commit 276e219f9fe89397332c91e601f34a37b3a0503f. merged upstream * Sync with linux-tkg master * Just stick every patch into it's own kernel version folder * update 5.4.72, 5.8.16 * check for sum check fail on Void, fix fsync patch * Update README.md * README.md formatting * forgot to move that * linux59-tkg: Import 5.9 version of the bcachefs patchset - https://gthub.com/koverstreet/bcachefs Co-authored-by: Lukáš Horáček <flgx@protonmail.com>
2020-10-25 18:43:11 +07:00
From f7f49141a5dbe9c99d78196b58c44307fb2e6be3 Mon Sep 17 00:00:00 2001
From: Tk-Glitch <ti3nou@gmail.com>
Date: Wed, 4 Jul 2018 04:30:08 +0200
Subject: glitched
diff --git a/scripts/mkcompile_h b/scripts/mkcompile_h
index 87f1fc9..b3be470 100755
--- a/scripts/mkcompile_h
+++ b/scripts/mkcompile_h
@@ -50,8 +50,8 @@ else
fi
UTS_VERSION="#$VERSION"
-CONFIG_FLAGS=""
-if [ -n "$SMP" ] ; then CONFIG_FLAGS="SMP"; fi
+CONFIG_FLAGS="TKG"
+if [ -n "$SMP" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS SMP"; fi
if [ -n "$PREEMPT" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT"; fi
UTS_VERSION="$UTS_VERSION $CONFIG_FLAGS $TIMESTAMP"
diff --git a/fs/dcache.c b/fs/dcache.c
index 2acfc69878f5..3f1131431e06 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -69,7 +69,7 @@
* If no ancestor relationship:
* arbitrary, since it's serialized on rename_lock
*/
-int sysctl_vfs_cache_pressure __read_mostly = 100;
+int sysctl_vfs_cache_pressure __read_mostly = 50;
EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure);
__cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 211890edf37e..37121563407d 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -41,7 +41,7 @@ const_debug unsigned int sysctl_sched_features =
* Number of tasks to iterate in a single balance run.
* Limited because this is done with IRQs disabled.
*/
-const_debug unsigned int sysctl_sched_nr_migrate = 32;
+const_debug unsigned int sysctl_sched_nr_migrate = 128;
/*
* period over which we average the RT time consumption, measured
@@ -61,9 +61,9 @@ __read_mostly int scheduler_running;
/*
* part of the period that we allow rt tasks to run in us.
- * default: 0.95s
+ * XanMod default: 0.98s
*/
-int sysctl_sched_rt_runtime = 950000;
+int sysctl_sched_rt_runtime = 980000;
/*
* __task_rq_lock - lock the rq @p resides on.
diff --git a/lib/Kconfig b/lib/Kconfig
index 5fe577673b98..c44c27cd6e05 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -10,6 +10,16 @@ menu "Library routines"
config RAID6_PQ
tristate
+config RAID6_USE_PREFER_GEN
+ bool "Use prefered raid6 gen function."
+ default n
+ depends on RAID6_PQ
+ help
+ This option is provided for using prefered raid6 gen function
+ directly instead of calculating the best durning boot-up.
+ The prefered function should be the same as the best one from
+ calculating.
+
config BITREVERSE
tristate
diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index 5065b1e7e327..1bf3c712a4ca 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -150,6 +150,29 @@ static inline const struct raid6_recov_calls *raid6_choose_recov(void)
return best;
}
+#ifdef CONFIG_RAID6_USE_PREFER_GEN
+static inline const struct raid6_calls *raid6_choose_prefer_gen(void)
+{
+ const struct raid6_calls *const *algo;
+ const struct raid6_calls *best;
+
+ for (best = NULL, algo = raid6_algos; *algo; algo++) {
+ if (!best || (*algo)->prefer >= best->prefer) {
+ if ((*algo)->valid && !(*algo)->valid())
+ continue;
+ best = *algo;
+ }
+ }
+
+ if (best) {
+ printk("raid6: using algorithm %s\n", best->name);
+ raid6_call = *best;
+ } else
+ printk("raid6: Yikes! No algorithm found!\n");
+
+ return best;
+}
+#else
static inline const struct raid6_calls *raid6_choose_gen(
void *(*const dptrs)[(65536/PAGE_SIZE)+2], const int disks)
{
@@ -221,6 +244,7 @@ static inline const struct raid6_calls *raid6_choose_gen(
return best;
}
+#endif
/* Try to pick the best algorithm */
@@ -228,10 +252,11 @@ static inline const struct raid6_calls *raid6_choose_gen(
int __init raid6_select_algo(void)
{
- const int disks = (65536/PAGE_SIZE)+2;
-
const struct raid6_calls *gen_best;
const struct raid6_recov_calls *rec_best;
+#ifndef CONFIG_RAID6_USE_PREFER_GEN
+ const int disks = (65536/PAGE_SIZE)+2;
+
char *syndromes;
void *dptrs[(65536/PAGE_SIZE)+2];
int i;
@@ -252,11 +277,16 @@ int __init raid6_select_algo(void)
/* select raid gen_syndrome function */
gen_best = raid6_choose_gen(&dptrs, disks);
+#else
+ gen_best = raid6_choose_prefer_gen();
+#endif
/* select raid recover functions */
rec_best = raid6_choose_recov();
+#ifndef CONFIG_RAID6_USE_PREFER_GEN
free_pages((unsigned long)syndromes, 1);
+#endif
return gen_best && rec_best ? 0 : -EINVAL;
}
diff --git a/mm/zswap.c b/mm/zswap.c
index 61a5c41972db..2674c2806130 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -91,7 +91,7 @@ static struct kernel_param_ops zswap_enabled_param_ops = {
module_param_cb(enabled, &zswap_enabled_param_ops, &zswap_enabled, 0644);
/* Crypto compressor to use */
-#define ZSWAP_COMPRESSOR_DEFAULT "lzo"
+#define ZSWAP_COMPRESSOR_DEFAULT "lz4"
static char *zswap_compressor = ZSWAP_COMPRESSOR_DEFAULT;
static int zswap_compressor_param_set(const char *,
const struct kernel_param *);
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index 71f39410691b..288f9679e883 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -54,7 +54,7 @@ scm_version()
# If only the short version is requested, don't bother
# running further git commands
if $short; then
- echo "+"
+ # echo "+"
return
fi
# If we are past a tagged commit (like
From f85ed068b4d0e6c31edce8574a95757a60e58b87 Mon Sep 17 00:00:00 2001
From: Etienne Juvigny <Ti3noU@gmail.com>
Date: Mon, 3 Sep 2018 17:36:25 +0200
Subject: Zenify & stuff
diff --git a/Documentation/tp_smapi.txt b/Documentation/tp_smapi.txt
new file mode 100644
index 000000000000..a249678a8866
--- /dev/null
+++ b/Documentation/tp_smapi.txt
@@ -0,0 +1,275 @@
+tp_smapi version 0.42
+IBM ThinkPad hardware functions driver
+
+Author: Shem Multinymous <multinymous@gmail.com>
+Project: http://sourceforge.net/projects/tpctl
+Wiki: http://thinkwiki.org/wiki/tp_smapi
+List: linux-thinkpad@linux-thinkpad.org
+ (http://mailman.linux-thinkpad.org/mailman/listinfo/linux-thinkpad)
+
+Description
+-----------
+
+ThinkPad laptops include a proprietary interface called SMAPI BIOS
+(System Management Application Program Interface) which provides some
+hardware control functionality that is not accessible by other means.
+
+This driver exposes some features of the SMAPI BIOS through a sysfs
+interface. It is suitable for newer models, on which SMAPI is invoked
+through IO port writes. Older models use a different SMAPI interface;
+for those, try the "thinkpad" module from the "tpctl" package.
+
+WARNING:
+This driver uses undocumented features and direct hardware access.
+It thus cannot be guaranteed to work, and may cause arbitrary damage
+(especially on models it wasn't tested on).
+
+
+Module parameters
+-----------------
+
+thinkpad_ec module:
+ force_io=1 lets thinkpad_ec load on some recent ThinkPad models
+ (e.g., T400 and T500) whose BIOS's ACPI DSDT reserves the ports we need.
+tp_smapi module:
+ debug=1 enables verbose dmesg output.
+
+
+Usage
+-----
+
+Control of battery charging thresholds (in percents of current full charge
+capacity):
+
+# echo 40 > /sys/devices/platform/smapi/BAT0/start_charge_thresh
+# echo 70 > /sys/devices/platform/smapi/BAT0/stop_charge_thresh
+# cat /sys/devices/platform/smapi/BAT0/*_charge_thresh
+
+ (This is useful since Li-Ion batteries wear out much faster at very
+ high or low charge levels. The driver will also keeps the thresholds
+ across suspend-to-disk with AC disconnected; this isn't done
+ automatically by the hardware.)
+
+Inhibiting battery charging for 17 minutes (overrides thresholds):
+
+# echo 17 > /sys/devices/platform/smapi/BAT0/inhibit_charge_minutes
+# echo 0 > /sys/devices/platform/smapi/BAT0/inhibit_charge_minutes # stop
+# cat /sys/devices/platform/smapi/BAT0/inhibit_charge_minutes
+
+ (This can be used to control which battery is charged when using an
+ Ultrabay battery.)
+
+Forcing battery discharging even if AC power available:
+
+# echo 1 > /sys/devices/platform/smapi/BAT0/force_discharge # start discharge
+# echo 0 > /sys/devices/platform/smapi/BAT0/force_discharge # stop discharge
+# cat /sys/devices/platform/smapi/BAT0/force_discharge
+
+ (When AC is connected, forced discharging will automatically stop
+ when battery is fully depleted -- this is useful for calibration.
+ Also, this attribute can be used to control which battery is discharged
+ when both a system battery and an Ultrabay battery are connected.)
+
+Misc read-only battery status attributes (see note about HDAPS below):
+
+/sys/devices/platform/smapi/BAT0/installed # 0 or 1
+/sys/devices/platform/smapi/BAT0/state # idle/charging/discharging
+/sys/devices/platform/smapi/BAT0/cycle_count # integer counter
+/sys/devices/platform/smapi/BAT0/current_now # instantaneous current
+/sys/devices/platform/smapi/BAT0/current_avg # last minute average
+/sys/devices/platform/smapi/BAT0/power_now # instantaneous power
+/sys/devices/platform/smapi/BAT0/power_avg # last minute average
+/sys/devices/platform/smapi/BAT0/last_full_capacity # in mWh
+/sys/devices/platform/smapi/BAT0/remaining_percent # remaining percent of energy (set by calibration)
+/sys/devices/platform/smapi/BAT0/remaining_percent_error # error range of remaing_percent (not reset by calibration)
+/sys/devices/platform/smapi/BAT0/remaining_running_time # in minutes, by last minute average power
+/sys/devices/platform/smapi/BAT0/remaining_running_time_now # in minutes, by instantenous power
+/sys/devices/platform/smapi/BAT0/remaining_charging_time # in minutes
+/sys/devices/platform/smapi/BAT0/remaining_capacity # in mWh
+/sys/devices/platform/smapi/BAT0/design_capacity # in mWh
+/sys/devices/platform/smapi/BAT0/voltage # in mV
+/sys/devices/platform/smapi/BAT0/design_voltage # in mV
+/sys/devices/platform/smapi/BAT0/charging_max_current # max charging current
+/sys/devices/platform/smapi/BAT0/charging_max_voltage # max charging voltage
+/sys/devices/platform/smapi/BAT0/group{0,1,2,3}_voltage # see below
+/sys/devices/platform/smapi/BAT0/manufacturer # string
+/sys/devices/platform/smapi/BAT0/model # string
+/sys/devices/platform/smapi/BAT0/barcoding # string
+/sys/devices/platform/smapi/BAT0/chemistry # string
+/sys/devices/platform/smapi/BAT0/serial # integer
+/sys/devices/platform/smapi/BAT0/manufacture_date # YYYY-MM-DD
+/sys/devices/platform/smapi/BAT0/first_use_date # YYYY-MM-DD
+/sys/devices/platform/smapi/BAT0/temperature # in milli-Celsius
+/sys/devices/platform/smapi/BAT0/dump # see below
+/sys/devices/platform/smapi/ac_connected # 0 or 1
+
+The BAT0/group{0,1,2,3}_voltage attribute refers to the separate cell groups
+in each battery. For example, on the ThinkPad 600, X3x, T4x and R5x models,
+the battery contains 3 cell groups in series, where each group consisting of 2
+or 3 cells connected in parallel. The voltage of each group is given by these
+attributes, and their sum (roughly) equals the "voltage" attribute.
+(The effective performance of the battery is determined by the weakest group,
+i.e., the one those voltage changes most rapidly during dis/charging.)
+
+The "BAT0/dump" attribute gives a a hex dump of the raw status data, which
+contains additional data now in the above (if you can figure it out). Some
+unused values are autodetected and replaced by "--":
+
+In all of the above, replace BAT0 with BAT1 to address the 2nd battery (e.g.
+in the UltraBay).
+
+
+Raw SMAPI calls:
+
+/sys/devices/platform/smapi/smapi_request
+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.
+If you did touch it, you would so something like
+# echo '211a 100 0 0' > /sys/devices/platform/smapi/smapi_request
+# cat /sys/devices/platform/smapi/smapi_request
+and notice that in the output "211a 34b b2 0 0 0 'OK'", the "4b" in the 2nd
+value, converted to decimal is 75: the current charge stop threshold.
+
+
+Model-specific status
+---------------------
+
+Works (at least partially) on the following ThinkPad model:
+* A30
+* G41
+* R40, R50p, R51, R52
+* T23, T40, T40p, T41, T41p, T42, T42p, T43, T43p, T60, T61, T400, T410, T420 (partially)
+* X24, X31, X32, X40, X41, X60, X61, X200, X201, X220 (partially)
+* Z60t, Z61m
+
+Does not work on:
+* X230 and newer
+* T430 and newer
+* Any ThinkPad Edge
+* Any ThinkPad Yoga
+* Any ThinkPad L series
+* Any ThinkPad P series
+
+Not all functions are available on all models; for detailed status, see:
+ http://thinkwiki.org/wiki/tp_smapi
+
+Please report success/failure by e-mail or on the Wiki.
+If you get a "not implemented" or "not supported" message, your laptop
+probably just can't do that (at least not via the SMAPI BIOS).
+For negative reports, follow the bug reporting guidelines below.
+If you send me the necessary technical data (i.e., SMAPI function
+interfaces), I will support additional models.
+
+
+Additional HDAPS features
+-------------------------
+
+The modified hdaps driver has several improvements on the one in mainline
+(beyond resolving the conflict with thinkpad_ec and tp_smapi):
+
+- Fixes reliability and improves support for recent ThinkPad models
+ (especially *60 and newer). Unlike the mainline driver, the modified hdaps
+ correctly follows the Embedded Controller communication protocol.
+
+- Extends the "invert" parameter to cover all possible axis orientations.
+ The possible values are as follows.
+ Let X,Y denote the hardware readouts.
+ Let R denote the laptop's roll (tilt left/right).
+ Let P denote the laptop's pitch (tilt forward/backward).
+ invert=0: R= X P= Y (same as mainline)
+ invert=1: R=-X P=-Y (same as mainline)
+ invert=2: R=-X P= Y (new)
+ invert=3: R= X P=-Y (new)
+ invert=4: R= Y P= X (new)
+ invert=5: R=-Y P=-X (new)
+ invert=6: R=-Y P= X (new)
+ invert=7: R= Y P=-X (new)
+ It's probably easiest to just try all 8 possibilities and see which yields
+ correct results (e.g., in the hdaps-gl visualisation).
+
+- Adds a whitelist which automatically sets the correct axis orientation for
+ some models. If the value for your model is wrong or missing, you can override
+ it using the "invert" parameter. Please also update the tables at
+ http://www.thinkwiki.org/wiki/tp_smapi and
+ http://www.thinkwiki.org/wiki/List_of_DMI_IDs
+ and submit a patch for the whitelist in hdaps.c.
+
+- Provides new attributes:
+ /sys/devices/platform/hdaps/sampling_rate:
+ This determines the frequency at which the host queries the embedded
+ controller for accelerometer data (and informs the hdaps input devices).
+ Default=50.
+ /sys/devices/platform/hdaps/oversampling_ratio:
+ When set to X, the embedded controller is told to do physical accelerometer
+ measurements at a rate that is X times higher than the rate at which
+ the driver reads those measurements (i.e., X*sampling_rate). This
+ makes the readouts from the embedded controller more fresh, and is also
+ useful for the running average filter (see next). Default=5
+ /sys/devices/platform/hdaps/running_avg_filter_order:
+ When set to X, reported readouts will be the average of the last X physical
+ accelerometer measurements. Current firmware allows 1<=X<=8. Setting to a
+ high value decreases readout fluctuations. The averaging is handled by the
+ embedded controller, so no CPU resources are used. Higher values make the
+ readouts smoother, since it averages out both sensor noise (good) and abrupt
+ changes (bad). Default=2.
+
+- Provides a second input device, which publishes the raw accelerometer
+ measurements (without the fuzzing needed for joystick emulation). This input
+ device can be matched by a udev rule such as the following (all on one line):
+ KERNEL=="event[0-9]*", ATTRS{phys}=="hdaps/input1",
+ ATTRS{modalias}=="input:b0019v1014p5054e4801-*",
+ SYMLINK+="input/hdaps/accelerometer-event
+
+A new version of the hdapsd userspace daemon, which uses the input device
+interface instead of polling sysfs, is available seprately. Using this reduces
+the total interrupts per second generated by hdaps+hdapsd (on tickless kernels)
+to 50, down from a value that fluctuates between 50 and 100. Set the
+sampling_rate sysfs attribute to a lower value to further reduce interrupts,
+at the expense of response latency.
+
+Licensing note: all my changes to the HDAPS driver are licensed under the
+GPL version 2 or, at your option and to the extent allowed by derivation from
+prior works, any later version. My version of hdaps is derived work from the
+mainline version, which at the time of writing is available only under
+GPL version 2.
+
+Bug reporting
+-------------
+
+Mail <multinymous@gmail.com>. Please include:
+* Details about your model,
+* Relevant "dmesg" output. Make sure thinkpad_ec and tp_smapi are loaded with
+ the "debug=1" parameter (e.g., use "make load HDAPS=1 DEBUG=1").
+* Output of "dmidecode | grep -C5 Product"
+* Does the failed functionality works under Windows?
+
+
+More about SMAPI
+----------------
+
+For hints about what may be possible via the SMAPI BIOS and how, see:
+
+* IBM Technical Reference Manual for the ThinkPad 770
+ (http://www-307.ibm.com/pc/support/site.wss/document.do?lndocid=PFAN-3TUQQD)
+* Exported symbols in PWRMGRIF.DLL or TPPWRW32.DLL (e.g., use "objdump -x").
+* drivers/char/mwave/smapi.c in the Linux kernel tree.*
+* The "thinkpad" SMAPI module (http://tpctl.sourceforge.net).
+* The SMAPI_* constants in tp_smapi.c.
+
+Note that in the above Technical Reference and in the "thinkpad" module,
+SMAPI is invoked through a function call to some physical address. However,
+the interface used by tp_smapi and the above mwave drive, and apparently
+required by newer ThinkPad, is different: you set the parameters up in the
+CPU's registers and write to ports 0xB2 (the APM control port) and 0x4F; this
+triggers an SMI (System Management Interrupt), causing the CPU to enter
+SMM (System Management Mode) and run the BIOS firmware; the results are
+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, 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
+thinkpad_ec module and the H8S hardware documentation:
+http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
diff --git a/Makefile b/Makefile
index 863f58503bee..f33cf760af6d 100644
--- a/Makefile
+++ b/Makefile
@@ -682,12 +682,16 @@ ifdef CONFIG_FUNCTION_TRACER
KBUILD_CFLAGS += $(call cc-disable-warning, format-overflow)
KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member)
+ifdef CONFIG_CC_OPTIMIZE_HARDER
+KBUILD_CFLAGS += -O3 $(call cc-disable-warning,maybe-uninitialized,)
+else
ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE
KBUILD_CFLAGS += -O2
else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3
KBUILD_CFLAGS += -O3
else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += -Os
endif
+endif
ifdef CONFIG_CC_DISABLE_WARN_MAYBE_UNINITIALIZED
KBUILD_CFLAGS += -Wno-maybe-uninitialized
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index 4f32c4062fb6..c0bf039e1b40 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -721,6 +721,7 @@ int rdma_addr_find_l2_eth_by_grh(const union ib_gid *sgid,
struct sockaddr _sockaddr;
struct sockaddr_in _sockaddr_in;
struct sockaddr_in6 _sockaddr_in6;
+ struct sockaddr_ib _sockaddr_ib;
} sgid_addr, dgid_addr;
int ret;
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 55d33500d55e..744e84228a1f 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -1338,7 +1338,9 @@ static int set_input_params(struct psmouse *psmouse,
if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
__set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit);
- }
+ } else if (SYN_CAP_CLICKPAD2BTN(info->ext_cap_0c) ||
+ SYN_CAP_CLICKPAD2BTN2(info->ext_cap_0c))
+ __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
return 0;
}
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index fc00e005c611..4cfbeec3ae4c 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -86,6 +86,7 @@
*/
#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & BIT(20)) /* 1-button ClickPad */
#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & BIT(8)) /* 2-button ClickPad */
+#define SYN_CAP_CLICKPAD2BTN2(ex0c) ((ex0c) & BIT(21)) /* 2-button ClickPad */
#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & BIT(17))
#define SYN_CAP_MIN_DIMENSIONS(ex0c) ((ex0c) & BIT(13))
#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & BIT(19))
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
index 97a420c11eed..c8621e9b2e4a 100644
--- a/drivers/macintosh/Kconfig
+++ b/drivers/macintosh/Kconfig
@@ -159,6 +159,13 @@ config INPUT_ADBHID
If unsure, say Y.
+config ADB_TRACKPAD_ABSOLUTE
+ bool "Enable absolute mode for adb trackpads"
+ depends on INPUT_ADBHID
+ help
+ Enable absolute mode in adb-base trackpads. This feature adds
+ compatibility with synaptics Xorg / Xfree drivers.
+
config MAC_EMUMOUSEBTN
tristate "Support for mouse button 2+3 emulation"
depends on SYSCTL && INPUT
diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c
index a261892c03b3..a85192de840c 100644
--- a/drivers/macintosh/adbhid.c
+++ b/drivers/macintosh/adbhid.c
@@ -262,6 +262,15 @@ static struct adb_ids buttons_ids;
#define ADBMOUSE_MS_A3 8 /* Mouse systems A3 trackball (handler 3) */
#define ADBMOUSE_MACALLY2 9 /* MacAlly 2-button mouse */
+#ifdef CONFIG_ADB_TRACKPAD_ABSOLUTE
+#define ABS_XMIN 310
+#define ABS_XMAX 1700
+#define ABS_YMIN 200
+#define ABS_YMAX 1000
+#define ABS_ZMIN 0
+#define ABS_ZMAX 55
+#endif
+
static void
adbhid_keyboard_input(unsigned char *data, int nb, int apoll)
{
@@ -405,6 +414,9 @@ static void
adbhid_mouse_input(unsigned char *data, int nb, int autopoll)
{
int id = (data[0] >> 4) & 0x0f;
+#ifdef CONFIG_ADB_TRACKPAD_ABSOLUTE
+ int btn = 0; int x_axis = 0; int y_axis = 0; int z_axis = 0;
+#endif
if (!adbhid[id]) {
pr_err("ADB HID on ID %d not yet registered\n", id);
@@ -436,6 +448,17 @@ adbhid_mouse_input(unsigned char *data, int nb, int autopoll)
high bits of y-axis motion. XY is additional
high bits of x-axis motion.
+ For ADB Absolute motion protocol the data array will contain the
+ following values:
+
+ BITS COMMENTS
+ data[0] = dddd 1100 ADB command: Talk, register 0, for device dddd.
+ data[1] = byyy yyyy Left button and y-axis motion.
+ data[2] = bxxx xxxx Second button and x-axis motion.
+ data[3] = 1yyy 1xxx Half bits of y-axis and x-axis motion.
+ data[4] = 1yyy 1xxx Higher bits of y-axis and x-axis motion.
+ data[5] = 1zzz 1zzz Higher and lower bits of z-pressure.
+
MacAlly 2-button mouse protocol.
For MacAlly 2-button mouse protocol the data array will contain the
@@ -458,8 +481,17 @@ adbhid_mouse_input(unsigned char *data, int nb, int autopoll)
switch (adbhid[id]->mouse_kind)
{
case ADBMOUSE_TRACKPAD:
+#ifdef CONFIG_ADB_TRACKPAD_ABSOLUTE
+ x_axis = (data[2] & 0x7f) | ((data[3] & 0x07) << 7) |
+ ((data[4] & 0x07) << 10);
+ y_axis = (data[1] & 0x7f) | ((data[3] & 0x70) << 3) |
+ ((data[4] & 0x70) << 6);
+ z_axis = (data[5] & 0x07) | ((data[5] & 0x70) >> 1);
+ btn = (!(data[1] >> 7)) & 1;
+#else
data[1] = (data[1] & 0x7f) | ((data[1] & data[2]) & 0x80);
data[2] = data[2] | 0x80;
+#endif
break;
case ADBMOUSE_MICROSPEED:
data[1] = (data[1] & 0x7f) | ((data[3] & 0x01) << 7);
@@ -485,17 +517,39 @@ adbhid_mouse_input(unsigned char *data, int nb, int autopoll)
break;
}
- input_report_key(adbhid[id]->input, BTN_LEFT, !((data[1] >> 7) & 1));
- input_report_key(adbhid[id]->input, BTN_MIDDLE, !((data[2] >> 7) & 1));
+#ifdef CONFIG_ADB_TRACKPAD_ABSOLUTE
+ if ( adbhid[id]->mouse_kind == ADBMOUSE_TRACKPAD ) {
- if (nb >= 4 && adbhid[id]->mouse_kind != ADBMOUSE_TRACKPAD)
- input_report_key(adbhid[id]->input, BTN_RIGHT, !((data[3] >> 7) & 1));
+ if(z_axis > 30) input_report_key(adbhid[id]->input, BTN_TOUCH, 1);
+ if(z_axis < 25) input_report_key(adbhid[id]->input, BTN_TOUCH, 0);
- input_report_rel(adbhid[id]->input, REL_X,
- ((data[2]&0x7f) < 64 ? (data[2]&0x7f) : (data[2]&0x7f)-128 ));
- input_report_rel(adbhid[id]->input, REL_Y,
- ((data[1]&0x7f) < 64 ? (data[1]&0x7f) : (data[1]&0x7f)-128 ));
+ if(z_axis > 0){
+ input_report_abs(adbhid[id]->input, ABS_X, x_axis);
+ input_report_abs(adbhid[id]->input, ABS_Y, y_axis);
+ input_report_key(adbhid[id]->input, BTN_TOOL_FINGER, 1);
+ input_report_key(adbhid[id]->input, ABS_TOOL_WIDTH, 5);
+ } else {
+ input_report_key(adbhid[id]->input, BTN_TOOL_FINGER, 0);
+ input_report_key(adbhid[id]->input, ABS_TOOL_WIDTH, 0);
+ }
+
+ input_report_abs(adbhid[id]->input, ABS_PRESSURE, z_axis);
+ input_report_key(adbhid[id]->input, BTN_LEFT, btn);
+ } else {
+#endif
+ input_report_key(adbhid[id]->input, BTN_LEFT, !((data[1] >> 7) & 1));
+ input_report_key(adbhid[id]->input, BTN_MIDDLE, !((data[2] >> 7) & 1));
+
+ if (nb >= 4 && adbhid[id]->mouse_kind != ADBMOUSE_TRACKPAD)
+ input_report_key(adbhid[id]->input, BTN_RIGHT, !((data[3] >> 7) & 1));
+ input_report_rel(adbhid[id]->input, REL_X,
+ ((data[2]&0x7f) < 64 ? (data[2]&0x7f) : (data[2]&0x7f)-128 ));
+ input_report_rel(adbhid[id]->input, REL_Y,
+ ((data[1]&0x7f) < 64 ? (data[1]&0x7f) : (data[1]&0x7f)-128 ));
+#ifdef CONFIG_ADB_TRACKPAD_ABSOLUTE
+ }
+#endif
input_sync(adbhid[id]->input);
}
@@ -849,6 +903,15 @@ adbhid_input_register(int id, int default_id, int original_handler_id,
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+#ifdef CONFIG_ADB_TRACKPAD_ABSOLUTE
+ set_bit(EV_ABS, input_dev->evbit);
+ input_set_abs_params(input_dev, ABS_X, ABS_XMIN, ABS_XMAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, ABS_YMIN, ABS_YMAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, ABS_ZMIN, ABS_ZMAX, 0, 0);
+ set_bit(BTN_TOUCH, input_dev->keybit);
+ set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+ set_bit(ABS_TOOL_WIDTH, input_dev->absbit);
+#endif
break;
case ADB_MISC:
@@ -1132,7 +1195,11 @@ init_trackpad(int id)
r1_buffer[3],
r1_buffer[4],
r1_buffer[5],
+#ifdef CONFIG_ADB_TRACKPAD_ABSOLUTE
+ 0x00, /* Enable absolute mode */
+#else
0x03, /*r1_buffer[6],*/
+#endif
r1_buffer[7]);
/* Without this flush, the trackpad may be locked up */
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index ac4d48830415..b272132ac742 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -573,9 +573,28 @@ config THINKPAD_ACPI_HOTKEY_POLL
If you are not sure, say Y here. The driver enables polling only if
it is strictly necessary to do so.
+config THINKPAD_EC
+ tristate
+ ---help---
+ This is a low-level driver for accessing the ThinkPad H8S embedded
+ controller over the LPC bus (not to be confused with the ACPI Embedded
+ Controller interface).
+
+config TP_SMAPI
+ tristate "ThinkPad SMAPI Support"
+ select THINKPAD_EC
+ default n
+ help
+ This adds SMAPI support on Lenovo/IBM ThinkPads, for features such
+ as battery charging control. For more information about this driver
+ see <http://www.thinkwiki.org/wiki/tp_smapi>.
+
+ If you have a Lenovo/IBM ThinkPad laptop, say Y or M here.
+
config SENSORS_HDAPS
tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
depends on INPUT
+ select THINKPAD_EC
select INPUT_POLLDEV
help
This driver provides support for the IBM Hard Drive Active Protection
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 2ba6cb795338..399f8b88646f 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -35,6 +35,8 @@ obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
+obj-$(CONFIG_THINKPAD_EC) += thinkpad_ec.o
+obj-$(CONFIG_TP_SMAPI) += tp_smapi.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
obj-$(CONFIG_FUJITSU_TABLET) += fujitsu-tablet.o
diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c
index c26baf77938e..1814614f240c 100644
--- a/drivers/platform/x86/hdaps.c
+++ b/drivers/platform/x86/hdaps.c
@@ -2,7 +2,7 @@
* hdaps.c - driver for IBM's Hard Drive Active Protection System
*
* Copyright (C) 2005 Robert Love <rml@novell.com>
- * Copyright (C) 2005 Jesper Juhl <jj@chaosbits.net>
+ * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com>
*
* The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads
* starting with the R40, T41, and X40. It provides a basic two-axis
@@ -30,266 +30,384 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
-#include <linux/input-polldev.h>
+#include <linux/input.h>
#include <linux/kernel.h>
-#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/dmi.h>
#include <linux/jiffies.h>
-#include <linux/io.h>
-
-#define HDAPS_LOW_PORT 0x1600 /* first port used by hdaps */
-#define HDAPS_NR_PORTS 0x30 /* number of ports: 0x1600 - 0x162f */
-
-#define HDAPS_PORT_STATE 0x1611 /* device state */
-#define HDAPS_PORT_YPOS 0x1612 /* y-axis position */
-#define HDAPS_PORT_XPOS 0x1614 /* x-axis position */
-#define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in Celsius */
-#define HDAPS_PORT_YVAR 0x1617 /* y-axis variance (what is this?) */
-#define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */
-#define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */
-#define HDAPS_PORT_UNKNOWN 0x161c /* what is this? */
-#define HDAPS_PORT_KMACT 0x161d /* keyboard or mouse activity */
-
-#define STATE_FRESH 0x50 /* accelerometer data is fresh */
+#include <linux/thinkpad_ec.h>
+#include <linux/pci_ids.h>
+#include <linux/version.h>
+
+/* Embedded controller accelerometer read command and its result: */
+static const struct thinkpad_ec_row ec_accel_args =
+ { .mask = 0x0001, .val = {0x11} };
+#define EC_ACCEL_IDX_READOUTS 0x1 /* readouts included in this read */
+ /* First readout, if READOUTS>=1: */
+#define EC_ACCEL_IDX_YPOS1 0x2 /* y-axis position word */
+#define EC_ACCEL_IDX_XPOS1 0x4 /* x-axis position word */
+#define EC_ACCEL_IDX_TEMP1 0x6 /* device temperature in Celsius */
+ /* Second readout, if READOUTS>=2: */
+#define EC_ACCEL_IDX_XPOS2 0x7 /* y-axis position word */
+#define EC_ACCEL_IDX_YPOS2 0x9 /* x-axis position word */
+#define EC_ACCEL_IDX_TEMP2 0xb /* device temperature in Celsius */
+#define EC_ACCEL_IDX_QUEUED 0xc /* Number of queued readouts left */
+#define EC_ACCEL_IDX_KMACT 0xd /* keyboard or mouse activity */
+#define EC_ACCEL_IDX_RETVAL 0xf /* command return value, good=0x00 */
#define KEYBD_MASK 0x20 /* set if keyboard activity */
#define MOUSE_MASK 0x40 /* set if mouse activity */
-#define KEYBD_ISSET(n) (!! (n & KEYBD_MASK)) /* keyboard used? */
-#define MOUSE_ISSET(n) (!! (n & MOUSE_MASK)) /* mouse used? */
-#define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */
-#define INIT_WAIT_MSECS 200 /* ... in 200ms increments */
+#define READ_TIMEOUT_MSECS 100 /* wait this long for device read */
+#define RETRY_MSECS 3 /* retry delay */
-#define HDAPS_POLL_INTERVAL 50 /* poll for input every 1/20s (50 ms)*/
#define HDAPS_INPUT_FUZZ 4 /* input event threshold */
#define HDAPS_INPUT_FLAT 4
-
-#define HDAPS_X_AXIS (1 << 0)
-#define HDAPS_Y_AXIS (1 << 1)
-#define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS)
-
+#define KMACT_REMEMBER_PERIOD (HZ/10) /* keyboard/mouse persistence */
+
+/* Input IDs */
+#define HDAPS_INPUT_VENDOR PCI_VENDOR_ID_IBM
+#define HDAPS_INPUT_PRODUCT 0x5054 /* "TP", shared with thinkpad_acpi */
+#define HDAPS_INPUT_JS_VERSION 0x6801 /* Joystick emulation input device */
+#define HDAPS_INPUT_RAW_VERSION 0x4801 /* Raw accelerometer input device */
+
+/* Axis orientation. */
+/* The unnatural bit-representation of inversions is for backward
+ * compatibility with the"invert=1" module parameter. */
+#define HDAPS_ORIENT_INVERT_XY 0x01 /* Invert both X and Y axes. */
+#define HDAPS_ORIENT_INVERT_X 0x02 /* Invert the X axis (uninvert if
+ * already inverted by INVERT_XY). */
+#define HDAPS_ORIENT_SWAP 0x04 /* Swap the axes. The swap occurs
+ * before inverting X or Y. */
+#define HDAPS_ORIENT_MAX 0x07
+#define HDAPS_ORIENT_UNDEFINED 0xFF /* Placeholder during initialization */
+#define HDAPS_ORIENT_INVERT_Y (HDAPS_ORIENT_INVERT_XY | HDAPS_ORIENT_INVERT_X)
+
+static struct timer_list hdaps_timer;
static struct platform_device *pdev;
-static struct input_polled_dev *hdaps_idev;
-static unsigned int hdaps_invert;
-static u8 km_activity;
-static int rest_x;
-static int rest_y;
-
-static DEFINE_MUTEX(hdaps_mtx);
-
-/*
- * __get_latch - Get the value from a given port. Callers must hold hdaps_mtx.
- */
-static inline u8 __get_latch(u16 port)
+static struct input_dev *hdaps_idev; /* joystick-like device with fuzz */
+static struct input_dev *hdaps_idev_raw; /* raw hdaps sensor readouts */
+static unsigned int hdaps_invert = HDAPS_ORIENT_UNDEFINED;
+static int needs_calibration;
+
+/* Configuration: */
+static int sampling_rate = 50; /* Sampling rate */
+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 */
+
+/* Latest state readout: */
+static int pos_x, pos_y; /* position */
+static int temperature; /* temperature */
+static int stale_readout = 1; /* last read invalid */
+static int rest_x, rest_y; /* calibrated rest position */
+
+/* Last time we saw keyboard and mouse activity: */
+static u64 last_keyboard_jiffies = INITIAL_JIFFIES;
+static u64 last_mouse_jiffies = INITIAL_JIFFIES;
+static u64 last_update_jiffies = INITIAL_JIFFIES;
+
+/* input device use count */
+static int hdaps_users;
+static DEFINE_MUTEX(hdaps_users_mtx);
+
+/* Some models require an axis transformation to the standard representation */
+static void transform_axes(int *x, int *y)
{
- return inb(port) & 0xff;
+ if (hdaps_invert & HDAPS_ORIENT_SWAP) {
+ int z;
+ z = *x;
+ *x = *y;
+ *y = z;
+ }
+ if (hdaps_invert & HDAPS_ORIENT_INVERT_XY) {
+ *x = -*x;
+ *y = -*y;
+ }
+ if (hdaps_invert & HDAPS_ORIENT_INVERT_X)
+ *x = -*x;
}
-/*
- * __check_latch - Check a port latch for a given value. Returns zero if the
- * port contains the given value. Callers must hold hdaps_mtx.
+/**
+ * __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 inline int __check_latch(u16 port, u8 val)
+static int __hdaps_update(int fast)
{
- if (__get_latch(port) == val)
- return 0;
- return -EINVAL;
-}
+ /* Read data: */
+ struct thinkpad_ec_row data;
+ int 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_mtx.
- */
-static int __wait_latch(u16 port, u8 val)
-{
- unsigned int i;
+ data.mask = (1 << EC_ACCEL_IDX_READOUTS) | (1 << EC_ACCEL_IDX_KMACT) |
+ (3 << EC_ACCEL_IDX_YPOS1) | (3 << EC_ACCEL_IDX_XPOS1) |
+ (1 << EC_ACCEL_IDX_TEMP1) | (1 << EC_ACCEL_IDX_RETVAL);
+ if (fast)
+ ret = thinkpad_ec_try_read_row(&ec_accel_args, &data);
+ else
+ ret = thinkpad_ec_read_row(&ec_accel_args, &data);
+ thinkpad_ec_prefetch_row(&ec_accel_args); /* Prefetch even if error */
+ if (ret)
+ return ret;
- for (i = 0; i < 20; i++) {
- if (!__check_latch(port, val))
- return 0;
- udelay(5);
+ /* Check status: */
+ if (data.val[EC_ACCEL_IDX_RETVAL] != 0x00) {
+ pr_warn("read RETVAL=0x%02x\n",
+ data.val[EC_ACCEL_IDX_RETVAL]);
+ return -EIO;
}
- return -EIO;
+ 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);
+ 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
+ * event for KMACT_REMEMBER_PERIOD jiffies.
+ */
+ if (data.val[EC_ACCEL_IDX_KMACT] & KEYBD_MASK)
+ last_keyboard_jiffies = get_jiffies_64();
+ if (data.val[EC_ACCEL_IDX_KMACT] & MOUSE_MASK)
+ last_mouse_jiffies = get_jiffies_64();
+
+ temperature = data.val[EC_ACCEL_IDX_TEMP1];
+
+ last_update_jiffies = get_jiffies_64();
+ stale_readout = 0;
+ if (needs_calibration) {
+ rest_x = pos_x;
+ rest_y = pos_y;
+ needs_calibration = 0;
+ }
+
+ return 0;
}
-/*
- * __device_refresh - request a refresh from the accelerometer. Does not wait
- * for refresh to complete. Callers must hold hdaps_mtx.
+/**
+ * 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.
*/
-static void __device_refresh(void)
+static int hdaps_update(void)
{
- udelay(200);
- if (inb(0x1604) != STATE_FRESH) {
- outb(0x11, 0x1610);
- outb(0x01, 0x161f);
+ u64 age = get_jiffies_64() - last_update_jiffies;
+ int total, ret;
+
+ if (!stale_readout && age < (9*HZ)/(10*sampling_rate))
+ return 0; /* already updated recently */
+ for (total = 0; total < READ_TIMEOUT_MSECS; total += RETRY_MSECS) {
+ ret = thinkpad_ec_lock();
+ if (ret)
+ return ret;
+ ret = __hdaps_update(0);
+ thinkpad_ec_unlock();
+
+ if (!ret)
+ return 0;
+ if (ret != -EBUSY)
+ break;
+ msleep(RETRY_MSECS);
}
+ return ret;
}
-/*
- * __device_refresh_sync - request a synchronous refresh from the
- * accelerometer. We wait for the refresh to complete. Returns zero if
- * successful and nonzero on error. Callers must hold hdaps_mtx.
+/**
+ * hdaps_set_power - enable or disable power to the accelerometer.
+ * Returns zero on success and negative error code on failure. Can sleep.
*/
-static int __device_refresh_sync(void)
+static int hdaps_set_power(int on)
{
- __device_refresh();
- return __wait_latch(0x1604, STATE_FRESH);
+ 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);
+ if (ret)
+ return ret;
+ if (data.val[0xF] != 0x00)
+ return -EIO;
+ return 0;
}
-/*
- * __device_complete - indicate to the accelerometer that we are done reading
- * data, and then initiate an async refresh. Callers must hold hdaps_mtx.
+/**
+ * hdaps_set_ec_config - set accelerometer parameters.
+ * @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 inline void __device_complete(void)
+static int hdaps_set_ec_config(int ec_rate, int order)
{
- inb(0x161f);
- inb(0x1604);
- __device_refresh();
+ 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);
+ pr_debug("setting ec_rate=%d, filter_order=%d\n", ec_rate, order);
+ if (ret)
+ return ret;
+ if (data.val[0xF] == 0x03) {
+ pr_warn("config param out of range\n");
+ return -EINVAL;
+ }
+ if (data.val[0xF] == 0x06) {
+ pr_warn("config change already pending\n");
+ return -EBUSY;
+ }
+ if (data.val[0xF] != 0x00) {
+ pr_warn("config change error, ret=%d\n",
+ data.val[0xF]);
+ return -EIO;
+ }
+ return 0;
}
-/*
- * 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_get_ec_config - get accelerometer parameters.
+ * @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_readb_one(unsigned int port, u8 *val)
+static int hdaps_get_ec_config(int *ec_rate, int *order)
{
- int ret;
-
- mutex_lock(&hdaps_mtx);
-
- /* do a sync refresh -- we need to be sure that we read fresh data */
- ret = __device_refresh_sync();
+ 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);
if (ret)
- goto out;
-
- *val = inb(port);
- __device_complete();
-
-out:
- mutex_unlock(&hdaps_mtx);
- return ret;
+ return ret;
+ if (data.val[0xF] != 0x00)
+ return -EIO;
+ if (!(data.val[0x1] & 0x01))
+ return -ENXIO; /* accelerometer polling not enabled */
+ if (data.val[0x1] & 0x02)
+ return -EBUSY; /* config change in progress, retry later */
+ *ec_rate = data.val[0x2] | ((int)(data.val[0x3]) << 8);
+ *order = data.val[0x4];
+ return 0;
}
-/* __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)
+/**
+ * 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)
{
- /* do a sync refresh -- we need to be sure that we read fresh data */
- if (__device_refresh_sync())
+ 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) {
+ pr_warn("accelerometer not implemented (0x%02x)\n",
+ data.val[0xF]);
return -EIO;
-
- *y = inw(port2);
- *x = inw(port1);
- km_activity = inb(HDAPS_PORT_KMACT);
- __device_complete();
-
- /* hdaps_invert is a bitvector to negate the axes */
- if (hdaps_invert & HDAPS_X_AXIS)
- *x = -*x;
- if (hdaps_invert & HDAPS_Y_AXIS)
- *y = -*y;
-
+ }
+ *mode = data.val[0x1];
return 0;
}
-/*
- * hdaps_read_pair - reads the values from a pair of ports, placing the values
- * in the given pointers. Returns zero on success. Can sleep.
+/**
+ * hdaps_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 hdaps_read_pair(unsigned int port1, unsigned int port2,
- int *val1, int *val2)
+static int hdaps_check_ec(void)
{
- int ret;
-
- mutex_lock(&hdaps_mtx);
- ret = __hdaps_read_pair(port1, port2, val1, val2);
- mutex_unlock(&hdaps_mtx);
-
- return ret;
+ 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);
+ if (ret)
+ return ret;
+ if (!((data.val[0x1] == 0x00 && data.val[0x2] == 0x60) || /* cleanroom spec */
+ (data.val[0x1] == 0x01 && data.val[0x2] == 0x00)) || /* seen on T61 */
+ data.val[0x3] != 0x00 || data.val[0xF] != 0x00) {
+ pr_warn("hdaps_check_ec: bad response (0x%x,0x%x,0x%x,0x%x)\n",
+ data.val[0x1], data.val[0x2],
+ data.val[0x3], data.val[0xF]);
+ return -EIO;
+ }
+ 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 FAILED_INIT(msg) pr_err("init failed at: %s\n", msg)
static int hdaps_device_init(void)
{
- int total, ret = -ENXIO;
+ int ret;
+ u8 mode;
- mutex_lock(&hdaps_mtx);
+ ret = thinkpad_ec_lock();
+ if (ret)
+ return ret;
- outb(0x13, 0x1610);
- outb(0x01, 0x161f);
- if (__wait_latch(0x161f, 0x00))
- goto out;
+ if (hdaps_get_ec_mode(&mode))
+ { FAILED_INIT("hdaps_get_ec_mode failed"); goto bad; }
- /*
- * Most ThinkPads return 0x01.
- *
- * Others--namely the R50p, T41p, and T42p--return 0x03. These laptops
- * have "inverted" axises.
- *
- * The 0x02 value occurs when the chip has been previously initialized.
- */
- if (__check_latch(0x1611, 0x03) &&
- __check_latch(0x1611, 0x02) &&
- __check_latch(0x1611, 0x01))
- goto out;
+ pr_debug("initial mode latch is 0x%02x\n", mode);
+ if (mode == 0x00)
+ { FAILED_INIT("accelerometer not available"); goto bad; }
- printk(KERN_DEBUG "hdaps: initial latch check good (0x%02x)\n",
- __get_latch(0x1611));
+ if (hdaps_check_ec())
+ { FAILED_INIT("hdaps_check_ec failed"); goto bad; }
- 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;
-
- /* 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;
+ if (hdaps_set_power(1))
+ { FAILED_INIT("hdaps_set_power failed"); goto bad; }
- /* a read of the device helps push it into action */
- __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y);
- if (!__wait_latch(0x1611, 0x02)) {
- ret = 0;
- break;
- }
+ if (hdaps_set_ec_config(sampling_rate*oversampling_ratio,
+ running_avg_filter_order))
+ { FAILED_INIT("hdaps_set_ec_config failed"); goto bad; }
- msleep(INIT_WAIT_MSECS);
- }
+ thinkpad_ec_invalidate();
+ udelay(200);
-out:
- mutex_unlock(&hdaps_mtx);
+ /* Just prefetch instead of reading, to avoid ~1sec delay on load */
+ ret = thinkpad_ec_prefetch_row(&ec_accel_args);
+ if (ret)
+ { FAILED_INIT("initial prefetch failed"); goto bad; }
+ goto good;
+bad:
+ thinkpad_ec_invalidate();
+ ret = -ENXIO;
+good:
+ stale_readout = 1;
+ thinkpad_ec_unlock();
return ret;
}
+/**
+ * hdaps_device_shutdown - power off the accelerometer
+ * Returns nonzero on failure. Can sleep.
+ */
+static int hdaps_device_shutdown(void)
+{
+ int ret;
+ ret = hdaps_set_power(0);
+ if (ret) {
+ pr_warn("cannot power off\n");
+ return ret;
+ }
+ ret = hdaps_set_ec_config(0, 1);
+ if (ret)
+ pr_warn("cannot stop EC sampling\n");
+ return ret;
+}
/* Device model stuff */
@@ -306,13 +424,29 @@ static int hdaps_probe(struct platform_device *dev)
}
#ifdef CONFIG_PM_SLEEP
+static int hdaps_suspend(struct device *dev)
+{
+ /* Don't do hdaps polls until resume re-initializes the sensor. */
+ del_timer_sync(&hdaps_timer);
+ hdaps_device_shutdown(); /* ignore errors, effect is negligible */
+ return 0;
+}
+
static int hdaps_resume(struct device *dev)
{
- return hdaps_device_init();
+ int ret = hdaps_device_init();
+ if (ret)
+ return ret;
+
+ mutex_lock(&hdaps_users_mtx);
+ if (hdaps_users)
+ mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
+ mutex_unlock(&hdaps_users_mtx);
+ return 0;
}
#endif
-static SIMPLE_DEV_PM_OPS(hdaps_pm, NULL, hdaps_resume);
+static SIMPLE_DEV_PM_OPS(hdaps_pm, hdaps_suspend, hdaps_resume);
static struct platform_driver hdaps_driver = {
.probe = hdaps_probe,
@@ -322,30 +456,51 @@ static struct platform_driver hdaps_driver = {
},
};
-/*
- * hdaps_calibrate - Set our "resting" values. Callers must hold hdaps_mtx.
+/**
+ * hdaps_calibrate - set our "resting" values.
+ * Does its own locking.
*/
static void hdaps_calibrate(void)
{
- __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y);
+ needs_calibration = 1;
+ hdaps_update();
+ /* If that fails, the mousedev poll will take care of things later. */
}
-static void hdaps_mousedev_poll(struct input_polled_dev *dev)
+/* Timer handler for updating the input device. Runs in softirq context,
+ * so avoid lenghty or blocking operations.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)
+static void hdaps_mousedev_poll(unsigned long unused)
+#else
+static void hdaps_mousedev_poll(struct timer_list *unused)
+#endif
{
- struct input_dev *input_dev = dev->input;
- int x, y;
+ int ret;
- mutex_lock(&hdaps_mtx);
+ stale_readout = 1;
- if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y))
- goto out;
+ /* Cannot sleep. Try nonblockingly. If we fail, try again later. */
+ if (thinkpad_ec_try_lock())
+ goto keep_active;
- input_report_abs(input_dev, ABS_X, x - rest_x);
- input_report_abs(input_dev, ABS_Y, y - rest_y);
- input_sync(input_dev);
+ ret = __hdaps_update(1); /* fast update, we're in softirq context */
+ thinkpad_ec_unlock();
+ /* Any of "successful", "not yet ready" and "not prefetched"? */
+ if (ret != 0 && ret != -EBUSY && ret != -ENODATA) {
+ pr_err("poll failed, disabling updates\n");
+ return;
+ }
-out:
- mutex_unlock(&hdaps_mtx);
+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);
+ input_report_abs(hdaps_idev_raw, ABS_X, pos_x);
+ input_report_abs(hdaps_idev_raw, ABS_Y, pos_y);
+ input_sync(hdaps_idev_raw);
+ mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
}
@@ -354,65 +509,41 @@ static void hdaps_mousedev_poll(struct input_polled_dev *dev)
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;
-
- return sprintf(buf, "(%d,%d)\n", x, y);
-}
-
-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);
- 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_temp1_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- u8 uninitialized_var(temp);
- int ret;
-
- ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp);
- if (ret)
- return ret;
-
- return sprintf(buf, "%u\n", temp);
-}
-
-static ssize_t hdaps_temp2_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- u8 uninitialized_var(temp);
- int ret;
-
- ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp);
+ int ret = hdaps_update();
if (ret)
return ret;
-
- return sprintf(buf, "%u\n", temp);
+ return sprintf(buf, "%d\n", temperature);
}
static ssize_t hdaps_keyboard_activity_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- return sprintf(buf, "%u\n", KEYBD_ISSET(km_activity));
+ int ret = hdaps_update();
+ if (ret)
+ return ret;
+ return sprintf(buf, "%u\n",
+ get_jiffies_64() < last_keyboard_jiffies + KMACT_REMEMBER_PERIOD);
}
static ssize_t hdaps_mouse_activity_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- return sprintf(buf, "%u\n", MOUSE_ISSET(km_activity));
+ int ret = hdaps_update();
+ if (ret)
+ return ret;
+ return sprintf(buf, "%u\n",
+ get_jiffies_64() < last_mouse_jiffies + KMACT_REMEMBER_PERIOD);
}
static ssize_t hdaps_calibrate_show(struct device *dev,
@@ -425,10 +556,7 @@ static ssize_t hdaps_calibrate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- mutex_lock(&hdaps_mtx);
hdaps_calibrate();
- mutex_unlock(&hdaps_mtx);
-
return count;
}
@@ -445,7 +573,7 @@ static ssize_t hdaps_invert_store(struct device *dev,
int invert;
if (sscanf(buf, "%d", &invert) != 1 ||
- invert < 0 || invert > HDAPS_BOTH_AXES)
+ invert < 0 || invert > HDAPS_ORIENT_MAX)
return -EINVAL;
hdaps_invert = invert;
@@ -454,24 +582,128 @@ static ssize_t hdaps_invert_store(struct device *dev,
return count;
}
+static ssize_t hdaps_sampling_rate_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", sampling_rate);
+}
+
+static ssize_t hdaps_sampling_rate_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rate, ret;
+ if (sscanf(buf, "%d", &rate) != 1 || rate > HZ || rate <= 0) {
+ pr_warn("must have 0<input_sampling_rate<=HZ=%d\n", HZ);
+ return -EINVAL;
+ }
+ ret = hdaps_set_ec_config(rate*oversampling_ratio,
+ running_avg_filter_order);
+ if (ret)
+ return ret;
+ sampling_rate = rate;
+ return count;
+}
+
+static ssize_t hdaps_oversampling_ratio_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int ec_rate, order;
+ int ret = hdaps_get_ec_config(&ec_rate, &order);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%u\n", ec_rate / sampling_rate);
+}
+
+static ssize_t hdaps_oversampling_ratio_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ratio, ret;
+ if (sscanf(buf, "%d", &ratio) != 1 || ratio < 1)
+ return -EINVAL;
+ ret = hdaps_set_ec_config(sampling_rate*ratio,
+ running_avg_filter_order);
+ if (ret)
+ return ret;
+ oversampling_ratio = ratio;
+ return count;
+}
+
+static ssize_t hdaps_running_avg_filter_order_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int rate, order;
+ int ret = hdaps_get_ec_config(&rate, &order);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%u\n", order);
+}
+
+static ssize_t hdaps_running_avg_filter_order_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int order, ret;
+ if (sscanf(buf, "%d", &order) != 1)
+ return -EINVAL;
+ ret = hdaps_set_ec_config(sampling_rate*oversampling_ratio, order);
+ if (ret)
+ return ret;
+ running_avg_filter_order = order;
+ return count;
+}
+
+static int hdaps_mousedev_open(struct input_dev *dev)
+{
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+
+ mutex_lock(&hdaps_users_mtx);
+ if (hdaps_users++ == 0) /* first input user */
+ mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
+ mutex_unlock(&hdaps_users_mtx);
+ return 0;
+}
+
+static void hdaps_mousedev_close(struct input_dev *dev)
+{
+ mutex_lock(&hdaps_users_mtx);
+ if (--hdaps_users == 0) /* no input users left */
+ del_timer_sync(&hdaps_timer);
+ mutex_unlock(&hdaps_users_mtx);
+
+ module_put(THIS_MODULE);
+}
+
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(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL);
+ /* "temp1" instead of "temperature" is hwmon convention */
+static DEVICE_ATTR(keyboard_activity, 0444,
+ hdaps_keyboard_activity_show, NULL);
static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL);
-static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store);
+static DEVICE_ATTR(calibrate, 0644,
+ hdaps_calibrate_show, hdaps_calibrate_store);
static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store);
+static DEVICE_ATTR(sampling_rate, 0644,
+ hdaps_sampling_rate_show, hdaps_sampling_rate_store);
+static DEVICE_ATTR(oversampling_ratio, 0644,
+ hdaps_oversampling_ratio_show,
+ hdaps_oversampling_ratio_store);
+static DEVICE_ATTR(running_avg_filter_order, 0644,
+ hdaps_running_avg_filter_order_show,
+ hdaps_running_avg_filter_order_store);
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,
&dev_attr_invert.attr,
+ &dev_attr_sampling_rate.attr,
+ &dev_attr_oversampling_ratio.attr,
+ &dev_attr_running_avg_filter_order.attr,
NULL,
};
@@ -482,84 +714,82 @@ static struct attribute_group hdaps_attribute_group = {
/* Module stuff */
-/* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */
-static int __init hdaps_dmi_match(const struct dmi_system_id *id)
-{
- pr_info("%s detected\n", id->ident);
- return 1;
-}
-
/* hdaps_dmi_match_invert - found an inverted match. */
static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id)
{
- hdaps_invert = (unsigned long)id->driver_data;
- pr_info("inverting axis (%u) readings\n", hdaps_invert);
- return hdaps_dmi_match(id);
+ unsigned int orient = (kernel_ulong_t) id->driver_data;
+ hdaps_invert = orient;
+ pr_info("%s detected, setting orientation %u\n", id->ident, orient);
+ return 1; /* stop enumeration */
}
-#define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \
+#define HDAPS_DMI_MATCH_INVERT(vendor, model, orient) { \
.ident = vendor " " model, \
.callback = hdaps_dmi_match_invert, \
- .driver_data = (void *)axes, \
+ .driver_data = (void *)(orient), \
.matches = { \
DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
DMI_MATCH(DMI_PRODUCT_VERSION, model) \
} \
}
-#define HDAPS_DMI_MATCH_NORMAL(vendor, model) \
- HDAPS_DMI_MATCH_INVERT(vendor, model, 0)
-
-/* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
- "ThinkPad T42p", so the order of the entries matters.
- If your ThinkPad is not recognized, please update to latest
- BIOS. This is especially the case for some R52 ThinkPads. */
-static const struct dmi_system_id hdaps_whitelist[] __initconst = {
- HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"),
- HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"),
- HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"),
- HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"),
- HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"),
- HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"),
- HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"),
- HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_Y_AXIS),
- HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"),
- HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m", HDAPS_BOTH_AXES),
- HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p", HDAPS_BOTH_AXES),
+/* List of models with abnormal axis configuration.
+ Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
+ "ThinkPad T42p", and enumeration stops after first match,
+ so the order of the entries matters. */
+const struct dmi_system_id hdaps_whitelist[] __initconst = {
+ HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R60", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X40", HDAPS_ORIENT_INVERT_Y),
+ HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_ORIENT_INVERT_Y),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R60", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R400", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R500", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60 Tablet", HDAPS_ORIENT_INVERT_Y),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60s", HDAPS_ORIENT_INVERT_Y),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400s", HDAPS_ORIENT_INVERT_X),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T410s", HDAPS_ORIENT_SWAP),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T410", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T500", HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T510", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X | HDAPS_ORIENT_INVERT_Y),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad W510", HDAPS_ORIENT_MAX),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad W520", HDAPS_ORIENT_MAX),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X200s", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X200", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X | HDAPS_ORIENT_INVERT_Y),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X201 Tablet", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X201s", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_XY),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X201", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X220", HDAPS_ORIENT_SWAP),
{ .ident = NULL }
};
static int __init hdaps_init(void)
{
- struct input_dev *idev;
int ret;
- if (!dmi_check_system(hdaps_whitelist)) {
- pr_warn("supported laptop not found!\n");
- ret = -ENODEV;
- goto out;
- }
-
- if (!request_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS, "hdaps")) {
- ret = -ENXIO;
- goto out;
- }
-
+ /* Determine axis orientation orientation */
+ if (hdaps_invert == HDAPS_ORIENT_UNDEFINED) /* set by module param? */
+ if (dmi_check_system(hdaps_whitelist) < 1) /* in whitelist? */
+ hdaps_invert = 0; /* default */
+
+ /* Init timer before platform_driver_register, in case of suspend */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)
+ init_timer(&hdaps_timer);
+ hdaps_timer.function = hdaps_mousedev_poll;
+#else
+ timer_setup(&hdaps_timer, hdaps_mousedev_poll, 0);
+#endif
ret = platform_driver_register(&hdaps_driver);
if (ret)
- goto out_region;
+ goto out;
pdev = platform_device_register_simple("hdaps", -1, NULL, 0);
if (IS_ERR(pdev)) {
@@ -571,47 +801,79 @@ static int __init hdaps_init(void)
if (ret)
goto out_device;
- hdaps_idev = input_allocate_polled_device();
+ hdaps_idev = input_allocate_device();
if (!hdaps_idev) {
ret = -ENOMEM;
goto out_group;
}
- hdaps_idev->poll = hdaps_mousedev_poll;
- hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL;
-
- /* initial calibrate for the input device */
- hdaps_calibrate();
+ hdaps_idev_raw = input_allocate_device();
+ if (!hdaps_idev_raw) {
+ ret = -ENOMEM;
+ goto out_idev_first;
+ }
- /* initialize the input class */
- idev = hdaps_idev->input;
- idev->name = "hdaps";
- idev->phys = "isa1600/input0";
- idev->id.bustype = BUS_ISA;
- idev->dev.parent = &pdev->dev;
- idev->evbit[0] = BIT_MASK(EV_ABS);
- input_set_abs_params(idev, ABS_X,
+ /* calibration for the input device (deferred to avoid delay) */
+ needs_calibration = 1;
+
+ /* initialize the joystick-like fuzzed input device */
+ hdaps_idev->name = "ThinkPad HDAPS joystick emulation";
+ hdaps_idev->phys = "hdaps/input0";
+ hdaps_idev->id.bustype = BUS_HOST;
+ hdaps_idev->id.vendor = HDAPS_INPUT_VENDOR;
+ hdaps_idev->id.product = HDAPS_INPUT_PRODUCT;
+ hdaps_idev->id.version = HDAPS_INPUT_JS_VERSION;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ hdaps_idev->cdev.dev = &pdev->dev;
+#endif
+ hdaps_idev->evbit[0] = BIT(EV_ABS);
+ hdaps_idev->open = hdaps_mousedev_open;
+ hdaps_idev->close = hdaps_mousedev_close;
+ input_set_abs_params(hdaps_idev, ABS_X,
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
- input_set_abs_params(idev, ABS_Y,
+ input_set_abs_params(hdaps_idev, ABS_Y,
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
- ret = input_register_polled_device(hdaps_idev);
+ ret = input_register_device(hdaps_idev);
if (ret)
goto out_idev;
- pr_info("driver successfully loaded\n");
+ /* initialize the raw data input device */
+ hdaps_idev_raw->name = "ThinkPad HDAPS accelerometer data";
+ hdaps_idev_raw->phys = "hdaps/input1";
+ hdaps_idev_raw->id.bustype = BUS_HOST;
+ hdaps_idev_raw->id.vendor = HDAPS_INPUT_VENDOR;
+ hdaps_idev_raw->id.product = HDAPS_INPUT_PRODUCT;
+ hdaps_idev_raw->id.version = HDAPS_INPUT_RAW_VERSION;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ hdaps_idev_raw->cdev.dev = &pdev->dev;
+#endif
+ hdaps_idev_raw->evbit[0] = BIT(EV_ABS);
+ hdaps_idev_raw->open = hdaps_mousedev_open;
+ hdaps_idev_raw->close = hdaps_mousedev_close;
+ input_set_abs_params(hdaps_idev_raw, ABS_X, -32768, 32767, 0, 0);
+ input_set_abs_params(hdaps_idev_raw, ABS_Y, -32768, 32767, 0, 0);
+
+ ret = input_register_device(hdaps_idev_raw);
+ if (ret)
+ goto out_idev_reg_first;
+
+ pr_info("driver successfully loaded.\n");
return 0;
+out_idev_reg_first:
+ input_unregister_device(hdaps_idev);
out_idev:
- input_free_polled_device(hdaps_idev);
+ input_free_device(hdaps_idev_raw);
+out_idev_first:
+ input_free_device(hdaps_idev);
out_group:
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
out_device:
platform_device_unregister(pdev);
out_driver:
platform_driver_unregister(&hdaps_driver);
-out_region:
- release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS);
+ hdaps_device_shutdown();
out:
pr_warn("driver init failed (ret=%d)!\n", ret);
return ret;
@@ -619,12 +881,12 @@ static int __init hdaps_init(void)
static void __exit hdaps_exit(void)
{
- input_unregister_polled_device(hdaps_idev);
- input_free_polled_device(hdaps_idev);
+ input_unregister_device(hdaps_idev_raw);
+ input_unregister_device(hdaps_idev);
+ 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);
- release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS);
pr_info("driver unloaded\n");
}
@@ -632,9 +894,8 @@ static void __exit hdaps_exit(void)
module_init(hdaps_init);
module_exit(hdaps_exit);
-module_param_named(invert, hdaps_invert, int, 0);
-MODULE_PARM_DESC(invert, "invert data along each axis. 1 invert x-axis, "
- "2 invert y-axis, 3 invert both axes.");
+module_param_named(invert, hdaps_invert, uint, 0);
+MODULE_PARM_DESC(invert, "axis orientation code");
MODULE_AUTHOR("Robert Love");
MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver");
diff --git a/drivers/platform/x86/thinkpad_ec.c b/drivers/platform/x86/thinkpad_ec.c
new file mode 100644
index 000000000000..597614bc17e6
--- /dev/null
+++ b/drivers/platform/x86/thinkpad_ec.c
@@ -0,0 +1,513 @@
+/*
+ * thinkpad_ec.c - ThinkPad embedded controller LPC3 functions
+ *
+ * The embedded controller on ThinkPad laptops has a non-standard interface,
+ * where LPC channel 3 of the H8S EC chip is hooked up to IO ports
+ * 0x1600-0x161F and implements (a special case of) the H8S LPC protocol.
+ * The EC LPC interface provides various system management services (currently
+ * known: battery information and accelerometer readouts). This driver
+ * provides access and mutual exclusion for the EC interface.
+*
+ * The LPC protocol and terminology are documented here:
+ * "H8S/2104B Group Hardware Manual",
+ * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
+ *
+ * Copyright (C) 2006-2007 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/thinkpad_ec.h>
+#include <linux/jiffies.h>
+#include <asm/io.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+ #include <asm/semaphore.h>
+#else
+ #include <linux/semaphore.h>
+#endif
+
+#define TP_VERSION "0.42"
+
+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. */
+ /* (and port TPC_TWR0_PORT+i is mapped to H8S reg TWRi for 0<i<16) */
+
+/* H8S STR3 status flags (see "H8S/2104B Group Hardware Manual" p.549) */
+#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 1000
+#define TPC_REQUEST_NDELAY 10
+#define TPC_PREFETCH_TIMEOUT (HZ/10) /* invalidate prefetch after 0.1sec */
+
+/* A few macros for printk()ing: */
+#define MSG_FMT(fmt, args...) \
+ "thinkpad_ec: %s: " fmt "\n", __func__, ## args
+#define REQ_FMT(msg, code) \
+ MSG_FMT("%s: (0x%02x:0x%02x)->0x%02x", \
+ msg, args->val[0x0], args->val[0xF], code)
+
+/* 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 */
+
+/* Locking: */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+static DECLARE_MUTEX(thinkpad_ec_mutex);
+#else
+static DEFINE_SEMAPHORE(thinkpad_ec_mutex);
+#endif
+
+/* Kludge in case the ACPI DSDT reserves the ports we need. */
+static bool force_io; /* Willing to do IO to ports we couldn't reserve? */
+static int reserved_io; /* Successfully reserved the ports? */
+module_param_named(force_io, force_io, bool, 0600);
+MODULE_PARM_DESC(force_io, "Force IO even if region already reserved (0=off, 1=on)");
+
+/**
+ * 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 ret;
+ ret = down_interruptible(&thinkpad_ec_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(thinkpad_ec_lock);
+
+/**
+ * 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)
+{
+ return down_trylock(&thinkpad_ec_mutex);
+}
+EXPORT_SYMBOL_GPL(thinkpad_ec_try_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)
+{
+ up(&thinkpad_ec_mutex);
+}
+EXPORT_SYMBOL_GPL(thinkpad_ec_unlock);
+
+/**
+ * thinkpad_ec_request_row - tell embedded controller to prepare a row
+ * @args Input register arguments
+ *
+ * Requests a data row by writing to H8S LPC registers TRW0 through TWR15 (or
+ * a subset thereof) following the protocol prescribed by the "H8S/2104B Group
+ * Hardware Manual". Does sanity checks via status register STR3.
+ */
+static int thinkpad_ec_request_row(const struct thinkpad_ec_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;
+ }
+
+ /* 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 REQ_FMT(
+ "EC has result from unrequested transaction",
+ 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 REQ_FMT(
+ "EC is busy with unrequested transaction",
+ str3));
+ return -EBUSY; /* data will be pending in a few usecs */
+ } else if (str3 != 0x00) { /* unexpected status? */
+ printk(KERN_WARNING REQ_FMT("unexpected initial STR3", str3));
+ return -EIO;
+ }
+
+ /* 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 REQ_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 (default to 0x01). This marks end of command. */
+ outb((args->mask & 0x8000) ? args->val[0xF] : 0x01, 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)))
+ /* Normal progress (the EC hasn't seen the request
+ * yet, or is processing it). Wait it out. */
+ ndelay(TPC_REQUEST_NDELAY);
+ else { /* weird EC status */
+ printk(KERN_WARNING
+ REQ_FMT("bad end STR3", str3));
+ return -EIO;
+ }
+ }
+ printk(KERN_WARNING REQ_FMT("EC is mysteriously silent", str3));
+ return -EIO;
+}
+
+/**
+ * thinkpad_ec_read_data - read pre-requested row-data from EC
+ * @args Input register arguments of pre-requested rows
+ * @data Output register values
+ *
+ * Reads current row data from the controller, assuming it's already
+ * requested. Follows the H8S spec for register access and status checks.
+ */
+static int thinkpad_ec_read_data(const struct thinkpad_ec_row *args,
+ struct thinkpad_ec_row *data)
+{
+ int i;
+ u8 str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
+ /* Once we make a request, STR3 assumes the sequence of values listed
+ * in the following 'if' as it reads the request and writes its data.
+ * It takes about a few dozen nanosecs total, with very high variance.
+ */
+ if (str3 == (H8S_STR3_IBF3B|H8S_STR3_MWMF) ||
+ str3 == 0x00 || /* the 0x00 is indistinguishable from idle EC! */
+ str3 == H8S_STR3_SWMF)
+ return -EBUSY; /* not ready yet */
+ /* Finally, the EC signals output buffer full: */
+ if (str3 != (H8S_STR3_OBF3B|H8S_STR3_SWMF)) {
+ printk(KERN_WARNING
+ REQ_FMT("bad initial STR3", 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++)
+ 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
+ REQ_FMT("OBF3B=1 after read", str3));
+ /* If port 0x161F returns 0x80 too often, the EC may lock up. Warn: */
+ if (data->val[0xF] == 0x80)
+ printk(KERN_WARNING
+ REQ_FMT("0x161F reports error", data->val[0xF]));
+ return 0;
+}
+
+/**
+ * thinkpad_ec_is_row_fetched - is the given row currently prefetched?
+ *
+ * To keep things simple we compare only the first and last args;
+ * this suffices for all known cases.
+ */
+static int thinkpad_ec_is_row_fetched(const struct thinkpad_ec_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);
+}
+
+/**
+ * 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 is specified by an
+ * array of 16 bytes, some of which may be undefined (but the first is
+ * mandatory). These bytes are given in @args->val[], where @args->val[i] is
+ * used iff (@args->mask>>i)&1). The resulting row 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&(1<<i)==0 then
+ * @data->val[i] is undefined.
+ *
+ * Returns -EBUSY on transient error and -EIO on abnormal condition.
+ * Caller must hold controller lock.
+ */
+int thinkpad_ec_read_row(const struct thinkpad_ec_row *args,
+ struct thinkpad_ec_row *data)
+{
+ int retries, ret;
+
+ if (thinkpad_ec_is_row_fetched(args))
+ goto read_row; /* already requested */
+
+ /* Request the row */
+ for (retries = 0; retries < TPC_READ_RETRIES; ++retries) {
+ ret = thinkpad_ec_request_row(args);
+ if (!ret)
+ goto read_row;
+ if (ret != -EBUSY)
+ break;
+ ndelay(TPC_READ_NDELAY);
+ }
+ printk(KERN_ERR REQ_FMT("failed requesting row", ret));
+ goto out;
+
+read_row:
+ /* Read the row's data */
+ for (retries = 0; retries < TPC_READ_RETRIES; ++retries) {
+ ret = thinkpad_ec_read_data(args, data);
+ if (!ret)
+ goto out;
+ if (ret != -EBUSY)
+ break;
+ ndelay(TPC_READ_NDELAY);
+ }
+
+ printk(KERN_ERR REQ_FMT("failed waiting for data", ret));
+
+out:
+ prefetch_jiffies = TPC_PREFETCH_JUNK;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(thinkpad_ec_read_row);
+
+/**
+ * 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.
+ */
+int thinkpad_ec_try_read_row(const struct thinkpad_ec_row *args,
+ struct thinkpad_ec_row *data)
+{
+ int ret;
+ if (!thinkpad_ec_is_row_fetched(args)) {
+ ret = -ENODATA;
+ } else {
+ ret = thinkpad_ec_read_data(args, data);
+ if (!ret)
+ prefetch_jiffies = TPC_PREFETCH_NONE; /* eaten up */
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(thinkpad_ec_try_read_row);
+
+/**
+ * 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.
+ */
+int thinkpad_ec_prefetch_row(const struct thinkpad_ec_row *args)
+{
+ int ret;
+ ret = thinkpad_ec_request_row(args);
+ if (ret) {
+ prefetch_jiffies = TPC_PREFETCH_JUNK;
+ } else {
+ prefetch_jiffies = get_jiffies_64();
+ prefetch_arg0 = args->val[0x0];
+ prefetch_argF = args->val[0xF];
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(thinkpad_ec_prefetch_row);
+
+/**
+ * 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)
+{
+ prefetch_jiffies = TPC_PREFETCH_JUNK;
+}
+EXPORT_SYMBOL_GPL(thinkpad_ec_invalidate);
+
+
+/*** Checking for EC hardware ***/
+
+/**
+ * thinkpad_ec_test - verify the EC is present and follows protocol
+ *
+ * Ensure the EC LPC3 channel really works on this machine by making
+ * an EC request and seeing if the EC follows the documented H8S protocol.
+ * The requested row just reads battery status, so it should be harmless to
+ * access it (on a correct EC).
+ * This test writes to IO ports, so execute only after checking DMI.
+ */
+static int __init 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} };
+ struct thinkpad_ec_row data = { .mask = 0x0000 };
+ ret = thinkpad_ec_lock();
+ if (ret)
+ return ret;
+ ret = thinkpad_ec_read_row(&args, &data);
+ thinkpad_ec_unlock();
+ return ret;
+}
+
+/* Search all DMI device names of a given type for a substring */
+static int __init dmi_find_substring(int type, const char *substr)
+{
+ const struct dmi_device *dev = NULL;
+ while ((dev = dmi_find_device(type, NULL, dev))) {
+ if (strstr(dev->name, substr))
+ return 1;
+ }
+ return 0;
+}
+
+#define TP_DMI_MATCH(vendor,model) { \
+ .ident = vendor " " model, \
+ .matches = { \
+ DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
+ DMI_MATCH(DMI_PRODUCT_VERSION, model) \
+ } \
+}
+
+/* Check DMI for existence of ThinkPad embedded controller */
+static int __init check_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[] = {
+ TP_DMI_MATCH("IBM", "ThinkPad A30"),
+ TP_DMI_MATCH("IBM", "ThinkPad T23"),
+ TP_DMI_MATCH("IBM", "ThinkPad X24"),
+ TP_DMI_MATCH("LENOVO", "ThinkPad"),
+ { .ident = NULL }
+ };
+ return dmi_find_substring(DMI_DEV_TYPE_OEM_STRING,
+ "IBM ThinkPad Embedded Controller") ||
+ dmi_check_system(tp_whitelist);
+}
+
+/*** Init and cleanup ***/
+
+static int __init thinkpad_ec_init(void)
+{
+ if (!check_dmi_for_ec()) {
+ printk(KERN_WARNING
+ "thinkpad_ec: no ThinkPad embedded controller!\n");
+ return -ENODEV;
+ }
+
+ if (request_region(TPC_BASE_PORT, TPC_NUM_PORTS, "thinkpad_ec")) {
+ reserved_io = 1;
+ } else {
+ printk(KERN_ERR "thinkpad_ec: cannot claim IO ports %#x-%#x... ",
+ TPC_BASE_PORT,
+ TPC_BASE_PORT + TPC_NUM_PORTS - 1);
+ if (force_io) {
+ printk("forcing use of unreserved IO ports.\n");
+ } else {
+ printk("consider using force_io=1.\n");
+ return -ENXIO;
+ }
+ }
+ prefetch_jiffies = TPC_PREFETCH_JUNK;
+ if (thinkpad_ec_test()) {
+ printk(KERN_ERR "thinkpad_ec: initial ec test failed\n");
+ if (reserved_io)
+ release_region(TPC_BASE_PORT, TPC_NUM_PORTS);
+ return -ENXIO;
+ }
+ printk(KERN_INFO "thinkpad_ec: thinkpad_ec " TP_VERSION " loaded.\n");
+ return 0;
+}
+
+static void __exit thinkpad_ec_exit(void)
+{
+ if (reserved_io)
+ release_region(TPC_BASE_PORT, TPC_NUM_PORTS);
+ printk(KERN_INFO "thinkpad_ec: unloaded.\n");
+}
+
+module_init(thinkpad_ec_init);
+module_exit(thinkpad_ec_exit);
diff --git a/drivers/platform/x86/tp_smapi.c b/drivers/platform/x86/tp_smapi.c
new file mode 100644
index 000000000000..209cb6487e24
--- /dev/null
+++ b/drivers/platform/x86/tp_smapi.c
@@ -0,0 +1,1493 @@
+/*
+ * tp_smapi.c - ThinkPad SMAPI support
+ *
+ * This driver exposes some features of the System Management Application
+ * Program Interface (SMAPI) BIOS found on ThinkPad laptops. It works on
+ * models in which the SMAPI BIOS runs in SMM and is invoked by writing
+ * to the APM control port 0xB2.
+ * It also exposes battery status information, obtained from the ThinkPad
+ * embedded controller (via the thinkpad_ec module).
+ * Ancient ThinkPad models use a different interface, supported by the
+ * "thinkpad" module from "tpctl".
+ *
+ * Many of the battery status values obtained from the EC simply mirror
+ * values provided by the battery's Smart Battery System (SBS) interface, so
+ * their meaning is defined by the Smart Battery Data Specification (see
+ * http://sbs-forum.org/specs/sbdat110.pdf). References to this SBS spec
+ * are given in the code where relevant.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/mc146818rtc.h> /* CMOS defines */
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/thinkpad_ec.h>
+#include <linux/platform_device.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#define TP_VERSION "0.42"
+#define TP_DESC "ThinkPad SMAPI Support"
+#define TP_DIR "smapi"
+
+MODULE_AUTHOR("Shem Multinymous");
+MODULE_DESCRIPTION(TP_DESC);
+MODULE_VERSION(TP_VERSION);
+MODULE_LICENSE("GPL");
+
+static struct platform_device *pdev;
+
+static int tp_debug;
+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 TPRINTK(level, fmt, args...) \
+ dev_printk(level, &(pdev->dev), "%s: " fmt "\n", __func__, ## args)
+#define DPRINTK(fmt, args...) \
+ do { if (tp_debug) TPRINTK(KERN_DEBUG, fmt, ## args); } while (0)
+
+/*********************************************************************
+ * SMAPI interface
+ */
+
+/* SMAPI functions (register BX when making the SMM call). */
+#define SMAPI_GET_INHIBIT_CHARGE 0x2114
+#define SMAPI_SET_INHIBIT_CHARGE 0x2115
+#define SMAPI_GET_THRESH_START 0x2116
+#define SMAPI_SET_THRESH_START 0x2117
+#define SMAPI_GET_FORCE_DISCHARGE 0x2118
+#define SMAPI_SET_FORCE_DISCHARGE 0x2119
+#define SMAPI_GET_THRESH_STOP 0x211a
+#define SMAPI_SET_THRESH_STOP 0x211b
+
+/* SMAPI error codes (see ThinkPad 770 Technical Reference Manual p.83 at
+ http://www-307.ibm.com/pc/support/site.wss/document.do?lndocid=PFAN-3TUQQD */
+#define SMAPI_RETCODE_EOF 0xff
+static struct { u8 rc; char *msg; int ret; } smapi_retcode[] =
+{
+ {0x00, "OK", 0},
+ {0x53, "SMAPI function is not available", -ENXIO},
+ {0x81, "Invalid parameter", -EINVAL},
+ {0x86, "Function is not supported by SMAPI BIOS", -EOPNOTSUPP},
+ {0x90, "System error", -EIO},
+ {0x91, "System is invalid", -EIO},
+ {0x92, "System is busy, -EBUSY"},
+ {0xa0, "Device error (disk read error)", -EIO},
+ {0xa1, "Device is busy", -EBUSY},
+ {0xa2, "Device is not attached", -ENXIO},
+ {0xa3, "Device is disbled", -EIO},
+ {0xa4, "Request parameter is out of range", -EINVAL},
+ {0xa5, "Request parameter is not accepted", -EINVAL},
+ {0xa6, "Transient error", -EBUSY}, /* ? */
+ {SMAPI_RETCODE_EOF, "Unknown error code", -EIO}
+};
+
+
+#define SMAPI_MAX_RETRIES 10
+#define SMAPI_PORT2 0x4F /* fixed port, meaning unclear */
+static unsigned short smapi_port; /* APM control port, normally 0xB2 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+static DECLARE_MUTEX(smapi_mutex);
+#else
+static DEFINE_SEMAPHORE(smapi_mutex);
+#endif
+
+/**
+ * find_smapi_port - read SMAPI port from NVRAM
+ */
+static int __init find_smapi_port(void)
+{
+ u16 smapi_id = 0;
+ unsigned short port = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ smapi_id = CMOS_READ(0x7C);
+ smapi_id |= (CMOS_READ(0x7D) << 8);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+
+ if (smapi_id != 0x5349) {
+ printk(KERN_ERR "SMAPI not supported (ID=0x%x)\n", smapi_id);
+ return -ENXIO;
+ }
+ spin_lock_irqsave(&rtc_lock, flags);
+ port = CMOS_READ(0x7E);
+ port |= (CMOS_READ(0x7F) << 8);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ if (port == 0) {
+ printk(KERN_ERR "unable to read SMAPI port number\n");
+ return -ENXIO;
+ }
+ return port;
+}
+
+/**
+ * smapi_request - make a SMAPI call
+ * @inEBX, @inECX, @inEDI, @inESI: input registers
+ * @outEBX, @outECX, @outEDX, @outEDI, @outESI: outputs registers
+ * @msg: textual error message
+ * Invokes the SMAPI SMBIOS with the given input and outpu args.
+ * All outputs are optional (can be %NULL).
+ * Returns 0 when successful, and a negative errno constant
+ * (see smapi_retcode above) upon failure.
+ */
+static int smapi_request(u32 inEBX, u32 inECX,
+ u32 inEDI, u32 inESI,
+ u32 *outEBX, u32 *outECX, u32 *outEDX,
+ u32 *outEDI, u32 *outESI, const char **msg)
+{
+ int ret = 0;
+ int i;
+ int retries;
+ u8 rc;
+ /* Must use local vars for output regs, due to reg pressure. */
+ u32 tmpEAX, tmpEBX, tmpECX, tmpEDX, tmpEDI, tmpESI;
+
+ for (retries = 0; retries < SMAPI_MAX_RETRIES; ++retries) {
+ DPRINTK("req_in: BX=%x CX=%x DI=%x SI=%x",
+ inEBX, inECX, inEDI, inESI);
+
+ /* SMAPI's SMBIOS call and thinkpad_ec end up using use
+ * different interfaces to the same chip, so play it safe. */
+ ret = thinkpad_ec_lock();
+ if (ret)
+ return ret;
+
+ __asm__ __volatile__(
+ "movl $0x00005380,%%eax\n\t"
+ "movl %6,%%ebx\n\t"
+ "movl %7,%%ecx\n\t"
+ "movl %8,%%edi\n\t"
+ "movl %9,%%esi\n\t"
+ "xorl %%edx,%%edx\n\t"
+ "movw %10,%%dx\n\t"
+ "out %%al,%%dx\n\t" /* trigger SMI to SMBIOS */
+ "out %%al,$0x4F\n\t"
+ "movl %%eax,%0\n\t"
+ "movl %%ebx,%1\n\t"
+ "movl %%ecx,%2\n\t"
+ "movl %%edx,%3\n\t"
+ "movl %%edi,%4\n\t"
+ "movl %%esi,%5\n\t"
+ :"=m"(tmpEAX),
+ "=m"(tmpEBX),
+ "=m"(tmpECX),
+ "=m"(tmpEDX),
+ "=m"(tmpEDI),
+ "=m"(tmpESI)
+ :"m"(inEBX), "m"(inECX), "m"(inEDI), "m"(inESI),
+ "m"((u16)smapi_port)
+ :"%eax", "%ebx", "%ecx", "%edx", "%edi",
+ "%esi");
+
+ thinkpad_ec_invalidate();
+ thinkpad_ec_unlock();
+
+ /* Don't let the next SMAPI access happen too quickly,
+ * may case problems. (We're hold smapi_mutex). */
+ msleep(50);
+
+ if (outEBX) *outEBX = tmpEBX;
+ if (outECX) *outECX = tmpECX;
+ if (outEDX) *outEDX = tmpEDX;
+ if (outESI) *outESI = tmpESI;
+ if (outEDI) *outEDI = tmpEDI;
+
+ /* Look up error code */
+ rc = (tmpEAX>>8)&0xFF;
+ for (i = 0; smapi_retcode[i].rc != SMAPI_RETCODE_EOF &&
+ smapi_retcode[i].rc != rc; ++i) {}
+ ret = smapi_retcode[i].ret;
+ if (msg)
+ *msg = smapi_retcode[i].msg;
+
+ DPRINTK("req_out: AX=%x BX=%x CX=%x DX=%x DI=%x SI=%x r=%d",
+ tmpEAX, tmpEBX, tmpECX, tmpEDX, tmpEDI, tmpESI, ret);
+ if (ret)
+ TPRINTK(KERN_NOTICE, "SMAPI error: %s (func=%x)",
+ smapi_retcode[i].msg, inEBX);
+
+ if (ret != -EBUSY)
+ return ret;
+ }
+ return ret;
+}
+
+/* Convenience wrapper: discard output arguments */
+static int smapi_write(u32 inEBX, u32 inECX,
+ u32 inEDI, u32 inESI, const char **msg)
+{
+ return smapi_request(inEBX, inECX, inEDI, inESI,
+ NULL, NULL, NULL, NULL, NULL, msg);
+}
+
+
+/*********************************************************************
+ * Specific SMAPI services
+ * All of these functions return 0 upon success, and a negative errno
+ * constant (see smapi_retcode) on failure.
+ */
+
+enum thresh_type {
+ THRESH_STOP = 0, /* the code assumes this is 0 for brevity */
+ THRESH_START
+};
+#define THRESH_NAME(which) ((which == THRESH_START) ? "start" : "stop")
+
+/**
+ * __get_real_thresh - read battery charge start/stop threshold from SMAPI
+ * @bat: battery number (0 or 1)
+ * @which: THRESH_START or THRESH_STOP
+ * @thresh: 1..99, 0=default 1..99, 0=default (pass this as-is to SMAPI)
+ * @outEDI: some additional state that needs to be preserved, meaning unknown
+ * @outESI: some additional state that needs to be preserved, meaning unknown
+ */
+static int __get_real_thresh(int bat, enum thresh_type which, int *thresh,
+ u32 *outEDI, u32 *outESI)
+{
+ u32 ebx = (which == THRESH_START) ? SMAPI_GET_THRESH_START
+ : SMAPI_GET_THRESH_STOP;
+ u32 ecx = (bat+1)<<8;
+ const char *msg;
+ int ret = smapi_request(ebx, ecx, 0, 0, NULL,
+ &ecx, NULL, outEDI, outESI, &msg);
+ if (ret) {
+ TPRINTK(KERN_NOTICE, "cannot get %s_thresh of bat=%d: %s",
+ THRESH_NAME(which), bat, msg);
+ return ret;
+ }
+ if (!(ecx&0x00000100)) {
+ TPRINTK(KERN_NOTICE, "cannot get %s_thresh of bat=%d: ecx=0%x",
+ THRESH_NAME(which), bat, ecx);
+ return -EIO;
+ }
+ if (thresh)
+ *thresh = ecx&0xFF;
+ return 0;
+}
+
+/**
+ * get_real_thresh - read battery charge start/stop threshold from SMAPI
+ * @bat: battery number (0 or 1)
+ * @which: THRESH_START or THRESH_STOP
+ * @thresh: 1..99, 0=default (passes as-is to SMAPI)
+ */
+static int get_real_thresh(int bat, enum thresh_type which, int *thresh)
+{
+ return __get_real_thresh(bat, which, thresh, NULL, NULL);
+}
+
+/**
+ * set_real_thresh - write battery start/top charge threshold to SMAPI
+ * @bat: battery number (0 or 1)
+ * @which: THRESH_START or THRESH_STOP
+ * @thresh: 1..99, 0=default (passes as-is to SMAPI)
+ */
+static int set_real_thresh(int bat, enum thresh_type which, int thresh)
+{
+ u32 ebx = (which == THRESH_START) ? SMAPI_SET_THRESH_START
+ : SMAPI_SET_THRESH_STOP;
+ u32 ecx = ((bat+1)<<8) + thresh;
+ u32 getDI, getSI;
+ const char *msg;
+ int ret;
+
+ /* verify read before writing */
+ ret = __get_real_thresh(bat, which, NULL, &getDI, &getSI);
+ if (ret)
+ return ret;
+
+ ret = smapi_write(ebx, ecx, getDI, getSI, &msg);
+ if (ret)
+ TPRINTK(KERN_NOTICE, "set %s to %d for bat=%d failed: %s",
+ THRESH_NAME(which), thresh, bat, msg);
+ else
+ TPRINTK(KERN_INFO, "set %s to %d for bat=%d",
+ THRESH_NAME(which), thresh, bat);
+ return ret;
+}
+
+/**
+ * __get_inhibit_charge_minutes - get inhibit charge period from SMAPI
+ * @bat: battery number (0 or 1)
+ * @minutes: period in minutes (1..65535 minutes, 0=disabled)
+ * @outECX: some additional state that needs to be preserved, meaning unknown
+ * Note that @minutes is the originally set value, it does not count down.
+ */
+static int __get_inhibit_charge_minutes(int bat, int *minutes, u32 *outECX)
+{
+ u32 ecx = (bat+1)<<8;
+ u32 esi;
+ const char *msg;
+ int ret = smapi_request(SMAPI_GET_INHIBIT_CHARGE, ecx, 0, 0,
+ NULL, &ecx, NULL, NULL, &esi, &msg);
+ if (ret) {
+ TPRINTK(KERN_NOTICE, "failed for bat=%d: %s", bat, msg);
+ return ret;
+ }
+ if (!(ecx&0x0100)) {
+ TPRINTK(KERN_NOTICE, "bad ecx=0x%x for bat=%d", ecx, bat);
+ return -EIO;
+ }
+ if (minutes)
+ *minutes = (ecx&0x0001)?esi:0;
+ if (outECX)
+ *outECX = ecx;
+ return 0;
+}
+
+/**
+ * get_inhibit_charge_minutes - get inhibit charge period from SMAPI
+ * @bat: battery number (0 or 1)
+ * @minutes: period in minutes (1..65535 minutes, 0=disabled)
+ * Note that @minutes is the originally set value, it does not count down.
+ */
+static int get_inhibit_charge_minutes(int bat, int *minutes)
+{
+ return __get_inhibit_charge_minutes(bat, minutes, NULL);
+}
+
+/**
+ * set_inhibit_charge_minutes - write inhibit charge period to SMAPI
+ * @bat: battery number (0 or 1)
+ * @minutes: period in minutes (1..65535 minutes, 0=disabled)
+ */
+static int set_inhibit_charge_minutes(int bat, int minutes)
+{
+ u32 ecx;
+ const char *msg;
+ int ret;
+
+ /* verify read before writing */
+ ret = __get_inhibit_charge_minutes(bat, NULL, &ecx);
+ if (ret)
+ return ret;
+
+ ecx = ((bat+1)<<8) | (ecx&0x00FE) | (minutes > 0 ? 0x0001 : 0x0000);
+ if (minutes > 0xFFFF)
+ minutes = 0xFFFF;
+ ret = smapi_write(SMAPI_SET_INHIBIT_CHARGE, ecx, 0, minutes, &msg);
+ if (ret)
+ TPRINTK(KERN_NOTICE,
+ "set to %d failed for bat=%d: %s", minutes, bat, msg);
+ else
+ TPRINTK(KERN_INFO, "set to %d for bat=%d\n", minutes, bat);
+ return ret;
+}
+
+
+/**
+ * get_force_discharge - get status of forced discharging from SMAPI
+ * @bat: battery number (0 or 1)
+ * @enabled: 1 if forced discharged is enabled, 0 if not
+ */
+static int get_force_discharge(int bat, int *enabled)
+{
+ u32 ecx = (bat+1)<<8;
+ const char *msg;
+ int ret = smapi_request(SMAPI_GET_FORCE_DISCHARGE, ecx, 0, 0,
+ NULL, &ecx, NULL, NULL, NULL, &msg);
+ if (ret) {
+ TPRINTK(KERN_NOTICE, "failed for bat=%d: %s", bat, msg);
+ return ret;
+ }
+ *enabled = (!(ecx&0x00000100) && (ecx&0x00000001))?1:0;
+ return 0;
+}
+
+/**
+ * set_force_discharge - write status of forced discharging to SMAPI
+ * @bat: battery number (0 or 1)
+ * @enabled: 1 if forced discharged is enabled, 0 if not
+ */
+static int set_force_discharge(int bat, int enabled)
+{
+ u32 ecx = (bat+1)<<8;
+ const char *msg;
+ int ret = smapi_request(SMAPI_GET_FORCE_DISCHARGE, ecx, 0, 0,
+ NULL, &ecx, NULL, NULL, NULL, &msg);
+ if (ret) {
+ TPRINTK(KERN_NOTICE, "get failed for bat=%d: %s", bat, msg);
+ return ret;
+ }
+ if (ecx&0x00000100) {
+ TPRINTK(KERN_NOTICE, "cannot force discharge bat=%d", bat);
+ return -EIO;
+ }
+
+ ecx = ((bat+1)<<8) | (ecx&0x000000FA) | (enabled?0x00000001:0);
+ ret = smapi_write(SMAPI_SET_FORCE_DISCHARGE, ecx, 0, 0, &msg);
+ if (ret)
+ TPRINTK(KERN_NOTICE, "set to %d failed for bat=%d: %s",
+ enabled, bat, msg);
+ else
+ TPRINTK(KERN_INFO, "set to %d for bat=%d", enabled, bat);
+ return ret;
+}
+
+
+/*********************************************************************
+ * Wrappers to threshold-related SMAPI functions, which handle default
+ * thresholds and related quirks.
+ */
+
+/* Minimum, default and minimum difference for battery charging thresholds: */
+#define MIN_THRESH_DELTA 4 /* Min delta between start and stop thresh */
+#define MIN_THRESH_START 2
+#define MAX_THRESH_START (100-MIN_THRESH_DELTA)
+#define MIN_THRESH_STOP (MIN_THRESH_START + MIN_THRESH_DELTA)
+#define MAX_THRESH_STOP 100
+#define DEFAULT_THRESH_START MAX_THRESH_START
+#define DEFAULT_THRESH_STOP MAX_THRESH_STOP
+
+/* The GUI of IBM's Battery Maximizer seems to show a start threshold that
+ * is 1 more than the value we set/get via SMAPI. Since the threshold is
+ * maintained across reboot, this can be confusing. So we kludge our
+ * interface for interoperability: */
+#define BATMAX_FIX 1
+
+/* Get charge start/stop threshold (1..100),
+ * substituting default values if needed and applying BATMAT_FIX. */
+static int get_thresh(int bat, enum thresh_type which, int *thresh)
+{
+ int ret = get_real_thresh(bat, which, thresh);
+ if (ret)
+ return ret;
+ if (*thresh == 0)
+ *thresh = (which == THRESH_START) ? DEFAULT_THRESH_START
+ : DEFAULT_THRESH_STOP;
+ else if (which == THRESH_START)
+ *thresh += BATMAX_FIX;
+ return 0;
+}
+
+
+/* Set charge start/stop threshold (1..100),
+ * substituting default values if needed and applying BATMAT_FIX. */
+static int set_thresh(int bat, enum thresh_type which, int thresh)
+{
+ if (which == THRESH_STOP && thresh == DEFAULT_THRESH_STOP)
+ thresh = 0; /* 100 is out of range, but default means 100 */
+ if (which == THRESH_START)
+ thresh -= BATMAX_FIX;
+ return set_real_thresh(bat, which, thresh);
+}
+
+/*********************************************************************
+ * ThinkPad embedded controller readout and basic functions
+ */
+
+/**
+ * read_tp_ec_row - read data row from the ThinkPad embedded controller
+ * @arg0: EC command code
+ * @bat: battery number, 0 or 1
+ * @j: the byte value to be used for "junk" (unused) input/outputs
+ * @dataval: result vector
+ */
+static int read_tp_ec_row(u8 arg0, int bat, u8 j, u8 *dataval)
+{
+ int ret;
+ const struct thinkpad_ec_row args = { .mask = 0xFFFF,
+ .val = {arg0, j,j,j,j,j,j,j,j,j,j,j,j,j,j, (u8)bat} };
+ struct thinkpad_ec_row data = { .mask = 0xFFFF };
+
+ ret = thinkpad_ec_lock();
+ if (ret)
+ return ret;
+ ret = thinkpad_ec_read_row(&args, &data);
+ thinkpad_ec_unlock();
+ memcpy(dataval, &data.val, TP_CONTROLLER_ROW_LEN);
+ return ret;
+}
+
+/**
+ * power_device_present - check for presence of battery or AC power
+ * @bat: 0 for battery 0, 1 for battery 1, otherwise AC power
+ * Returns 1 if present, 0 if not present, negative if error.
+ */
+static int power_device_present(int bat)
+{
+ u8 row[TP_CONTROLLER_ROW_LEN];
+ u8 test;
+ int ret = read_tp_ec_row(1, bat, 0, row);
+ if (ret)
+ return ret;
+ switch (bat) {
+ case 0: test = 0x40; break; /* battery 0 */
+ case 1: test = 0x20; break; /* battery 1 */
+ default: test = 0x80; /* AC power */
+ }
+ return (row[0] & test) ? 1 : 0;
+}
+
+/**
+ * bat_has_status - check if battery can report detailed status
+ * @bat: 0 for battery 0, 1 for battery 1
+ * Returns 1 if yes, 0 if no, negative if error.
+ */
+static int bat_has_status(int bat)
+{
+ u8 row[TP_CONTROLLER_ROW_LEN];
+ int ret = read_tp_ec_row(1, bat, 0, row);
+ if (ret)
+ return ret;
+ if ((row[0] & (bat?0x20:0x40)) == 0) /* no battery */
+ return 0;
+ if ((row[1] & (0x60)) == 0) /* no status */
+ return 0;
+ return 1;
+}
+
+/**
+ * get_tp_ec_bat_16 - read a 16-bit value from EC battery status data
+ * @arg0: first argument to EC
+ * @off: offset in row returned from EC
+ * @bat: battery (0 or 1)
+ * @val: the 16-bit value obtained
+ * Returns nonzero on error.
+ */
+static int get_tp_ec_bat_16(u8 arg0, int offset, int bat, u16 *val)
+{
+ u8 row[TP_CONTROLLER_ROW_LEN];
+ int ret;
+ if (bat_has_status(bat) != 1)
+ return -ENXIO;
+ ret = read_tp_ec_row(arg0, bat, 0, row);
+ if (ret)
+ return ret;
+ *val = *(u16 *)(row+offset);
+ return 0;
+}
+
+/*********************************************************************
+ * sysfs attributes for batteries -
+ * definitions and helper functions
+ */
+
+/* A custom device attribute struct which holds a battery number */
+struct bat_device_attribute {
+ struct device_attribute dev_attr;
+ int bat;
+};
+
+/**
+ * attr_get_bat - get the battery to which the attribute belongs
+ */
+static int attr_get_bat(struct device_attribute *attr)
+{
+ return container_of(attr, struct bat_device_attribute, dev_attr)->bat;
+}
+
+/**
+ * show_tp_ec_bat_u16 - show an unsigned 16-bit battery attribute
+ * @arg0: specified 1st argument of EC raw to read
+ * @offset: byte offset in EC raw data
+ * @mul: correction factor to multiply by
+ * @na_msg: string to output is value not available (0xFFFFFFFF)
+ * @attr: battery attribute
+ * @buf: output buffer
+ * The 16-bit value is read from the EC, treated as unsigned,
+ * transformed as x->mul*x, and printed to the buffer.
+ * If the value is 0xFFFFFFFF and na_msg!=%NULL, na_msg is printed instead.
+ */
+static ssize_t show_tp_ec_bat_u16(u8 arg0, int offset, int mul,
+ const char *na_msg,
+ struct device_attribute *attr, char *buf)
+{
+ u16 val;
+ int ret = get_tp_ec_bat_16(arg0, offset, attr_get_bat(attr), &val);
+ if (ret)
+ return ret;
+ if (na_msg && val == 0xFFFF)
+ return sprintf(buf, "%s\n", na_msg);
+ else
+ return sprintf(buf, "%u\n", mul*(unsigned int)val);
+}
+
+/**
+ * show_tp_ec_bat_s16 - show an signed 16-bit battery attribute
+ * @arg0: specified 1st argument of EC raw to read
+ * @offset: byte offset in EC raw data
+ * @mul: correction factor to multiply by
+ * @add: correction term to add after multiplication
+ * @attr: battery attribute
+ * @buf: output buffer
+ * The 16-bit value is read from the EC, treated as signed,
+ * transformed as x->mul*x+add, and printed to the buffer.
+ */
+static ssize_t show_tp_ec_bat_s16(u8 arg0, int offset, int mul, int add,
+ struct device_attribute *attr, char *buf)
+{
+ u16 val;
+ int ret = get_tp_ec_bat_16(arg0, offset, attr_get_bat(attr), &val);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%d\n", mul*(s16)val+add);
+}
+
+/**
+ * show_tp_ec_bat_str - show a string from EC battery status data
+ * @arg0: specified 1st argument of EC raw to read
+ * @offset: byte offset in EC raw data
+ * @maxlen: maximum string length
+ * @attr: battery attribute
+ * @buf: output buffer
+ */
+static ssize_t show_tp_ec_bat_str(u8 arg0, int offset, int maxlen,
+ struct device_attribute *attr, char *buf)
+{
+ int bat = attr_get_bat(attr);
+ u8 row[TP_CONTROLLER_ROW_LEN];
+ int ret;
+ if (bat_has_status(bat) != 1)
+ return -ENXIO;
+ ret = read_tp_ec_row(arg0, bat, 0, row);
+ if (ret)
+ return ret;
+ strncpy(buf, (char *)row+offset, maxlen);
+ buf[maxlen] = 0;
+ strcat(buf, "\n");
+ return strlen(buf);
+}
+
+/**
+ * show_tp_ec_bat_power - show a power readout from EC battery status data
+ * @arg0: specified 1st argument of EC raw to read
+ * @offV: byte offset of voltage in EC raw data
+ * @offI: byte offset of current in EC raw data
+ * @attr: battery attribute
+ * @buf: output buffer
+ * Computes the power as current*voltage from the two given readout offsets.
+ */
+static ssize_t show_tp_ec_bat_power(u8 arg0, int offV, int offI,
+ struct device_attribute *attr, char *buf)
+{
+ u8 row[TP_CONTROLLER_ROW_LEN];
+ int milliamp, millivolt, ret;
+ int bat = attr_get_bat(attr);
+ if (bat_has_status(bat) != 1)
+ return -ENXIO;
+ ret = read_tp_ec_row(1, bat, 0, row);
+ if (ret)
+ return ret;
+ millivolt = *(u16 *)(row+offV);
+ milliamp = *(s16 *)(row+offI);
+ return sprintf(buf, "%d\n", milliamp*millivolt/1000); /* units: mW */
+}
+
+/**
+ * show_tp_ec_bat_date - decode and show a date from EC battery status data
+ * @arg0: specified 1st argument of EC raw to read
+ * @offset: byte offset in EC raw data
+ * @attr: battery attribute
+ * @buf: output buffer
+ */
+static ssize_t show_tp_ec_bat_date(u8 arg0, int offset,
+ struct device_attribute *attr, char *buf)
+{
+ u8 row[TP_CONTROLLER_ROW_LEN];
+ u16 v;
+ int ret;
+ int day, month, year;
+ int bat = attr_get_bat(attr);
+ if (bat_has_status(bat) != 1)
+ return -ENXIO;
+ ret = read_tp_ec_row(arg0, bat, 0, row);
+ if (ret)
+ return ret;
+
+ /* Decode bit-packed: v = day | (month<<5) | ((year-1980)<<9) */
+ v = *(u16 *)(row+offset);
+ day = v & 0x1F;
+ month = (v >> 5) & 0xF;
+ year = (v >> 9) + 1980;
+
+ return sprintf(buf, "%04d-%02d-%02d\n", year, month, day);
+}
+
+
+/*********************************************************************
+ * sysfs attribute I/O for batteries -
+ * the actual attribute show/store functions
+ */
+
+static ssize_t show_battery_start_charge_thresh(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int thresh;
+ int bat = attr_get_bat(attr);
+ int ret = get_thresh(bat, THRESH_START, &thresh);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%d\n", thresh); /* units: percent */
+}
+
+static ssize_t show_battery_stop_charge_thresh(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int thresh;
+ int bat = attr_get_bat(attr);
+ int ret = get_thresh(bat, THRESH_STOP, &thresh);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%d\n", thresh); /* units: percent */
+}
+
+/**
+ * store_battery_start_charge_thresh - store battery_start_charge_thresh attr
+ * Since this is a kernel<->user interface, we ensure a valid state for
+ * the hardware. We do this by clamping the requested threshold to the
+ * valid range and, if necessary, moving the other threshold so that
+ * it's MIN_THRESH_DELTA away from this one.
+ */
+static ssize_t store_battery_start_charge_thresh(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int thresh, other_thresh, ret;
+ int bat = attr_get_bat(attr);
+
+ if (sscanf(buf, "%d", &thresh) != 1 || thresh < 1 || thresh > 100)
+ return -EINVAL;
+
+ if (thresh < MIN_THRESH_START) /* clamp up to MIN_THRESH_START */
+ thresh = MIN_THRESH_START;
+ if (thresh > MAX_THRESH_START) /* clamp down to MAX_THRESH_START */
+ thresh = MAX_THRESH_START;
+
+ down(&smapi_mutex);
+ ret = get_thresh(bat, THRESH_STOP, &other_thresh);
+ if (ret != -EOPNOTSUPP && ret != -ENXIO) {
+ if (ret) /* other threshold is set? */
+ goto out;
+ ret = get_real_thresh(bat, THRESH_START, NULL);
+ if (ret) /* this threshold is set? */
+ goto out;
+ if (other_thresh < thresh+MIN_THRESH_DELTA) {
+ /* move other thresh to keep it above this one */
+ ret = set_thresh(bat, THRESH_STOP,
+ thresh+MIN_THRESH_DELTA);
+ if (ret)
+ goto out;
+ }
+ }
+ ret = set_thresh(bat, THRESH_START, thresh);
+out:
+ up(&smapi_mutex);
+ return count;
+
+}
+
+/**
+ * store_battery_stop_charge_thresh - store battery_stop_charge_thresh attr
+ * Since this is a kernel<->user interface, we ensure a valid state for
+ * the hardware. We do this by clamping the requested threshold to the
+ * valid range and, if necessary, moving the other threshold so that
+ * it's MIN_THRESH_DELTA away from this one.
+ */
+static ssize_t store_battery_stop_charge_thresh(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int thresh, other_thresh, ret;
+ int bat = attr_get_bat(attr);
+
+ if (sscanf(buf, "%d", &thresh) != 1 || thresh < 1 || thresh > 100)
+ return -EINVAL;
+
+ if (thresh < MIN_THRESH_STOP) /* clamp up to MIN_THRESH_STOP */
+ thresh = MIN_THRESH_STOP;
+
+ down(&smapi_mutex);
+ ret = get_thresh(bat, THRESH_START, &other_thresh);
+ if (ret != -EOPNOTSUPP && ret != -ENXIO) { /* other threshold exists? */
+ if (ret)
+ goto out;
+ /* this threshold exists? */
+ ret = get_real_thresh(bat, THRESH_STOP, NULL);
+ if (ret)
+ goto out;
+ if (other_thresh >= thresh-MIN_THRESH_DELTA) {
+ /* move other thresh to be below this one */
+ ret = set_thresh(bat, THRESH_START,
+ thresh-MIN_THRESH_DELTA);
+ if (ret)
+ goto out;
+ }
+ }
+ ret = set_thresh(bat, THRESH_STOP, thresh);
+out:
+ up(&smapi_mutex);
+ return count;
+}
+
+static ssize_t show_battery_inhibit_charge_minutes(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int minutes;
+ int bat = attr_get_bat(attr);
+ int ret = get_inhibit_charge_minutes(bat, &minutes);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%d\n", minutes); /* units: minutes */
+}
+
+static ssize_t store_battery_inhibit_charge_minutes(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ int minutes;
+ int bat = attr_get_bat(attr);
+ if (sscanf(buf, "%d", &minutes) != 1 || minutes < 0) {
+ TPRINTK(KERN_ERR, "inhibit_charge_minutes: "
+ "must be a non-negative integer");
+ return -EINVAL;
+ }
+ ret = set_inhibit_charge_minutes(bat, minutes);
+ if (ret)
+ return ret;
+ return count;
+}
+
+static ssize_t show_battery_force_discharge(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int enabled;
+ int bat = attr_get_bat(attr);
+ int ret = get_force_discharge(bat, &enabled);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%d\n", enabled); /* type: boolean */
+}
+
+static ssize_t store_battery_force_discharge(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ int enabled;
+ int bat = attr_get_bat(attr);
+ if (sscanf(buf, "%d", &enabled) != 1 || enabled < 0 || enabled > 1)
+ return -EINVAL;
+ ret = set_force_discharge(bat, enabled);
+ if (ret)
+ return ret;
+ return count;
+}
+
+static ssize_t show_battery_installed(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int bat = attr_get_bat(attr);
+ int ret = power_device_present(bat);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", ret); /* type: boolean */
+}
+
+static ssize_t show_battery_state(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ u8 row[TP_CONTROLLER_ROW_LEN];
+ const char *txt;
+ int ret;
+ int bat = attr_get_bat(attr);
+ if (bat_has_status(bat) != 1)
+ return sprintf(buf, "none\n");
+ ret = read_tp_ec_row(1, bat, 0, row);
+ if (ret)
+ return ret;
+ switch (row[1] & 0xf0) {
+ case 0xc0: txt = "idle"; break;
+ case 0xd0: txt = "discharging"; break;
+ case 0xe0: txt = "charging"; break;
+ default: return sprintf(buf, "unknown (0x%x)\n", row[1]);
+ }
+ return sprintf(buf, "%s\n", txt); /* type: string from fixed set */
+}
+
+static ssize_t show_battery_manufacturer(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* type: string. SBS spec v1.1 p34: ManufacturerName() */
+ return show_tp_ec_bat_str(4, 2, TP_CONTROLLER_ROW_LEN-2, attr, buf);
+}
+
+static ssize_t show_battery_model(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* type: string. SBS spec v1.1 p34: DeviceName() */
+ return show_tp_ec_bat_str(5, 2, TP_CONTROLLER_ROW_LEN-2, attr, buf);
+}
+
+static ssize_t show_battery_barcoding(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* type: string */
+ return show_tp_ec_bat_str(7, 2, TP_CONTROLLER_ROW_LEN-2, attr, buf);
+}
+
+static ssize_t show_battery_chemistry(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* type: string. SBS spec v1.1 p34-35: DeviceChemistry() */
+ return show_tp_ec_bat_str(6, 2, 5, attr, buf);
+}
+
+static ssize_t show_battery_voltage(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mV. SBS spec v1.1 p24: Voltage() */
+ return show_tp_ec_bat_u16(1, 6, 1, NULL, attr, buf);
+}
+
+static ssize_t show_battery_design_voltage(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mV. SBS spec v1.1 p32: DesignVoltage() */
+ return show_tp_ec_bat_u16(3, 4, 1, NULL, attr, buf);
+}
+
+static ssize_t show_battery_charging_max_voltage(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mV. SBS spec v1.1 p37,39: ChargingVoltage() */
+ return show_tp_ec_bat_u16(9, 8, 1, NULL, attr, buf);
+}
+
+static ssize_t show_battery_group0_voltage(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mV */
+ return show_tp_ec_bat_u16(0xA, 12, 1, NULL, attr, buf);
+}
+
+static ssize_t show_battery_group1_voltage(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mV */
+ return show_tp_ec_bat_u16(0xA, 10, 1, NULL, attr, buf);
+}
+
+static ssize_t show_battery_group2_voltage(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mV */
+ return show_tp_ec_bat_u16(0xA, 8, 1, NULL, attr, buf);
+}
+
+static ssize_t show_battery_group3_voltage(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mV */
+ return show_tp_ec_bat_u16(0xA, 6, 1, NULL, attr, buf);
+}
+
+static ssize_t show_battery_current_now(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mA. SBS spec v1.1 p24: Current() */
+ return show_tp_ec_bat_s16(1, 8, 1, 0, attr, buf);
+}
+
+static ssize_t show_battery_current_avg(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mA. SBS spec v1.1 p24: AverageCurrent() */
+ return show_tp_ec_bat_s16(1, 10, 1, 0, attr, buf);
+}
+
+static ssize_t show_battery_charging_max_current(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mA. SBS spec v1.1 p36,38: ChargingCurrent() */
+ return show_tp_ec_bat_s16(9, 6, 1, 0, attr, buf);
+}
+
+static ssize_t show_battery_power_now(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mW. SBS spec v1.1: Voltage()*Current() */
+ return show_tp_ec_bat_power(1, 6, 8, attr, buf);
+}
+
+static ssize_t show_battery_power_avg(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mW. SBS spec v1.1: Voltage()*AverageCurrent() */
+ return show_tp_ec_bat_power(1, 6, 10, attr, buf);
+}
+
+static ssize_t show_battery_remaining_percent(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: percent. SBS spec v1.1 p25: RelativeStateOfCharge() */
+ return show_tp_ec_bat_u16(1, 12, 1, NULL, attr, buf);
+}
+
+static ssize_t show_battery_remaining_percent_error(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: percent. SBS spec v1.1 p25: MaxError() */
+ return show_tp_ec_bat_u16(9, 4, 1, NULL, attr, buf);
+}
+
+static ssize_t show_battery_remaining_charging_time(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: minutes. SBS spec v1.1 p27: AverageTimeToFull() */
+ return show_tp_ec_bat_u16(2, 8, 1, "not_charging", attr, buf);
+}
+
+static ssize_t show_battery_remaining_running_time(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: minutes. SBS spec v1.1 p27: RunTimeToEmpty() */
+ return show_tp_ec_bat_u16(2, 6, 1, "not_discharging", attr, buf);
+}
+
+static ssize_t show_battery_remaining_running_time_now(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: minutes. SBS spec v1.1 p27: RunTimeToEmpty() */
+ return show_tp_ec_bat_u16(2, 4, 1, "not_discharging", attr, buf);
+}
+
+static ssize_t show_battery_remaining_capacity(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mWh. SBS spec v1.1 p26. */
+ return show_tp_ec_bat_u16(1, 14, 10, "", attr, buf);
+}
+
+static ssize_t show_battery_last_full_capacity(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mWh. SBS spec v1.1 p26: FullChargeCapacity() */
+ return show_tp_ec_bat_u16(2, 2, 10, "", attr, buf);
+}
+
+static ssize_t show_battery_design_capacity(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: mWh. SBS spec v1.1 p32: DesignCapacity() */
+ return show_tp_ec_bat_u16(3, 2, 10, "", attr, buf);
+}
+
+static ssize_t show_battery_cycle_count(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: ordinal. SBS spec v1.1 p32: CycleCount() */
+ return show_tp_ec_bat_u16(2, 12, 1, "", attr, buf);
+}
+
+static ssize_t show_battery_temperature(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* units: millicelsius. SBS spec v1.1: Temperature()*10 */
+ return show_tp_ec_bat_s16(1, 4, 100, -273100, attr, buf);
+}
+
+static ssize_t show_battery_serial(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* type: int. SBS spec v1.1 p34: SerialNumber() */
+ return show_tp_ec_bat_u16(3, 10, 1, "", attr, buf);
+}
+
+static ssize_t show_battery_manufacture_date(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* type: YYYY-MM-DD. SBS spec v1.1 p34: ManufactureDate() */
+ return show_tp_ec_bat_date(3, 8, attr, buf);
+}
+
+static ssize_t show_battery_first_use_date(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ /* type: YYYY-MM-DD */
+ return show_tp_ec_bat_date(8, 2, attr, buf);
+}
+
+/**
+ * show_battery_dump - show the battery's dump attribute
+ * The dump attribute gives a hex dump of all EC readouts related to a
+ * battery. Some of the enumerated values don't really exist (i.e., the
+ * EC function just leaves them untouched); we use a kludge to detect and
+ * denote these.
+ */
+#define MIN_DUMP_ARG0 0x00
+#define MAX_DUMP_ARG0 0x0a /* 0x0b is useful too but hangs old EC firmware */
+static ssize_t show_battery_dump(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int i;
+ char *p = buf;
+ int bat = attr_get_bat(attr);
+ u8 arg0; /* first argument to EC */
+ u8 rowa[TP_CONTROLLER_ROW_LEN],
+ rowb[TP_CONTROLLER_ROW_LEN];
+ const u8 junka = 0xAA,
+ junkb = 0x55; /* junk values for testing changes */
+ int ret;
+
+ for (arg0 = MIN_DUMP_ARG0; arg0 <= MAX_DUMP_ARG0; ++arg0) {
+ if ((p-buf) > PAGE_SIZE-TP_CONTROLLER_ROW_LEN*5)
+ return -ENOMEM; /* don't overflow sysfs buf */
+ /* Read raw twice with different junk values,
+ * to detect unused output bytes which are left unchaged: */
+ ret = read_tp_ec_row(arg0, bat, junka, rowa);
+ if (ret)
+ return ret;
+ ret = read_tp_ec_row(arg0, bat, junkb, rowb);
+ if (ret)
+ return ret;
+ for (i = 0; i < TP_CONTROLLER_ROW_LEN; i++) {
+ if (rowa[i] == junka && rowb[i] == junkb)
+ p += sprintf(p, "-- "); /* unused by EC */
+ else
+ p += sprintf(p, "%02x ", rowa[i]);
+ }
+ p += sprintf(p, "\n");
+ }
+ return p-buf;
+}
+
+
+/*********************************************************************
+ * sysfs attribute I/O, other than batteries
+ */
+
+static ssize_t show_ac_connected(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int ret = power_device_present(0xFF);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", ret); /* type: boolean */
+}
+
+/*********************************************************************
+ * The the "smapi_request" sysfs attribute executes a raw SMAPI call.
+ * You write to make a request and read to get the result. The state
+ * is saved globally rather than per fd (sysfs limitation), so
+ * simultaenous requests may get each other's results! So this is for
+ * development and debugging only.
+ */
+#define MAX_SMAPI_ATTR_ANSWER_LEN 128
+static char smapi_attr_answer[MAX_SMAPI_ATTR_ANSWER_LEN] = "";
+
+static ssize_t show_smapi_request(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret = snprintf(buf, PAGE_SIZE, "%s", smapi_attr_answer);
+ smapi_attr_answer[0] = '\0';
+ return ret;
+}
+
+static ssize_t store_smapi_request(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int inEBX, inECX, inEDI, inESI;
+ u32 outEBX, outECX, outEDX, outEDI, outESI;
+ const char *msg;
+ int ret;
+ if (sscanf(buf, "%x %x %x %x", &inEBX, &inECX, &inEDI, &inESI) != 4) {
+ smapi_attr_answer[0] = '\0';
+ return -EINVAL;
+ }
+ ret = smapi_request(
+ inEBX, inECX, inEDI, inESI,
+ &outEBX, &outECX, &outEDX, &outEDI, &outESI, &msg);
+ snprintf(smapi_attr_answer, MAX_SMAPI_ATTR_ANSWER_LEN,
+ "%x %x %x %x %x %d '%s'\n",
+ (unsigned int)outEBX, (unsigned int)outECX,
+ (unsigned int)outEDX, (unsigned int)outEDI,
+ (unsigned int)outESI, ret, msg);
+ if (ret)
+ return ret;
+ else
+ return count;
+}
+
+/*********************************************************************
+ * Power management: the embedded controller forgets the battery
+ * thresholds when the system is suspended to disk and unplugged from
+ * AC and battery, so we restore it upon resume.
+ */
+
+static int saved_threshs[4] = {-1, -1, -1, -1}; /* -1 = don't know */
+
+static int tp_suspend(struct platform_device *dev, pm_message_t state)
+{
+ int restore = (state.event == PM_EVENT_HIBERNATE ||
+ state.event == PM_EVENT_FREEZE);
+ if (!restore || get_real_thresh(0, THRESH_STOP , &saved_threshs[0]))
+ saved_threshs[0] = -1;
+ if (!restore || get_real_thresh(0, THRESH_START, &saved_threshs[1]))
+ saved_threshs[1] = -1;
+ if (!restore || get_real_thresh(1, THRESH_STOP , &saved_threshs[2]))
+ saved_threshs[2] = -1;
+ if (!restore || get_real_thresh(1, THRESH_START, &saved_threshs[3]))
+ saved_threshs[3] = -1;
+ DPRINTK("suspend saved: %d %d %d %d", saved_threshs[0],
+ saved_threshs[1], saved_threshs[2], saved_threshs[3]);
+ return 0;
+}
+
+static int tp_resume(struct platform_device *dev)
+{
+ DPRINTK("resume restoring: %d %d %d %d", saved_threshs[0],
+ saved_threshs[1], saved_threshs[2], saved_threshs[3]);
+ if (saved_threshs[0] >= 0)
+ set_real_thresh(0, THRESH_STOP , saved_threshs[0]);
+ if (saved_threshs[1] >= 0)
+ set_real_thresh(0, THRESH_START, saved_threshs[1]);
+ if (saved_threshs[2] >= 0)
+ set_real_thresh(1, THRESH_STOP , saved_threshs[2]);
+ if (saved_threshs[3] >= 0)
+ set_real_thresh(1, THRESH_START, saved_threshs[3]);
+ return 0;
+}
+
+
+/*********************************************************************
+ * Driver model
+ */
+
+static struct platform_driver tp_driver = {
+ .suspend = tp_suspend,
+ .resume = tp_resume,
+ .driver = {
+ .name = "smapi",
+ .owner = THIS_MODULE
+ },
+};
+
+
+/*********************************************************************
+ * Sysfs device model
+ */
+
+/* Attributes in /sys/devices/platform/smapi/ */
+
+static DEVICE_ATTR(ac_connected, 0444, show_ac_connected, NULL);
+static DEVICE_ATTR(smapi_request, 0600, show_smapi_request,
+ store_smapi_request);
+
+static struct attribute *tp_root_attributes[] = {
+ &dev_attr_ac_connected.attr,
+ &dev_attr_smapi_request.attr,
+ NULL
+};
+static struct attribute_group tp_root_attribute_group = {
+ .attrs = tp_root_attributes
+};
+
+/* Attributes under /sys/devices/platform/smapi/BAT{0,1}/ :
+ * Every attribute needs to be defined (i.e., statically allocated) for
+ * each battery, and then referenced in the attribute list of each battery.
+ * We use preprocessor voodoo to avoid duplicating the list of attributes 4
+ * times. The preprocessor output is just normal sysfs attributes code.
+ */
+
+/**
+ * FOREACH_BAT_ATTR - invoke the given macros on all our battery attributes
+ * @_BAT: battery number (0 or 1)
+ * @_ATTR_RW: macro to invoke for each read/write attribute
+ * @_ATTR_R: macro to invoke for each read-only attribute
+ */
+#define FOREACH_BAT_ATTR(_BAT, _ATTR_RW, _ATTR_R) \
+ _ATTR_RW(_BAT, start_charge_thresh) \
+ _ATTR_RW(_BAT, stop_charge_thresh) \
+ _ATTR_RW(_BAT, inhibit_charge_minutes) \
+ _ATTR_RW(_BAT, force_discharge) \
+ _ATTR_R(_BAT, installed) \
+ _ATTR_R(_BAT, state) \
+ _ATTR_R(_BAT, manufacturer) \
+ _ATTR_R(_BAT, model) \
+ _ATTR_R(_BAT, barcoding) \
+ _ATTR_R(_BAT, chemistry) \
+ _ATTR_R(_BAT, voltage) \
+ _ATTR_R(_BAT, group0_voltage) \
+ _ATTR_R(_BAT, group1_voltage) \
+ _ATTR_R(_BAT, group2_voltage) \
+ _ATTR_R(_BAT, group3_voltage) \
+ _ATTR_R(_BAT, current_now) \
+ _ATTR_R(_BAT, current_avg) \
+ _ATTR_R(_BAT, charging_max_current) \
+ _ATTR_R(_BAT, power_now) \
+ _ATTR_R(_BAT, power_avg) \
+ _ATTR_R(_BAT, remaining_percent) \
+ _ATTR_R(_BAT, remaining_percent_error) \
+ _ATTR_R(_BAT, remaining_charging_time) \
+ _ATTR_R(_BAT, remaining_running_time) \
+ _ATTR_R(_BAT, remaining_running_time_now) \
+ _ATTR_R(_BAT, remaining_capacity) \
+ _ATTR_R(_BAT, last_full_capacity) \
+ _ATTR_R(_BAT, design_voltage) \
+ _ATTR_R(_BAT, charging_max_voltage) \
+ _ATTR_R(_BAT, design_capacity) \
+ _ATTR_R(_BAT, cycle_count) \
+ _ATTR_R(_BAT, temperature) \
+ _ATTR_R(_BAT, serial) \
+ _ATTR_R(_BAT, manufacture_date) \
+ _ATTR_R(_BAT, first_use_date) \
+ _ATTR_R(_BAT, dump)
+
+/* Define several macros we will feed into FOREACH_BAT_ATTR: */
+
+#define DEFINE_BAT_ATTR_RW(_BAT,_NAME) \
+ static struct bat_device_attribute dev_attr_##_NAME##_##_BAT = { \
+ .dev_attr = __ATTR(_NAME, 0644, show_battery_##_NAME, \
+ store_battery_##_NAME), \
+ .bat = _BAT \
+ };
+
+#define DEFINE_BAT_ATTR_R(_BAT,_NAME) \
+ static struct bat_device_attribute dev_attr_##_NAME##_##_BAT = { \
+ .dev_attr = __ATTR(_NAME, 0644, show_battery_##_NAME, 0), \
+ .bat = _BAT \
+ };
+
+#define REF_BAT_ATTR(_BAT,_NAME) \
+ &dev_attr_##_NAME##_##_BAT.dev_attr.attr,
+
+/* This provide all attributes for one battery: */
+
+#define PROVIDE_BAT_ATTRS(_BAT) \
+ FOREACH_BAT_ATTR(_BAT, DEFINE_BAT_ATTR_RW, DEFINE_BAT_ATTR_R) \
+ static struct attribute *tp_bat##_BAT##_attributes[] = { \
+ FOREACH_BAT_ATTR(_BAT, REF_BAT_ATTR, REF_BAT_ATTR) \
+ NULL \
+ }; \
+ static struct attribute_group tp_bat##_BAT##_attribute_group = { \
+ .name = "BAT" #_BAT, \
+ .attrs = tp_bat##_BAT##_attributes \
+ };
+
+/* Finally genereate the attributes: */
+
+PROVIDE_BAT_ATTRS(0)
+PROVIDE_BAT_ATTRS(1)
+
+/* List of attribute groups */
+
+static struct attribute_group *attr_groups[] = {
+ &tp_root_attribute_group,
+ &tp_bat0_attribute_group,
+ &tp_bat1_attribute_group,
+ NULL
+};
+
+
+/*********************************************************************
+ * Init and cleanup
+ */
+
+static struct attribute_group **next_attr_group; /* next to register */
+
+static int __init tp_init(void)
+{
+ int ret;
+ printk(KERN_INFO "tp_smapi " TP_VERSION " loading...\n");
+
+ ret = find_smapi_port();
+ if (ret < 0)
+ goto err;
+ else
+ smapi_port = ret;
+
+ if (!request_region(smapi_port, 1, "smapi")) {
+ printk(KERN_ERR "tp_smapi cannot claim port 0x%x\n",
+ smapi_port);
+ ret = -ENXIO;
+ goto err;
+ }
+
+ if (!request_region(SMAPI_PORT2, 1, "smapi")) {
+ printk(KERN_ERR "tp_smapi cannot claim port 0x%x\n",
+ SMAPI_PORT2);
+ ret = -ENXIO;
+ goto err_port1;
+ }
+
+ ret = platform_driver_register(&tp_driver);
+ if (ret)
+ goto err_port2;
+
+ pdev = platform_device_alloc("smapi", -1);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto err_driver;
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto err_device_free;
+
+ for (next_attr_group = attr_groups; *next_attr_group;
+ ++next_attr_group) {
+ ret = sysfs_create_group(&pdev->dev.kobj, *next_attr_group);
+ if (ret)
+ goto err_attr;
+ }
+
+ printk(KERN_INFO "tp_smapi successfully loaded (smapi_port=0x%x).\n",
+ smapi_port);
+ return 0;
+
+err_attr:
+ while (--next_attr_group >= attr_groups)
+ sysfs_remove_group(&pdev->dev.kobj, *next_attr_group);
+ platform_device_unregister(pdev);
+err_device_free:
+ platform_device_put(pdev);
+err_driver:
+ platform_driver_unregister(&tp_driver);
+err_port2:
+ release_region(SMAPI_PORT2, 1);
+err_port1:
+ release_region(smapi_port, 1);
+err:
+ printk(KERN_ERR "tp_smapi init failed (ret=%d)!\n", ret);
+ return ret;
+}
+
+static void __exit tp_exit(void)
+{
+ while (next_attr_group && --next_attr_group >= attr_groups)
+ sysfs_remove_group(&pdev->dev.kobj, *next_attr_group);
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&tp_driver);
+ release_region(SMAPI_PORT2, 1);
+ if (smapi_port)
+ release_region(smapi_port, 1);
+
+ printk(KERN_INFO "tp_smapi unloaded.\n");
+}
+
+module_init(tp_init);
+module_exit(tp_exit);
diff --git a/fs/exec.c b/fs/exec.c
index 65eaacaba4f4..1d3b310bd5f0 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -63,6 +63,8 @@
#include <linux/compat.h>
#include <linux/vmalloc.h>
+#include <trace/events/fs.h>
+
#include <linux/uaccess.h>
#include <asm/mmu_context.h>
#include <asm/tlb.h>
@@ -866,9 +868,12 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
if (err)
goto exit;
- if (name->name[0] != '\0')
+ if (name->name[0] != '\0') {
fsnotify_open(file);
+ trace_open_exec(name->name);
+ }
+
out:
return file;
diff --git a/fs/open.c b/fs/open.c
index cb81623a8b09..a92b0f6061ac 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -34,6 +34,9 @@
#include "internal.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/fs.h>
+
int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
struct file *filp)
{
@@ -1068,6 +1071,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
} else {
fsnotify_open(f);
fd_install(fd, f);
+ trace_do_sys_open(tmp->name, flags, mode);
}
}
putname(tmp);
diff --git a/include/trace/events/fs.h b/include/trace/events/fs.h
new file mode 100644
index 000000000000..fb634b74adf3
--- /dev/null
+++ b/include/trace/events/fs.h
@@ -0,0 +1,53 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM fs
+
+#if !defined(_TRACE_FS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_FS_H
+
+#include <linux/fs.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(do_sys_open,
+
+ TP_PROTO(const char *filename, int flags, int mode),
+
+ TP_ARGS(filename, flags, mode),
+
+ TP_STRUCT__entry(
+ __string( filename, filename )
+ __field( int, flags )
+ __field( int, mode )
+ ),
+
+ TP_fast_assign(
+ __assign_str(filename, filename);
+ __entry->flags = flags;
+ __entry->mode = mode;
+ ),
+
+ TP_printk("\"%s\" %x %o",
+ __get_str(filename), __entry->flags, __entry->mode)
+);
+
+TRACE_EVENT(open_exec,
+
+ TP_PROTO(const char *filename),
+
+ TP_ARGS(filename),
+
+ TP_STRUCT__entry(
+ __string( filename, filename )
+ ),
+
+ TP_fast_assign(
+ __assign_str(filename, filename);
+ ),
+
+ TP_printk("\"%s\"",
+ __get_str(filename))
+);
+
+#endif /* _TRACE_FS_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 79226ca8f80f..2a30060e7e1d 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -47,7 +47,11 @@ struct blk_queue_stats;
struct blk_stat_callback;
#define BLKDEV_MIN_RQ 4
+#ifdef CONFIG_ZENIFY
+#define BLKDEV_MAX_RQ 512
+#else
#define BLKDEV_MAX_RQ 128 /* Default maximum */
+#endif
/* Must be consistent with blk_mq_poll_stats_bkt() */
#define BLK_MQ_POLL_STATS_BKTS 16
diff --git a/include/linux/thinkpad_ec.h b/include/linux/thinkpad_ec.h
new file mode 100644
index 000000000000..1b80d7ee5493
--- /dev/null
+++ b/include/linux/thinkpad_ec.h
@@ -0,0 +1,47 @@
+/*
+ * thinkpad_ec.h - interface to ThinkPad embedded controller LPC3 functions
+ *
+ * Copyright (C) 2005 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _THINKPAD_EC_H
+#define _THINKPAD_EC_H
+
+#ifdef __KERNEL__
+
+#define TP_CONTROLLER_ROW_LEN 16
+
+/* EC transactions input and output (possibly partial) vectors of 16 bytes. */
+struct thinkpad_ec_row {
+ u16 mask; /* bitmap of which entries of val[] are meaningful */
+ u8 val[TP_CONTROLLER_ROW_LEN];
+};
+
+extern int __must_check thinkpad_ec_lock(void);
+extern int __must_check thinkpad_ec_try_lock(void);
+extern void thinkpad_ec_unlock(void);
+
+extern int thinkpad_ec_read_row(const struct thinkpad_ec_row *args,
+ struct thinkpad_ec_row *data);
+extern int thinkpad_ec_try_read_row(const struct thinkpad_ec_row *args,
+ struct thinkpad_ec_row *mask);
+extern int thinkpad_ec_prefetch_row(const struct thinkpad_ec_row *args);
+extern void thinkpad_ec_invalidate(void);
+
+
+#endif /* __KERNEL */
+#endif /* _THINKPAD_EC_H */
diff --git a/init/Kconfig b/init/Kconfig
index 041f3a022122..5ed70eb1ad3a 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -45,6 +45,38 @@ config THREAD_INFO_IN_TASK
menu "General setup"
+config ZENIFY
+ bool "A selection of patches from Zen/Liquorix kernel and additional tweaks for a better gaming experience"
+ default y
+ help
+ Tunes the kernel for responsiveness at the cost of throughput and power usage.
+
+ --- Virtual Memory Subsystem ---------------------------
+
+ Mem dirty before bg writeback..: 10 % -> 20 %
+ Mem dirty before sync writeback: 20 % -> 50 %
+
+ --- Block Layer ----------------------------------------
+
+ Queue depth...............: 128 -> 512
+ Default MQ scheduler......: mq-deadline -> bfq
+
+ --- CFS CPU Scheduler ----------------------------------
+
+ Scheduling latency.............: 6 -> 3 ms
+ Minimal granularity............: 0.75 -> 0.3 ms
+ Wakeup granularity.............: 1 -> 0.5 ms
+ CPU migration cost.............: 0.5 -> 0.25 ms
+ Bandwidth slice size...........: 5 -> 3 ms
+ Ondemand fine upscaling limit..: 95 % -> 85 %
+
+ --- MuQSS CPU Scheduler --------------------------------
+
+ Scheduling interval............: 6 -> 3 ms
+ ISO task max realtime use......: 70 % -> 25 %
+ Ondemand coarse upscaling limit: 80 % -> 45 %
+ Ondemand fine upscaling limit..: 95 % -> 45 %
+
config BROKEN
bool
@@ -1026,6 +1058,13 @@ config CC_OPTIMIZE_FOR_PERFORMANCE
with the "-O2" compiler flag for best performance and most
helpful compile-time warnings.
+config CC_OPTIMIZE_HARDER
+ bool "Optimize harder"
+ help
+ This option will pass "-O3" to your compiler resulting in a
+ larger and faster kernel. The more complex optimizations also
+ increase compilation time and may affect stability.
+
config CC_OPTIMIZE_FOR_SIZE
bool "Optimize for size"
help
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 2f0a0be4d344..bada807c7e59 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -37,8 +37,13 @@
*
* (default: 6ms * (1 + ilog(ncpus)), units: nanoseconds)
*/
+#ifdef CONFIG_ZENIFY
+unsigned int sysctl_sched_latency = 3000000ULL;
+static unsigned int normalized_sysctl_sched_latency = 3000000ULL;
+#else
unsigned int sysctl_sched_latency = 6000000ULL;
static unsigned int normalized_sysctl_sched_latency = 6000000ULL;
+#endif
/*
* The initial- and re-scaling of tunables is configurable
@@ -58,13 +63,22 @@ enum sched_tunable_scaling sysctl_sched_tunable_scaling = SCHED_TUNABLESCALING_L
*
* (default: 0.75 msec * (1 + ilog(ncpus)), units: nanoseconds)
*/
+#ifdef CONFIG_ZENIFY
+unsigned int sysctl_sched_min_granularity = 300000ULL;
+static unsigned int normalized_sysctl_sched_min_granularity = 300000ULL;
+#else
unsigned int sysctl_sched_min_granularity = 750000ULL;
static unsigned int normalized_sysctl_sched_min_granularity = 750000ULL;
+#endif
/*
* This value is kept at sysctl_sched_latency/sysctl_sched_min_granularity
*/
+#ifdef CONFIG_ZENIFY
+static unsigned int sched_nr_latency = 10;
+#else
static unsigned int sched_nr_latency = 8;
+#endif
/*
* After fork, child runs first. If set to 0 (default) then
@@ -81,10 +95,17 @@ unsigned int sysctl_sched_child_runs_first __read_mostly;
*
* (default: 1 msec * (1 + ilog(ncpus)), units: nanoseconds)
*/
+#ifdef CONFIG_ZENIFY
+unsigned int sysctl_sched_wakeup_granularity = 500000UL;
+static unsigned int normalized_sysctl_sched_wakeup_granularity = 500000UL;
+
+const_debug unsigned int sysctl_sched_migration_cost = 50000UL;
+#else
unsigned int sysctl_sched_wakeup_granularity = 1000000UL;
static unsigned int normalized_sysctl_sched_wakeup_granularity = 1000000UL;
const_debug unsigned int sysctl_sched_migration_cost = 500000UL;
+#endif
#ifdef CONFIG_SMP
/*
@@ -107,8 +128,12 @@ int __weak arch_asym_cpu_priority(int cpu)
*
* (default: 5 msec, units: microseconds)
*/
+#ifdef CONFIG_ZENIFY
+unsigned int sysctl_sched_cfs_bandwidth_slice = 3000UL;
+#else
unsigned int sysctl_sched_cfs_bandwidth_slice = 5000UL;
#endif
+#endif
/*
* The margin used when comparing utilization with CPU capacity:
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 337c6afb3345..9315e358f292 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -71,7 +71,11 @@ static long ratelimit_pages = 32;
/*
* Start background writeback (via writeback threads) at this percentage
*/
+#ifdef CONFIG_ZENIFY
+int dirty_background_ratio = 20;
+#else
int dirty_background_ratio = 10;
+#endif
/*
* dirty_background_bytes starts at 0 (disabled) so that it is a function of
@@ -88,7 +92,11 @@ int vm_highmem_is_dirtyable;
/*
* The generator of dirty data starts writeback at this percentage
*/
+#ifdef CONFIG_ZENIFY
+int vm_dirty_ratio = 50;
+#else
int vm_dirty_ratio = 20;
+#endif
/*
* vm_dirty_bytes starts at 0 (disabled) so that it is a function of
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 80dad301361d..42b7fa7d01f8 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -702,6 +702,9 @@ choice
config DEFAULT_VEGAS
bool "Vegas" if TCP_CONG_VEGAS=y
+ config DEFAULT_YEAH
+ bool "YeAH" if TCP_CONG_YEAH=y
+
config DEFAULT_VENO
bool "Veno" if TCP_CONG_VENO=y
@@ -735,6 +738,7 @@ config DEFAULT_TCP_CONG
default "htcp" if DEFAULT_HTCP
default "hybla" if DEFAULT_HYBLA
default "vegas" if DEFAULT_VEGAS
+ default "yeah" if DEFAULT_YEAH
default "westwood" if DEFAULT_WESTWOOD
default "veno" if DEFAULT_VENO
default "reno" if DEFAULT_RENO
From: Nick Desaulniers <ndesaulniers@google.com>
Date: Mon, 24 Dec 2018 13:37:41 +0200
Subject: include/linux/compiler*.h: define asm_volatile_goto
asm_volatile_goto should also be defined for other compilers that
support asm goto.
Fixes commit 815f0dd ("include/linux/compiler*.h: make compiler-*.h
mutually exclusive").
Signed-off-by: Nick Desaulniers <ndesaulniers@google.com>
Signed-off-by: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index ba814f1..e77eeb0 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -188,6 +188,10 @@ struct ftrace_likely_data {
#define asm_volatile_goto(x...) asm goto(x)
#endif
+#ifndef asm_volatile_goto
+#define asm_volatile_goto(x...) asm goto(x)
+#endif
+
/* Are two types/vars the same type (ignoring qualifiers)? */
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
From: Andy Lavr <andy.lavr@gmail.com>
Date: Mon, 24 Dec 2018 14:57:47 +0200
Subject: avl: Use [defer+madvise] as default khugepaged defrag strategy
For some reason, the default strategy to respond to THP fault fallbacks
is still just madvise, meaning stall if the program wants transparent
hugepages, but don't trigger a background reclaim / compaction if THP
begins to fail allocations. This creates a snowball affect where we
still use the THP code paths, but we almost always fail once a system
has been active and busy for a while.
The option "defer" was created for interactive systems where THP can
still improve performance. If we have to fallback to a regular page due
to an allocation failure or anything else, we will trigger a background
reclaim and compaction so future THP attempts succeed and previous
attempts eventually have their smaller pages combined without stalling
running applications.
We still want madvise to stall applications that explicitely want THP,
so defer+madvise _does_ make a ton of sense. Make it the default for
interactive systems, especially if the kernel maintainer left
transparent hugepages on "always".
Reasoning and details in the original patch:
https://lwn.net/Articles/711248/
Signed-off-by: Andy Lavr <andy.lavr@gmail.com>
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index e84a10b..21d62b7 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -53,7 +53,11 @@ unsigned long transparent_hugepage_flags __read_mostly =
#ifdef CONFIG_TRANSPARENT_HUGEPAGE_MADVISE
(1<<TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG)|
#endif
+#ifdef CONFIG_AVL_INTERACTIVE
+ (1<<TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG)|
+#else
(1<<TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG)|
+#endif
(1<<TRANSPARENT_HUGEPAGE_DEFRAG_KHUGEPAGED_FLAG)|
(1<<TRANSPARENT_HUGEPAGE_USE_ZERO_PAGE_FLAG);
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -429,6 +429,9 @@
Select the queueing discipline that will be used by default
for all network devices.
+ config DEFAULT_CAKE
+ bool "Common Applications Kept Enhanced" if NET_SCH_CAKE
+
config DEFAULT_FQ
bool "Fair Queue" if NET_SCH_FQ
@@ -448,6 +451,7 @@
config DEFAULT_NET_SCH
string
default "pfifo_fast" if DEFAULT_PFIFO_FAST
+ default "cake" if DEFAULT_CAKE
default "fq" if DEFAULT_FQ
default "fq_codel" if DEFAULT_FQ_CODEL
default "sfq" if DEFAULT_SFQ
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index a29043ea9..3fb219747 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -263,7 +263,7 @@ compound_page_dtor * const compound_page_dtors[] = {
#else
int watermark_boost_factor __read_mostly = 15000;
#endif
-int watermark_scale_factor = 10;
+int watermark_scale_factor = 200;
static unsigned long nr_kernel_pages __initdata;
static unsigned long nr_all_pages __initdata;
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 80bb6408f..6c8b55cd1 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -146,8 +146,7 @@ extern int mmap_rnd_compat_bits __read_mostly;
* not a hard limit any more. Although some userspace tools can be surprised by
* that.
*/
-#define MAPCOUNT_ELF_CORE_MARGIN (5)
-#define DEFAULT_MAX_MAP_COUNT (USHRT_MAX - MAPCOUNT_ELF_CORE_MARGIN)
+#define DEFAULT_MAX_MAP_COUNT (262144)
extern int sysctl_max_map_count;
diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h
index fd550dee4982..63c3c79380a1 100644
--- a/drivers/net/ethernet/intel/e1000e/defines.h
+++ b/drivers/net/ethernet/intel/e1000e/defines.h
@@ -222,9 +222,6 @@
#define E1000_STATUS_PHYRA 0x00000400 /* PHY Reset Asserted */
#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Master Req status */
-/* PCIm function state */
-#define E1000_STATUS_PCIM_STATE 0x40000000
-
#define HALF_DUPLEX 1
#define FULL_DUPLEX 2
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index b5fed6177ad6..e4baa13b3cda 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -5161,9 +5161,8 @@ static void e1000_watchdog_task(struct work_struct *work)
struct e1000_mac_info *mac = &adapter->hw.mac;
struct e1000_phy_info *phy = &adapter->hw.phy;
struct e1000_ring *tx_ring = adapter->tx_ring;
- u32 dmoff_exit_timeout = 100, tries = 0;
struct e1000_hw *hw = &adapter->hw;
+ u32 link, tctl;
- u32 link, tctl, pcim_state;
if (test_bit(__E1000_DOWN, &adapter->state))
return;
@@ -5188,21 +5187,6 @@ static void e1000_watchdog_task(struct work_struct *work)
/* Cancel scheduled suspend requests. */
pm_runtime_resume(netdev->dev.parent);
- /* Checking if MAC is in DMoff state*/
- pcim_state = er32(STATUS);
- while (pcim_state & E1000_STATUS_PCIM_STATE) {
- if (tries++ == dmoff_exit_timeout) {
- e_dbg("Error in exiting dmoff\n");
- break;
- }
- usleep_range(10000, 20000);
- pcim_state = er32(STATUS);
-
- /* Checking if MAC exited DMoff state */
- if (!(pcim_state & E1000_STATUS_PCIM_STATE))
- e1000_phy_hw_reset(&adapter->hw);
- }
-
/* update snapshot of PHY registers on LSC */
e1000_phy_read_status(adapter);
mac->ops.get_link_up_info(&adapter->hw,
From adb1f9df27f08e6488bcd80b1607987c6114a77a Mon Sep 17 00:00:00 2001
From: Alexandre Frade <admfrade@gmail.com>
Date: Mon, 25 Nov 2019 15:13:06 -0300
Subject: [PATCH] elevator: set default scheduler to bfq for blk-mq
Signed-off-by: Alexandre Frade <admfrade@gmail.com>
---
block/elevator.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/block/elevator.c b/block/elevator.c
index 076ba7308e65..81f89095aa77 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -623,15 +623,15 @@ static inline bool elv_support_iosched(struct request_queue *q)
}
/*
- * For single queue devices, default to using mq-deadline. If we have multiple
- * queues or mq-deadline is not available, default to "none".
+ * For single queue devices, default to using bfq. If we have multiple
+ * queues or bfq is not available, default to "none".
*/
static struct elevator_type *elevator_get_default(struct request_queue *q)
{
if (q->nr_hw_queues != 1)
return NULL;
- return elevator_get(q, "mq-deadline", false);
+ return elevator_get(q, "bfq", false);
}
/*
From c3ec05777c46e19a8a26d0fc4ca0c0db8a19de97 Mon Sep 17 00:00:00 2001
From: Alexandre Frade <admfrade@gmail.com>
Date: Fri, 10 May 2019 16:45:59 -0300
Subject: [PATCH] block: set rq_affinity = 2 for full multithreading I/O
requests
Signed-off-by: Alexandre Frade <admfrade@gmail.com>
---
include/linux/blkdev.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index f3ea78b0c91c..4dbacc6b073b 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -621,7 +621,8 @@ struct request_queue {
#define QUEUE_FLAG_RQ_ALLOC_TIME 27 /* record rq->alloc_time_ns */
#define QUEUE_FLAG_MQ_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \
- (1 << QUEUE_FLAG_SAME_COMP))
+ (1 << QUEUE_FLAG_SAME_COMP) | \
+ (1 << QUEUE_FLAG_SAME_FORCE))
void blk_queue_flag_set(unsigned int flag, struct request_queue *q);
void blk_queue_flag_clear(unsigned int flag, struct request_queue *q);
From 8171d33d0b84a953649863538fdbe4c26c035e4f Mon Sep 17 00:00:00 2001
From: Alexandre Frade <admfrade@gmail.com>
Date: Fri, 10 May 2019 14:32:50 -0300
Subject: [PATCH] mm: set 2 megabytes for address_space-level file read-ahead
pages size
Signed-off-by: Alexandre Frade <admfrade@gmail.com>
---
include/linux/mm.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index a2adf95b3f9c..e804d9f7583a 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2416,7 +2416,7 @@ int __must_check write_one_page(struct page *page);
void task_dirty_inc(struct task_struct *tsk);
/* readahead.c */
-#define VM_READAHEAD_PAGES (SZ_128K / PAGE_SIZE)
+#define VM_READAHEAD_PAGES (SZ_2M / PAGE_SIZE)
int force_page_cache_readahead(struct address_space *mapping, struct file *filp,
pgoff_t offset, unsigned long nr_to_read);