From: Marek Majtyka <marekx.majt...@intel.com>

Implemented CMEM EDAC support on rte config load/unload.
 - fixed bugs found CMEM/SMEM:
        - work queue hang on driver removal
        - unnecessary edac sysfs entries (valid only for ddr3)

Signed-off-by: Marek Majtyka <marekx.majt...@intel.com>
---
 drivers/edac/axxia_edac-cmc_56xx.c | 671 +++++++++++++++++++++++++++++--------
 drivers/edac/axxia_edac-mc_56xx.c  |  45 ++-
 2 files changed, 560 insertions(+), 156 deletions(-)

diff --git a/drivers/edac/axxia_edac-cmc_56xx.c 
b/drivers/edac/axxia_edac-cmc_56xx.c
index d72e618..1f5d3aa 100644
--- a/drivers/edac/axxia_edac-cmc_56xx.c
+++ b/drivers/edac/axxia_edac-cmc_56xx.c
@@ -25,6 +25,9 @@
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
 #include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
 #include "edac_core.h"
 #include "edac_module.h"
 
@@ -78,12 +81,17 @@
 #define INT_BIT_5  (0x00000020)
 #define INT_BIT_6  (0x00000040)
 #define INT_BIT_7  (0x00000080)
+#define INT_BIT_8  (0x00000100)
 #define INT_BIT_11 (0x00000800)
 #define INT_BIT_21 (0x00200000)
 #define INT_BIT_25 (0x02000000)
 #define INT_BIT_30 (0x40000000)
 #define INT_BIT_31 (0x80000000)
 
+#define CM_INT_MASK_BASE_PROBE (~(\
+                       INT_BIT_8 |\
+                       INT_BIT_31))
+
 #define CM_INT_MASK_BASE (~(\
                        INT_BIT_1 |\
                        INT_BIT_2 |\
@@ -110,7 +118,7 @@
                        INT_BIT_30 |\
                        INT_BIT_31))
 
-#define CM_INT_MASK_ALL (0xffffffff)
+#define CM_INT_MASK_ALL (0x7fffffff)
 #define ALIVE_NOTIFICATION_PERIOD (90*1000)
 
 static int log = 1;
@@ -337,6 +345,15 @@ struct __packed mpr_dump {
        u8      cs;
 };
 
