Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
[safe/jmp/linux-2.6] / drivers / char / ipmi / ipmi_si_intf.c
index 5a54555..176f175 100644 (file)
 #include <linux/dmi.h>
 #include <linux/string.h>
 #include <linux/ctype.h>
+#include <linux/pnp.h>
 
 #ifdef CONFIG_PPC_OF
-#include <asm/of_device.h>
-#include <asm/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
 #endif
 
 #define PFX "ipmi_si: "
 #define SI_SHORT_TIMEOUT_USEC  250 /* .25ms when the SM request a
                                      short timeout */
 
-/* Bit for BMC global enables. */
-#define IPMI_BMC_RCV_MSG_INTR     0x01
-#define IPMI_BMC_EVT_MSG_INTR     0x02
-#define IPMI_BMC_EVT_MSG_BUFF     0x04
-#define IPMI_BMC_SYS_LOG          0x08
-
 enum si_intf_state {
        SI_NORMAL,
        SI_GETTING_FLAGS,
@@ -114,9 +109,11 @@ static char *si_to_str[] = { "kcs", "smic", "bt" };
 
 #define DEVICE_NAME "ipmi_si"
 
-static struct device_driver ipmi_driver = {
-       .name = DEVICE_NAME,
-       .bus = &platform_bus_type
+static struct platform_driver ipmi_driver = {
+       .driver = {
+               .name = DEVICE_NAME,
+               .bus = &platform_bus_type
+       }
 };
 
 
@@ -218,6 +215,9 @@ struct smi_info {
                             OEM2_DATA_AVAIL)
        unsigned char       msg_flags;
 
+       /* Does the BMC have an event buffer? */
+       char                has_event_buffer;
+
        /*
         * If set to true, this will request events the next time the
         * state machine is idle.
@@ -966,7 +966,8 @@ static void request_events(void *send_info)
 {
        struct smi_info *smi_info = send_info;
 
-       if (atomic_read(&smi_info->stop_operation))
+       if (atomic_read(&smi_info->stop_operation) ||
+                               !smi_info->has_event_buffer)
                return;
 
        atomic_set(&smi_info->req_events, 1);
@@ -1919,7 +1920,7 @@ struct SPMITable {
        s8      spmi_id[1]; /* A '\0' terminated array starts here. */
 };
 
-static __devinit int try_init_acpi(struct SPMITable *spmi)
+static __devinit int try_init_spmi(struct SPMITable *spmi)
 {
        struct smi_info  *info;
        u8               addr_space;
@@ -1940,7 +1941,7 @@ static __devinit int try_init_acpi(struct SPMITable *spmi)
                return -ENOMEM;
        }
 
-       info->addr_source = "ACPI";
+       info->addr_source = "SPMI";
 
        /* Figure out the interface type. */
        switch (spmi->InterfaceType) {
@@ -2002,7 +2003,7 @@ static __devinit int try_init_acpi(struct SPMITable *spmi)
        return 0;
 }
 
-static __devinit void acpi_find_bmc(void)
+static __devinit void spmi_find_bmc(void)
 {
        acpi_status      status;
        struct SPMITable *spmi;
@@ -2020,9 +2021,106 @@ static __devinit void acpi_find_bmc(void)
                if (status != AE_OK)
                        return;
 
-               try_init_acpi(spmi);
+               try_init_spmi(spmi);
+       }
+}
+
+static int __devinit ipmi_pnp_probe(struct pnp_dev *dev,
+                                   const struct pnp_device_id *dev_id)
+{
+       struct acpi_device *acpi_dev;
+       struct smi_info *info;
+       acpi_handle handle;
+       acpi_status status;
+       unsigned long long tmp;
+
+       acpi_dev = pnp_acpi_device(dev);
+       if (!acpi_dev)
+               return -ENODEV;
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->addr_source = "ACPI";
+
+       handle = acpi_dev->handle;
+
+       /* _IFT tells us the interface type: KCS, BT, etc */
+       status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp);
+       if (ACPI_FAILURE(status))
+               goto err_free;
+
+       switch (tmp) {
+       case 1:
+               info->si_type = SI_KCS;
+               break;
+       case 2:
+               info->si_type = SI_SMIC;
+               break;
+       case 3:
+               info->si_type = SI_BT;
+               break;
+       default:
+               dev_info(&dev->dev, "unknown interface type %lld\n", tmp);
+               goto err_free;
+       }
+
+       if (pnp_port_valid(dev, 0)) {
+               info->io_setup = port_setup;
+               info->io.addr_type = IPMI_IO_ADDR_SPACE;
+               info->io.addr_data = pnp_port_start(dev, 0);
+       } else if (pnp_mem_valid(dev, 0)) {
+               info->io_setup = mem_setup;
+               info->io.addr_type = IPMI_MEM_ADDR_SPACE;
+               info->io.addr_data = pnp_mem_start(dev, 0);
+       } else {
+               dev_err(&dev->dev, "no I/O or memory address\n");
+               goto err_free;
+       }
+
+       info->io.regspacing = DEFAULT_REGSPACING;
+       info->io.regsize = DEFAULT_REGSPACING;
+       info->io.regshift = 0;
+
+       /* If _GPE exists, use it; otherwise use standard interrupts */
+       status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
+       if (ACPI_SUCCESS(status)) {
+               info->irq = tmp;
+               info->irq_setup = acpi_gpe_irq_setup;
+       } else if (pnp_irq_valid(dev, 0)) {
+               info->irq = pnp_irq(dev, 0);
+               info->irq_setup = std_irq_setup;
        }
