From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.1 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 80DABC0044C for ; Fri, 9 Nov 2018 07:04:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2A75B20840 for ; Fri, 9 Nov 2018 07:04:29 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=alliedtelesis.co.nz header.i=@alliedtelesis.co.nz header.b="IKmfpZy2" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2A75B20840 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=alliedtelesis.co.nz Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728342AbeKIQnj (ORCPT ); Fri, 9 Nov 2018 11:43:39 -0500 Received: from gate2.alliedtelesis.co.nz ([202.36.163.20]:45853 "EHLO gate2.alliedtelesis.co.nz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728294AbeKIQnj (ORCPT ); Fri, 9 Nov 2018 11:43:39 -0500 Received: from mmarshal3.atlnz.lc (mmarshal3.atlnz.lc [10.32.18.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by gate2.alliedtelesis.co.nz (Postfix) with ESMTPS id 632FD89155; Fri, 9 Nov 2018 20:04:24 +1300 (NZDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=alliedtelesis.co.nz; s=mail181024; t=1541747064; bh=iya7e0S8Z0m3VpAf1aEuS6XziLLsVmevqJSPc6l+CBw=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=IKmfpZy2PET6rSho5H654NvXL0Vo2lNDcChlsi+/VufQld0GMj78xRo/6jBmAb5ZM E1oI7LSh93HlGGO9TtW1cr3TJ7G8WysSahfNNscGE1BVQqnPgHyP1UPHtkfzRwNMkg EOWVhUy5gnZGSI7yqpv/LbspfDljQ3dCtktuPWC6fPyOZfYEllM6TeABV+GyD+ev6/ coOfI7t+hIDc6kCTMmCKtlAVkNSgBflTy1rURsZc6eX/R+VlN5HjoM/xmhISuzL0gv 3aCHPaK1exHGgPgULfTNIZGFB7vaYxU7XVD4GtwMin2xdep3ZOyEkpTymyvsJN0Uyn WAl2CIGNu1CPg== Received: from smtp (Not Verified[10.32.16.33]) by mmarshal3.atlnz.lc with Trustwave SEG (v7,5,8,10121) id ; Fri, 09 Nov 2018 20:04:00 +1300 Received: from chrisp-dl.ws.atlnz.lc (chrisp-dl.ws.atlnz.lc [10.33.22.30]) by smtp (Postfix) with ESMTP id 2ED0E13EEA1; Fri, 9 Nov 2018 20:04:05 +1300 (NZDT) Received: by chrisp-dl.ws.atlnz.lc (Postfix, from userid 1030) id 0BA9B1E0BC9; Fri, 9 Nov 2018 20:04:00 +1300 (NZDT) From: Chris Packham To: linux@armlinux.org.uk, bp@alien8.de, arnd@arndb.de, jlu@pengutronix.de, gregory.clement@bootlin.com Cc: linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, linux-edac@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Packham , Mauro Carvalho Chehab Subject: [PATCH v6 8/9] EDAC: Add driver for the Marvell Armada XP SDRAM and L2 cache ECC Date: Fri, 9 Nov 2018 20:03:48 +1300 Message-Id: <20181109070349.20464-9-chris.packham@alliedtelesis.co.nz> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181109070349.20464-1-chris.packham@alliedtelesis.co.nz> References: <20181109070349.20464-1-chris.packham@alliedtelesis.co.nz> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable x-atlnz-ls: pat Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Jan Luebbe Add support for the ECC functionality as found in the DDR RAM and L2 cache controllers on the MV78230/MV78x60 SoCs. This driver has been tested on the MV78460 (on a custom board with a DDR3 ECC DIMM). Signed-off-by: Jan Luebbe [cp use SPDX license] Signed-off-by: Chris Packham --- =20MAINTAINERS | 6 + =20drivers/edac/Kconfig | 7 + =20drivers/edac/Makefile | 1 + =20drivers/edac/armada_xp_edac.c | 630 ++++++++++++++++++++++++++++++++++= =204 files changed, 644 insertions(+) =20create mode 100644 drivers/edac/armada_xp_edac.c diff --git a/MAINTAINERS b/MAINTAINERS index 6c7ed26e84fa..7ae4cfa5c121 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5242,6 +5242,12 @@ L: linux-edac@vger.kernel.org =20S: Maintained =20F: drivers/edac/amd64_edac* =20 +EDAC-ARMADA +M: Jan Luebbe +L: linux-edac@vger.kernel.org +S: Maintained +F: drivers/edac/armada_xp_* + =20EDAC-CALXEDA =20M: Robert Richter =20L: linux-edac@vger.kernel.org diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 57304b2e989f..4567757d9f82 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -439,6 +439,13 @@ config EDAC_ALTERA_SDMMC =20 Support for error detection and correction on the =20 Altera SDMMC FIFO Memory for Altera SoCs. =20 +config EDAC_ARMADA_XP + bool "Marvell Armada XP DDR and L2 Cache ECC" + depends on MACH_MVEBU_V7 + help + Support for error correction and detection on the Marvell Aramada XP + DDR RAM and L2 cache controllers. + =20config EDAC_SYNOPSYS =20 tristate "Synopsys DDR Memory Controller" =20 depends on ARCH_ZYNQ diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 02b43a7d8c3e..f3ea40b0ce9c 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_EDAC_OCTEON_PCI) +=3D octeon_edac-pci.o =20obj-$(CONFIG_EDAC_THUNDERX) +=3D thunderx_edac.o =20 =20obj-$(CONFIG_EDAC_ALTERA) +=3D altera_edac.o +obj-$(CONFIG_EDAC_ARMADA_XP) +=3D armada_xp_edac.o =20obj-$(CONFIG_EDAC_SYNOPSYS) +=3D synopsys_edac.o =20obj-$(CONFIG_EDAC_XGENE) +=3D xgene_edac.o =20obj-$(CONFIG_EDAC_TI) +=3D ti_edac.o diff --git a/drivers/edac/armada_xp_edac.c b/drivers/edac/armada_xp_edac.= c new file mode 100644 index 000000000000..3759a4fbbdee --- /dev/null +++ b/drivers/edac/armada_xp_edac.c @@ -0,0 +1,630 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017 Pengutronix, Jan Luebbe + */ + +#include +#include +#include + +#include +#include + +#include "edac_mc.h" +#include "edac_device.h" +#include "edac_module.h" + +/************************ EDAC MC (DDR RAM) ****************************= ****/ + +#define SDRAM_NUM_CS 4 + +#define SDRAM_CONFIG_REG 0x0 +#define SDRAM_CONFIG_ECC_MASK BIT(18) +#define SDRAM_CONFIG_REGISTERED_MASK BIT(17) +#define SDRAM_CONFIG_BUS_WIDTH_MASK BIT(15) + +#define SDRAM_ADDR_CTRL_REG 0x10 +#define SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(cs) (20+cs) +#define SDRAM_ADDR_CTRL_SIZE_HIGH_MASK(cs) (0x1 << SDRAM_ADDR_CTRL_SIZ= E_HIGH_OFFSET(cs)) +#define SDRAM_ADDR_CTRL_ADDR_SEL_MASK(cs) BIT(16+cs) +#define SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(cs) (cs*4+2) +#define SDRAM_ADDR_CTRL_SIZE_LOW_MASK(cs) (0x3 << SDRAM_ADDR_CTRL_SIZ= E_LOW_OFFSET(cs)) +#define SDRAM_ADDR_CTRL_STRUCT_OFFSET(cs) (cs*4) +#define SDRAM_ADDR_CTRL_STRUCT_MASK(cs) (0x3 << SDRAM_ADDR_CTRL_STR= UCT_OFFSET(cs)) + +#define SDRAM_ERR_DATA_H_REG 0x40 +#define SDRAM_ERR_DATA_L_REG 0x44 + +#define SDRAM_ERR_RECV_ECC_REG 0x48 +#define SDRAM_ERR_RECV_ECC_VALUE_MASK 0xff + +#define SDRAM_ERR_CALC_ECC_REG 0x4c +#define SDRAM_ERR_CALC_ECC_ROW_OFFSET 8 +#define SDRAM_ERR_CALC_ECC_ROW_MASK (0xffff << SDRAM_ERR_CALC_ECC_ROW_= OFFSET) +#define SDRAM_ERR_CALC_ECC_VALUE_MASK 0xff + +#define SDRAM_ERR_ADDR_REG 0x50 +#define SDRAM_ERR_ADDR_BANK_OFFSET 23 +#define SDRAM_ERR_ADDR_BANK_MASK (0x7 << SDRAM_ERR_ADDR_BANK_OFFSET= ) +#define SDRAM_ERR_ADDR_COL_OFFSET 8 +#define SDRAM_ERR_ADDR_COL_MASK (0x7fff << SDRAM_ERR_ADDR_COL_OFFS= ET) +#define SDRAM_ERR_ADDR_CS_OFFSET 1 +#define SDRAM_ERR_ADDR_CS_MASK (0x3 << SDRAM_ERR_ADDR_CS_OFFSET) +#define SDRAM_ERR_ADDR_TYPE_MASK BIT(0) + +#define SDRAM_ERR_CTRL_REG 0x54 +#define SDRAM_ERR_CTRL_THR_OFFSET 16 +#define SDRAM_ERR_CTRL_THR_MASK (0xff << SDRAM_ERR_CTRL_THR_OFFSET= ) +#define SDRAM_ERR_CTRL_PROP_MASK BIT(9) + +#define SDRAM_ERR_SBE_COUNT_REG 0x58 +#define SDRAM_ERR_DBE_COUNT_REG 0x5c + +#define SDRAM_ERR_CAUSE_ERR_REG 0xd0 +#define SDRAM_ERR_CAUSE_MSG_REG 0xd8 +#define SDRAM_ERR_CAUSE_DBE_MASK BIT(1) +#define SDRAM_ERR_CAUSE_SBE_MASK BIT(0) + +#define SDRAM_RANK_CTRL_REG 0x1e0 +#define SDRAM_RANK_CTRL_EXIST_MASK(cs) BIT(cs) + +struct axp_mc_drvdata { + void __iomem *base; + /* width in bytes */ + unsigned int width; + /* bank interleaving */ + bool cs_addr_sel[SDRAM_NUM_CS]; + + char msg[128]; +}; + +/* derived from "DRAM Address Multiplexing" in the ARAMDA XP Functional = Spec */ +static uint32_t axp_mc_calc_address(struct axp_mc_drvdata *drvdata, + uint8_t cs, uint8_t bank, uint16_t row, + uint16_t col) +{ + if (drvdata->width =3D=3D 8) { + /* 64 bit */ + if (drvdata->cs_addr_sel[cs]) + /* bank interleaved */ + return (((row & 0xfff8) << 16) | + ((bank & 0x7) << 16) | + ((row & 0x7) << 13) | + ((col & 0x3ff) << 3)); + else + return (((row & 0xffff << 16) | + ((bank & 0x7) << 13) | + ((col & 0x3ff)) << 3)); + } else if (drvdata->width =3D=3D 4) { + /* 32 bit */ + if (drvdata->cs_addr_sel[cs]) + /* bank interleaved */ + return (((row & 0xfff0) << 15) | + ((bank & 0x7) << 16) | + ((row & 0xf) << 12) | + ((col & 0x3ff) << 2)); + else + return (((row & 0xffff << 15) | + ((bank & 0x7) << 12) | + ((col & 0x3ff)) << 2)); + } else { + /* 16 bit */ + if (drvdata->cs_addr_sel[cs]) + /* bank interleaved */ + return (((row & 0xffe0) << 14) | + ((bank & 0x7) << 16) | + ((row & 0x1f) << 11) | + ((col & 0x3ff) << 1)); + else + return (((row & 0xffff << 14) | + ((bank & 0x7) << 11) | + ((col & 0x3ff)) << 1)); + } +} + +static void axp_mc_check(struct mem_ctl_info *mci) +{ + struct axp_mc_drvdata *drvdata =3D mci->pvt_info; + uint32_t data_h, data_l, recv_ecc, calc_ecc, addr; + uint32_t cnt_sbe, cnt_dbe, cause_err, cause_msg; + uint32_t row_val, col_val, bank_val, addr_val; + uint8_t syndrome_val, cs_val; + char *msg =3D drvdata->msg; + + data_h =3D readl(drvdata->base + SDRAM_ERR_DATA_H_REG); + data_l =3D readl(drvdata->base + SDRAM_ERR_DATA_L_REG); + recv_ecc =3D readl(drvdata->base + SDRAM_ERR_RECV_ECC_REG); + calc_ecc =3D readl(drvdata->base + SDRAM_ERR_CALC_ECC_REG); + addr =3D readl(drvdata->base + SDRAM_ERR_ADDR_REG); + cnt_sbe =3D readl(drvdata->base + SDRAM_ERR_SBE_COUNT_REG); + cnt_dbe =3D readl(drvdata->base + SDRAM_ERR_DBE_COUNT_REG); + cause_err =3D readl(drvdata->base + SDRAM_ERR_CAUSE_ERR_REG); + cause_msg =3D readl(drvdata->base + SDRAM_ERR_CAUSE_MSG_REG); + + /* clear cause registers */ + writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), + drvdata->base + SDRAM_ERR_CAUSE_ERR_REG); + writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), + drvdata->base + SDRAM_ERR_CAUSE_MSG_REG); + + /* clear error counter registers */ + if (cnt_sbe) + writel(0, drvdata->base + SDRAM_ERR_SBE_COUNT_REG); + if (cnt_dbe) + writel(0, drvdata->base + SDRAM_ERR_DBE_COUNT_REG); + + if (!cnt_sbe && !cnt_dbe) + return; + + if (!(addr & SDRAM_ERR_ADDR_TYPE_MASK)) { + if (cnt_sbe) + cnt_sbe--; + else + dev_warn(mci->pdev, "inconsistent SBE count detected"); + } else { + if (cnt_dbe) + cnt_dbe--; + else + dev_warn(mci->pdev, "inconsistent DBE count detected"); + } + + /* report earlier errors */ + if (cnt_sbe) + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + cnt_sbe, /* error count */ + 0, 0, 0, /* pfn, offset, syndrome */ + -1, -1, -1, /* top, mid, low layer */ + mci->ctl_name, + "details unavailable (multiple errors)"); + if (cnt_dbe) + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + cnt_sbe, /* error count */ + 0, 0, 0, /* pfn, offset, syndrome */ + -1, -1, -1, /* top, mid, low layer */ + mci->ctl_name, + "details unavailable (multiple errors)"); + + /* report details for most recent error */ + cs_val =3D (addr & SDRAM_ERR_ADDR_CS_MASK) >> SDRAM_ERR_ADDR_CS_OFFSE= T; + bank_val =3D (addr & SDRAM_ERR_ADDR_BANK_MASK) >> SDRAM_ERR_ADDR_BANK_O= FFSET; + row_val =3D (calc_ecc & SDRAM_ERR_CALC_ECC_ROW_MASK) >> SDRAM_ERR_CALC= _ECC_ROW_OFFSET; + col_val =3D (addr & SDRAM_ERR_ADDR_COL_MASK) >> SDRAM_ERR_ADDR_COL_OFF= SET; + syndrome_val =3D (recv_ecc ^ calc_ecc) & 0xff; + addr_val =3D axp_mc_calc_address(drvdata, cs_val, bank_val, row_val, + col_val); + msg +=3D sprintf(msg, "row=3D0x%04x ", row_val); /* 11 chars */ + msg +=3D sprintf(msg, "bank=3D0x%x ", bank_val); /* 9 chars */ + msg +=3D sprintf(msg, "col=3D0x%04x ", col_val); /* 11 chars */ + msg +=3D sprintf(msg, "cs=3D%d", cs_val); /* 4 chars */ + + if (!(addr & SDRAM_ERR_ADDR_TYPE_MASK)) { + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + 1, /* error count */ + addr_val >> PAGE_SHIFT, + addr_val & ~PAGE_MASK, + syndrome_val, + cs_val, -1, -1, /* top, mid, low layer */ + mci->ctl_name, drvdata->msg); + } else { + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + 1, /* error count */ + addr_val >> PAGE_SHIFT, + addr_val & ~PAGE_MASK, + syndrome_val, + cs_val, -1, -1, /* top, mid, low layer */ + mci->ctl_name, drvdata->msg); + } +} + +static void axp_mc_read_config(struct mem_ctl_info *mci) +{ + struct axp_mc_drvdata *drvdata =3D mci->pvt_info; + uint32_t config, addr_ctrl, rank_ctrl; + unsigned int i, cs_struct, cs_size; + struct dimm_info *dimm; + + config =3D readl(drvdata->base + SDRAM_CONFIG_REG); + if (config & SDRAM_CONFIG_BUS_WIDTH_MASK) + /* 64 bit */ + drvdata->width =3D 8; + else + /* 32 bit */ + drvdata->width =3D 4; + + addr_ctrl =3D readl(drvdata->base + SDRAM_ADDR_CTRL_REG); + rank_ctrl =3D readl(drvdata->base + SDRAM_RANK_CTRL_REG); + for (i =3D 0; i < SDRAM_NUM_CS; i++) { + dimm =3D mci->dimms[i]; + + if (!(rank_ctrl & SDRAM_RANK_CTRL_EXIST_MASK(i))) + continue; + + drvdata->cs_addr_sel[i] =3D + !!(addr_ctrl & SDRAM_ADDR_CTRL_ADDR_SEL_MASK(i)); + + cs_struct =3D (addr_ctrl & SDRAM_ADDR_CTRL_STRUCT_MASK(i)) >> SDRAM_AD= DR_CTRL_STRUCT_OFFSET(i); + cs_size =3D ((addr_ctrl & SDRAM_ADDR_CTRL_SIZE_HIGH_MASK(i)) >> (SDR= AM_ADDR_CTRL_SIZE_HIGH_OFFSET(i) - 2) | + ((addr_ctrl & SDRAM_ADDR_CTRL_SIZE_LOW_MASK(i)) >> SDRAM_ADDR_CTR= L_SIZE_LOW_OFFSET(i))); + + switch (cs_size) { + case 0: /* 2GBit */ + dimm->nr_pages =3D 524288; + break; + case 1: /* 256MBit */ + dimm->nr_pages =3D 65536; + break; + case 2: /* 512MBit */ + dimm->nr_pages =3D 131072; + break; + case 3: /* 1GBit */ + dimm->nr_pages =3D 262144; + break; + case 4: /* 4GBit */ + dimm->nr_pages =3D 1048576; + break; + case 5: /* 8GBit */ + dimm->nr_pages =3D 2097152; + break; + } + dimm->grain =3D 8; + dimm->dtype =3D cs_struct ? DEV_X16 : DEV_X8; + dimm->mtype =3D (config & SDRAM_CONFIG_REGISTERED_MASK) ? + MEM_RDDR3 : MEM_DDR3; + dimm->edac_mode =3D EDAC_SECDED; + } +} + +static const struct of_device_id axp_mc_of_match[] =3D { + {.compatible =3D "marvell,armada-xp-sdram-controller",}, + {}, +}; +MODULE_DEVICE_TABLE(of, axp_mc_of_match); + +static int axp_mc_probe(struct platform_device *pdev) +{ + struct axp_mc_drvdata *drvdata; + struct edac_mc_layer layers[1]; + const struct of_device_id *id; + struct mem_ctl_info *mci; + struct resource *r; + void __iomem *base; + uint32_t config; + + r =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "Unable to get mem resource\n"); + return -ENODEV; + } + + base =3D devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "Unable to map regs\n"); + return PTR_ERR(base); + } + + config =3D readl(base + SDRAM_CONFIG_REG); + if (!(config & SDRAM_CONFIG_ECC_MASK)) { + dev_warn(&pdev->dev, "SDRAM ECC is not enabled"); + return -EINVAL; + } + + layers[0].type =3D EDAC_MC_LAYER_CHIP_SELECT; + layers[0].size =3D SDRAM_NUM_CS; + layers[0].is_virt_csrow =3D true; + + mci =3D edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*drvdata));= + if (!mci) + return -ENOMEM; + + drvdata =3D mci->pvt_info; + drvdata->base =3D base; + mci->pdev =3D &pdev->dev; + platform_set_drvdata(pdev, mci); + + id =3D of_match_device(axp_mc_of_match, &pdev->dev); + mci->edac_check =3D axp_mc_check; + mci->mtype_cap =3D MEM_FLAG_DDR3; + mci->edac_cap =3D EDAC_FLAG_SECDED; + mci->mod_name =3D pdev->dev.driver->name; + mci->ctl_name =3D id ? id->compatible : "unknown"; + mci->dev_name =3D dev_name(&pdev->dev); + mci->scrub_mode =3D SCRUB_NONE; + + axp_mc_read_config(mci); + + /* configure SBE threshold */ + /* it seems that SBEs are not captured otherwise */ + writel(1 << SDRAM_ERR_CTRL_THR_OFFSET, drvdata->base + SDRAM_ERR_CTRL_R= EG); + + /* clear cause registers */ + writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), drvdata-= >base + SDRAM_ERR_CAUSE_ERR_REG); + writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), drvdata-= >base + SDRAM_ERR_CAUSE_MSG_REG); + + /* clear counter registers */ + writel(0, drvdata->base + SDRAM_ERR_SBE_COUNT_REG); + writel(0, drvdata->base + SDRAM_ERR_DBE_COUNT_REG); + + if (edac_mc_add_mc(mci)) { + edac_mc_free(mci); + return -EINVAL; + } + edac_op_state =3D EDAC_OPSTATE_POLL; + + return 0; +} + +static int axp_mc_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci =3D platform_get_drvdata(pdev); + + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver axp_mc_driver =3D { + .probe =3D axp_mc_probe, + .remove =3D axp_mc_remove, + .driver =3D { + .name =3D "armada_xp_mc_edac", + .of_match_table =3D of_match_ptr(axp_mc_of_match), + }, +}; + +/************************ EDAC Device (L2 Cache) ***********************= ****/ + +struct aurora_l2_drvdata { + void __iomem *base; + + char msg[128]; + + /* error injection via debugfs */ + uint32_t inject_addr; + uint32_t inject_mask; + uint8_t inject_ctl; + + struct dentry *debugfs; +}; + +#ifdef CONFIG_EDAC_DEBUG +static void aurora_l2_inject(struct aurora_l2_drvdata *drvdata) +{ + drvdata->inject_addr &=3D AURORA_ERR_INJECT_CTL_ADDR_MASK; + drvdata->inject_ctl &=3D AURORA_ERR_INJECT_CTL_EN_MASK; + writel(0, drvdata->base + AURORA_ERR_INJECT_CTL_REG); + writel(drvdata->inject_mask, drvdata->base + AURORA_ERR_INJECT_MASK_REG= ); + writel(drvdata->inject_addr | drvdata->inject_ctl, drvdata->base + AURO= RA_ERR_INJECT_CTL_REG); +} +#endif + +static void aurora_l2_check(struct edac_device_ctl_info *dci) +{ + struct aurora_l2_drvdata *drvdata =3D dci->pvt_info; + uint32_t cnt, src, txn, err, attr_cap, addr_cap, way_cap; + unsigned int cnt_ce, cnt_ue; + char *msg =3D drvdata->msg; + size_t size =3D sizeof(drvdata->msg); + size_t len =3D 0; + + cnt =3D readl(drvdata->base + AURORA_ERR_CNT_REG); + attr_cap =3D readl(drvdata->base + AURORA_ERR_ATTR_CAP_REG); + addr_cap =3D readl(drvdata->base + AURORA_ERR_ADDR_CAP_REG); + way_cap =3D readl(drvdata->base + AURORA_ERR_WAY_CAP_REG); + + cnt_ce =3D (cnt & AURORA_ERR_CNT_CE_MASK) >> AURORA_ERR_CNT_CE_OFFSET; + cnt_ue =3D (cnt & AURORA_ERR_CNT_UE_MASK) >> AURORA_ERR_CNT_UE_OFFSET; + /* clear error counter registers */ + if (cnt_ce || cnt_ue) + writel(AURORA_ERR_CNT_CLR, drvdata->base + AURORA_ERR_CNT_REG); + + if (!(attr_cap & AURORA_ERR_ATTR_CAP_VALID)) + goto clear_remaining; + + src =3D (attr_cap & AURORA_ERR_ATTR_SRC_MSK) >> AURORA_ERR_ATTR_SRC_OFF= ; + if (src <=3D 3) + len +=3D snprintf(msg+len, size-len, "src=3DCPU%d ", src); + else + len +=3D snprintf(msg+len, size-len, "src=3DIO "); + + txn =3D (attr_cap & AURORA_ERR_ATTR_TXN_MSK) >> AURORA_ERR_ATTR_TXN_OF= F; + switch (txn) { + case 0: + len +=3D snprintf(msg+len, size-len, "txn=3DData-Read "); + break; + case 1: + len +=3D snprintf(msg+len, size-len, "txn=3DIsn-Read "); + break; + case 2: + len +=3D snprintf(msg+len, size-len, "txn=3DClean-Flush "); + break; + case 3: + len +=3D snprintf(msg+len, size-len, "txn=3DEviction "); + break; + case 4: + len +=3D snprintf(msg+len, size-len, + "txn=3DRead-Modify-Write "); + break; + } + + err =3D (attr_cap & AURORA_ERR_ATTR_ERR_MSK) >> AURORA_ERR_ATTR_ERR_OFF= ; + switch (err) { + case 0: + len +=3D snprintf(msg+len, size-len, "err=3DCorrECC "); + break; + case 1: + len +=3D snprintf(msg+len, size-len, "err=3DUnCorrECC "); + break; + case 2: + len +=3D snprintf(msg+len, size-len, "err=3DTagParity "); + break; + } + + len +=3D snprintf(msg+len, size-len, "addr=3D0x%x ", addr_cap & AURORA_= ERR_ADDR_CAP_ADDR_MASK); + len +=3D snprintf(msg+len, size-len, "index=3D0x%x ", (way_cap & AURORA= _ERR_WAY_IDX_MSK) >> AURORA_ERR_WAY_IDX_OFF); + len +=3D snprintf(msg+len, size-len, "way=3D0x%x", (way_cap & AURORA_ER= R_WAY_CAP_WAY_MASK) >> AURORA_ERR_WAY_CAP_WAY_OFFSET); + + /* clear error capture registers */ + writel(AURORA_ERR_ATTR_CAP_VALID, drvdata->base + AURORA_ERR_ATTR_CAP_R= EG); + if (err) { + /* UnCorrECC or TagParity */ + if (cnt_ue) + cnt_ue--; + edac_device_handle_ue(dci, 0, 0, drvdata->msg); + } else { + if (cnt_ce) + cnt_ce--; + edac_device_handle_ce(dci, 0, 0, drvdata->msg); + } + +clear_remaining: + /* report remaining errors */ + while (cnt_ue--) + edac_device_handle_ue(dci, 0, 0, "details unavailable (multiple errors= )"); + while (cnt_ce--) + edac_device_handle_ue(dci, 0, 0, "details unavailable (multiple errors= )"); +} + +static void aurora_l2_poll(struct edac_device_ctl_info *dci) +{ +#ifdef CONFIG_EDAC_DEBUG + struct aurora_l2_drvdata *drvdata =3D dci->pvt_info; +#endif + + aurora_l2_check(dci); +#ifdef CONFIG_EDAC_DEBUG + aurora_l2_inject(drvdata); +#endif +} + +static const struct of_device_id aurora_l2_of_match[] =3D { + {.compatible =3D "marvell,aurora-system-cache",}, + {}, +}; +MODULE_DEVICE_TABLE(of, aurora_l2_of_match); + +static int aurora_l2_probe(struct platform_device *pdev) +{ + struct aurora_l2_drvdata *drvdata; + struct edac_device_ctl_info *dci; + const struct of_device_id *id; + uint32_t l2x0_aux_ctrl; + void __iomem *base; + struct resource *r; + + r =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "Unable to get mem resource\n"); + return -ENODEV; + } + + base =3D devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "Unable to map regs\n"); + return PTR_ERR(base); + } + + l2x0_aux_ctrl =3D readl(base + L2X0_AUX_CTRL); + if (!(l2x0_aux_ctrl & AURORA_ACR_PARITY_EN)) + dev_warn(&pdev->dev, "tag parity is not enabled"); + if (!(l2x0_aux_ctrl & AURORA_ACR_ECC_EN)) + dev_warn(&pdev->dev, "data ECC is not enabled"); + + dci =3D edac_device_alloc_ctl_info(sizeof(*drvdata), + "cpu", 1, "L", 1, 2, NULL, 0, 0); + if (!dci) + return -ENOMEM; + + drvdata =3D dci->pvt_info; + drvdata->base =3D base; + dci->dev =3D &pdev->dev; + platform_set_drvdata(pdev, dci); + + id =3D of_match_device(aurora_l2_of_match, &pdev->dev); + dci->edac_check =3D aurora_l2_poll; + dci->mod_name =3D pdev->dev.driver->name; + dci->ctl_name =3D id ? id->compatible : "unknown"; + dci->dev_name =3D dev_name(&pdev->dev); + + /* clear registers */ + writel(AURORA_ERR_CNT_CLR, drvdata->base + AURORA_ERR_CNT_REG); + writel(AURORA_ERR_ATTR_CAP_VALID, drvdata->base + AURORA_ERR_ATTR_CAP_R= EG); + + if (edac_device_add_device(dci)) { + edac_device_free_ctl_info(dci); + return -EINVAL; + } + +#ifdef CONFIG_EDAC_DEBUG + drvdata->debugfs =3D edac_debugfs_create_dir(dev_name(&pdev->dev)); + if (drvdata->debugfs) { + edac_debugfs_create_x32("inject_addr", 0644, + drvdata->debugfs, + &drvdata->inject_addr); + edac_debugfs_create_x32("inject_mask", 0644, + drvdata->debugfs, + &drvdata->inject_mask); + edac_debugfs_create_x8("inject_ctl", 0644, + drvdata->debugfs, &drvdata->inject_ctl); + } +#endif + + return 0; +} + +static int aurora_l2_remove(struct platform_device *pdev) +{ + struct edac_device_ctl_info *dci =3D platform_get_drvdata(pdev); +#ifdef CONFIG_EDAC_DEBUG + struct aurora_l2_drvdata *drvdata =3D dci->pvt_info; + + edac_debugfs_remove_recursive(drvdata->debugfs); +#endif + edac_device_del_device(&pdev->dev); + edac_device_free_ctl_info(dci); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver aurora_l2_driver =3D { + .probe =3D aurora_l2_probe, + .remove =3D aurora_l2_remove, + .driver =3D { + .name =3D "aurora_l2_edac", + .of_match_table =3D of_match_ptr(aurora_l2_of_match), + }, +}; + +/************************ Driver registration **************************= ****/ + +static struct platform_driver * const drivers[] =3D { + &axp_mc_driver, + &aurora_l2_driver, +}; + +static int __init armada_xp_edac_init(void) +{ + int res; + + /* only polling is supported */ + edac_op_state =3D EDAC_OPSTATE_POLL; + + res =3D platform_register_drivers(drivers, ARRAY_SIZE(drivers)); + if (res) + pr_warn("Aramda XP EDAC drivers fail to register\n"); + + return 0; +} +module_init(armada_xp_edac_init); + +static void __exit armada_xp_edac_exit(void) +{ + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); +} +module_exit(armada_xp_edac_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Pengutronix"); +MODULE_DESCRIPTION("EDAC Drivers for Marvell Armada XP SDRAM and L2 Cach= e Controller"); --=20 2.19.1 From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Subject: [v6,8/9] EDAC: Add driver for the Marvell Armada XP SDRAM and L2 cache ECC From: Chris Packham Message-Id: <20181109070349.20464-9-chris.packham@alliedtelesis.co.nz> Date: Fri, 9 Nov 2018 20:03:48 +1300 To: linux@armlinux.org.uk, bp@alien8.de, arnd@arndb.de, jlu@pengutronix.de, gregory.clement@bootlin.com Cc: linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, linux-edac@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Packham , Mauro Carvalho Chehab List-ID: RnJvbTogSmFuIEx1ZWJiZSA8amx1QHBlbmd1dHJvbml4LmRlPgoKQWRkIHN1cHBvcnQgZm9yIHRo ZSBFQ0MgZnVuY3Rpb25hbGl0eSBhcyBmb3VuZCBpbiB0aGUgRERSIFJBTSBhbmQgTDIKY2FjaGUg Y29udHJvbGxlcnMgb24gdGhlIE1WNzgyMzAvTVY3OHg2MCBTb0NzLiBUaGlzIGRyaXZlciBoYXMg YmVlbgp0ZXN0ZWQgb24gdGhlIE1WNzg0NjAgKG9uIGEgY3VzdG9tIGJvYXJkIHdpdGggYSBERFIz IEVDQyBESU1NKS4KClNpZ25lZC1vZmYtYnk6IEphbiBMdWViYmUgPGpsdUBwZW5ndXRyb25peC5k ZT4KW2NwIHVzZSBTUERYIGxpY2Vuc2VdClNpZ25lZC1vZmYtYnk6IENocmlzIFBhY2toYW0gPGNo cmlzLnBhY2toYW1AYWxsaWVkdGVsZXNpcy5jby5uej4KLS0tCiBNQUlOVEFJTkVSUyAgICAgICAg ICAgICAgICAgICB8ICAgNiArCiBkcml2ZXJzL2VkYWMvS2NvbmZpZyAgICAgICAgICB8ICAgNyAr CiBkcml2ZXJzL2VkYWMvTWFrZWZpbGUgICAgICAgICB8ICAgMSArCiBkcml2ZXJzL2VkYWMvYXJt YWRhX3hwX2VkYWMuYyB8IDYzMCArKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrCiA0 IGZpbGVzIGNoYW5nZWQsIDY0NCBpbnNlcnRpb25zKCspCiBjcmVhdGUgbW9kZSAxMDA2NDQgZHJp dmVycy9lZGFjL2FybWFkYV94cF9lZGFjLmMKCmRpZmYgLS1naXQgYS9NQUlOVEFJTkVSUyBiL01B SU5UQUlORVJTCmluZGV4IDZjN2VkMjZlODRmYS4uN2FlNGNmYTVjMTIxIDEwMDY0NAotLS0gYS9N QUlOVEFJTkVSUworKysgYi9NQUlOVEFJTkVSUwpAQCAtNTI0Miw2ICs1MjQyLDEyIEBAIEw6CWxp bnV4LWVkYWNAdmdlci5rZXJuZWwub3JnCiBTOglNYWludGFpbmVkCiBGOglkcml2ZXJzL2VkYWMv YW1kNjRfZWRhYyoKIAorRURBQy1BUk1BREEKK006CUphbiBMdWViYmUgPGpsdUBwZW5ndXRyb25p eC5kZT4KK0w6CWxpbnV4LWVkYWNAdmdlci5rZXJuZWwub3JnCitTOglNYWludGFpbmVkCitGOglk cml2ZXJzL2VkYWMvYXJtYWRhX3hwXyoKKwogRURBQy1DQUxYRURBCiBNOglSb2JlcnQgUmljaHRl ciA8cnJpY0BrZXJuZWwub3JnPgogTDoJbGludXgtZWRhY0B2Z2VyLmtlcm5lbC5vcmcKZGlmZiAt LWdpdCBhL2RyaXZlcnMvZWRhYy9LY29uZmlnIGIvZHJpdmVycy9lZGFjL0tjb25maWcKaW5kZXgg NTczMDRiMmU5ODlmLi40NTY3NzU3ZDlmODIgMTAwNjQ0Ci0tLSBhL2RyaXZlcnMvZWRhYy9LY29u ZmlnCisrKyBiL2RyaXZlcnMvZWRhYy9LY29uZmlnCkBAIC00MzksNiArNDM5LDEzIEBAIGNvbmZp ZyBFREFDX0FMVEVSQV9TRE1NQwogCSAgU3VwcG9ydCBmb3IgZXJyb3IgZGV0ZWN0aW9uIGFuZCBj b3JyZWN0aW9uIG9uIHRoZQogCSAgQWx0ZXJhIFNETU1DIEZJRk8gTWVtb3J5IGZvciBBbHRlcmEg U29Dcy4KIAorY29uZmlnIEVEQUNfQVJNQURBX1hQCisJYm9vbCAiTWFydmVsbCBBcm1hZGEgWFAg RERSIGFuZCBMMiBDYWNoZSBFQ0MiCisJZGVwZW5kcyBvbiBNQUNIX01WRUJVX1Y3CisJaGVscAor CSAgU3VwcG9ydCBmb3IgZXJyb3IgY29ycmVjdGlvbiBhbmQgZGV0ZWN0aW9uIG9uIHRoZSBNYXJ2 ZWxsIEFyYW1hZGEgWFAKKwkgIEREUiBSQU0gYW5kIEwyIGNhY2hlIGNvbnRyb2xsZXJzLgorCiBj b25maWcgRURBQ19TWU5PUFNZUwogCXRyaXN0YXRlICJTeW5vcHN5cyBERFIgTWVtb3J5IENvbnRy b2xsZXIiCiAJZGVwZW5kcyBvbiBBUkNIX1pZTlEKZGlmZiAtLWdpdCBhL2RyaXZlcnMvZWRhYy9N YWtlZmlsZSBiL2RyaXZlcnMvZWRhYy9NYWtlZmlsZQppbmRleCAwMmI0M2E3ZDhjM2UuLmYzZWE0 MGIwY2U5YyAxMDA2NDQKLS0tIGEvZHJpdmVycy9lZGFjL01ha2VmaWxlCisrKyBiL2RyaXZlcnMv ZWRhYy9NYWtlZmlsZQpAQCAtNzQsNiArNzQsNyBAQCBvYmotJChDT05GSUdfRURBQ19PQ1RFT05f UENJKQkJKz0gb2N0ZW9uX2VkYWMtcGNpLm8KIG9iai0kKENPTkZJR19FREFDX1RIVU5ERVJYKQkJ Kz0gdGh1bmRlcnhfZWRhYy5vCiAKIG9iai0kKENPTkZJR19FREFDX0FMVEVSQSkJCSs9IGFsdGVy YV9lZGFjLm8KK29iai0kKENPTkZJR19FREFDX0FSTUFEQV9YUCkJCSs9IGFybWFkYV94cF9lZGFj Lm8KIG9iai0kKENPTkZJR19FREFDX1NZTk9QU1lTKQkJKz0gc3lub3BzeXNfZWRhYy5vCiBvYmot JChDT05GSUdfRURBQ19YR0VORSkJCSs9IHhnZW5lX2VkYWMubwogb2JqLSQoQ09ORklHX0VEQUNf VEkpCQkJKz0gdGlfZWRhYy5vCmRpZmYgLS1naXQgYS9kcml2ZXJzL2VkYWMvYXJtYWRhX3hwX2Vk YWMuYyBiL2RyaXZlcnMvZWRhYy9hcm1hZGFfeHBfZWRhYy5jCm5ldyBmaWxlIG1vZGUgMTAwNjQ0 CmluZGV4IDAwMDAwMDAwMDAwMC4uMzc1OWE0ZmJiZGVlCi0tLSAvZGV2L251bGwKKysrIGIvZHJp dmVycy9lZGFjL2FybWFkYV94cF9lZGFjLmMKQEAgLTAsMCArMSw2MzAgQEAKKy8vIFNQRFgtTGlj ZW5zZS1JZGVudGlmaWVyOiBHUEwtMi4wCisvKgorICogQ29weXJpZ2h0IChDKSAyMDE3IFBlbmd1 dHJvbml4LCBKYW4gTHVlYmJlIDxrZXJuZWxAcGVuZ3V0cm9uaXguZGU+CisgKi8KKworI2luY2x1 ZGUgPGxpbnV4L2tlcm5lbC5oPgorI2luY2x1ZGUgPGxpbnV4L2VkYWMuaD4KKyNpbmNsdWRlIDxs aW51eC9vZl9wbGF0Zm9ybS5oPgorCisjaW5jbHVkZSA8YXNtL2hhcmR3YXJlL2NhY2hlLWwyeDAu aD4KKyNpbmNsdWRlIDxhc20vaGFyZHdhcmUvY2FjaGUtYXVyb3JhLWwyLmg+CisKKyNpbmNsdWRl ICJlZGFjX21jLmgiCisjaW5jbHVkZSAiZWRhY19kZXZpY2UuaCIKKyNpbmNsdWRlICJlZGFjX21v ZHVsZS5oIgorCisvKioqKioqKioqKioqKioqKioqKioqKioqIEVEQUMgTUMgKEREUiBSQU0pICoq KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqLworCisjZGVmaW5lIFNEUkFNX05VTV9DUyA0 CisKKyNkZWZpbmUgU0RSQU1fQ09ORklHX1JFRyAgICAgICAgMHgwCisjZGVmaW5lIFNEUkFNX0NP TkZJR19FQ0NfTUFTSyAgICAgICAgIEJJVCgxOCkKKyNkZWZpbmUgU0RSQU1fQ09ORklHX1JFR0lT VEVSRURfTUFTSyAgQklUKDE3KQorI2RlZmluZSBTRFJBTV9DT05GSUdfQlVTX1dJRFRIX01BU0sg ICBCSVQoMTUpCisKKyNkZWZpbmUgU0RSQU1fQUREUl9DVFJMX1JFRyAgICAgMHgxMAorI2RlZmlu ZSBTRFJBTV9BRERSX0NUUkxfU0laRV9ISUdIX09GRlNFVChjcykgKDIwK2NzKQorI2RlZmluZSBT RFJBTV9BRERSX0NUUkxfU0laRV9ISUdIX01BU0soY3MpICAgKDB4MSA8PCBTRFJBTV9BRERSX0NU UkxfU0laRV9ISUdIX09GRlNFVChjcykpCisjZGVmaW5lIFNEUkFNX0FERFJfQ1RSTF9BRERSX1NF TF9NQVNLKGNzKSAgICBCSVQoMTYrY3MpCisjZGVmaW5lIFNEUkFNX0FERFJfQ1RSTF9TSVpFX0xP V19PRkZTRVQoY3MpICAoY3MqNCsyKQorI2RlZmluZSBTRFJBTV9BRERSX0NUUkxfU0laRV9MT1df TUFTSyhjcykgICAgKDB4MyA8PCBTRFJBTV9BRERSX0NUUkxfU0laRV9MT1dfT0ZGU0VUKGNzKSkK KyNkZWZpbmUgU0RSQU1fQUREUl9DVFJMX1NUUlVDVF9PRkZTRVQoY3MpICAgIChjcyo0KQorI2Rl ZmluZSBTRFJBTV9BRERSX0NUUkxfU1RSVUNUX01BU0soY3MpICAgICAgKDB4MyA8PCBTRFJBTV9B RERSX0NUUkxfU1RSVUNUX09GRlNFVChjcykpCisKKyNkZWZpbmUgU0RSQU1fRVJSX0RBVEFfSF9S RUcgICAgMHg0MAorI2RlZmluZSBTRFJBTV9FUlJfREFUQV9MX1JFRyAgICAweDQ0CisKKyNkZWZp bmUgU0RSQU1fRVJSX1JFQ1ZfRUNDX1JFRyAgMHg0OAorI2RlZmluZSBTRFJBTV9FUlJfUkVDVl9F Q0NfVkFMVUVfTUFTSyAweGZmCisKKyNkZWZpbmUgU0RSQU1fRVJSX0NBTENfRUNDX1JFRyAgMHg0 YworI2RlZmluZSBTRFJBTV9FUlJfQ0FMQ19FQ0NfUk9XX09GRlNFVCA4CisjZGVmaW5lIFNEUkFN X0VSUl9DQUxDX0VDQ19ST1dfTUFTSyAgICgweGZmZmYgPDwgU0RSQU1fRVJSX0NBTENfRUNDX1JP V19PRkZTRVQpCisjZGVmaW5lIFNEUkFNX0VSUl9DQUxDX0VDQ19WQUxVRV9NQVNLIDB4ZmYKKwor I2RlZmluZSBTRFJBTV9FUlJfQUREUl9SRUcgICAgICAweDUwCisjZGVmaW5lIFNEUkFNX0VSUl9B RERSX0JBTktfT0ZGU0VUICAgIDIzCisjZGVmaW5lIFNEUkFNX0VSUl9BRERSX0JBTktfTUFTSyAg ICAgICgweDcgPDwgU0RSQU1fRVJSX0FERFJfQkFOS19PRkZTRVQpCisjZGVmaW5lIFNEUkFNX0VS Ul9BRERSX0NPTF9PRkZTRVQgICAgIDgKKyNkZWZpbmUgU0RSQU1fRVJSX0FERFJfQ09MX01BU0sg ICAgICAgKDB4N2ZmZiA8PCBTRFJBTV9FUlJfQUREUl9DT0xfT0ZGU0VUKQorI2RlZmluZSBTRFJB TV9FUlJfQUREUl9DU19PRkZTRVQgICAgICAxCisjZGVmaW5lIFNEUkFNX0VSUl9BRERSX0NTX01B U0sgICAgICAgICgweDMgPDwgU0RSQU1fRVJSX0FERFJfQ1NfT0ZGU0VUKQorI2RlZmluZSBTRFJB TV9FUlJfQUREUl9UWVBFX01BU0sgICAgICBCSVQoMCkKKworI2RlZmluZSBTRFJBTV9FUlJfQ1RS TF9SRUcgICAgICAweDU0CisjZGVmaW5lIFNEUkFNX0VSUl9DVFJMX1RIUl9PRkZTRVQgICAgIDE2 CisjZGVmaW5lIFNEUkFNX0VSUl9DVFJMX1RIUl9NQVNLICAgICAgICgweGZmIDw8IFNEUkFNX0VS Ul9DVFJMX1RIUl9PRkZTRVQpCisjZGVmaW5lIFNEUkFNX0VSUl9DVFJMX1BST1BfTUFTSyAgICAg IEJJVCg5KQorCisjZGVmaW5lIFNEUkFNX0VSUl9TQkVfQ09VTlRfUkVHIDB4NTgKKyNkZWZpbmUg U0RSQU1fRVJSX0RCRV9DT1VOVF9SRUcgMHg1YworCisjZGVmaW5lIFNEUkFNX0VSUl9DQVVTRV9F UlJfUkVHIDB4ZDAKKyNkZWZpbmUgU0RSQU1fRVJSX0NBVVNFX01TR19SRUcgMHhkOAorI2RlZmlu ZSBTRFJBTV9FUlJfQ0FVU0VfREJFX01BU0sgICAgICBCSVQoMSkKKyNkZWZpbmUgU0RSQU1fRVJS X0NBVVNFX1NCRV9NQVNLICAgICAgQklUKDApCisKKyNkZWZpbmUgU0RSQU1fUkFOS19DVFJMX1JF RyAweDFlMAorI2RlZmluZSBTRFJBTV9SQU5LX0NUUkxfRVhJU1RfTUFTSyhjcykgQklUKGNzKQor CitzdHJ1Y3QgYXhwX21jX2RydmRhdGEgeworCXZvaWQgX19pb21lbSAqYmFzZTsKKwkvKiB3aWR0 aCBpbiBieXRlcyAqLworCXVuc2lnbmVkIGludCB3aWR0aDsKKwkvKiBiYW5rIGludGVybGVhdmlu ZyAqLworCWJvb2wgY3NfYWRkcl9zZWxbU0RSQU1fTlVNX0NTXTsKKworCWNoYXIgbXNnWzEyOF07 Cit9OworCisvKiBkZXJpdmVkIGZyb20gIkRSQU0gQWRkcmVzcyBNdWx0aXBsZXhpbmciIGluIHRo ZSBBUkFNREEgWFAgRnVuY3Rpb25hbCBTcGVjICovCitzdGF0aWMgdWludDMyX3QgYXhwX21jX2Nh bGNfYWRkcmVzcyhzdHJ1Y3QgYXhwX21jX2RydmRhdGEgKmRydmRhdGEsCisJCQkJICAgIHVpbnQ4 X3QgY3MsIHVpbnQ4X3QgYmFuaywgdWludDE2X3Qgcm93LAorCQkJCSAgICB1aW50MTZfdCBjb2wp Cit7CisJaWYgKGRydmRhdGEtPndpZHRoID09IDgpIHsKKwkJLyogNjQgYml0ICovCisJCWlmIChk cnZkYXRhLT5jc19hZGRyX3NlbFtjc10pCisJCQkvKiBiYW5rIGludGVybGVhdmVkICovCisJCQly ZXR1cm4gKCgocm93ICYgMHhmZmY4KSA8PCAxNikgfAorCQkJCSgoYmFuayAmIDB4NykgPDwgMTYp IHwKKwkJCQkoKHJvdyAmIDB4NykgPDwgMTMpIHwKKwkJCQkoKGNvbCAmIDB4M2ZmKSA8PCAzKSk7 CisJCWVsc2UKKwkJCXJldHVybiAoKChyb3cgJiAweGZmZmYgPDwgMTYpIHwKKwkJCQkgKChiYW5r ICYgMHg3KSA8PCAxMykgfAorCQkJCSAoKGNvbCAmIDB4M2ZmKSkgPDwgMykpOworCX0gZWxzZSBp ZiAoZHJ2ZGF0YS0+d2lkdGggPT0gNCkgeworCQkvKiAzMiBiaXQgKi8KKwkJaWYgKGRydmRhdGEt PmNzX2FkZHJfc2VsW2NzXSkKKwkJCS8qIGJhbmsgaW50ZXJsZWF2ZWQgKi8KKwkJCXJldHVybiAo KChyb3cgJiAweGZmZjApIDw8IDE1KSB8CisJCQkJKChiYW5rICYgMHg3KSA8PCAxNikgfAorCQkJ CSgocm93ICYgMHhmKSA8PCAxMikgfAorCQkJCSgoY29sICYgMHgzZmYpIDw8IDIpKTsKKwkJZWxz ZQorCQkJcmV0dXJuICgoKHJvdyAmIDB4ZmZmZiA8PCAxNSkgfAorCQkJCSAoKGJhbmsgJiAweDcp IDw8IDEyKSB8CisJCQkJICgoY29sICYgMHgzZmYpKSA8PCAyKSk7CisJfSBlbHNlIHsKKwkJLyog MTYgYml0ICovCisJCWlmIChkcnZkYXRhLT5jc19hZGRyX3NlbFtjc10pCisJCQkvKiBiYW5rIGlu dGVybGVhdmVkICovCisJCQlyZXR1cm4gKCgocm93ICYgMHhmZmUwKSA8PCAxNCkgfAorCQkJCSgo YmFuayAmIDB4NykgPDwgMTYpIHwKKwkJCQkoKHJvdyAmIDB4MWYpIDw8IDExKSB8CisJCQkJKChj b2wgJiAweDNmZikgPDwgMSkpOworCQllbHNlCisJCQlyZXR1cm4gKCgocm93ICYgMHhmZmZmIDw8 IDE0KSB8CisJCQkJICgoYmFuayAmIDB4NykgPDwgMTEpIHwKKwkJCQkgKChjb2wgJiAweDNmZikp IDw8IDEpKTsKKwl9Cit9CisKK3N0YXRpYyB2b2lkIGF4cF9tY19jaGVjayhzdHJ1Y3QgbWVtX2N0 bF9pbmZvICptY2kpCit7CisJc3RydWN0IGF4cF9tY19kcnZkYXRhICpkcnZkYXRhID0gbWNpLT5w dnRfaW5mbzsKKwl1aW50MzJfdCBkYXRhX2gsIGRhdGFfbCwgcmVjdl9lY2MsIGNhbGNfZWNjLCBh ZGRyOworCXVpbnQzMl90IGNudF9zYmUsIGNudF9kYmUsIGNhdXNlX2VyciwgY2F1c2VfbXNnOwor CXVpbnQzMl90IHJvd192YWwsIGNvbF92YWwsIGJhbmtfdmFsLCBhZGRyX3ZhbDsKKwl1aW50OF90 IHN5bmRyb21lX3ZhbCwgY3NfdmFsOworCWNoYXIgKm1zZyA9IGRydmRhdGEtPm1zZzsKKworCWRh dGFfaCAgICA9IHJlYWRsKGRydmRhdGEtPmJhc2UgKyBTRFJBTV9FUlJfREFUQV9IX1JFRyk7CisJ ZGF0YV9sICAgID0gcmVhZGwoZHJ2ZGF0YS0+YmFzZSArIFNEUkFNX0VSUl9EQVRBX0xfUkVHKTsK KwlyZWN2X2VjYyAgPSByZWFkbChkcnZkYXRhLT5iYXNlICsgU0RSQU1fRVJSX1JFQ1ZfRUNDX1JF Ryk7CisJY2FsY19lY2MgID0gcmVhZGwoZHJ2ZGF0YS0+YmFzZSArIFNEUkFNX0VSUl9DQUxDX0VD Q19SRUcpOworCWFkZHIgICAgICA9IHJlYWRsKGRydmRhdGEtPmJhc2UgKyBTRFJBTV9FUlJfQURE Ul9SRUcpOworCWNudF9zYmUgICA9IHJlYWRsKGRydmRhdGEtPmJhc2UgKyBTRFJBTV9FUlJfU0JF X0NPVU5UX1JFRyk7CisJY250X2RiZSAgID0gcmVhZGwoZHJ2ZGF0YS0+YmFzZSArIFNEUkFNX0VS Ul9EQkVfQ09VTlRfUkVHKTsKKwljYXVzZV9lcnIgPSByZWFkbChkcnZkYXRhLT5iYXNlICsgU0RS QU1fRVJSX0NBVVNFX0VSUl9SRUcpOworCWNhdXNlX21zZyA9IHJlYWRsKGRydmRhdGEtPmJhc2Ug KyBTRFJBTV9FUlJfQ0FVU0VfTVNHX1JFRyk7CisKKwkvKiBjbGVhciBjYXVzZSByZWdpc3RlcnMg Ki8KKwl3cml0ZWwofihTRFJBTV9FUlJfQ0FVU0VfREJFX01BU0sgfCBTRFJBTV9FUlJfQ0FVU0Vf U0JFX01BU0spLAorCSAgICAgICBkcnZkYXRhLT5iYXNlICsgU0RSQU1fRVJSX0NBVVNFX0VSUl9S RUcpOworCXdyaXRlbCh+KFNEUkFNX0VSUl9DQVVTRV9EQkVfTUFTSyB8IFNEUkFNX0VSUl9DQVVT RV9TQkVfTUFTSyksCisJICAgICAgIGRydmRhdGEtPmJhc2UgKyBTRFJBTV9FUlJfQ0FVU0VfTVNH X1JFRyk7CisKKwkvKiBjbGVhciBlcnJvciBjb3VudGVyIHJlZ2lzdGVycyAqLworCWlmIChjbnRf c2JlKQorCQl3cml0ZWwoMCwgZHJ2ZGF0YS0+YmFzZSArIFNEUkFNX0VSUl9TQkVfQ09VTlRfUkVH KTsKKwlpZiAoY250X2RiZSkKKwkJd3JpdGVsKDAsIGRydmRhdGEtPmJhc2UgKyBTRFJBTV9FUlJf REJFX0NPVU5UX1JFRyk7CisKKwlpZiAoIWNudF9zYmUgJiYgIWNudF9kYmUpCisJCXJldHVybjsK KworCWlmICghKGFkZHIgJiBTRFJBTV9FUlJfQUREUl9UWVBFX01BU0spKSB7CisJCWlmIChjbnRf c2JlKQorCQkJY250X3NiZS0tOworCQllbHNlCisJCQlkZXZfd2FybihtY2ktPnBkZXYsICJpbmNv bnNpc3RlbnQgU0JFIGNvdW50IGRldGVjdGVkIik7CisJfSBlbHNlIHsKKwkJaWYgKGNudF9kYmUp CisJCQljbnRfZGJlLS07CisJCWVsc2UKKwkJCWRldl93YXJuKG1jaS0+cGRldiwgImluY29uc2lz dGVudCBEQkUgY291bnQgZGV0ZWN0ZWQiKTsKKwl9CisKKwkvKiByZXBvcnQgZWFybGllciBlcnJv cnMgKi8KKwlpZiAoY250X3NiZSkKKwkJZWRhY19tY19oYW5kbGVfZXJyb3IoSFdfRVZFTlRfRVJS X0NPUlJFQ1RFRCwgbWNpLAorCQkJCSAgICAgY250X3NiZSwgLyogZXJyb3IgY291bnQgKi8KKwkJ CQkgICAgIDAsIDAsIDAsIC8qIHBmbiwgb2Zmc2V0LCBzeW5kcm9tZSAqLworCQkJCSAgICAgLTEs IC0xLCAtMSwgLyogdG9wLCBtaWQsIGxvdyBsYXllciAqLworCQkJCSAgICAgbWNpLT5jdGxfbmFt ZSwKKwkJCQkgICAgICJkZXRhaWxzIHVuYXZhaWxhYmxlIChtdWx0aXBsZSBlcnJvcnMpIik7CisJ aWYgKGNudF9kYmUpCisJCWVkYWNfbWNfaGFuZGxlX2Vycm9yKEhXX0VWRU5UX0VSUl9VTkNPUlJF Q1RFRCwgbWNpLAorCQkJCSAgICAgY250X3NiZSwgLyogZXJyb3IgY291bnQgKi8KKwkJCQkgICAg IDAsIDAsIDAsIC8qIHBmbiwgb2Zmc2V0LCBzeW5kcm9tZSAqLworCQkJCSAgICAgLTEsIC0xLCAt MSwgLyogdG9wLCBtaWQsIGxvdyBsYXllciAqLworCQkJCSAgICAgbWNpLT5jdGxfbmFtZSwKKwkJ CQkgICAgICJkZXRhaWxzIHVuYXZhaWxhYmxlIChtdWx0aXBsZSBlcnJvcnMpIik7CisKKwkvKiBy ZXBvcnQgZGV0YWlscyBmb3IgbW9zdCByZWNlbnQgZXJyb3IgKi8KKwljc192YWwgICA9IChhZGRy ICYgU0RSQU1fRVJSX0FERFJfQ1NfTUFTSykgPj4gU0RSQU1fRVJSX0FERFJfQ1NfT0ZGU0VUOwor CWJhbmtfdmFsID0gKGFkZHIgJiBTRFJBTV9FUlJfQUREUl9CQU5LX01BU0spID4+IFNEUkFNX0VS Ul9BRERSX0JBTktfT0ZGU0VUOworCXJvd192YWwgID0gKGNhbGNfZWNjICYgU0RSQU1fRVJSX0NB TENfRUNDX1JPV19NQVNLKSA+PiBTRFJBTV9FUlJfQ0FMQ19FQ0NfUk9XX09GRlNFVDsKKwljb2xf dmFsICA9IChhZGRyICYgU0RSQU1fRVJSX0FERFJfQ09MX01BU0spID4+IFNEUkFNX0VSUl9BRERS X0NPTF9PRkZTRVQ7CisJc3luZHJvbWVfdmFsID0gKHJlY3ZfZWNjIF4gY2FsY19lY2MpICYgMHhm ZjsKKwlhZGRyX3ZhbCA9IGF4cF9tY19jYWxjX2FkZHJlc3MoZHJ2ZGF0YSwgY3NfdmFsLCBiYW5r X3ZhbCwgcm93X3ZhbCwKKwkJCQkgICAgICAgY29sX3ZhbCk7CisJbXNnICs9IHNwcmludGYobXNn LCAicm93PTB4JTA0eCAiLCByb3dfdmFsKTsgLyogMTEgY2hhcnMgKi8KKwltc2cgKz0gc3ByaW50 Zihtc2csICJiYW5rPTB4JXggIiwgYmFua192YWwpOyAvKiAgOSBjaGFycyAqLworCW1zZyArPSBz cHJpbnRmKG1zZywgImNvbD0weCUwNHggIiwgY29sX3ZhbCk7IC8qIDExIGNoYXJzICovCisJbXNn ICs9IHNwcmludGYobXNnLCAiY3M9JWQiLCBjc192YWwpOwkgICAgIC8qICA0IGNoYXJzICovCisK KwlpZiAoIShhZGRyICYgU0RSQU1fRVJSX0FERFJfVFlQRV9NQVNLKSkgeworCQllZGFjX21jX2hh bmRsZV9lcnJvcihIV19FVkVOVF9FUlJfQ09SUkVDVEVELCBtY2ksCisJCQkJICAgICAxLAkvKiBl cnJvciBjb3VudCAqLworCQkJCSAgICAgYWRkcl92YWwgPj4gUEFHRV9TSElGVCwKKwkJCQkgICAg IGFkZHJfdmFsICYgflBBR0VfTUFTSywKKwkJCQkgICAgIHN5bmRyb21lX3ZhbCwKKwkJCQkgICAg IGNzX3ZhbCwgLTEsIC0xLCAvKiB0b3AsIG1pZCwgbG93IGxheWVyICovCisJCQkJICAgICBtY2kt PmN0bF9uYW1lLCBkcnZkYXRhLT5tc2cpOworCX0gZWxzZSB7CisJCWVkYWNfbWNfaGFuZGxlX2Vy cm9yKEhXX0VWRU5UX0VSUl9VTkNPUlJFQ1RFRCwgbWNpLAorCQkJCSAgICAgMSwJLyogZXJyb3Ig Y291bnQgKi8KKwkJCQkgICAgIGFkZHJfdmFsID4+IFBBR0VfU0hJRlQsCisJCQkJICAgICBhZGRy X3ZhbCAmIH5QQUdFX01BU0ssCisJCQkJICAgICBzeW5kcm9tZV92YWwsCisJCQkJICAgICBjc192 YWwsIC0xLCAtMSwgLyogdG9wLCBtaWQsIGxvdyBsYXllciAqLworCQkJCSAgICAgbWNpLT5jdGxf bmFtZSwgZHJ2ZGF0YS0+bXNnKTsKKwl9Cit9CisKK3N0YXRpYyB2b2lkIGF4cF9tY19yZWFkX2Nv bmZpZyhzdHJ1Y3QgbWVtX2N0bF9pbmZvICptY2kpCit7CisJc3RydWN0IGF4cF9tY19kcnZkYXRh ICpkcnZkYXRhID0gbWNpLT5wdnRfaW5mbzsKKwl1aW50MzJfdCBjb25maWcsIGFkZHJfY3RybCwg cmFua19jdHJsOworCXVuc2lnbmVkIGludCBpLCBjc19zdHJ1Y3QsIGNzX3NpemU7CisJc3RydWN0 IGRpbW1faW5mbyAqZGltbTsKKworCWNvbmZpZyA9IHJlYWRsKGRydmRhdGEtPmJhc2UgKyBTRFJB TV9DT05GSUdfUkVHKTsKKwlpZiAoY29uZmlnICYgU0RSQU1fQ09ORklHX0JVU19XSURUSF9NQVNL KQorCQkvKiA2NCBiaXQgKi8KKwkJZHJ2ZGF0YS0+d2lkdGggPSA4OworCWVsc2UKKwkJLyogMzIg Yml0ICovCisJCWRydmRhdGEtPndpZHRoID0gNDsKKworCWFkZHJfY3RybCA9IHJlYWRsKGRydmRh dGEtPmJhc2UgKyBTRFJBTV9BRERSX0NUUkxfUkVHKTsKKwlyYW5rX2N0cmwgPSByZWFkbChkcnZk YXRhLT5iYXNlICsgU0RSQU1fUkFOS19DVFJMX1JFRyk7CisJZm9yIChpID0gMDsgaSA8IFNEUkFN X05VTV9DUzsgaSsrKSB7CisJCWRpbW0gPSBtY2ktPmRpbW1zW2ldOworCisJCWlmICghKHJhbmtf Y3RybCAmIFNEUkFNX1JBTktfQ1RSTF9FWElTVF9NQVNLKGkpKSkKKwkJCWNvbnRpbnVlOworCisJ CWRydmRhdGEtPmNzX2FkZHJfc2VsW2ldID0KKwkJCSEhKGFkZHJfY3RybCAmIFNEUkFNX0FERFJf Q1RSTF9BRERSX1NFTF9NQVNLKGkpKTsKKworCQljc19zdHJ1Y3QgPSAoYWRkcl9jdHJsICYgU0RS QU1fQUREUl9DVFJMX1NUUlVDVF9NQVNLKGkpKSA+PiBTRFJBTV9BRERSX0NUUkxfU1RSVUNUX09G RlNFVChpKTsKKwkJY3Nfc2l6ZSAgID0gKChhZGRyX2N0cmwgJiBTRFJBTV9BRERSX0NUUkxfU0la RV9ISUdIX01BU0soaSkpID4+IChTRFJBTV9BRERSX0NUUkxfU0laRV9ISUdIX09GRlNFVChpKSAt IDIpIHwKKwkJCSAgICAoKGFkZHJfY3RybCAmIFNEUkFNX0FERFJfQ1RSTF9TSVpFX0xPV19NQVNL KGkpKSA+PiBTRFJBTV9BRERSX0NUUkxfU0laRV9MT1dfT0ZGU0VUKGkpKSk7CisKKwkJc3dpdGNo IChjc19zaXplKSB7CisJCWNhc2UgMDogLyogMkdCaXQgKi8KKwkJCWRpbW0tPm5yX3BhZ2VzID0g NTI0Mjg4OworCQkJYnJlYWs7CisJCWNhc2UgMTogLyogMjU2TUJpdCAqLworCQkJZGltbS0+bnJf cGFnZXMgPSA2NTUzNjsKKwkJCWJyZWFrOworCQljYXNlIDI6IC8qIDUxMk1CaXQgKi8KKwkJCWRp bW0tPm5yX3BhZ2VzID0gMTMxMDcyOworCQkJYnJlYWs7CisJCWNhc2UgMzogLyogMUdCaXQgKi8K KwkJCWRpbW0tPm5yX3BhZ2VzID0gMjYyMTQ0OworCQkJYnJlYWs7CisJCWNhc2UgNDogLyogNEdC aXQgKi8KKwkJCWRpbW0tPm5yX3BhZ2VzID0gMTA0ODU3NjsKKwkJCWJyZWFrOworCQljYXNlIDU6 IC8qIDhHQml0ICovCisJCQlkaW1tLT5ucl9wYWdlcyA9IDIwOTcxNTI7CisJCQlicmVhazsKKwkJ fQorCQlkaW1tLT5ncmFpbiA9IDg7CisJCWRpbW0tPmR0eXBlID0gY3Nfc3RydWN0ID8gREVWX1gx NiA6IERFVl9YODsKKwkJZGltbS0+bXR5cGUgPSAoY29uZmlnICYgU0RSQU1fQ09ORklHX1JFR0lT VEVSRURfTUFTSykgPworCQkJTUVNX1JERFIzIDogTUVNX0REUjM7CisJCWRpbW0tPmVkYWNfbW9k ZSA9IEVEQUNfU0VDREVEOworCX0KK30KKworc3RhdGljIGNvbnN0IHN0cnVjdCBvZl9kZXZpY2Vf aWQgYXhwX21jX29mX21hdGNoW10gPSB7CisJey5jb21wYXRpYmxlID0gIm1hcnZlbGwsYXJtYWRh LXhwLXNkcmFtLWNvbnRyb2xsZXIiLH0sCisJe30sCit9OworTU9EVUxFX0RFVklDRV9UQUJMRShv ZiwgYXhwX21jX29mX21hdGNoKTsKKworc3RhdGljIGludCBheHBfbWNfcHJvYmUoc3RydWN0IHBs YXRmb3JtX2RldmljZSAqcGRldikKK3sKKwlzdHJ1Y3QgYXhwX21jX2RydmRhdGEgKmRydmRhdGE7 CisJc3RydWN0IGVkYWNfbWNfbGF5ZXIgbGF5ZXJzWzFdOworCWNvbnN0IHN0cnVjdCBvZl9kZXZp Y2VfaWQgKmlkOworCXN0cnVjdCBtZW1fY3RsX2luZm8gKm1jaTsKKwlzdHJ1Y3QgcmVzb3VyY2Ug KnI7CisJdm9pZCBfX2lvbWVtICpiYXNlOworCXVpbnQzMl90IGNvbmZpZzsKKworCXIgPSBwbGF0 Zm9ybV9nZXRfcmVzb3VyY2UocGRldiwgSU9SRVNPVVJDRV9NRU0sIDApOworCWlmICghcikgewor CQlkZXZfZXJyKCZwZGV2LT5kZXYsICJVbmFibGUgdG8gZ2V0IG1lbSByZXNvdXJjZVxuIik7CisJ CXJldHVybiAtRU5PREVWOworCX0KKworCWJhc2UgPSBkZXZtX2lvcmVtYXBfcmVzb3VyY2UoJnBk ZXYtPmRldiwgcik7CisJaWYgKElTX0VSUihiYXNlKSkgeworCQlkZXZfZXJyKCZwZGV2LT5kZXYs ICJVbmFibGUgdG8gbWFwIHJlZ3NcbiIpOworCQlyZXR1cm4gUFRSX0VSUihiYXNlKTsKKwl9CisK Kwljb25maWcgPSByZWFkbChiYXNlICsgU0RSQU1fQ09ORklHX1JFRyk7CisJaWYgKCEoY29uZmln ICYgU0RSQU1fQ09ORklHX0VDQ19NQVNLKSkgeworCQlkZXZfd2FybigmcGRldi0+ZGV2LCAiU0RS QU0gRUNDIGlzIG5vdCBlbmFibGVkIik7CisJCXJldHVybiAtRUlOVkFMOworCX0KKworCWxheWVy c1swXS50eXBlID0gRURBQ19NQ19MQVlFUl9DSElQX1NFTEVDVDsKKwlsYXllcnNbMF0uc2l6ZSA9 IFNEUkFNX05VTV9DUzsKKwlsYXllcnNbMF0uaXNfdmlydF9jc3JvdyA9IHRydWU7CisKKwltY2kg PSBlZGFjX21jX2FsbG9jKDAsIEFSUkFZX1NJWkUobGF5ZXJzKSwgbGF5ZXJzLCBzaXplb2YoKmRy dmRhdGEpKTsKKwlpZiAoIW1jaSkKKwkJcmV0dXJuIC1FTk9NRU07CisKKwlkcnZkYXRhID0gbWNp LT5wdnRfaW5mbzsKKwlkcnZkYXRhLT5iYXNlID0gYmFzZTsKKwltY2ktPnBkZXYgPSAmcGRldi0+ ZGV2OworCXBsYXRmb3JtX3NldF9kcnZkYXRhKHBkZXYsIG1jaSk7CisKKwlpZCA9IG9mX21hdGNo X2RldmljZShheHBfbWNfb2ZfbWF0Y2gsICZwZGV2LT5kZXYpOworCW1jaS0+ZWRhY19jaGVjayA9 IGF4cF9tY19jaGVjazsKKwltY2ktPm10eXBlX2NhcCA9IE1FTV9GTEFHX0REUjM7CisJbWNpLT5l ZGFjX2NhcCA9IEVEQUNfRkxBR19TRUNERUQ7CisJbWNpLT5tb2RfbmFtZSA9IHBkZXYtPmRldi5k cml2ZXItPm5hbWU7CisJbWNpLT5jdGxfbmFtZSA9IGlkID8gaWQtPmNvbXBhdGlibGUgOiAidW5r bm93biI7CisJbWNpLT5kZXZfbmFtZSA9IGRldl9uYW1lKCZwZGV2LT5kZXYpOworCW1jaS0+c2Ny dWJfbW9kZSA9IFNDUlVCX05PTkU7CisKKwlheHBfbWNfcmVhZF9jb25maWcobWNpKTsKKworCS8q IGNvbmZpZ3VyZSBTQkUgdGhyZXNob2xkICovCisJLyogaXQgc2VlbXMgdGhhdCBTQkVzIGFyZSBu b3QgY2FwdHVyZWQgb3RoZXJ3aXNlICovCisJd3JpdGVsKDEgPDwgU0RSQU1fRVJSX0NUUkxfVEhS X09GRlNFVCwgZHJ2ZGF0YS0+YmFzZSArIFNEUkFNX0VSUl9DVFJMX1JFRyk7CisKKwkvKiBjbGVh ciBjYXVzZSByZWdpc3RlcnMgKi8KKwl3cml0ZWwofihTRFJBTV9FUlJfQ0FVU0VfREJFX01BU0sg fCBTRFJBTV9FUlJfQ0FVU0VfU0JFX01BU0spLCBkcnZkYXRhLT5iYXNlICsgU0RSQU1fRVJSX0NB VVNFX0VSUl9SRUcpOworCXdyaXRlbCh+KFNEUkFNX0VSUl9DQVVTRV9EQkVfTUFTSyB8IFNEUkFN X0VSUl9DQVVTRV9TQkVfTUFTSyksIGRydmRhdGEtPmJhc2UgKyBTRFJBTV9FUlJfQ0FVU0VfTVNH X1JFRyk7CisKKwkvKiBjbGVhciBjb3VudGVyIHJlZ2lzdGVycyAqLworCXdyaXRlbCgwLCBkcnZk YXRhLT5iYXNlICsgU0RSQU1fRVJSX1NCRV9DT1VOVF9SRUcpOworCXdyaXRlbCgwLCBkcnZkYXRh LT5iYXNlICsgU0RSQU1fRVJSX0RCRV9DT1VOVF9SRUcpOworCisJaWYgKGVkYWNfbWNfYWRkX21j KG1jaSkpIHsKKwkJZWRhY19tY19mcmVlKG1jaSk7CisJCXJldHVybiAtRUlOVkFMOworCX0KKwll ZGFjX29wX3N0YXRlID0gRURBQ19PUFNUQVRFX1BPTEw7CisKKwlyZXR1cm4gMDsKK30KKworc3Rh dGljIGludCBheHBfbWNfcmVtb3ZlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpCit7CisJ c3RydWN0IG1lbV9jdGxfaW5mbyAqbWNpID0gcGxhdGZvcm1fZ2V0X2RydmRhdGEocGRldik7CisK KwllZGFjX21jX2RlbF9tYygmcGRldi0+ZGV2KTsKKwllZGFjX21jX2ZyZWUobWNpKTsKKwlwbGF0 Zm9ybV9zZXRfZHJ2ZGF0YShwZGV2LCBOVUxMKTsKKworCXJldHVybiAwOworfQorCitzdGF0aWMg c3RydWN0IHBsYXRmb3JtX2RyaXZlciBheHBfbWNfZHJpdmVyID0geworCS5wcm9iZSA9IGF4cF9t Y19wcm9iZSwKKwkucmVtb3ZlID0gYXhwX21jX3JlbW92ZSwKKwkuZHJpdmVyID0geworCQkubmFt ZSA9ICJhcm1hZGFfeHBfbWNfZWRhYyIsCisJCS5vZl9tYXRjaF90YWJsZSA9IG9mX21hdGNoX3B0 cihheHBfbWNfb2ZfbWF0Y2gpLAorCX0sCit9OworCisvKioqKioqKioqKioqKioqKioqKioqKioq IEVEQUMgRGV2aWNlIChMMiBDYWNoZSkgKioqKioqKioqKioqKioqKioqKioqKioqKioqLworCitz dHJ1Y3QgYXVyb3JhX2wyX2RydmRhdGEgeworCXZvaWQgX19pb21lbSAqYmFzZTsKKworCWNoYXIg bXNnWzEyOF07CisKKwkvKiBlcnJvciBpbmplY3Rpb24gdmlhIGRlYnVnZnMgKi8KKwl1aW50MzJf dCBpbmplY3RfYWRkcjsKKwl1aW50MzJfdCBpbmplY3RfbWFzazsKKwl1aW50OF90IGluamVjdF9j dGw7CisKKwlzdHJ1Y3QgZGVudHJ5ICpkZWJ1Z2ZzOworfTsKKworI2lmZGVmIENPTkZJR19FREFD X0RFQlVHCitzdGF0aWMgdm9pZCBhdXJvcmFfbDJfaW5qZWN0KHN0cnVjdCBhdXJvcmFfbDJfZHJ2 ZGF0YSAqZHJ2ZGF0YSkKK3sKKwlkcnZkYXRhLT5pbmplY3RfYWRkciAmPSBBVVJPUkFfRVJSX0lO SkVDVF9DVExfQUREUl9NQVNLOworCWRydmRhdGEtPmluamVjdF9jdGwgJj0gQVVST1JBX0VSUl9J TkpFQ1RfQ1RMX0VOX01BU0s7CisJd3JpdGVsKDAsIGRydmRhdGEtPmJhc2UgKyBBVVJPUkFfRVJS X0lOSkVDVF9DVExfUkVHKTsKKwl3cml0ZWwoZHJ2ZGF0YS0+aW5qZWN0X21hc2ssIGRydmRhdGEt PmJhc2UgKyBBVVJPUkFfRVJSX0lOSkVDVF9NQVNLX1JFRyk7CisJd3JpdGVsKGRydmRhdGEtPmlu amVjdF9hZGRyIHwgZHJ2ZGF0YS0+aW5qZWN0X2N0bCwgZHJ2ZGF0YS0+YmFzZSArIEFVUk9SQV9F UlJfSU5KRUNUX0NUTF9SRUcpOworfQorI2VuZGlmCisKK3N0YXRpYyB2b2lkIGF1cm9yYV9sMl9j aGVjayhzdHJ1Y3QgZWRhY19kZXZpY2VfY3RsX2luZm8gKmRjaSkKK3sKKwlzdHJ1Y3QgYXVyb3Jh X2wyX2RydmRhdGEgKmRydmRhdGEgPSBkY2ktPnB2dF9pbmZvOworCXVpbnQzMl90IGNudCwgc3Jj LCB0eG4sIGVyciwgYXR0cl9jYXAsIGFkZHJfY2FwLCB3YXlfY2FwOworCXVuc2lnbmVkIGludCBj bnRfY2UsIGNudF91ZTsKKwljaGFyICptc2cgPSBkcnZkYXRhLT5tc2c7CisJc2l6ZV90IHNpemUg PSBzaXplb2YoZHJ2ZGF0YS0+bXNnKTsKKwlzaXplX3QgbGVuID0gMDsKKworCWNudCA9IHJlYWRs KGRydmRhdGEtPmJhc2UgKyBBVVJPUkFfRVJSX0NOVF9SRUcpOworCWF0dHJfY2FwID0gcmVhZGwo ZHJ2ZGF0YS0+YmFzZSArIEFVUk9SQV9FUlJfQVRUUl9DQVBfUkVHKTsKKwlhZGRyX2NhcCA9IHJl YWRsKGRydmRhdGEtPmJhc2UgKyBBVVJPUkFfRVJSX0FERFJfQ0FQX1JFRyk7CisJd2F5X2NhcCA9 IHJlYWRsKGRydmRhdGEtPmJhc2UgKyBBVVJPUkFfRVJSX1dBWV9DQVBfUkVHKTsKKworCWNudF9j ZSA9IChjbnQgJiBBVVJPUkFfRVJSX0NOVF9DRV9NQVNLKSA+PiBBVVJPUkFfRVJSX0NOVF9DRV9P RkZTRVQ7CisJY250X3VlID0gKGNudCAmIEFVUk9SQV9FUlJfQ05UX1VFX01BU0spID4+IEFVUk9S QV9FUlJfQ05UX1VFX09GRlNFVDsKKwkvKiBjbGVhciBlcnJvciBjb3VudGVyIHJlZ2lzdGVycyAq LworCWlmIChjbnRfY2UgfHwgY250X3VlKQorCQl3cml0ZWwoQVVST1JBX0VSUl9DTlRfQ0xSLCBk cnZkYXRhLT5iYXNlICsgQVVST1JBX0VSUl9DTlRfUkVHKTsKKworCWlmICghKGF0dHJfY2FwICYg QVVST1JBX0VSUl9BVFRSX0NBUF9WQUxJRCkpCisJCWdvdG8gY2xlYXJfcmVtYWluaW5nOworCisJ c3JjID0gKGF0dHJfY2FwICYgQVVST1JBX0VSUl9BVFRSX1NSQ19NU0spID4+IEFVUk9SQV9FUlJf QVRUUl9TUkNfT0ZGOworCWlmIChzcmMgPD0gMykKKwkJbGVuICs9IHNucHJpbnRmKG1zZytsZW4s IHNpemUtbGVuLCAic3JjPUNQVSVkICIsIHNyYyk7CisJZWxzZQorCQlsZW4gKz0gc25wcmludGYo bXNnK2xlbiwgc2l6ZS1sZW4sICJzcmM9SU8gIik7CisKKwl0eG4gPSAgKGF0dHJfY2FwICYgQVVS T1JBX0VSUl9BVFRSX1RYTl9NU0spID4+IEFVUk9SQV9FUlJfQVRUUl9UWE5fT0ZGOworCXN3aXRj aCAodHhuKSB7CisJY2FzZSAwOgorCQlsZW4gKz0gc25wcmludGYobXNnK2xlbiwgc2l6ZS1sZW4s ICJ0eG49RGF0YS1SZWFkICIpOworCQlicmVhazsKKwljYXNlIDE6CisJCWxlbiArPSBzbnByaW50 Zihtc2crbGVuLCBzaXplLWxlbiwgInR4bj1Jc24tUmVhZCAiKTsKKwkJYnJlYWs7CisJY2FzZSAy OgorCQlsZW4gKz0gc25wcmludGYobXNnK2xlbiwgc2l6ZS1sZW4sICJ0eG49Q2xlYW4tRmx1c2gg Iik7CisJCWJyZWFrOworCWNhc2UgMzoKKwkJbGVuICs9IHNucHJpbnRmKG1zZytsZW4sIHNpemUt bGVuLCAidHhuPUV2aWN0aW9uICIpOworCQlicmVhazsKKwljYXNlIDQ6CisJCWxlbiArPSBzbnBy aW50Zihtc2crbGVuLCBzaXplLWxlbiwKKwkJCQkidHhuPVJlYWQtTW9kaWZ5LVdyaXRlICIpOwor CQlicmVhazsKKwl9CisKKwllcnIgPSAoYXR0cl9jYXAgJiBBVVJPUkFfRVJSX0FUVFJfRVJSX01T SykgPj4gQVVST1JBX0VSUl9BVFRSX0VSUl9PRkY7CisJc3dpdGNoIChlcnIpIHsKKwljYXNlIDA6 CisJCWxlbiArPSBzbnByaW50Zihtc2crbGVuLCBzaXplLWxlbiwgImVycj1Db3JyRUNDICIpOwor CQlicmVhazsKKwljYXNlIDE6CisJCWxlbiArPSBzbnByaW50Zihtc2crbGVuLCBzaXplLWxlbiwg ImVycj1VbkNvcnJFQ0MgIik7CisJCWJyZWFrOworCWNhc2UgMjoKKwkJbGVuICs9IHNucHJpbnRm KG1zZytsZW4sIHNpemUtbGVuLCAiZXJyPVRhZ1Bhcml0eSAiKTsKKwkJYnJlYWs7CisJfQorCisJ bGVuICs9IHNucHJpbnRmKG1zZytsZW4sIHNpemUtbGVuLCAiYWRkcj0weCV4ICIsIGFkZHJfY2Fw ICYgQVVST1JBX0VSUl9BRERSX0NBUF9BRERSX01BU0spOworCWxlbiArPSBzbnByaW50Zihtc2cr bGVuLCBzaXplLWxlbiwgImluZGV4PTB4JXggIiwgKHdheV9jYXAgJiBBVVJPUkFfRVJSX1dBWV9J RFhfTVNLKSA+PiBBVVJPUkFfRVJSX1dBWV9JRFhfT0ZGKTsKKwlsZW4gKz0gc25wcmludGYobXNn K2xlbiwgc2l6ZS1sZW4sICJ3YXk9MHgleCIsICh3YXlfY2FwICYgQVVST1JBX0VSUl9XQVlfQ0FQ X1dBWV9NQVNLKSA+PiBBVVJPUkFfRVJSX1dBWV9DQVBfV0FZX09GRlNFVCk7CisKKwkvKiBjbGVh ciBlcnJvciBjYXB0dXJlIHJlZ2lzdGVycyAqLworCXdyaXRlbChBVVJPUkFfRVJSX0FUVFJfQ0FQ X1ZBTElELCBkcnZkYXRhLT5iYXNlICsgQVVST1JBX0VSUl9BVFRSX0NBUF9SRUcpOworCWlmIChl cnIpIHsKKwkJLyogVW5Db3JyRUNDIG9yIFRhZ1Bhcml0eSAqLworCQlpZiAoY250X3VlKQorCQkJ Y250X3VlLS07CisJCWVkYWNfZGV2aWNlX2hhbmRsZV91ZShkY2ksIDAsIDAsIGRydmRhdGEtPm1z Zyk7CisJfSBlbHNlIHsKKwkJaWYgKGNudF9jZSkKKwkJCWNudF9jZS0tOworCQllZGFjX2Rldmlj ZV9oYW5kbGVfY2UoZGNpLCAwLCAwLCBkcnZkYXRhLT5tc2cpOworCX0KKworY2xlYXJfcmVtYWlu aW5nOgorCS8qIHJlcG9ydCByZW1haW5pbmcgZXJyb3JzICovCisJd2hpbGUgKGNudF91ZS0tKQor CQllZGFjX2RldmljZV9oYW5kbGVfdWUoZGNpLCAwLCAwLCAiZGV0YWlscyB1bmF2YWlsYWJsZSAo bXVsdGlwbGUgZXJyb3JzKSIpOworCXdoaWxlIChjbnRfY2UtLSkKKwkJZWRhY19kZXZpY2VfaGFu ZGxlX3VlKGRjaSwgMCwgMCwgImRldGFpbHMgdW5hdmFpbGFibGUgKG11bHRpcGxlIGVycm9ycyki KTsKK30KKworc3RhdGljIHZvaWQgYXVyb3JhX2wyX3BvbGwoc3RydWN0IGVkYWNfZGV2aWNlX2N0 bF9pbmZvICpkY2kpCit7CisjaWZkZWYgQ09ORklHX0VEQUNfREVCVUcKKwlzdHJ1Y3QgYXVyb3Jh X2wyX2RydmRhdGEgKmRydmRhdGEgPSBkY2ktPnB2dF9pbmZvOworI2VuZGlmCisKKwlhdXJvcmFf bDJfY2hlY2soZGNpKTsKKyNpZmRlZiBDT05GSUdfRURBQ19ERUJVRworCWF1cm9yYV9sMl9pbmpl Y3QoZHJ2ZGF0YSk7CisjZW5kaWYKK30KKworc3RhdGljIGNvbnN0IHN0cnVjdCBvZl9kZXZpY2Vf aWQgYXVyb3JhX2wyX29mX21hdGNoW10gPSB7CisJey5jb21wYXRpYmxlID0gIm1hcnZlbGwsYXVy b3JhLXN5c3RlbS1jYWNoZSIsfSwKKwl7fSwKK307CitNT0RVTEVfREVWSUNFX1RBQkxFKG9mLCBh dXJvcmFfbDJfb2ZfbWF0Y2gpOworCitzdGF0aWMgaW50IGF1cm9yYV9sMl9wcm9iZShzdHJ1Y3Qg cGxhdGZvcm1fZGV2aWNlICpwZGV2KQoreworCXN0cnVjdCBhdXJvcmFfbDJfZHJ2ZGF0YSAqZHJ2 ZGF0YTsKKwlzdHJ1Y3QgZWRhY19kZXZpY2VfY3RsX2luZm8gKmRjaTsKKwljb25zdCBzdHJ1Y3Qg b2ZfZGV2aWNlX2lkICppZDsKKwl1aW50MzJfdCBsMngwX2F1eF9jdHJsOworCXZvaWQgX19pb21l bSAqYmFzZTsKKwlzdHJ1Y3QgcmVzb3VyY2UgKnI7CisKKwlyID0gcGxhdGZvcm1fZ2V0X3Jlc291 cmNlKHBkZXYsIElPUkVTT1VSQ0VfTUVNLCAwKTsKKwlpZiAoIXIpIHsKKwkJZGV2X2VycigmcGRl di0+ZGV2LCAiVW5hYmxlIHRvIGdldCBtZW0gcmVzb3VyY2VcbiIpOworCQlyZXR1cm4gLUVOT0RF VjsKKwl9CisKKwliYXNlID0gZGV2bV9pb3JlbWFwX3Jlc291cmNlKCZwZGV2LT5kZXYsIHIpOwor CWlmIChJU19FUlIoYmFzZSkpIHsKKwkJZGV2X2VycigmcGRldi0+ZGV2LCAiVW5hYmxlIHRvIG1h cCByZWdzXG4iKTsKKwkJcmV0dXJuIFBUUl9FUlIoYmFzZSk7CisJfQorCisJbDJ4MF9hdXhfY3Ry bCA9IHJlYWRsKGJhc2UgKyBMMlgwX0FVWF9DVFJMKTsKKwlpZiAoIShsMngwX2F1eF9jdHJsICYg QVVST1JBX0FDUl9QQVJJVFlfRU4pKQorCQlkZXZfd2FybigmcGRldi0+ZGV2LCAidGFnIHBhcml0 eSBpcyBub3QgZW5hYmxlZCIpOworCWlmICghKGwyeDBfYXV4X2N0cmwgJiBBVVJPUkFfQUNSX0VD Q19FTikpCisJCWRldl93YXJuKCZwZGV2LT5kZXYsICJkYXRhIEVDQyBpcyBub3QgZW5hYmxlZCIp OworCisJZGNpID0gZWRhY19kZXZpY2VfYWxsb2NfY3RsX2luZm8oc2l6ZW9mKCpkcnZkYXRhKSwK KwkJCQkJICJjcHUiLCAxLCAiTCIsIDEsIDIsIE5VTEwsIDAsIDApOworCWlmICghZGNpKQorCQly ZXR1cm4gLUVOT01FTTsKKworCWRydmRhdGEgPSBkY2ktPnB2dF9pbmZvOworCWRydmRhdGEtPmJh c2UgPSBiYXNlOworCWRjaS0+ZGV2ID0gJnBkZXYtPmRldjsKKwlwbGF0Zm9ybV9zZXRfZHJ2ZGF0 YShwZGV2LCBkY2kpOworCisJaWQgPSBvZl9tYXRjaF9kZXZpY2UoYXVyb3JhX2wyX29mX21hdGNo LCAmcGRldi0+ZGV2KTsKKwlkY2ktPmVkYWNfY2hlY2sgPSBhdXJvcmFfbDJfcG9sbDsKKwlkY2kt Pm1vZF9uYW1lID0gcGRldi0+ZGV2LmRyaXZlci0+bmFtZTsKKwlkY2ktPmN0bF9uYW1lID0gaWQg PyBpZC0+Y29tcGF0aWJsZSA6ICJ1bmtub3duIjsKKwlkY2ktPmRldl9uYW1lID0gZGV2X25hbWUo JnBkZXYtPmRldik7CisKKwkvKiBjbGVhciByZWdpc3RlcnMgKi8KKwl3cml0ZWwoQVVST1JBX0VS Ul9DTlRfQ0xSLCBkcnZkYXRhLT5iYXNlICsgQVVST1JBX0VSUl9DTlRfUkVHKTsKKwl3cml0ZWwo QVVST1JBX0VSUl9BVFRSX0NBUF9WQUxJRCwgZHJ2ZGF0YS0+YmFzZSArIEFVUk9SQV9FUlJfQVRU Ul9DQVBfUkVHKTsKKworCWlmIChlZGFjX2RldmljZV9hZGRfZGV2aWNlKGRjaSkpIHsKKwkJZWRh Y19kZXZpY2VfZnJlZV9jdGxfaW5mbyhkY2kpOworCQlyZXR1cm4gLUVJTlZBTDsKKwl9CisKKyNp ZmRlZiBDT05GSUdfRURBQ19ERUJVRworCWRydmRhdGEtPmRlYnVnZnMgPSBlZGFjX2RlYnVnZnNf Y3JlYXRlX2RpcihkZXZfbmFtZSgmcGRldi0+ZGV2KSk7CisJaWYgKGRydmRhdGEtPmRlYnVnZnMp IHsKKwkJZWRhY19kZWJ1Z2ZzX2NyZWF0ZV94MzIoImluamVjdF9hZGRyIiwgMDY0NCwKKwkJCQkJ ZHJ2ZGF0YS0+ZGVidWdmcywKKwkJCQkJJmRydmRhdGEtPmluamVjdF9hZGRyKTsKKwkJZWRhY19k ZWJ1Z2ZzX2NyZWF0ZV94MzIoImluamVjdF9tYXNrIiwgMDY0NCwKKwkJCQkJZHJ2ZGF0YS0+ZGVi dWdmcywKKwkJCQkJJmRydmRhdGEtPmluamVjdF9tYXNrKTsKKwkJZWRhY19kZWJ1Z2ZzX2NyZWF0 ZV94OCgiaW5qZWN0X2N0bCIsIDA2NDQsCisJCQkJICAgICAgIGRydmRhdGEtPmRlYnVnZnMsICZk cnZkYXRhLT5pbmplY3RfY3RsKTsKKwl9CisjZW5kaWYKKworCXJldHVybiAwOworfQorCitzdGF0 aWMgaW50IGF1cm9yYV9sMl9yZW1vdmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikKK3sK KwlzdHJ1Y3QgZWRhY19kZXZpY2VfY3RsX2luZm8gKmRjaSA9IHBsYXRmb3JtX2dldF9kcnZkYXRh KHBkZXYpOworI2lmZGVmIENPTkZJR19FREFDX0RFQlVHCisJc3RydWN0IGF1cm9yYV9sMl9kcnZk YXRhICpkcnZkYXRhID0gZGNpLT5wdnRfaW5mbzsKKworCWVkYWNfZGVidWdmc19yZW1vdmVfcmVj dXJzaXZlKGRydmRhdGEtPmRlYnVnZnMpOworI2VuZGlmCisJZWRhY19kZXZpY2VfZGVsX2Rldmlj ZSgmcGRldi0+ZGV2KTsKKwllZGFjX2RldmljZV9mcmVlX2N0bF9pbmZvKGRjaSk7CisJcGxhdGZv cm1fc2V0X2RydmRhdGEocGRldiwgTlVMTCk7CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIHN0 cnVjdCBwbGF0Zm9ybV9kcml2ZXIgYXVyb3JhX2wyX2RyaXZlciA9IHsKKwkucHJvYmUgPSBhdXJv cmFfbDJfcHJvYmUsCisJLnJlbW92ZSA9IGF1cm9yYV9sMl9yZW1vdmUsCisJLmRyaXZlciA9IHsK KwkJLm5hbWUgPSAiYXVyb3JhX2wyX2VkYWMiLAorCQkub2ZfbWF0Y2hfdGFibGUgPSBvZl9tYXRj aF9wdHIoYXVyb3JhX2wyX29mX21hdGNoKSwKKwl9LAorfTsKKworLyoqKioqKioqKioqKioqKioq KioqKioqKiBEcml2ZXIgcmVnaXN0cmF0aW9uICoqKioqKioqKioqKioqKioqKioqKioqKioqKioq Ki8KKworc3RhdGljIHN0cnVjdCBwbGF0Zm9ybV9kcml2ZXIgKiBjb25zdCBkcml2ZXJzW10gPSB7 CisJJmF4cF9tY19kcml2ZXIsCisJJmF1cm9yYV9sMl9kcml2ZXIsCit9OworCitzdGF0aWMgaW50 IF9faW5pdCBhcm1hZGFfeHBfZWRhY19pbml0KHZvaWQpCit7CisJaW50IHJlczsKKworCS8qIG9u bHkgcG9sbGluZyBpcyBzdXBwb3J0ZWQgKi8KKwllZGFjX29wX3N0YXRlID0gRURBQ19PUFNUQVRF X1BPTEw7CisKKwlyZXMgPSBwbGF0Zm9ybV9yZWdpc3Rlcl9kcml2ZXJzKGRyaXZlcnMsIEFSUkFZ X1NJWkUoZHJpdmVycykpOworCWlmIChyZXMpCisJCXByX3dhcm4oIkFyYW1kYSBYUCBFREFDIGRy aXZlcnMgZmFpbCB0byByZWdpc3RlclxuIik7CisKKwlyZXR1cm4gMDsKK30KK21vZHVsZV9pbml0 KGFybWFkYV94cF9lZGFjX2luaXQpOworCitzdGF0aWMgdm9pZCBfX2V4aXQgYXJtYWRhX3hwX2Vk YWNfZXhpdCh2b2lkKQoreworCXBsYXRmb3JtX3VucmVnaXN0ZXJfZHJpdmVycyhkcml2ZXJzLCBB UlJBWV9TSVpFKGRyaXZlcnMpKTsKK30KK21vZHVsZV9leGl0KGFybWFkYV94cF9lZGFjX2V4aXQp OworCitNT0RVTEVfTElDRU5TRSgiR1BMIHYyIik7CitNT0RVTEVfQVVUSE9SKCJQZW5ndXRyb25p eCIpOworTU9EVUxFX0RFU0NSSVBUSU9OKCJFREFDIERyaXZlcnMgZm9yIE1hcnZlbGwgQXJtYWRh IFhQIFNEUkFNIGFuZCBMMiBDYWNoZSBDb250cm9sbGVyIik7Cg== From mboxrd@z Thu Jan 1 00:00:00 1970 From: chris.packham@alliedtelesis.co.nz (Chris Packham) Date: Fri, 9 Nov 2018 20:03:48 +1300 Subject: [PATCH v6 8/9] EDAC: Add driver for the Marvell Armada XP SDRAM and L2 cache ECC In-Reply-To: <20181109070349.20464-1-chris.packham@alliedtelesis.co.nz> References: <20181109070349.20464-1-chris.packham@alliedtelesis.co.nz> Message-ID: <20181109070349.20464-9-chris.packham@alliedtelesis.co.nz> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org From: Jan Luebbe Add support for the ECC functionality as found in the DDR RAM and L2 cache controllers on the MV78230/MV78x60 SoCs. This driver has been tested on the MV78460 (on a custom board with a DDR3 ECC DIMM). Signed-off-by: Jan Luebbe [cp use SPDX license] Signed-off-by: Chris Packham --- MAINTAINERS | 6 + drivers/edac/Kconfig | 7 + drivers/edac/Makefile | 1 + drivers/edac/armada_xp_edac.c | 630 ++++++++++++++++++++++++++++++++++ 4 files changed, 644 insertions(+) create mode 100644 drivers/edac/armada_xp_edac.c diff --git a/MAINTAINERS b/MAINTAINERS index 6c7ed26e84fa..7ae4cfa5c121 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5242,6 +5242,12 @@ L: linux-edac at vger.kernel.org S: Maintained F: drivers/edac/amd64_edac* +EDAC-ARMADA +M: Jan Luebbe +L: linux-edac at vger.kernel.org +S: Maintained +F: drivers/edac/armada_xp_* + EDAC-CALXEDA M: Robert Richter L: linux-edac at vger.kernel.org diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 57304b2e989f..4567757d9f82 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -439,6 +439,13 @@ config EDAC_ALTERA_SDMMC Support for error detection and correction on the Altera SDMMC FIFO Memory for Altera SoCs. +config EDAC_ARMADA_XP + bool "Marvell Armada XP DDR and L2 Cache ECC" + depends on MACH_MVEBU_V7 + help + Support for error correction and detection on the Marvell Aramada XP + DDR RAM and L2 cache controllers. + config EDAC_SYNOPSYS tristate "Synopsys DDR Memory Controller" depends on ARCH_ZYNQ diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 02b43a7d8c3e..f3ea40b0ce9c 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o obj-$(CONFIG_EDAC_THUNDERX) += thunderx_edac.o obj-$(CONFIG_EDAC_ALTERA) += altera_edac.o +obj-$(CONFIG_EDAC_ARMADA_XP) += armada_xp_edac.o obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o obj-$(CONFIG_EDAC_TI) += ti_edac.o diff --git a/drivers/edac/armada_xp_edac.c b/drivers/edac/armada_xp_edac.c new file mode 100644 index 000000000000..3759a4fbbdee --- /dev/null +++ b/drivers/edac/armada_xp_edac.c @@ -0,0 +1,630 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017 Pengutronix, Jan Luebbe + */ + +#include +#include +#include + +#include +#include + +#include "edac_mc.h" +#include "edac_device.h" +#include "edac_module.h" + +/************************ EDAC MC (DDR RAM) ********************************/ + +#define SDRAM_NUM_CS 4 + +#define SDRAM_CONFIG_REG 0x0 +#define SDRAM_CONFIG_ECC_MASK BIT(18) +#define SDRAM_CONFIG_REGISTERED_MASK BIT(17) +#define SDRAM_CONFIG_BUS_WIDTH_MASK BIT(15) + +#define SDRAM_ADDR_CTRL_REG 0x10 +#define SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(cs) (20+cs) +#define SDRAM_ADDR_CTRL_SIZE_HIGH_MASK(cs) (0x1 << SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(cs)) +#define SDRAM_ADDR_CTRL_ADDR_SEL_MASK(cs) BIT(16+cs) +#define SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(cs) (cs*4+2) +#define SDRAM_ADDR_CTRL_SIZE_LOW_MASK(cs) (0x3 << SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(cs)) +#define SDRAM_ADDR_CTRL_STRUCT_OFFSET(cs) (cs*4) +#define SDRAM_ADDR_CTRL_STRUCT_MASK(cs) (0x3 << SDRAM_ADDR_CTRL_STRUCT_OFFSET(cs)) + +#define SDRAM_ERR_DATA_H_REG 0x40 +#define SDRAM_ERR_DATA_L_REG 0x44 + +#define SDRAM_ERR_RECV_ECC_REG 0x48 +#define SDRAM_ERR_RECV_ECC_VALUE_MASK 0xff + +#define SDRAM_ERR_CALC_ECC_REG 0x4c +#define SDRAM_ERR_CALC_ECC_ROW_OFFSET 8 +#define SDRAM_ERR_CALC_ECC_ROW_MASK (0xffff << SDRAM_ERR_CALC_ECC_ROW_OFFSET) +#define SDRAM_ERR_CALC_ECC_VALUE_MASK 0xff + +#define SDRAM_ERR_ADDR_REG 0x50 +#define SDRAM_ERR_ADDR_BANK_OFFSET 23 +#define SDRAM_ERR_ADDR_BANK_MASK (0x7 << SDRAM_ERR_ADDR_BANK_OFFSET) +#define SDRAM_ERR_ADDR_COL_OFFSET 8 +#define SDRAM_ERR_ADDR_COL_MASK (0x7fff << SDRAM_ERR_ADDR_COL_OFFSET) +#define SDRAM_ERR_ADDR_CS_OFFSET 1 +#define SDRAM_ERR_ADDR_CS_MASK (0x3 << SDRAM_ERR_ADDR_CS_OFFSET) +#define SDRAM_ERR_ADDR_TYPE_MASK BIT(0) + +#define SDRAM_ERR_CTRL_REG 0x54 +#define SDRAM_ERR_CTRL_THR_OFFSET 16 +#define SDRAM_ERR_CTRL_THR_MASK (0xff << SDRAM_ERR_CTRL_THR_OFFSET) +#define SDRAM_ERR_CTRL_PROP_MASK BIT(9) + +#define SDRAM_ERR_SBE_COUNT_REG 0x58 +#define SDRAM_ERR_DBE_COUNT_REG 0x5c + +#define SDRAM_ERR_CAUSE_ERR_REG 0xd0 +#define SDRAM_ERR_CAUSE_MSG_REG 0xd8 +#define SDRAM_ERR_CAUSE_DBE_MASK BIT(1) +#define SDRAM_ERR_CAUSE_SBE_MASK BIT(0) + +#define SDRAM_RANK_CTRL_REG 0x1e0 +#define SDRAM_RANK_CTRL_EXIST_MASK(cs) BIT(cs) + +struct axp_mc_drvdata { + void __iomem *base; + /* width in bytes */ + unsigned int width; + /* bank interleaving */ + bool cs_addr_sel[SDRAM_NUM_CS]; + + char msg[128]; +}; + +/* derived from "DRAM Address Multiplexing" in the ARAMDA XP Functional Spec */ +static uint32_t axp_mc_calc_address(struct axp_mc_drvdata *drvdata, + uint8_t cs, uint8_t bank, uint16_t row, + uint16_t col) +{ + if (drvdata->width == 8) { + /* 64 bit */ + if (drvdata->cs_addr_sel[cs]) + /* bank interleaved */ + return (((row & 0xfff8) << 16) | + ((bank & 0x7) << 16) | + ((row & 0x7) << 13) | + ((col & 0x3ff) << 3)); + else + return (((row & 0xffff << 16) | + ((bank & 0x7) << 13) | + ((col & 0x3ff)) << 3)); + } else if (drvdata->width == 4) { + /* 32 bit */ + if (drvdata->cs_addr_sel[cs]) + /* bank interleaved */ + return (((row & 0xfff0) << 15) | + ((bank & 0x7) << 16) | + ((row & 0xf) << 12) | + ((col & 0x3ff) << 2)); + else + return (((row & 0xffff << 15) | + ((bank & 0x7) << 12) | + ((col & 0x3ff)) << 2)); + } else { + /* 16 bit */ + if (drvdata->cs_addr_sel[cs]) + /* bank interleaved */ + return (((row & 0xffe0) << 14) | + ((bank & 0x7) << 16) | + ((row & 0x1f) << 11) | + ((col & 0x3ff) << 1)); + else + return (((row & 0xffff << 14) | + ((bank & 0x7) << 11) | + ((col & 0x3ff)) << 1)); + } +} + +static void axp_mc_check(struct mem_ctl_info *mci) +{ + struct axp_mc_drvdata *drvdata = mci->pvt_info; + uint32_t data_h, data_l, recv_ecc, calc_ecc, addr; + uint32_t cnt_sbe, cnt_dbe, cause_err, cause_msg; + uint32_t row_val, col_val, bank_val, addr_val; + uint8_t syndrome_val, cs_val; + char *msg = drvdata->msg; + + data_h = readl(drvdata->base + SDRAM_ERR_DATA_H_REG); + data_l = readl(drvdata->base + SDRAM_ERR_DATA_L_REG); + recv_ecc = readl(drvdata->base + SDRAM_ERR_RECV_ECC_REG); + calc_ecc = readl(drvdata->base + SDRAM_ERR_CALC_ECC_REG); + addr = readl(drvdata->base + SDRAM_ERR_ADDR_REG); + cnt_sbe = readl(drvdata->base + SDRAM_ERR_SBE_COUNT_REG); + cnt_dbe = readl(drvdata->base + SDRAM_ERR_DBE_COUNT_REG); + cause_err = readl(drvdata->base + SDRAM_ERR_CAUSE_ERR_REG); + cause_msg = readl(drvdata->base + SDRAM_ERR_CAUSE_MSG_REG); + + /* clear cause registers */ + writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), + drvdata->base + SDRAM_ERR_CAUSE_ERR_REG); + writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), + drvdata->base + SDRAM_ERR_CAUSE_MSG_REG); + + /* clear error counter registers */ + if (cnt_sbe) + writel(0, drvdata->base + SDRAM_ERR_SBE_COUNT_REG); + if (cnt_dbe) + writel(0, drvdata->base + SDRAM_ERR_DBE_COUNT_REG); + + if (!cnt_sbe && !cnt_dbe) + return; + + if (!(addr & SDRAM_ERR_ADDR_TYPE_MASK)) { + if (cnt_sbe) + cnt_sbe--; + else + dev_warn(mci->pdev, "inconsistent SBE count detected"); + } else { + if (cnt_dbe) + cnt_dbe--; + else + dev_warn(mci->pdev, "inconsistent DBE count detected"); + } + + /* report earlier errors */ + if (cnt_sbe) + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + cnt_sbe, /* error count */ + 0, 0, 0, /* pfn, offset, syndrome */ + -1, -1, -1, /* top, mid, low layer */ + mci->ctl_name, + "details unavailable (multiple errors)"); + if (cnt_dbe) + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + cnt_sbe, /* error count */ + 0, 0, 0, /* pfn, offset, syndrome */ + -1, -1, -1, /* top, mid, low layer */ + mci->ctl_name, + "details unavailable (multiple errors)"); + + /* report details for most recent error */ + cs_val = (addr & SDRAM_ERR_ADDR_CS_MASK) >> SDRAM_ERR_ADDR_CS_OFFSET; + bank_val = (addr & SDRAM_ERR_ADDR_BANK_MASK) >> SDRAM_ERR_ADDR_BANK_OFFSET; + row_val = (calc_ecc & SDRAM_ERR_CALC_ECC_ROW_MASK) >> SDRAM_ERR_CALC_ECC_ROW_OFFSET; + col_val = (addr & SDRAM_ERR_ADDR_COL_MASK) >> SDRAM_ERR_ADDR_COL_OFFSET; + syndrome_val = (recv_ecc ^ calc_ecc) & 0xff; + addr_val = axp_mc_calc_address(drvdata, cs_val, bank_val, row_val, + col_val); + msg += sprintf(msg, "row=0x%04x ", row_val); /* 11 chars */ + msg += sprintf(msg, "bank=0x%x ", bank_val); /* 9 chars */ + msg += sprintf(msg, "col=0x%04x ", col_val); /* 11 chars */ + msg += sprintf(msg, "cs=%d", cs_val); /* 4 chars */ + + if (!(addr & SDRAM_ERR_ADDR_TYPE_MASK)) { + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + 1, /* error count */ + addr_val >> PAGE_SHIFT, + addr_val & ~PAGE_MASK, + syndrome_val, + cs_val, -1, -1, /* top, mid, low layer */ + mci->ctl_name, drvdata->msg); + } else { + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + 1, /* error count */ + addr_val >> PAGE_SHIFT, + addr_val & ~PAGE_MASK, + syndrome_val, + cs_val, -1, -1, /* top, mid, low layer */ + mci->ctl_name, drvdata->msg); + } +} + +static void axp_mc_read_config(struct mem_ctl_info *mci) +{ + struct axp_mc_drvdata *drvdata = mci->pvt_info; + uint32_t config, addr_ctrl, rank_ctrl; + unsigned int i, cs_struct, cs_size; + struct dimm_info *dimm; + + config = readl(drvdata->base + SDRAM_CONFIG_REG); + if (config & SDRAM_CONFIG_BUS_WIDTH_MASK) + /* 64 bit */ + drvdata->width = 8; + else + /* 32 bit */ + drvdata->width = 4; + + addr_ctrl = readl(drvdata->base + SDRAM_ADDR_CTRL_REG); + rank_ctrl = readl(drvdata->base + SDRAM_RANK_CTRL_REG); + for (i = 0; i < SDRAM_NUM_CS; i++) { + dimm = mci->dimms[i]; + + if (!(rank_ctrl & SDRAM_RANK_CTRL_EXIST_MASK(i))) + continue; + + drvdata->cs_addr_sel[i] = + !!(addr_ctrl & SDRAM_ADDR_CTRL_ADDR_SEL_MASK(i)); + + cs_struct = (addr_ctrl & SDRAM_ADDR_CTRL_STRUCT_MASK(i)) >> SDRAM_ADDR_CTRL_STRUCT_OFFSET(i); + cs_size = ((addr_ctrl & SDRAM_ADDR_CTRL_SIZE_HIGH_MASK(i)) >> (SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(i) - 2) | + ((addr_ctrl & SDRAM_ADDR_CTRL_SIZE_LOW_MASK(i)) >> SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(i))); + + switch (cs_size) { + case 0: /* 2GBit */ + dimm->nr_pages = 524288; + break; + case 1: /* 256MBit */ + dimm->nr_pages = 65536; + break; + case 2: /* 512MBit */ + dimm->nr_pages = 131072; + break; + case 3: /* 1GBit */ + dimm->nr_pages = 262144; + break; + case 4: /* 4GBit */ + dimm->nr_pages = 1048576; + break; + case 5: /* 8GBit */ + dimm->nr_pages = 2097152; + break; + } + dimm->grain = 8; + dimm->dtype = cs_struct ? DEV_X16 : DEV_X8; + dimm->mtype = (config & SDRAM_CONFIG_REGISTERED_MASK) ? + MEM_RDDR3 : MEM_DDR3; + dimm->edac_mode = EDAC_SECDED; + } +} + +static const struct of_device_id axp_mc_of_match[] = { + {.compatible = "marvell,armada-xp-sdram-controller",}, + {}, +}; +MODULE_DEVICE_TABLE(of, axp_mc_of_match); + +static int axp_mc_probe(struct platform_device *pdev) +{ + struct axp_mc_drvdata *drvdata; + struct edac_mc_layer layers[1]; + const struct of_device_id *id; + struct mem_ctl_info *mci; + struct resource *r; + void __iomem *base; + uint32_t config; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "Unable to get mem resource\n"); + return -ENODEV; + } + + base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "Unable to map regs\n"); + return PTR_ERR(base); + } + + config = readl(base + SDRAM_CONFIG_REG); + if (!(config & SDRAM_CONFIG_ECC_MASK)) { + dev_warn(&pdev->dev, "SDRAM ECC is not enabled"); + return -EINVAL; + } + + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; + layers[0].size = SDRAM_NUM_CS; + layers[0].is_virt_csrow = true; + + mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*drvdata)); + if (!mci) + return -ENOMEM; + + drvdata = mci->pvt_info; + drvdata->base = base; + mci->pdev = &pdev->dev; + platform_set_drvdata(pdev, mci); + + id = of_match_device(axp_mc_of_match, &pdev->dev); + mci->edac_check = axp_mc_check; + mci->mtype_cap = MEM_FLAG_DDR3; + mci->edac_cap = EDAC_FLAG_SECDED; + mci->mod_name = pdev->dev.driver->name; + mci->ctl_name = id ? id->compatible : "unknown"; + mci->dev_name = dev_name(&pdev->dev); + mci->scrub_mode = SCRUB_NONE; + + axp_mc_read_config(mci); + + /* configure SBE threshold */ + /* it seems that SBEs are not captured otherwise */ + writel(1 << SDRAM_ERR_CTRL_THR_OFFSET, drvdata->base + SDRAM_ERR_CTRL_REG); + + /* clear cause registers */ + writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), drvdata->base + SDRAM_ERR_CAUSE_ERR_REG); + writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), drvdata->base + SDRAM_ERR_CAUSE_MSG_REG); + + /* clear counter registers */ + writel(0, drvdata->base + SDRAM_ERR_SBE_COUNT_REG); + writel(0, drvdata->base + SDRAM_ERR_DBE_COUNT_REG); + + if (edac_mc_add_mc(mci)) { + edac_mc_free(mci); + return -EINVAL; + } + edac_op_state = EDAC_OPSTATE_POLL; + + return 0; +} + +static int axp_mc_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = platform_get_drvdata(pdev); + + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver axp_mc_driver = { + .probe = axp_mc_probe, + .remove = axp_mc_remove, + .driver = { + .name = "armada_xp_mc_edac", + .of_match_table = of_match_ptr(axp_mc_of_match), + }, +}; + +/************************ EDAC Device (L2 Cache) ***************************/ + +struct aurora_l2_drvdata { + void __iomem *base; + + char msg[128]; + + /* error injection via debugfs */ + uint32_t inject_addr; + uint32_t inject_mask; + uint8_t inject_ctl; + + struct dentry *debugfs; +}; + +#ifdef CONFIG_EDAC_DEBUG +static void aurora_l2_inject(struct aurora_l2_drvdata *drvdata) +{ + drvdata->inject_addr &= AURORA_ERR_INJECT_CTL_ADDR_MASK; + drvdata->inject_ctl &= AURORA_ERR_INJECT_CTL_EN_MASK; + writel(0, drvdata->base + AURORA_ERR_INJECT_CTL_REG); + writel(drvdata->inject_mask, drvdata->base + AURORA_ERR_INJECT_MASK_REG); + writel(drvdata->inject_addr | drvdata->inject_ctl, drvdata->base + AURORA_ERR_INJECT_CTL_REG); +} +#endif + +static void aurora_l2_check(struct edac_device_ctl_info *dci) +{ + struct aurora_l2_drvdata *drvdata = dci->pvt_info; + uint32_t cnt, src, txn, err, attr_cap, addr_cap, way_cap; + unsigned int cnt_ce, cnt_ue; + char *msg = drvdata->msg; + size_t size = sizeof(drvdata->msg); + size_t len = 0; + + cnt = readl(drvdata->base + AURORA_ERR_CNT_REG); + attr_cap = readl(drvdata->base + AURORA_ERR_ATTR_CAP_REG); + addr_cap = readl(drvdata->base + AURORA_ERR_ADDR_CAP_REG); + way_cap = readl(drvdata->base + AURORA_ERR_WAY_CAP_REG); + + cnt_ce = (cnt & AURORA_ERR_CNT_CE_MASK) >> AURORA_ERR_CNT_CE_OFFSET; + cnt_ue = (cnt & AURORA_ERR_CNT_UE_MASK) >> AURORA_ERR_CNT_UE_OFFSET; + /* clear error counter registers */ + if (cnt_ce || cnt_ue) + writel(AURORA_ERR_CNT_CLR, drvdata->base + AURORA_ERR_CNT_REG); + + if (!(attr_cap & AURORA_ERR_ATTR_CAP_VALID)) + goto clear_remaining; + + src = (attr_cap & AURORA_ERR_ATTR_SRC_MSK) >> AURORA_ERR_ATTR_SRC_OFF; + if (src <= 3) + len += snprintf(msg+len, size-len, "src=CPU%d ", src); + else + len += snprintf(msg+len, size-len, "src=IO "); + + txn = (attr_cap & AURORA_ERR_ATTR_TXN_MSK) >> AURORA_ERR_ATTR_TXN_OFF; + switch (txn) { + case 0: + len += snprintf(msg+len, size-len, "txn=Data-Read "); + break; + case 1: + len += snprintf(msg+len, size-len, "txn=Isn-Read "); + break; + case 2: + len += snprintf(msg+len, size-len, "txn=Clean-Flush "); + break; + case 3: + len += snprintf(msg+len, size-len, "txn=Eviction "); + break; + case 4: + len += snprintf(msg+len, size-len, + "txn=Read-Modify-Write "); + break; + } + + err = (attr_cap & AURORA_ERR_ATTR_ERR_MSK) >> AURORA_ERR_ATTR_ERR_OFF; + switch (err) { + case 0: + len += snprintf(msg+len, size-len, "err=CorrECC "); + break; + case 1: + len += snprintf(msg+len, size-len, "err=UnCorrECC "); + break; + case 2: + len += snprintf(msg+len, size-len, "err=TagParity "); + break; + } + + len += snprintf(msg+len, size-len, "addr=0x%x ", addr_cap & AURORA_ERR_ADDR_CAP_ADDR_MASK); + len += snprintf(msg+len, size-len, "index=0x%x ", (way_cap & AURORA_ERR_WAY_IDX_MSK) >> AURORA_ERR_WAY_IDX_OFF); + len += snprintf(msg+len, size-len, "way=0x%x", (way_cap & AURORA_ERR_WAY_CAP_WAY_MASK) >> AURORA_ERR_WAY_CAP_WAY_OFFSET); + + /* clear error capture registers */ + writel(AURORA_ERR_ATTR_CAP_VALID, drvdata->base + AURORA_ERR_ATTR_CAP_REG); + if (err) { + /* UnCorrECC or TagParity */ + if (cnt_ue) + cnt_ue--; + edac_device_handle_ue(dci, 0, 0, drvdata->msg); + } else { + if (cnt_ce) + cnt_ce--; + edac_device_handle_ce(dci, 0, 0, drvdata->msg); + } + +clear_remaining: + /* report remaining errors */ + while (cnt_ue--) + edac_device_handle_ue(dci, 0, 0, "details unavailable (multiple errors)"); + while (cnt_ce--) + edac_device_handle_ue(dci, 0, 0, "details unavailable (multiple errors)"); +} + +static void aurora_l2_poll(struct edac_device_ctl_info *dci) +{ +#ifdef CONFIG_EDAC_DEBUG + struct aurora_l2_drvdata *drvdata = dci->pvt_info; +#endif + + aurora_l2_check(dci); +#ifdef CONFIG_EDAC_DEBUG + aurora_l2_inject(drvdata); +#endif +} + +static const struct of_device_id aurora_l2_of_match[] = { + {.compatible = "marvell,aurora-system-cache",}, + {}, +}; +MODULE_DEVICE_TABLE(of, aurora_l2_of_match); + +static int aurora_l2_probe(struct platform_device *pdev) +{ + struct aurora_l2_drvdata *drvdata; + struct edac_device_ctl_info *dci; + const struct of_device_id *id; + uint32_t l2x0_aux_ctrl; + void __iomem *base; + struct resource *r; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "Unable to get mem resource\n"); + return -ENODEV; + } + + base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "Unable to map regs\n"); + return PTR_ERR(base); + } + + l2x0_aux_ctrl = readl(base + L2X0_AUX_CTRL); + if (!(l2x0_aux_ctrl & AURORA_ACR_PARITY_EN)) + dev_warn(&pdev->dev, "tag parity is not enabled"); + if (!(l2x0_aux_ctrl & AURORA_ACR_ECC_EN)) + dev_warn(&pdev->dev, "data ECC is not enabled"); + + dci = edac_device_alloc_ctl_info(sizeof(*drvdata), + "cpu", 1, "L", 1, 2, NULL, 0, 0); + if (!dci) + return -ENOMEM; + + drvdata = dci->pvt_info; + drvdata->base = base; + dci->dev = &pdev->dev; + platform_set_drvdata(pdev, dci); + + id = of_match_device(aurora_l2_of_match, &pdev->dev); + dci->edac_check = aurora_l2_poll; + dci->mod_name = pdev->dev.driver->name; + dci->ctl_name = id ? id->compatible : "unknown"; + dci->dev_name = dev_name(&pdev->dev); + + /* clear registers */ + writel(AURORA_ERR_CNT_CLR, drvdata->base + AURORA_ERR_CNT_REG); + writel(AURORA_ERR_ATTR_CAP_VALID, drvdata->base + AURORA_ERR_ATTR_CAP_REG); + + if (edac_device_add_device(dci)) { + edac_device_free_ctl_info(dci); + return -EINVAL; + } + +#ifdef CONFIG_EDAC_DEBUG + drvdata->debugfs = edac_debugfs_create_dir(dev_name(&pdev->dev)); + if (drvdata->debugfs) { + edac_debugfs_create_x32("inject_addr", 0644, + drvdata->debugfs, + &drvdata->inject_addr); + edac_debugfs_create_x32("inject_mask", 0644, + drvdata->debugfs, + &drvdata->inject_mask); + edac_debugfs_create_x8("inject_ctl", 0644, + drvdata->debugfs, &drvdata->inject_ctl); + } +#endif + + return 0; +} + +static int aurora_l2_remove(struct platform_device *pdev) +{ + struct edac_device_ctl_info *dci = platform_get_drvdata(pdev); +#ifdef CONFIG_EDAC_DEBUG + struct aurora_l2_drvdata *drvdata = dci->pvt_info; + + edac_debugfs_remove_recursive(drvdata->debugfs); +#endif + edac_device_del_device(&pdev->dev); + edac_device_free_ctl_info(dci); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver aurora_l2_driver = { + .probe = aurora_l2_probe, + .remove = aurora_l2_remove, + .driver = { + .name = "aurora_l2_edac", + .of_match_table = of_match_ptr(aurora_l2_of_match), + }, +}; + +/************************ Driver registration ******************************/ + +static struct platform_driver * const drivers[] = { + &axp_mc_driver, + &aurora_l2_driver, +}; + +static int __init armada_xp_edac_init(void) +{ + int res; + + /* only polling is supported */ + edac_op_state = EDAC_OPSTATE_POLL; + + res = platform_register_drivers(drivers, ARRAY_SIZE(drivers)); + if (res) + pr_warn("Aramda XP EDAC drivers fail to register\n"); + + return 0; +} +module_init(armada_xp_edac_init); + +static void __exit armada_xp_edac_exit(void) +{ + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); +} +module_exit(armada_xp_edac_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Pengutronix"); +MODULE_DESCRIPTION("EDAC Drivers for Marvell Armada XP SDRAM and L2 Cache Controller"); -- 2.19.1