From patchwork Fri Aug 4 12:00:25 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Simek X-Patchwork-Id: 9881095 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 2EE276031B for ; Fri, 4 Aug 2017 12:02:37 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0C18E28501 for ; Fri, 4 Aug 2017 12:02:37 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 00EDE289D6; Fri, 4 Aug 2017 12:02:36 +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 3298228501 for ; Fri, 4 Aug 2017 12:02:36 +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=vEICPJ8tvDC5FTSmiOUhxwc9gNetwIW9izQoVBOxvfk=; b=By4p4SK69Yae/IBlnJgtoc+n9c G7HXZBrd3YRae1oLGvn1bwfbbpYmBy1Z36QzG/suX7IoGvx/kukcKY/ymu74jS2prauoe+j8JHMLW mYtvZPLMXYVCr9JAtIV6xntAaKK/MLcfWzgwU5S7fNJr3MCvaP6QuOuuea0GTmaOW3usEMEC7s3Aj hXCkTkrwVOoLp/RoE+1QQrjmM8MQQKNbRto3WIklvym/VuZaj7FA7hDzNaoDSsjDHRcqIkGBCUxY9 2TaXby6Vs5vAZFjezqlzLK8valdfcgZ7yhnlFWzPSjO3pAsIjlQR75y2e815gMQJO9dm+PmPMZ47D 64pTUPhw==; 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 1ddbJJ-0004Jo-4O; Fri, 04 Aug 2017 12:02:33 +0000 Received: from mail-wr0-x244.google.com ([2a00:1450:400c:c0c::244]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1ddbHi-0002bi-Eq for linux-arm-kernel@lists.infradead.org; Fri, 04 Aug 2017 12:01:23 +0000 Received: by mail-wr0-x244.google.com with SMTP id 12so2853889wrb.4 for ; Fri, 04 Aug 2017 05:00:34 -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=jVa429JMahIOSjzOnYHXXD8kaBjDqWUiGgLXutH9zoI=; b=aN3/A21ZNxpyY5pSfjJyHKnDamkjifrcwfm33UwPWqXd8ABA90dKGTYET54rfElohF 51VacGhKmu2d0x3EWZQ2/NJS2HaVIvKrZesNOmk3FwebpDAepN51ALlYT3vYz4obAylX FhmXr7yvTX8E5gcMreYUlfr1vATyv/aqAzM3V5JZs/5I1qH0hhxirSDFlNY/GXboOobk i7Fmj76z0Bb8UHMTbmKGrj28mHKrd1RjbQbg9REQoJ1T0dEVmMHzxa+McSSK5F5T3QZ2 /Qc4pN93WmaKcxg8iNdMbFnhewezqLDUYO6sAcOG7UctChtQ2/l0bWA0MB5pA6e2Tu1v T8iA== 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=jVa429JMahIOSjzOnYHXXD8kaBjDqWUiGgLXutH9zoI=; b=F6xFcv8Rjqdu9G/PrVh+n0hb3WiALDtvE63obw9Yj3E99du8SZkG8yDGhRDn6dcpac aBtzhGPSqFzlL3xDOafaKNd/M2KzNT7OAMWRPKr0nXB2MBajbb6lNVbrNrketr7XWRVS 8TGMlov5cdgiPyR0hL0VJgX6oaRHagJE3iBAhydim5WF7oRSptTzZtZUSsEmxWuRTt4I E8qk2uqNI7uRguff9fRNnnr/wfs92PwnDrYpMk+cCWnpEQVzlMwh+efAaKBBrQ0njHp3 YqandKBIupg+H41jTPyAbEyeAHVfkwtZo7qQGBT/6QUwisvEBO40fuafG8A2irLfNTtU 9jqA== X-Gm-Message-State: AIVw1101NcMljcnZ7ZN0PWkGjXE0cuxZqu0umSLbn/lRho5UxaQLMxVK nrWl/wjrme64Qwex X-Received: by 10.223.164.208 with SMTP id h16mr1712344wrb.100.1501848032313; Fri, 04 Aug 2017 05:00:32 -0700 (PDT) Received: from localhost (nat-35.starnet.cz. [178.255.168.35]) by smtp.gmail.com with ESMTPSA id 94sm4345753wrb.55.2017.08.04.05.00.31 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Fri, 04 Aug 2017 05:00:31 -0700 (PDT) From: Michal Simek To: linux-kernel@vger.kernel.org, monstr@monstr.eu Subject: [PATCH 3/5] edac: synopsys: Add ecc error injection support Date: Fri, 4 Aug 2017 14:00:25 +0200 Message-Id: <8d9d9be950282733fdbe7b50c6768eee7471f82e.1501848023.git.michal.simek@xilinx.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <60ff19f90adfd252cf296fe8988c614c4c3e43fd.1501848023.git.michal.simek@xilinx.com> References: <60ff19f90adfd252cf296fe8988c614c4c3e43fd.1501848023.git.michal.simek@xilinx.com> In-Reply-To: <60ff19f90adfd252cf296fe8988c614c4c3e43fd.1501848023.git.michal.simek@xilinx.com> References: <60ff19f90adfd252cf296fe8988c614c4c3e43fd.1501848023.git.michal.simek@xilinx.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170804_050055_414401_818A048B X-CRM114-Status: GOOD ( 20.63 ) 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 --- drivers/edac/synopsys_edac.c | 291 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 290 insertions(+), 1 deletion(-) diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index fdf1186151c1..546adc243bca 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; }; /** @@ -628,6 +668,7 @@ static enum mem_type synps_enh_edac_get_mtype(const void __iomem *base) memtype = readl(base + CTRL_OFST); + mt = MEM_UNKNOWN; if ((memtype & MEM_TYPE_DDR3) || (memtype & MEM_TYPE_LPDDR3)) mt = MEM_DDR3; else if (memtype & MEM_TYPE_DDR2) @@ -732,7 +773,8 @@ static int synps_edac_mc_init(struct mem_ctl_info *mci, .synps_edac_get_mtype = synps_enh_edac_get_mtype, .synps_edac_get_dtype = synps_enh_edac_get_dtype, .synps_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 +786,242 @@ 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 + * Return: none. + */ +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 + * Return: none. + */ +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->synps_edac_get_mtype( + priv->baseaddr); + dttype = priv->p_data->synps_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. + * + * Return: none. + */ +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 +1109,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 +1139,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;