octeon_edac-pc.c 3.3 KB
Newer Older
R
Ralf Baechle 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2009 Wind River Systems,
 *   written by Ralf Baechle <ralf@linux-mips.org>
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/edac.h>

#include "edac_core.h"
#include "edac_module.h"

#include <asm/octeon/cvmx.h>
#include <asm/mipsregs.h>

#define EDAC_MOD_STR "octeon"

extern int register_co_cache_error_notifier(struct notifier_block *nb);
extern int unregister_co_cache_error_notifier(struct notifier_block *nb);

extern unsigned long long cache_err_dcache[NR_CPUS];

static struct edac_device_ctl_info *ed_cavium;

/*
 * EDAC CPU cache error callback
 *
 */

static int  co_cache_error_event(struct notifier_block *this,
	unsigned long event, void *ptr)
{
	unsigned int core = cvmx_get_core_num();
	unsigned int cpu = smp_processor_id();
	uint64_t icache_err = read_octeon_c0_icacheerr();
	struct edac_device_ctl_info *ed = ed_cavium;

	edac_device_printk(ed, KERN_ERR,
			   "Cache error exception on core %d / processor %d:\n",
			   core, cpu);
	edac_device_printk(ed, KERN_ERR,
			   "cp0_errorepc == %lx\n", read_c0_errorepc());
	if (icache_err & 1) {
		edac_device_printk(ed, KERN_ERR, "CacheErr (Icache) == %llx\n",
				   (unsigned long long)icache_err);
		write_octeon_c0_icacheerr(0);
		edac_device_handle_ce(ed, 0, 0, ed->ctl_name);
	}
	if (cache_err_dcache[core] & 1) {
		edac_device_printk(ed, KERN_ERR, "CacheErr (Dcache) == %llx\n",
				   (unsigned long long)cache_err_dcache[core]);
		cache_err_dcache[core] = 0;
		edac_device_handle_ue(ed, 0, 0, ed->ctl_name);
	}

	return NOTIFY_DONE;
}

static struct notifier_block co_cache_error_notifier = {
	.notifier_call = co_cache_error_event,
};

static int __devinit co_cache_error_probe(struct platform_device *pdev)
{
	struct edac_device_ctl_info *ed;
	int res = 0;

	ed = edac_device_alloc_ctl_info(0, "cpu", 1, NULL, 0, 0, NULL, 0,
					edac_device_alloc_index());

	ed->dev = &pdev->dev;
	platform_set_drvdata(pdev, ed);
	ed->dev_name = dev_name(&pdev->dev);

	ed->mod_name = "octeon-cpu";
	ed->ctl_name = "co_cpu_err";

	if (edac_device_add_device(ed) > 0) {
		pr_err("%s: edac_device_add_device() failed\n", __func__);
		goto err;
	}

	register_co_cache_error_notifier(&co_cache_error_notifier);
	ed_cavium = ed;

	return 0;

err:
	edac_device_free_ctl_info(ed);

	return res;
}

static int co_cache_error_remove(struct platform_device *pdev)
{
	struct edac_device_ctl_info *ed = platform_get_drvdata(pdev);

	unregister_co_cache_error_notifier(&co_cache_error_notifier);
	ed_cavium = NULL;
	edac_device_del_device(&pdev->dev);
	edac_device_free_ctl_info(ed);

	return 0;
}

static struct platform_driver co_cache_error_driver = {
	.probe = co_cache_error_probe,
	.remove = co_cache_error_remove,
	.driver = {
		   .name = "co_pc_edac",
	}
};

static int __init co_edac_init(void)
{
	int ret;

	ret = platform_driver_register(&co_cache_error_driver);
	if (ret)
		pr_warning(EDAC_MOD_STR "CPU err failed to register\n");

	return ret;
}

static void __exit co_edac_exit(void)
{
	platform_driver_unregister(&co_cache_error_driver);
}

module_init(co_edac_init);
module_exit(co_edac_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");