diff mbox

[3/4] dmi: Move IPMI DMI scanning to the DMI code

Message ID 1454107394-8914-4-git-send-email-minyard@acm.org
State New
Headers show

Commit Message

Corey Minyard Jan. 29, 2016, 10:43 p.m. UTC
From: Corey Minyard <cminyard@mvista.com>


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 <cminyard@mvista.com>

Cc: Jean Delvare <jdelvare@suse.de>
Cc: Andy Lutomirski <luto@kernel.org>
---
 drivers/char/ipmi/ipmi_si_intf.c | 119 +++++++--------------------------------
 drivers/char/ipmi/ipmi_ssif.c    |  40 +++++--------
 drivers/firmware/dmi_scan.c      | 108 +++++++++++++++++++++++++++++++++--
 include/linux/dmi.h              |  26 +++++++++
 4 files changed, 162 insertions(+), 131 deletions(-)

-- 
2.5.0
diff mbox

Patch

diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 9fda22e..22292e1 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -2272,98 +2272,36 @@  static void spmi_find_bmc(void)
 #endif
 
 #ifdef CONFIG_DMI
-struct dmi_ipmi_data {
-	u8   		type;
-	u8   		addr_space;
-	unsigned long	base_addr;
-	u8   		irq;
-	u8              offset;
-	u8              slave_addr;
-};
-
-static int decode_dmi(const struct dmi_header *dm,
-				struct dmi_ipmi_data *dmi)
+static void try_init_dmi(struct dmi_device *dmi_dev)
 {
-	const u8	*data = (const u8 *)dm;
-	unsigned long  	base_addr;
-	u8		reg_spacing;
-	u8              len = dm->length;
+	struct dmi_dev_ipmi *ipmi_data = to_dmi_dev_ipmi(dmi_dev);
+	struct smi_info *info;
 
-	dmi->type = data[4];
+	if (!ipmi_data)
+		return;
 
-	memcpy(&base_addr, data+8, sizeof(unsigned long));
-	if (len >= 0x11) {
-		if (base_addr & 1) {
-			/* I/O */
-			base_addr &= 0xFFFE;
-			dmi->addr_space = IPMI_IO_ADDR_SPACE;
-		} else
-			/* Memory */
-			dmi->addr_space = IPMI_MEM_ADDR_SPACE;
-
-		/* If bit 4 of byte 0x10 is set, then the lsb for the address
-		   is odd. */
-		dmi->base_addr = base_addr | ((data[0x10] & 0x10) >> 4);
-
-		dmi->irq = data[0x11];
-
-		/* The top two bits of byte 0x10 hold the register spacing. */
-		reg_spacing = (data[0x10] & 0xC0) >> 6;
-		switch (reg_spacing) {
-		case 0x00: /* Byte boundaries */
-		    dmi->offset = 1;
-		    break;
-		case 0x01: /* 32-bit boundaries */
-		    dmi->offset = 4;
-		    break;
-		case 0x02: /* 16-byte boundaries */
-		    dmi->offset = 16;
-		    break;
-		default:
-		    /* Some other interface, just ignore it. */
-		    return -EIO;
-		}
-	} 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.
-		 */
-		dmi->base_addr = base_addr & 0xfffe;
-		dmi->addr_space = IPMI_IO_ADDR_SPACE;
-		dmi->offset = 1;
+	if (!ipmi_data->good_data) {
+		pr_err(PFX "DMI data for this device was invalid.\n");
+		return;
 	}
 
-	dmi->slave_addr = data[6];
-
-	return 0;
-}
-
-static void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
-{
-	struct smi_info *info;
-
 	info = smi_info_alloc();
 	if (!info) {
-		printk(KERN_ERR PFX "Could not allocate SI data\n");
+		pr_err(PFX "Could not allocate SI data\n");
 		return;
 	}
 
 	info->addr_source = SI_SMBIOS;
-	printk(KERN_INFO PFX "probing via SMBIOS\n");
+	pr_info(PFX "probing via SMBIOS\n");
 
 	switch (ipmi_data->type) {
-	case 0x01: /* KCS */
+	case IPMI_DMI_TYPE_KCS:
 		info->si_type = SI_KCS;
 		break;
-	case 0x02: /* SMIC */
+	case IPMI_DMI_TYPE_SMIC:
 		info->si_type = SI_SMIC;
 		break;
-	case 0x03: /* BT */
+	case IPMI_DMI_TYPE_BT:
 		info->si_type = SI_BT;
 		break;
 	default:
@@ -2371,22 +2309,12 @@  static void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
 		return;
 	}
 
-	switch (ipmi_data->addr_space) {
-	case IPMI_MEM_ADDR_SPACE:
-		info->io_setup = mem_setup;
-		info->io.addr_type = IPMI_MEM_ADDR_SPACE;
-		break;
-
-	case IPMI_IO_ADDR_SPACE:
+	if (ipmi_data->is_io_space) {
 		info->io_setup = port_setup;
 		info->io.addr_type = IPMI_IO_ADDR_SPACE;
-		break;
-
-	default:
-		kfree(info);
-		printk(KERN_WARNING PFX "Unknown SMBIOS I/O Address type: %d\n",
-		       ipmi_data->addr_space);
-		return;
+	} else {
+		info->io_setup = mem_setup;
+		info->io.addr_type = IPMI_MEM_ADDR_SPACE;
 	}
 	info->io.addr_data = ipmi_data->base_addr;
 
@@ -2413,17 +2341,10 @@  static void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
 
 static void dmi_find_bmc(void)
 {
-	const struct dmi_device *dev = NULL;
-	struct dmi_ipmi_data data;
-	int                  rv;
+	struct dmi_device *dev = NULL;
 
-	while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) {
-		memset(&data, 0, sizeof(data));
-		rv = decode_dmi((const struct dmi_header *) dev->device_data,
-				&data);
-		if (!rv)
-			try_init_dmi(&data);
-	}
+	while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev)))
+		try_init_dmi(dev);
 }
 #endif /* CONFIG_DMI */
 
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index 5f1c3d0..7be0109 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -1906,42 +1906,28 @@  static void spmi_find_bmc(void) { }
 #endif
 
 #ifdef CONFIG_DMI
