Merge branch 'for-linus' of git://git.kernel.dk/linux-2.6-block
[firefly-linux-kernel-4.4.55.git] / drivers / edac / i7300_edac.c
index 728f9b0ab62cc6410609f17364d6388313613b5b..05523b504271f09e08fd8a4c63441aaa5094748b 100644 (file)
 #define i7300_mc_printk(mci, level, fmt, arg...) \
        edac_mc_chipset_printk(mci, level, "i7300", fmt, ##arg)
 
+/***********************************************
+ * i7300 Limit constants Structs and static vars
+ ***********************************************/
+
 /*
  * Memory topology is organized as:
  *     Branch 0 - 2 channels: channels 0 and 1 (FDB0 PCI dev 21.0)
 #define to_csrow(slot, ch, branch)                                     \
                (to_channel(ch, branch) | ((slot) << 2))
 
-/*
- * I7300 devices
- * All 3 functions of Device 16 (0,1,2) share the SAME DID and
- * uses PCI_DEVICE_ID_INTEL_I7300_MCH_ERR for device 16 (0,1,2),
- * PCI_DEVICE_ID_INTEL_I7300_MCH_FB0 and PCI_DEVICE_ID_INTEL_I7300_MCH_FB1
- * for device 21 (0,1).
- */
+/* Device name and register DID (Device ID) */
+struct i7300_dev_info {
+       const char *ctl_name;   /* name for this device */
+       u16 fsb_mapping_errors; /* DID for the branchmap,control */
+};
+
+/* Table of devices attributes supported by this driver */
+static const struct i7300_dev_info i7300_devs[] = {
+       {
+               .ctl_name = "I7300",
+               .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7300_MCH_ERR,
+       },
+};
+
+struct i7300_dimm_info {
+       int megabytes;          /* size, 0 means not present  */
+};
+
+/* driver private data structure */
+struct i7300_pvt {
+       struct pci_dev *pci_dev_16_0_fsb_ctlr;          /* 16.0 */
+       struct pci_dev *pci_dev_16_1_fsb_addr_map;      /* 16.1 */
+       struct pci_dev *pci_dev_16_2_fsb_err_regs;      /* 16.2 */
+       struct pci_dev *pci_dev_2x_0_fbd_branch[MAX_BRANCHES];  /* 21.0  and 22.0 */
+
+       u16 tolm;                               /* top of low memory */
+       u64 ambase;                             /* AMB BAR */
+
+       u32 mc_settings;                        /* Report several settings */
+       u32 mc_settings_a;
+
+       u16 mir[MAX_MIR];                       /* Memory Interleave Reg*/
+
+       u16 mtr[MAX_SLOTS][MAX_BRANCHES];       /* Memory Technlogy Reg */
+       u16 ambpresent[MAX_CHANNELS];           /* AMB present regs */
+
+       /* DIMM information matrix, allocating architecture maximums */
+       struct i7300_dimm_info dimm_info[MAX_SLOTS][MAX_CHANNELS];
+
+       /* Temporary buffer for use when preparing error messages */
+       char *tmp_prt_buffer;
+};
+
+/* FIXME: Why do we need to have this static? */
+static struct edac_pci_ctl_info *i7300_pci;
 
-/****************************************************
- * i7300 Register definitions for memory enumberation
- ****************************************************/
+/***************************************************
+ * i7300 Register definitions for memory enumeration
+ ***************************************************/
 
 /*
  * Device 16,
   #define IS_SINGLE_MODE(mca)          ((mca) & (1 << 14))
 
 #define TOLM                   0x6C
-#define REDMEMB                        0x7C
 
 #define MIR0                   0x80
 #define MIR1                   0x84
 #define AMBPRESENT_0   0x64
 #define AMBPRESENT_1   0x66
 
-const static u16 mtr_regs [MAX_SLOTS] = {
+const static u16 mtr_regs[MAX_SLOTS] = {
        0x80, 0x84, 0x88, 0x8c,
        0x82, 0x86, 0x8a, 0x8e
 };
 
-/* Defines to extract the vaious fields from the
+/*
+ * Defines to extract the vaious fields from the
  *     MTRx - Memory Technology Registers
  */
 #define MTR_DIMMS_PRESENT(mtr)         ((mtr) & (1 << 8))
@@ -268,60 +310,51 @@ static const char *ferr_global_lo_name[] = {
 };
 #define ferr_global_lo_is_fatal(errno) ((errno < 16) ? 0 : 1)
 
-/* Device name and register DID (Device ID) */
-struct i7300_dev_info {
-       const char *ctl_name;   /* name for this device */
-       u16 fsb_mapping_errors; /* DID for the branchmap,control */
-};
-
-/* Table of devices attributes supported by this driver */
-static const struct i7300_dev_info i7300_devs[] = {
-       {
-               .ctl_name = "I7300",
-               .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7300_MCH_ERR,
-       },
-};
-
-struct i7300_dimm_info {
-       int megabytes;          /* size, 0 means not present  */
-};
-
-/* driver private data structure */
-struct i7300_pvt {
-       struct pci_dev *pci_dev_16_0_fsb_ctlr;          /* 16.0 */
-       struct pci_dev *pci_dev_16_1_fsb_addr_map;      /* 16.1 */
-       struct pci_dev *pci_dev_16_2_fsb_err_regs;      /* 16.2 */
-       struct pci_dev *pci_dev_2x_0_fbd_branch[MAX_BRANCHES];  /* 21.0  and 22.0 */
-
-       u16 tolm;                               /* top of low memory */
-       u64 ambase;                             /* AMB BAR */
+#define NRECMEMA       0xbe
+  #define NRECMEMA_BANK(v)     (((v) >> 12) & 7)
+  #define NRECMEMA_RANK(v)     (((v) >> 8) & 15)
 
-       u32 mc_settings;                        /* Report several settings */
-       u32 mc_settings_a;
+#define NRECMEMB       0xc0
+  #define NRECMEMB_IS_WR(v)    ((v) & (1 << 31))
+  #define NRECMEMB_CAS(v)      (((v) >> 16) & 0x1fff)
+  #define NRECMEMB_RAS(v)      ((v) & 0xffff)
 
-       u16 mir[MAX_MIR];                       /* Memory Interleave Reg*/
+#define REDMEMA                0xdc
 
-       u16 mtr[MAX_SLOTS][MAX_BRANCHES];               /* Memory Technlogy Reg */
-       u16 ambpresent[MAX_CHANNELS];           /* AMB present regs */
+#define REDMEMB                0x7c
+  #define IS_SECOND_CH(v)      ((v) * (1 << 17))
 
-       /* DIMM information matrix, allocating architecture maximums */
-       struct i7300_dimm_info dimm_info[MAX_SLOTS][MAX_CHANNELS];
-};
+#define RECMEMA                0xe0
+  #define RECMEMA_BANK(v)      (((v) >> 12) & 7)
+  #define RECMEMA_RANK(v)      (((v) >> 8) & 15)
 
-/* FIXME: Why do we need to have this static? */
-static struct edac_pci_ctl_info *i7300_pci;
+#define RECMEMB                0xe4
+  #define RECMEMB_IS_WR(v)     ((v) & (1 << 31))
+  #define RECMEMB_CAS(v)       (((v) >> 16) & 0x1fff)
+  #define RECMEMB_RAS(v)       ((v) & 0xffff)
 
 /********************************************
  * i7300 Functions related to error detection
  ********************************************/
 
-struct i7300_error_info {
-       int dummy;      /* FIXME */
-};
-
-const char *get_err_from_table(const char *table[], int size, int pos)
+/**
+ * get_err_from_table() - Gets the error message from a table
+ * @table:     table name (array of char *)
+ * @size:      number of elements at the table
+ * @pos:       position of the element to be returned
+ *
+ * This is a small routine that gets the pos-th element of a table. If the
+ * element doesn't exist (or it is empty), it returns "reserved".
+ * Instead of calling it directly, the better is to call via the macro
+ * GET_ERR_FROM_TABLE(), that automatically checks the table size via
+ * ARRAY_SIZE() macro
+ */
+static const char *get_err_from_table(const char *table[], int size, int pos)
 {
-       if (pos >= size)
+       if (unlikely(pos >= size))
+               return "Reserved";
+
+       if (unlikely(!table[pos]))
                return "Reserved";
 
        return table[pos];
@@ -330,23 +363,13 @@ const char *get_err_from_table(const char *table[], int size, int pos)
 #define GET_ERR_FROM_TABLE(table, pos)                         \
        get_err_from_table(table, ARRAY_SIZE(table), pos)
 
-/*
- *     i7300_get_error_info    Retrieve the hardware error information from
- *                             the hardware and cache it in the 'info'
- *                             structure
- */
-static void i7300_get_error_info(struct mem_ctl_info *mci,
-                                struct i7300_error_info *info)
-{
-}
-
-/*
- *     i7300_process_error_global Retrieve the hardware error information from
- *                             the hardware and cache it in the 'info'
- *                             structure
+/**
+ * i7300_process_error_global() - Retrieve the hardware error information from
+ *                               the hardware global error registers and
+ *                               sends it to dmesg
+ * @mci: struct mem_ctl_info pointer
  */
-static void i7300_process_error_global(struct mem_ctl_info *mci,
-                                struct i7300_error_info *info)
+static void i7300_process_error_global(struct mem_ctl_info *mci)
 {
        struct i7300_pvt *pvt;
        u32 errnum, value;
@@ -395,20 +418,23 @@ error_global:
                        is_fatal ? "Fatal" : "NOT fatal", specific);
 }
 
-/*
- *     i7300_process_fbd_error Retrieve the hardware error information from
- *                             the hardware and cache it in the 'info'
- *                             structure
+/**
+ * i7300_process_fbd_error() - Retrieve the hardware error information from
+ *                            the FBD error registers and sends it via
+ *                            EDAC error API calls
+ * @mci: struct mem_ctl_info pointer
  */
-static void i7300_process_fbd_error(struct mem_ctl_info *mci,
-                                   struct i7300_error_info *info)
+static void i7300_process_fbd_error(struct mem_ctl_info *mci)
 {
        struct i7300_pvt *pvt;
        u32 errnum, value;
-       int branch;
+       u16 val16;
+       unsigned branch, channel, bank, rank, cas, ras;
+       u32 syndrome;
+
        unsigned long errors;
        const char *specific;
-       bool is_fatal;
+       bool is_wr;
 
        pvt = mci->pvt_info;
 
@@ -420,11 +446,32 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci,
                errnum = find_first_bit(&errors,
                                        ARRAY_SIZE(ferr_fat_fbd_name));
                specific = GET_ERR_FROM_TABLE(ferr_fat_fbd_name, errnum);
-               is_fatal = 1;
 
                branch = (GET_FBD_FAT_IDX(value) == 2) ? 1 : 0;
-
-               goto error_fbd;
+               pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map,
+                                    NRECMEMA, &val16);
+               bank = NRECMEMA_BANK(val16);
+               rank = NRECMEMA_RANK(val16);
+
+               pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
+                               NRECMEMB, &value);
+
+               is_wr = NRECMEMB_IS_WR(value);
+               cas = NRECMEMB_CAS(value);
+               ras = NRECMEMB_RAS(value);
+
+               snprintf(pvt->tmp_prt_buffer, PAGE_SIZE,
+                       "FATAL (Branch=%d DRAM-Bank=%d %s "
+                       "RAS=%d CAS=%d Err=0x%lx (%s))",
+                       branch, bank,
+                       is_wr ? "RDWR" : "RD",
+                       ras, cas,
+                       errors, specific);
+
+               /* Call the helper to output message */
+               edac_mc_handle_fbd_ue(mci, rank, branch << 1,
+                                     (branch << 1) + 1,
+                                     pvt->tmp_prt_buffer);
        }
 
        /* read in the 1st NON-FATAL error register */
