[linux-yocto] [PATCH 2/2] drivers/edac: CMEM EDAC support on config load.
Daniel Dragomir
daniel.dragomir at windriver.com
Fri Oct 6 08:49:12 PDT 2017
From: Marek Majtyka <marekx.majtyka at 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.majtyka at 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
More information about the linux-yocto
mailing list