i7core_edac: return -ENODEV when devices were already probed
[pandora-kernel.git] / drivers / edac / i7core_edac.c
index d64ac2c..362861c 100644 (file)
@@ -243,8 +243,6 @@ struct i7core_pvt {
        struct i7core_inject    inject;
        struct i7core_channel   channel[NUM_CHANS];
 
-       int             channels; /* Number of active channels */
-
        int             ce_count_available;
        int             csrow_map[NUM_CHANS][MAX_DIMMS];
 
@@ -283,7 +281,8 @@ static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
                /* Memory controller */
        { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR)     },
        { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD)  },
-                       /* Exists only for RDIMM */
+
+               /* Exists only for RDIMM */
        { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1  },
        { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
 
@@ -304,16 +303,6 @@ static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
        { 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)   },
-
-               /* Generic Non-core registers */
-       /*
-        * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
-        * On Xeon 55xx, however, it has a different id (8086:2c40). So,
-        * the probing code needs to test for the other address in case of
-        * failure of this one
-        */
-       { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE)  },
-
 };
 
 static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
@@ -330,12 +319,6 @@ static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
        { PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
        { PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
        { PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC)   },
-
-       /*
-        * This is the PCI device has an alternate address on some
-        * processors like Core i7 860
-        */
-       { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE)     },
 };
 
 static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
@@ -363,10 +346,6 @@ static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
        { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) },
        { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) },
        { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2)   },
-
-               /* Generic Non-core registers */
-       { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2)  },
-
 };
 
 #define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) }
@@ -374,6 +353,7 @@ static const struct pci_id_table pci_dev_table[] = {
        PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem),
        PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield),
        PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere),
+       {0,}                    /* 0 terminated list. */
 };
 
 /*
@@ -447,6 +427,36 @@ static struct i7core_dev *get_i7core_dev(u8 socket)
        return NULL;
 }
 
+static struct i7core_dev *alloc_i7core_dev(u8 socket,
+                                          const struct pci_id_table *table)
+{
+       struct i7core_dev *i7core_dev;
+
+       i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
+       if (!i7core_dev)
+               return NULL;
+
+       i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * table->n_devs,
+                                  GFP_KERNEL);
+       if (!i7core_dev->pdev) {
+               kfree(i7core_dev);
+               return NULL;
+       }
+
+       i7core_dev->socket = socket;
+       i7core_dev->n_devs = table->n_devs;
+       list_add_tail(&i7core_dev->list, &i7core_edac_list);
+
+       return i7core_dev;
+}
+
+static void free_i7core_dev(struct i7core_dev *i7core_dev)
+{
+       list_del(&i7core_dev->list);
+       kfree(i7core_dev->pdev);
+       kfree(i7core_dev);
+}
+
 /****************************************************************************
                        Memory check routines
  ****************************************************************************/
@@ -550,12 +560,13 @@ static int i7core_get_active_channels(const u8 socket, unsigned *channels,
        return 0;
 }
 