@@ -435,62 +482,107 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci,
                errnum = find_first_bit(&errors,
                                        ARRAY_SIZE(ferr_nf_fbd_name));
                specific = GET_ERR_FROM_TABLE(ferr_nf_fbd_name, errnum);
-               is_fatal = 0;
 
                /* Clear the error bit */
                pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
                                       FERR_GLOBAL_LO, value);
 
-               goto error_fbd;
+               pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
+                       REDMEMA, &syndrome);
+
+               branch = (GET_FBD_FAT_IDX(value) == 2) ? 1 : 0;
+               pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map,
+                                    RECMEMA, &val16);
+               bank = RECMEMA_BANK(val16);
+               rank = RECMEMA_RANK(val16);
+
+               pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
+                               RECMEMB, &value);
+
+               is_wr = RECMEMB_IS_WR(value);
+               cas = RECMEMB_CAS(value);
+               ras = RECMEMB_RAS(value);
+
+               pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
+                                    REDMEMB, &value);
+
+               channel = (branch << 1);
+               if (IS_SECOND_CH(value))
+                       channel++;
+
+               /* Form out message */
+               snprintf(pvt->tmp_prt_buffer, PAGE_SIZE,
+                       "Corrected error (Branch=%d, Channel %d), "
+                       " DRAM-Bank=%d %s "
+                       "RAS=%d CAS=%d, CE Err=0x%lx, Syndrome=0x%08x(%s))",
+                       branch, channel,
+                       bank,
+                       is_wr ? "RDWR" : "RD",
+                       ras, cas,
+                       errors, syndrome, specific);
+
+               /*
+                * Call the helper to output message
+                * NOTE: Errors are reported per-branch, and not per-channel
+                *       Currently, we don't know how to identify the right
+                *       channel.
+                */
+               edac_mc_handle_fbd_ce(mci, rank, channel,
+                                     pvt->tmp_prt_buffer);
        }
        return;
