File Index
Files and directories
Sign in
FILE

loongson_edac.c

File preview with syntax highlighting.

linux-6.19.14/drivers/edac/loongson_edac.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2024 Loongson Technology Corporation Limited.
  4 */
  5
  6#include <linux/acpi.h>
  7#include <linux/edac.h>
  8#include <linux/init.h>
  9#include <linux/io-64-nonatomic-lo-hi.h>
 10#include <linux/module.h>
 11#include <linux/platform_device.h>
 12#include "edac_module.h"
 13
 14#define ECC_CS_COUNT_REG	0x18
 15
 16struct loongson_edac_pvt {
 17	void __iomem *ecc_base;
 18
 19	/*
 20	 * The ECC register in this controller records the number of errors
 21	 * encountered since reset and cannot be zeroed so in order to be able
 22	 * to report the error count at each check, this records the previous
 23	 * register state.
 24	 */
 25	int last_ce_count;
 26};
 27
 28static int read_ecc(struct mem_ctl_info *mci)
 29{
 30	struct loongson_edac_pvt *pvt = mci->pvt_info;
 31	u64 ecc;
 32	int cs;
 33
 34	ecc = readq(pvt->ecc_base + ECC_CS_COUNT_REG);
 35	/* cs0 -- cs3 */
 36	cs = ecc & 0xff;
 37	cs += (ecc >> 8) & 0xff;
 38	cs += (ecc >> 16) & 0xff;
 39	cs += (ecc >> 24) & 0xff;
 40
 41	return cs;
 42}
 43
 44static void edac_check(struct mem_ctl_info *mci)
 45{
 46	struct loongson_edac_pvt *pvt = mci->pvt_info;
 47	int new, add;
 48
 49	new = read_ecc(mci);
 50	add = new - pvt->last_ce_count;
 51	pvt->last_ce_count = new;
 52	if (add <= 0)
 53		return;
 54
 55	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add,
 56			     0, 0, 0, 0, 0, -1, "error", "");
 57}
 58
 59static void dimm_config_init(struct mem_ctl_info *mci)
 60{
 61	struct dimm_info *dimm;
 62	u32 size, npages;
 63
 64	/* size not used */
 65	size = -1;
 66	npages = MiB_TO_PAGES(size);
 67
 68	dimm = edac_get_dimm(mci, 0, 0, 0);
 69	dimm->nr_pages = npages;
 70	snprintf(dimm->label, sizeof(dimm->label),
 71		 "MC#%uChannel#%u_DIMM#%u", mci->mc_idx, 0, 0);
 72	dimm->grain = 8;
 73}
 74
 75static void pvt_init(struct mem_ctl_info *mci, void __iomem *vbase)
 76{
 77	struct loongson_edac_pvt *pvt = mci->pvt_info;
 78
 79	pvt->ecc_base = vbase;
 80	pvt->last_ce_count = read_ecc(mci);
 81}
 82
 83static int edac_probe(struct platform_device *pdev)
 84{
 85	struct edac_mc_layer layers[2];
 86	struct mem_ctl_info *mci;
 87	void __iomem *vbase;
 88	int ret;
 89
 90	vbase = devm_platform_ioremap_resource(pdev, 0);
 91	if (IS_ERR(vbase))
 92		return PTR_ERR(vbase);
 93
 94	layers[0].type = EDAC_MC_LAYER_CHANNEL;
 95	layers[0].size = 1;
 96	layers[0].is_virt_csrow = false;
 97	layers[1].type = EDAC_MC_LAYER_SLOT;
 98	layers[1].size = 1;
 99	layers[1].is_virt_csrow = true;
100	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
101			    sizeof(struct loongson_edac_pvt));
102	if (mci == NULL)
103		return -ENOMEM;
104
105	mci->mc_idx = edac_device_alloc_index();
106	mci->mtype_cap = MEM_FLAG_RDDR4;
107	mci->edac_ctl_cap = EDAC_FLAG_NONE;
108	mci->edac_cap = EDAC_FLAG_NONE;
109	mci->mod_name = "loongson_edac.c";
110	mci->ctl_name = "loongson_edac_ctl";
111	mci->dev_name = "loongson_edac_dev";
112	mci->ctl_page_to_phys = NULL;
113	mci->pdev = &pdev->dev;
114	mci->error_desc.grain = 8;
115	mci->edac_check = edac_check;
116
117	pvt_init(mci, vbase);
118	dimm_config_init(mci);
119
120	ret = edac_mc_add_mc(mci);
121	if (ret) {
122		edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
123		edac_mc_free(mci);
124		return ret;
125	}
126	edac_op_state = EDAC_OPSTATE_POLL;
127
128	return 0;
129}
130
131static void edac_remove(struct platform_device *pdev)
132{
133	struct mem_ctl_info *mci = edac_mc_del_mc(&pdev->dev);
134
135	if (mci)
136		edac_mc_free(mci);
137}
138
139static const struct acpi_device_id loongson_edac_acpi_match[] = {
140	{"LOON0010", 0},
141	{}
142};
143MODULE_DEVICE_TABLE(acpi, loongson_edac_acpi_match);
144
145static struct platform_driver loongson_edac_driver = {
146	.probe		= edac_probe,
147	.remove		= edac_remove,
148	.driver		= {
149		.name	= "loongson-mc-edac",
150		.acpi_match_table = loongson_edac_acpi_match,
151	},
152};
153module_platform_driver(loongson_edac_driver);
154
155MODULE_LICENSE("GPL");
156MODULE_AUTHOR("Zhao Qunqin <zhaoqunqin@loongson.cn>");
157MODULE_DESCRIPTION("EDAC driver for loongson memory controller");
~/ Read-only file preview.