+
+       info->dev = &acpi_dev->dev;
+       pnp_set_drvdata(dev, info);
+
+       return try_smi_init(info);
+
+err_free:
+       kfree(info);
+       return -EINVAL;
+}
+
+static void __devexit ipmi_pnp_remove(struct pnp_dev *dev)
+{
+       struct smi_info *info = pnp_get_drvdata(dev);
+
+       cleanup_one_si(info);
 }
+
+static const struct pnp_device_id pnp_dev_table[] = {
+       {"IPI0001", 0},
+       {"", 0},
+};
+
+static struct pnp_driver ipmi_pnp_driver = {
+       .name           = DEVICE_NAME,
+       .probe          = ipmi_pnp_probe,
+       .remove         = __devexit_p(ipmi_pnp_remove),
+       .id_table       = pnp_dev_table,
+};
 #endif
 
 #ifdef CONFIG_DMI
@@ -2202,7 +2300,6 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
        int rv;
        int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK;
        struct smi_info *info;
-       int first_reg_offset = 0;
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
@@ -2241,9 +2338,6 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
        info->addr_source_cleanup = ipmi_pci_cleanup;
        info->addr_source_data = pdev;
 
-       if (pdev->subsystem_vendor == PCI_HP_VENDOR_ID)
-               first_reg_offset = 1;
-
        if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
                info->io_setup = port_setup;
                info->io.addr_type = IPMI_IO_ADDR_SPACE;
@@ -2352,10 +2446,16 @@ static int __devinit ipmi_of_probe(struct of_device *dev,
 
        info->si_type           = (enum si_type) match->data;
        info->addr_source       = "device-tree";
-       info->io_setup          = mem_setup;
        info->irq_setup         = std_irq_setup;
 
-       info->io.addr_type      = IPMI_MEM_ADDR_SPACE;
+       if (resource.flags & IORESOURCE_IO) {
+               info->io_setup          = port_setup;
+               info->io.addr_type      = IPMI_IO_ADDR_SPACE;
+       } else {
+               info->io_setup          = mem_setup;
+               info->io.addr_type      = IPMI_MEM_ADDR_SPACE;
+       }
+
        info->io.addr_data      = resource.start;
 
        info->io.regsize        = regsize ? *regsize : DEFAULT_REGSIZE;
@@ -2369,14 +2469,14 @@ static int __devinit ipmi_of_probe(struct of_device *dev,
                info->io.addr_data, info->io.regsize, info->io.regspacing,
                info->irq);
 
-       dev->dev.driver_data = (void *) info;
+       dev_set_drvdata(&dev->dev, info);
 
        return try_smi_init(info);
 }
 
 static int __devexit ipmi_of_remove(struct of_device *dev)
 {
-       cleanup_one_si(dev->dev.driver_data);
+       cleanup_one_si(dev_get_drvdata(&dev->dev));
        return 0;
 }
 
@@ -2399,26 +2499,9 @@ static struct of_platform_driver ipmi_of_platform_driver = {
 };
 #endif /* CONFIG_PPC_OF */
 
-
-static int try_get_dev_id(struct smi_info *smi_info)
+static int wait_for_msg_done(struct smi_info *smi_info)
 {
-       unsigned char         msg[2];
-       unsigned char         *resp;
-       unsigned long         resp_len;
        enum si_sm_result     smi_result;
-       int                   rv = 0;
-
-       resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
-       if (!resp)
-               return -ENOMEM;
-
-       /*
-        * Do a Get Device ID command, since it comes back with some
-        * useful info.
-        */
-       msg[0] = IPMI_NETFN_APP_REQUEST << 2;
-       msg[1] = IPMI_GET_DEVICE_ID_CMD;
-       smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
 
        smi_result = smi_info->handlers->event(smi_info->si_sm, 0);
        for (;;) {
@@ -2433,16 +2516,39 @@ static int try_get_dev_id(struct smi_info *smi_info)
                } else
                        break;
        }