-
-error_fbd:
-       i7300_mc_printk(mci, KERN_EMERG, "%s FBD error on branch %d: %s\n",
-                       is_fatal ? "Fatal" : "NOT fatal", branch, specific);
 }
 
-/*
- *     i7300_process_error_info Retrieve the hardware error information from
- *                             the hardware and cache it in the 'info'
- *                             structure
+/**
+ * i7300_check_error() - Calls the error checking subroutines
+ * @mci: struct mem_ctl_info pointer
  */
-static void i7300_process_error_info(struct mem_ctl_info *mci,
-                                struct i7300_error_info *info)
+static void i7300_check_error(struct mem_ctl_info *mci)
 {
-       i7300_process_error_global(mci, info);
-       i7300_process_fbd_error(mci, info);
+       i7300_process_error_global(mci);
+       i7300_process_fbd_error(mci);
 };
 
-/*
- *     i7300_clear_error       Retrieve any error from the hardware
- *                             but do NOT process that error.
- *                             Used for 'clearing' out of previous errors
- *                             Called by the Core module.
+/**
+ * i7300_clear_error() - Clears the error registers
+ * @mci: struct mem_ctl_info pointer
  */
 static void i7300_clear_error(struct mem_ctl_info *mci)
 {
-       struct i7300_error_info info;
+       struct i7300_pvt *pvt = mci->pvt_info;
+       u32 value;
+       /*
+        * All error values are RWC - we need to read and write 1 to the
+        * bit that we want to cleanup
+        */
 
-       i7300_get_error_info(mci, &info);
-}
+       /* Clear global error registers */
+       pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
+                             FERR_GLOBAL_HI, &value);
+       pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
+                             FERR_GLOBAL_HI, value);
 
