From patchwork Mon Aug 7 07:39:26 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Simek X-Patchwork-Id: 9884653 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 3D7A360364 for ; Mon, 7 Aug 2017 07:55:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2D8ED285A9 for ; Mon, 7 Aug 2017 07:55:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 20DD4285C6; Mon, 7 Aug 2017 07:55:52 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_NONE autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 639EE285A9 for ; Mon, 7 Aug 2017 07:55:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=TGV4IRr4jopYqMdtjj4VsCDsTmRHWzn7Qs0RQcvwebk=; b=tT6rKmHW+aLFIm2a3EQ5nlOUoU tF1/3KfqlEAXg05kpfm7anueK8M5cmPQvB/jghUbrdVk6JSTsYGDD2ootwqyCcQmpfcVmkgrTic7v Rx7oYZQBvS2Zz/rGzAT1/xLCIaGcliQ+eePdd7fwkY4nTNTG5OLOjU+ysdVvU6/oJKcfNEO51O2dE g4KRgzxiAZQBOnA+lc+Xth5Jv0mBISVF6di4/DDg1Ig23E6V1vHdzBUCQdf8tAht5vQzC5va1dPwD hry6dZwF626OLH9ey5JkLnF35FeoJImRo/En8YVw0S0X61H6WU2dpa7osSfs4OkqYBIABRbY5lqjD UKxikCJQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1dectA-000231-UP; Mon, 07 Aug 2017 07:55:48 +0000 Received: from casper.infradead.org ([2001:8b0:10b:1236::1]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1decst-0001Lf-PV for linux-arm-kernel@bombadil.infradead.org; Mon, 07 Aug 2017 07:55:31 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=casper.20170209; h=References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Sender:Reply-To:MIME-Version:Content-Type: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id: List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=QpO7oliYP9yKiX0NkoOmNWwB2j1DQN4RCM3XeJ+Ud08=; b=b6CjMoqA8ReC14T1jCdKtX7J5 A7bKco7HD8bVLq5s+JR5ODsHXkHK9+svBFp1KvhqZriKxLp0virpRpdxD4Mikkp2a5esMx4TSaUqj k1TSXWJ/LMBEIinXOvvQ3qROIHV1AwTXlAmD2saO+k7dn2B1FntTQxmuWoSvuO86JCLUtV6a+qCTf z/a0dLZNkmVH1bmF1NO5QalkPzjfHMBPjFxKgJQ706AcjUk3tpMe5sIbFREBW0SndSOuJSGB9Byzo 7lI0eEMpk6pJfkXc4lvE2IbFmlcmVz2eQ0QDBNqsnEArUWzEeuw9+RXGgKaqOWID2ComAV80l/Hf1 tJmCqwwcg==; Received: from mail-wr0-x243.google.com ([2a00:1450:400c:c0c::243]) by casper.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1decds-0005eN-BL for linux-arm-kernel@lists.infradead.org; Mon, 07 Aug 2017 07:40:03 +0000 Received: by mail-wr0-x243.google.com with SMTP id y43so7048521wrd.0 for ; Mon, 07 Aug 2017 00:39:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=monstr-eu.20150623.gappssmtp.com; s=20150623; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=QpO7oliYP9yKiX0NkoOmNWwB2j1DQN4RCM3XeJ+Ud08=; b=0tqtznByiuxWE/jPYfKjAMZZp+qh4okT+qOBsbFXIh5hSH5KFRUJFoh6B021tkZlQ/ ygqNlvioKh8zqv8uMZUTHo4acdrIkjFsZRVOU/RFH1TaWnQ4vG+DsumW9pI1t33PFGoC ztwStQNtkt9GZs5NAwahaclU21R+wS55gicYGaX6Swdj5cnijIwoJgC7i5D1KjJifgT2 7cort4Sls7roP4bShtZHVvmTMxRYr7JVkowCy4/OxZhG5KQJ+Pgoa6yQgrTEePjAVzr+ Aah/lTcdj8Ax5+X4hLdZNqh4pihwCJK+NiYvjYaEP+PssXaDD6I2bWddW17sXZCHYkOn kAeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:in-reply-to:references; bh=QpO7oliYP9yKiX0NkoOmNWwB2j1DQN4RCM3XeJ+Ud08=; b=XcoiGRh61MNcqzVFeYQ8x7e6/jLqajwytwIR93hi3A4FMkh9kqzQqyouDb3LiUuGpX TrstIKaVdyGFvTY5nmlAKy9NoGAE2JIG6M1bQnPVVkb5hHza2fcBILh9skrgv2o9gXpn cHmXyowFxxO+OWjtQ4TW94qDxieI29zElh1ML3vToFgeMwHVi0RtdpUxUikJvy1UfJry wDS3pJzw7t1qzY9NskV3oNuqxpL6JvUYqxTBXGFYfyOTLdezFHiOEJ3S8V2xr/d2wXrM oZSe5oqIpcHq/USQZEh+JH3ip9RHGIt1iEncWEPhG7xG+3C69kyr14ppDQyT8Hj+Qk2b vDjQ== X-Gm-Message-State: AIVw111uQPkhjr4/uAaWJ32sbAYDWDq43rLvXWzzurkSRQ3vztLaV26e 5AKPD2isg6uw6AZT X-Received: by 10.223.154.183 with SMTP id a52mr7343237wrc.47.1502091577400; Mon, 07 Aug 2017 00:39:37 -0700 (PDT) Received: from localhost (nat-35.starnet.cz. [178.255.168.35]) by smtp.gmail.com with ESMTPSA id 27sm13601016wru.62.2017.08.07.00.39.36 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Mon, 07 Aug 2017 00:39:36 -0700 (PDT) From: Michal Simek To: linux-kernel@vger.kernel.org, monstr@monstr.eu Subject: [PATCH v2 4/6] edac: synopsys: Add ECC error injection support Date: Mon, 7 Aug 2017 09:39:26 +0200 Message-Id: <738dd1ea12078f98e91d782b3266a41f7918f922.1502091561.git.michal.simek@xilinx.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <91fd6532076e4c905b5a228d852bba4941c54a28.1502091561.git.michal.simek@xilinx.com> References: <91fd6532076e4c905b5a228d852bba4941c54a28.1502091561.git.michal.simek@xilinx.com> In-Reply-To: <91fd6532076e4c905b5a228d852bba4941c54a28.1502091561.git.michal.simek@xilinx.com> References: <91fd6532076e4c905b5a228d852bba4941c54a28.1502091561.git.michal.simek@xilinx.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170807_084000_425012_E28E4248 X-CRM114-Status: GOOD ( 27.34 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Naga Sureshkumar Relli , Borislav Petkov , =?UTF-8?q?S=C3=B6ren=20Brinkmann?= , Mauro Carvalho Chehab , linux-arm-kernel@lists.infradead.org, linux-edac@vger.kernel.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP From: Naga Sureshkumar Relli The ZynqMP DDRC controller has data poisoning support to inject CE or UE errors. this patch adds this support using sysfs attributes. created the following sysfs entries to support this. -> /sys/devices/system/edac/mc/mc0/inject_data_poison -> /sys/devices/system/edac/mc/mc0/inject_data_error Signed-off-by: Naga Sureshkumar Relli Signed-off-by: Michal Simek --- Changes in v2: - s/ecc/ECC/ is subject - fix function parameters alignment - fix some kernel-doc descriptions drivers/edac/synopsys_edac.c | 288 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 287 insertions(+), 1 deletion(-) diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index 11016cd13a08..4cd9e3d8161c 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -99,6 +99,7 @@ /* DDR ECC Quirks */ #define DDR_ECC_INTR_SUPPORT BIT(0) +#define DDR_ECC_DATA_POISON_SUPPORT BIT(1) /* ZynqMP Enhanced DDR memory controller registers that are relevant to ECC */ /* ECC Configuration Registers */ @@ -174,6 +175,11 @@ #define ECC_CEADDR1_BNKGRP_SHIFT 24 #define ECC_CEADDR1_BNKNR_SHIFT 16 +/* ECC Poison register shifts */ +#define ECC_POISON0_RANK_SHIFT 24 +#define ECC_POISON1_BANKGRP_SHIFT 28 +#define ECC_POISON1_BANKNR_SHIFT 24 + /* DDR Memory type defines */ #define MEM_TYPE_DDR3 0x1 #define MEM_TYPE_LPDDR3 0x1 @@ -181,6 +187,38 @@ #define MEM_TYPE_DDR4 0x10 #define MEM_TYPE_LPDDR4 0x10 +/* DDRC Software control register */ +#define DDRC_SWCTL 0x320 + +/* DDRC ECC CE & UE poison mask */ +#define ECC_CEPOISON_MASK 0x3 +#define ECC_UEPOISON_MASK 0x1 + +/* DDRC Device config masks */ +#define DDRC_MSTR_DEV_CONFIG_MASK 0xC0000000 +#define DDRC_MSTR_DEV_CONFIG_SHIFT 30 +#define DDRC_MSTR_DEV_CONFIG_X4_MASK 0 +#define DDRC_MSTR_DEV_CONFIG_X8_MASK 1 +#define DDRC_MSTR_DEV_CONFIG_X16_MASK 0x10 +#define DDRC_MSTR_DEV_CONFIG_X32_MASK 0X11 + +/* DDR4 and DDR3 device Row,Column,Bank Mapping */ +#define DDR4_COL_SHIFT 3 +#define DDR4_BANKGRP_SHIFT 13 +#define DDR4_BANK_SHIFT 15 +#define DDR4_ROW_SHIFT 17 +#define DDR4_COL_MASK 0x3FF +#define DDR4_BANKGRP_MASK 0x3 +#define DDR4_BANK_MASK 0x3 +#define DDR4_ROW_MASK 0x7FFF + +#define DDR3_COL_SHIFT 3 +#define DDR3_BANK_SHIFT 13 +#define DDR3_ROW_SHIFT 16 +#define DDR3_COL_MASK 0x3FF +#define DDR3_BANK_MASK 0x7 +#define DDR3_ROW_MASK 0x3FFF + /** * struct ecc_error_info - ECC error log information * @row: Row number @@ -223,6 +261,7 @@ struct synps_ecc_status { * @p_data: Pointer to platform data * @ce_cnt: Correctable Error count * @ue_cnt: Uncorrectable Error count + * @poison_addr: Data poison address */ struct synps_edac_priv { void __iomem *baseaddr; @@ -231,6 +270,7 @@ struct synps_edac_priv { const struct synps_platform_data *p_data; u32 ce_cnt; u32 ue_cnt; + ulong poison_addr; }; /** @@ -732,7 +772,8 @@ static int synps_edac_mc_init(struct mem_ctl_info *mci, .edac_get_mtype = synps_enh_edac_get_mtype, .edac_get_dtype = synps_enh_edac_get_dtype, .edac_get_eccstate = synps_enh_edac_get_eccstate, - .quirks = DDR_ECC_INTR_SUPPORT, + .quirks = (DDR_ECC_INTR_SUPPORT | + DDR_ECC_DATA_POISON_SUPPORT), }; static const struct of_device_id synps_edac_match[] = { @@ -744,6 +785,240 @@ static int synps_edac_mc_init(struct mem_ctl_info *mci, MODULE_DEVICE_TABLE(of, synps_edac_match); +#define to_mci(k) container_of(k, struct mem_ctl_info, dev) + +/** + * ddr4_poison_setup - update poison registers + * @dttype: Device structure variable + * @device_config: Device configuration + * @priv: Pointer to synps_edac_priv struct + * + * Update poison registers as per ddr4 mapping + */ +static void ddr4_poison_setup(enum dev_type dttype, int device_config, + struct synps_edac_priv *priv) +{ + int col, row, bank, bankgrp, regval, shift_val = 0, col_shift; + + /* Check the Configuration of the device */ + if (device_config & DDRC_MSTR_DEV_CONFIG_X8_MASK) { + /* For Full Dq bus */ + if (dttype == DEV_X8) + shift_val = 0; + /* For Half Dq bus */ + else if (dttype == DEV_X4) + shift_val = 1; + col_shift = 0; + } else if (device_config & DDRC_MSTR_DEV_CONFIG_X16_MASK) { + if (dttype == DEV_X8) + shift_val = 1; + else if (dttype == DEV_X4) + shift_val = 2; + col_shift = 1; + } + + col = (priv->poison_addr >> (DDR4_COL_SHIFT - + (shift_val - col_shift))) & + DDR4_COL_MASK; + row = priv->poison_addr >> (DDR4_ROW_SHIFT - shift_val); + row &= DDR4_ROW_MASK; + bank = priv->poison_addr >> (DDR4_BANK_SHIFT - shift_val); + bank &= DDR4_BANK_MASK; + bankgrp = (priv->poison_addr >> (DDR4_BANKGRP_SHIFT - + (shift_val - col_shift))) & + DDR4_BANKGRP_MASK; + + writel(col, priv->baseaddr + ECC_POISON0_OFST); + regval = (bankgrp << ECC_POISON1_BANKGRP_SHIFT) | + (bank << ECC_POISON1_BANKNR_SHIFT) | row; + writel(regval, priv->baseaddr + ECC_POISON1_OFST); +} + +/** + * ddr3_poison_setup - update poison registers + * @dttype: Device structure variable + * @device_config: Device configuration + * @priv: Pointer to synps_edac_priv struct + * + * Update poison registers as per ddr3 mapping + */ +static void ddr3_poison_setup(enum dev_type dttype, int device_config, + struct synps_edac_priv *priv) +{ + int col, row, bank, bankgrp, regval, shift_val = 0; + + if (dttype == DEV_X8) + /* For Full Dq bus */ + shift_val = 0; + else if (dttype == DEV_X4) + /* For Half Dq bus */ + shift_val = 1; + + col = (priv->poison_addr >> (DDR3_COL_SHIFT - shift_val)) & + DDR3_COL_MASK; + row = priv->poison_addr >> (DDR3_ROW_SHIFT - shift_val); + row &= DDR3_ROW_MASK; + bank = priv->poison_addr >> (DDR3_BANK_SHIFT - shift_val); + bank &= DDR3_BANK_MASK; + bankgrp = 0; + writel(col, priv->baseaddr + ECC_POISON0_OFST); + regval = (bankgrp << ECC_POISON1_BANKGRP_SHIFT) | + (bank << ECC_POISON1_BANKNR_SHIFT) | row; + writel(regval, priv->baseaddr + ECC_POISON1_OFST); +} + +/** + * synps_edac_mc_inject_data_error_show - Get Poison0 & 1 register contents + * @dev: Pointer to the device struct + * @mattr: Pointer to device attributes + * @data: Pointer to user data + * + * Get the Poison0 and Poison1 register contents + * Return: Number of bytes copied. + */ +static ssize_t +synps_edac_mc_inject_data_error_show(struct device *dev, + struct device_attribute *mattr, + char *data) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct synps_edac_priv *priv = mci->pvt_info; + + return sprintf(data, "Poison0 Addr: 0x%08x\n\rPoison1 Addr: 0x%08x\n\r" + "Error injection Address: 0x%lx\n\r", + readl(priv->baseaddr + ECC_POISON0_OFST), + readl(priv->baseaddr + ECC_POISON1_OFST), + priv->poison_addr); +} + +/** + * synps_edac_mc_inject_data_error_store - Configure Poison0 Poison1 registers + * @dev: Pointer to the device struct + * @mattr: Pointer to device attributes + * @data: Pointer to user data + * @count: read the size bytes from buffer + * + * Configures the Poison0 and Poison1 register contents as per user given + * address + * Return: Number of bytes copied. + */ +static ssize_t +synps_edac_mc_inject_data_error_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct synps_edac_priv *priv = mci->pvt_info; + int device_config; + enum mem_type mttype; + enum dev_type dttype; + + mttype = priv->p_data->edac_get_mtype(priv->baseaddr); + dttype = priv->p_data->edac_get_dtype(priv->baseaddr); + if (kstrtoul(data, 0, &priv->poison_addr)) + return -EINVAL; + + device_config = readl(priv->baseaddr + CTRL_OFST); + device_config = (device_config & DDRC_MSTR_DEV_CONFIG_MASK) >> + DDRC_MSTR_DEV_CONFIG_SHIFT; + if (mttype == MEM_DDR4) + ddr4_poison_setup(dttype, device_config, priv); + else if (mttype == MEM_DDR3) + ddr3_poison_setup(dttype, device_config, priv); + + return count; +} + +/** + * synps_edac_mc_inject_data_poison_show - Shows type of Data poison + * @dev: Pointer to the device struct + * @mattr: Pointer to device attributes + * @data: Pointer to user data + * + * Shows the type of Error injection enabled, either UE or CE + * Return: Number of bytes copied. + */ +static ssize_t +synps_edac_mc_inject_data_poison_show(struct device *dev, + struct device_attribute *mattr, + char *data) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct synps_edac_priv *priv = mci->pvt_info; + + return sprintf(data, "Data Poisoning: %s\n\r", + (readl(priv->baseaddr + ECC_CFG1_OFST)) & 0x3 ? + "Correctable Error" : "UnCorrectable Error"); +} + +/** + * synps_edac_mc_inject_data_poison_store - Enbles Data poison CE/UE + * @dev: Pointer to the device struct + * @mattr: Pointer to device attributes + * @data: Pointer to user data + * @count: read the size bytes from buffer + * + * Enables the CE or UE Data poison + * Return: Number of bytes copied. + */ +static ssize_t +synps_edac_mc_inject_data_poison_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct synps_edac_priv *priv = mci->pvt_info; + + writel(0, priv->baseaddr + DDRC_SWCTL); + if (strncmp(data, "CE", 2) == 0) + writel(ECC_CEPOISON_MASK, priv->baseaddr + ECC_CFG1_OFST); + else + writel(ECC_UEPOISON_MASK, priv->baseaddr + ECC_CFG1_OFST); + writel(1, priv->baseaddr + DDRC_SWCTL); + + return count; +} + +static DEVICE_ATTR(inject_data_error, 0644, + synps_edac_mc_inject_data_error_show, + synps_edac_mc_inject_data_error_store); +static DEVICE_ATTR(inject_data_poison, 0644, + synps_edac_mc_inject_data_poison_show, + synps_edac_mc_inject_data_poison_store); + +/** + * synps_edac_create_sysfs_attributes - Create sysfs entries + * @mci: Pointer to the edac memory controller instance + * + * Create sysfs attributes for injecting ECC errors using data poison. + * + * Return: 0 if sysfs creation was successful, else return negative error code. + */ +static int synps_edac_create_sysfs_attributes(struct mem_ctl_info *mci) +{ + int rc; + + rc = device_create_file(&mci->dev, &dev_attr_inject_data_error); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_inject_data_poison); + if (rc < 0) + return rc; + return 0; +} + +/** + * synps_edac_remove_sysfs_attributes - Removes sysfs entries + * @mci: Pointer to the edac memory controller instance + * + * Removes sysfs attributes. + */ +static void synps_edac_remove_sysfs_attributes(struct mem_ctl_info *mci) +{ + device_remove_file(&mci->dev, &dev_attr_inject_data_error); + device_remove_file(&mci->dev, &dev_attr_inject_data_poison); +} + /** * synps_edac_mc_probe - Check controller and bind driver * @pdev: Pointer to the platform_device struct @@ -831,6 +1106,13 @@ static int synps_edac_mc_probe(struct platform_device *pdev) goto free_edac_mc; } + if (priv->p_data->quirks & DDR_ECC_DATA_POISON_SUPPORT) { + if (synps_edac_create_sysfs_attributes(mci)) { + edac_printk(KERN_ERR, EDAC_MC, + "Failed to create sysfs entries\n"); + goto free_edac_mc; + } + } /* * Start capturing the correctable and uncorrectable errors. A write of * 0 starts the counters. @@ -854,8 +1136,12 @@ static int synps_edac_mc_probe(struct platform_device *pdev) static int synps_edac_mc_remove(struct platform_device *pdev) { struct mem_ctl_info *mci = platform_get_drvdata(pdev); + struct synps_edac_priv *priv; + priv = mci->pvt_info; edac_mc_del_mc(&pdev->dev); + if (priv->p_data->quirks & DDR_ECC_DATA_POISON_SUPPORT) + synps_edac_remove_sysfs_attributes(mci); edac_mc_free(mci); return 0;