12e97506aSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /* toshiba.c -- Linux driver for accessing the SMM on Toshiba laptops
31da177e4SLinus Torvalds *
41da177e4SLinus Torvalds * Copyright (c) 1996-2001 Jonathan A. Buzzard (jonathan@buzzard.org.uk)
51da177e4SLinus Torvalds *
61da177e4SLinus Torvalds * Valuable assistance and patches from:
71da177e4SLinus Torvalds * Tom May <tom@you-bastards.com>
81da177e4SLinus Torvalds * Rob Napier <rnapier@employees.org>
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * Fn status port numbers for machine ID's courtesy of
111da177e4SLinus Torvalds * 0xfc02: Scott Eisert <scott.e@sky-eye.com>
121da177e4SLinus Torvalds * 0xfc04: Steve VanDevender <stevev@efn.org>
131da177e4SLinus Torvalds * 0xfc08: Garth Berry <garth@itsbruce.net>
141da177e4SLinus Torvalds * 0xfc0a: Egbert Eich <eich@xfree86.org>
151da177e4SLinus Torvalds * 0xfc10: Andrew Lofthouse <Andrew.Lofthouse@robins.af.mil>
161da177e4SLinus Torvalds * 0xfc11: Spencer Olson <solson@novell.com>
171da177e4SLinus Torvalds * 0xfc13: Claudius Frankewitz <kryp@gmx.de>
181da177e4SLinus Torvalds * 0xfc15: Tom May <tom@you-bastards.com>
191da177e4SLinus Torvalds * 0xfc17: Dave Konrad <konrad@xenia.it>
201da177e4SLinus Torvalds * 0xfc1a: George Betzos <betzos@engr.colostate.edu>
211da177e4SLinus Torvalds * 0xfc1b: Munemasa Wada <munemasa@jnovel.co.jp>
221da177e4SLinus Torvalds * 0xfc1d: Arthur Liu <armie@slap.mine.nu>
231da177e4SLinus Torvalds * 0xfc5a: Jacques L'helgoualc'h <lhh@free.fr>
241da177e4SLinus Torvalds * 0xfcd1: Mr. Dave Konrad <konrad@xenia.it>
251da177e4SLinus Torvalds *
261da177e4SLinus Torvalds * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
271da177e4SLinus Torvalds *
281da177e4SLinus Torvalds * This code is covered by the GNU GPL and you are free to make any
291da177e4SLinus Torvalds * changes you wish to it under the terms of the license. However the
301da177e4SLinus Torvalds * code has the potential to render your computer and/or someone else's
311da177e4SLinus Torvalds * unusable. Please proceed with care when modifying the code.
321da177e4SLinus Torvalds *
331da177e4SLinus Torvalds * Note: Unfortunately the laptop hardware can close the System Configuration
341da177e4SLinus Torvalds * Interface on it's own accord. It is therefore necessary for *all*
351da177e4SLinus Torvalds * programs using this driver to be aware that *any* SCI call can fail at
361da177e4SLinus Torvalds * *any* time. It is up to any program to be aware of this eventuality
371da177e4SLinus Torvalds * and take appropriate steps.
381da177e4SLinus Torvalds *
391da177e4SLinus Torvalds * The information used to write this driver has been obtained by reverse
401da177e4SLinus Torvalds * engineering the software supplied by Toshiba for their portable computers in
411da177e4SLinus Torvalds * strict accordance with the European Council Directive 92/250/EEC on the legal
421da177e4SLinus Torvalds * protection of computer programs, and it's implementation into English Law by
431da177e4SLinus Torvalds * the Copyright (Computer Programs) Regulations 1992 (S.I. 1992 No.3233).
441da177e4SLinus Torvalds */
451da177e4SLinus Torvalds
461da177e4SLinus Torvalds #define TOSH_VERSION "1.11 26/9/2001"
471da177e4SLinus Torvalds #define TOSH_DEBUG 0
481da177e4SLinus Torvalds
491da177e4SLinus Torvalds #include <linux/module.h>
501da177e4SLinus Torvalds #include <linux/kernel.h>
511da177e4SLinus Torvalds #include <linux/types.h>
521da177e4SLinus Torvalds #include <linux/fcntl.h>
531da177e4SLinus Torvalds #include <linux/miscdevice.h>
541da177e4SLinus Torvalds #include <linux/ioport.h>
551da177e4SLinus Torvalds #include <asm/io.h>
567c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
571da177e4SLinus Torvalds #include <linux/init.h>
581da177e4SLinus Torvalds #include <linux/stat.h>
591da177e4SLinus Torvalds #include <linux/proc_fs.h>
60967bb77cSAlexey Dobriyan #include <linux/seq_file.h>
61613655faSArnd Bergmann #include <linux/mutex.h>
621da177e4SLinus Torvalds #include <linux/toshiba.h>
631da177e4SLinus Torvalds
643f5f7e2eSDmitry Torokhov MODULE_LICENSE("GPL");
653f5f7e2eSDmitry Torokhov MODULE_AUTHOR("Jonathan Buzzard <jonathan@buzzard.org.uk>");
663f5f7e2eSDmitry Torokhov MODULE_DESCRIPTION("Toshiba laptop SMM driver");
671da177e4SLinus Torvalds
68613655faSArnd Bergmann static DEFINE_MUTEX(tosh_mutex);
693f5f7e2eSDmitry Torokhov static int tosh_fn;
703f5f7e2eSDmitry Torokhov module_param_named(fn, tosh_fn, int, 0);
713f5f7e2eSDmitry Torokhov MODULE_PARM_DESC(fn, "User specified Fn key detection port");
721da177e4SLinus Torvalds
733f5f7e2eSDmitry Torokhov static int tosh_id;
743f5f7e2eSDmitry Torokhov static int tosh_bios;
753f5f7e2eSDmitry Torokhov static int tosh_date;
763f5f7e2eSDmitry Torokhov static int tosh_sci;
773f5f7e2eSDmitry Torokhov static int tosh_fan;
781da177e4SLinus Torvalds
793e8d95d9SAlan Cox static long tosh_ioctl(struct file *, unsigned int,
801da177e4SLinus Torvalds unsigned long);
811da177e4SLinus Torvalds
821da177e4SLinus Torvalds
8362322d25SArjan van de Ven static const struct file_operations tosh_fops = {
841da177e4SLinus Torvalds .owner = THIS_MODULE,
853e8d95d9SAlan Cox .unlocked_ioctl = tosh_ioctl,
866038f373SArnd Bergmann .llseek = noop_llseek,
871da177e4SLinus Torvalds };
881da177e4SLinus Torvalds
891da177e4SLinus Torvalds static struct miscdevice tosh_device = {
901da177e4SLinus Torvalds TOSH_MINOR_DEV,
911da177e4SLinus Torvalds "toshiba",
921da177e4SLinus Torvalds &tosh_fops
931da177e4SLinus Torvalds };
941da177e4SLinus Torvalds
951da177e4SLinus Torvalds /*
961da177e4SLinus Torvalds * Read the Fn key status
971da177e4SLinus Torvalds */
981da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
tosh_fn_status(void)991da177e4SLinus Torvalds static int tosh_fn_status(void)
1001da177e4SLinus Torvalds {
1011da177e4SLinus Torvalds unsigned char scan;
1021da177e4SLinus Torvalds unsigned long flags;
1031da177e4SLinus Torvalds
1041da177e4SLinus Torvalds if (tosh_fn!=0) {
1051da177e4SLinus Torvalds scan = inb(tosh_fn);
1061da177e4SLinus Torvalds } else {
1071da177e4SLinus Torvalds local_irq_save(flags);
1081da177e4SLinus Torvalds outb(0x8e, 0xe4);
1091da177e4SLinus Torvalds scan = inb(0xe5);
1101da177e4SLinus Torvalds local_irq_restore(flags);
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds return (int) scan;
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds #endif
1161da177e4SLinus Torvalds
1171da177e4SLinus Torvalds
1181da177e4SLinus Torvalds /*
1191da177e4SLinus Torvalds * For the Portage 610CT and the Tecra 700CS/700CDT emulate the HCI fan function
1201da177e4SLinus Torvalds */
tosh_emulate_fan(SMMRegisters * regs)1211da177e4SLinus Torvalds static int tosh_emulate_fan(SMMRegisters *regs)
1221da177e4SLinus Torvalds {
1231da177e4SLinus Torvalds unsigned long eax,ecx,flags;
1241da177e4SLinus Torvalds unsigned char al;
1251da177e4SLinus Torvalds
1261da177e4SLinus Torvalds eax = regs->eax & 0xff00;
1271da177e4SLinus Torvalds ecx = regs->ecx & 0xffff;
1281da177e4SLinus Torvalds
1291da177e4SLinus Torvalds /* Portage 610CT */
1301da177e4SLinus Torvalds
1311da177e4SLinus Torvalds if (tosh_id==0xfccb) {
1321da177e4SLinus Torvalds if (eax==0xfe00) {
1331da177e4SLinus Torvalds /* fan status */
1341da177e4SLinus Torvalds local_irq_save(flags);
1351da177e4SLinus Torvalds outb(0xbe, 0xe4);
1361da177e4SLinus Torvalds al = inb(0xe5);
1371da177e4SLinus Torvalds local_irq_restore(flags);
1381da177e4SLinus Torvalds regs->eax = 0x00;
1391da177e4SLinus Torvalds regs->ecx = (unsigned int) (al & 0x01);
1401da177e4SLinus Torvalds }
1411da177e4SLinus Torvalds if ((eax==0xff00) && (ecx==0x0000)) {
1421da177e4SLinus Torvalds /* fan off */
1431da177e4SLinus Torvalds local_irq_save(flags);
1441da177e4SLinus Torvalds outb(0xbe, 0xe4);
1451da177e4SLinus Torvalds al = inb(0xe5);
1461da177e4SLinus Torvalds outb(0xbe, 0xe4);
1471da177e4SLinus Torvalds outb (al | 0x01, 0xe5);
1481da177e4SLinus Torvalds local_irq_restore(flags);
1491da177e4SLinus Torvalds regs->eax = 0x00;
1501da177e4SLinus Torvalds regs->ecx = 0x00;
1511da177e4SLinus Torvalds }
1521da177e4SLinus Torvalds if ((eax==0xff00) && (ecx==0x0001)) {
1531da177e4SLinus Torvalds /* fan on */
1541da177e4SLinus Torvalds local_irq_save(flags);
1551da177e4SLinus Torvalds outb(0xbe, 0xe4);
1561da177e4SLinus Torvalds al = inb(0xe5);
1571da177e4SLinus Torvalds outb(0xbe, 0xe4);
1581da177e4SLinus Torvalds outb(al & 0xfe, 0xe5);
1591da177e4SLinus Torvalds local_irq_restore(flags);
1601da177e4SLinus Torvalds regs->eax = 0x00;
1611da177e4SLinus Torvalds regs->ecx = 0x01;
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds }
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds /* Tecra 700CS/CDT */
1661da177e4SLinus Torvalds
1671da177e4SLinus Torvalds if (tosh_id==0xfccc) {
1681da177e4SLinus Torvalds if (eax==0xfe00) {
1691da177e4SLinus Torvalds /* fan status */
1701da177e4SLinus Torvalds local_irq_save(flags);
1711da177e4SLinus Torvalds outb(0xe0, 0xe4);
1721da177e4SLinus Torvalds al = inb(0xe5);
1731da177e4SLinus Torvalds local_irq_restore(flags);
1741da177e4SLinus Torvalds regs->eax = 0x00;
1751da177e4SLinus Torvalds regs->ecx = al & 0x01;
1761da177e4SLinus Torvalds }
1771da177e4SLinus Torvalds if ((eax==0xff00) && (ecx==0x0000)) {
1781da177e4SLinus Torvalds /* fan off */
1791da177e4SLinus Torvalds local_irq_save(flags);
1801da177e4SLinus Torvalds outb(0xe0, 0xe4);
1811da177e4SLinus Torvalds al = inb(0xe5);
1821da177e4SLinus Torvalds outw(0xe0 | ((al & 0xfe) << 8), 0xe4);
1831da177e4SLinus Torvalds local_irq_restore(flags);
1841da177e4SLinus Torvalds regs->eax = 0x00;
1851da177e4SLinus Torvalds regs->ecx = 0x00;
1861da177e4SLinus Torvalds }
1871da177e4SLinus Torvalds if ((eax==0xff00) && (ecx==0x0001)) {
1881da177e4SLinus Torvalds /* fan on */
1891da177e4SLinus Torvalds local_irq_save(flags);
1901da177e4SLinus Torvalds outb(0xe0, 0xe4);
1911da177e4SLinus Torvalds al = inb(0xe5);
1921da177e4SLinus Torvalds outw(0xe0 | ((al | 0x01) << 8), 0xe4);
1931da177e4SLinus Torvalds local_irq_restore(flags);
1941da177e4SLinus Torvalds regs->eax = 0x00;
1951da177e4SLinus Torvalds regs->ecx = 0x01;
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds
1991da177e4SLinus Torvalds return 0;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds
2021da177e4SLinus Torvalds
2031da177e4SLinus Torvalds /*
2041da177e4SLinus Torvalds * Put the laptop into System Management Mode
2051da177e4SLinus Torvalds */
tosh_smm(SMMRegisters * regs)2061da177e4SLinus Torvalds int tosh_smm(SMMRegisters *regs)
2071da177e4SLinus Torvalds {
2081da177e4SLinus Torvalds int eax;
2091da177e4SLinus Torvalds
2101da177e4SLinus Torvalds asm ("# load the values into the registers\n\t" \
2111da177e4SLinus Torvalds "pushl %%eax\n\t" \
2121da177e4SLinus Torvalds "movl 0(%%eax),%%edx\n\t" \
2131da177e4SLinus Torvalds "push %%edx\n\t" \
2141da177e4SLinus Torvalds "movl 4(%%eax),%%ebx\n\t" \
2151da177e4SLinus Torvalds "movl 8(%%eax),%%ecx\n\t" \
2161da177e4SLinus Torvalds "movl 12(%%eax),%%edx\n\t" \
2171da177e4SLinus Torvalds "movl 16(%%eax),%%esi\n\t" \
2181da177e4SLinus Torvalds "movl 20(%%eax),%%edi\n\t" \
2191da177e4SLinus Torvalds "popl %%eax\n\t" \
2201da177e4SLinus Torvalds "# call the System Management mode\n\t" \
2211da177e4SLinus Torvalds "inb $0xb2,%%al\n\t"
2221da177e4SLinus Torvalds "# fill out the memory with the values in the registers\n\t" \
2231da177e4SLinus Torvalds "xchgl %%eax,(%%esp)\n\t"
2241da177e4SLinus Torvalds "movl %%ebx,4(%%eax)\n\t" \
2251da177e4SLinus Torvalds "movl %%ecx,8(%%eax)\n\t" \
2261da177e4SLinus Torvalds "movl %%edx,12(%%eax)\n\t" \
2271da177e4SLinus Torvalds "movl %%esi,16(%%eax)\n\t" \
2281da177e4SLinus Torvalds "movl %%edi,20(%%eax)\n\t" \
2291da177e4SLinus Torvalds "popl %%edx\n\t" \
2301da177e4SLinus Torvalds "movl %%edx,0(%%eax)\n\t" \
2311da177e4SLinus Torvalds "# setup the return value to the carry flag\n\t" \
2321da177e4SLinus Torvalds "lahf\n\t" \
2331da177e4SLinus Torvalds "shrl $8,%%eax\n\t" \
2341da177e4SLinus Torvalds "andl $1,%%eax\n" \
2351da177e4SLinus Torvalds : "=a" (eax)
2361da177e4SLinus Torvalds : "a" (regs)
2371da177e4SLinus Torvalds : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
2381da177e4SLinus Torvalds
2391da177e4SLinus Torvalds return eax;
2401da177e4SLinus Torvalds }
2410bf93226SRandy Dunlap EXPORT_SYMBOL(tosh_smm);
2421da177e4SLinus Torvalds
2431da177e4SLinus Torvalds
tosh_ioctl(struct file * fp,unsigned int cmd,unsigned long arg)2443e8d95d9SAlan Cox static long tosh_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
2451da177e4SLinus Torvalds {
2461da177e4SLinus Torvalds SMMRegisters regs;
2471da177e4SLinus Torvalds SMMRegisters __user *argp = (SMMRegisters __user *)arg;
2481da177e4SLinus Torvalds unsigned short ax,bx;
2491da177e4SLinus Torvalds int err;
2501da177e4SLinus Torvalds
2511da177e4SLinus Torvalds if (!argp)
2521da177e4SLinus Torvalds return -EINVAL;
2531da177e4SLinus Torvalds
2541da177e4SLinus Torvalds if (copy_from_user(®s, argp, sizeof(SMMRegisters)))
2551da177e4SLinus Torvalds return -EFAULT;
2561da177e4SLinus Torvalds
2571da177e4SLinus Torvalds switch (cmd) {
2581da177e4SLinus Torvalds case TOSH_SMM:
2591da177e4SLinus Torvalds ax = regs.eax & 0xff00;
2601da177e4SLinus Torvalds bx = regs.ebx & 0xffff;
2611da177e4SLinus Torvalds /* block HCI calls to read/write memory & PCI devices */
2621da177e4SLinus Torvalds if (((ax==0xff00) || (ax==0xfe00)) && (bx>0x0069))
2631da177e4SLinus Torvalds return -EINVAL;
2641da177e4SLinus Torvalds
2651da177e4SLinus Torvalds /* do we need to emulate the fan ? */
266613655faSArnd Bergmann mutex_lock(&tosh_mutex);
2671da177e4SLinus Torvalds if (tosh_fan==1) {
2681da177e4SLinus Torvalds if (((ax==0xf300) || (ax==0xf400)) && (bx==0x0004)) {
2691da177e4SLinus Torvalds err = tosh_emulate_fan(®s);
270613655faSArnd Bergmann mutex_unlock(&tosh_mutex);
2711da177e4SLinus Torvalds break;
2721da177e4SLinus Torvalds }
2731da177e4SLinus Torvalds }
2741da177e4SLinus Torvalds err = tosh_smm(®s);
275613655faSArnd Bergmann mutex_unlock(&tosh_mutex);
2761da177e4SLinus Torvalds break;
2771da177e4SLinus Torvalds default:
2781da177e4SLinus Torvalds return -EINVAL;
2791da177e4SLinus Torvalds }
2801da177e4SLinus Torvalds
2811da177e4SLinus Torvalds if (copy_to_user(argp, ®s, sizeof(SMMRegisters)))
2821da177e4SLinus Torvalds return -EFAULT;
2831da177e4SLinus Torvalds
2841da177e4SLinus Torvalds return (err==0) ? 0:-EINVAL;
2851da177e4SLinus Torvalds }
2861da177e4SLinus Torvalds
2871da177e4SLinus Torvalds
2881da177e4SLinus Torvalds /*
2891da177e4SLinus Torvalds * Print the information for /proc/toshiba
2901da177e4SLinus Torvalds */
2911da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
proc_toshiba_show(struct seq_file * m,void * v)292967bb77cSAlexey Dobriyan static int proc_toshiba_show(struct seq_file *m, void *v)
2931da177e4SLinus Torvalds {
2941da177e4SLinus Torvalds int key;
2951da177e4SLinus Torvalds
2961da177e4SLinus Torvalds key = tosh_fn_status();
2971da177e4SLinus Torvalds
2981da177e4SLinus Torvalds /* Arguments
2991da177e4SLinus Torvalds 0) Linux driver version (this will change if format changes)
3001da177e4SLinus Torvalds 1) Machine ID
3011da177e4SLinus Torvalds 2) SCI version
3021da177e4SLinus Torvalds 3) BIOS version (major, minor)
3031da177e4SLinus Torvalds 4) BIOS date (in SCI date format)
3041da177e4SLinus Torvalds 5) Fn Key status
3051da177e4SLinus Torvalds */
306967bb77cSAlexey Dobriyan seq_printf(m, "1.1 0x%04x %d.%d %d.%d 0x%04x 0x%02x\n",
3071da177e4SLinus Torvalds tosh_id,
3081da177e4SLinus Torvalds (tosh_sci & 0xff00)>>8,
3091da177e4SLinus Torvalds tosh_sci & 0xff,
3101da177e4SLinus Torvalds (tosh_bios & 0xff00)>>8,
3111da177e4SLinus Torvalds tosh_bios & 0xff,
3121da177e4SLinus Torvalds tosh_date,
3131da177e4SLinus Torvalds key);
314967bb77cSAlexey Dobriyan return 0;
3151da177e4SLinus Torvalds }
3161da177e4SLinus Torvalds #endif
3171da177e4SLinus Torvalds
3181da177e4SLinus Torvalds
3191da177e4SLinus Torvalds /*
3201da177e4SLinus Torvalds * Determine which port to use for the Fn key status
3211da177e4SLinus Torvalds */
tosh_set_fn_port(void)3221da177e4SLinus Torvalds static void tosh_set_fn_port(void)
3231da177e4SLinus Torvalds {
3241da177e4SLinus Torvalds switch (tosh_id) {
3251da177e4SLinus Torvalds case 0xfc02: case 0xfc04: case 0xfc09: case 0xfc0a: case 0xfc10:
3261da177e4SLinus Torvalds case 0xfc11: case 0xfc13: case 0xfc15: case 0xfc1a: case 0xfc1b:
3271da177e4SLinus Torvalds case 0xfc5a:
3281da177e4SLinus Torvalds tosh_fn = 0x62;
3291da177e4SLinus Torvalds break;
3301da177e4SLinus Torvalds case 0xfc08: case 0xfc17: case 0xfc1d: case 0xfcd1: case 0xfce0:
3311da177e4SLinus Torvalds case 0xfce2:
3321da177e4SLinus Torvalds tosh_fn = 0x68;
3331da177e4SLinus Torvalds break;
3341da177e4SLinus Torvalds default:
3351da177e4SLinus Torvalds tosh_fn = 0x00;
3361da177e4SLinus Torvalds break;
3371da177e4SLinus Torvalds }
3381da177e4SLinus Torvalds
3391da177e4SLinus Torvalds return;
3401da177e4SLinus Torvalds }
3411da177e4SLinus Torvalds
3421da177e4SLinus Torvalds
3431da177e4SLinus Torvalds /*
3441da177e4SLinus Torvalds * Get the machine identification number of the current model
3451da177e4SLinus Torvalds */
tosh_get_machine_id(void __iomem * bios)346ef5a4c8bSAl Viro static int tosh_get_machine_id(void __iomem *bios)
3471da177e4SLinus Torvalds {
3481da177e4SLinus Torvalds int id;
3491da177e4SLinus Torvalds SMMRegisters regs;
3501da177e4SLinus Torvalds unsigned short bx,cx;
3511da177e4SLinus Torvalds unsigned long address;
3521da177e4SLinus Torvalds
353ef5a4c8bSAl Viro id = (0x100*(int) readb(bios+0xfffe))+((int) readb(bios+0xfffa));
3541da177e4SLinus Torvalds
3551da177e4SLinus Torvalds /* do we have a SCTTable machine identication number on our hands */
3561da177e4SLinus Torvalds
3571da177e4SLinus Torvalds if (id==0xfc2f) {
3581da177e4SLinus Torvalds
3591da177e4SLinus Torvalds /* start by getting a pointer into the BIOS */
3601da177e4SLinus Torvalds
3611da177e4SLinus Torvalds regs.eax = 0xc000;
3621da177e4SLinus Torvalds regs.ebx = 0x0000;
3631da177e4SLinus Torvalds regs.ecx = 0x0000;
3641da177e4SLinus Torvalds tosh_smm(®s);
3651da177e4SLinus Torvalds bx = (unsigned short) (regs.ebx & 0xffff);
3661da177e4SLinus Torvalds
3671da177e4SLinus Torvalds /* At this point in the Toshiba routines under MS Windows
3681da177e4SLinus Torvalds the bx register holds 0xe6f5. However my code is producing
3691da177e4SLinus Torvalds a different value! For the time being I will just fudge the
3701da177e4SLinus Torvalds value. This has been verified on a Satellite Pro 430CDT,
3711da177e4SLinus Torvalds Tecra 750CDT, Tecra 780DVD and Satellite 310CDT. */
3721da177e4SLinus Torvalds #if TOSH_DEBUG
373*13c1d4b3SRishi Gupta pr_debug("toshiba: debugging ID ebx=0x%04x\n", regs.ebx);
3741da177e4SLinus Torvalds #endif
3751da177e4SLinus Torvalds bx = 0xe6f5;
3761da177e4SLinus Torvalds
3771da177e4SLinus Torvalds /* now twiddle with our pointer a bit */
3781da177e4SLinus Torvalds
379ef5a4c8bSAl Viro address = bx;
380ef5a4c8bSAl Viro cx = readw(bios + address);
381ef5a4c8bSAl Viro address = 9+bx+cx;
382ef5a4c8bSAl Viro cx = readw(bios + address);
383ef5a4c8bSAl Viro address = 0xa+cx;
384ef5a4c8bSAl Viro cx = readw(bios + address);
3851da177e4SLinus Torvalds
3861da177e4SLinus Torvalds /* now construct our machine identification number */
3871da177e4SLinus Torvalds
3881da177e4SLinus Torvalds id = ((cx & 0xff)<<8)+((cx & 0xff00)>>8);
3891da177e4SLinus Torvalds }
3901da177e4SLinus Torvalds
3911da177e4SLinus Torvalds return id;
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds
3941da177e4SLinus Torvalds
3951da177e4SLinus Torvalds /*
3961da177e4SLinus Torvalds * Probe for the presence of a Toshiba laptop
3971da177e4SLinus Torvalds *
3981da177e4SLinus Torvalds * returns and non-zero if unable to detect the presence of a Toshiba
3991da177e4SLinus Torvalds * laptop, otherwise zero and determines the Machine ID, BIOS version and
4001da177e4SLinus Torvalds * date, and SCI version.
4011da177e4SLinus Torvalds */
tosh_probe(void)4021da177e4SLinus Torvalds static int tosh_probe(void)
4031da177e4SLinus Torvalds {
4041da177e4SLinus Torvalds int i,major,minor,day,year,month,flag;
4051da177e4SLinus Torvalds unsigned char signature[7] = { 0x54,0x4f,0x53,0x48,0x49,0x42,0x41 };
4061da177e4SLinus Torvalds SMMRegisters regs;
40748166932SDan Williams void __iomem *bios = ioremap(0xf0000, 0x10000);
408ef5a4c8bSAl Viro
409ef5a4c8bSAl Viro if (!bios)
410ef5a4c8bSAl Viro return -ENOMEM;
4111da177e4SLinus Torvalds
4121da177e4SLinus Torvalds /* extra sanity check for the string "TOSHIBA" in the BIOS because
4131da177e4SLinus Torvalds some machines that are not Toshiba's pass the next test */
4141da177e4SLinus Torvalds
4151da177e4SLinus Torvalds for (i=0;i<7;i++) {
416ef5a4c8bSAl Viro if (readb(bios+0xe010+i)!=signature[i]) {
417*13c1d4b3SRishi Gupta pr_err("toshiba: not a supported Toshiba laptop\n");
418ef5a4c8bSAl Viro iounmap(bios);
4191da177e4SLinus Torvalds return -ENODEV;
4201da177e4SLinus Torvalds }
4211da177e4SLinus Torvalds }
4221da177e4SLinus Torvalds
4231da177e4SLinus Torvalds /* call the Toshiba SCI support check routine */
4241da177e4SLinus Torvalds
4251da177e4SLinus Torvalds regs.eax = 0xf0f0;
4261da177e4SLinus Torvalds regs.ebx = 0x0000;
4271da177e4SLinus Torvalds regs.ecx = 0x0000;
4281da177e4SLinus Torvalds flag = tosh_smm(®s);
4291da177e4SLinus Torvalds
4301da177e4SLinus Torvalds /* if this is not a Toshiba laptop carry flag is set and ah=0x86 */
4311da177e4SLinus Torvalds
4321da177e4SLinus Torvalds if ((flag==1) || ((regs.eax & 0xff00)==0x8600)) {
433*13c1d4b3SRishi Gupta pr_err("toshiba: not a supported Toshiba laptop\n");
434ef5a4c8bSAl Viro iounmap(bios);
4351da177e4SLinus Torvalds return -ENODEV;
4361da177e4SLinus Torvalds }
4371da177e4SLinus Torvalds
4381da177e4SLinus Torvalds /* if we get this far then we are running on a Toshiba (probably)! */
4391da177e4SLinus Torvalds
4401da177e4SLinus Torvalds tosh_sci = regs.edx & 0xffff;
4411da177e4SLinus Torvalds
4421da177e4SLinus Torvalds /* next get the machine ID of the current laptop */
4431da177e4SLinus Torvalds
444ef5a4c8bSAl Viro tosh_id = tosh_get_machine_id(bios);
4451da177e4SLinus Torvalds
4461da177e4SLinus Torvalds /* get the BIOS version */
4471da177e4SLinus Torvalds
448ef5a4c8bSAl Viro major = readb(bios+0xe009)-'0';
449ef5a4c8bSAl Viro minor = ((readb(bios+0xe00b)-'0')*10)+(readb(bios+0xe00c)-'0');
4501da177e4SLinus Torvalds tosh_bios = (major*0x100)+minor;
4511da177e4SLinus Torvalds
4521da177e4SLinus Torvalds /* get the BIOS date */
4531da177e4SLinus Torvalds
454ef5a4c8bSAl Viro day = ((readb(bios+0xfff5)-'0')*10)+(readb(bios+0xfff6)-'0');
455ef5a4c8bSAl Viro month = ((readb(bios+0xfff8)-'0')*10)+(readb(bios+0xfff9)-'0');
456ef5a4c8bSAl Viro year = ((readb(bios+0xfffb)-'0')*10)+(readb(bios+0xfffc)-'0');
4571da177e4SLinus Torvalds tosh_date = (((year-90) & 0x1f)<<10) | ((month & 0xf)<<6)
4581da177e4SLinus Torvalds | ((day & 0x1f)<<1);
4591da177e4SLinus Torvalds
4601da177e4SLinus Torvalds
4611da177e4SLinus Torvalds /* in theory we should check the ports we are going to use for the
4621da177e4SLinus Torvalds fn key detection (and the fan on the Portage 610/Tecra700), and
4631da177e4SLinus Torvalds then request them to stop other drivers using them. However as
4641da177e4SLinus Torvalds the keyboard driver grabs 0x60-0x6f and the pic driver grabs
4651da177e4SLinus Torvalds 0xa0-0xbf we can't. We just have to live dangerously and use the
4661da177e4SLinus Torvalds ports anyway, oh boy! */
4671da177e4SLinus Torvalds
4681da177e4SLinus Torvalds /* do we need to emulate the fan? */
4691da177e4SLinus Torvalds
4701da177e4SLinus Torvalds if ((tosh_id==0xfccb) || (tosh_id==0xfccc))
4711da177e4SLinus Torvalds tosh_fan = 1;
4721da177e4SLinus Torvalds
473ef5a4c8bSAl Viro iounmap(bios);
474ef5a4c8bSAl Viro
4751da177e4SLinus Torvalds return 0;
4761da177e4SLinus Torvalds }
4771da177e4SLinus Torvalds
toshiba_init(void)4783f5f7e2eSDmitry Torokhov static int __init toshiba_init(void)
4791da177e4SLinus Torvalds {
4801da177e4SLinus Torvalds int retval;
4811da177e4SLinus Torvalds /* are we running on a Toshiba laptop */
4821da177e4SLinus Torvalds
4833f5f7e2eSDmitry Torokhov if (tosh_probe())
4843f5f7e2eSDmitry Torokhov return -ENODEV;
4851da177e4SLinus Torvalds
486*13c1d4b3SRishi Gupta pr_info("Toshiba System Management Mode driver v" TOSH_VERSION "\n");
4871da177e4SLinus Torvalds
4881da177e4SLinus Torvalds /* set the port to use for Fn status if not specified as a parameter */
4891da177e4SLinus Torvalds if (tosh_fn==0x00)
4901da177e4SLinus Torvalds tosh_set_fn_port();
4911da177e4SLinus Torvalds
4921da177e4SLinus Torvalds /* register the device file */
4931da177e4SLinus Torvalds retval = misc_register(&tosh_device);
4941da177e4SLinus Torvalds if (retval < 0)
4951da177e4SLinus Torvalds return retval;
4961da177e4SLinus Torvalds
4971da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
498967bb77cSAlexey Dobriyan {
499967bb77cSAlexey Dobriyan struct proc_dir_entry *pde;
500967bb77cSAlexey Dobriyan
5013f3942acSChristoph Hellwig pde = proc_create_single("toshiba", 0, NULL, proc_toshiba_show);
502967bb77cSAlexey Dobriyan if (!pde) {
5031da177e4SLinus Torvalds misc_deregister(&tosh_device);
5041da177e4SLinus Torvalds return -ENOMEM;
5051da177e4SLinus Torvalds }
506967bb77cSAlexey Dobriyan }
5071da177e4SLinus Torvalds #endif
5081da177e4SLinus Torvalds
5091da177e4SLinus Torvalds return 0;
5101da177e4SLinus Torvalds }
5111da177e4SLinus Torvalds
toshiba_exit(void)5123f5f7e2eSDmitry Torokhov static void __exit toshiba_exit(void)
5131da177e4SLinus Torvalds {
5141da177e4SLinus Torvalds remove_proc_entry("toshiba", NULL);
5151da177e4SLinus Torvalds misc_deregister(&tosh_device);
5161da177e4SLinus Torvalds }
5171da177e4SLinus Torvalds
5183f5f7e2eSDmitry Torokhov module_init(toshiba_init);
5193f5f7e2eSDmitry Torokhov module_exit(toshiba_exit);
5201da177e4SLinus Torvalds
521