-/*
- *     i7300_check_error       Retrieve and process errors reported by the
- *                             hardware. Called by the Core module.
- */
-static void i7300_check_error(struct mem_ctl_info *mci)
-{
-       struct i7300_error_info info;
-       debugf4("MC%d: " __FILE__ ": %s()\n", mci->mc_idx, __func__);
+       pci_read_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
+                             FERR_GLOBAL_LO, &value);
+       pci_write_config_dword(pvt->pci_dev_16_2_fsb_err_regs,
+                             FERR_GLOBAL_LO, value);
+
+       /* Clear FBD error registers */
+       pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
+                             FERR_FAT_FBD, &value);
+       pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
+                             FERR_FAT_FBD, value);
 
-       i7300_get_error_info(mci, &info);
-       i7300_process_error_info(mci, &info);
+       pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
+                             FERR_NF_FBD, &value);
+       pci_write_config_dword(pvt->pci_dev_16_1_fsb_addr_map,
+                             FERR_NF_FBD, value);
 }
 
-/*
- *     i7300_enable_error_reporting
- *                     Turn on the memory reporting features of the hardware
+/**
+ * i7300_enable_error_reporting() - Enable the memory reporting logic at the
+ *                                 hardware
+ * @mci: struct mem_ctl_info pointer
  */
 static void i7300_enable_error_reporting(struct mem_ctl_info *mci)
 {
@@ -512,15 +604,20 @@ static void i7300_enable_error_reporting(struct mem_ctl_info *mci)
  * i7300 Functions related to memory enumberation
  ************************************************/
 
-/*
- * determine_mtr(pvt, csrow, channel)
- *
- * return the proper MTR register as determine by the csrow and desired channel
+/**
+ * decode_mtr() - Decodes the MTR descriptor, filling the edac structs
+ * @pvt: pointer to the private data struct used by i7300 driver
+ * @slot: DIMM slot (0 to 7)
+ * @ch: Channel number within the branch (0 or 1)
+ * @branch: Branch number (0 or 1)
+ * @dinfo: Pointer to DIMM info where dimm size is stored
+ * @p_csrow: Pointer to the struct csrow_info that corresponds to that element
  */
 static int decode_mtr(struct i7300_pvt *pvt,
                      int slot, int ch, int branch,
                      struct i7300_dimm_info *dinfo,
-                     struct csrow_info *p_csrow)
+                     struct csrow_info *p_csrow,
+                     u32 *nr_pages)
 {
        int mtr, ans, addrBits, channel;
 
@@ -534,14 +631,8 @@ static int decode_mtr(struct i7300_pvt *pvt,
                ans ? "Present" : "NOT Present");
 
        /* Determine if there is a DIMM present in this DIMM slot */
-
-#if 0
-       if (!amb_present || !ans)
-               return 0;
-#else
        if (!ans)
                return 0;
-#endif
 
        /* Start with the number of bits for a Bank
        * on the DRAM */
@@ -558,6 +649,7 @@ static int decode_mtr(struct i7300_pvt *pvt,
        addrBits -= 3;  /* 8 bits per bytes */
 
        dinfo->megabytes = 1 << addrBits;
+       *nr_pages = dinfo->megabytes << 8;
 
        debugf2("\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
 
@@ -571,8 +663,9 @@ static int decode_mtr(struct i7300_pvt *pvt,
        debugf2("\t\tSIZE: %d MB\n", dinfo->megabytes);
 
        p_csrow->grain = 8;
-       p_csrow->nr_pages = dinfo->megabytes << 8;
        p_csrow->mtype = MEM_FB_DDR2;
+       p_csrow->csrow_idx = slot;
+       p_csrow->page_mask = 0;
 
        /*
         * The type of error detection actually depends of the
@@ -585,18 +678,18 @@ static int decode_mtr(struct i7300_pvt *pvt,
 
        if (IS_SINGLE_MODE(pvt->mc_settings_a)) {
                p_csrow->edac_mode = EDAC_SECDED;
-               debugf0("ECC code is 8-byte-over-32-byte SECDED+ code\n");
+               debugf2("\t\tECC code is 8-byte-over-32-byte SECDED+ code\n");
        } else {
-               debugf0("ECC code is on Lockstep mode\n");
-               if (MTR_DRAM_WIDTH(mtr))
+               debugf2("\t\tECC code is on Lockstep mode\n");
+               if (MTR_DRAM_WIDTH(mtr) == 8)
                        p_csrow->edac_mode = EDAC_S8ECD8ED;
                else
                        p_csrow->edac_mode = EDAC_S4ECD4ED;
        }
 
        /* ask what device type on this row */
-       if (MTR_DRAM_WIDTH(mtr)) {
-               debugf0("Scrub algorithm for x8 is on %s mode\n",
+       if (MTR_DRAM_WIDTH(mtr) == 8) {
+               debugf2("\t\tScrub algorithm for x8 is on %s mode\n",
                        IS_SCRBALGO_ENHANCED(pvt->mc_settings) ?
                                            "enhanced" : "normal");
 
@@ -607,26 +700,22 @@ static int decode_mtr(struct i7300_pvt *pvt,
        return mtr;
 }
 
-/*
- *     print_dimm_size
+/**
+ * print_dimm_size() - Prints dump of the memory organization
+ * @pvt: pointer to the private data struct used by i7300 driver
  *
- *     also will output a DIMM matrix map, if debug is enabled, for viewing
- *     how the DIMMs are populated
+ * Useful for debug. If debug is disabled, this routine do nothing
  */
 static void print_dimm_size(struct i7300_pvt *pvt)
 {
+#ifdef CONFIG_EDAC_DEBUG
        struct i7300_dimm_info *dinfo;
-       char *p, *mem_buffer;
+       char *p;
        int space, n;
        int channel, slot;
 
        space = PAGE_SIZE;
-       mem_buffer = p = kmalloc(space, GFP_KERNEL);
-       if (p == NULL) {
-               i7300_printk(KERN_ERR, "MC: %s:%s() kmalloc() failed\n",
-                       __FILE__, __func__);
-               return;
-       }
+       p = pvt->tmp_prt_buffer;
 
        n = snprintf(p, space, "              ");
        p += n;
@@ -636,15 +725,15 @@ static void print_dimm_size(struct i7300_pvt *pvt)
                p += n;
                space -= n;
        }
-       debugf2("%s\n", mem_buffer);
-       p = mem_buffer;
+       debugf2("%s\n", pvt->tmp_prt_buffer);
+       p = pvt->tmp_prt_buffer;
        space = PAGE_SIZE;
        n = snprintf(p, space, "-------------------------------"
-                              "------------------------------");
+                              "------------------------------");
        p += n;
        space -= n;
-       debugf2("%s\n", mem_buffer);
-       p = mem_buffer;
+       debugf2("%s\n", pvt->tmp_prt_buffer);
+       p = pvt->tmp_prt_buffer;
        space = PAGE_SIZE;
 
        for (slot = 0; slot < MAX_SLOTS; slot++) {
@@ -659,57 +748,54 @@ static void print_dimm_size(struct i7300_pvt *pvt)
                        space -= n;
                }
 
-               debugf2("%s\n", mem_buffer);
-               p = mem_buffer;
+               debugf2("%s\n", pvt->tmp_prt_buffer);
+               p = pvt->tmp_prt_buffer;
                space = PAGE_SIZE;
        }
 
        n = snprintf(p, space, "-------------------------------"
-                              "------------------------------");
+                              "------------------------------");
        p += n;
        space -= n;
-       debugf2("%s\n", mem_buffer);
-       p = mem_buffer;
+       debugf2("%s\n", pvt->tmp_prt_buffer);
+       p = pvt->tmp_prt_buffer;
        space = PAGE_SIZE;
-
-       kfree(mem_buffer);
+#endif
 }
 
-/*
- *     i7300_init_csrows       Initialize the 'csrows' table within
- *                             the mci control structure with the
- *                             addressing of memory.
- *
- *     return:
- *             0       success
- *             1       no actual memory found on this MC
+/**
+ * i7300_init_csrows() - Initialize the 'csrows' table within
+ *                      the mci control structure with the
+ *                      addressing of memory.
+ * @mci: struct mem_ctl_info pointer
  */
 static int i7300_init_csrows(struct mem_ctl_info *mci)
 {
        struct i7300_pvt *pvt;
        struct i7300_dimm_info *dinfo;
        struct csrow_info *p_csrow;
-       int empty;
+       int rc = -ENODEV;
        int mtr;
        int ch, branch, slot, channel;
+       u32 last_page = 0, nr_pages;
 
        pvt = mci->pvt_info;
 
-       empty = 1;              /* Assume NO memory */
-
        debugf2("Memory Technology Registers:\n");
 
        /* Get the AMB present registers for the four channels */
        for (branch = 0; branch < MAX_BRANCHES; branch++) {
                /* Read and dump branch 0's MTRs */
                channel = to_channel(0, branch);
-               pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch], AMBPRESENT_0,
+               pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch],
+                                    AMBPRESENT_0,
                                &pvt->ambpresent[channel]);
                debugf2("\t\tAMB-present CH%d = 0x%x:\n",
                        channel, pvt->ambpresent[channel]);
 
                channel = to_channel(1, branch);
-               pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch], AMBPRESENT_1,
+               pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch],
+                                    AMBPRESENT_1,
                                &pvt->ambpresent[channel]);
                debugf2("\t\tAMB-present CH%d = 0x%x:\n",
                        channel, pvt->ambpresent[channel]);
