i7core_edac: Registers all supported MC functions

Now, it will try to register on all supported Memory Controller
functions.

It should be noticed that dev3, function 2 is present only on chips with
Registered DIMM's, according to the datasheet. So, the driver doesn't
return -ENODEV is all functions but this one were successfully
registered and enabled:

    EDAC i7core: Registered device 8086:2c18 fn=3 0
    EDAC i7core: Registered device 8086:2c19 fn=3 1
    EDAC i7core: Device not found: PCI ID 8086:2c1a (dev 3, func 2)
    EDAC i7core: Registered device 8086:2c1c fn=3 4
    EDAC i7core: Registered device 8086:2c20 fn=4 0
    EDAC i7core: Registered device 8086:2c21 fn=4 1
    EDAC i7core: Registered device 8086:2c22 fn=4 2
    EDAC i7core: Registered device 8086:2c23 fn=4 3
    EDAC i7core: Registered device 8086:2c28 fn=5 0
    EDAC i7core: Registered device 8086:2c29 fn=5 1
    EDAC i7core: Registered device 8086:2c2a fn=5 2
    EDAC i7core: Registered device 8086:2c2b fn=5 3
    EDAC i7core: Registered device 8086:2c30 fn=6 0
    EDAC i7core: Registered device 8086:2c31 fn=6 1
    EDAC i7core: Registered device 8086:2c32 fn=6 2
    EDAC i7core: Registered device 8086:2c33 fn=6 3
    EDAC i7core: Driver loaded.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index da40730..79aa84e 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -128,7 +128,8 @@
  */
 
 #define NUM_CHANS 3
-#define NUM_FUNCS 1
+#define NUM_MCR_FUNCS  4
+#define NUM_CHAN_FUNCS 3
 
 struct i7core_info {
 	u32	mc_control;
@@ -153,9 +154,16 @@
 	u32 dimms;
 };
 
+struct pci_id_descr {
+	int		dev;
+	int		func;
+	int 		dev_id;
+	struct pci_dev	*pdev;
+};
+
 struct i7core_pvt {
-	struct pci_dev		*pci_mcr;	/* Dev 3:0 */
-	struct pci_dev		*pci_ch[NUM_CHANS][NUM_FUNCS];
+	struct pci_dev		*pci_mcr[NUM_MCR_FUNCS];
+	struct pci_dev		*pci_ch[NUM_CHANS][NUM_CHAN_FUNCS];
 	struct i7core_info	info;
 	struct i7core_inject	inject;
 	struct i7core_channel	channel[NUM_CHANS];
@@ -167,11 +175,47 @@
 	u16 fsb_mapping_errors;	/* DID for the branchmap,control */
 };
 