-static int get_dimm_config(const struct mem_ctl_info *mci, int *csrow)
+static int get_dimm_config(const struct mem_ctl_info *mci)
 {
        struct i7core_pvt *pvt = mci->pvt_info;
        struct csrow_info *csr;
        struct pci_dev *pdev;
        int i, j;
+       int csrow = 0;
        unsigned long last_page = 0;
        enum edac_type mode;
        enum mem_type mtype;
@@ -671,7 +682,7 @@ static int get_dimm_config(const struct mem_ctl_info *mci, int *csrow)
 
                        npages = MiB_TO_PAGES(size);
 
-                       csr = &mci->csrows[*csrow];
+                       csr = &mci->csrows[csrow];
                        csr->first_page = last_page + 1;
                        last_page += npages;
                        csr->last_page = last_page;
@@ -679,13 +690,13 @@ static int get_dimm_config(const struct mem_ctl_info *mci, int *csrow)
 
                        csr->page_mask = 0;
                        csr->grain = 8;
-                       csr->csrow_idx = *csrow;
+                       csr->csrow_idx = csrow;
                        csr->nr_channels = 1;
 
                        csr->channels[0].chan_idx = i;
                        csr->channels[0].ce_count = 0;
 
-                       pvt->csrow_map[i][j] = *csrow;
+                       pvt->csrow_map[i][j] = csrow;
 
                        switch (banks) {
                        case 4:
@@ -704,7 +715,7 @@ static int get_dimm_config(const struct mem_ctl_info *mci, int *csrow)
                        csr->edac_mode = mode;
                        csr->mtype = mtype;
 
-                       (*csrow)++;
+                       csrow++;
                }
 
                pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
@@ -1225,7 +1236,7 @@ static const struct mcidev_sysfs_attribute i7core_sysfs_udimm_attrs[] = {
  ****************************************************************************/
 
 /*
- *     i7core_put_devices      'put' all the devices that we have
+ *     i7core_put_all_devices  'put' all the devices that we have
  *                             reserved via 'get'
  */
 static void i7core_put_devices(struct i7core_dev *i7core_dev)
@@ -1242,7 +1253,6 @@ static void i7core_put_devices(struct i7core_dev *i7core_dev)
                        PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
                pci_dev_put(pdev);
        }
-       kfree(i7core_dev->pdev);
 }
 
 static void i7core_put_all_devices(void)
@@ -1251,8 +1261,7 @@ static void i7core_put_all_devices(void)
 
        list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
                i7core_put_devices(i7core_dev);
-               list_del(&i7core_dev->list);
-               kfree(i7core_dev);
+               free_i7core_dev(i7core_dev);
        }
 }
 
@@ -1295,17 +1304,18 @@ static unsigned i7core_pci_lastbus(void)
 }
 
 /*
- *     i7core_get_devices      Find and perform 'get' operation on the MCH's
+ *     i7core_get_all_devices  Find and perform 'get' operation on the MCH's
  *                     device/functions we want to reference for this driver
  *
  *                     Need to 'get' device 16 func 1 and func 2
  */
