^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /* toshiba.c -- Linux driver for accessing the SMM on Toshiba laptops
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright (c) 1996-2001 Jonathan A. Buzzard (jonathan@buzzard.org.uk)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Valuable assistance and patches from:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Tom May <tom@you-bastards.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Rob Napier <rnapier@employees.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Fn status port numbers for machine ID's courtesy of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * 0xfc02: Scott Eisert <scott.e@sky-eye.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * 0xfc04: Steve VanDevender <stevev@efn.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * 0xfc08: Garth Berry <garth@itsbruce.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * 0xfc0a: Egbert Eich <eich@xfree86.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * 0xfc10: Andrew Lofthouse <Andrew.Lofthouse@robins.af.mil>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * 0xfc11: Spencer Olson <solson@novell.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) * 0xfc13: Claudius Frankewitz <kryp@gmx.de>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * 0xfc15: Tom May <tom@you-bastards.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * 0xfc17: Dave Konrad <konrad@xenia.it>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * 0xfc1a: George Betzos <betzos@engr.colostate.edu>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * 0xfc1b: Munemasa Wada <munemasa@jnovel.co.jp>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * 0xfc1d: Arthur Liu <armie@slap.mine.nu>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * 0xfc5a: Jacques L'helgoualc'h <lhh@free.fr>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * 0xfcd1: Mr. Dave Konrad <konrad@xenia.it>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * This code is covered by the GNU GPL and you are free to make any
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * changes you wish to it under the terms of the license. However the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * code has the potential to render your computer and/or someone else's
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * unusable. Please proceed with care when modifying the code.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * Note: Unfortunately the laptop hardware can close the System Configuration
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) * Interface on it's own accord. It is therefore necessary for *all*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) * programs using this driver to be aware that *any* SCI call can fail at
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * *any* time. It is up to any program to be aware of this eventuality
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * and take appropriate steps.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) * The information used to write this driver has been obtained by reverse
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) * engineering the software supplied by Toshiba for their portable computers in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) * strict accordance with the European Council Directive 92/250/EEC on the legal
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) * protection of computer programs, and it's implementation into English Law by
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) * the Copyright (Computer Programs) Regulations 1992 (S.I. 1992 No.3233).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) #define TOSH_VERSION "1.11 26/9/2001"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #define TOSH_DEBUG 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) #include <linux/fcntl.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) #include <linux/miscdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) #include <linux/ioport.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #include <asm/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) #include <linux/stat.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) #include <linux/proc_fs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) #include <linux/seq_file.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) #include <linux/mutex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) #include <linux/toshiba.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) MODULE_AUTHOR("Jonathan Buzzard <jonathan@buzzard.org.uk>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) MODULE_DESCRIPTION("Toshiba laptop SMM driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) MODULE_SUPPORTED_DEVICE("toshiba");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) static DEFINE_MUTEX(tosh_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) static int tosh_fn;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) module_param_named(fn, tosh_fn, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) MODULE_PARM_DESC(fn, "User specified Fn key detection port");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) static int tosh_id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) static int tosh_bios;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) static int tosh_date;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) static int tosh_sci;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) static int tosh_fan;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) static long tosh_ioctl(struct file *, unsigned int,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) unsigned long);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) static const struct file_operations tosh_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) .unlocked_ioctl = tosh_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) .llseek = noop_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) static struct miscdevice tosh_device = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) TOSH_MINOR_DEV,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) "toshiba",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) &tosh_fops
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) * Read the Fn key status
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) #ifdef CONFIG_PROC_FS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) static int tosh_fn_status(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) unsigned char scan;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) if (tosh_fn!=0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) scan = inb(tosh_fn);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) local_irq_save(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) outb(0x8e, 0xe4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) scan = inb(0xe5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) local_irq_restore(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) return (int) scan;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) * For the Portage 610CT and the Tecra 700CS/700CDT emulate the HCI fan function
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) static int tosh_emulate_fan(SMMRegisters *regs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) unsigned long eax,ecx,flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) unsigned char al;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) eax = regs->eax & 0xff00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) ecx = regs->ecx & 0xffff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) /* Portage 610CT */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) if (tosh_id==0xfccb) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) if (eax==0xfe00) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) /* fan status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) local_irq_save(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) outb(0xbe, 0xe4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) al = inb(0xe5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) local_irq_restore(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) regs->eax = 0x00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) regs->ecx = (unsigned int) (al & 0x01);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) if ((eax==0xff00) && (ecx==0x0000)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) /* fan off */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) local_irq_save(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) outb(0xbe, 0xe4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) al = inb(0xe5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) outb(0xbe, 0xe4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) outb (al | 0x01, 0xe5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) local_irq_restore(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) regs->eax = 0x00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) regs->ecx = 0x00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) if ((eax==0xff00) && (ecx==0x0001)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) /* fan on */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) local_irq_save(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) outb(0xbe, 0xe4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) al = inb(0xe5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) outb(0xbe, 0xe4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) outb(al & 0xfe, 0xe5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) local_irq_restore(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) regs->eax = 0x00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) regs->ecx = 0x01;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) /* Tecra 700CS/CDT */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) if (tosh_id==0xfccc) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) if (eax==0xfe00) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) /* fan status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) local_irq_save(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) outb(0xe0, 0xe4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) al = inb(0xe5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) local_irq_restore(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) regs->eax = 0x00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) regs->ecx = al & 0x01;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) if ((eax==0xff00) && (ecx==0x0000)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) /* fan off */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) local_irq_save(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) outb(0xe0, 0xe4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) al = inb(0xe5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) outw(0xe0 | ((al & 0xfe) << 8), 0xe4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) local_irq_restore(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) regs->eax = 0x00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) regs->ecx = 0x00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) if ((eax==0xff00) && (ecx==0x0001)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) /* fan on */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) local_irq_save(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) outb(0xe0, 0xe4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) al = inb(0xe5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) outw(0xe0 | ((al | 0x01) << 8), 0xe4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) local_irq_restore(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) regs->eax = 0x00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) regs->ecx = 0x01;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) * Put the laptop into System Management Mode
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) int tosh_smm(SMMRegisters *regs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) int eax;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) asm ("# load the values into the registers\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) "pushl %%eax\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) "movl 0(%%eax),%%edx\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) "push %%edx\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) "movl 4(%%eax),%%ebx\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) "movl 8(%%eax),%%ecx\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) "movl 12(%%eax),%%edx\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) "movl 16(%%eax),%%esi\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) "movl 20(%%eax),%%edi\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) "popl %%eax\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) "# call the System Management mode\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) "inb $0xb2,%%al\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) "# fill out the memory with the values in the registers\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) "xchgl %%eax,(%%esp)\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) "movl %%ebx,4(%%eax)\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) "movl %%ecx,8(%%eax)\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) "movl %%edx,12(%%eax)\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) "movl %%esi,16(%%eax)\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) "movl %%edi,20(%%eax)\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) "popl %%edx\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) "movl %%edx,0(%%eax)\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) "# setup the return value to the carry flag\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) "lahf\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) "shrl $8,%%eax\n\t" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) "andl $1,%%eax\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) : "=a" (eax)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) : "a" (regs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) return eax;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) EXPORT_SYMBOL(tosh_smm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) static long tosh_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) SMMRegisters regs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) SMMRegisters __user *argp = (SMMRegisters __user *)arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) unsigned short ax,bx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) if (!argp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) if (copy_from_user(®s, argp, sizeof(SMMRegisters)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) case TOSH_SMM:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) ax = regs.eax & 0xff00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) bx = regs.ebx & 0xffff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) /* block HCI calls to read/write memory & PCI devices */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) if (((ax==0xff00) || (ax==0xfe00)) && (bx>0x0069))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) /* do we need to emulate the fan ? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) mutex_lock(&tosh_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) if (tosh_fan==1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) if (((ax==0xf300) || (ax==0xf400)) && (bx==0x0004)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) err = tosh_emulate_fan(®s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) mutex_unlock(&tosh_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) err = tosh_smm(®s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) mutex_unlock(&tosh_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) if (copy_to_user(argp, ®s, sizeof(SMMRegisters)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) return (err==0) ? 0:-EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) * Print the information for /proc/toshiba
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) #ifdef CONFIG_PROC_FS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) static int proc_toshiba_show(struct seq_file *m, void *v)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) int key;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) key = tosh_fn_status();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) /* Arguments
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) 0) Linux driver version (this will change if format changes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) 1) Machine ID
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) 2) SCI version
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) 3) BIOS version (major, minor)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) 4) BIOS date (in SCI date format)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) 5) Fn Key status
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) seq_printf(m, "1.1 0x%04x %d.%d %d.%d 0x%04x 0x%02x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) tosh_id,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) (tosh_sci & 0xff00)>>8,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) tosh_sci & 0xff,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) (tosh_bios & 0xff00)>>8,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) tosh_bios & 0xff,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) tosh_date,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) key);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) * Determine which port to use for the Fn key status
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) static void tosh_set_fn_port(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) switch (tosh_id) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) case 0xfc02: case 0xfc04: case 0xfc09: case 0xfc0a: case 0xfc10:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) case 0xfc11: case 0xfc13: case 0xfc15: case 0xfc1a: case 0xfc1b:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) case 0xfc5a:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) tosh_fn = 0x62;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) case 0xfc08: case 0xfc17: case 0xfc1d: case 0xfcd1: case 0xfce0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) case 0xfce2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) tosh_fn = 0x68;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) tosh_fn = 0x00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) * Get the machine identification number of the current model
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) static int tosh_get_machine_id(void __iomem *bios)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) int id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) SMMRegisters regs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) unsigned short bx,cx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) unsigned long address;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) id = (0x100*(int) readb(bios+0xfffe))+((int) readb(bios+0xfffa));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) /* do we have a SCTTable machine identication number on our hands */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) if (id==0xfc2f) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) /* start by getting a pointer into the BIOS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) regs.eax = 0xc000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) regs.ebx = 0x0000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) regs.ecx = 0x0000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) tosh_smm(®s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) bx = (unsigned short) (regs.ebx & 0xffff);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) /* At this point in the Toshiba routines under MS Windows
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) the bx register holds 0xe6f5. However my code is producing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) a different value! For the time being I will just fudge the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) value. This has been verified on a Satellite Pro 430CDT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) Tecra 750CDT, Tecra 780DVD and Satellite 310CDT. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) #if TOSH_DEBUG
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) pr_debug("toshiba: debugging ID ebx=0x%04x\n", regs.ebx);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) bx = 0xe6f5;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) /* now twiddle with our pointer a bit */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) address = bx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) cx = readw(bios + address);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) address = 9+bx+cx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) cx = readw(bios + address);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) address = 0xa+cx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) cx = readw(bios + address);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) /* now construct our machine identification number */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) id = ((cx & 0xff)<<8)+((cx & 0xff00)>>8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) return id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) * Probe for the presence of a Toshiba laptop
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) * returns and non-zero if unable to detect the presence of a Toshiba
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) * laptop, otherwise zero and determines the Machine ID, BIOS version and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) * date, and SCI version.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) static int tosh_probe(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) int i,major,minor,day,year,month,flag;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) unsigned char signature[7] = { 0x54,0x4f,0x53,0x48,0x49,0x42,0x41 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) SMMRegisters regs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) void __iomem *bios = ioremap(0xf0000, 0x10000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) if (!bios)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) /* extra sanity check for the string "TOSHIBA" in the BIOS because
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) some machines that are not Toshiba's pass the next test */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) for (i=0;i<7;i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) if (readb(bios+0xe010+i)!=signature[i]) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) pr_err("toshiba: not a supported Toshiba laptop\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) iounmap(bios);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) /* call the Toshiba SCI support check routine */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) regs.eax = 0xf0f0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) regs.ebx = 0x0000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) regs.ecx = 0x0000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) flag = tosh_smm(®s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) /* if this is not a Toshiba laptop carry flag is set and ah=0x86 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) if ((flag==1) || ((regs.eax & 0xff00)==0x8600)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) pr_err("toshiba: not a supported Toshiba laptop\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) iounmap(bios);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) /* if we get this far then we are running on a Toshiba (probably)! */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) tosh_sci = regs.edx & 0xffff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) /* next get the machine ID of the current laptop */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) tosh_id = tosh_get_machine_id(bios);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) /* get the BIOS version */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) major = readb(bios+0xe009)-'0';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) minor = ((readb(bios+0xe00b)-'0')*10)+(readb(bios+0xe00c)-'0');
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) tosh_bios = (major*0x100)+minor;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) /* get the BIOS date */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) day = ((readb(bios+0xfff5)-'0')*10)+(readb(bios+0xfff6)-'0');
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) month = ((readb(bios+0xfff8)-'0')*10)+(readb(bios+0xfff9)-'0');
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) year = ((readb(bios+0xfffb)-'0')*10)+(readb(bios+0xfffc)-'0');
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) tosh_date = (((year-90) & 0x1f)<<10) | ((month & 0xf)<<6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) | ((day & 0x1f)<<1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) /* in theory we should check the ports we are going to use for the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) fn key detection (and the fan on the Portage 610/Tecra700), and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) then request them to stop other drivers using them. However as
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) the keyboard driver grabs 0x60-0x6f and the pic driver grabs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) 0xa0-0xbf we can't. We just have to live dangerously and use the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) ports anyway, oh boy! */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) /* do we need to emulate the fan? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) if ((tosh_id==0xfccb) || (tosh_id==0xfccc))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) tosh_fan = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) iounmap(bios);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) static int __init toshiba_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) int retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) /* are we running on a Toshiba laptop */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) if (tosh_probe())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) pr_info("Toshiba System Management Mode driver v" TOSH_VERSION "\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) /* set the port to use for Fn status if not specified as a parameter */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) if (tosh_fn==0x00)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) tosh_set_fn_port();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) /* register the device file */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) retval = misc_register(&tosh_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) if (retval < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) #ifdef CONFIG_PROC_FS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) struct proc_dir_entry *pde;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) pde = proc_create_single("toshiba", 0, NULL, proc_toshiba_show);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) if (!pde) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) misc_deregister(&tosh_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) static void __exit toshiba_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) remove_proc_entry("toshiba", NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) misc_deregister(&tosh_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) module_init(toshiba_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) module_exit(toshiba_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521)