From: Corey Minyard <[email protected]>

It makes more sense to be there, and it's cleaner with the
upcoming conversion of IPMI DMI to a platform device.

Signed-off-by: Corey Minyard <[email protected]>
Cc: Jean Delvare <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Tested-by: Andy Lutomirski <[email protected]>
---
 drivers/firmware/dmi_scan.c | 108 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/dmi.h         |  26 +++++++++++
 2 files changed, 129 insertions(+), 5 deletions(-)

diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 5519b4f..16e5174 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -303,9 +303,105 @@ static void __init dmi_save_oem_strings_devices(const 
struct dmi_header *dm)
        }
 }
 
+#define DMI_IPMI_MIN_LENGTH    0x10
+#define DMI_IPMI_VER2_LENGTH   0x12
+#define DMI_IPMI_TYPE          4
+#define DMI_IPMI_SLAVEADDR     6
+#define DMI_IPMI_ADDR          8
+#define DMI_IPMI_ACCESS                0x10
+#define DMI_IPMI_IRQ           0x11
+#define DMI_IPMI_IO_MASK       0xfffe
+
+static void __init dmi_decode_ipmi(struct dmi_dev_ipmi *dev,
+                                  const struct dmi_header *dm)
+{
+       const u8        *data = (const u8 *) dm;
+       unsigned long   base_addr;
+       u8              len = dm->length;
+       bool            slave_addr_set;
+
+       if (len < DMI_IPMI_MIN_LENGTH)
+               return;
+
+       dev->type = data[DMI_IPMI_TYPE];
+
+       memcpy(&base_addr, data + DMI_IPMI_ADDR, sizeof(unsigned long));
+       if (len >= DMI_IPMI_VER2_LENGTH) {
+               if (dev->type == IPMI_DMI_TYPE_SSIF) {
+                       if ((data[DMI_IPMI_ADDR] >> 1) == 0) {
+                               /*
+                                * Some broken systems put the I2C address in
+                                * the slave address field.  We try to
+                                * accommodate them here.
+                                */
+                               dev->base_addr = data[DMI_IPMI_SLAVEADDR] >> 1;
+                               dev->slave_addr = 0;
+                               slave_addr_set = true;
+                       } else {
+                               dev->base_addr = data[DMI_IPMI_ADDR] >> 1;
+                       }
+               } else {
+                       if (base_addr & 1) {
+                               /* I/O */
+                               base_addr &= DMI_IPMI_IO_MASK;
+                               dev->is_io_space = 1;
+                       } else {
+                               /* Memory */
+                               dev->is_io_space = 0;
+                       }
+
+                       /*
+                        * If bit 4 of byte 0x10 is set, then the lsb
+                        * for the address is odd.
+                        */
+                       base_addr |= (data[DMI_IPMI_ACCESS] >> 4) & 1;
+
+                       dev->base_addr = base_addr;
+                       dev->irq = data[DMI_IPMI_IRQ];
+
+                       /*
+                        * The top two bits of byte 0x10 hold the
+                        * register spacing.
+                        */
+                       switch ((data[DMI_IPMI_ACCESS] >> 6) & 3) {
+                       case 0: /* Byte boundaries */
+                               dev->offset = 1;
+                               break;
+                       case 1: /* 32-bit boundaries */
+                               dev->offset = 4;
+                               break;
+                       case 2: /* 16-byte boundaries */
+                               dev->offset = 16;
+                               break;
+                       default:
+                               /* Leave 0 for undefined. */
+                               return;
+                       }
+               }
+       } else {
+               /* Old DMI spec. */
+               /*
+                * Note that technically, the lower bit of the base
+                * address should be 1 if the address is I/O and 0 if
+                * the address is in memory.  So many systems get that
+                * wrong (and all that I have seen are I/O) so we just
+                * ignore that bit and assume I/O.  Systems that use
+                * memory should use the newer spec, anyway.
+                */
+               dev->base_addr = base_addr & DMI_IPMI_IO_MASK;
+               dev->is_io_space = 1;
+               dev->offset = 1;
+       }
+
+       if (!slave_addr_set)
+               dev->slave_addr = data[DMI_IPMI_SLAVEADDR];
+
+       dev->good_data = 1;
+}
+
 static void __init dmi_save_ipmi_device(const struct dmi_header *dm)
 {
-       struct dmi_device *dev;
+       struct dmi_dev_ipmi *dev;
        void *data;
 
        data = dmi_alloc(dm->length);
@@ -318,11 +414,13 @@ static void __init dmi_save_ipmi_device(const struct 
dmi_header *dm)
        if (!dev)
                return;
 
-       dev->type = DMI_DEV_TYPE_IPMI;
-       dev->name = "IPMI controller";
-       dev->device_data = data;
+       dev->dev.type = DMI_DEV_TYPE_IPMI;
+       dev->dev.name = "IPMI controller";
+       dev->dev.device_data = data;
 
-       dmi_devices_list_add(dev);
+       dmi_decode_ipmi(dev, dm);
+
+       dmi_devices_list_add(&dev->dev);
 }
 
 static void __init dmi_save_dev_pciaddr(int instance, int segment, int bus,
diff --git a/include/linux/dmi.h b/include/linux/dmi.h
index c8dd5b8..96dc644 100644
--- a/include/linux/dmi.h
+++ b/include/linux/dmi.h
@@ -97,6 +97,24 @@ struct dmi_dev_onboard {
        int devfn;
 };
 
+/* Device data for an IPMI entry. */
+struct dmi_dev_ipmi {
+       struct dmi_device dev;
+       u8              good_data;
+       u8              type;
+       u8              is_io_space;
+       u8              irq;
+       u8              offset;
+       u8              slave_addr;
+       unsigned long   base_addr;
+};
+
+/* dmi_ipmi_data type field */
+#define IPMI_DMI_TYPE_KCS      0x01
+#define IPMI_DMI_TYPE_SMIC     0x02
+#define IPMI_DMI_TYPE_BT       0x03
+#define IPMI_DMI_TYPE_SSIF     0x04
+
 extern struct kobject *dmi_kobj;
 extern int dmi_check_system(const struct dmi_system_id *list);
 const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list);
@@ -127,6 +145,14 @@ static inline struct dmi_device *to_dmi_device(struct 
fwnode_handle *fwnode)
        return NULL;
 }
 
+static inline struct dmi_dev_ipmi *to_dmi_dev_ipmi(struct dmi_device *dev)
+{
+       if (!dev || dev->type != DMI_DEV_TYPE_IPMI)
+               return NULL;
+
+       return container_of(dev, struct dmi_dev_ipmi, dev);
+}
+
 #else
 
 static inline int dmi_check_system(const struct dmi_system_id *list) { return 
0; }
-- 
2.7.4


------------------------------------------------------------------------------
Find and fix application performance issues faster with Applications Manager
Applications Manager provides deep performance insights into multiple tiers of
your business applications. It resolves application problems quickly and
reduces your MTTR. Get your free trial!
https://ad.doubleclick.net/ddm/clk/302982198;130105516;z
_______________________________________________
Openipmi-developer mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openipmi-developer

Reply via email to