@@ -729,41 +815,46 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
                                p_csrow = &mci->csrows[slot];
 
                                mtr = decode_mtr(pvt, slot, ch, branch,
-                                                       dinfo, p_csrow);
+                                                dinfo, p_csrow, &nr_pages);
                                /* if no DIMMS on this row, continue */
                                if (!MTR_DIMMS_PRESENT(mtr))
                                        continue;
 
-                               p_csrow->csrow_idx = slot;
-
-                               /* FAKE OUT VALUES, FIXME */
-                               p_csrow->first_page = 0 + slot * 20;
-                               p_csrow->last_page = 9 + slot * 20;
-                               p_csrow->page_mask = 0xfff;
+                               /* Update per_csrow memory count */
+                               p_csrow->nr_pages += nr_pages;
+                               p_csrow->first_page = last_page;
+                               last_page += nr_pages;
+                               p_csrow->last_page = last_page;
 
-                               empty = 0;
+                               rc = 0;
                        }
                }
        }
 
-       return empty;
+       return rc;
 }
 
+/**
+ * decode_mir() - Decodes Memory Interleave Register (MIR) info
+ * @int mir_no: number of the MIR register to decode
+ * @mir: array with the MIR data cached on the driver
+ */
 static void decode_mir(int mir_no, u16 mir[MAX_MIR])
 {
        if (mir[mir_no] & 3)
-               debugf2("MIR%d: limit= 0x%x Branch(es) that participate: %s %s\n",
+               debugf2("MIR%d: limit= 0x%x Branch(es) that participate:"
+                       " %s %s\n",
                        mir_no,
                        (mir[mir_no] >> 4) & 0xfff,
                        (mir[mir_no] & 1) ? "B0" : "",
-                       (mir[mir_no] & 2) ? "B1": "");
+                       (mir[mir_no] & 2) ? "B1" : "");
 }
 
