11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * i8k.c -- Linux driver for accessing the SMM BIOS on Dell laptops. 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * See http://www.debian.org/~dz/i8k/ for more information 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and for latest version of this driver. 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org> 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 8949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare * Hwmon integration: 9949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare * Copyright (C) 2011 Jean Delvare <khali@linux-fr.org> 10949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare * 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify it 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * under the terms of the GNU General Public License as published by the 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Free Software Foundation; either version 2, or (at your option) any 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * later version. 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful, but 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * WITHOUT ANY WARRANTY; without even the implied warranty of 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * General Public License for more details. 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h> 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/proc_fs.h> 26352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov#include <linux/seq_file.h> 27e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov#include <linux/dmi.h> 287e80d0d0b64f5c00b0ac7e623d96189309c298caAlexey Dobriyan#include <linux/capability.h> 29613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmann#include <linux/mutex.h> 30949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare#include <linux/hwmon.h> 31949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare#include <linux/hwmon-sysfs.h> 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h> 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h> 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/i8k.h> 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 37352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov#define I8K_VERSION "1.14 21/02/2005" 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_FN_STATUS 0x0025 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_POWER_STATUS 0x0069 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_SET_FAN 0x01a3 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_GET_FAN 0x00a3 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_GET_SPEED 0x02a3 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_GET_TEMP 0x10a3 457e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov#define I8K_SMM_GET_DELL_SIG1 0xfea3 467e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov#define I8K_SMM_GET_DELL_SIG2 0xffa3 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_SMM_BIOS_VERSION 0x00a6 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FAN_MULT 30 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_MAX_TEMP 127 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FN_NONE 0x00 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FN_UP 0x01 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FN_DOWN 0x02 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FN_MUTE 0x04 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FN_MASK 0x07 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_FN_SHIFT 8 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_POWER_AC 0x05 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_POWER_BATTERY 0x01 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I8K_TEMPERATURE_BUG 1 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 64613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmannstatic DEFINE_MUTEX(i8k_mutex); 65e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhovstatic char bios_version[4]; 66949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic struct device *i8k_hwmon_dev; 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops"); 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7290ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool force; 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(force, bool, 0); 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(force, "Force loading without checking for supported models"); 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7690ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool ignore_dmi; 77e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhovmodule_param(ignore_dmi, bool, 0); 78e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry TorokhovMODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match"); 79e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov 8090ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool restricted; 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(restricted, bool, 0); 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set"); 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8490ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool power_status; 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(power_status, bool, 0600); 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(power_status, "Report power status in /proc/i8k"); 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 884ed99a27d161ce6f1eb6657c5cd5e6aef365c665Jochen Eisingerstatic int fan_mult = I8K_FAN_MULT; 894ed99a27d161ce6f1eb6657c5cd5e6aef365c665Jochen Eisingermodule_param(fan_mult, int, 0); 904ed99a27d161ce6f1eb6657c5cd5e6aef365c665Jochen EisingerMODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with"); 914ed99a27d161ce6f1eb6657c5cd5e6aef365c665Jochen Eisinger 92352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhovstatic int i8k_open_fs(struct inode *inode, struct file *file); 93d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbeckerstatic long i8k_ioctl(struct file *, unsigned int, unsigned long); 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9562322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations i8k_fops = { 961b50221738108c438d5f25c7a043fb89e9e27044Denis V. Lunev .owner = THIS_MODULE, 97352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov .open = i8k_open_fs, 98352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov .read = seq_read, 99352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov .llseek = seq_lseek, 100352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov .release = single_release, 101d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker .unlocked_ioctl = i8k_ioctl, 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1048378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhovstruct smm_regs { 105dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov unsigned int eax; 106dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov unsigned int ebx __attribute__ ((packed)); 107dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov unsigned int ecx __attribute__ ((packed)); 108dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov unsigned int edx __attribute__ ((packed)); 109dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov unsigned int esi __attribute__ ((packed)); 110dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov unsigned int edi __attribute__ ((packed)); 1118378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov}; 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1131855256c497ecfefc730df6032243f26855ce52cJeff Garzikstatic inline const char *i8k_get_dmi_data(int field) 114e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov{ 1151855256c497ecfefc730df6032243f26855ce52cJeff Garzik const char *dmi_data = dmi_get_system_info(field); 1164f005551a8fac21b6fec8d10d57cd12d373d79e1Dmitry Torokhov 1174f005551a8fac21b6fec8d10d57cd12d373d79e1Dmitry Torokhov return dmi_data && *dmi_data ? dmi_data : "?"; 118e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov} 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard. 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1238378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhovstatic int i8k_smm(struct smm_regs *regs) 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 125dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov int rc; 126dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov int eax = regs->eax; 127dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 128fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith#if defined(CONFIG_X86_64) 12922d3243de86bc92d874abb7c5b185d5c47aba323Jim Bos asm volatile("pushq %%rax\n\t" 130fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "movl 0(%%rax),%%edx\n\t" 131fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "pushq %%rdx\n\t" 132fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "movl 4(%%rax),%%ebx\n\t" 133fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "movl 8(%%rax),%%ecx\n\t" 134fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "movl 12(%%rax),%%edx\n\t" 135fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "movl 16(%%rax),%%esi\n\t" 136fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "movl 20(%%rax),%%edi\n\t" 137fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "popq %%rax\n\t" 138fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "out %%al,$0xb2\n\t" 139fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "out %%al,$0x84\n\t" 140fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "xchgq %%rax,(%%rsp)\n\t" 141fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "movl %%ebx,4(%%rax)\n\t" 142fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "movl %%ecx,8(%%rax)\n\t" 143fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "movl %%edx,12(%%rax)\n\t" 144fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "movl %%esi,16(%%rax)\n\t" 145fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "movl %%edi,20(%%rax)\n\t" 146fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "popq %%rdx\n\t" 147fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "movl %%edx,0(%%rax)\n\t" 148bc1f419c76a2d6450413ce4349f4e4a07be011d5Luca Tettamanti "pushfq\n\t" 149bc1f419c76a2d6450413ce4349f4e4a07be011d5Luca Tettamanti "popq %%rax\n\t" 150fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith "andl $1,%%eax\n" 15122d3243de86bc92d874abb7c5b185d5c47aba323Jim Bos :"=a"(rc) 152fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith : "a"(regs) 153fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 154fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith#else 15522d3243de86bc92d874abb7c5b185d5c47aba323Jim Bos asm volatile("pushl %%eax\n\t" 156dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "movl 0(%%eax),%%edx\n\t" 157dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "push %%edx\n\t" 158dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "movl 4(%%eax),%%ebx\n\t" 159dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "movl 8(%%eax),%%ecx\n\t" 160dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "movl 12(%%eax),%%edx\n\t" 161dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "movl 16(%%eax),%%esi\n\t" 162dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "movl 20(%%eax),%%edi\n\t" 163dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "popl %%eax\n\t" 164dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "out %%al,$0xb2\n\t" 165dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "out %%al,$0x84\n\t" 166dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "xchgl %%eax,(%%esp)\n\t" 167dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "movl %%ebx,4(%%eax)\n\t" 168dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "movl %%ecx,8(%%eax)\n\t" 169dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "movl %%edx,12(%%eax)\n\t" 170dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "movl %%esi,16(%%eax)\n\t" 171dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "movl %%edi,20(%%eax)\n\t" 172dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "popl %%edx\n\t" 173dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "movl %%edx,0(%%eax)\n\t" 174dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "lahf\n\t" 175dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "shrl $8,%%eax\n\t" 1766b4e81db2552bad04100e7d5ddeed7e848f53b48Jim Bos "andl $1,%%eax\n" 17722d3243de86bc92d874abb7c5b185d5c47aba323Jim Bos :"=a"(rc) 178dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov : "a"(regs) 179dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 180fe04f22fd2bc84dfcc0ef1c7acb863bd98b9ac93Bradley Smith#endif 1818378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) 182dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -EINVAL; 183dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 184dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return 0; 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the bios version. Return the version as an integer corresponding 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to the ascii value, for example "A17" is returned as 0x00413137. 1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int i8k_get_bios_version(void) 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1938378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov struct smm_regs regs = { .eax = I8K_SMM_BIOS_VERSION, }; 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1958378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov return i8k_smm(®s) ? : regs.eax; 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the Fn key status. 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int i8k_get_fn_status(void) 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2038378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, }; 204dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov int rc; 205dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 2068378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if ((rc = i8k_smm(®s)) < 0) 207dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return rc; 208dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 209dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) { 210dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_FN_UP: 211dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return I8K_VOL_UP; 212dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_FN_DOWN: 213dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return I8K_VOL_DOWN; 214dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_FN_MUTE: 215dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return I8K_VOL_MUTE; 216dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov default: 217dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return 0; 218dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov } 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the power status. 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int i8k_get_power_status(void) 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2268378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, }; 227dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov int rc; 228dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 2298378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if ((rc = i8k_smm(®s)) < 0) 230dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return rc; 231dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 2328378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY; 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the fan status. 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int i8k_get_fan_status(int fan) 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2408378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, }; 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 242dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov regs.ebx = fan & 0xff; 2438378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov return i8k_smm(®s) ? : regs.eax & 0xff; 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the fan speed in RPM. 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int i8k_get_fan_speed(int fan) 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2518378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, }; 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 253dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov regs.ebx = fan & 0xff; 2544ed99a27d161ce6f1eb6657c5cd5e6aef365c665Jochen Eisinger return i8k_smm(®s) ? : (regs.eax & 0xffff) * fan_mult; 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Set the fan speed (off, low, high). Returns the new fan status. 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int i8k_set_fan(int fan, int speed) 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2628378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, }; 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 264dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov speed = (speed < 0) ? 0 : ((speed > I8K_FAN_MAX) ? I8K_FAN_MAX : speed); 265dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov regs.ebx = (fan & 0xff) | (speed << 8); 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2678378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov return i8k_smm(®s) ? : i8k_get_fan_status(fan); 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the cpu temperature. 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2737e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhovstatic int i8k_get_temp(int sensor) 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2758378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP, }; 276dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov int rc; 277dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov int temp; 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef I8K_TEMPERATURE_BUG 2808378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov static int prev; 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 2827e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov regs.ebx = sensor & 0xff; 2838378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if ((rc = i8k_smm(®s)) < 0) 284dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return rc; 2858378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov 286dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov temp = regs.eax & 0xff; 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef I8K_TEMPERATURE_BUG 289dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov /* 290dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * Sometimes the temperature sensor returns 0x99, which is out of range. 291dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * In this case we return (once) the previous cached value. For example: 292dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov # 1003655137 00000058 00005a4b 293dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees 294dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov # 1003655139 00000054 00005c52 295dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov */ 296dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov if (temp > I8K_MAX_TEMP) { 297dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov temp = prev; 298dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov prev = I8K_MAX_TEMP; 299dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov } else { 300dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov prev = temp; 301dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov } 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 304dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return temp; 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3077e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhovstatic int i8k_get_dell_signature(int req_fn) 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3097e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov struct smm_regs regs = { .eax = req_fn, }; 310dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov int rc; 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3128378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if ((rc = i8k_smm(®s)) < 0) 313dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return rc; 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3158378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1; 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 318d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbeckerstatic int 319d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbeckeri8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 321e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov int val = 0; 322dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov int speed; 323dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov unsigned char buff[16]; 324dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov int __user *argp = (int __user *)arg; 325dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 326dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov if (!argp) 327dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -EINVAL; 328dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 329dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov switch (cmd) { 330dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_BIOS_VERSION: 331dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov val = i8k_get_bios_version(); 332dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov break; 333dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 334dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_MACHINE_ID: 335dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov memset(buff, 0, 16); 336e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), sizeof(buff)); 337dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov break; 338dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 339dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_FN_STATUS: 340dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov val = i8k_get_fn_status(); 341dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov break; 342dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 343dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_POWER_STATUS: 344dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov val = i8k_get_power_status(); 345dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov break; 346dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 347dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_GET_TEMP: 3487e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov val = i8k_get_temp(0); 349dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov break; 350dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 351dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_GET_SPEED: 3528378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if (copy_from_user(&val, argp, sizeof(int))) 353dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -EFAULT; 3548378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov 355dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov val = i8k_get_fan_speed(val); 356dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov break; 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 358dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_GET_FAN: 3598378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if (copy_from_user(&val, argp, sizeof(int))) 360dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -EFAULT; 3618378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov 362dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov val = i8k_get_fan_status(val); 363dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov break; 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 365dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_SET_FAN: 3668378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if (restricted && !capable(CAP_SYS_ADMIN)) 367dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -EPERM; 3688378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov 3698378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if (copy_from_user(&val, argp, sizeof(int))) 370dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -EFAULT; 3718378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov 3728378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if (copy_from_user(&speed, argp + 1, sizeof(int))) 373dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -EFAULT; 3748378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov 375dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov val = i8k_set_fan(val, speed); 376dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov break; 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 378dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov default: 379dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -EINVAL; 3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3828378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if (val < 0) 383dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return val; 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 385dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov switch (cmd) { 386dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_BIOS_VERSION: 3878378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if (copy_to_user(argp, &val, 4)) 388dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -EFAULT; 3898378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov 390dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov break; 391dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov case I8K_MACHINE_ID: 3928378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if (copy_to_user(argp, buff, 16)) 393dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -EFAULT; 3948378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov 395dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov break; 396dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov default: 3978378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if (copy_to_user(argp, &val, sizeof(int))) 398dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -EFAULT; 3998378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov 400dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov break; 4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 403dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return 0; 4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 406d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbeckerstatic long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) 407d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker{ 408d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker long ret; 409d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker 410613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmann mutex_lock(&i8k_mutex); 411d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker ret = i8k_ioctl_unlocked(fp, cmd, arg); 412613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmann mutex_unlock(&i8k_mutex); 413d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker 414d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker return ret; 415d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker} 416d79b6f4de5db0103ceb4734e42ad101d836d61d9Frederic Weisbecker 4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Print the information for /proc/i8k. 4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 420352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhovstatic int i8k_proc_show(struct seq_file *seq, void *offset) 4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 422352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov int fn_key, cpu_temp, ac_power; 423dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov int left_fan, right_fan, left_speed, right_speed; 424dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 42596de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt cpu_temp = i8k_get_temp(0); /* 11100 µs */ 42696de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt left_fan = i8k_get_fan_status(I8K_FAN_LEFT); /* 580 µs */ 42796de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt right_fan = i8k_get_fan_status(I8K_FAN_RIGHT); /* 580 µs */ 42896de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt left_speed = i8k_get_fan_speed(I8K_FAN_LEFT); /* 580 µs */ 42996de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt right_speed = i8k_get_fan_speed(I8K_FAN_RIGHT); /* 580 µs */ 43096de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt fn_key = i8k_get_fn_status(); /* 750 µs */ 4318378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov if (power_status) 43296de0e252cedffad61b3cb5e05662c591898e69aJan Engelhardt ac_power = i8k_get_power_status(); /* 14700 µs */ 4338378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhov else 434dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov ac_power = -1; 435dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 436dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov /* 437dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * Info: 438dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * 439dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * 1) Format version (this will change if format changes) 440dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * 2) BIOS version 441dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * 3) BIOS machine ID 442dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * 4) Cpu temperature 443dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * 5) Left fan status 444dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * 6) Right fan status 445dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * 7) Left fan speed 446dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * 8) Right fan speed 447dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * 9) AC power 448dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * 10) Fn Key status 449dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov */ 450352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov return seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", 451352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov I8K_PROC_FMT, 452352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov bios_version, 4534f005551a8fac21b6fec8d10d57cd12d373d79e1Dmitry Torokhov i8k_get_dmi_data(DMI_PRODUCT_SERIAL), 454352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov cpu_temp, 455352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov left_fan, right_fan, left_speed, right_speed, 456352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov ac_power, fn_key); 4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 459352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhovstatic int i8k_open_fs(struct inode *inode, struct file *file) 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 461352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov return single_open(file, i8k_proc_show, NULL); 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 464949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 465949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare/* 466949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare * Hwmon interface 467949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare */ 468949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 469949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic ssize_t i8k_hwmon_show_temp(struct device *dev, 470949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare struct device_attribute *devattr, 471949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare char *buf) 472949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare{ 473949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare int cpu_temp; 474949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 475949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare cpu_temp = i8k_get_temp(0); 476949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (cpu_temp < 0) 477949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare return cpu_temp; 478949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare return sprintf(buf, "%d\n", cpu_temp * 1000); 479949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare} 480949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 481949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic ssize_t i8k_hwmon_show_fan(struct device *dev, 482949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare struct device_attribute *devattr, 483949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare char *buf) 484949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare{ 485949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare int index = to_sensor_dev_attr(devattr)->index; 486949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare int fan_speed; 487949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 488949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare fan_speed = i8k_get_fan_speed(index); 489949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (fan_speed < 0) 490949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare return fan_speed; 491949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare return sprintf(buf, "%d\n", fan_speed); 492949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare} 493949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 494949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic ssize_t i8k_hwmon_show_label(struct device *dev, 495949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare struct device_attribute *devattr, 496949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare char *buf) 497949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare{ 498949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare static const char *labels[4] = { 499949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare "i8k", 500949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare "CPU", 501949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare "Left Fan", 502949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare "Right Fan", 503949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare }; 504949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare int index = to_sensor_dev_attr(devattr)->index; 505949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 506949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare return sprintf(buf, "%s\n", labels[index]); 507949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare} 508949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 509949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL); 510949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL, 511949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare I8K_FAN_LEFT); 512949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL, 513949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare I8K_FAN_RIGHT); 514949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic SENSOR_DEVICE_ATTR(name, S_IRUGO, i8k_hwmon_show_label, NULL, 0); 515949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1); 516949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2); 517949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 3); 518949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 519949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic void i8k_hwmon_remove_files(struct device *dev) 520949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare{ 521949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare device_remove_file(dev, &dev_attr_temp1_input); 522949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare device_remove_file(dev, &sensor_dev_attr_fan1_input.dev_attr); 523949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare device_remove_file(dev, &sensor_dev_attr_fan2_input.dev_attr); 524949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare device_remove_file(dev, &sensor_dev_attr_temp1_label.dev_attr); 525949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare device_remove_file(dev, &sensor_dev_attr_fan1_label.dev_attr); 526949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare device_remove_file(dev, &sensor_dev_attr_fan2_label.dev_attr); 527949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare device_remove_file(dev, &sensor_dev_attr_name.dev_attr); 528949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare} 529949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 530949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic int __init i8k_init_hwmon(void) 531949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare{ 532949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare int err; 533949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 534949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare i8k_hwmon_dev = hwmon_device_register(NULL); 535949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (IS_ERR(i8k_hwmon_dev)) { 536949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare err = PTR_ERR(i8k_hwmon_dev); 537949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare i8k_hwmon_dev = NULL; 538949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare printk(KERN_ERR "i8k: hwmon registration failed (%d)\n", err); 539949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare return err; 540949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare } 541949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 542949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare /* Required name attribute */ 543949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare err = device_create_file(i8k_hwmon_dev, 544949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare &sensor_dev_attr_name.dev_attr); 545949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (err) 546949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare goto exit_unregister; 547949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 548949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare /* CPU temperature attributes, if temperature reading is OK */ 549949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare err = i8k_get_temp(0); 550949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (err < 0) { 551949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare dev_dbg(i8k_hwmon_dev, 552949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare "Not creating temperature attributes (%d)\n", err); 553949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare } else { 554949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare err = device_create_file(i8k_hwmon_dev, &dev_attr_temp1_input); 555949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (err) 556949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare goto exit_remove_files; 557949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare err = device_create_file(i8k_hwmon_dev, 558949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare &sensor_dev_attr_temp1_label.dev_attr); 559949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (err) 560949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare goto exit_remove_files; 561949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare } 562949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 563949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare /* Left fan attributes, if left fan is present */ 564949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare err = i8k_get_fan_status(I8K_FAN_LEFT); 565949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (err < 0) { 566949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare dev_dbg(i8k_hwmon_dev, 567949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare "Not creating %s fan attributes (%d)\n", "left", err); 568949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare } else { 569949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare err = device_create_file(i8k_hwmon_dev, 570949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare &sensor_dev_attr_fan1_input.dev_attr); 571949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (err) 572949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare goto exit_remove_files; 573949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare err = device_create_file(i8k_hwmon_dev, 574949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare &sensor_dev_attr_fan1_label.dev_attr); 575949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (err) 576949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare goto exit_remove_files; 577949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare } 578949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 579949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare /* Right fan attributes, if right fan is present */ 580949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare err = i8k_get_fan_status(I8K_FAN_RIGHT); 581949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (err < 0) { 582949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare dev_dbg(i8k_hwmon_dev, 583949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare "Not creating %s fan attributes (%d)\n", "right", err); 584949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare } else { 585949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare err = device_create_file(i8k_hwmon_dev, 586949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare &sensor_dev_attr_fan2_input.dev_attr); 587949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (err) 588949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare goto exit_remove_files; 589949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare err = device_create_file(i8k_hwmon_dev, 590949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare &sensor_dev_attr_fan2_label.dev_attr); 591949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (err) 592949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare goto exit_remove_files; 593949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare } 594949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 595949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare return 0; 596949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 597949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare exit_remove_files: 598949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare i8k_hwmon_remove_files(i8k_hwmon_dev); 599949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare exit_unregister: 600949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare hwmon_device_unregister(i8k_hwmon_dev); 601949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare return err; 602949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare} 603949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 604949a9d70020defd7c241607ab3ed037ea88f551cJean Delvarestatic void __exit i8k_exit_hwmon(void) 605949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare{ 606949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare i8k_hwmon_remove_files(i8k_hwmon_dev); 607949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare hwmon_device_unregister(i8k_hwmon_dev); 608949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare} 609949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 610e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhovstatic struct dmi_system_id __initdata i8k_dmi_table[] = { 611e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov { 612e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov .ident = "Dell Inspiron", 613e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov .matches = { 614e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), 615e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), 616e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov }, 617e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov }, 618e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov { 619e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov .ident = "Dell Latitude", 620e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov .matches = { 621e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), 622e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), 623e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov }, 624e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov }, 6257e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov { 6267e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov .ident = "Dell Inspiron 2", 6277e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov .matches = { 6287e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 6297e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), 6307e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov }, 6317e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov }, 6327e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov { 6337e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov .ident = "Dell Latitude 2", 6347e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov .matches = { 6357e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 6367e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), 6377e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov }, 6387e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov }, 639a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne { /* UK Inspiron 6400 */ 640a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne .ident = "Dell Inspiron 3", 641a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne .matches = { 642a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 643a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne DMI_MATCH(DMI_PRODUCT_NAME, "MM061"), 644a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne }, 645a9000d037d7dd08ac46168560b3a3d3bb743bfa6Nick Warne }, 64648103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson { 64748103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson .ident = "Dell Inspiron 3", 64848103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson .matches = { 64948103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 65048103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson DMI_MATCH(DMI_PRODUCT_NAME, "MP061"), 65148103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson }, 65248103c527b2fcf5ead13ef14b34eb8893eaec06aFrank Sorenson }, 6537ab21a8692094872298df172f54d55cba72fd308Andy Spencer { 6547ab21a8692094872298df172f54d55cba72fd308Andy Spencer .ident = "Dell Precision", 6557ab21a8692094872298df172f54d55cba72fd308Andy Spencer .matches = { 6567ab21a8692094872298df172f54d55cba72fd308Andy Spencer DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 6577ab21a8692094872298df172f54d55cba72fd308Andy Spencer DMI_MATCH(DMI_PRODUCT_NAME, "Precision"), 6587ab21a8692094872298df172f54d55cba72fd308Andy Spencer }, 6597ab21a8692094872298df172f54d55cba72fd308Andy Spencer }, 660bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz { 661bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz .ident = "Dell Vostro", 662bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz .matches = { 663bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 664bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"), 665bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz }, 666bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz }, 667bef2a508b4276fd7897b2cb27df037d26361842cFederico Heinz { } 668e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov}; 6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Probe for the presence of a supported laptop. 6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init i8k_probe(void) 6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 675dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov char buff[4]; 676dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov int version; 677dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 679dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * Get DMI information 6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 681e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov if (!dmi_check_system(i8k_dmi_table)) { 682e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov if (!ignore_dmi && !force) 683e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov return -ENODEV; 684e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov 685e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov printk(KERN_INFO "i8k: not running on a supported Dell system.\n"); 686dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov printk(KERN_INFO "i8k: vendor=%s, model=%s, version=%s\n", 687e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov i8k_get_dmi_data(DMI_SYS_VENDOR), 688e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov i8k_get_dmi_data(DMI_PRODUCT_NAME), 689e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov i8k_get_dmi_data(DMI_BIOS_VERSION)); 6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 691dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 692e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), sizeof(bios_version)); 693e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov 6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 695dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * Get SMM Dell signature 6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 6977e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) && 6987e0fa31dbf5968ce1e94f73c04a9402170432ecfDmitry Torokhov i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) { 699e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov printk(KERN_ERR "i8k: unable to get SMM Dell signature\n"); 700e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov if (!force) 701e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov return -ENODEV; 7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 704dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov /* 705dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * Get SMM BIOS version. 706dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov */ 707dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov version = i8k_get_bios_version(); 708dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov if (version <= 0) { 709e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov printk(KERN_WARNING "i8k: unable to get SMM BIOS version\n"); 710dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov } else { 711dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov buff[0] = (version >> 16) & 0xff; 712dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov buff[1] = (version >> 8) & 0xff; 713dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov buff[2] = (version) & 0xff; 714dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov buff[3] = '\0'; 715dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov /* 716dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * If DMI BIOS version is unknown use SMM BIOS version. 717dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov */ 718e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov if (!dmi_get_system_info(DMI_BIOS_VERSION)) 719e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov strlcpy(bios_version, buff, sizeof(bios_version)); 720e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov 721dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov /* 722dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov * Check if the two versions match. 723dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov */ 724e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov if (strncmp(buff, bios_version, sizeof(bios_version)) != 0) 725e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov printk(KERN_WARNING "i8k: BIOS version mismatch: %s != %s\n", 726e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov buff, bios_version); 727dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov } 7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 729dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return 0; 7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7328378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhovstatic int __init i8k_init(void) 7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 734dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov struct proc_dir_entry *proc_i8k; 735949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare int err; 736dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 737dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov /* Are we running on an supported laptop? */ 738e70c9d5e61c6cb2272c866fc1303e62975006752Dmitry Torokhov if (i8k_probe()) 739dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -ENODEV; 740dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 741dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov /* Register the proc entry */ 7421b50221738108c438d5f25c7a043fb89e9e27044Denis V. Lunev proc_i8k = proc_create("i8k", 0, NULL, &i8k_fops); 743352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov if (!proc_i8k) 744dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return -ENOENT; 745352f8f8bfbfb401c8af4c685beaafeb95c27fdd1Dmitry Torokhov 746949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare err = i8k_init_hwmon(); 747949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare if (err) 748949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare goto exit_remove_proc; 749949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 750dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov printk(KERN_INFO 751dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n", 752dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov I8K_VERSION); 753dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov 754dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov return 0; 755949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare 756949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare exit_remove_proc: 757949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare remove_proc_entry("i8k", NULL); 758949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare return err; 7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7618378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhovstatic void __exit i8k_exit(void) 7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 763949a9d70020defd7c241607ab3ed037ea88f551cJean Delvare i8k_exit_hwmon(); 764dec63ec32ea486ab915138e8790084c22a3f7bf6Dmitry Torokhov remove_proc_entry("i8k", NULL); 7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7678378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhovmodule_init(i8k_init); 7688378b92405dd606c6f3a0b1e303b67c8f8c9f743Dmitry Torokhovmodule_exit(i8k_exit); 769