···398398399399Most ThinkPads include six or more separate temperature sensors but400400only expose the CPU temperature through the standard ACPI methods.401401-This feature shows readings from up to eight different sensors. Some402402-readings may not be valid, e.g. may show large negative values. For403403-example, on the X40, a typical output may be:404404-405405-temperatures: 42 42 45 41 36 -128 33 -128406406-407407-Thomas Gruber took his R51 apart and traced all six active sensors in408408-his laptop (the location of sensors may vary on other models):409409-410410-1: CPU411411-2: Mini PCI Module412412-3: HDD413413-4: GPU414414-5: Battery415415-6: N/A416416-7: Battery417417-8: N/A401401+This feature shows readings from up to eight different sensors on older402402+ThinkPads, and it has experimental support for up to sixteen different403403+sensors on newer ThinkPads. Readings from sensors that are not available404404+return -128.418405419406No commands can be written to this file.407407+408408+EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the409409+implementation directly accesses hardware registers and may not work as410410+expected. USE WITH CAUTION! To use this feature, you need to supply the411411+experimental=1 parameter when loading the module. When EXPERIMENTAL412412+mode is enabled, reading the first 8 sensors on newer ThinkPads will413413+also use an new experimental thermal sensor access mode.414414+415415+For example, on the X40, a typical output may be:416416+temperatures: 42 42 45 41 36 -128 33 -128417417+418418+EXPERIMENTAL: On the T43/p, a typical output may be:419419+temperatures: 48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128420420+421421+The mapping of thermal sensors to physical locations varies depending on422422+system-board model (and thus, on ThinkPad model).423423+424424+http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that425425+tries to track down these locations for various models.426426+427427+Most (newer?) models seem to follow this pattern:428428+429429+1: CPU430430+2: (depends on model)431431+3: (depends on model)432432+4: GPU433433+5: Main battery: main sensor434434+6: Bay battery: main sensor435435+7: Main battery: secondary sensor436436+8: Bay battery: secondary sensor437437+9-15: (depends on model)438438+439439+For the R51 (source: Thomas Gruber):440440+2: Mini-PCI441441+3: Internal HDD442442+443443+For the T43, T43/p (source: Shmidoax/Thinkwiki.org)444444+http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p445445+2: System board, left side (near PCMCIA slot), reported as HDAPS temp446446+3: PCMCIA slot447447+9: MCH (northbridge) to DRAM Bus448448+10: ICH (southbridge), under Mini-PCI card, under touchpad449449+11: Power regulator, underside of system board, below F2 key450450+451451+The A31 has a very atypical layout for the thermal sensors452452+(source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31)453453+1: CPU454454+2: Main Battery: main sensor455455+3: Power Converter456456+4: Bay Battery: main sensor457457+5: MCH (northbridge)458458+6: PCMCIA/ambient459459+7: Main Battery: secondary sensor460460+8: Bay Battery: secondary sensor461461+420462421463EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump422464------------------------------------------------------------------------···571529WITH CAUTION! To use this feature, you need to supply the572530experimental=1 parameter when loading the module.573531574574-This feature attempts to show the current fan speed. The speed is read575575-directly from the hardware registers of the embedded controller. This576576-is known to work on later R, T and X series ThinkPads but may show a577577-bogus value on other models.532532+This feature attempts to show the current fan speed, control mode and533533+other fan data that might be available. The speed is read directly534534+from the hardware registers of the embedded controller. This is known535535+to work on later R, T and X series ThinkPads but may show a bogus536536+value on other models.537537+538538+Most ThinkPad fans work in "levels". Level 0 stops the fan. The higher539539+the level, the higher the fan speed, although adjacent levels often map540540+to the same fan speed. 7 is the highest level, where the fan reaches541541+the maximum recommended speed. Level "auto" means the EC changes the542542+fan level according to some internal algorithm, usually based on543543+readings from the thermal sensors. Level "disengaged" means the EC544544+disables the speed-locked closed-loop fan control, and drives the fan as545545+fast as it can go, which might exceed hardware limits, so use this level546546+with caution.547547+548548+The fan usually ramps up or down slowly from one speed to another,549549+and it is normal for the EC to take several seconds to react to fan550550+commands.578551579552The fan may be enabled or disabled with the following commands:580553581554 echo enable >/proc/acpi/ibm/fan582555 echo disable >/proc/acpi/ibm/fan583556584584-WARNING WARNING WARNING: do not leave the fan disabled unless you are585585-monitoring the temperature sensor readings and you are ready to enable586586-it if necessary to avoid overheating.557557+Placing a fan on level 0 is the same as disabling it. Enabling a fan558558+will try to place it in a safe level if it is too slow or disabled.587559588588-The fan only runs if it's enabled *and* the various temperature589589-sensors which control it read high enough. On the X40, this seems to590590-depend on the CPU and HDD temperatures. Specifically, the fan is591591-turned on when either the CPU temperature climbs to 56 degrees or the592592-HDD temperature climbs to 46 degrees. The fan is turned off when the593593-CPU temperature drops to 49 degrees and the HDD temperature drops to594594-41 degrees. These thresholds cannot currently be controlled.560560+WARNING WARNING WARNING: do not leave the fan disabled unless you are561561+monitoring all of the temperature sensor readings and you are ready to562562+enable it if necessary to avoid overheating.563563+564564+An enabled fan in level "auto" may stop spinning if the EC decides the565565+ThinkPad is cool enough and doesn't need the extra airflow. This is566566+normal, and the EC will spin the fan up if the varios thermal readings567567+rise too much.568568+569569+On the X40, this seems to depend on the CPU and HDD temperatures.570570+Specifically, the fan is turned on when either the CPU temperature571571+climbs to 56 degrees or the HDD temperature climbs to 46 degrees. The572572+fan is turned off when the CPU temperature drops to 49 degrees and the573573+HDD temperature drops to 41 degrees. These thresholds cannot574574+currently be controlled.575575+576576+The fan level can be controlled with the command:577577+578578+ echo 'level <level>' > /proc/acpi/ibm/thermal579579+580580+Where <level> is an integer from 0 to 7, or one of the words "auto"581581+or "disengaged" (without the quotes). Not all ThinkPads support the582582+"auto" and "disengaged" levels.595583596584On the X31 and X40 (and ONLY on those models), the fan speed can be597585controlled to a certain degree. Once the fan is running, it can be···634562any effect or the fan speed eventually settles somewhere in that635563range. The fan cannot be stopped or started with this command.636564637637-On the 570, temperature readings are not available through this638638-feature and the fan control works a little differently. The fan speed639639-is reported in levels from 0 (off) to 7 (max) and can be controlled640640-with the following command:641641-642642- echo 'level <level>' > /proc/acpi/ibm/thermal565565+The ThinkPad's ACPI DSDT code will reprogram the fan on its own when566566+certain conditions are met. It will override any fan programming done567567+through ibm-acpi.643568644569EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan645570---------------------------------------···669600example:670601671602 modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable603603+604604+The ibm-acpi kernel driver can be programmed to revert the fan level605605+to a safe setting if userspace does not issue one of the fan commands:606606+"enable", "disable", "level" or "watchdog" within a configurable607607+ammount of time. To do this, use the "watchdog" command.608608+609609+ echo 'watchdog <interval>' > /proc/acpi/ibm/fan610610+611611+Interval is the ammount of time in seconds to wait for one of the612612+above mentioned fan commands before reseting the fan level to a safe613613+one. If set to zero, the watchdog is disabled (default). When the614614+watchdog timer runs out, it does the exact equivalent of the "enable"615615+fan command.616616+617617+Note that the watchdog timer stops after it enables the fan. It will618618+be rearmed again automatically (using the same interval) when one of619619+the above mentioned fan commands is received. The fan watchdog is,620620+therefore, not suitable to protect against fan mode changes made621621+through means other than the "enable", "disable", and "level" fan622622+commands.672623673624674625Example Configuration
+14
drivers/acpi/Kconfig
···173173config ACPI_ASUS174174 tristate "ASUS/Medion Laptop Extras"175175 depends on X86176176+ select BACKLIGHT_CLASS_DEVICE176177 ---help---177178 This driver provides support for extra features of ACPI-compatible178179 ASUS laptops. As some of Medion laptops are made by ASUS, it may also···202201config ACPI_IBM203202 tristate "IBM ThinkPad Laptop Extras"204203 depends on X86204204+ select BACKLIGHT_CLASS_DEVICE205205 ---help---206206 This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds207207 support for Fn-Fx key combinations, Bluetooth control, video···225223226224 If you are not sure, say N here.227225226226+config ACPI_IBM_BAY227227+ bool "Legacy Removable Bay Support"228228+ depends on ACPI_IBM229229+ depends on ACPI_BAY=n230230+ default n231231+ ---help---232232+ Allows the ibm_acpi driver to handle removable bays.233233+ This support is obsoleted by CONFIG_ACPI_BAY.234234+235235+ If you are not sure, say N here.236236+228237config ACPI_TOSHIBA229238 tristate "Toshiba Laptop Extras"230239 depends on X86240240+ select BACKLIGHT_CLASS_DEVICE231241 ---help---232242 This driver adds support for access to certain system settings233243 on "legacy free" Toshiba laptops. These laptops can be recognized by
+46-16
drivers/acpi/asus_acpi.c
···3535#include <linux/init.h>3636#include <linux/types.h>3737#include <linux/proc_fs.h>3838+#include <linux/backlight.h>3839#include <acpi/acpi_drivers.h>3940#include <acpi/acpi_bus.h>4041#include <asm/uaccess.h>···402401403402/* procdir we use */404403static struct proc_dir_entry *asus_proc_dir;404404+405405+static struct backlight_device *asus_backlight_device;405406406407/*407408 * This header is made available to allow proper configuration given model,···782779 return rv;783780}784781785785-static int read_brightness(void)782782+static int read_brightness(struct backlight_device *bd)786783{787784 int value;788785···804801/*805802 * Change the brightness level806803 */807807-static void set_brightness(int value)804804+static int set_brightness(int value)808805{809806 acpi_status status = 0;807807+ int ret = 0;810808811809 /* SPLV laptop */812810 if (hotk->methods->brightness_set) {···815811 value, NULL))816812 printk(KERN_WARNING817813 "Asus ACPI: Error changing brightness\n");818818- return;814814+ ret = -EIO;815815+ goto out;819816 }820817821818 /* No SPLV method if we are here, act as appropriate */822822- value -= read_brightness();819819+ value -= read_brightness(NULL);823820 while (value != 0) {824821 status = acpi_evaluate_object(NULL, (value > 0) ?825822 hotk->methods->brightness_up :···830825 if (ACPI_FAILURE(status))831826 printk(KERN_WARNING832827 "Asus ACPI: Error changing brightness\n");828828+ ret = -EIO;833829 }834834- return;830830+out:831831+ return ret;832832+}833833+834834+static int set_brightness_status(struct backlight_device *bd)835835+{836836+ return set_brightness(bd->props->brightness);835837}836838837839static int838840proc_read_brn(char *page, char **start, off_t off, int count, int *eof,839841 void *data)840842{841841- return sprintf(page, "%d\n", read_brightness());843843+ return sprintf(page, "%d\n", read_brightness(NULL));842844}843845844846static int···13451333 return 0;13461334}1347133513361336+static struct backlight_properties asus_backlight_data = {13371337+ .owner = THIS_MODULE,13381338+ .get_brightness = read_brightness,13391339+ .update_status = set_brightness_status,13401340+ .max_brightness = 15,13411341+};13421342+13431343+static void __exit asus_acpi_exit(void)13441344+{13451345+ if (asus_backlight_device)13461346+ backlight_device_unregister(asus_backlight_device);13471347+13481348+ acpi_bus_unregister_driver(&asus_hotk_driver);13491349+ remove_proc_entry(PROC_ASUS, acpi_root_dir);13501350+13511351+ kfree(asus_info);13521352+13531353+ return;13541354+}13551355+13481356static int __init asus_acpi_init(void)13491357{13501358 int result;···14021370 return result;14031371 }1404137213731373+ asus_backlight_device = backlight_device_register("asus", NULL,13741374+ &asus_backlight_data);13751375+ if (IS_ERR(asus_backlight_device)) {13761376+ printk(KERN_ERR "Could not register asus backlight device\n");13771377+ asus_backlight_device = NULL;13781378+ asus_acpi_exit();13791379+ }13801380+14051381 return 0;14061406-}14071407-14081408-static void __exit asus_acpi_exit(void)14091409-{14101410- acpi_bus_unregister_driver(&asus_hotk_driver);14111411- remove_proc_entry(PROC_ASUS, acpi_root_dir);14121412-14131413- kfree(asus_info);14141414-14151415- return;14161382}1417138314181384module_init(asus_acpi_init);
+892-157
drivers/acpi/ibm_acpi.c
···33 *44 *55 * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>66+ * Copyright (C) 2006 Henrique de Moraes Holschuh <hmh@hmh.eng.br>67 *78 * This program is free software; you can redistribute it and/or modify89 * it under the terms of the GNU General Public License as published by···2019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA2120 */22212323-#define IBM_VERSION "0.12a"2222+#define IBM_VERSION "0.13"24232524/*2625 * Changelog:2626+ *2727+ * 2006-11-22 0.13 new maintainer2828+ * changelog now lives in git commit history, and will2929+ * not be updated further in-file.2730 * 2831 * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels2932 * 2005-03-17 0.11 support for 600e, 770x···8277#include <linux/module.h>8378#include <linux/init.h>8479#include <linux/types.h>8080+#include <linux/string.h>8181+8582#include <linux/proc_fs.h>8383+#include <linux/backlight.h>8684#include <asm/uaccess.h>8585+8686+#include <linux/dmi.h>8787+#include <linux/jiffies.h>8888+#include <linux/workqueue.h>87898890#include <acpi/acpi_drivers.h>8991#include <acpi/acnamesp.h>···10088#define IBM_FILE "ibm_acpi"10189#define IBM_URL "http://ibm-acpi.sf.net/"10290103103-MODULE_AUTHOR("Borislav Deianov");9191+MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");10492MODULE_DESCRIPTION(IBM_DESC);10593MODULE_VERSION(IBM_VERSION);10694MODULE_LICENSE("GPL");···127115 static acpi_handle *object##_parent = &parent##_handle; \128116 static char *object##_path; \129117 static char *object##_paths[] = { paths }130130-131131-/*132132- * The following models are supported to various degrees:133133- *134134- * 570, 600e, 600x, 770e, 770x135135- * A20m, A21e, A21m, A21p, A22p, A30, A30p, A31, A31p136136- * G40, G41137137- * R30, R31, R32, R40, R40e, R50, R50e, R50p, R51138138- * T20, T21, T22, T23, T30, T40, T40p, T41, T41p, T42, T42p, T43139139- * X20, X21, X22, X23, X24, X30, X31, X40140140- *141141- * The following models have no supported features:142142- *143143- * 240, 240x, i1400144144- *145145- * Still missing DSDTs for the following models:146146- *147147- * A20p, A22e, A22m148148- * R52149149- * S31150150- * T43p151151- */152118153119IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */154120 "\\_SB.PCI.ISA.EC", /* 570 */···157167 "\\_SB.PCI.ISA.SLCE", /* 570 */158168 ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */159169#endif170170+#ifdef CONFIG_ACPI_IBM_BAY160171IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */161172 "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */173173+ "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ 162174 "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */163175 ); /* A21e, R30, R31 */164176···175183IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */176184 "_EJ0", /* 770x */177185 ); /* all others */186186+#endif178187179188/* don't list other alternatives as we install a notify handler on the 570 */180189IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */···196203IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */197204IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */198205IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */199199-IBM_HANDLE(fans, ec, "FANS"); /* X31, X40 */206206+IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */200207201208IBM_HANDLE(gfan, ec, "GFAN", /* 570 */202209 "\\FSPD", /* 600e/x, 770e, 770x */···208215209216#define IBM_HKEY_HID "IBM0068"210217#define IBM_PCI_HID "PNP0A03"218218+219219+enum thermal_access_mode {220220+ IBMACPI_THERMAL_NONE = 0, /* No thermal support */221221+ IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */222222+ IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */223223+ IBMACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */224224+ IBMACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */225225+};226226+227227+#define IBMACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */228228+struct ibm_thermal_sensors_struct {229229+ s32 temp[IBMACPI_MAX_THERMAL_SENSORS];230230+};231231+232232+/*233233+ * FAN ACCESS MODES234234+ *235235+ * IBMACPI_FAN_RD_ACPI_GFAN:236236+ * ACPI GFAN method: returns fan level237237+ *238238+ * see IBMACPI_FAN_WR_ACPI_SFAN239239+ * EC 0x2f not available if GFAN exists240240+ *241241+ * IBMACPI_FAN_WR_ACPI_SFAN:242242+ * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)243243+ *244244+ * EC 0x2f might be available *for reading*, but never for writing.245245+ *246246+ * IBMACPI_FAN_WR_TPEC:247247+ * ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported248248+ * on almost all ThinkPads249249+ *250250+ * Fan speed changes of any sort (including those caused by the251251+ * disengaged mode) are usually done slowly by the firmware as the252252+ * maximum ammount of fan duty cycle change per second seems to be253253+ * limited.254254+ *255255+ * Reading is not available if GFAN exists.256256+ * Writing is not available if SFAN exists.257257+ *258258+ * Bits259259+ * 7 automatic mode engaged;260260+ * (default operation mode of the ThinkPad)261261+ * fan level is ignored in this mode.262262+ * 6 disengage mode (takes precedence over bit 7);263263+ * not available on all thinkpads. May disable264264+ * the tachometer, and speeds up fan to 100% duty-cycle,265265+ * which speeds it up far above the standard RPM266266+ * levels. It is not impossible that it could cause267267+ * hardware damage.268268+ * 5-3 unused in some models. Extra bits for fan level269269+ * in others, but still useless as all values above270270+ * 7 map to the same speed as level 7 in these models.271271+ * 2-0 fan level (0..7 usually)272272+ * 0x00 = stop273273+ * 0x07 = max (set when temperatures critical)274274+ * Some ThinkPads may have other levels, see275275+ * IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41)276276+ *277277+ * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at278278+ * boot. Apparently the EC does not intialize it, so unless ACPI DSDT279279+ * does so, its initial value is meaningless (0x07).280280+ *281281+ * For firmware bugs, refer to:282282+ * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues283283+ *284284+ * ----285285+ *286286+ * ThinkPad EC register 0x84 (LSB), 0x85 (MSB):287287+ * Main fan tachometer reading (in RPM)288288+ *289289+ * This register is present on all ThinkPads with a new-style EC, and290290+ * it is known not to be present on the A21m/e, and T22, as there is291291+ * something else in offset 0x84 according to the ACPI DSDT. Other292292+ * ThinkPads from this same time period (and earlier) probably lack the293293+ * tachometer as well.294294+ *295295+ * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare296296+ * was never fixed by IBM to report the EC firmware version string297297+ * probably support the tachometer (like the early X models), so298298+ * detecting it is quite hard. We need more data to know for sure.299299+ *300300+ * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings301301+ * might result.302302+ *303303+ * FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this304304+ * register is not invalidated in ThinkPads that disable tachometer305305+ * readings. Thus, the tachometer readings go stale.306306+ *307307+ * For firmware bugs, refer to:308308+ * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues309309+ *310310+ * IBMACPI_FAN_WR_ACPI_FANS:311311+ * ThinkPad X31, X40, X41. Not available in the X60.312312+ *313313+ * FANS ACPI handle: takes three arguments: low speed, medium speed,314314+ * high speed. ACPI DSDT seems to map these three speeds to levels315315+ * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH316316+ * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")317317+ *318318+ * The speeds are stored on handles319319+ * (FANA:FAN9), (FANC:FANB), (FANE:FAND).320320+ *321321+ * There are three default speed sets, acessible as handles:322322+ * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H323323+ *324324+ * ACPI DSDT switches which set is in use depending on various325325+ * factors.326326+ *327327+ * IBMACPI_FAN_WR_TPEC is also available and should be used to328328+ * command the fan. The X31/X40/X41 seems to have 8 fan levels,329329+ * but the ACPI tables just mention level 7.330330+ */331331+332332+enum fan_status_access_mode {333333+ IBMACPI_FAN_NONE = 0, /* No fan status or control */334334+ IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */335335+ IBMACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */336336+};337337+338338+enum fan_control_access_mode {339339+ IBMACPI_FAN_WR_NONE = 0, /* No fan control */340340+ IBMACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */341341+ IBMACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */342342+ IBMACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */343343+};344344+345345+enum fan_control_commands {346346+ IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */347347+ IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */348348+ IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd,349349+ * and also watchdog cmd */350350+};351351+352352+enum { /* Fan control constants */353353+ fan_status_offset = 0x2f, /* EC register 0x2f */354354+ fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM)355355+ * 0x84 must be read before 0x85 */356356+357357+ IBMACPI_FAN_EC_DISENGAGED = 0x40, /* EC mode: tachometer358358+ * disengaged */359359+ IBMACPI_FAN_EC_AUTO = 0x80, /* EC mode: auto fan360360+ * control */361361+};362362+363363+static char *ibm_thinkpad_ec_found = NULL;211364212365struct ibm_struct {213366 char *name;···381242};382243383244static struct proc_dir_entry *proc_dir = NULL;245245+246246+static struct backlight_device *ibm_backlight_device = NULL;384247385248#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")386249#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")···722581{723582 int status;724583725725- if (!wan_supported ||726726- !acpi_evalf(hkey_handle, &status, "GWAN", "d"))584584+ if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d"))727585 status = 0;728586729587 return status;···770630 return 0;771631}772632773773-static int video_supported;774774-static int video_orig_autosw;633633+enum video_access_mode {634634+ IBMACPI_VIDEO_NONE = 0,635635+ IBMACPI_VIDEO_570, /* 570 */636636+ IBMACPI_VIDEO_770, /* 600e/x, 770e, 770x */637637+ IBMACPI_VIDEO_NEW, /* all others */638638+};775639776776-#define VIDEO_570 1777777-#define VIDEO_770 2778778-#define VIDEO_NEW 3640640+static enum video_access_mode video_supported;641641+static int video_orig_autosw;779642780643static int video_init(void)781644{···790647791648 if (!vid_handle)792649 /* video switching not supported on R30, R31 */793793- video_supported = 0;650650+ video_supported = IBMACPI_VIDEO_NONE;794651 else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))795652 /* 570 */796796- video_supported = VIDEO_570;653653+ video_supported = IBMACPI_VIDEO_570;797654 else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))798655 /* 600e/x, 770e, 770x */799799- video_supported = VIDEO_770;656656+ video_supported = IBMACPI_VIDEO_770;800657 else801658 /* all others */802802- video_supported = VIDEO_NEW;659659+ video_supported = IBMACPI_VIDEO_NEW;803660804661 return 0;805662}···809666 int status = 0;810667 int i;811668812812- if (video_supported == VIDEO_570) {669669+ if (video_supported == IBMACPI_VIDEO_570) {813670 if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87))814671 status = i & 3;815815- } else if (video_supported == VIDEO_770) {672672+ } else if (video_supported == IBMACPI_VIDEO_770) {816673 if (acpi_evalf(NULL, &i, "\\VCDL", "d"))817674 status |= 0x01 * i;818675 if (acpi_evalf(NULL, &i, "\\VCDC", "d"))819676 status |= 0x02 * i;820820- } else if (video_supported == VIDEO_NEW) {677677+ } else if (video_supported == IBMACPI_VIDEO_NEW) {821678 acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1);822679 if (acpi_evalf(NULL, &i, "\\VCDC", "d"))823680 status |= 0x02 * i;···836693{837694 int autosw = 0;838695839839- if (video_supported == VIDEO_570)696696+ if (video_supported == IBMACPI_VIDEO_570)840697 acpi_evalf(vid_handle, &autosw, "SWIT", "d");841841- else if (video_supported == VIDEO_770 || video_supported == VIDEO_NEW)698698+ else if (video_supported == IBMACPI_VIDEO_770 ||699699+ video_supported == IBMACPI_VIDEO_NEW)842700 acpi_evalf(vid_handle, &autosw, "^VDEE", "d");843701844702 return autosw & 1;···859715 len += sprintf(p + len, "status:\t\tsupported\n");860716 len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));861717 len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));862862- if (video_supported == VIDEO_NEW)718718+ if (video_supported == IBMACPI_VIDEO_NEW)863719 len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));864720 len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));865721 len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");866722 len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");867867- if (video_supported == VIDEO_NEW)723723+ if (video_supported == IBMACPI_VIDEO_NEW)868724 len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");869725 len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");870726 len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");···879735880736 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))881737 return -EIO;882882- ret = video_supported == VIDEO_570 ?738738+ ret = video_supported == IBMACPI_VIDEO_570 ?883739 acpi_evalf(ec_handle, NULL, "_Q16", "v") :884740 acpi_evalf(vid_handle, NULL, "VSWT", "v");885741 acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw);···889745890746static int video_expand(void)891747{892892- if (video_supported == VIDEO_570)748748+ if (video_supported == IBMACPI_VIDEO_570)893749 return acpi_evalf(ec_handle, NULL, "_Q17", "v");894894- else if (video_supported == VIDEO_770)750750+ else if (video_supported == IBMACPI_VIDEO_770)895751 return acpi_evalf(vid_handle, NULL, "VEXP", "v");896752 else897753 return acpi_evalf(NULL, NULL, "\\VEXP", "v");···901757{902758 int ret;903759904904- if (video_supported == VIDEO_570) {760760+ if (video_supported == IBMACPI_VIDEO_570) {905761 ret = acpi_evalf(NULL, NULL,906762 "\\_SB.PHS2", "vdd", 0x8b, status | 0x80);907907- } else if (video_supported == VIDEO_770) {763763+ } else if (video_supported == IBMACPI_VIDEO_770) {908764 int autosw = video_autosw();909765 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))910766 return -EIO;···940796 enable |= 0x02;941797 } else if (strlencmp(cmd, "crt_disable") == 0) {942798 disable |= 0x02;943943- } else if (video_supported == VIDEO_NEW &&799799+ } else if (video_supported == IBMACPI_VIDEO_NEW &&944800 strlencmp(cmd, "dvi_enable") == 0) {945801 enable |= 0x08;946946- } else if (video_supported == VIDEO_NEW &&802802+ } else if (video_supported == IBMACPI_VIDEO_NEW &&947803 strlencmp(cmd, "dvi_disable") == 0) {948804 disable |= 0x08;949805 } else if (strlencmp(cmd, "auto_enable") == 0) {···1042898 return 0;1043899}1044900901901+#if defined(CONFIG_ACPI_IBM_DOCK) || defined(CONFIG_ACPI_IBM_BAY)1045902static int _sta(acpi_handle handle)1046903{1047904 int status;···10529071053908 return status;1054909}910910+#endif1055911#ifdef CONFIG_ACPI_IBM_DOCK1056912#define dock_docked() (_sta(dock_handle) & 1)1057913···1118972}1119973#endif1120974975975+#ifdef CONFIG_ACPI_IBM_BAY1121976static int bay_status_supported;1122977static int bay_status2_supported;1123978static int bay_eject_supported;···11941047{11951048 acpi_bus_generate_event(ibm->device, event, 0);11961049}10501050+#endif1197105111981052static int cmos_read(char *p)11991053{···12421094 return 0;12431095}1244109612451245-static int led_supported;12461246-12471247-#define LED_570 112481248-#define LED_OLD 212491249-#define LED_NEW 310971097+enum led_access_mode {10981098+ IBMACPI_LED_NONE = 0,10991099+ IBMACPI_LED_570, /* 570 */11001100+ IBMACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */11011101+ IBMACPI_LED_NEW, /* all others */11021102+};11031103+static enum led_access_mode led_supported;1250110412511105static int led_init(void)12521106{12531107 if (!led_handle)12541108 /* led not supported on R30, R31 */12551255- led_supported = 0;11091109+ led_supported = IBMACPI_LED_NONE;12561110 else if (strlencmp(led_path, "SLED") == 0)12571111 /* 570 */12581258- led_supported = LED_570;11121112+ led_supported = IBMACPI_LED_570;12591113 else if (strlencmp(led_path, "SYSL") == 0)12601114 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */12611261- led_supported = LED_OLD;11151115+ led_supported = IBMACPI_LED_OLD;12621116 else12631117 /* all others */12641264- led_supported = LED_NEW;11181118+ led_supported = IBMACPI_LED_NEW;1265111912661120 return 0;12671121}···12801130 }12811131 len += sprintf(p + len, "status:\t\tsupported\n");1282113212831283- if (led_supported == LED_570) {11331133+ if (led_supported == IBMACPI_LED_570) {12841134 /* 570 */12851135 int i, status;12861136 for (i = 0; i < 8; i++) {···13291179 } else13301180 return -EINVAL;1331118113321332- if (led_supported == LED_570) {11821182+ if (led_supported == IBMACPI_LED_570) {13331183 /* 570 */13341184 led = 1 << led;13351185 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",13361186 led, led_sled_arg1[ind]))13371187 return -EIO;13381338- } else if (led_supported == LED_OLD) {11881188+ } else if (led_supported == IBMACPI_LED_OLD) {13391189 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */13401190 led = 1 << led;13411191 ret = ec_write(EC_HLMS, led);···14221272 return 1;14231273}1424127414251425-static int thermal_tmp_supported;14261426-static int thermal_updt_supported;12751275+static enum thermal_access_mode thermal_read_mode;1427127614281277static int thermal_init(void)14291278{14301430- /* temperatures not supported on 570, G4x, R30, R31, R32 */14311431- thermal_tmp_supported = acpi_evalf(ec_handle, NULL, "TMP7", "qv");12791279+ u8 t, ta1, ta2;12801280+ int i;12811281+ int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");1432128214331433- /* 600e/x, 770e, 770x */14341434- thermal_updt_supported = acpi_evalf(ec_handle, NULL, "UPDT", "qv");12831283+ if (ibm_thinkpad_ec_found && experimental) {12841284+ /*12851285+ * Direct EC access mode: sensors at registers12861286+ * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for12871287+ * non-implemented, thermal sensors return 0x80 when12881288+ * not available12891289+ */12901290+12911291+ ta1 = ta2 = 0;12921292+ for (i = 0; i < 8; i++) {12931293+ if (likely(acpi_ec_read(0x78 + i, &t))) {12941294+ ta1 |= t;12951295+ } else {12961296+ ta1 = 0;12971297+ break;12981298+ }12991299+ if (likely(acpi_ec_read(0xC0 + i, &t))) {13001300+ ta2 |= t;13011301+ } else {13021302+ ta1 = 0;13031303+ break;13041304+ }13051305+ }13061306+ if (ta1 == 0) {13071307+ /* This is sheer paranoia, but we handle it anyway */13081308+ if (acpi_tmp7) {13091309+ printk(IBM_ERR13101310+ "ThinkPad ACPI EC access misbehaving, "13111311+ "falling back to ACPI TMPx access mode\n");13121312+ thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07;13131313+ } else {13141314+ printk(IBM_ERR13151315+ "ThinkPad ACPI EC access misbehaving, "13161316+ "disabling thermal sensors access\n");13171317+ thermal_read_mode = IBMACPI_THERMAL_NONE;13181318+ }13191319+ } else {13201320+ thermal_read_mode =13211321+ (ta2 != 0) ?13221322+ IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8;13231323+ }13241324+ } else if (acpi_tmp7) {13251325+ if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {13261326+ /* 600e/x, 770e, 770x */13271327+ thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT;13281328+ } else {13291329+ /* Standard ACPI TMPx access, max 8 sensors */13301330+ thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07;13311331+ }13321332+ } else {13331333+ /* temperatures not supported on 570, G4x, R30, R31, R32 */13341334+ thermal_read_mode = IBMACPI_THERMAL_NONE;13351335+ }1435133614361337 return 0;13381338+}13391339+13401340+static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)13411341+{13421342+ int i, t;13431343+ s8 tmp;13441344+ char tmpi[] = "TMPi";13451345+13461346+ if (!s)13471347+ return -EINVAL;13481348+13491349+ switch (thermal_read_mode) {13501350+#if IBMACPI_MAX_THERMAL_SENSORS >= 1613511351+ case IBMACPI_THERMAL_TPEC_16:13521352+ for (i = 0; i < 8; i++) {13531353+ if (!acpi_ec_read(0xC0 + i, &tmp))13541354+ return -EIO;13551355+ s->temp[i + 8] = tmp * 1000;13561356+ }13571357+ /* fallthrough */13581358+#endif13591359+ case IBMACPI_THERMAL_TPEC_8:13601360+ for (i = 0; i < 8; i++) {13611361+ if (!acpi_ec_read(0x78 + i, &tmp))13621362+ return -EIO;13631363+ s->temp[i] = tmp * 1000;13641364+ }13651365+ return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8;13661366+13671367+ case IBMACPI_THERMAL_ACPI_UPDT:13681368+ if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))13691369+ return -EIO;13701370+ for (i = 0; i < 8; i++) {13711371+ tmpi[3] = '0' + i;13721372+ if (!acpi_evalf(ec_handle, &t, tmpi, "d"))13731373+ return -EIO;13741374+ s->temp[i] = (t - 2732) * 100;13751375+ }13761376+ return 8;13771377+13781378+ case IBMACPI_THERMAL_ACPI_TMP07:13791379+ for (i = 0; i < 8; i++) {13801380+ tmpi[3] = '0' + i;13811381+ if (!acpi_evalf(ec_handle, &t, tmpi, "d"))13821382+ return -EIO;13831383+ s->temp[i] = t * 1000;13841384+ }13851385+ return 8;13861386+13871387+ case IBMACPI_THERMAL_NONE:13881388+ default:13891389+ return 0;13901390+ }14371391}1438139214391393static int thermal_read(char *p)14401394{14411395 int len = 0;13961396+ int n, i;13971397+ struct ibm_thermal_sensors_struct t;1442139814431443- if (!thermal_tmp_supported)14441444- len += sprintf(p + len, "temperatures:\tnot supported\n");14451445- else {14461446- int i, t;14471447- char tmpi[] = "TMPi";14481448- s8 tmp[8];13991399+ n = thermal_get_sensors(&t);14001400+ if (unlikely(n < 0))14011401+ return n;1449140214501450- if (thermal_updt_supported)14511451- if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))14521452- return -EIO;14031403+ len += sprintf(p + len, "temperatures:\t");1453140414541454- for (i = 0; i < 8; i++) {14551455- tmpi[3] = '0' + i;14561456- if (!acpi_evalf(ec_handle, &t, tmpi, "d"))14571457- return -EIO;14581458- if (thermal_updt_supported)14591459- tmp[i] = (t - 2732 + 5) / 10;14601460- else14611461- tmp[i] = t;14621462- }14631463-14641464- len += sprintf(p + len,14651465- "temperatures:\t%d %d %d %d %d %d %d %d\n",14661466- tmp[0], tmp[1], tmp[2], tmp[3],14671467- tmp[4], tmp[5], tmp[6], tmp[7]);14681468- }14051405+ if (n > 0) {14061406+ for (i = 0; i < (n - 1); i++)14071407+ len += sprintf(p + len, "%d ", t.temp[i] / 1000);14081408+ len += sprintf(p + len, "%d\n", t.temp[i] / 1000);14091409+ } else14101410+ len += sprintf(p + len, "not supported\n");1469141114701412 return len;14711413}···1623138116241382static int brightness_offset = 0x31;1625138313841384+static int brightness_get(struct backlight_device *bd)13851385+{13861386+ u8 level;13871387+ if (!acpi_ec_read(brightness_offset, &level))13881388+ return -EIO;13891389+13901390+ level &= 0x7;13911391+13921392+ return level;13931393+}13941394+16261395static int brightness_read(char *p)16271396{16281397 int len = 0;16291629- u8 level;13981398+ int level;1630139916311631- if (!acpi_ec_read(brightness_offset, &level)) {14001400+ if ((level = brightness_get(NULL)) < 0) {16321401 len += sprintf(p + len, "level:\t\tunreadable\n");16331402 } else {16341403 len += sprintf(p + len, "level:\t\t%d\n", level & 0x7);···16541401#define BRIGHTNESS_UP 416551402#define BRIGHTNESS_DOWN 51656140316571657-static int brightness_write(char *buf)14041404+static int brightness_set(int value)16581405{16591406 int cmos_cmd, inc, i;16601660- u8 level;14071407+ int current_value = brightness_get(NULL);14081408+14091409+ value &= 7;14101410+14111411+ cmos_cmd = value > current_value ? BRIGHTNESS_UP : BRIGHTNESS_DOWN;14121412+ inc = value > current_value ? 1 : -1;14131413+ for (i = current_value; i != value; i += inc) {14141414+ if (!cmos_eval(cmos_cmd))14151415+ return -EIO;14161416+ if (!acpi_ec_write(brightness_offset, i + inc))14171417+ return -EIO;14181418+ }14191419+14201420+ return 0;14211421+}14221422+14231423+static int brightness_write(char *buf)14241424+{14251425+ int level;16611426 int new_level;16621427 char *cmd;1663142816641429 while ((cmd = next_cmd(&buf))) {16651665- if (!acpi_ec_read(brightness_offset, &level))16661666- return -EIO;14301430+ if ((level = brightness_get(NULL)) < 0)14311431+ return level;16671432 level &= 7;1668143316691434 if (strlencmp(cmd, "up") == 0) {···16941423 } else16951424 return -EINVAL;1696142516971697- cmos_cmd = new_level > level ? BRIGHTNESS_UP : BRIGHTNESS_DOWN;16981698- inc = new_level > level ? 1 : -1;16991699- for (i = level; i != new_level; i += inc) {17001700- if (!cmos_eval(cmos_cmd))17011701- return -EIO;17021702- if (!acpi_ec_write(brightness_offset, i + inc))17031703- return -EIO;17041704- }14261426+ brightness_set(new_level);17051427 }1706142817071429 return 0;14301430+}14311431+14321432+static int brightness_update_status(struct backlight_device *bd)14331433+{14341434+ return brightness_set(bd->props->brightness);14351435+}14361436+14371437+static struct backlight_properties ibm_backlight_data = {14381438+ .owner = THIS_MODULE,14391439+ .get_brightness = brightness_get,14401440+ .update_status = brightness_update_status,14411441+ .max_brightness = 7,14421442+};14431443+14441444+static int brightness_init(void)14451445+{14461446+ ibm_backlight_device = backlight_device_register("ibm", NULL,14471447+ &ibm_backlight_data);14481448+ if (IS_ERR(ibm_backlight_device)) {14491449+ printk(IBM_ERR "Could not register backlight device\n");14501450+ return PTR_ERR(ibm_backlight_device);14511451+ }14521452+14531453+ return 0;14541454+}14551455+14561456+static void brightness_exit(void)14571457+{14581458+ if (ibm_backlight_device) {14591459+ backlight_device_unregister(ibm_backlight_device);14601460+ ibm_backlight_device = NULL;14611461+ }17081462}1709146317101464static int volume_offset = 0x30;···18181522 return 0;18191523}1820152418211821-static int fan_status_offset = 0x2f;18221822-static int fan_rpm_offset = 0x84;15251525+static enum fan_status_access_mode fan_status_access_mode;15261526+static enum fan_control_access_mode fan_control_access_mode;15271527+static enum fan_control_commands fan_control_commands;15281528+15291529+static int fan_control_status_known;15301530+static u8 fan_control_initial_status;15311531+15321532+static void fan_watchdog_fire(void *ignored);15331533+static int fan_watchdog_maxinterval;15341534+static DECLARE_WORK(fan_watchdog_task, fan_watchdog_fire, NULL);15351535+15361536+static int fan_init(void)15371537+{15381538+ fan_status_access_mode = IBMACPI_FAN_NONE;15391539+ fan_control_access_mode = IBMACPI_FAN_WR_NONE;15401540+ fan_control_commands = 0;15411541+ fan_control_status_known = 1;15421542+ fan_watchdog_maxinterval = 0;15431543+15441544+ if (gfan_handle) {15451545+ /* 570, 600e/x, 770e, 770x */15461546+ fan_status_access_mode = IBMACPI_FAN_RD_ACPI_GFAN;15471547+ } else {15481548+ /* all other ThinkPads: note that even old-style15491549+ * ThinkPad ECs supports the fan control register */15501550+ if (likely(acpi_ec_read(fan_status_offset,15511551+ &fan_control_initial_status))) {15521552+ fan_status_access_mode = IBMACPI_FAN_RD_TPEC;15531553+15541554+ /* In some ThinkPads, neither the EC nor the ACPI15551555+ * DSDT initialize the fan status, and it ends up15561556+ * being set to 0x07 when it *could* be either15571557+ * 0x07 or 0x80.15581558+ *15591559+ * Enable for TP-1Y (T43), TP-78 (R51e),15601560+ * TP-76 (R52), TP-70 (T43, R52), which are known15611561+ * to be buggy. */15621562+ if (fan_control_initial_status == 0x07 &&15631563+ ibm_thinkpad_ec_found &&15641564+ ((ibm_thinkpad_ec_found[0] == '1' &&15651565+ ibm_thinkpad_ec_found[1] == 'Y') ||15661566+ (ibm_thinkpad_ec_found[0] == '7' &&15671567+ (ibm_thinkpad_ec_found[1] == '6' ||15681568+ ibm_thinkpad_ec_found[1] == '8' ||15691569+ ibm_thinkpad_ec_found[1] == '0'))15701570+ )) {15711571+ printk(IBM_NOTICE15721572+ "fan_init: initial fan status is "15731573+ "unknown, assuming it is in auto "15741574+ "mode\n");15751575+ fan_control_status_known = 0;15761576+ }15771577+ } else {15781578+ printk(IBM_ERR15791579+ "ThinkPad ACPI EC access misbehaving, "15801580+ "fan status and control unavailable\n");15811581+ return 0;15821582+ }15831583+ }15841584+15851585+ if (sfan_handle) {15861586+ /* 570, 770x-JL */15871587+ fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN;15881588+ fan_control_commands |=15891589+ IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE;15901590+ } else {15911591+ if (!gfan_handle) {15921592+ /* gfan without sfan means no fan control */15931593+ /* all other models implement TP EC 0x2f control */15941594+15951595+ if (fans_handle) {15961596+ /* X31, X40, X41 */15971597+ fan_control_access_mode =15981598+ IBMACPI_FAN_WR_ACPI_FANS;15991599+ fan_control_commands |=16001600+ IBMACPI_FAN_CMD_SPEED |16011601+ IBMACPI_FAN_CMD_LEVEL |16021602+ IBMACPI_FAN_CMD_ENABLE;16031603+ } else {16041604+ fan_control_access_mode = IBMACPI_FAN_WR_TPEC;16051605+ fan_control_commands |=16061606+ IBMACPI_FAN_CMD_LEVEL |16071607+ IBMACPI_FAN_CMD_ENABLE;16081608+ }16091609+ }16101610+ }16111611+16121612+ return 0;16131613+}16141614+16151615+static int fan_get_status(u8 *status)16161616+{16171617+ u8 s;16181618+16191619+ /* TODO:16201620+ * Add IBMACPI_FAN_RD_ACPI_FANS ? */16211621+16221622+ switch (fan_status_access_mode) {16231623+ case IBMACPI_FAN_RD_ACPI_GFAN:16241624+ /* 570, 600e/x, 770e, 770x */16251625+16261626+ if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))16271627+ return -EIO;16281628+16291629+ if (likely(status))16301630+ *status = s & 0x07;16311631+16321632+ break;16331633+16341634+ case IBMACPI_FAN_RD_TPEC:16351635+ /* all except 570, 600e/x, 770e, 770x */16361636+ if (unlikely(!acpi_ec_read(fan_status_offset, &s)))16371637+ return -EIO;16381638+16391639+ if (likely(status))16401640+ *status = s;16411641+16421642+ break;16431643+16441644+ default:16451645+ return -ENXIO;16461646+ }16471647+16481648+ return 0;16491649+}16501650+16511651+static int fan_get_speed(unsigned int *speed)16521652+{16531653+ u8 hi, lo;16541654+16551655+ switch (fan_status_access_mode) {16561656+ case IBMACPI_FAN_RD_TPEC:16571657+ /* all except 570, 600e/x, 770e, 770x */16581658+ if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||16591659+ !acpi_ec_read(fan_rpm_offset + 1, &hi)))16601660+ return -EIO;16611661+16621662+ if (likely(speed))16631663+ *speed = (hi << 8) | lo;16641664+16651665+ break;16661666+16671667+ default:16681668+ return -ENXIO;16691669+ }16701670+16711671+ return 0;16721672+}16731673+16741674+static void fan_exit(void)16751675+{16761676+ cancel_delayed_work(&fan_watchdog_task);16771677+ flush_scheduled_work();16781678+}16791679+16801680+static void fan_watchdog_reset(void)16811681+{16821682+ static int fan_watchdog_active = 0;16831683+16841684+ if (fan_watchdog_active)16851685+ cancel_delayed_work(&fan_watchdog_task);16861686+16871687+ if (fan_watchdog_maxinterval > 0) {16881688+ fan_watchdog_active = 1;16891689+ if (!schedule_delayed_work(&fan_watchdog_task,16901690+ msecs_to_jiffies(fan_watchdog_maxinterval16911691+ * 1000))) {16921692+ printk(IBM_ERR "failed to schedule the fan watchdog, "16931693+ "watchdog will not trigger\n");16941694+ }16951695+ } else16961696+ fan_watchdog_active = 0;16971697+}1823169818241699static int fan_read(char *p)18251700{18261701 int len = 0;18271827- int s;18281828- u8 lo, hi, status;17021702+ int rc;17031703+ u8 status;17041704+ unsigned int speed = 0;1829170518301830- if (gfan_handle) {17061706+ switch (fan_status_access_mode) {17071707+ case IBMACPI_FAN_RD_ACPI_GFAN:18311708 /* 570, 600e/x, 770e, 770x */18321832- if (!acpi_evalf(gfan_handle, &s, NULL, "d"))18331833- return -EIO;17091709+ if ((rc = fan_get_status(&status)) < 0)17101710+ return rc;1834171118351835- len += sprintf(p + len, "level:\t\t%d\n", s);18361836- } else {17121712+ len += sprintf(p + len, "status:\t\t%s\n"17131713+ "level:\t\t%d\n",17141714+ (status != 0) ? "enabled" : "disabled", status);17151715+ break;17161716+17171717+ case IBMACPI_FAN_RD_TPEC:18371718 /* all except 570, 600e/x, 770e, 770x */18381838- if (!acpi_ec_read(fan_status_offset, &status))18391839- len += sprintf(p + len, "status:\t\tunreadable\n");18401840- else18411841- len += sprintf(p + len, "status:\t\t%s\n",18421842- enabled(status, 7));17191719+ if ((rc = fan_get_status(&status)) < 0)17201720+ return rc;1843172118441844- if (!acpi_ec_read(fan_rpm_offset, &lo) ||18451845- !acpi_ec_read(fan_rpm_offset + 1, &hi))18461846- len += sprintf(p + len, "speed:\t\tunreadable\n");17221722+ if (unlikely(!fan_control_status_known)) {17231723+ if (status != fan_control_initial_status)17241724+ fan_control_status_known = 1;17251725+ else17261726+ /* Return most likely status. In fact, it17271727+ * might be the only possible status */17281728+ status = IBMACPI_FAN_EC_AUTO;17291729+ }17301730+17311731+ len += sprintf(p + len, "status:\t\t%s\n",17321732+ (status != 0) ? "enabled" : "disabled");17331733+17341734+ /* No ThinkPad boots on disengaged mode, we can safely17351735+ * assume the tachometer is online if fan control status17361736+ * was unknown */17371737+ if ((rc = fan_get_speed(&speed)) < 0)17381738+ return rc;17391739+17401740+ len += sprintf(p + len, "speed:\t\t%d\n", speed);17411741+17421742+ if (status & IBMACPI_FAN_EC_DISENGAGED)17431743+ /* Disengaged mode takes precedence */17441744+ len += sprintf(p + len, "level:\t\tdisengaged\n");17451745+ else if (status & IBMACPI_FAN_EC_AUTO)17461746+ len += sprintf(p + len, "level:\t\tauto\n");18471747 else18481848- len += sprintf(p + len, "speed:\t\t%d\n",18491849- (hi << 8) + lo);17481748+ len += sprintf(p + len, "level:\t\t%d\n", status);17491749+ break;17501750+17511751+ case IBMACPI_FAN_NONE:17521752+ default:17531753+ len += sprintf(p + len, "status:\t\tnot supported\n");18501754 }1851175518521852- if (sfan_handle)18531853- /* 570, 770x-JL */18541854- len += sprintf(p + len, "commands:\tlevel <level>"18551855- " (<level> is 0-7)\n");18561856- if (!gfan_handle)18571857- /* all except 570, 600e/x, 770e, 770x */18581858- len += sprintf(p + len, "commands:\tenable, disable\n");18591859- if (fans_handle)18601860- /* X31, X40 */17561756+ if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) {17571757+ len += sprintf(p + len, "commands:\tlevel <level>");17581758+17591759+ switch (fan_control_access_mode) {17601760+ case IBMACPI_FAN_WR_ACPI_SFAN:17611761+ len += sprintf(p + len, " (<level> is 0-7)\n");17621762+ break;17631763+17641764+ default:17651765+ len += sprintf(p + len, " (<level> is 0-7, "17661766+ "auto, disengaged)\n");17671767+ break;17681768+ }17691769+ }17701770+17711771+ if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE)17721772+ len += sprintf(p + len, "commands:\tenable, disable\n"17731773+ "commands:\twatchdog <timeout> (<timeout> is 0 (off), "17741774+ "1-120 (seconds))\n");17751775+17761776+ if (fan_control_commands & IBMACPI_FAN_CMD_SPEED)18611777 len += sprintf(p + len, "commands:\tspeed <speed>"18621778 " (<speed> is 0-65535)\n");1863177918641780 return len;18651781}1866178218671867-static int fan_write(char *buf)17831783+static int fan_set_level(int level)18681784{18691869- char *cmd;18701870- int level, speed;18711871-18721872- while ((cmd = next_cmd(&buf))) {18731873- if (sfan_handle &&18741874- sscanf(cmd, "level %d", &level) == 1 &&18751875- level >= 0 && level <= 7) {18761876- /* 570, 770x-JL */17851785+ switch (fan_control_access_mode) {17861786+ case IBMACPI_FAN_WR_ACPI_SFAN:17871787+ if (level >= 0 && level <= 7) {18771788 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))18781789 return -EIO;18791879- } else if (!gfan_handle && strlencmp(cmd, "enable") == 0) {18801880- /* all except 570, 600e/x, 770e, 770x */18811881- if (!acpi_ec_write(fan_status_offset, 0x80))18821882- return -EIO;18831883- } else if (!gfan_handle && strlencmp(cmd, "disable") == 0) {18841884- /* all except 570, 600e/x, 770e, 770x */18851885- if (!acpi_ec_write(fan_status_offset, 0x00))18861886- return -EIO;18871887- } else if (fans_handle &&18881888- sscanf(cmd, "speed %d", &speed) == 1 &&18891889- speed >= 0 && speed <= 65535) {18901890- /* X31, X40 */17901790+ } else17911791+ return -EINVAL;17921792+ break;17931793+17941794+ case IBMACPI_FAN_WR_ACPI_FANS:17951795+ case IBMACPI_FAN_WR_TPEC:17961796+ if ((level != IBMACPI_FAN_EC_AUTO) &&17971797+ (level != IBMACPI_FAN_EC_DISENGAGED) &&17981798+ ((level < 0) || (level > 7)))17991799+ return -EINVAL;18001800+18011801+ if (!acpi_ec_write(fan_status_offset, level))18021802+ return -EIO;18031803+ else18041804+ fan_control_status_known = 1;18051805+ break;18061806+18071807+ default:18081808+ return -ENXIO;18091809+ }18101810+ return 0;18111811+}18121812+18131813+static int fan_set_enable(void)18141814+{18151815+ u8 s;18161816+ int rc;18171817+18181818+ switch (fan_control_access_mode) {18191819+ case IBMACPI_FAN_WR_ACPI_FANS:18201820+ case IBMACPI_FAN_WR_TPEC:18211821+ if ((rc = fan_get_status(&s)) < 0)18221822+ return rc;18231823+18241824+ /* Don't go out of emergency fan mode */18251825+ if (s != 7)18261826+ s = IBMACPI_FAN_EC_AUTO;18271827+18281828+ if (!acpi_ec_write(fan_status_offset, s))18291829+ return -EIO;18301830+ else18311831+ fan_control_status_known = 1;18321832+ break;18331833+18341834+ case IBMACPI_FAN_WR_ACPI_SFAN:18351835+ if ((rc = fan_get_status(&s)) < 0)18361836+ return rc;18371837+18381838+ s &= 0x07;18391839+18401840+ /* Set fan to at least level 4 */18411841+ if (s < 4)18421842+ s = 4;18431843+18441844+ if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))18451845+ return -EIO;18461846+ break;18471847+18481848+ default:18491849+ return -ENXIO;18501850+ }18511851+ return 0;18521852+}18531853+18541854+static int fan_set_disable(void)18551855+{18561856+ switch (fan_control_access_mode) {18571857+ case IBMACPI_FAN_WR_ACPI_FANS:18581858+ case IBMACPI_FAN_WR_TPEC:18591859+ if (!acpi_ec_write(fan_status_offset, 0x00))18601860+ return -EIO;18611861+ else18621862+ fan_control_status_known = 1;18631863+ break;18641864+18651865+ case IBMACPI_FAN_WR_ACPI_SFAN:18661866+ if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))18671867+ return -EIO;18681868+ break;18691869+18701870+ default:18711871+ return -ENXIO;18721872+ }18731873+ return 0;18741874+}18751875+18761876+static int fan_set_speed(int speed)18771877+{18781878+ switch (fan_control_access_mode) {18791879+ case IBMACPI_FAN_WR_ACPI_FANS:18801880+ if (speed >= 0 && speed <= 65535) {18911881 if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",18921882 speed, speed, speed))18931883 return -EIO;18941884 } else18951885 return -EINVAL;18861886+ break;18871887+18881888+ default:18891889+ return -ENXIO;18901890+ }18911891+ return 0;18921892+}18931893+18941894+static int fan_write_cmd_level(const char *cmd, int *rc)18951895+{18961896+ int level;18971897+18981898+ if (strlencmp(cmd, "level auto") == 0)18991899+ level = IBMACPI_FAN_EC_AUTO;19001900+ else if (strlencmp(cmd, "level disengaged") == 0)19011901+ level = IBMACPI_FAN_EC_DISENGAGED;19021902+ else if (sscanf(cmd, "level %d", &level) != 1)19031903+ return 0;19041904+19051905+ if ((*rc = fan_set_level(level)) == -ENXIO)19061906+ printk(IBM_ERR "level command accepted for unsupported "19071907+ "access mode %d", fan_control_access_mode);19081908+19091909+ return 1;19101910+}19111911+19121912+static int fan_write_cmd_enable(const char *cmd, int *rc)19131913+{19141914+ if (strlencmp(cmd, "enable") != 0)19151915+ return 0;19161916+19171917+ if ((*rc = fan_set_enable()) == -ENXIO)19181918+ printk(IBM_ERR "enable command accepted for unsupported "19191919+ "access mode %d", fan_control_access_mode);19201920+19211921+ return 1;19221922+}19231923+19241924+static int fan_write_cmd_disable(const char *cmd, int *rc)19251925+{19261926+ if (strlencmp(cmd, "disable") != 0)19271927+ return 0;19281928+19291929+ if ((*rc = fan_set_disable()) == -ENXIO)19301930+ printk(IBM_ERR "disable command accepted for unsupported "19311931+ "access mode %d", fan_control_access_mode);19321932+19331933+ return 1;19341934+}19351935+19361936+static int fan_write_cmd_speed(const char *cmd, int *rc)19371937+{19381938+ int speed;19391939+19401940+ /* TODO:19411941+ * Support speed <low> <medium> <high> ? */19421942+19431943+ if (sscanf(cmd, "speed %d", &speed) != 1)19441944+ return 0;19451945+19461946+ if ((*rc = fan_set_speed(speed)) == -ENXIO)19471947+ printk(IBM_ERR "speed command accepted for unsupported "19481948+ "access mode %d", fan_control_access_mode);19491949+19501950+ return 1;19511951+}19521952+19531953+static int fan_write_cmd_watchdog(const char *cmd, int *rc)19541954+{19551955+ int interval;19561956+19571957+ if (sscanf(cmd, "watchdog %d", &interval) != 1)19581958+ return 0;19591959+19601960+ if (interval < 0 || interval > 120)19611961+ *rc = -EINVAL;19621962+ else19631963+ fan_watchdog_maxinterval = interval;19641964+19651965+ return 1;19661966+}19671967+19681968+static int fan_write(char *buf)19691969+{19701970+ char *cmd;19711971+ int rc = 0;19721972+19731973+ while (!rc && (cmd = next_cmd(&buf))) {19741974+ if (!((fan_control_commands & IBMACPI_FAN_CMD_LEVEL) &&19751975+ fan_write_cmd_level(cmd, &rc)) &&19761976+ !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) &&19771977+ (fan_write_cmd_enable(cmd, &rc) ||19781978+ fan_write_cmd_disable(cmd, &rc) ||19791979+ fan_write_cmd_watchdog(cmd, &rc))) &&19801980+ !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) &&19811981+ fan_write_cmd_speed(cmd, &rc))19821982+ )19831983+ rc = -EINVAL;19841984+ else if (!rc)19851985+ fan_watchdog_reset();18961986 }1897198718981898- return 0;19881988+ return rc;19891989+}19901990+19911991+static void fan_watchdog_fire(void *ignored)19921992+{19931993+ printk(IBM_NOTICE "fan watchdog: enabling fan\n");19941994+ if (fan_set_enable()) {19951995+ printk(IBM_ERR "fan watchdog: error while enabling fan\n");19961996+ /* reschedule for later */19971997+ fan_watchdog_reset();19981998+ }18991999}1900200019012001static struct ibm_struct ibms[] = {···23541662 .type = ACPI_SYSTEM_NOTIFY,23551663 },23561664#endif16651665+#ifdef CONFIG_ACPI_IBM_BAY23571666 {23581667 .name = "bay",23591668 .init = bay_init,···23641671 .handle = &bay_handle,23651672 .type = ACPI_SYSTEM_NOTIFY,23661673 },16741674+#endif23671675 {23681676 .name = "cmos",23691677 .read = cmos_read,···23961702 .name = "brightness",23971703 .read = brightness_read,23981704 .write = brightness_write,17051705+ .init = brightness_init,17061706+ .exit = brightness_exit,23991707 },24001708 {24011709 .name = "volume",···24081712 .name = "fan",24091713 .read = fan_read,24101714 .write = fan_write,17151715+ .init = fan_init,17161716+ .exit = fan_exit,24111717 .experimental = 1,24121718 },24131719};···25231825 }2524182625251827 memset(ibm->driver, 0, sizeof(struct acpi_driver));25262526- sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name);18281828+ sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name);25271829 ibm->driver->ids = ibm->hid;25281830 ibm->driver->ops.add = &ibm_device_add;25291831···26521954#ifdef CONFIG_ACPI_IBM_DOCK26531955IBM_PARAM(dock);26541956#endif19571957+#ifdef CONFIG_ACPI_IBM_BAY26551958IBM_PARAM(bay);19591959+#endif26561960IBM_PARAM(cmos);26571961IBM_PARAM(led);26581962IBM_PARAM(beep);···26711971 ibm_exit(&ibms[i]);2672197226731973 remove_proc_entry(IBM_DIR, acpi_root_dir);19741974+19751975+ if (ibm_thinkpad_ec_found)19761976+ kfree(ibm_thinkpad_ec_found);19771977+}19781978+19791979+static char* __init check_dmi_for_ec(void)19801980+{19811981+ struct dmi_device *dev = NULL;19821982+ char ec_fw_string[18];19831983+19841984+ /*19851985+ * ThinkPad T23 or newer, A31 or newer, R50e or newer,19861986+ * X32 or newer, all Z series; Some models must have an19871987+ * up-to-date BIOS or they will not be detected.19881988+ *19891989+ * See http://thinkwiki.org/wiki/List_of_DMI_IDs19901990+ */19911991+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {19921992+ if (sscanf(dev->name,19931993+ "IBM ThinkPad Embedded Controller -[%17c",19941994+ ec_fw_string) == 1) {19951995+ ec_fw_string[sizeof(ec_fw_string) - 1] = 0;19961996+ ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;19971997+ return kstrdup(ec_fw_string, GFP_KERNEL);19981998+ }19991999+ }20002000+ return NULL;26742001}2675200226762003static int __init acpi_ibm_init(void)···27191992 return -ENODEV;27201993 }2721199419951995+ /* Models with newer firmware report the EC in DMI */19961996+ ibm_thinkpad_ec_found = check_dmi_for_ec();19971997+ if (ibm_thinkpad_ec_found)19981998+ printk(IBM_INFO "ThinkPad EC firmware %s\n",19991999+ ibm_thinkpad_ec_found);20002000+27222001 /* these handles are not required */27232002 IBM_HANDLE_INIT(vid);27242003 IBM_HANDLE_INIT(vid2);···27372004 IBM_HANDLE_INIT(dock);27382005#endif27392006 IBM_HANDLE_INIT(pci);20072007+#ifdef CONFIG_ACPI_IBM_BAY27402008 IBM_HANDLE_INIT(bay);27412009 if (bay_handle)27422010 IBM_HANDLE_INIT(bay_ej);27432011 IBM_HANDLE_INIT(bay2);27442012 if (bay2_handle)27452013 IBM_HANDLE_INIT(bay2_ej);20142014+#endif27462015 IBM_HANDLE_INIT(beep);27472016 IBM_HANDLE_INIT(ecrd);27482017 IBM_HANDLE_INIT(ecwr);
+65-23
drivers/acpi/toshiba_acpi.c
···4141#include <linux/init.h>4242#include <linux/types.h>4343#include <linux/proc_fs.h>4444+#include <linux/backlight.h>4545+4446#include <asm/uaccess.h>45474648#include <acpi/acpi_drivers.h>···212210}213211214212static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;213213+static struct backlight_device *toshiba_backlight_device;215214static int force_fan;216215static int last_key_event;217216static int key_event_valid;···274271 return result;275272}276273277277-static char *read_lcd(char *p)274274+static int get_lcd(struct backlight_device *bd)278275{279276 u32 hci_result;280277 u32 value;281278282279 hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);283280 if (hci_result == HCI_SUCCESS) {284284- value = value >> HCI_LCD_BRIGHTNESS_SHIFT;281281+ return (value >> HCI_LCD_BRIGHTNESS_SHIFT);282282+ } else283283+ return -EFAULT;284284+}285285+286286+static char *read_lcd(char *p)287287+{288288+ int value = get_lcd(NULL);289289+290290+ if (value >= 0) {285291 p += sprintf(p, "brightness: %d\n", value);286292 p += sprintf(p, "brightness_levels: %d\n",287293 HCI_LCD_BRIGHTNESS_LEVELS);···301289 return p;302290}303291292292+static int set_lcd(int value)293293+{294294+ u32 hci_result;295295+296296+ value = value << HCI_LCD_BRIGHTNESS_SHIFT;297297+ hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);298298+ if (hci_result != HCI_SUCCESS)299299+ return -EFAULT;300300+301301+ return 0;302302+}303303+304304+static int set_lcd_status(struct backlight_device *bd)305305+{306306+ return set_lcd(bd->props->brightness);307307+}308308+304309static unsigned long write_lcd(const char *buffer, unsigned long count)305310{306311 int value;307307- u32 hci_result;312312+ int ret = count;308313309314 if (sscanf(buffer, " brightness : %i", &value) == 1 &&310310- value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) {311311- value = value << HCI_LCD_BRIGHTNESS_SHIFT;312312- hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);313313- if (hci_result != HCI_SUCCESS)314314- return -EFAULT;315315- } else {316316- return -EINVAL;317317- }318318-319319- return count;315315+ value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS)316316+ ret = set_lcd(value);317317+ else318318+ ret = -EINVAL;319319+ return ret;320320}321321322322static char *read_video(char *p)···530506 return AE_OK;531507}532508509509+static struct backlight_properties toshiba_backlight_data = {510510+ .owner = THIS_MODULE,511511+ .get_brightness = get_lcd,512512+ .update_status = set_lcd_status,513513+ .max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1,514514+};515515+516516+static void __exit toshiba_acpi_exit(void)517517+{518518+ if (toshiba_backlight_device)519519+ backlight_device_unregister(toshiba_backlight_device);520520+521521+ remove_device();522522+523523+ if (toshiba_proc_dir)524524+ remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);525525+526526+ return;527527+}528528+533529static int __init toshiba_acpi_init(void)534530{535531 acpi_status status = AE_OK;···590546 remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);591547 }592548549549+ toshiba_backlight_device = backlight_device_register("toshiba", NULL,550550+ &toshiba_backlight_data);551551+ if (IS_ERR(toshiba_backlight_device)) {552552+ printk(KERN_ERR "Could not register toshiba backlight device\n");553553+ toshiba_backlight_device = NULL;554554+ toshiba_acpi_exit();555555+ }556556+593557 return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;594594-}595595-596596-static void __exit toshiba_acpi_exit(void)597597-{598598- remove_device();599599-600600- if (toshiba_proc_dir)601601- remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);602602-603603- return;604558}605559606560module_init(toshiba_acpi_init);