-/*
- *     i7300_get_mc_regs       read in the necessary registers and
- *                             cache locally
+/**
+ * i7300_get_mc_regs() - Get the contents of the MC enumeration registers
+ * @mci: struct mem_ctl_info pointer
  *
- *                     Fills in the private data members
+ * Data read is cached internally for its usage when needed
  */
 static int i7300_get_mc_regs(struct mem_ctl_info *mci)
 {
@@ -806,9 +897,12 @@ static int i7300_get_mc_regs(struct mem_ctl_info *mci)
                IS_RETRY_ENABLED(pvt->mc_settings) ? "enabled" : "disabled");
 
        /* Get Memory Interleave Range registers */
-       pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR0, &pvt->mir[0]);
-       pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR1, &pvt->mir[1]);
-       pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR2, &pvt->mir[2]);
+       pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR0,
+                            &pvt->mir[0]);
+       pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR1,
+                            &pvt->mir[1]);
+       pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR2,
+                            &pvt->mir[2]);
 
        /* Decode the MIR regs */
        for (i = 0; i < MAX_MIR; i++)
@@ -829,9 +923,9 @@ static int i7300_get_mc_regs(struct mem_ctl_info *mci)
  * i7300 Functions related to device probe/release
  *************************************************/
 
-/*
- *     i7300_put_devices       'put' all the devices that we have
- *                             reserved via 'get'
+/**
+ * i7300_put_devices() - Release the PCI devices
+ * @mci: struct mem_ctl_info pointer
  */
 static void i7300_put_devices(struct mem_ctl_info *mci)
 {
@@ -847,13 +941,18 @@ static void i7300_put_devices(struct mem_ctl_info *mci)
        pci_dev_put(pvt->pci_dev_16_1_fsb_addr_map);
 }
 