-static int chan_pci_ids[NUM_CHANS] = {
-	PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL,	/* Dev 4 */
-	PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL,	/* Dev 5 */
-	PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL,	/* Dev 6 */
+#define PCI_DESCR(device, function, device_id)	\
+	.dev = (device),			\
+	.func = (function),			\
+	.dev_id = (device_id)
+
+struct pci_id_descr pci_devs[] = {
+		/* Memory controller */
+	{ PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR)     },
+	{ PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD)  },
+	{ PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS)  }, /* if RDIMM is supported */
+	{ PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
+
+		/* Channel 0 */
+	{ PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
+	{ PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
+	{ PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
+	{ PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC)   },
+
+		/* Channel 1 */
+	{ PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
+	{ PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
+	{ PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
+	{ PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC)   },
+
+		/* Channel 2 */
+	{ PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
+	{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
+	{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
+	{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC)   },
 };
+#define N_DEVS ARRAY_SIZE(pci_devs)
+
+/*
+ *	pci_device_id	table for which devices we are looking for
+ * This should match the first device at pci_devs table
+ */
+static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)},
+	{0,}			/* 0 terminated list. */
+};
+
 
 /* Table of devices attributes supported by this driver */
 static const struct i7core_dev_info i7core_devs[] = {
@@ -242,9 +286,12 @@
 	struct i7core_pvt *pvt = mci->pvt_info;
 	int i;
 
-	pci_read_config_dword(pvt->pci_mcr, MC_CONTROL, &pvt->info.mc_control);
-	pci_read_config_dword(pvt->pci_mcr, MC_STATUS, &pvt->info.mc_status);
-	pci_read_config_dword(pvt->pci_mcr, MC_MAX_DOD, &pvt->info.max_dod);
+	if (!pvt->pci_mcr[0])
+		return -ENODEV;
+
+	pci_read_config_dword(pvt->pci_mcr[0], MC_CONTROL, &pvt->info.mc_control);
+	pci_read_config_dword(pvt->pci_mcr[0], MC_STATUS, &pvt->info.mc_status);
+	pci_read_config_dword(pvt->pci_mcr[0], MC_MAX_DOD, &pvt->info.max_dod);
 
 	if (ECC_ENABLED(pvt))
 		debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4);
@@ -303,14 +350,19 @@
    we're disabling error injection on all write calls to the sysfs nodes that
    controls the error code injection.
  */
-static void disable_inject(struct mem_ctl_info *mci)
+static int disable_inject(struct mem_ctl_info *mci)
 {
 	struct i7core_pvt *pvt = mci->pvt_info;
 
 	pvt->inject.enable = 0;
 
+	if (!pvt->pci_ch[pvt->inject.channel][0])
+		return -ENODEV;
+
 	pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
 				MC_CHANNEL_ERROR_MASK, 0);
+
+	return 0;
 }
 
 /*
@@ -550,6 +602,9 @@
 	int  rc;
 	long enable;
 
+	if (!pvt->pci_ch[pvt->inject.channel][0])
+		return 0;
+
 	rc = strict_strtoul(data, 10, &enable);
 	if ((rc < 0))
 		return 0;
@@ -684,17 +739,12 @@
  *	i7core_put_devices	'put' all the devices that we have
  *				reserved via 'get'
  */
-static void i7core_put_devices(struct mem_ctl_info *mci)
+static void i7core_put_devices(void)
 {
-	struct i7core_pvt *pvt = mci->pvt_info;
-	int i, n;
+	int i;
 
-	pci_dev_put(pvt->pci_mcr);
-
-	/* Release all PCI device functions at MTR channel controllers */
-	for (i = 0; i < NUM_CHANS; i++)
-		for (n = 0; n < NUM_FUNCS; n++)
-			pci_dev_put(pvt->pci_ch[i][n]);
+	for (i = 0; i < N_DEVS; i++)
+		pci_dev_put(pci_devs[i].pdev);
 }
 
 /*
@@ -703,50 +753,67 @@
  *
  *			Need to 'get' device 16 func 1 and func 2
  */
-static int i7core_get_devices(struct mem_ctl_info *mci, int dev_idx)
+static int i7core_get_devices(struct mem_ctl_info *mci, struct pci_dev *mcidev)
 {
-	struct i7core_pvt *pvt;
-	struct pci_dev *pdev;
-	int i, n, func;
+	struct i7core_pvt *pvt = mci->pvt_info;
+	int rc, i,func;
+	struct pci_dev *pdev = NULL;
 
 	pvt = mci->pvt_info;
 	memset(pvt, 0, sizeof(*pvt));
 
-	pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR,
-			      NULL);
-	if (!pdev) {
-		i7core_printk(KERN_ERR,
-			"Couldn't get PCI ID %04x:%04x function 0\n",
-			PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR);
-		return -ENODEV;
-	}
-	pvt->pci_mcr=pdev;
+	for (i = 0; i < N_DEVS; i++) {
+		pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
+					pci_devs[i].dev_id, NULL);
+		if (!pdev) {
+			/* End of list, leave */
+			i7core_printk(KERN_ERR,
+				"Device not found: PCI ID %04x:%04x "
+				"(dev %d, func %d)\n",
+				PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
+				pci_devs[i].dev,pci_devs[i].func);
+			if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2))
+				continue; /* Only on chips with RDIMMs */
+			else
+				i7core_put_devices();
+		}
+		pci_devs[i].pdev = pdev;
 