+enum init_return_codes {ERR_STAGE_8 = -8,
+                       ERR_STAGE_7 = -7,
+                       ERR_STAGE_6 = -6,
+                       ERR_STAGE_5 = -5,
+                       ERR_STAGE_4 = -4,
+                       ERR_STAGE_3 = -3,
+                       ERR_STAGE_2 = -2,
+                       ERR_STAGE_1 = -1
+};
 enum events {
        EV_ILLEGAL = 0,
        EV_MULT_ILLEGAL,
@@ -437,11 +454,19 @@ struct intel_edac_dev_info {
        struct mc_edac_data *data;
        char *ctl_name;
        char *blk_name;
+       char *proc_name;
+#ifdef CONFIG_DEBUG_CMEM
+       struct proc_dir_entry *dir_entry;
+#endif
+       struct mutex state_machine_lock;
        struct work_struct offload_alerts;
        struct work_struct offload_events;
+       int finish_alerts;
+       int finish_events;
        struct workqueue_struct *wq_alerts;
        struct workqueue_struct *wq_events;
        int is_ddr4;
+       int is_controller_configured;
        int edac_idx;
        u32 cm_region;
        struct regmap *syscon;
@@ -450,7 +475,7 @@ struct intel_edac_dev_info {
        void (*check)(struct edac_device_ctl_info *edac_dev);
 };
 
-
+#ifdef CONFIG_DEBUG_CMEM
 static ssize_t mpr1_dump_show(struct edac_device_ctl_info
                                 *edac_dev, char *data)
 {
@@ -536,6 +561,8 @@ no_mem_buffer:
        return 0;
 }
 
+
+
 static struct edac_dev_sysfs_attribute device_block_attr[] = {
        {
                .attr = {
@@ -543,7 +570,8 @@ static struct edac_dev_sysfs_attribute device_block_attr[] 
= {
                        .mode = (S_IRUGO | S_IWUSR)
                },
                .show = mpr1_dump_show,
-               .store = NULL},
+               .store = NULL
+       },
        /* End of list */
        {
                .attr = {.name = NULL}
@@ -554,6 +582,7 @@ static void axxia_mc_sysfs_attributes(struct 
edac_device_ctl_info *edac_dev)
 {
                edac_dev->sysfs_attributes = &device_block_attr[0];
 }
+#endif
 
 static inline void __attribute__((always_inline))
 handle_events(struct intel_edac_dev_info *edac_dev,
@@ -686,12 +715,19 @@ cmmon_isr_hw(int interrupt, void *device)
        return IRQ_WAKE_THREAD;
 }
 
+static int initialize(struct intel_edac_dev_info *dev_info);
+static int enable_workers(struct intel_edac_dev_info *dev_info);
+static void uninitialize(struct intel_edac_dev_info *dev_info,
+                       int ret, int only_disable);
+
 static irqreturn_t
 cmmon_isr_sw(int interrupt, void *device)
 {
        struct intel_edac_dev_info *dev_info = device;
        struct cm_56xx_denali_ctl_84 denali_ctl_84;
        struct cm_56xx_denali_ctl_85 denali_ctl_85 = {0};
+       struct cm_56xx_denali_ctl_86 denali_ctl_86;
+       int ret = 0;
 
        /*
         * NOTE:
@@ -710,12 +746,47 @@ cmmon_isr_sw(int interrupt, void *device)
                4, (u32 *) &denali_ctl_84))
                goto error_read;
 
+       if (denali_ctl_84.int_status & INT_BIT_8) {
+               if (dev_info->is_controller_configured == 0) {
+                       ret = initialize(dev_info);
+                       if (ret)
+                               goto error_init;
+
+                       ret = enable_workers(dev_info);
+                       if (ret)
+                               goto error_init;
+
+                       dev_info->is_controller_configured = 1;
+               }
+
+               if (dev_info->is_ddr4)
+                       denali_ctl_86.int_mask = CM_INT_MASK_FULL;
+               else
+                       denali_ctl_86.int_mask = CM_INT_MASK_BASE;
+
+               if (ncr_write(dev_info->cm_region,
+                                       CM_56XX_DENALI_CTL_86,
+                                       4, (u32 *) &denali_ctl_86)) {
+                       goto error_write;
+               }
+               return IRQ_HANDLED;
+       }
+
+       /*
+        * SAFETY CHECK
+        * one cannot go further if driver is not fully functional!!!
+        */
+       if (dev_info->is_controller_configured == 0)
+               return IRQ_HANDLED;
+
+
        handle_events(dev_info, &denali_ctl_84);
        atomic_set(&dev_info->data->event_ready, 1);
        wake_up(&dev_info->data->event_wq);
 
        denali_ctl_85.int_ack =
-               (denali_ctl_84.int_status & (~(INT_BIT_25 | INT_BIT_31)));
+               (denali_ctl_84.int_status &
+                  (~(INT_BIT_25 | INT_BIT_31 | INT_BIT_8)));
 
        if (dev_info->is_ddr4) {
                if (denali_ctl_84.int_status & INT_BIT_25) {
@@ -745,6 +816,12 @@ error_read:
        printk_ratelimited("%s: Error reading interrupt status\n",
                       dev_name(&dev_info->pdev->dev));
        return IRQ_HANDLED;
+error_init:
+       printk_ratelimited("%s: Error during driver initialization\n",
+                      dev_name(&dev_info->pdev->dev));
+       uninitialize(dev_info, ret,
+                       0 == dev_info->is_controller_configured ? 1 : 0);
+       return IRQ_HANDLED;
 }
 
 
@@ -755,7 +832,7 @@ static void intel_cm_alerts_error_check(struct 
edac_device_ctl_info *edac_dev)
        struct event_counter (*alerts)[MAX_DQ][MPR_ERRORS] =
                        dev_info->data->alerts;
        struct cm_56xx_denali_ctl_34 denali_ctl_34;
-       int i, j, k, l;
+       int i, j, k, l, ret;
        u32 counter;
 
 start:
@@ -765,6 +842,9 @@ start:
                msecs_to_jiffies(ALIVE_NOTIFICATION_PERIOD)))
                goto start;
 
+       if (dev_info->finish_alerts)
+               goto finish;
+
        for (i = 0; i < dev_info->data->cs_count; ++i) {
 
                /* trigger dump */
@@ -787,9 +867,15 @@ start:
                        CM_56XX_DENALI_CTL_34,
                        4, (u32 *) &denali_ctl_34))
                        goto error_write;
+
                /* wait */
-               wait_event(dev_info->data->dump_wq,
-                          atomic_read(&dev_info->data->dump_ready));
+               ret = wait_event_timeout(dev_info->data->dump_wq,
+                          atomic_read(&dev_info->data->dump_ready),
+                          msecs_to_jiffies(1000));
+               if (dev_info->finish_alerts)
+                       goto finish;
+               if (0 == ret)
+                       goto timeout_error;
 
                atomic_set(&dev_info->data->dump_ready, 0);
                /* collect data */
@@ -819,11 +905,21 @@ start:
        atomic_set(&dev_info->data->dump_in_progress, 0);
        goto start;
 
+timeout_error:
+       printk_ratelimited("Timeout occurred during MPR dump.\n");
+       atomic_set(&dev_info->data->dump_ready, 0);
+       atomic_set(&dev_info->data->dump_in_progress, 0);
+       goto start;
+
 error_read:
 error_write:
        printk_ratelimited("Could not collect MPR dump.\n");
        atomic_set(&dev_info->data->dump_in_progress, 0);
        goto start;
+
+finish:
+       atomic_set(&dev_info->data->dump_ready, 0);
+       atomic_set(&dev_info->data->dump_in_progress, 0);
 }
 
 static void intel_cm_events_error_check(struct edac_device_ctl_info *edac_dev)
@@ -842,6 +938,9 @@ static void intel_cm_events_error_check(struct 
edac_device_ctl_info *edac_dev)
 
                atomic_set(&dev_info->data->event_ready, 0);
 
+               if (dev_info->finish_events)
+                       break;
+
                mutex_lock(&dev_info->data->edac_sysfs_data_lock);
                for (i = 0; i < NR_EVENTS; ++i) {
                        counter = atomic_xchg(&events[i].counter, 0);
@@ -919,6 +1018,7 @@ static int get_active_dram(struct intel_edac_dev_info 
*dev_info)
        if (ncr_read(dev_info->cm_region, CM_56XX_DENALI_CTL_74,
                4, (u32 *) &denali_ctl_74)) {
                pr_err("Could not read number of lanes.\n");
+               return dram;
        }
 
        if (0 == denali_ctl_74.bank_diff)
@@ -930,122 +1030,121 @@ static int get_active_dram(struct intel_edac_dev_info 
*dev_info)
        return dram;
 }
 
-static int intel_edac_mc_probe(struct platform_device *pdev)
+static int get_ddr4(struct intel_edac_dev_info *dev_info)
 {
-       struct edac_device_instance *instance;
-       struct edac_device_block *block;
-       int i, j, k, l;
-       int count;
-       struct intel_edac_dev_info *dev_info = NULL;
-       struct resource *io;
-       struct device_node *np = pdev->dev.of_node;
-       int irq = -1, rc = 0;
        struct cm_56xx_denali_ctl_00 denali_ctl_00;
-       struct cm_56xx_denali_ctl_86 denali_ctl_86;
-       int cs_count = MAX_CS;
-       int dram_count = MAX_DQ;
-
-       count = atomic64_inc_return(&mc_counter);
-       if ((count - 1) == MEMORY_CONTROLLERS)
-               goto err_nodev;
-
-       dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
-       if (!dev_info)
-               goto err_nomem;
-
-       dev_info->ctl_name =
-               devm_kzalloc(&pdev->dev, 32*sizeof(char), GFP_KERNEL);
-       if (!dev_info->ctl_name)
-               goto err_nomem;
-
-       dev_info->blk_name =
-               devm_kzalloc(&pdev->dev, 32*sizeof(char), GFP_KERNEL);
-       if (!dev_info->blk_name)
-               goto err_nomem;
 
-       dev_info->data =
-               devm_kzalloc(&pdev->dev, sizeof(*dev_info->data), GFP_KERNEL);
-       if (!dev_info->data)
-               goto err_noctlinfo;
-
-       init_waitqueue_head(&dev_info->data->dump_wq);
-       init_waitqueue_head(&dev_info->data->event_wq);
-
-       raw_spin_lock_init(&dev_info->data->mpr_data_lock);
-       mutex_init(&dev_info->data->edac_sysfs_data_lock);
-
-       strncpy(dev_info->ctl_name, np->name, 32);
-       dev_info->ctl_name[31] = '\0';
-
-       strncpy(dev_info->blk_name, "ECC", 32);
-       dev_info->ctl_name[31] = '\0';
+       if (ncr_read(dev_info->cm_region, CM_56XX_DENALI_CTL_00,
+               4, (u32 *) &denali_ctl_00)) {
+               pr_err("Could not read ddr version.\n");
+               return -1;
+       }
 
-       edac_op_state = EDAC_OPSTATE_POLL;
+       if (denali_ctl_00.dram_class == 0xa)
+               return 1;
 
-       dev_info->pdev = pdev;
-       dev_info->edac_idx = edac_device_alloc_index();
-       dev_info->data->irq = 0;
+       return 0;
+}
 
-       /* setup all counters */
-       for (i = 0; i < NR_EVENTS; ++i)
-               atomic_set(&dev_info->data->events[i].counter, 0);
+static void uninitialize(struct intel_edac_dev_info *dev_info,
+                       int ret, int only_disable)
+{
+       struct cm_56xx_denali_ctl_86 denali_ctl_86;
 
-       for (j = 0; j < MAX_CS; ++j) {
-               for (l = 0; l < MAX_DQ; ++l) {
-                       for (k = 0; k < MPR_ERRORS; ++k, ++i) {
-                               atomic_set(&dev_info->data->
-                                               alerts[j][l][k].counter, 0);
+       switch (ret) {
+       case ERR_STAGE_8:
+               if (dev_info->data->irq) {
+                       disable_irq(dev_info->data->irq);
+                       devm_free_irq(&dev_info->pdev->dev,
+                                       dev_info->data->irq, dev_info);
+                       dev_info->data->irq = 0;
+               }
+               /* fall-through */
+       case ERR_STAGE_7:
+               denali_ctl_86.int_mask = CM_INT_MASK_ALL;
+               if (ncr_write(dev_info->cm_region,
+                                       CM_56XX_DENALI_CTL_86,
+                                       4, (u32 *) &denali_ctl_86)) {
+                       pr_err("Could not mask interrupts (%s - ctl_86).\n",
+                               dev_info->ctl_name);
+               }
+               if (only_disable)
+                       break;
+               /* fall-through */
+       case ERR_STAGE_6:
+               if (dev_info->is_ddr4) {
+                       dev_info->finish_alerts = 1;
+                       atomic_inc(&dev_info->data->dump_in_progress);
+                       atomic_set(&dev_info->data->dump_ready, 1);
+                       wake_up(&dev_info->data->dump_wq);
+                       cancel_work_sync(&dev_info->offload_alerts);
+               }
+               dev_info->finish_events = 1;
+               atomic_set(&dev_info->data->event_ready, 1);
+               wake_up(&dev_info->data->event_wq);
+               cancel_work_sync(&dev_info->offload_events);
+               /* fall-through */
+       case ERR_STAGE_5:
+               if (dev_info->is_ddr4)
+                       if (dev_info->wq_alerts) {
+                               destroy_workqueue(dev_info->wq_alerts);
+                               dev_info->wq_alerts = NULL;
                        }
+               /* fall-through */
+       case ERR_STAGE_4:
+               if (dev_info->wq_events) {
+                       destroy_workqueue(dev_info->wq_events);
+                       dev_info->wq_events = NULL;
+
+               }
+               /* fall-through */
+       case ERR_STAGE_3:
+               edac_device_del_device(&dev_info->pdev->dev);
+               /* fall-through */
+       case ERR_STAGE_2:
+               if (dev_info->edac_dev) {
+                       edac_device_free_ctl_info(dev_info->edac_dev);
+                       dev_info->edac_dev = NULL;
                }
+               /* fall-through */
+       case ERR_STAGE_1:
+               /* fall-through */
+       default:
+               break;
        }
+}
 
-       /* set up dump in progress flag */
-       atomic_set(&dev_info->data->dump_in_progress, 0);
+static int initialize(struct intel_edac_dev_info *dev_info)
+{
+       struct edac_device_instance *instance;
+       struct edac_device_block *block;
+       int i, j, k, l;
 
-       io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!io) {
-               dev_err(&pdev->dev, "Unable to get mem resource\n");
-               goto err_noctlinfo;
-       }
-       dev_info->cm_region = io->start;
-       dev_info->syscon =
-               syscon_regmap_lookup_by_phandle(np, "syscon");
-       if (IS_ERR(dev_info->syscon)) {
-               pr_info(FMT, np->name);
-               dev_info->axi2ser3_region = ioremap(AXI2_SER3_PHY_ADDR,
-                       AXI2_SER3_PHY_SIZE);
-               if (!dev_info->axi2ser3_region) {
-                       pr_err("ioremap of axi2ser3 region failed\n");
-                       goto err_noctlinfo;
-               }
-       }
+       int cs_count = MAX_CS;
+       int dram_count = MAX_DQ;
 
-       if (ncr_read(dev_info->cm_region, CM_56XX_DENALI_CTL_00,
-               4, (u32 *) &denali_ctl_00)) {
-               pr_err("Could not read ddr version.\n");
-               goto err_noctlinfo;
+       cs_count = get_active_cs(dev_info);
+       if (cs_count == 0) {
+               pr_err("Could not get cs number. Is config loaded?\n");
+               return ERR_STAGE_1;
        }
 
-       if (0xa == denali_ctl_00.dram_class) {
-               pr_info("%s supports mpr dump (DDR4).\n", dev_info->ctl_name);
-               dev_info->is_ddr4 = 1;
-       } else {
-               if (0x6 == denali_ctl_00.dram_class) {
-                       pr_info("%s doesn't support mpr dump (DDR3).\n",
-                               dev_info->ctl_name);
-               } else {
-                       pr_err("CMEM is not configured. Check uboot 
settings.\n");
-                       goto err_noctlinfo;
-               }
+       dram_count = get_active_dram(dev_info);
+       if (dram_count == 0) {
+               pr_err("Could not get dram number. Is config loaded?\n");
+               return ERR_STAGE_1;
        }
 
-       cs_count = get_active_cs(dev_info);
-       if (cs_count == 0)
-               goto err_noctlinfo;
+       dev_info->is_ddr4 = get_ddr4(dev_info);
 
-       dram_count = get_active_dram(dev_info);
-       if (dram_count == 0)
-               goto err_noctlinfo;
+       if (dev_info->is_ddr4 == -1) {
+               pr_err("Could not get dram version. Is config loaded?\n");
+               return ERR_STAGE_1;
+       }
+       /*dev_info->is_ddr4 = 1;*/
+
+       dev_info->finish_alerts = 0;
+       dev_info->finish_events = 0;
 
        dev_info->data->cs_count = cs_count;
        dev_info->data->dram_count = dram_count;
@@ -1053,13 +1152,15 @@ static int intel_edac_mc_probe(struct platform_device 
*pdev)
        dev_info->edac_dev =
                edac_device_alloc_ctl_info(0, dev_info->ctl_name,
                                         1, dev_info->blk_name,
-                                        NR_EVENTS +
-                                        cs_count * dram_count * MPR_ERRORS,
+                                        NR_EVENTS + (dev_info->is_ddr4 ?
+                                        cs_count * dram_count * MPR_ERRORS
+                                        :
+                                        0),
                                         0, NULL, 0, dev_info->edac_idx);
 
        if (!dev_info->edac_dev) {
                pr_info("No memory for edac device\n");
-               goto err_noctlinfo;
+               return ERR_STAGE_1;
        }
 
        instance = &dev_info->edac_dev->instances[0];
@@ -1108,29 +1209,37 @@ static int intel_edac_mc_probe(struct platform_device 
*pdev)
        dev_info->edac_dev->dev_name = dev_name(&dev_info->pdev->dev);
        dev_info->edac_dev->edac_check = NULL;
 
+#ifdef CONFIG_DEBUG_CMEM
        if (dev_info->is_ddr4)
                axxia_mc_sysfs_attributes(dev_info->edac_dev);
+#endif
 
        if (edac_device_add_device(dev_info->edac_dev) != 0) {
                pr_info("Unable to add edac device for %s\n",
                        dev_info->ctl_name);
-               goto err_nosysfs;
+               return ERR_STAGE_2;
        }
 
-       snprintf(&dev_info->data->irq_name[0], IRQ_NAME_LEN,
-                       "%s-mon", dev_info->ctl_name);
+       return 0;
+}
+
+static int enable_workers(struct intel_edac_dev_info *dev_info)
+{
+       atomic_set(&dev_info->data->dump_ready, 0);
+       atomic_set(&dev_info->data->event_ready, 0);
+       atomic_set(&dev_info->data->dump_in_progress, 0);
 
        dev_info->wq_events = alloc_workqueue("%s-events", WQ_MEM_RECLAIM, 1,
                                                (dev_info->ctl_name));
        if (!dev_info->wq_events)
-               goto err_nosysfs;
+               return ERR_STAGE_3;
 
        if (dev_info->is_ddr4) {
                dev_info->wq_alerts =
                        alloc_workqueue("%s-alerts", WQ_MEM_RECLAIM, 1,
                                        (dev_info->ctl_name));
                if (!dev_info->wq_alerts)
-                       goto err_noevents;
+                       return ERR_STAGE_4;
        }
        if (dev_info->is_ddr4)
                INIT_WORK(&dev_info->offload_alerts, axxia_alerts_work);
@@ -1141,29 +1250,43 @@ static int intel_edac_mc_probe(struct platform_device 
*pdev)
                queue_work(dev_info->wq_alerts, &dev_info->offload_alerts);
        queue_work(dev_info->wq_events, &dev_info->offload_events);
 
-       irq = platform_get_irq(pdev, 0);
+       return 0;
+}
+
+static int enable_driver_irq(struct intel_edac_dev_info *dev_info)
+{
+       int irq = -1, rc = 0;
+       struct cm_56xx_denali_ctl_86 denali_ctl_86;
+
+       snprintf(&dev_info->data->irq_name[0], IRQ_NAME_LEN,
+                       "%s-mon", dev_info->ctl_name);
+
+       irq = platform_get_irq(dev_info->pdev, 0);
        if (irq < 0) {
                pr_err("Could not get irq number.\n");
-               goto err_noirq;
+               return ERR_STAGE_5;
        }
 
        /*
         * Enable memory controller interrupts.
         */
-       if (dev_info->is_ddr4)
-               denali_ctl_86.int_mask = CM_INT_MASK_FULL;
-       else
-               denali_ctl_86.int_mask = CM_INT_MASK_BASE;
+       if (dev_info->is_controller_configured) {
+               if (dev_info->is_ddr4)
+                       denali_ctl_86.int_mask = CM_INT_MASK_FULL;
+               else
+                       denali_ctl_86.int_mask = CM_INT_MASK_BASE;
+       } else
+               denali_ctl_86.int_mask = CM_INT_MASK_BASE_PROBE;
 
        if (ncr_write(dev_info->cm_region, CM_56XX_DENALI_CTL_86,
                4, (u32 *) &denali_ctl_86)) {
                pr_err("Could not write interrupt mask reg (%s - ctl_86).\n",
                        dev_info->ctl_name);
-               goto err_noirq;
+               return ERR_STAGE_6;
        }
 
        dev_info->data->irq = irq;
-       rc = devm_request_threaded_irq(&pdev->dev, irq,
+       rc = devm_request_threaded_irq(&dev_info->pdev->dev, irq,
                        cmmon_isr_hw, cmmon_isr_sw, IRQF_ONESHOT,
                        &dev_info->data->irq_name[0], dev_info);
 
@@ -1179,28 +1302,285 @@ static int intel_edac_mc_probe(struct platform_device 
*pdev)
                                        4, (u32 *) &denali_ctl_86)) {
                        pr_err("Could not mask interrupts (%s - ctl_86).\n",
                                        dev_info->ctl_name);
+                       return ERR_STAGE_7;
                }
 
-               goto err_noirq;
+               return ERR_STAGE_6;
        }
        return 0;
+}
 
-err_noirq:
-       if (dev_info->is_ddr4)
-               cancel_work_sync(&dev_info->offload_alerts);
-       cancel_work_sync(&dev_info->offload_events);
 
-       edac_device_del_device(&dev_info->pdev->dev);
-       if (dev_info->is_ddr4)
-               destroy_workqueue(dev_info->wq_alerts);
+#ifdef CONFIG_DEBUG_CMEM
+static ssize_t
+axxia_cmem_read(struct file *filp, char *buffer, size_t length, loff_t *offset)
+{
+       char *buf = NULL;
+       struct intel_edac_dev_info *dev_info =
+               (struct intel_edac_dev_info *) filp->private_data;
+       ssize_t len;
+
+       if (*offset > 0)
+               return 0;
+
+       buf = kmalloc(PAGE_SIZE, __GFP_WAIT);
+       if (NULL == buf)
+               goto no_mem_buffer;
+
+       mutex_lock(&dev_info->state_machine_lock);
+
+       /*
+        * Do not modify this. Content is used by rte driver.
+        * Once changed modify rte code.
+        */
+       len = snprintf(buf, PAGE_SIZE-1, "Node: 0x%x\n"
+               "Command available:\n"
+               "          dump - triggers mpr_page1 dump.\n",
+               (int) dev_info->cm_region >> 16);
+
+       mutex_unlock(&dev_info->state_machine_lock);
+
+       buf[len] = '\0';
+       if (copy_to_user(buffer, buf, len))
+               len = -EFAULT;
+
+       kfree(buf);
+       *offset += len;
+       return len;
+
+no_mem_buffer:
+       pr_err("Could not allocate memory for cmem edac control buffer.\n");
+       return -ENOSPC;
+}
+
+static ssize_t
+axxia_cmem_write(struct file *file, const char __user *buffer,
+                size_t count, loff_t *ppos)
+{
+       char *buf = NULL;
+       struct intel_edac_dev_info *dev_info =
+               (struct intel_edac_dev_info *) file->private_data;
+
+       buf = kmalloc(count + 1, __GFP_WAIT);
+       if (NULL == buf)
+               goto no_mem_buffer;
+
+       memset(buf, 0, count + 1);
+
+       if (copy_from_user(buf, buffer, count)) {
+               pr_err("Could not copy data from user.\n");
+               goto cfu_failed;
+       }
+
+       if (!strncmp(buf, "dump", 4)) {
+               atomic_inc(&dev_info->data->dump_in_progress);
+               wake_up(&dev_info->data->dump_wq);
+       }
+
+       kfree(buf);
+       return count;
+
+cfu_failed:
+       kfree(buf);
+       return -EFAULT;
+
+no_mem_buffer:
+       pr_err("Could not allocate memory for cmem edac control buffer.\n");
+       return -ENOSPC;
+}
+
+int axxia_cmem_open(struct inode *inode, struct file *filp)
+{
+       try_module_get(THIS_MODULE);
+       filp->private_data = PDE_DATA(inode);
+       return 0;
+}
+
+int axxia_cmem_close(struct inode *inode, struct file *filp)
+{
+       module_put(THIS_MODULE);
+       filp->private_data = 0;
+       return 0;
+}
+
+static const struct file_operations axxia_edac_cmem_proc_ops = {
+       .owner      = THIS_MODULE,
+       .open       = axxia_cmem_open,
+       .read       = axxia_cmem_read,
+       .write      = axxia_cmem_write,
+       .release    = axxia_cmem_close,
+       .llseek     = noop_llseek
+};
+
+static void remove_procfs_entry(struct intel_edac_dev_info *dev_info)
+{
+       if (dev_info && dev_info->dir_entry) {
+               proc_remove(dev_info->dir_entry);
+               dev_info->dir_entry = NULL;
+       }
+}
+#endif
+
+static int intel_edac_mc_probe(struct platform_device *pdev)
+{
+       int i, j, k, l;
+       int count;
+       struct intel_edac_dev_info *dev_info = NULL;
+       struct resource *io;
+       struct device_node *np = pdev->dev.of_node;
+       struct cm_56xx_denali_ctl_00 denali_ctl_00;
+       int ret = -1;
+
+       count = atomic64_inc_return(&mc_counter);
+       if ((count - 1) == MEMORY_CONTROLLERS)
+               goto err_nodev;
+
+       dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
+       if (!dev_info)
+               goto err_nomem;
+
+       dev_info->ctl_name =
+               devm_kzalloc(&pdev->dev, 32*sizeof(char), GFP_KERNEL);
+       if (!dev_info->ctl_name)
+               goto err_nomem;
+
+       dev_info->blk_name =
+               devm_kzalloc(&pdev->dev, 32*sizeof(char), GFP_KERNEL);
+       if (!dev_info->blk_name)
+               goto err_nomem;
+
+       dev_info->data =
+               devm_kzalloc(&pdev->dev, sizeof(*dev_info->data), GFP_KERNEL);
+       if (!dev_info->data)
+               goto err_nomem;
+
+       init_waitqueue_head(&dev_info->data->dump_wq);
+       init_waitqueue_head(&dev_info->data->event_wq);
+
+       raw_spin_lock_init(&dev_info->data->mpr_data_lock);
+       mutex_init(&dev_info->data->edac_sysfs_data_lock);
+       mutex_init(&dev_info->state_machine_lock);
+
+       strncpy(dev_info->ctl_name, np->name, 32);
+       dev_info->ctl_name[31] = '\0';
+
+       strncpy(dev_info->blk_name, "ECC", 32);
+       dev_info->blk_name[31] = '\0';
+
+       edac_op_state = EDAC_OPSTATE_POLL;
+
+       dev_info->pdev = pdev;
+       dev_info->edac_idx = edac_device_alloc_index();
+       dev_info->data->irq = 0;
+
+       /* setup all counters */
+       for (i = 0; i < NR_EVENTS; ++i)
+               atomic_set(&dev_info->data->events[i].counter, 0);
+
+       for (j = 0; j < MAX_CS; ++j) {
+               for (l = 0; l < MAX_DQ; ++l) {
+                       for (k = 0; k < MPR_ERRORS; ++k, ++i) {
+                               atomic_set(&dev_info->data->
+                                               alerts[j][l][k].counter, 0);
+                       }
+               }
+       }
+
+       io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!io) {
+               dev_err(&pdev->dev, "Unable to get mem resource\n");
+               goto err_init;
+       }
+       dev_info->cm_region = io->start;
+       dev_info->syscon =
+               syscon_regmap_lookup_by_phandle(np, "syscon");
+       if (IS_ERR(dev_info->syscon)) {
+               pr_info(FMT, np->name);
+               dev_info->axi2ser3_region = ioremap(AXI2_SER3_PHY_ADDR,
+                       AXI2_SER3_PHY_SIZE);
+               if (!dev_info->axi2ser3_region) {
+                       pr_err("ioremap of axi2ser3 region failed\n");
+                       goto err_init;
+               }
+       }
+
+       if (ncr_read(dev_info->cm_region, CM_56XX_DENALI_CTL_00,
+               4, (u32 *) &denali_ctl_00)) {
+               pr_err("Could not read ddr version.\n");
+               goto err_init;
+       }
 
-err_noevents:
-       destroy_workqueue(dev_info->wq_events);
+       if (denali_ctl_00.start == 1) {
+               /* uboot has configured CMEM */
+               if (0xa == denali_ctl_00.dram_class) {
+                       pr_info("%s supports mpr dump (DDR4).\n",
+                                       dev_info->ctl_name);
+                       dev_info->is_ddr4 = 1;
+               }
+               if (0x6 == denali_ctl_00.dram_class) {
+                       pr_info("%s doesn't support mpr dump (DDR3).\n",
+                               dev_info->ctl_name);
+               }
+               dev_info->is_controller_configured = 1;
+
+               ret = initialize(dev_info);
+               if (ret)
+                       goto err_uninit;
+
+               ret = enable_workers(dev_info);
+               if (ret)
+                       goto err_uninit;
+
+               ret = enable_driver_irq(dev_info);
+               if (ret)
+                       goto err_uninit;
+
+       } else {
+               /* CMEM is not configured */
+               dev_info->is_controller_configured = 0;
 
-err_nosysfs:
-       edac_device_free_ctl_info(dev_info->edac_dev);
-err_noctlinfo:
+               ret = enable_driver_irq(dev_info);
+               if (ret)
+                       goto err_uninit;
+
+               pr_info("CMEM base init: controller: %s DEV %s (INTERRUPT).\n",
+                       dev_info->ctl_name,
+                       dev_name(&dev_info->pdev->dev));
+       }
+
+#ifdef CONFIG_DEBUG_CMEM
+       /* in this case create procfs file to be used by rte */
+       dev_info->proc_name =
+               devm_kzalloc(&pdev->dev, 32*sizeof(char),
+                               GFP_KERNEL);
+       if (!dev_info->proc_name)
+               goto err_uninit;
+
+       snprintf(dev_info->proc_name, 31*sizeof(char),
+               "driver/axxia_edac_%s_control",
+               dev_info->ctl_name);
+
+       /* each instance shall know each private data */
+       dev_info->dir_entry =
+               proc_create_data(dev_info->proc_name, S_IWUSR,
+                               NULL, &axxia_edac_cmem_proc_ops,
+                               dev_info);
+
+       if (dev_info->dir_entry == NULL) {
+               pr_err("Could not create proc entry for %s.\n",
+                               dev_info->ctl_name);
+               goto err_uninit;
+       }
+#endif
+       return 0;
+
+
+err_uninit:
+       uninitialize(dev_info, ret,
+                       0 == dev_info->is_controller_configured ? 1 : 0);
+err_init:
        mutex_destroy(&dev_info->data->edac_sysfs_data_lock);
+       mutex_destroy(&dev_info->state_machine_lock);
        atomic64_dec(&mc_counter);
        return 1;
 err_nomem:
@@ -1211,27 +1591,20 @@ err_nodev:
        return -ENODEV;
 }
 
+
+
 static int intel_edac_mc_remove(struct platform_device *pdev)
 {
        struct intel_edac_dev_info *dev_info =
                (struct intel_edac_dev_info *) &pdev->dev;
 
        if (dev_info) {
-               if (dev_info->data->irq > 0) {
-                       disable_irq(dev_info->data->irq);
-                       devm_free_irq(&pdev->dev,
-                                       dev_info->data->irq, dev_info);
-
-                       dev_info->data->irq = 0;
-
-                       if (dev_info->is_ddr4)
-                               cancel_work_sync(&dev_info->offload_alerts);
-                       cancel_work_sync(&dev_info->offload_events);
+#ifdef CONFIG_DEBUG_CMEM
+               remove_procfs_entry(dev_info);
+#endif
 
-                       if (dev_info->is_ddr4)
-                               destroy_workqueue(dev_info->wq_alerts);
-                       destroy_workqueue(dev_info->wq_events);
-               }
+               uninitialize(dev_info, ERR_STAGE_8,
+                       0 == dev_info->is_controller_configured ? 1 : 0);
 
                if (dev_info->edac_dev != NULL) {
                        edac_device_del_device(&dev_info->pdev->dev);
@@ -1239,6 +1612,8 @@ static int intel_edac_mc_remove(struct platform_device 
*pdev)
                }
 
                mutex_destroy(&dev_info->data->edac_sysfs_data_lock);
+               mutex_destroy(&dev_info->state_machine_lock);
+
                atomic64_dec(&mc_counter);
        }
        platform_device_unregister(pdev);
diff --git a/drivers/edac/axxia_edac-mc_56xx.c 
b/drivers/edac/axxia_edac-mc_56xx.c
index ad61cbf..cfd73c3 100644
--- a/drivers/edac/axxia_edac-mc_56xx.c
+++ b/drivers/edac/axxia_edac-mc_56xx.c
@@ -614,6 +614,8 @@ struct intel_edac_dev_info {
        struct work_struct offload_events;
        struct workqueue_struct *wq_alerts;
        struct workqueue_struct *wq_events;
+       int finish_alerts;
+       int finish_events;
        int is_ddr4;
        int edac_idx;
        u32 sm_region;
@@ -1003,6 +1005,9 @@ start:
                msecs_to_jiffies(ALIVE_NOTIFICATION_PERIOD)))
                goto start;
 
+       if (dev_info->finish_alerts)
+               goto finish;
+
                /* the only one running workqueue */
        for (i = 0; i < dev_info->data->cs_count; ++i) {
 
@@ -1029,6 +1034,9 @@ start:
                wait_event(dev_info->data->dump_wq,
                           atomic_read(&dev_info->data->dump_ready));
 
+               if (dev_info->finish_alerts)
+                       goto finish;
+
                atomic_set(&dev_info->data->dump_ready, 0);
                /* collect data */
                collect_mpr_dump(dev_info, SM_MPR_PAGE, i);
@@ -1062,6 +1070,10 @@ error_write:
        printk_ratelimited("Could not collect MPR dump.\n");
        atomic_set(&dev_info->data->dump_in_progress, 0);
        goto start;
+
+finish:
+       atomic_set(&dev_info->data->dump_ready, 0);
+       atomic_set(&dev_info->data->dump_in_progress, 0);
 }
 
 static void intel_sm_events_error_check(struct edac_device_ctl_info *edac_dev)
@@ -1080,6 +1092,9 @@ static void intel_sm_events_error_check(struct 
edac_device_ctl_info *edac_dev)
 
                atomic_set(&dev_info->data->event_ready, 0);
 
+               if (dev_info->finish_events)
+                       break;
+
                mutex_lock(&dev_info->data->edac_sysfs_data_lock);
                for (i = 0; i < NR_EVENTS; ++i) {
                        counter = atomic_xchg(&events[i].counter, 0);
@@ -1168,6 +1183,22 @@ static int get_active_dram(struct intel_edac_dev_info 
*dev_info)
        return dram;
 }
 
+static void finish_workqueues(struct intel_edac_dev_info *dev_info)
+{
+       if (dev_info->is_ddr4) {
+               dev_info->finish_alerts = 1;
+               atomic_inc(&dev_info->data->dump_in_progress);
+               atomic_set(&dev_info->data->dump_ready, 1);
+               wake_up(&dev_info->data->dump_wq);
+               cancel_work_sync(&dev_info->offload_alerts);
+       }
+
+       dev_info->finish_events = 1;
+       atomic_set(&dev_info->data->event_ready, 1);
+       wake_up(&dev_info->data->event_wq);
+       cancel_work_sync(&dev_info->offload_events);
+}
+
 static int intel_edac_mc_probe(struct platform_device *pdev)
 {
        struct edac_device_instance *instance;
@@ -1292,8 +1323,10 @@ static int intel_edac_mc_probe(struct platform_device 
*pdev)
        dev_info->edac_dev =
                edac_device_alloc_ctl_info(0, dev_info->ctl_name,
                                         1, dev_info->blk_name,
-                                        NR_EVENTS +
-                                        cs_count * dram_count * MPR_ERRORS,
+                                        NR_EVENTS + (dev_info->is_ddr4 ?
+                                        cs_count * dram_count * MPR_ERRORS
+                                        :
+                                        0),
                                         0, NULL, 0, dev_info->edac_idx);
 
        if (!dev_info->edac_dev) {
@@ -1449,9 +1482,7 @@ static int intel_edac_mc_probe(struct platform_device 
*pdev)
        return 0;
 
 err_noirq:
-       if (dev_info->is_ddr4)
-               cancel_work_sync(&dev_info->offload_alerts);
-       cancel_work_sync(&dev_info->offload_events);
+       finish_workqueues(dev_info);
        edac_device_del_device(&dev_info->pdev->dev);
 
        if (dev_info->is_ddr4)
@@ -1489,9 +1520,7 @@ static int intel_edac_mc_remove(struct platform_device 
*pdev)
 
                        dev_info->data->irq = 0;
 
-                       if (dev_info->is_ddr4)
-                               cancel_work_sync(&dev_info->offload_alerts);
-                       cancel_work_sync(&dev_info->offload_events);
+                       finish_workqueues(dev_info);
 
                        if (dev_info->is_ddr4)
                                destroy_workqueue(dev_info->wq_alerts);
-- 
2.7.4

-- 
_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to