-/*
- *     i7300_get_devices       Find and perform 'get' operation on the MCH's
- *                     device/functions we want to reference for this driver
+/**
+ * i7300_get_devices() - Find and perform 'get' operation on the MCH's
+ *                      device/functions we want to reference for this driver
+ * @mci: struct mem_ctl_info pointer
  *
- *                     Need to 'get' device 16 func 1 and func 2
+ * Access and prepare the several devices for usage:
+ * I7300 devices used by this driver:
+ *    Device 16, functions 0,1 and 2:  PCI_DEVICE_ID_INTEL_I7300_MCH_ERR
+ *    Device 21 function 0:            PCI_DEVICE_ID_INTEL_I7300_MCH_FB0
+ *    Device 22 function 0:            PCI_DEVICE_ID_INTEL_I7300_MCH_FB1
  */
-static int i7300_get_devices(struct mem_ctl_info *mci, int dev_idx)
+static int __devinit i7300_get_devices(struct mem_ctl_info *mci)
 {
        struct i7300_pvt *pvt;
        struct pci_dev *pdev;
@@ -862,7 +961,8 @@ static int i7300_get_devices(struct mem_ctl_info *mci, int dev_idx)
 
        /* Attempt to 'get' the MCH register we want */
        pdev = NULL;
-       while (!pvt->pci_dev_16_1_fsb_addr_map || !pvt->pci_dev_16_2_fsb_err_regs) {
+       while (!pvt->pci_dev_16_1_fsb_addr_map ||
+              !pvt->pci_dev_16_2_fsb_err_regs) {
                pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
                                      PCI_DEVICE_ID_INTEL_I7300_MCH_ERR, pdev);
                if (!pdev) {
@@ -890,16 +990,19 @@ static int i7300_get_devices(struct mem_ctl_info *mci, int dev_idx)
 
        debugf1("System Address, processor bus- PCI Bus ID: %s  %x:%x\n",
                pci_name(pvt->pci_dev_16_0_fsb_ctlr),
-               pvt->pci_dev_16_0_fsb_ctlr->vendor, pvt->pci_dev_16_0_fsb_ctlr->device);
+               pvt->pci_dev_16_0_fsb_ctlr->vendor,
+               pvt->pci_dev_16_0_fsb_ctlr->device);
        debugf1("Branchmap, control and errors - PCI Bus ID: %s  %x:%x\n",
                pci_name(pvt->pci_dev_16_1_fsb_addr_map),
-               pvt->pci_dev_16_1_fsb_addr_map->vendor, pvt->pci_dev_16_1_fsb_addr_map->device);
+               pvt->pci_dev_16_1_fsb_addr_map->vendor,
+               pvt->pci_dev_16_1_fsb_addr_map->device);
        debugf1("FSB Error Regs - PCI Bus ID: %s  %x:%x\n",
                pci_name(pvt->pci_dev_16_2_fsb_err_regs),
-               pvt->pci_dev_16_2_fsb_err_regs->vendor, pvt->pci_dev_16_2_fsb_err_regs->device);
+               pvt->pci_dev_16_2_fsb_err_regs->vendor,
+               pvt->pci_dev_16_2_fsb_err_regs->device);
 
        pvt->pci_dev_2x_0_fbd_branch[0] = pci_get_device(PCI_VENDOR_ID_INTEL,
-                                           PCI_DEVICE_ID_INTEL_I7300_MCH_FB0,
+                                           PCI_DEVICE_ID_INTEL_I7300_MCH_FB0,
                                            NULL);
        if (!pvt->pci_dev_2x_0_fbd_branch[0]) {
                i7300_printk(KERN_ERR,
@@ -929,23 +1032,25 @@ error:
        return -ENODEV;
 }
 
-/*
- *     i7300_probe1    Probe for ONE instance of device to see if it is
- *                     present.
- *     return:
- *             0 for FOUND a device
- *             < 0 for error code
+/**
+ * i7300_init_one() - Probe for one instance of the device
+ * @pdev: struct pci_dev pointer
+ * @id: struct pci_device_id pointer - currently unused
  */
-static int i7300_probe1(struct pci_dev *pdev, int dev_idx)
+static int __devinit i7300_init_one(struct pci_dev *pdev,
+                                   const struct pci_device_id *id)
 {
        struct mem_ctl_info *mci;
        struct i7300_pvt *pvt;
        int num_channels;
        int num_dimms_per_channel;
        int num_csrows;
+       int rc;
 
-       if (dev_idx >= ARRAY_SIZE(i7300_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__,
@@ -983,8 +1088,14 @@ static int i7300_probe1(struct pci_dev *pdev, int dev_idx)
        pvt = mci->pvt_info;
        pvt->pci_dev_16_0_fsb_ctlr = pdev;      /* Record this device in our private */
 
+       pvt->tmp_prt_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!pvt->tmp_prt_buffer) {
+               edac_mc_free(mci);
+               return -ENOMEM;
+       }
+
        /* 'get' the pci devices we want to reserve for our use */
-       if (i7300_get_devices(mci, dev_idx))
+       if (i7300_get_devices(mci))
                goto fail0;
 
        mci->mc_idx = 0;
@@ -993,7 +1104,7 @@ static int i7300_probe1(struct pci_dev *pdev, int dev_idx)
        mci->edac_cap = EDAC_FLAG_NONE;
        mci->mod_name = "i7300_edac.c";
        mci->mod_ver = I7300_REVISION;
-       mci->ctl_name = i7300_devs[dev_idx].ctl_name;
+       mci->ctl_name = i7300_devs[0].ctl_name;
        mci->dev_name = pci_name(pdev);
        mci->ctl_page_to_phys = NULL;
 
@@ -1043,40 +1154,19 @@ fail1:
        i7300_put_devices(mci);
 
 fail0:
+       kfree(pvt->tmp_prt_buffer);
        edac_mc_free(mci);
        return -ENODEV;
 }
 
-/*
- *     i7300_init_one  constructor for one instance of device
- *
- *     returns:
- *             negative on error
- *             count (>= 0)
- */
-static int __devinit i7300_init_one(struct pci_dev *pdev,
-                               const struct pci_device_id *id)
-{
-       int rc;
-
-       debugf0("MC: " __FILE__ ": %s()\n", __func__);
-
-       /* wake up device */
-       rc = pci_enable_device(pdev);
-       if (rc == -EIO)
-               return rc;
-
-       /* now probe and enable the device */
-       return i7300_probe1(pdev, id->driver_data);
-}
-
-/*
- *     i7300_remove_one        destructor for one instance of device
- *
+/**
+ * i7300_remove_one() - Remove the driver
+ * @pdev: struct pci_dev pointer
  */
 static void __devexit i7300_remove_one(struct pci_dev *pdev)
 {
        struct mem_ctl_info *mci;
+       char *tmp;
 
        debugf0(__FILE__ ": %s()\n", __func__);
 
@@ -1087,16 +1177,19 @@ static void __devexit i7300_remove_one(struct pci_dev *pdev)
        if (!mci)
                return;
 
+       tmp = ((struct i7300_pvt *)mci->pvt_info)->tmp_prt_buffer;
+
        /* retrieve references to resources, and free those resources */
        i7300_put_devices(mci);
 
+       kfree(tmp);
        edac_mc_free(mci);
 }
 
 /*
- *     pci_device_id   table for which devices we are looking for
+ * pci_device_id: table for which devices we are looking for
  *
- *     The "E500P" device is the first device supported.
+ * Has only 8086:360c PCI ID
  */
 static const struct pci_device_id i7300_pci_tbl[] __devinitdata = {
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7300_MCH_ERR)},
@@ -1106,8 +1199,7 @@ static const struct pci_device_id i7300_pci_tbl[] __devinitdata = {
 MODULE_DEVICE_TABLE(pci, i7300_pci_tbl);
 
 /*
- *     i7300_driver    pci_driver structure for this module
- *
+ * i7300_driver: pci_driver structure for this module
  */
 static struct pci_driver i7300_driver = {
        .name = "i7300_edac",
@@ -1116,9 +1208,8 @@ static struct pci_driver i7300_driver = {
        .id_table = i7300_pci_tbl,
 };
 
-/*
- *     i7300_init              Module entry function
- *                     Try to initialize this module for its devices
+/**
+ * i7300_init() - Registers the driver
  */
 static int __init i7300_init(void)
 {
@@ -1134,9 +1225,8 @@ static int __init i7300_init(void)
        return (pci_rc < 0) ? pci_rc : 0;
 }
 
-/*
- *     i7300_exit()    Module exit function
- *                     Unregister the driver
+/**
+ * i7300_init() - Unregisters the driver
  */
 static void __exit i7300_exit(void)
 {