-	/* Retrieve all needed functions at MTR channel controllers */
-	for (i = 0; i < NUM_CHANS; i++) {
-		pdev = NULL;
-		for (n = 0; n < NUM_FUNCS; n++) {
-			pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
-					      chan_pci_ids[i], pdev);
-			if (!pdev) {
-				/* End of list, leave */
-				i7core_printk(KERN_ERR,
-					"Device not found: PCI ID %04x:%04x "
-					"found only %d functions "
-					"(broken BIOS?)\n",
-					PCI_VENDOR_ID_INTEL,
-					chan_pci_ids[i], n);
-				i7core_put_devices(mci);
-				return -ENODEV;
-			}
-			func = PCI_FUNC(pdev->devfn);
-			pvt->pci_ch[i][func] = pdev;
+		rc = pci_enable_device(pdev);
+		if (rc < 0) {
+			i7core_printk(KERN_ERR,
+				"Couldn't enable PCI ID %04x:%04x "
+				"(dev %d, func %d)\n",
+				PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
+				pci_devs[i].dev, pci_devs[i].func);
+			i7core_put_devices();
+			return rc;
+		}
+		/* Sanity check */
+		if (PCI_FUNC(pdev->devfn) != pci_devs[i].func) {
+			i7core_printk(KERN_ERR,
+				"Device PCI ID %04x:%04x "
+				"has function %d instead of %d\n",
+				PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
+				PCI_FUNC(pdev->devfn), pci_devs[i].func);
+			i7core_put_devices();
+			return -EINVAL;
+		}
+
+		i7core_printk(KERN_INFO,
+				"Registered device %0x:%0x fn=%0x %0x\n",
+				PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
+				PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+
+		func = PCI_FUNC(pdev->devfn);
+		if (pci_devs[i].dev < 4) {
+			pvt->pci_mcr[func] = pdev;
+		} else {
+			pvt->pci_ch[pci_devs[i].dev - 4][func] = pdev;
 		}
 	}
-	i7core_printk(KERN_INFO, "Driver loaded.\n");
 
-	/* Get dimm basic config */
-	get_dimm_config(mci);
+	i7core_printk(KERN_INFO, "Driver loaded.\n");
 
 	return 0;
 }
@@ -763,7 +830,6 @@
 {
 	struct mem_ctl_info *mci;
 	struct i7core_pvt *pvt;
-	int rc;
 	int num_channels;
 	int num_csrows;
 	int num_dimms_per_channel;
@@ -772,20 +838,6 @@
 	if (dev_idx >= ARRAY_SIZE(i7core_devs))
 		return -EINVAL;
 
-	/* wake up device */
-	rc = pci_enable_device(pdev);
-	if (rc == -EIO)
-		return rc;
-
-	debugf0("MC: " __FILE__ ": %s(), pdev bus %u dev=0x%x fn=0x%x\n",
-		__func__,
-		pdev->bus->number,
-		PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
-
-	/* We only are looking for func 0 of the set */
-	if (PCI_FUNC(pdev->devfn) != 0)
-		return -ENODEV;
-
 	num_channels = NUM_CHANS;
 
 	/* FIXME: FAKE data, since we currently don't now how to get this */
@@ -808,10 +860,6 @@
 //	pvt->maxch = num_channels;
 //	pvt->maxdimmperch = num_dimms_per_channel;
 
-	/* 'get' the pci devices we want to reserve for our use */
-	if (i7core_get_devices(mci, dev_idx))
-		goto fail0;
-
 	mci->mc_idx = 0;
 	mci->mtype_cap = MEM_FLAG_FB_DDR2;	/* FIXME: it uses DDR3 */
 	mci->edac_ctl_cap = EDAC_FLAG_NONE;
@@ -823,6 +871,10 @@
 	mci->ctl_page_to_phys = NULL;
 	mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
 
+	/* 'get' the pci devices we want to reserve for our use */
+	if (i7core_get_devices(mci, pdev))
+		goto fail0;
+
 	/* add this new MC control structure to EDAC's list of MCs */
 	if (edac_mc_add_mc(mci)) {
 		debugf0("MC: " __FILE__
@@ -852,10 +904,13 @@
 	pvt->inject.page = -1;
 	pvt->inject.col = -1;
 
+	/* Get dimm basic config */
+	get_dimm_config(mci);
+
 	return 0;
 
 fail1:
-	i7core_put_devices(mci);
+	i7core_put_devices();
 
 fail0:
 	edac_mc_free(mci);
@@ -880,21 +935,11 @@
 		return;
 
 	/* retrieve references to resources, and free those resources */
-	i7core_put_devices(mci);
+	i7core_put_devices();
 
 	edac_mc_free(mci);
 }
 
-/*
- *	pci_device_id	table for which devices we are looking for
- *
- *	The "E500P" device is the first device supported.
- */
-static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
-	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)},
-	{0,}			/* 0 terminated list. */
-};
-
 MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
 
 /*