-       if (smi_result == SI_SM_HOSED) {
+       if (smi_result == SI_SM_HOSED)
                /*
                 * We couldn't get the state machine to run, so whatever's at
                 * the port is probably not an IPMI SMI interface.
                 */
-               rv = -ENODEV;
+               return -ENODEV;
+
+       return 0;
+}
+
+static int try_get_dev_id(struct smi_info *smi_info)
+{
+       unsigned char         msg[2];
+       unsigned char         *resp;
+       unsigned long         resp_len;
+       int                   rv = 0;
+
+       resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
+       if (!resp)
+               return -ENOMEM;
+
+       /*
+        * Do a Get Device ID command, since it comes back with some
+        * useful info.
+        */
+       msg[0] = IPMI_NETFN_APP_REQUEST << 2;
+       msg[1] = IPMI_GET_DEVICE_ID_CMD;
+       smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
+
+       rv = wait_for_msg_done(smi_info);
+       if (rv)
                goto out;
-       }
 
-       /* Otherwise, we got some data. */
        resp_len = smi_info->handlers->get_result(smi_info->si_sm,
                                                  resp, IPMI_MAX_MSG_LENGTH);
 
@@ -2454,6 +2560,88 @@ static int try_get_dev_id(struct smi_info *smi_info)
        return rv;
 }
 
