diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 17 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/fujitsu-laptop.c | 358 | ||||
-rw-r--r-- | drivers/misc/ibmasm/remote.c | 8 | ||||
-rw-r--r-- | drivers/misc/phantom.c | 97 | ||||
-rw-r--r-- | drivers/misc/sony-laptop.c | 210 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 207 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.h | 37 |
8 files changed, 801 insertions, 134 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 346c44e..cf02ddc 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -111,6 +111,21 @@ config ASUS_LAPTOP If you have an ACPI-compatible ASUS laptop, say Y or M here. +config FUJITSU_LAPTOP + tristate "Fujitsu Laptop Extras" + depends on X86 + depends on ACPI + depends on BACKLIGHT_CLASS_DEVICE + ---help--- + This is a driver for laptops built by Fujitsu: + + * P2xxx/P5xxx/S6xxx/S7xxx series Lifebooks + * Possibly other Fujitsu laptop models + + It adds support for LCD brightness control. + + If you have a Fujitsu laptop, say Y or M here. + config MSI_LAPTOP tristate "MSI Laptop Extras" depends on X86 @@ -134,6 +149,7 @@ config SONY_LAPTOP tristate "Sony Laptop Extras" depends on X86 && ACPI select BACKLIGHT_CLASS_DEVICE + depends on INPUT ---help--- This mini-driver drives the SNC and SPIC devices present in the ACPI BIOS of the Sony Vaio laptops. @@ -156,6 +172,7 @@ config THINKPAD_ACPI select BACKLIGHT_CLASS_DEVICE select HWMON select NVRAM + depends on INPUT ---help--- This is a driver for the IBM and Lenovo ThinkPad laptops. It adds support for Fn-Fx key combinations, Bluetooth control, video diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a24c614..87f2685 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -15,4 +15,5 @@ obj-$(CONFIG_PHANTOM) += phantom.o obj-$(CONFIG_SGI_IOC4) += ioc4.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o +obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c new file mode 100644 index 0000000..d366a6cc --- /dev/null +++ b/drivers/misc/fujitsu-laptop.c @@ -0,0 +1,358 @@ +/*-*-linux-c-*-*/ + +/* + Copyright (C) 2007 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> + Based on earlier work: + Copyright (C) 2003 Shane Spencer <shane@bogomip.com> + Adrian Yee <brewt-fujitsu@brewt.org> + + Templated from msi-laptop.c which is copyright by its respective authors. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + */ + +/* + * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional + * features made available on a range of Fujitsu laptops including the + * P2xxx/P5xxx/S6xxx/S7xxx series. + * + * This driver exports a few files in /sys/devices/platform/fujitsu-laptop/; + * others may be added at a later date. + * + * lcd_level - Screen brightness: contains a single integer in the + * range 0..7. (rw) + * + * In addition to these platform device attributes the driver + * registers itself in the Linux backlight control subsystem and is + * available to userspace under /sys/class/backlight/fujitsu-laptop/. + * + * This driver has been tested on a Fujitsu Lifebook S7020. It should + * work on most P-series and S-series Lifebooks, but YMMV. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/dmi.h> +#include <linux/backlight.h> +#include <linux/platform_device.h> +#include <linux/autoconf.h> + +#define FUJITSU_DRIVER_VERSION "0.3" + +#define FUJITSU_LCD_N_LEVELS 8 + +#define ACPI_FUJITSU_CLASS "fujitsu" +#define ACPI_FUJITSU_HID "FUJ02B1" +#define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI extras driver" +#define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1" + +struct fujitsu_t { + acpi_handle acpi_handle; + struct backlight_device *bl_device; + struct platform_device *pf_device; + + unsigned long fuj02b1_state; + unsigned int brightness_changed; + unsigned int brightness_level; +}; + +static struct fujitsu_t *fujitsu; + +/* Hardware access */ + +static int set_lcd_level(int level) +{ + acpi_status status = AE_OK; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list arg_list = { 1, &arg0 }; + acpi_handle handle = NULL; + + if (level < 0 || level >= FUJITSU_LCD_N_LEVELS) + return -EINVAL; + + if (!fujitsu) + return -EINVAL; + + status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SBLL not present\n")); + return -ENODEV; + } + + arg0.integer.value = level; + + status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return 0; +} + +static int get_lcd_level(void) +{ + unsigned long state = 0; + acpi_status status = AE_OK; + + // Get the Brightness + status = + acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state); + if (status < 0) + return status; + + fujitsu->fuj02b1_state = state; + fujitsu->brightness_level = state & 0x0fffffff; + + if (state & 0x80000000) + fujitsu->brightness_changed = 1; + else + fujitsu->brightness_changed = 0; + + if (status < 0) + return status; + + return fujitsu->brightness_level; +} + +/* Backlight device stuff */ + +static int bl_get_brightness(struct backlight_device *b) +{ + return get_lcd_level(); +} + +static int bl_update_status(struct backlight_device *b) +{ + return set_lcd_level(b->props.brightness); +} + +static struct backlight_ops fujitsubl_ops = { + .get_brightness = bl_get_brightness, + .update_status = bl_update_status, +}; + +/* Platform device */ + +static ssize_t show_lcd_level(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + int ret; + + ret = get_lcd_level(); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", ret); +} + +static ssize_t store_lcd_level(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + + int level, ret; + + if (sscanf(buf, "%i", &level) != 1 + || (level < 0 || level >= FUJITSU_LCD_N_LEVELS)) + return -EINVAL; + + ret = set_lcd_level(level); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); + +static struct attribute *fujitsupf_attributes[] = { + &dev_attr_lcd_level.attr, + NULL +}; + +static struct attribute_group fujitsupf_attribute_group = { + .attrs = fujitsupf_attributes +}; + +static struct platform_driver fujitsupf_driver = { + .driver = { + .name = "fujitsu-laptop", + .owner = THIS_MODULE, + } +}; + +/* ACPI device */ + +int acpi_fujitsu_add(struct acpi_device *device) +{ + int result = 0; + int state = 0; + + ACPI_FUNCTION_TRACE("acpi_fujitsu_add"); + + if (!device) + return -EINVAL; + + fujitsu->acpi_handle = device->handle; + sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME); + sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); + acpi_driver_data(device) = fujitsu; + + result = acpi_bus_get_power(fujitsu->acpi_handle, &state); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error reading power state\n")); + goto end; + } + + printk(KERN_INFO PREFIX "%s [%s] (%s)\n", + acpi_device_name(device), acpi_device_bid(device), + !device->power.state ? "on" : "off"); + + end: + + return result; +} + +int acpi_fujitsu_remove(struct acpi_device *device, int type) +{ + ACPI_FUNCTION_TRACE("acpi_fujitsu_remove"); + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + fujitsu->acpi_handle = 0; + + return 0; +} + +static const struct acpi_device_id fujitsu_device_ids[] = { + {ACPI_FUJITSU_HID, 0}, + {"", 0}, +}; + +static struct acpi_driver acpi_fujitsu_driver = { + .name = ACPI_FUJITSU_DRIVER_NAME, + .class = ACPI_FUJITSU_CLASS, + .ids = fujitsu_device_ids, + .ops = { + .add = acpi_fujitsu_add, + .remove = acpi_fujitsu_remove, + }, +}; + +/* Initialization */ + +static int __init fujitsu_init(void) +{ + int ret, result; + + if (acpi_disabled) + return -ENODEV; + + fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL); + if (!fujitsu) + return -ENOMEM; + memset(fujitsu, 0, sizeof(struct fujitsu_t)); + + result = acpi_bus_register_driver(&acpi_fujitsu_driver); + if (result < 0) { + ret = -ENODEV; + goto fail_acpi; + } + + /* Register backlight stuff */ + + fujitsu->bl_device = + backlight_device_register("fujitsu-laptop", NULL, NULL, + &fujitsubl_ops); + if (IS_ERR(fujitsu->bl_device)) + return PTR_ERR(fujitsu->bl_device); + + fujitsu->bl_device->props.max_brightness = FUJITSU_LCD_N_LEVELS - 1; + ret = platform_driver_register(&fujitsupf_driver); + if (ret) + goto fail_backlight; + + /* Register platform stuff */ + + fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1); + if (!fujitsu->pf_device) { + ret = -ENOMEM; + goto fail_platform_driver; + } + + ret = platform_device_add(fujitsu->pf_device); + if (ret) + goto fail_platform_device1; + + ret = + sysfs_create_group(&fujitsu->pf_device->dev.kobj, + &fujitsupf_attribute_group); + if (ret) + goto fail_platform_device2; + + printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION + " successfully loaded.\n"); + + return 0; + + fail_platform_device2: + + platform_device_del(fujitsu->pf_device); + + fail_platform_device1: + + platform_device_put(fujitsu->pf_device); + + fail_platform_driver: + + platform_driver_unregister(&fujitsupf_driver); + + fail_backlight: + + backlight_device_unregister(fujitsu->bl_device); + + fail_acpi: + + kfree(fujitsu); + + return ret; +} + +static void __exit fujitsu_cleanup(void) +{ + sysfs_remove_group(&fujitsu->pf_device->dev.kobj, + &fujitsupf_attribute_group); + platform_device_unregister(fujitsu->pf_device); + platform_driver_unregister(&fujitsupf_driver); + backlight_device_unregister(fujitsu->bl_device); + + acpi_bus_unregister_driver(&acpi_fujitsu_driver); + + kfree(fujitsu); + + printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n"); +} + +module_init(fujitsu_init); +module_exit(fujitsu_cleanup); + +MODULE_AUTHOR("Jonathan Woithe"); +MODULE_DESCRIPTION("Fujitsu laptop extras support"); +MODULE_VERSION(FUJITSU_DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/ibmasm/remote.c b/drivers/misc/ibmasm/remote.c index 0550ce0..1d9defb 100644 --- a/drivers/misc/ibmasm/remote.c +++ b/drivers/misc/ibmasm/remote.c @@ -226,9 +226,9 @@ int ibmasm_init_remote_input_dev(struct service_processor *sp) mouse_dev->id.product = pdev->device; mouse_dev->id.version = 1; mouse_dev->dev.parent = sp->dev; - mouse_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - mouse_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | - BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + mouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + mouse_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); set_bit(BTN_TOUCH, mouse_dev->keybit); mouse_dev->name = "ibmasm RSA I remote mouse"; input_set_abs_params(mouse_dev, ABS_X, 0, MOUSE_X_MAX, 0, 0); @@ -239,7 +239,7 @@ int ibmasm_init_remote_input_dev(struct service_processor *sp) keybd_dev->id.product = pdev->device; keybd_dev->id.version = 2; keybd_dev->dev.parent = sp->dev; - keybd_dev->evbit[0] = BIT(EV_KEY); + keybd_dev->evbit[0] = BIT_MASK(EV_KEY); keybd_dev->name = "ibmasm RSA I remote keyboard"; for (i = 0; i < XLATE_SIZE; i++) { diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 5108b7c..cd221fd 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -9,6 +9,7 @@ * You need an userspace library to cooperate with this driver. It (and other * info) may be obtained here: * http://www.fi.muni.cz/~xslaby/phantom.html + * or alternatively, you might use OpenHaptics provided by Sensable. */ #include <linux/kernel.h> @@ -24,13 +25,14 @@ #include <asm/atomic.h> #include <asm/io.h> -#define PHANTOM_VERSION "n0.9.5" +#define PHANTOM_VERSION "n0.9.7" #define PHANTOM_MAX_MINORS 8 #define PHN_IRQCTL 0x4c /* irq control in caddr space */ #define PHB_RUNNING 1 +#define PHB_NOT_OH 2 static struct class *phantom_class; static int phantom_major; @@ -47,7 +49,11 @@ struct phantom_device { struct cdev cdev; struct mutex open_lock; - spinlock_t ioctl_lock; + spinlock_t regs_lock; + + /* used in NOT_OH mode */ + struct phm_regs oregs; + u32 ctl_reg; }; static unsigned char phantom_devices[PHANTOM_MAX_MINORS]; @@ -82,6 +88,7 @@ static long phantom_ioctl(struct file *file, unsigned int cmd, struct phm_regs rs; struct phm_reg r; void __user *argp = (void __user *)arg; + unsigned long flags; unsigned int i; if (_IOC_TYPE(cmd) != PH_IOC_MAGIC || @@ -96,32 +103,45 @@ static long phantom_ioctl(struct file *file, unsigned int cmd, if (r.reg > 7) return -EINVAL; - spin_lock(&dev->ioctl_lock); + spin_lock_irqsave(&dev->regs_lock, flags); if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) && phantom_status(dev, dev->status | PHB_RUNNING)){ - spin_unlock(&dev->ioctl_lock); + spin_unlock_irqrestore(&dev->regs_lock, flags); return -ENODEV; } pr_debug("phantom: writing %x to %u\n", r.value, r.reg); + + /* preserve amp bit (don't allow to change it when in NOT_OH) */ + if (r.reg == PHN_CONTROL && (dev->status & PHB_NOT_OH)) { + r.value &= ~PHN_CTL_AMP; + r.value |= dev->ctl_reg & PHN_CTL_AMP; + dev->ctl_reg = r.value; + } + iowrite32(r.value, dev->iaddr + r.reg); ioread32(dev->iaddr); /* PCI posting */ if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ)) phantom_status(dev, dev->status & ~PHB_RUNNING); - spin_unlock(&dev->ioctl_lock); + spin_unlock_irqrestore(&dev->regs_lock, flags); break; case PHN_SET_REGS: if (copy_from_user(&rs, argp, sizeof(rs))) return -EFAULT; pr_debug("phantom: SRS %u regs %x\n", rs.count, rs.mask); - spin_lock(&dev->ioctl_lock); - for (i = 0; i < min(rs.count, 8U); i++) - if ((1 << i) & rs.mask) - iowrite32(rs.values[i], dev->oaddr + i); - ioread32(dev->iaddr); /* PCI posting */ - spin_unlock(&dev->ioctl_lock); + spin_lock_irqsave(&dev->regs_lock, flags); + if (dev->status & PHB_NOT_OH) + memcpy(&dev->oregs, &rs, sizeof(rs)); + else { + u32 m = min(rs.count, 8U); + for (i = 0; i < m; i++) + if (rs.mask & BIT(i)) + iowrite32(rs.values[i], dev->oaddr + i); + ioread32(dev->iaddr); /* PCI posting */ + } + spin_unlock_irqrestore(&dev->regs_lock, flags); break; case PHN_GET_REG: if (copy_from_user(&r, argp, sizeof(r))) @@ -135,20 +155,35 @@ static long phantom_ioctl(struct file *file, unsigned int cmd, if (copy_to_user(argp, &r, sizeof(r))) return -EFAULT; break; - case PHN_GET_REGS: + case PHN_GET_REGS: { + u32 m; + if (copy_from_user(&rs, argp, sizeof(rs))) return -EFAULT; + m = min(rs.count, 8U); + pr_debug("phantom: GRS %u regs %x\n", rs.count, rs.mask); - spin_lock(&dev->ioctl_lock); - for (i = 0; i < min(rs.count, 8U); i++) - if ((1 << i) & rs.mask) + spin_lock_irqsave(&dev->regs_lock, flags); + for (i = 0; i < m; i++) + if (rs.mask & BIT(i)) rs.values[i] = ioread32(dev->iaddr + i); - spin_unlock(&dev->ioctl_lock); + spin_unlock_irqrestore(&dev->regs_lock, flags); if (copy_to_user(argp, &rs, sizeof(rs))) return -EFAULT; break; + } case PHN_NOT_OH: + spin_lock_irqsave(&dev->regs_lock, flags); + if (dev->status & PHB_RUNNING) { + printk(KERN_ERR "phantom: you need to set NOT_OH " + "before you start the device!\n"); + spin_unlock_irqrestore(&dev->regs_lock, flags); + return -EINVAL; + } + dev->status |= PHB_NOT_OH; + spin_unlock_irqrestore(&dev->regs_lock, flags); + break; default: return -ENOTTY; } @@ -171,8 +206,11 @@ static int phantom_open(struct inode *inode, struct file *file) return -EINVAL; } + WARN_ON(dev->status & PHB_NOT_OH); + file->private_data = dev; + atomic_set(&dev->counter, 0); dev->opened++; mutex_unlock(&dev->open_lock); @@ -187,6 +225,7 @@ static int phantom_release(struct inode *inode, struct file *file) dev->opened = 0; phantom_status(dev, dev->status & ~PHB_RUNNING); + dev->status &= ~PHB_NOT_OH; mutex_unlock(&dev->open_lock); @@ -220,12 +259,32 @@ static struct file_operations phantom_file_ops = { static irqreturn_t phantom_isr(int irq, void *data) { struct phantom_device *dev = data; + unsigned int i; + u32 ctl; - if (!(ioread32(dev->iaddr + PHN_CONTROL) & PHN_CTL_IRQ)) + spin_lock(&dev->regs_lock); + ctl = ioread32(dev->iaddr + PHN_CONTROL); + if (!(ctl & PHN_CTL_IRQ)) { + spin_unlock(&dev->regs_lock); return IRQ_NONE; + } iowrite32(0, dev->iaddr); iowrite32(0xc0, dev->iaddr); + + if (dev->status & PHB_NOT_OH) { + struct phm_regs *r = &dev->oregs; + u32 m = min(r->count, 8U); + + for (i = 0; i < m; i++) + if (r->mask & BIT(i)) + iowrite32(r->values[i], dev->oaddr + i); + + dev->ctl_reg ^= PHN_CTL_AMP; + iowrite32(dev->ctl_reg, dev->iaddr + PHN_CONTROL); + } + spin_unlock(&dev->regs_lock); + ioread32(dev->iaddr); /* PCI posting */ atomic_inc(&dev->counter); @@ -297,7 +356,7 @@ static int __devinit phantom_probe(struct pci_dev *pdev, } mutex_init(&pht->open_lock); - spin_lock_init(&pht->ioctl_lock); + spin_lock_init(&pht->regs_lock); init_waitqueue_head(&pht->wait); cdev_init(&pht->cdev, &phantom_file_ops); pht->cdev.owner = THIS_MODULE; @@ -378,6 +437,8 @@ static int phantom_suspend(struct pci_dev *pdev, pm_message_t state) iowrite32(0, dev->caddr + PHN_IRQCTL); ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */ + synchronize_irq(pdev->irq); + return 0; } diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index e73a71f0..1bfbb87 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c @@ -411,9 +411,9 @@ static int sony_laptop_setup_input(void) jog_dev->id.bustype = BUS_ISA; jog_dev->id.vendor = PCI_VENDOR_ID_SONY; - jog_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); - jog_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_MIDDLE); - jog_dev->relbit[0] = BIT(REL_WHEEL); + jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE); + jog_dev->relbit[0] = BIT_MASK(REL_WHEEL); error = input_register_device(jog_dev); if (error) @@ -1173,7 +1173,8 @@ static struct acpi_driver sony_nc_driver = { #define SONYPI_TYPE3_OFFSET 0x12 struct sony_pic_ioport { - struct acpi_resource_io io; + struct acpi_resource_io io1; + struct acpi_resource_io io2; struct list_head list; }; @@ -1443,11 +1444,11 @@ static u8 sony_pic_call1(u8 dev) { u8 v1, v2; - wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, + wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); - outb(dev, spic_dev.cur_ioport->io.minimum + 4); - v1 = inb_p(spic_dev.cur_ioport->io.minimum + 4); - v2 = inb_p(spic_dev.cur_ioport->io.minimum); + outb(dev, spic_dev.cur_ioport->io1.minimum + 4); + v1 = inb_p(spic_dev.cur_ioport->io1.minimum + 4); + v2 = inb_p(spic_dev.cur_ioport->io1.minimum); dprintk("sony_pic_call1: 0x%.4x\n", (v2 << 8) | v1); return v2; } @@ -1456,13 +1457,13 @@ static u8 sony_pic_call2(u8 dev, u8 fn) { u8 v1; - wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, + wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); - outb(dev, spic_dev.cur_ioport->io.minimum + 4); - wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, + outb(dev, spic_dev.cur_ioport->io1.minimum + 4); + wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); - outb(fn, spic_dev.cur_ioport->io.minimum); - v1 = inb_p(spic_dev.cur_ioport->io.minimum); + outb(fn, spic_dev.cur_ioport->io1.minimum); + v1 = inb_p(spic_dev.cur_ioport->io1.minimum); dprintk("sony_pic_call2: 0x%.4x\n", v1); return v1; } @@ -1471,13 +1472,13 @@ static u8 sony_pic_call3(u8 dev, u8 fn, u8 v) { u8 v1; - wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG); - outb(dev, spic_dev.cur_ioport->io.minimum + 4); - wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG); - outb(fn, spic_dev.cur_ioport->io.minimum); - wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG); - outb(v, spic_dev.cur_ioport->io.minimum); - v1 = inb_p(spic_dev.cur_ioport->io.minimum); + wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); + outb(dev, spic_dev.cur_ioport->io1.minimum + 4); + wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); + outb(fn, spic_dev.cur_ioport->io1.minimum); + wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); + outb(v, spic_dev.cur_ioport->io1.minimum); + v1 = inb_p(spic_dev.cur_ioport->io1.minimum); dprintk("sony_pic_call3: 0x%.4x\n", v1); return v1; } @@ -2074,7 +2075,18 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) switch (resource->type) { case ACPI_RESOURCE_TYPE_START_DEPENDENT: + { + /* start IO enumeration */ + struct sony_pic_ioport *ioport = kzalloc(sizeof(*ioport), GFP_KERNEL); + if (!ioport) + return AE_ERROR; + + list_add(&ioport->list, &dev->ioports); + return AE_OK; + } + case ACPI_RESOURCE_TYPE_END_DEPENDENT: + /* end IO enumeration */ return AE_OK; case ACPI_RESOURCE_TYPE_IRQ: @@ -2101,7 +2113,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) if (!interrupt) return AE_ERROR; - list_add_tail(&interrupt->list, &dev->interrupts); + list_add(&interrupt->list, &dev->interrupts); interrupt->irq.triggering = p->triggering; interrupt->irq.polarity = p->polarity; interrupt->irq.sharable = p->sharable; @@ -2113,18 +2125,27 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) case ACPI_RESOURCE_TYPE_IO: { struct acpi_resource_io *io = &resource->data.io; - struct sony_pic_ioport *ioport = NULL; + struct sony_pic_ioport *ioport = + list_first_entry(&dev->ioports, struct sony_pic_ioport, list); if (!io) { dprintk("Blank IO resource\n"); return AE_OK; } - ioport = kzalloc(sizeof(*ioport), GFP_KERNEL); - if (!ioport) + if (!ioport->io1.minimum) { + memcpy(&ioport->io1, io, sizeof(*io)); + dprintk("IO1 at 0x%.4x (0x%.2x)\n", ioport->io1.minimum, + ioport->io1.address_length); + } + else if (!ioport->io2.minimum) { + memcpy(&ioport->io2, io, sizeof(*io)); + dprintk("IO2 at 0x%.4x (0x%.2x)\n", ioport->io2.minimum, + ioport->io2.address_length); + } + else { + printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n"); return AE_ERROR; - - list_add_tail(&ioport->list, &dev->ioports); - memcpy(&ioport->io, io, sizeof(*io)); + } return AE_OK; } default: @@ -2199,10 +2220,22 @@ static int sony_pic_enable(struct acpi_device *device, { acpi_status status; int result = 0; + /* Type 1 resource layout is: + * IO + * IO + * IRQNoFlags + * End + * + * Type 2 and 3 resource layout is: + * IO + * IRQNoFlags + * End + */ struct { - struct acpi_resource io_res; - struct acpi_resource irq_res; - struct acpi_resource end; + struct acpi_resource res1; + struct acpi_resource res2; + struct acpi_resource res3; + struct acpi_resource res4; } *resource; struct acpi_buffer buffer = { 0, NULL }; @@ -2217,21 +2250,49 @@ static int sony_pic_enable(struct acpi_device *device, buffer.length = sizeof(*resource) + 1; buffer.pointer = resource; - /* setup io resource */ - resource->io_res.type = ACPI_RESOURCE_TYPE_IO; - resource->io_res.length = sizeof(struct acpi_resource); - memcpy(&resource->io_res.data.io, &ioport->io, - sizeof(struct acpi_resource_io)); + /* setup Type 1 resources */ + if (spic_dev.model == SONYPI_DEVICE_TYPE1) { - /* setup irq resource */ - resource->irq_res.type = ACPI_RESOURCE_TYPE_IRQ; - resource->irq_res.length = sizeof(struct acpi_resource); - memcpy(&resource->irq_res.data.irq, &irq->irq, - sizeof(struct acpi_resource_irq)); - /* we requested a shared irq */ - resource->irq_res.data.irq.sharable = ACPI_SHARED; + /* setup io resources */ + resource->res1.type = ACPI_RESOURCE_TYPE_IO; + resource->res1.length = sizeof(struct acpi_resource); + memcpy(&resource->res1.data.io, &ioport->io1, + sizeof(struct acpi_resource_io)); - resource->end.type = ACPI_RESOURCE_TYPE_END_TAG; + resource->res2.type = ACPI_RESOURCE_TYPE_IO; + resource->res2.length = sizeof(struct acpi_resource); + memcpy(&resource->res2.data.io, &ioport->io2, + sizeof(struct acpi_resource_io)); + + /* setup irq resource */ + resource->res3.type = ACPI_RESOURCE_TYPE_IRQ; + resource->res3.length = sizeof(struct acpi_resource); + memcpy(&resource->res3.data.irq, &irq->irq, + sizeof(struct acpi_resource_irq)); + /* we requested a shared irq */ + resource->res3.data.irq.sharable = ACPI_SHARED; + + resource->res4.type = ACPI_RESOURCE_TYPE_END_TAG; + + } + /* setup Type 2/3 resources */ + else { + /* setup io resource */ + resource->res1.type = ACPI_RESOURCE_TYPE_IO; + resource->res1.length = sizeof(struct acpi_resource); + memcpy(&resource->res1.data.io, &ioport->io1, + sizeof(struct acpi_resource_io)); + + /* setup irq resource */ + resource->res2.type = ACPI_RESOURCE_TYPE_IRQ; + resource->res2.length = sizeof(struct acpi_resource); + memcpy(&resource->res2.data.irq, &irq->irq, + sizeof(struct acpi_resource_irq)); + /* we requested a shared irq */ + resource->res2.data.irq.sharable = ACPI_SHARED; + + resource->res3.type = ACPI_RESOURCE_TYPE_END_TAG; + } /* Attempt to set the resource */ dprintk("Evaluating _SRS\n"); @@ -2239,7 +2300,7 @@ static int sony_pic_enable(struct acpi_device *device, /* check for total failure */ if (ACPI_FAILURE(status)) { - printk(KERN_ERR DRV_PFX "Error evaluating _SRS"); + printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n"); result = -ENODEV; goto end; } @@ -2268,11 +2329,14 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id) struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id; - ev = inb_p(dev->cur_ioport->io.minimum); - data_mask = inb_p(dev->cur_ioport->io.minimum + dev->evport_offset); + ev = inb_p(dev->cur_ioport->io1.minimum); + if (dev->cur_ioport->io2.minimum) + data_mask = inb_p(dev->cur_ioport->io2.minimum); + else + data_mask = inb_p(dev->cur_ioport->io1.minimum + dev->evport_offset); dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n", - ev, data_mask, dev->cur_ioport->io.minimum, dev->evport_offset); + ev, data_mask, dev->cur_ioport->io1.minimum, dev->evport_offset); if (ev == 0x00 || ev == 0xff) return IRQ_HANDLED; @@ -2323,8 +2387,11 @@ static int sony_pic_remove(struct acpi_device *device, int type) } free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); - release_region(spic_dev.cur_ioport->io.minimum, - spic_dev.cur_ioport->io.address_length); + release_region(spic_dev.cur_ioport->io1.minimum, + spic_dev.cur_ioport->io1.address_length); + if (spic_dev.cur_ioport->io2.minimum) + release_region(spic_dev.cur_ioport->io2.minimum, + spic_dev.cur_ioport->io2.address_length); sonypi_compat_exit(); @@ -2397,14 +2464,36 @@ static int sony_pic_add(struct acpi_device *device) goto err_remove_input; /* request io port */ - list_for_each_entry(io, &spic_dev.ioports, list) { - if (request_region(io->io.minimum, io->io.address_length, + list_for_each_entry_reverse(io, &spic_dev.ioports, list) { + if (request_region(io->io1.minimum, io->io1.address_length, "Sony Programable I/O Device")) { - dprintk("I/O port: 0x%.4x (0x%.4x) + 0x%.2x\n", - io->io.minimum, io->io.maximum, - io->io.address_length); - spic_dev.cur_ioport = io; - break; + dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n", + io->io1.minimum, io->io1.maximum, + io->io1.address_length); + /* Type 1 have 2 ioports */ + if (io->io2.minimum) { + if (request_region(io->io2.minimum, + io->io2.address_length, + "Sony Programable I/O Device")) { + dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n", + io->io2.minimum, io->io2.maximum, + io->io2.address_length); + spic_dev.cur_ioport = io; + break; + } + else { + dprintk("Unable to get I/O port2: " + "0x%.4x (0x%.4x) + 0x%.2x\n", + io->io2.minimum, io->io2.maximum, + io->io2.address_length); + release_region(io->io1.minimum, + io->io1.address_length); + } + } + else { + spic_dev.cur_ioport = io; + break; + } } } if (!spic_dev.cur_ioport) { @@ -2414,7 +2503,7 @@ static int sony_pic_add(struct acpi_device *device) } /* request IRQ */ - list_for_each_entry(irq, &spic_dev.interrupts, list) { + list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) { if (!request_irq(irq->irq.interrupts[0], sony_pic_irq, IRQF_SHARED, "sony-laptop", &spic_dev)) { dprintk("IRQ: %d - triggering: %d - " @@ -2462,8 +2551,11 @@ err_free_irq: free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); err_release_region: - release_region(spic_dev.cur_ioport->io.minimum, - spic_dev.cur_ioport->io.address_length); + release_region(spic_dev.cur_ioport->io1.minimum, + spic_dev.cur_ioport->io1.address_length); + if (spic_dev.cur_ioport->io2.minimum) + release_region(spic_dev.cur_ioport->io2.minimum, + spic_dev.cur_ioport->io2.address_length); err_remove_compat: sonypi_compat_exit(); diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 81e068f..e953276 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -22,7 +22,7 @@ */ #define IBM_VERSION "0.16" -#define TPACPI_SYSFS_VERSION 0x010000 +#define TPACPI_SYSFS_VERSION 0x020000 /* * Changelog: @@ -117,6 +117,12 @@ IBM_BIOS_MODULE_ALIAS("K[U,X-Z]"); #define __unused __attribute__ ((unused)) +static enum { + TPACPI_LIFE_INIT = 0, + TPACPI_LIFE_RUNNING, + TPACPI_LIFE_EXITING, +} tpacpi_lifecycle; + /**************************************************************************** **************************************************************************** * @@ -342,6 +348,9 @@ static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data) { struct ibm_struct *ibm = data; + if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING) + return; + if (!ibm || !ibm->acpi || !ibm->acpi->notify) return; @@ -517,8 +526,10 @@ static char *next_cmd(char **cmds) ****************************************************************************/ static struct platform_device *tpacpi_pdev; +static struct platform_device *tpacpi_sensors_pdev; static struct device *tpacpi_hwmon; static struct input_dev *tpacpi_inputdev; +static struct mutex tpacpi_inputdev_send_mutex; static int tpacpi_resume_handler(struct platform_device *pdev) @@ -543,6 +554,12 @@ static struct platform_driver tpacpi_pdriver = { .resume = tpacpi_resume_handler, }; +static struct platform_driver tpacpi_hwmon_pdriver = { + .driver = { + .name = IBM_HWMON_DRVR_NAME, + .owner = THIS_MODULE, + }, +}; /************************************************************************* * thinkpad-acpi driver attributes @@ -692,6 +709,8 @@ static int parse_strtoul(const char *buf, { char *endp; + while (*buf && isspace(*buf)) + buf++; *value = simple_strtoul(buf, &endp, 0); while (*endp && isspace(*endp)) endp++; @@ -989,6 +1008,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) int res, i; int status; + int hkeyv; vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); @@ -1014,18 +1034,35 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) return res; /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, - A30, R30, R31, T20-22, X20-21, X22-24 */ - tp_features.hotkey_mask = - acpi_evalf(hkey_handle, NULL, "DHKN", "qv"); + A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking + for HKEY interface version 0x100 */ + if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { + if ((hkeyv >> 8) != 1) { + printk(IBM_ERR "unknown version of the " + "HKEY interface: 0x%x\n", hkeyv); + printk(IBM_ERR "please report this to %s\n", + IBM_MAIL); + } else { + /* + * MHKV 0x100 in A31, R40, R40e, + * T4x, X31, and later + * */ + tp_features.hotkey_mask = 1; + } + } vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n", str_supported(tp_features.hotkey_mask)); if (tp_features.hotkey_mask) { - /* MHKA available in A31, R40, R40e, T4x, X31, and later */ if (!acpi_evalf(hkey_handle, &hotkey_all_mask, - "MHKA", "qd")) + "MHKA", "qd")) { + printk(IBM_ERR + "missing MHKA handler, " + "please report this to %s\n", + IBM_MAIL); hotkey_all_mask = 0x080cU; /* FN+F12, FN+F4, FN+F3 */ + } } res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask); @@ -1131,6 +1168,8 @@ static void tpacpi_input_send_key(unsigned int scancode, unsigned int keycode) { if (keycode != KEY_RESERVED) { + mutex_lock(&tpacpi_inputdev_send_mutex); + input_report_key(tpacpi_inputdev, keycode, 1); if (keycode == KEY_UNKNOWN) input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, @@ -1142,6 +1181,8 @@ static void tpacpi_input_send_key(unsigned int scancode, input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); input_sync(tpacpi_inputdev); + + mutex_unlock(&tpacpi_inputdev_send_mutex); } } @@ -1149,18 +1190,47 @@ static void tpacpi_input_send_radiosw(void) { int wlsw; - if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) + mutex_lock(&tpacpi_inputdev_send_mutex); + + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { input_report_switch(tpacpi_inputdev, SW_RADIO, !!wlsw); + input_sync(tpacpi_inputdev); + } + + mutex_unlock(&tpacpi_inputdev_send_mutex); } static void hotkey_notify(struct ibm_struct *ibm, u32 event) { u32 hkey; unsigned int keycode, scancode; - int send_acpi_ev = 0; + int send_acpi_ev; + int ignore_acpi_ev; + + if (event != 0x80) { + printk(IBM_ERR "unknown HKEY notification event %d\n", event); + /* forward it to userspace, maybe it knows how to handle it */ + acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, + ibm->acpi->device->dev.bus_id, + event, 0); + return; + } + + while (1) { + if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) { + printk(IBM_ERR "failed to retrieve HKEY event\n"); + return; + } + + if (hkey == 0) { + /* queue empty */ + return; + } + + send_acpi_ev = 0; + ignore_acpi_ev = 0; - if (event == 0x80 && acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) { switch (hkey >> 12) { case 1: /* 0x1000-0x1FFF: key presses */ @@ -1182,9 +1252,11 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) * eat up known LID events */ if (hkey != 0x5001 && hkey != 0x5002) { printk(IBM_ERR - "unknown LID-related hotkey event: 0x%04x\n", - hkey); + "unknown LID-related HKEY event: 0x%04x\n", + hkey); send_acpi_ev = 1; + } else { + ignore_acpi_ev = 1; } break; case 7: @@ -1202,21 +1274,18 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) printk(IBM_NOTICE "unhandled HKEY event 0x%04x\n", hkey); send_acpi_ev = 1; } - } else { - printk(IBM_ERR "unknown hotkey notification event %d\n", event); - hkey = 0; - send_acpi_ev = 1; - } - /* Legacy events */ - if (send_acpi_ev || hotkey_report_mode < 2) - acpi_bus_generate_proc_event(ibm->acpi->device, event, hkey); + /* Legacy events */ + if (!ignore_acpi_ev && (send_acpi_ev || hotkey_report_mode < 2)) { + acpi_bus_generate_proc_event(ibm->acpi->device, event, hkey); + } - /* netlink events */ - if (send_acpi_ev) { - acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, - ibm->acpi->device->dev.bus_id, - event, hkey); + /* netlink events */ + if (!ignore_acpi_ev && send_acpi_ev) { + acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, + ibm->acpi->device->dev.bus_id, + event, hkey); + } } } @@ -2812,7 +2881,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm) switch(thermal_read_mode) { case TPACPI_THERMAL_TPEC_16: - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, &thermal_temp_input16_group); if (res) return res; @@ -2820,7 +2889,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm) case TPACPI_THERMAL_TPEC_8: case TPACPI_THERMAL_ACPI_TMP07: case TPACPI_THERMAL_ACPI_UPDT: - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, &thermal_temp_input8_group); if (res) return res; @@ -2837,13 +2906,13 @@ static void thermal_exit(void) { switch(thermal_read_mode) { case TPACPI_THERMAL_TPEC_16: - sysfs_remove_group(&tpacpi_pdev->dev.kobj, + sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &thermal_temp_input16_group); break; case TPACPI_THERMAL_TPEC_8: case TPACPI_THERMAL_ACPI_TMP07: case TPACPI_THERMAL_ACPI_UPDT: - sysfs_remove_group(&tpacpi_pdev->dev.kobj, + sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &thermal_temp_input16_group); break; case TPACPI_THERMAL_NONE: @@ -3626,7 +3695,7 @@ static struct device_attribute dev_attr_fan_fan1_input = __ATTR(fan1_input, S_IRUGO, fan_fan1_input_show, NULL); -/* sysfs fan fan_watchdog (driver) ------------------------------------- */ +/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ static ssize_t fan_fan_watchdog_show(struct device_driver *drv, char *buf) { @@ -3768,10 +3837,10 @@ static int __init fan_init(struct ibm_init_struct *iibm) if (fan_status_access_mode != TPACPI_FAN_NONE || fan_control_access_mode != TPACPI_FAN_WR_NONE) { - rc = sysfs_create_group(&tpacpi_pdev->dev.kobj, + rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group); if (!(rc < 0)) - rc = driver_create_file(&tpacpi_pdriver.driver, + rc = driver_create_file(&tpacpi_hwmon_pdriver.driver, &driver_attr_fan_watchdog); if (rc < 0) return rc; @@ -3854,8 +3923,8 @@ static void fan_exit(void) vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n"); /* FIXME: can we really do this unconditionally? */ - sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group); - driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog); + sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group); + driver_remove_file(&tpacpi_hwmon_pdriver.driver, &driver_attr_fan_watchdog); cancel_delayed_work(&fan_watchdog_task); flush_scheduled_work(); @@ -3888,6 +3957,9 @@ static void fan_watchdog_fire(struct work_struct *ignored) { int rc; + if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING) + return; + printk(IBM_NOTICE "fan watchdog: enabling fan\n"); rc = fan_set_enable(); if (rc < 0) { @@ -3908,7 +3980,8 @@ static void fan_watchdog_reset(void) if (fan_watchdog_active) cancel_delayed_work(&fan_watchdog_task); - if (fan_watchdog_maxinterval > 0) { + if (fan_watchdog_maxinterval > 0 && + tpacpi_lifecycle != TPACPI_LIFE_EXITING) { fan_watchdog_active = 1; if (!schedule_delayed_work(&fan_watchdog_task, msecs_to_jiffies(fan_watchdog_maxinterval @@ -4302,6 +4375,19 @@ static struct ibm_struct fan_driver_data = { **************************************************************************** ****************************************************************************/ +/* sysfs name ---------------------------------------------------------- */ +static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", IBM_NAME); +} + +static struct device_attribute dev_attr_thinkpad_acpi_pdev_name = + __ATTR(name, S_IRUGO, thinkpad_acpi_pdev_name_show, NULL); + +/* --------------------------------------------------------------------- */ + /* /proc support */ static struct proc_dir_entry *proc_dir; @@ -4674,6 +4760,8 @@ static int __init thinkpad_acpi_module_init(void) { int ret, i; + tpacpi_lifecycle = TPACPI_LIFE_INIT; + /* Parameter checking */ if (hotkey_report_mode > 2) return -EINVAL; @@ -4702,19 +4790,31 @@ static int __init thinkpad_acpi_module_init(void) ret = platform_driver_register(&tpacpi_pdriver); if (ret) { - printk(IBM_ERR "unable to register platform driver\n"); + printk(IBM_ERR "unable to register main platform driver\n"); thinkpad_acpi_module_exit(); return ret; } tp_features.platform_drv_registered = 1; + ret = platform_driver_register(&tpacpi_hwmon_pdriver); + if (ret) { + printk(IBM_ERR "unable to register hwmon platform driver\n"); + thinkpad_acpi_module_exit(); + return ret; + } + tp_features.sensors_pdrv_registered = 1; + ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver); + if (!ret) { + tp_features.platform_drv_attrs_registered = 1; + ret = tpacpi_create_driver_attributes(&tpacpi_hwmon_pdriver.driver); + } if (ret) { printk(IBM_ERR "unable to create sysfs driver attributes\n"); thinkpad_acpi_module_exit(); return ret; } - tp_features.platform_drv_attrs_registered = 1; + tp_features.sensors_pdrv_attrs_registered = 1; /* Device initialization */ @@ -4727,7 +4827,26 @@ static int __init thinkpad_acpi_module_init(void) thinkpad_acpi_module_exit(); return ret; } - tpacpi_hwmon = hwmon_device_register(&tpacpi_pdev->dev); + tpacpi_sensors_pdev = platform_device_register_simple( + IBM_HWMON_DRVR_NAME, + -1, NULL, 0); + if (IS_ERR(tpacpi_sensors_pdev)) { + ret = PTR_ERR(tpacpi_sensors_pdev); + tpacpi_sensors_pdev = NULL; + printk(IBM_ERR "unable to register hwmon platform device\n"); + thinkpad_acpi_module_exit(); + return ret; + } + ret = device_create_file(&tpacpi_sensors_pdev->dev, + &dev_attr_thinkpad_acpi_pdev_name); + if (ret) { + printk(IBM_ERR + "unable to create sysfs hwmon device attributes\n"); + thinkpad_acpi_module_exit(); + return ret; + } + tp_features.sensors_pdev_attrs_registered = 1; + tpacpi_hwmon = hwmon_device_register(&tpacpi_sensors_pdev->dev); if (IS_ERR(tpacpi_hwmon)) { ret = PTR_ERR(tpacpi_hwmon); tpacpi_hwmon = NULL; @@ -4735,6 +4854,7 @@ static int __init thinkpad_acpi_module_init(void) thinkpad_acpi_module_exit(); return ret; } + mutex_init(&tpacpi_inputdev_send_mutex); tpacpi_inputdev = input_allocate_device(); if (!tpacpi_inputdev) { printk(IBM_ERR "unable to allocate input device\n"); @@ -4769,6 +4889,7 @@ static int __init thinkpad_acpi_module_init(void) tp_features.input_device_registered = 1; } + tpacpi_lifecycle = TPACPI_LIFE_RUNNING; return 0; } @@ -4776,6 +4897,8 @@ static void thinkpad_acpi_module_exit(void) { struct ibm_struct *ibm, *itmp; + tpacpi_lifecycle = TPACPI_LIFE_EXITING; + list_for_each_entry_safe_reverse(ibm, itmp, &tpacpi_all_drivers, all_drivers) { @@ -4794,12 +4917,22 @@ static void thinkpad_acpi_module_exit(void) if (tpacpi_hwmon) hwmon_device_unregister(tpacpi_hwmon); + if (tp_features.sensors_pdev_attrs_registered) + device_remove_file(&tpacpi_sensors_pdev->dev, + &dev_attr_thinkpad_acpi_pdev_name); + if (tpacpi_sensors_pdev) + platform_device_unregister(tpacpi_sensors_pdev); if (tpacpi_pdev) platform_device_unregister(tpacpi_pdev); + if (tp_features.sensors_pdrv_attrs_registered) + tpacpi_remove_driver_attributes(&tpacpi_hwmon_pdriver.driver); if (tp_features.platform_drv_attrs_registered) tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver); + if (tp_features.sensors_pdrv_registered) + platform_driver_unregister(&tpacpi_hwmon_pdriver); + if (tp_features.platform_drv_registered) platform_driver_unregister(&tpacpi_pdriver); diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index acd5835..3abcc81 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -58,13 +58,14 @@ #define IBM_NAME "thinkpad" #define IBM_DESC "ThinkPad ACPI Extras" -#define IBM_FILE "thinkpad_acpi" +#define IBM_FILE IBM_NAME "_acpi" #define IBM_URL "http://ibm-acpi.sf.net/" #define IBM_MAIL "ibm-acpi-devel@lists.sourceforge.net" #define IBM_PROC_DIR "ibm" #define IBM_ACPI_EVENT_PREFIX "ibm" #define IBM_DRVR_NAME IBM_FILE +#define IBM_HWMON_DRVR_NAME IBM_NAME "_hwmon" #define IBM_LOG IBM_FILE ": " #define IBM_ERR KERN_ERR IBM_LOG @@ -171,6 +172,7 @@ static int parse_strtoul(const char *buf, unsigned long max, /* Device model */ static struct platform_device *tpacpi_pdev; +static struct platform_device *tpacpi_sensors_pdev; static struct device *tpacpi_hwmon; static struct platform_driver tpacpi_pdriver; static struct input_dev *tpacpi_inputdev; @@ -233,22 +235,25 @@ struct ibm_init_struct { static struct { #ifdef CONFIG_THINKPAD_ACPI_BAY - u16 bay_status:1; - u16 bay_eject:1; - u16 bay_status2:1; - u16 bay_eject2:1; + u32 bay_status:1; + u32 bay_eject:1; + u32 bay_status2:1; + u32 bay_eject2:1; #endif - u16 bluetooth:1; - u16 hotkey:1; - u16 hotkey_mask:1; - u16 hotkey_wlsw:1; - u16 light:1; - u16 light_status:1; - u16 wan:1; - u16 fan_ctrl_status_undef:1; - u16 input_device_registered:1; - u16 platform_drv_registered:1; - u16 platform_drv_attrs_registered:1; + u32 bluetooth:1; + u32 hotkey:1; + u32 hotkey_mask:1; + u32 hotkey_wlsw:1; + u32 light:1; + u32 light_status:1; + u32 wan:1; + u32 fan_ctrl_status_undef:1; + u32 input_device_registered:1; + u32 platform_drv_registered:1; + u32 platform_drv_attrs_registered:1; + u32 sensors_pdrv_registered:1; + u32 sensors_pdrv_attrs_registered:1; + u32 sensors_pdev_attrs_registered:1; } tp_features; struct thinkpad_id_data { |