-int i7core_get_onedevice(struct pci_dev **prev, const int devno,
-                        const struct pci_id_descr *dev_descr,
-                        const unsigned n_devs,
-                        const unsigned last_bus)
+static int i7core_get_onedevice(struct pci_dev **prev,
+                               const struct pci_id_table *table,
+                               const unsigned devno,
+                               const unsigned last_bus)
 {
        struct i7core_dev *i7core_dev;
+       const struct pci_id_descr *dev_descr = &table->descr[devno];
 
        struct pci_dev *pdev = NULL;
        u8 bus = 0;
@@ -1314,20 +1324,6 @@ int i7core_get_onedevice(struct pci_dev **prev, const int devno,
        pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
                              dev_descr->dev_id, *prev);
 
-       /*
-        * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
-        * is at addr 8086:2c40, instead of 8086:2c41. So, we need
-        * to probe for the alternate address in case of failure
-        */
-       if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
-               pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
-                                     PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
-
-       if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev)
-               pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
-                                     PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT,
-                                     *prev);
-
        if (!pdev) {
                if (*prev) {
                        *prev = pdev;
@@ -1354,18 +1350,11 @@ int i7core_get_onedevice(struct pci_dev **prev, const int devno,
 
        i7core_dev = get_i7core_dev(socket);
        if (!i7core_dev) {
-               i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
-               if (!i7core_dev)
-                       return -ENOMEM;
-               i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * n_devs,
-                                          GFP_KERNEL);
-               if (!i7core_dev->pdev) {
-                       kfree(i7core_dev);
+               i7core_dev = alloc_i7core_dev(socket, table);
+               if (!i7core_dev) {
+                       pci_dev_put(pdev);
                        return -ENOMEM;
                }
-               i7core_dev->socket = socket;
-               i7core_dev->n_devs = n_devs;
-               list_add_tail(&i7core_dev->list, &i7core_edac_list);
        }
 
        if (i7core_dev->pdev[devno]) {
@@ -1407,27 +1396,31 @@ int i7core_get_onedevice(struct pci_dev **prev, const int devno,
                dev_descr->func,
                PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
 
+       /*
+        * As stated on drivers/pci/search.c, the reference count for
+        * @from is always decremented if it is not %NULL. So, as we need
+        * to get all devices up to null, we need to do a get for the device
+        */
+       pci_dev_get(pdev);
+
        *prev = pdev;
 
        return 0;
 }
 
-static int i7core_get_devices(const struct pci_id_table *table)
+static int i7core_get_all_devices(void)
 {
        int i, rc, last_bus;
        struct pci_dev *pdev = NULL;
-       const struct pci_id_descr *dev_descr;
+       const struct pci_id_table *table = pci_dev_table;
 
        last_bus = i7core_pci_lastbus();
 
        while (table && table->descr) {
-               dev_descr = table->descr;
                for (i = 0; i < table->n_devs; i++) {
                        pdev = NULL;
                        do {
-                               rc = i7core_get_onedevice(&pdev, i,
-                                                         &dev_descr[i],
-                                                         table->n_devs,
+                               rc = i7core_get_onedevice(&pdev, table, i,
                                                          last_bus);
                                if (rc < 0) {
                                        if (i == 0) {
@@ -1452,10 +1445,6 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
        struct pci_dev *pdev;
        int i, func, slot;
 
-       /* Associates i7core_dev and mci for future usage */
-       pvt->i7core_dev = i7core_dev;
-       i7core_dev->mci = mci;
-
        pvt->is_registered = 0;
        for (i = 0; i < i7core_dev->n_devs; i++) {
                pdev = i7core_dev->pdev[i];
@@ -1879,29 +1868,85 @@ static int i7core_mce_check_error(void *priv, struct mce *mce)
        return 1;
 }
 
-static int i7core_register_mci(struct i7core_dev *i7core_dev,
-                              const int num_channels, const int num_csrows)
+static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
+{
+       pvt->i7core_pci = edac_pci_create_generic_ctl(
+                                               &pvt->i7core_dev->pdev[0]->dev,
+                                               EDAC_MOD_STR);
+       if (unlikely(!pvt->i7core_pci))
+               pr_warn("Unable to setup PCI error report via EDAC\n");
+}
+
+static void i7core_pci_ctl_release(struct i7core_pvt *pvt)
+{
+       if (likely(pvt->i7core_pci))
+               edac_pci_release_generic_ctl(pvt->i7core_pci);
+       else
+               i7core_printk(KERN_ERR,
+                               "Couldn't find mem_ctl_info for socket %d\n",
+                               pvt->i7core_dev->socket);
+       pvt->i7core_pci = NULL;
+}
+
+static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
+{
+       struct mem_ctl_info *mci = i7core_dev->mci;
+       struct i7core_pvt *pvt;
+
+       if (unlikely(!mci || !mci->pvt_info)) {
+               debugf0("MC: " __FILE__ ": %s(): dev = %p\n",
+                       __func__, &i7core_dev->pdev[0]->dev);
+
+               i7core_printk(KERN_ERR, "Couldn't find mci handler\n");
+               return;
+       }
+
+       pvt = mci->pvt_info;
+
+       debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
+               __func__, mci, &i7core_dev->pdev[0]->dev);
+
+       /* Disable MCE NMI handler */
+       edac_mce_unregister(&pvt->edac_mce);
+
+       /* Disable EDAC polling */
+       i7core_pci_ctl_release(pvt);
+
+       /* Remove MC sysfs nodes */
+       edac_mc_del_mc(mci->dev);
+
+       debugf1("%s: free mci struct\n", mci->ctl_name);
+       kfree(mci->ctl_name);
+       edac_mc_free(mci);
+       i7core_dev->mci = NULL;
+}
+
+static int i7core_register_mci(struct i7core_dev *i7core_dev)
 {
        struct mem_ctl_info *mci;
        struct i7core_pvt *pvt;
-       int csrow = 0;
-       int rc;
+       int rc, channels, csrows;
+
+       /* Check the number of active and not disabled channels */
+       rc = i7core_get_active_channels(i7core_dev->socket, &channels, &csrows);
+       if (unlikely(rc < 0))
+               return rc;
 
        /* allocate a new MC control structure */
-       mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
-                           i7core_dev->socket);
+       mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, i7core_dev->socket);
        if (unlikely(!mci))
                return -ENOMEM;
 
        debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
                __func__, mci, &i7core_dev->pdev[0]->dev);
 
-       /* record ptr to the generic device */
-       mci->dev = &i7core_dev->pdev[0]->dev;
-
        pvt = mci->pvt_info;
        memset(pvt, 0, sizeof(*pvt));
 
+       /* Associates i7core_dev and mci for future usage */
+       pvt->i7core_dev = i7core_dev;
+       i7core_dev->mci = mci;
+
        /*
         * FIXME: how to handle RDDR3 at MCI level? It is possible to have
         * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
@@ -1917,22 +1962,23 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev,
        mci->dev_name = pci_name(i7core_dev->pdev[0]);
        mci->ctl_page_to_phys = NULL;
 
+       /* Store pci devices at mci for faster access */
+       rc = mci_bind_devs(mci, i7core_dev);
+       if (unlikely(rc < 0))
+               goto fail0;
+
        if (pvt->is_registered)
                mci->mc_driver_sysfs_attributes = i7core_sysfs_rdimm_attrs;
        else
                mci->mc_driver_sysfs_attributes = i7core_sysfs_udimm_attrs;
 
+       /* Get dimm basic config */
+       get_dimm_config(mci);
+       /* record ptr to the generic device */
+       mci->dev = &i7core_dev->pdev[0]->dev;
        /* Set the function pointer to an actual operation function */
        mci->edac_check = i7core_check_error;
 
-       /* Store pci devices at mci for faster access */
-       rc = mci_bind_devs(mci, i7core_dev);
-       if (unlikely(rc < 0))
-               goto fail;
-
-       /* Get dimm basic config */
-       get_dimm_config(mci, &csrow);
-
        /* add this new MC control structure to EDAC's list of MCs */
        if (unlikely(edac_mc_add_mc(mci))) {
                debugf0("MC: " __FILE__
@@ -1942,7 +1988,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev,
                 */
 
                rc = -EINVAL;
-               goto fail;
+               goto fail0;
        }
 
        /* Default error mask is any memory */
@@ -1953,31 +1999,28 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev,
        pvt->inject.page = -1;
        pvt->inject.col = -1;
 
+       /* allocating generic PCI control info */
+       i7core_pci_ctl_create(pvt);
+
        /* Registers on edac_mce in order to receive memory errors */
        pvt->edac_mce.priv = mci;
        pvt->edac_mce.check_error = i7core_mce_check_error;
-
-       /* allocating generic PCI control info */
-       pvt->i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
-                                                EDAC_MOD_STR);
-       if (unlikely(!pvt->i7core_pci)) {
-               printk(KERN_WARNING
-                       "%s(): Unable to create PCI control\n",
-                       __func__);
-               printk(KERN_WARNING
-                       "%s(): PCI error report via EDAC not setup\n",
-                       __func__);
-       }
-
        rc = edac_mce_register(&pvt->edac_mce);
        if (unlikely(rc < 0)) {
                debugf0("MC: " __FILE__
                        ": %s(): failed edac_mce_register()\n", __func__);
+               goto fail1;
        }
 
-fail:
-       if (rc < 0)
-               edac_mc_free(mci);
+       return 0;
+
+fail1:
+       i7core_pci_ctl_release(pvt);
+       edac_mc_del_mc(mci->dev);
+fail0:
+       kfree(mci->ctl_name);
+       edac_mc_free(mci);
+       i7core_dev->mci = NULL;
        return rc;
 }
 
@@ -2003,25 +2046,16 @@ static int __devinit i7core_probe(struct pci_dev *pdev,
         */
        if (unlikely(probed >= 1)) {
                mutex_unlock(&i7core_edac_lock);
-               return -EINVAL;
+               return -ENODEV;
        }
        probed++;
 
-       rc = i7core_get_devices(pci_dev_table);
+       rc = i7core_get_all_devices();
        if (unlikely(rc < 0))
                goto fail0;
 
        list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
-               int channels;
-               int csrows;
-
-               /* Check the number of active and not disabled channels */
-               rc = i7core_get_active_channels(i7core_dev->socket,
-                                               &channels, &csrows);
-               if (unlikely(rc < 0))
-                       goto fail1;
-
-               rc = i7core_register_mci(i7core_dev, channels, csrows);
+               rc = i7core_register_mci(i7core_dev);
                if (unlikely(rc < 0))
                        goto fail1;
        }
@@ -2032,6 +2066,9 @@ static int __devinit i7core_probe(struct pci_dev *pdev,
        return 0;
 
 fail1:
+       list_for_each_entry(i7core_dev, &i7core_edac_list, list)
+               i7core_unregister_mci(i7core_dev);
+
        i7core_put_all_devices();
 fail0:
        mutex_unlock(&i7core_edac_lock);
@@ -2044,9 +2081,7 @@ fail0:
  */
 static void __devexit i7core_remove(struct pci_dev *pdev)
 {
-       struct mem_ctl_info *mci;
-       struct i7core_dev *i7core_dev, *tmp;
-       struct i7core_pvt *pvt;
+       struct i7core_dev *i7core_dev;
 
        debugf0(__FILE__ ": %s()\n", __func__);
 
@@ -2059,46 +2094,18 @@ static void __devexit i7core_remove(struct pci_dev *pdev)
         */
 
        mutex_lock(&i7core_edac_lock);
-       list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
-               mci = find_mci_by_dev(&i7core_dev->pdev[0]->dev);
-               if (unlikely(!mci || !mci->pvt_info)) {
-                       debugf0("MC: " __FILE__ ": %s(): dev = %p\n",
-                               __func__, &i7core_dev->pdev[0]->dev);
 
-                               i7core_printk(KERN_ERR,
-                                     "Couldn't find mci hanler\n");
-               } else {
-                       pvt = mci->pvt_info;
-                       i7core_dev = pvt->i7core_dev;
-
-                       debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
-                               __func__, mci, &i7core_dev->pdev[0]->dev);
-
-                       /* Disable MCE NMI handler */
-                       edac_mce_unregister(&pvt->edac_mce);
-
-                       /* Disable EDAC polling */
-                       if (likely(pvt->i7core_pci))
-                               edac_pci_release_generic_ctl(pvt->i7core_pci);
-                       else
-                               i7core_printk(KERN_ERR,
-                                             "Couldn't find mem_ctl_info for socket %d\n",
-                                             i7core_dev->socket);
-                       pvt->i7core_pci = NULL;
-
-                       /* Remove MC sysfs nodes */
-                       edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
-
-                       debugf1("%s: free mci struct\n", mci->ctl_name);
-                       kfree(mci->ctl_name);
-                       edac_mc_free(mci);
-
-                       /* Release PCI resources */
-                       i7core_put_devices(i7core_dev);
-                       list_del(&i7core_dev->list);
-                       kfree(i7core_dev);
-               }
+       if (unlikely(!probed)) {
+               mutex_unlock(&i7core_edac_lock);
+               return;
        }
+
+       list_for_each_entry(i7core_dev, &i7core_edac_list, list)
+               i7core_unregister_mci(i7core_dev);
+
+       /* Release PCI resources */
+       i7core_put_all_devices();
+
        probed--;
 
        mutex_unlock(&i7core_edac_lock);