+static int try_enable_event_buffer(struct smi_info *smi_info)
+{
+       unsigned char         msg[3];
+       unsigned char         *resp;
+       unsigned long         resp_len;
+       int                   rv = 0;
+
+       resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
+       if (!resp)
+               return -ENOMEM;
+
+       msg[0] = IPMI_NETFN_APP_REQUEST << 2;
+       msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
+       smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
+
+       rv = wait_for_msg_done(smi_info);
+       if (rv) {
+               printk(KERN_WARNING
+                      "ipmi_si: Error getting response from get global,"
+                      " enables command, the event buffer is not"
+                      " enabled.\n");
+               goto out;
+       }
+
+       resp_len = smi_info->handlers->get_result(smi_info->si_sm,
+                                                 resp, IPMI_MAX_MSG_LENGTH);
+
+       if (resp_len < 4 ||
+                       resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
+                       resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD   ||
+                       resp[2] != 0) {
+               printk(KERN_WARNING
+                      "ipmi_si: Invalid return from get global"
+                      " enables command, cannot enable the event"
+                      " buffer.\n");
+               rv = -EINVAL;
+               goto out;
+       }
+
+       if (resp[3] & IPMI_BMC_EVT_MSG_BUFF)
+               /* buffer is already enabled, nothing to do. */
+               goto out;
+
+       msg[0] = IPMI_NETFN_APP_REQUEST << 2;
+       msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
+       msg[2] = resp[3] | IPMI_BMC_EVT_MSG_BUFF;
+       smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3);
+
+       rv = wait_for_msg_done(smi_info);
+       if (rv) {
+               printk(KERN_WARNING
+                      "ipmi_si: Error getting response from set global,"
+                      " enables command, the event buffer is not"
+                      " enabled.\n");
+               goto out;
+       }
+
+       resp_len = smi_info->handlers->get_result(smi_info->si_sm,
+                                                 resp, IPMI_MAX_MSG_LENGTH);
+
+       if (resp_len < 3 ||
+                       resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
+                       resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) {
+               printk(KERN_WARNING
+                      "ipmi_si: Invalid return from get global,"
+                      "enables command, not enable the event"
+                      " buffer.\n");
+               rv = -EINVAL;
+               goto out;
+       }
+
+       if (resp[2] != 0)
+               /*
+                * An error when setting the event buffer bit means
+                * that the event buffer is not supported.
+                */
+               rv = -ENOENT;
+ out:
+       kfree(resp);
+       return rv;
+}
+
 static int type_file_read_proc(char *page, char **start, off_t off,
                               int count, int *eof, void *data)
 {
@@ -2689,15 +2877,13 @@ static __devinit void default_find_bmc(void)
        for (i = 0; ; i++) {
                if (!ipmi_defaults[i].port)
                        break;
-
-               info = kzalloc(sizeof(*info), GFP_KERNEL);
-               if (!info)
-                       return;
-
-#ifdef CONFIG_PPC_MERGE
+#ifdef CONFIG_PPC
                if (check_legacy_ioport(ipmi_defaults[i].port))
                        continue;
 #endif
+               info = kzalloc(sizeof(*info), GFP_KERNEL);
+               if (!info)
+                       return;
 
                info->addr_source = NULL;
 
@@ -2841,6 +3027,10 @@ static int try_smi_init(struct smi_info *new_smi)
        new_smi->intf_num = smi_num;
        smi_num++;
 
+       rv = try_enable_event_buffer(new_smi);
+       if (rv == 0)
+               new_smi->has_event_buffer = 1;
+
        /*
         * Start clearing the flags before we enable interrupts or the
         * timer to avoid racing with the timer.
@@ -2857,14 +3047,14 @@ static int try_smi_init(struct smi_info *new_smi)
                 */
                new_smi->pdev = platform_device_alloc("ipmi_si",
                                                      new_smi->intf_num);
-               if (rv) {
+               if (!new_smi->pdev) {
                        printk(KERN_ERR
                               "ipmi_si_intf:"
                               " Unable to allocate platform device\n");
                        goto out_err;
                }
                new_smi->dev = &new_smi->pdev->dev;
-               new_smi->dev->driver = &ipmi_driver;
+               new_smi->dev->driver = &ipmi_driver.driver;
 
                rv = platform_device_add(new_smi->pdev);
                if (rv) {
@@ -2893,7 +3083,7 @@ static int try_smi_init(struct smi_info *new_smi)
 
        rv = ipmi_smi_add_proc_entry(new_smi->intf, "type",
                                     type_file_read_proc,
-                                    new_smi, THIS_MODULE);
+                                    new_smi);
        if (rv) {
                printk(KERN_ERR
                       "ipmi_si: Unable to create proc entry: %d\n",
@@ -2903,7 +3093,7 @@ static int try_smi_init(struct smi_info *new_smi)
 
        rv = ipmi_smi_add_proc_entry(new_smi->intf, "si_stats",
                                     stat_file_read_proc,
-                                    new_smi, THIS_MODULE);
+                                    new_smi);
        if (rv) {
                printk(KERN_ERR
                       "ipmi_si: Unable to create proc entry: %d\n",
@@ -2913,7 +3103,7 @@ static int try_smi_init(struct smi_info *new_smi)
 
        rv = ipmi_smi_add_proc_entry(new_smi->intf, "params",
                                     param_read_proc,
-                                    new_smi, THIS_MODULE);
+                                    new_smi);
        if (rv) {
                printk(KERN_ERR
                       "ipmi_si: Unable to create proc entry: %d\n",
@@ -2979,7 +3169,7 @@ static __devinit int init_ipmi_si(void)
        initialized = 1;
 
        /* Register the device drivers. */
-       rv = driver_register(&ipmi_driver);
+       rv = driver_register(&ipmi_driver.driver);
        if (rv) {
                printk(KERN_ERR
                       "init_ipmi_si: Unable to register driver: %d\n",
@@ -3012,7 +3202,10 @@ static __devinit int init_ipmi_si(void)
 #endif
 
 #ifdef CONFIG_ACPI
-       acpi_find_bmc();
+       spmi_find_bmc();
+#endif
+#ifdef CONFIG_ACPI
+       pnp_register_driver(&ipmi_pnp_driver);
 #endif
 
 #ifdef CONFIG_PCI
@@ -3048,7 +3241,7 @@ static __devinit int init_ipmi_si(void)
 #ifdef CONFIG_PPC_OF
                of_unregister_platform_driver(&ipmi_of_platform_driver);
 #endif
-               driver_unregister(&ipmi_driver);
+               driver_unregister(&ipmi_driver.driver);
                printk(KERN_WARNING
                       "ipmi_si: Unable to find any System Interface(s)\n");
                return -ENODEV;
@@ -3137,6 +3330,9 @@ static __exit void cleanup_ipmi_si(void)
 #ifdef CONFIG_PCI
        pci_unregister_driver(&ipmi_pci_driver);
 #endif
+#ifdef CONFIG_ACPI
+       pnp_unregister_driver(&ipmi_pnp_driver);
+#endif
 
 #ifdef CONFIG_PPC_OF
        of_unregister_platform_driver(&ipmi_of_platform_driver);
@@ -3147,7 +3343,7 @@ static __exit void cleanup_ipmi_si(void)
                cleanup_one_si(e);
        mutex_unlock(&smi_infos_lock);
 
-       driver_unregister(&ipmi_driver);
+       driver_unregister(&ipmi_driver.driver);
 }
 module_exit(cleanup_ipmi_si);