162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* toshiba.c -- Linux driver for accessing the SMM on Toshiba laptops 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 1996-2001 Jonathan A. Buzzard (jonathan@buzzard.org.uk) 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Valuable assistance and patches from: 762306a36Sopenharmony_ci * Tom May <tom@you-bastards.com> 862306a36Sopenharmony_ci * Rob Napier <rnapier@employees.org> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Fn status port numbers for machine ID's courtesy of 1162306a36Sopenharmony_ci * 0xfc02: Scott Eisert <scott.e@sky-eye.com> 1262306a36Sopenharmony_ci * 0xfc04: Steve VanDevender <stevev@efn.org> 1362306a36Sopenharmony_ci * 0xfc08: Garth Berry <garth@itsbruce.net> 1462306a36Sopenharmony_ci * 0xfc0a: Egbert Eich <eich@xfree86.org> 1562306a36Sopenharmony_ci * 0xfc10: Andrew Lofthouse <Andrew.Lofthouse@robins.af.mil> 1662306a36Sopenharmony_ci * 0xfc11: Spencer Olson <solson@novell.com> 1762306a36Sopenharmony_ci * 0xfc13: Claudius Frankewitz <kryp@gmx.de> 1862306a36Sopenharmony_ci * 0xfc15: Tom May <tom@you-bastards.com> 1962306a36Sopenharmony_ci * 0xfc17: Dave Konrad <konrad@xenia.it> 2062306a36Sopenharmony_ci * 0xfc1a: George Betzos <betzos@engr.colostate.edu> 2162306a36Sopenharmony_ci * 0xfc1b: Munemasa Wada <munemasa@jnovel.co.jp> 2262306a36Sopenharmony_ci * 0xfc1d: Arthur Liu <armie@slap.mine.nu> 2362306a36Sopenharmony_ci * 0xfc5a: Jacques L'helgoualc'h <lhh@free.fr> 2462306a36Sopenharmony_ci * 0xfcd1: Mr. Dave Konrad <konrad@xenia.it> 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * This code is covered by the GNU GPL and you are free to make any 2962306a36Sopenharmony_ci * changes you wish to it under the terms of the license. However the 3062306a36Sopenharmony_ci * code has the potential to render your computer and/or someone else's 3162306a36Sopenharmony_ci * unusable. Please proceed with care when modifying the code. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * Note: Unfortunately the laptop hardware can close the System Configuration 3462306a36Sopenharmony_ci * Interface on it's own accord. It is therefore necessary for *all* 3562306a36Sopenharmony_ci * programs using this driver to be aware that *any* SCI call can fail at 3662306a36Sopenharmony_ci * *any* time. It is up to any program to be aware of this eventuality 3762306a36Sopenharmony_ci * and take appropriate steps. 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * The information used to write this driver has been obtained by reverse 4062306a36Sopenharmony_ci * engineering the software supplied by Toshiba for their portable computers in 4162306a36Sopenharmony_ci * strict accordance with the European Council Directive 92/250/EEC on the legal 4262306a36Sopenharmony_ci * protection of computer programs, and it's implementation into English Law by 4362306a36Sopenharmony_ci * the Copyright (Computer Programs) Regulations 1992 (S.I. 1992 No.3233). 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define TOSH_VERSION "1.11 26/9/2001" 4762306a36Sopenharmony_ci#define TOSH_DEBUG 0 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#include <linux/module.h> 5062306a36Sopenharmony_ci#include <linux/kernel.h> 5162306a36Sopenharmony_ci#include <linux/types.h> 5262306a36Sopenharmony_ci#include <linux/fcntl.h> 5362306a36Sopenharmony_ci#include <linux/miscdevice.h> 5462306a36Sopenharmony_ci#include <linux/ioport.h> 5562306a36Sopenharmony_ci#include <asm/io.h> 5662306a36Sopenharmony_ci#include <linux/uaccess.h> 5762306a36Sopenharmony_ci#include <linux/init.h> 5862306a36Sopenharmony_ci#include <linux/stat.h> 5962306a36Sopenharmony_ci#include <linux/proc_fs.h> 6062306a36Sopenharmony_ci#include <linux/seq_file.h> 6162306a36Sopenharmony_ci#include <linux/mutex.h> 6262306a36Sopenharmony_ci#include <linux/toshiba.h> 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 6562306a36Sopenharmony_ciMODULE_AUTHOR("Jonathan Buzzard <jonathan@buzzard.org.uk>"); 6662306a36Sopenharmony_ciMODULE_DESCRIPTION("Toshiba laptop SMM driver"); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic DEFINE_MUTEX(tosh_mutex); 6962306a36Sopenharmony_cistatic int tosh_fn; 7062306a36Sopenharmony_cimodule_param_named(fn, tosh_fn, int, 0); 7162306a36Sopenharmony_ciMODULE_PARM_DESC(fn, "User specified Fn key detection port"); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int tosh_id; 7462306a36Sopenharmony_cistatic int tosh_bios; 7562306a36Sopenharmony_cistatic int tosh_date; 7662306a36Sopenharmony_cistatic int tosh_sci; 7762306a36Sopenharmony_cistatic int tosh_fan; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic long tosh_ioctl(struct file *, unsigned int, 8062306a36Sopenharmony_ci unsigned long); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const struct file_operations tosh_fops = { 8462306a36Sopenharmony_ci .owner = THIS_MODULE, 8562306a36Sopenharmony_ci .unlocked_ioctl = tosh_ioctl, 8662306a36Sopenharmony_ci .llseek = noop_llseek, 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic struct miscdevice tosh_device = { 9062306a36Sopenharmony_ci TOSH_MINOR_DEV, 9162306a36Sopenharmony_ci "toshiba", 9262306a36Sopenharmony_ci &tosh_fops 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* 9662306a36Sopenharmony_ci * Read the Fn key status 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 9962306a36Sopenharmony_cistatic int tosh_fn_status(void) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci unsigned char scan; 10262306a36Sopenharmony_ci unsigned long flags; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (tosh_fn!=0) { 10562306a36Sopenharmony_ci scan = inb(tosh_fn); 10662306a36Sopenharmony_ci } else { 10762306a36Sopenharmony_ci local_irq_save(flags); 10862306a36Sopenharmony_ci outb(0x8e, 0xe4); 10962306a36Sopenharmony_ci scan = inb(0xe5); 11062306a36Sopenharmony_ci local_irq_restore(flags); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return (int) scan; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci#endif 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* 11962306a36Sopenharmony_ci * For the Portage 610CT and the Tecra 700CS/700CDT emulate the HCI fan function 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_cistatic int tosh_emulate_fan(SMMRegisters *regs) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci unsigned long eax,ecx,flags; 12462306a36Sopenharmony_ci unsigned char al; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci eax = regs->eax & 0xff00; 12762306a36Sopenharmony_ci ecx = regs->ecx & 0xffff; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Portage 610CT */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (tosh_id==0xfccb) { 13262306a36Sopenharmony_ci if (eax==0xfe00) { 13362306a36Sopenharmony_ci /* fan status */ 13462306a36Sopenharmony_ci local_irq_save(flags); 13562306a36Sopenharmony_ci outb(0xbe, 0xe4); 13662306a36Sopenharmony_ci al = inb(0xe5); 13762306a36Sopenharmony_ci local_irq_restore(flags); 13862306a36Sopenharmony_ci regs->eax = 0x00; 13962306a36Sopenharmony_ci regs->ecx = (unsigned int) (al & 0x01); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci if ((eax==0xff00) && (ecx==0x0000)) { 14262306a36Sopenharmony_ci /* fan off */ 14362306a36Sopenharmony_ci local_irq_save(flags); 14462306a36Sopenharmony_ci outb(0xbe, 0xe4); 14562306a36Sopenharmony_ci al = inb(0xe5); 14662306a36Sopenharmony_ci outb(0xbe, 0xe4); 14762306a36Sopenharmony_ci outb (al | 0x01, 0xe5); 14862306a36Sopenharmony_ci local_irq_restore(flags); 14962306a36Sopenharmony_ci regs->eax = 0x00; 15062306a36Sopenharmony_ci regs->ecx = 0x00; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci if ((eax==0xff00) && (ecx==0x0001)) { 15362306a36Sopenharmony_ci /* fan on */ 15462306a36Sopenharmony_ci local_irq_save(flags); 15562306a36Sopenharmony_ci outb(0xbe, 0xe4); 15662306a36Sopenharmony_ci al = inb(0xe5); 15762306a36Sopenharmony_ci outb(0xbe, 0xe4); 15862306a36Sopenharmony_ci outb(al & 0xfe, 0xe5); 15962306a36Sopenharmony_ci local_irq_restore(flags); 16062306a36Sopenharmony_ci regs->eax = 0x00; 16162306a36Sopenharmony_ci regs->ecx = 0x01; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Tecra 700CS/CDT */ 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (tosh_id==0xfccc) { 16862306a36Sopenharmony_ci if (eax==0xfe00) { 16962306a36Sopenharmony_ci /* fan status */ 17062306a36Sopenharmony_ci local_irq_save(flags); 17162306a36Sopenharmony_ci outb(0xe0, 0xe4); 17262306a36Sopenharmony_ci al = inb(0xe5); 17362306a36Sopenharmony_ci local_irq_restore(flags); 17462306a36Sopenharmony_ci regs->eax = 0x00; 17562306a36Sopenharmony_ci regs->ecx = al & 0x01; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci if ((eax==0xff00) && (ecx==0x0000)) { 17862306a36Sopenharmony_ci /* fan off */ 17962306a36Sopenharmony_ci local_irq_save(flags); 18062306a36Sopenharmony_ci outb(0xe0, 0xe4); 18162306a36Sopenharmony_ci al = inb(0xe5); 18262306a36Sopenharmony_ci outw(0xe0 | ((al & 0xfe) << 8), 0xe4); 18362306a36Sopenharmony_ci local_irq_restore(flags); 18462306a36Sopenharmony_ci regs->eax = 0x00; 18562306a36Sopenharmony_ci regs->ecx = 0x00; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci if ((eax==0xff00) && (ecx==0x0001)) { 18862306a36Sopenharmony_ci /* fan on */ 18962306a36Sopenharmony_ci local_irq_save(flags); 19062306a36Sopenharmony_ci outb(0xe0, 0xe4); 19162306a36Sopenharmony_ci al = inb(0xe5); 19262306a36Sopenharmony_ci outw(0xe0 | ((al | 0x01) << 8), 0xe4); 19362306a36Sopenharmony_ci local_irq_restore(flags); 19462306a36Sopenharmony_ci regs->eax = 0x00; 19562306a36Sopenharmony_ci regs->ecx = 0x01; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci/* 20462306a36Sopenharmony_ci * Put the laptop into System Management Mode 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ciint tosh_smm(SMMRegisters *regs) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci int eax; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci asm ("# load the values into the registers\n\t" \ 21162306a36Sopenharmony_ci "pushl %%eax\n\t" \ 21262306a36Sopenharmony_ci "movl 0(%%eax),%%edx\n\t" \ 21362306a36Sopenharmony_ci "push %%edx\n\t" \ 21462306a36Sopenharmony_ci "movl 4(%%eax),%%ebx\n\t" \ 21562306a36Sopenharmony_ci "movl 8(%%eax),%%ecx\n\t" \ 21662306a36Sopenharmony_ci "movl 12(%%eax),%%edx\n\t" \ 21762306a36Sopenharmony_ci "movl 16(%%eax),%%esi\n\t" \ 21862306a36Sopenharmony_ci "movl 20(%%eax),%%edi\n\t" \ 21962306a36Sopenharmony_ci "popl %%eax\n\t" \ 22062306a36Sopenharmony_ci "# call the System Management mode\n\t" \ 22162306a36Sopenharmony_ci "inb $0xb2,%%al\n\t" 22262306a36Sopenharmony_ci "# fill out the memory with the values in the registers\n\t" \ 22362306a36Sopenharmony_ci "xchgl %%eax,(%%esp)\n\t" 22462306a36Sopenharmony_ci "movl %%ebx,4(%%eax)\n\t" \ 22562306a36Sopenharmony_ci "movl %%ecx,8(%%eax)\n\t" \ 22662306a36Sopenharmony_ci "movl %%edx,12(%%eax)\n\t" \ 22762306a36Sopenharmony_ci "movl %%esi,16(%%eax)\n\t" \ 22862306a36Sopenharmony_ci "movl %%edi,20(%%eax)\n\t" \ 22962306a36Sopenharmony_ci "popl %%edx\n\t" \ 23062306a36Sopenharmony_ci "movl %%edx,0(%%eax)\n\t" \ 23162306a36Sopenharmony_ci "# setup the return value to the carry flag\n\t" \ 23262306a36Sopenharmony_ci "lahf\n\t" \ 23362306a36Sopenharmony_ci "shrl $8,%%eax\n\t" \ 23462306a36Sopenharmony_ci "andl $1,%%eax\n" \ 23562306a36Sopenharmony_ci : "=a" (eax) 23662306a36Sopenharmony_ci : "a" (regs) 23762306a36Sopenharmony_ci : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return eax; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ciEXPORT_SYMBOL(tosh_smm); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic long tosh_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci SMMRegisters regs; 24762306a36Sopenharmony_ci SMMRegisters __user *argp = (SMMRegisters __user *)arg; 24862306a36Sopenharmony_ci unsigned short ax,bx; 24962306a36Sopenharmony_ci int err; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (!argp) 25262306a36Sopenharmony_ci return -EINVAL; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (copy_from_user(®s, argp, sizeof(SMMRegisters))) 25562306a36Sopenharmony_ci return -EFAULT; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci switch (cmd) { 25862306a36Sopenharmony_ci case TOSH_SMM: 25962306a36Sopenharmony_ci ax = regs.eax & 0xff00; 26062306a36Sopenharmony_ci bx = regs.ebx & 0xffff; 26162306a36Sopenharmony_ci /* block HCI calls to read/write memory & PCI devices */ 26262306a36Sopenharmony_ci if (((ax==0xff00) || (ax==0xfe00)) && (bx>0x0069)) 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* do we need to emulate the fan ? */ 26662306a36Sopenharmony_ci mutex_lock(&tosh_mutex); 26762306a36Sopenharmony_ci if (tosh_fan==1) { 26862306a36Sopenharmony_ci if (((ax==0xf300) || (ax==0xf400)) && (bx==0x0004)) { 26962306a36Sopenharmony_ci err = tosh_emulate_fan(®s); 27062306a36Sopenharmony_ci mutex_unlock(&tosh_mutex); 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci err = tosh_smm(®s); 27562306a36Sopenharmony_ci mutex_unlock(&tosh_mutex); 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci default: 27862306a36Sopenharmony_ci return -EINVAL; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (copy_to_user(argp, ®s, sizeof(SMMRegisters))) 28262306a36Sopenharmony_ci return -EFAULT; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return (err==0) ? 0:-EINVAL; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* 28962306a36Sopenharmony_ci * Print the information for /proc/toshiba 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 29262306a36Sopenharmony_cistatic int proc_toshiba_show(struct seq_file *m, void *v) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci int key; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci key = tosh_fn_status(); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Arguments 29962306a36Sopenharmony_ci 0) Linux driver version (this will change if format changes) 30062306a36Sopenharmony_ci 1) Machine ID 30162306a36Sopenharmony_ci 2) SCI version 30262306a36Sopenharmony_ci 3) BIOS version (major, minor) 30362306a36Sopenharmony_ci 4) BIOS date (in SCI date format) 30462306a36Sopenharmony_ci 5) Fn Key status 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci seq_printf(m, "1.1 0x%04x %d.%d %d.%d 0x%04x 0x%02x\n", 30762306a36Sopenharmony_ci tosh_id, 30862306a36Sopenharmony_ci (tosh_sci & 0xff00)>>8, 30962306a36Sopenharmony_ci tosh_sci & 0xff, 31062306a36Sopenharmony_ci (tosh_bios & 0xff00)>>8, 31162306a36Sopenharmony_ci tosh_bios & 0xff, 31262306a36Sopenharmony_ci tosh_date, 31362306a36Sopenharmony_ci key); 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci#endif 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* 32062306a36Sopenharmony_ci * Determine which port to use for the Fn key status 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_cistatic void tosh_set_fn_port(void) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci switch (tosh_id) { 32562306a36Sopenharmony_ci case 0xfc02: case 0xfc04: case 0xfc09: case 0xfc0a: case 0xfc10: 32662306a36Sopenharmony_ci case 0xfc11: case 0xfc13: case 0xfc15: case 0xfc1a: case 0xfc1b: 32762306a36Sopenharmony_ci case 0xfc5a: 32862306a36Sopenharmony_ci tosh_fn = 0x62; 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci case 0xfc08: case 0xfc17: case 0xfc1d: case 0xfcd1: case 0xfce0: 33162306a36Sopenharmony_ci case 0xfce2: 33262306a36Sopenharmony_ci tosh_fn = 0x68; 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci default: 33562306a36Sopenharmony_ci tosh_fn = 0x00; 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci/* 34462306a36Sopenharmony_ci * Get the machine identification number of the current model 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_cistatic int tosh_get_machine_id(void __iomem *bios) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci int id; 34962306a36Sopenharmony_ci SMMRegisters regs; 35062306a36Sopenharmony_ci unsigned short bx,cx; 35162306a36Sopenharmony_ci unsigned long address; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci id = (0x100*(int) readb(bios+0xfffe))+((int) readb(bios+0xfffa)); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* do we have a SCTTable machine identication number on our hands */ 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (id==0xfc2f) { 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* start by getting a pointer into the BIOS */ 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci regs.eax = 0xc000; 36262306a36Sopenharmony_ci regs.ebx = 0x0000; 36362306a36Sopenharmony_ci regs.ecx = 0x0000; 36462306a36Sopenharmony_ci tosh_smm(®s); 36562306a36Sopenharmony_ci bx = (unsigned short) (regs.ebx & 0xffff); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* At this point in the Toshiba routines under MS Windows 36862306a36Sopenharmony_ci the bx register holds 0xe6f5. However my code is producing 36962306a36Sopenharmony_ci a different value! For the time being I will just fudge the 37062306a36Sopenharmony_ci value. This has been verified on a Satellite Pro 430CDT, 37162306a36Sopenharmony_ci Tecra 750CDT, Tecra 780DVD and Satellite 310CDT. */ 37262306a36Sopenharmony_ci#if TOSH_DEBUG 37362306a36Sopenharmony_ci pr_debug("toshiba: debugging ID ebx=0x%04x\n", regs.ebx); 37462306a36Sopenharmony_ci#endif 37562306a36Sopenharmony_ci bx = 0xe6f5; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* now twiddle with our pointer a bit */ 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci address = bx; 38062306a36Sopenharmony_ci cx = readw(bios + address); 38162306a36Sopenharmony_ci address = 9+bx+cx; 38262306a36Sopenharmony_ci cx = readw(bios + address); 38362306a36Sopenharmony_ci address = 0xa+cx; 38462306a36Sopenharmony_ci cx = readw(bios + address); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* now construct our machine identification number */ 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci id = ((cx & 0xff)<<8)+((cx & 0xff00)>>8); 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return id; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci/* 39662306a36Sopenharmony_ci * Probe for the presence of a Toshiba laptop 39762306a36Sopenharmony_ci * 39862306a36Sopenharmony_ci * returns and non-zero if unable to detect the presence of a Toshiba 39962306a36Sopenharmony_ci * laptop, otherwise zero and determines the Machine ID, BIOS version and 40062306a36Sopenharmony_ci * date, and SCI version. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_cistatic int tosh_probe(void) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci int i,major,minor,day,year,month,flag; 40562306a36Sopenharmony_ci unsigned char signature[7] = { 0x54,0x4f,0x53,0x48,0x49,0x42,0x41 }; 40662306a36Sopenharmony_ci SMMRegisters regs; 40762306a36Sopenharmony_ci void __iomem *bios = ioremap(0xf0000, 0x10000); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (!bios) 41062306a36Sopenharmony_ci return -ENOMEM; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* extra sanity check for the string "TOSHIBA" in the BIOS because 41362306a36Sopenharmony_ci some machines that are not Toshiba's pass the next test */ 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci for (i=0;i<7;i++) { 41662306a36Sopenharmony_ci if (readb(bios+0xe010+i)!=signature[i]) { 41762306a36Sopenharmony_ci pr_err("toshiba: not a supported Toshiba laptop\n"); 41862306a36Sopenharmony_ci iounmap(bios); 41962306a36Sopenharmony_ci return -ENODEV; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* call the Toshiba SCI support check routine */ 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci regs.eax = 0xf0f0; 42662306a36Sopenharmony_ci regs.ebx = 0x0000; 42762306a36Sopenharmony_ci regs.ecx = 0x0000; 42862306a36Sopenharmony_ci flag = tosh_smm(®s); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* if this is not a Toshiba laptop carry flag is set and ah=0x86 */ 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if ((flag==1) || ((regs.eax & 0xff00)==0x8600)) { 43362306a36Sopenharmony_ci pr_err("toshiba: not a supported Toshiba laptop\n"); 43462306a36Sopenharmony_ci iounmap(bios); 43562306a36Sopenharmony_ci return -ENODEV; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* if we get this far then we are running on a Toshiba (probably)! */ 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci tosh_sci = regs.edx & 0xffff; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* next get the machine ID of the current laptop */ 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci tosh_id = tosh_get_machine_id(bios); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* get the BIOS version */ 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci major = readb(bios+0xe009)-'0'; 44962306a36Sopenharmony_ci minor = ((readb(bios+0xe00b)-'0')*10)+(readb(bios+0xe00c)-'0'); 45062306a36Sopenharmony_ci tosh_bios = (major*0x100)+minor; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* get the BIOS date */ 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci day = ((readb(bios+0xfff5)-'0')*10)+(readb(bios+0xfff6)-'0'); 45562306a36Sopenharmony_ci month = ((readb(bios+0xfff8)-'0')*10)+(readb(bios+0xfff9)-'0'); 45662306a36Sopenharmony_ci year = ((readb(bios+0xfffb)-'0')*10)+(readb(bios+0xfffc)-'0'); 45762306a36Sopenharmony_ci tosh_date = (((year-90) & 0x1f)<<10) | ((month & 0xf)<<6) 45862306a36Sopenharmony_ci | ((day & 0x1f)<<1); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* in theory we should check the ports we are going to use for the 46262306a36Sopenharmony_ci fn key detection (and the fan on the Portage 610/Tecra700), and 46362306a36Sopenharmony_ci then request them to stop other drivers using them. However as 46462306a36Sopenharmony_ci the keyboard driver grabs 0x60-0x6f and the pic driver grabs 46562306a36Sopenharmony_ci 0xa0-0xbf we can't. We just have to live dangerously and use the 46662306a36Sopenharmony_ci ports anyway, oh boy! */ 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* do we need to emulate the fan? */ 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if ((tosh_id==0xfccb) || (tosh_id==0xfccc)) 47162306a36Sopenharmony_ci tosh_fan = 1; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci iounmap(bios); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int __init toshiba_init(void) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci int retval; 48162306a36Sopenharmony_ci /* are we running on a Toshiba laptop */ 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (tosh_probe()) 48462306a36Sopenharmony_ci return -ENODEV; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci pr_info("Toshiba System Management Mode driver v" TOSH_VERSION "\n"); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* set the port to use for Fn status if not specified as a parameter */ 48962306a36Sopenharmony_ci if (tosh_fn==0x00) 49062306a36Sopenharmony_ci tosh_set_fn_port(); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* register the device file */ 49362306a36Sopenharmony_ci retval = misc_register(&tosh_device); 49462306a36Sopenharmony_ci if (retval < 0) 49562306a36Sopenharmony_ci return retval; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 49862306a36Sopenharmony_ci { 49962306a36Sopenharmony_ci struct proc_dir_entry *pde; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci pde = proc_create_single("toshiba", 0, NULL, proc_toshiba_show); 50262306a36Sopenharmony_ci if (!pde) { 50362306a36Sopenharmony_ci misc_deregister(&tosh_device); 50462306a36Sopenharmony_ci return -ENOMEM; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci#endif 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic void __exit toshiba_exit(void) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci remove_proc_entry("toshiba", NULL); 51562306a36Sopenharmony_ci misc_deregister(&tosh_device); 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cimodule_init(toshiba_init); 51962306a36Sopenharmony_cimodule_exit(toshiba_exit); 52062306a36Sopenharmony_ci 521