-static int decode_dmi(const struct dmi_device *dmi_dev)
+static int decode_dmi(struct dmi_device *dmi_dev)
 {
-	struct dmi_header *dm = dmi_dev->device_data;
-	u8             *data = (u8 *) dm;
-	u8             len = dm->length;
-	unsigned short myaddr;
-	int            slave_addr;
-
-	if (num_addrs >= MAX_SSIF_BMCS)
-		return -1;
+	struct dmi_dev_ipmi *ipmi_data = to_dmi_dev_ipmi(dmi_dev);
 
-	if (len < 9)
-		return -1;
-
-	if (data[0x04] != 4) /* Not SSIF */
-		return -1;
+	if (!ipmi_data)
+		return -ENODEV;
 
-	if ((data[8] >> 1) == 0) {
-		/*
-		 * Some broken systems put the I2C address in
-		 * the slave address field.  We try to
-		 * accommodate them here.
-		 */
-		myaddr = data[6] >> 1;
-		slave_addr = 0;
-	} else {
-		myaddr = data[8] >> 1;
-		slave_addr = data[6];
+	if (!ipmi_data->good_data) {
+		pr_err(PFX "DMI data for this device was invalid.\n");
+		return -EINVAL;
 	}
 
-	return new_ssif_client(myaddr, NULL, 0, 0, SI_SMBIOS);
+	if (ipmi_data->type != IPMI_DMI_TYPE_SSIF)
+		return -ENODEV;
+
+	return new_ssif_client(ipmi_data->base_addr, NULL, 0,
+			       ipmi_data->slave_addr, SI_SMBIOS);
 }
 
 static void dmi_iterator(void)
 {
-	const struct dmi_device *dev = NULL;
+	struct dmi_device *dev = NULL;
 
 	while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev)))
 		decode_dmi(dev);
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 13d9bca..6e4859d 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -313,9 +313,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);
@@ -328,11 +424,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 e130c38..ca37ba5 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; }