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