diff options
Diffstat (limited to 'drivers/firmware/dcdbas.c')
| -rw-r--r-- | drivers/firmware/dcdbas.c | 230 |
1 files changed, 142 insertions, 88 deletions
diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 8ed6ddbb9c5..7160c43c59f 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -8,7 +8,7 @@ * * See Documentation/dcdbas.txt for more information. * - * Copyright (C) 1995-2005 Dell Inc. + * Copyright (C) 1995-2006 Dell Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License v2.0 as published by @@ -23,6 +23,7 @@ #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/errno.h> +#include <linux/gfp.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/mc146818rtc.h> @@ -33,13 +34,13 @@ #include <linux/spinlock.h> #include <linux/string.h> #include <linux/types.h> +#include <linux/mutex.h> #include <asm/io.h> -#include <asm/semaphore.h> #include "dcdbas.h" #define DRIVER_NAME "dcdbas" -#define DRIVER_VERSION "5.6.0-1" +#define DRIVER_VERSION "5.6.0-3.2" #define DRIVER_DESCRIPTION "Dell Systems Management Base Driver" static struct platform_device *dcdbas_pdev; @@ -48,7 +49,7 @@ static u8 *smi_data_buf; static dma_addr_t smi_data_buf_handle; static unsigned long smi_data_buf_size; static u32 smi_data_buf_phys_addr; -static DECLARE_MUTEX(smi_data_lock); +static DEFINE_MUTEX(smi_data_lock); static unsigned int host_control_action; static unsigned int host_control_smi_type; @@ -63,7 +64,7 @@ static void smi_data_buf_free(void) return; dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", - __FUNCTION__, smi_data_buf_phys_addr, smi_data_buf_size); + __func__, smi_data_buf_phys_addr, smi_data_buf_size); dma_free_coherent(&dcdbas_pdev->dev, smi_data_buf_size, smi_data_buf, smi_data_buf_handle); @@ -92,7 +93,7 @@ static int smi_data_buf_realloc(unsigned long size) if (!buf) { dev_dbg(&dcdbas_pdev->dev, "%s: failed to allocate memory size %lu\n", - __FUNCTION__, size); + __func__, size); return -ENOMEM; } /* memory zeroed by dma_alloc_coherent */ @@ -110,7 +111,7 @@ static int smi_data_buf_realloc(unsigned long size) smi_data_buf_size = size; dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", - __FUNCTION__, smi_data_buf_phys_addr, smi_data_buf_size); + __func__, smi_data_buf_phys_addr, smi_data_buf_size); return 0; } @@ -139,42 +140,38 @@ static ssize_t smi_data_buf_size_store(struct device *dev, buf_size = simple_strtoul(buf, NULL, 10); /* make sure SMI data buffer is at least buf_size */ - down(&smi_data_lock); + mutex_lock(&smi_data_lock); ret = smi_data_buf_realloc(buf_size); - up(&smi_data_lock); + mutex_unlock(&smi_data_lock); if (ret) return ret; return count; } -static ssize_t smi_data_read(struct kobject *kobj, char *buf, loff_t pos, - size_t count) +static ssize_t smi_data_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t count) { - size_t max_read; ssize_t ret; - down(&smi_data_lock); - - if (pos >= smi_data_buf_size) { - ret = 0; - goto out; - } - - max_read = smi_data_buf_size - pos; - ret = min(max_read, count); - memcpy(buf, smi_data_buf + pos, ret); -out: - up(&smi_data_lock); + mutex_lock(&smi_data_lock); + ret = memory_read_from_buffer(buf, count, &pos, smi_data_buf, + smi_data_buf_size); + mutex_unlock(&smi_data_lock); return ret; } -static ssize_t smi_data_write(struct kobject *kobj, char *buf, loff_t pos, - size_t count) +static ssize_t smi_data_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t count) { ssize_t ret; - down(&smi_data_lock); + if ((pos + count) > MAX_SMI_DATA_BUF_SIZE) + return -EINVAL; + + mutex_lock(&smi_data_lock); ret = smi_data_buf_realloc(pos + count); if (ret) @@ -183,7 +180,7 @@ static ssize_t smi_data_write(struct kobject *kobj, char *buf, loff_t pos, memcpy(smi_data_buf + pos, buf, count); ret = count; out: - up(&smi_data_lock); + mutex_unlock(&smi_data_lock); return ret; } @@ -201,9 +198,9 @@ static ssize_t host_control_action_store(struct device *dev, ssize_t ret; /* make sure buffer is available for host control command */ - down(&smi_data_lock); + mutex_lock(&smi_data_lock); ret = smi_data_buf_realloc(sizeof(struct apm_cmd)); - up(&smi_data_lock); + mutex_unlock(&smi_data_lock); if (ret) return ret; @@ -242,34 +239,39 @@ static ssize_t host_control_on_shutdown_store(struct device *dev, } /** - * smi_request: generate SMI request + * dcdbas_smi_request: generate SMI request * * Called with smi_data_lock. */ -static int smi_request(struct smi_cmd *smi_cmd) +int dcdbas_smi_request(struct smi_cmd *smi_cmd) { - cpumask_t old_mask; + cpumask_var_t old_mask; int ret = 0; if (smi_cmd->magic != SMI_CMD_MAGIC) { dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n", - __FUNCTION__); + __func__); return -EBADR; } /* SMI requires CPU 0 */ - old_mask = current->cpus_allowed; - set_cpus_allowed(current, cpumask_of_cpu(0)); + if (!alloc_cpumask_var(&old_mask, GFP_KERNEL)) + return -ENOMEM; + + cpumask_copy(old_mask, ¤t->cpus_allowed); + set_cpus_allowed_ptr(current, cpumask_of(0)); if (smp_processor_id() != 0) { dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n", - __FUNCTION__); + __func__); ret = -EBUSY; goto out; } /* generate SMI */ + /* inb to force posted write through and make SMI happen now */ asm volatile ( - "outb %b0,%w1" + "outb %b0,%w1\n" + "inb %w1" : /* no output args */ : "a" (smi_cmd->command_code), "d" (smi_cmd->command_address), @@ -279,7 +281,8 @@ static int smi_request(struct smi_cmd *smi_cmd) ); out: - set_cpus_allowed(current, old_mask); + set_cpus_allowed_ptr(current, old_mask); + free_cpumask_var(old_mask); return ret; } @@ -302,7 +305,7 @@ static ssize_t smi_request_store(struct device *dev, unsigned long val = simple_strtoul(buf, NULL, 10); ssize_t ret; - down(&smi_data_lock); + mutex_lock(&smi_data_lock); if (smi_data_buf_size < sizeof(struct smi_cmd)) { ret = -ENODEV; @@ -313,14 +316,14 @@ static ssize_t smi_request_store(struct device *dev, switch (val) { case 2: /* Raw SMI */ - ret = smi_request(smi_cmd); + ret = dcdbas_smi_request(smi_cmd); if (!ret) ret = count; break; case 1: /* Calling Interface SMI */ smi_cmd->ebx = (u32) virt_to_phys(smi_cmd->command_buffer); - ret = smi_request(smi_cmd); + ret = dcdbas_smi_request(smi_cmd); if (!ret) ret = count; break; @@ -334,9 +337,10 @@ static ssize_t smi_request_store(struct device *dev, } out: - up(&smi_data_lock); + mutex_unlock(&smi_data_lock); return ret; } +EXPORT_SYMBOL(dcdbas_smi_request); /** * host_control_smi: generate host control SMI @@ -423,7 +427,7 @@ static int host_control_smi(void) default: dev_dbg(&dcdbas_pdev->dev, "%s: invalid SMI type %u\n", - __FUNCTION__, host_control_smi_type); + __func__, host_control_smi_type); return -ENOSYS; } @@ -451,13 +455,13 @@ static void dcdbas_host_control(void) host_control_action = HC_ACTION_NONE; if (!smi_data_buf) { - dev_dbg(&dcdbas_pdev->dev, "%s: no SMI buffer\n", __FUNCTION__); + dev_dbg(&dcdbas_pdev->dev, "%s: no SMI buffer\n", __func__); return; } if (smi_data_buf_size < sizeof(struct apm_cmd)) { dev_dbg(&dcdbas_pdev->dev, "%s: SMI buffer too small\n", - __FUNCTION__); + __func__); return; } @@ -483,26 +487,15 @@ static void dcdbas_host_control(void) static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, void *unused) { - static unsigned int notify_cnt = 0; - switch (code) { case SYS_DOWN: case SYS_HALT: case SYS_POWER_OFF: if (host_control_on_shutdown) { /* firmware is going to perform host control action */ - if (++notify_cnt == 2) { - printk(KERN_WARNING - "Please wait for shutdown " - "action to complete...\n"); - dcdbas_host_control(); - } - /* - * register again and initiate the host control - * action on the second notification to allow - * everyone that registered to be notified - */ - register_reboot_notifier(nb); + printk(KERN_WARNING "Please wait for shutdown " + "action to complete...\n"); + dcdbas_host_control(); } break; } @@ -513,7 +506,7 @@ static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, static struct notifier_block dcdbas_reboot_nb = { .notifier_call = dcdbas_reboot_notify, .next = NULL, - .priority = 0 + .priority = INT_MIN }; static DCDBAS_BIN_ATTR_RW(smi_data); @@ -530,50 +523,97 @@ static DCDBAS_DEV_ATTR_RW(host_control_action); static DCDBAS_DEV_ATTR_RW(host_control_smi_type); static DCDBAS_DEV_ATTR_RW(host_control_on_shutdown); -static struct device_attribute *dcdbas_dev_attrs[] = { - &dev_attr_smi_data_buf_size, - &dev_attr_smi_data_buf_phys_addr, - &dev_attr_smi_request, - &dev_attr_host_control_action, - &dev_attr_host_control_smi_type, - &dev_attr_host_control_on_shutdown, +static struct attribute *dcdbas_dev_attrs[] = { + &dev_attr_smi_data_buf_size.attr, + &dev_attr_smi_data_buf_phys_addr.attr, + &dev_attr_smi_request.attr, + &dev_attr_host_control_action.attr, + &dev_attr_host_control_smi_type.attr, + &dev_attr_host_control_on_shutdown.attr, NULL }; -/** - * dcdbas_init: initialize driver - */ -static int __init dcdbas_init(void) +static struct attribute_group dcdbas_attr_group = { + .attrs = dcdbas_dev_attrs, + .bin_attrs = dcdbas_bin_attrs, +}; + +static int dcdbas_probe(struct platform_device *dev) { - int i; + int error; host_control_action = HC_ACTION_NONE; host_control_smi_type = HC_SMITYPE_NONE; - dcdbas_pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); - if (IS_ERR(dcdbas_pdev)) - return PTR_ERR(dcdbas_pdev); + dcdbas_pdev = dev; /* * BIOS SMI calls require buffer addresses be in 32-bit address space. * This is done by setting the DMA mask below. */ - dcdbas_pdev->dev.coherent_dma_mask = DMA_32BIT_MASK; - dcdbas_pdev->dev.dma_mask = &dcdbas_pdev->dev.coherent_dma_mask; + error = dma_set_coherent_mask(&dcdbas_pdev->dev, DMA_BIT_MASK(32)); + if (error) + return error; + + error = sysfs_create_group(&dev->dev.kobj, &dcdbas_attr_group); + if (error) + return error; register_reboot_notifier(&dcdbas_reboot_nb); - for (i = 0; dcdbas_bin_attrs[i]; i++) - sysfs_create_bin_file(&dcdbas_pdev->dev.kobj, - dcdbas_bin_attrs[i]); + dev_info(&dev->dev, "%s (version %s)\n", + DRIVER_DESCRIPTION, DRIVER_VERSION); + + return 0; +} - for (i = 0; dcdbas_dev_attrs[i]; i++) - device_create_file(&dcdbas_pdev->dev, dcdbas_dev_attrs[i]); +static int dcdbas_remove(struct platform_device *dev) +{ + unregister_reboot_notifier(&dcdbas_reboot_nb); + sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group); - dev_info(&dcdbas_pdev->dev, "%s (version %s)\n", - DRIVER_DESCRIPTION, DRIVER_VERSION); + return 0; +} + +static struct platform_driver dcdbas_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = dcdbas_probe, + .remove = dcdbas_remove, +}; + +static const struct platform_device_info dcdbas_dev_info __initconst = { + .name = DRIVER_NAME, + .id = -1, + .dma_mask = DMA_BIT_MASK(32), +}; + +static struct platform_device *dcdbas_pdev_reg; + +/** + * dcdbas_init: initialize driver + */ +static int __init dcdbas_init(void) +{ + int error; + + error = platform_driver_register(&dcdbas_driver); + if (error) + return error; + + dcdbas_pdev_reg = platform_device_register_full(&dcdbas_dev_info); + if (IS_ERR(dcdbas_pdev_reg)) { + error = PTR_ERR(dcdbas_pdev_reg); + goto err_unregister_driver; + } return 0; + + err_unregister_driver: + platform_driver_unregister(&dcdbas_driver); + return error; } /** @@ -581,9 +621,22 @@ static int __init dcdbas_init(void) */ static void __exit dcdbas_exit(void) { - platform_device_unregister(dcdbas_pdev); + /* + * make sure functions that use dcdbas_pdev are called + * before platform_device_unregister + */ unregister_reboot_notifier(&dcdbas_reboot_nb); - smi_data_buf_free(); + + /* + * We have to free the buffer here instead of dcdbas_remove + * because only in module exit function we can be sure that + * all sysfs attributes belonging to this module have been + * released. + */ + if (dcdbas_pdev) + smi_data_buf_free(); + platform_device_unregister(dcdbas_pdev_reg); + platform_driver_unregister(&dcdbas_driver); } module_init(dcdbas_init); @@ -593,4 +646,5 @@ MODULE_DESCRIPTION(DRIVER_DESCRIPTION " (version " DRIVER_VERSION ")"); MODULE_VERSION(DRIVER_VERSION); MODULE_AUTHOR("Dell Inc."); MODULE_LICENSE("GPL"); - +/* Any System or BIOS claiming to be by Dell */ +MODULE_ALIAS("dmi:*:[bs]vnD[Ee][Ll][Ll]*:*"); |
