From patchwork Sun Apr 24 03:25:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chuanhong Guo X-Patchwork-Id: 12824753 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 416DBC433F5 for ; Sun, 24 Apr 2022 03:26:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=oLL2G94jmyTELcWeV98hCMdHFZt1u6jVVRgHtLPTsqk=; b=gRSTlgql181xeh ktWQ6uytnyZ5PCYgvf8pjA9cz37yzZwlXUd1TG6toflnQnBN6kLyULFk/eT3mott+yvScg+1W29c4 p6DTpUw7n+vgYAJBksEM/A1KRMoW24jhs0xG7NVm1c0RwuOCE9kUfVmVqAUAvDBzStoXSQ2ErBOCW A6YHU9h+a67xkG9J2HIuAzbYAxAOjO+ctj885QTdVZPPVZwJI3kBiehZqzR+nlf0kn/OxkbNU+F89 eUwsGfcYbsuw7NxdLGhOY1vc2Chmcp8nH9UlSZ/KAQD1mA9O/82NydXDxZz9G1LQsqrl+T4BQUtT5 vESaI06Yq1RqGz/eKTVQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1niStL-005eKp-7X; Sun, 24 Apr 2022 03:26:31 +0000 Received: from mail-pl1-x62a.google.com ([2607:f8b0:4864:20::62a]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1niSt1-005eEX-7C; Sun, 24 Apr 2022 03:26:12 +0000 Received: by mail-pl1-x62a.google.com with SMTP id h12so15918140plf.12; Sat, 23 Apr 2022 20:26:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=NzkswhKQ6erM0NwyzJei2dswpgqgrnf/ri44n3S0Bds=; b=d+tUAW85ZO1mE/kt+Ee3FH0Bu5gu9QcnL8S8y+lZaPHT/r7uJs8dq2jgRMjD+vrTPc KEK2JxktLrDzQWxqsdlnTUYx6HqlCDSxFCF2+cckTJZEEkBZPHcbxKiclZcbS5s86xut U289NJmVTDMSFpjKl7s/COE/AQH+PTmmijz4U/ubJLURsHpXkWvwi60V0B4y3HV9uhRb MQH07P6HIxcLt7iZeZj5aFGMtWbzInJmfeMqNBmj6XJr6Om0hczpvlNziciglboH21LN qvNUvZYcYs9Q82v4Uv528XwDApMCy4gE+vHRWS8rFyrgLnOvzxj3kWkfdpzXCDZQRIxZ nRPw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=NzkswhKQ6erM0NwyzJei2dswpgqgrnf/ri44n3S0Bds=; b=1AcYuWBEH68WVOXusHhDB+RkYIlv0KawKir0IRZekD3plEiI+rqCZdwWfyA/DGxnuD ALz+eSfOMzfSkMTQ5fZHanQvQoaiSIUKjB6brrJmY3cvJrI31MmJsvu0GPHMPIxLtFnA dY9antfqa9vFXgfvK1Eb9S/hkRWvAWrU+fStqN6q0LB35u/8Q9e1t9ySPTLmU26n1IpY k8Kpqq2Edf2rklqWyugIsOHsEl7mOTjADCGaza0oRat3Z3djkJmh0qROvBhkzCehuHLX OctxXH9AjF2TgvI/6UYekwHkCzkk7zL/6sBfBGDfD6xisDgL0uZpbS6sEGMUiQ/7nRFh PMPQ== X-Gm-Message-State: AOAM531ONHVJ3DdnLvjbnYThErm+cG/ZE18I2sYBOaNfZTSspZo0Aso9 gzlr8bC3Y3Df3fq9fflbey7zZ4Iwx0TpKBaL X-Google-Smtp-Source: ABdhPJxvdWZ8OS2kKPVTfL383FAYfimJSgUQ8hqox5sClY0wNwufNCLP4rFRU1nnmMj6nykdneKAIg== X-Received: by 2002:a17:902:bb8d:b0:156:51a1:3f5a with SMTP id m13-20020a170902bb8d00b0015651a13f5amr11806435pls.65.1650770770259; Sat, 23 Apr 2022 20:26:10 -0700 (PDT) Received: from guoguo-omen.lan ([222.201.153.219]) by smtp.gmail.com with ESMTPSA id r76-20020a632b4f000000b003820643e1c2sm5790724pgr.59.2022.04.23.20.26.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 23 Apr 2022 20:26:09 -0700 (PDT) From: Chuanhong Guo To: linux-spi@vger.kernel.org Cc: Chuanhong Guo , Mark Brown , Rob Herring , Krzysztof Kozlowski , Matthias Brugger , Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Roger Quadros , Thomas Bogendoerfer , Cai Huoqing , Florian Fainelli , Colin Ian King , Wolfram Sang , Paul Cercueil , Pratyush Yadav , Yu Kuai , devicetree@vger.kernel.org (open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS), linux-arm-kernel@lists.infradead.org (moderated list:ARM/Mediatek SoC support), linux-mediatek@lists.infradead.org (moderated list:ARM/Mediatek SoC support), linux-kernel@vger.kernel.org (open list), linux-mtd@lists.infradead.org (open list:NAND FLASH SUBSYSTEM) Subject: [PATCH v6 1/5] mtd: nand: make mtk_ecc.c a separated module Date: Sun, 24 Apr 2022 11:25:23 +0800 Message-Id: <20220424032527.673605-2-gch981213@gmail.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220424032527.673605-1-gch981213@gmail.com> References: <20220424032527.673605-1-gch981213@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220423_202611_312785_748B43FC X-CRM114-Status: GOOD ( 12.66 ) X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+linux-mediatek=archiver.kernel.org@lists.infradead.org this code will be used in mediatek snfi spi-mem controller with pipelined ECC engine. Signed-off-by: Chuanhong Guo --- Change since v1: actually make it a module instead of a part of nandcore.o Change in v2-v6: None drivers/mtd/nand/Kconfig | 7 +++++++ drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/{raw/mtk_ecc.c => ecc-mtk.c} | 3 +-- drivers/mtd/nand/raw/Kconfig | 1 + drivers/mtd/nand/raw/Makefile | 2 +- drivers/mtd/nand/raw/mtk_nand.c | 2 +- .../nand/raw/mtk_ecc.h => include/linux/mtd/nand-ecc-mtk.h | 0 7 files changed, 12 insertions(+), 4 deletions(-) rename drivers/mtd/nand/{raw/mtk_ecc.c => ecc-mtk.c} (99%) rename drivers/mtd/nand/raw/mtk_ecc.h => include/linux/mtd/nand-ecc-mtk.h (100%) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 9b249826ef93..2f3e02ab72ed 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -53,6 +53,13 @@ config MTD_NAND_ECC_MXIC help This enables support for the hardware ECC engine from Macronix. +config MTD_NAND_ECC_MEDIATEK + tristate "Mediatek hardware ECC engine" + depends on HAS_IOMEM + select MTD_NAND_ECC + help + This enables support for the hardware ECC engine from Mediatek. + endmenu endmenu diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index a4e6b7ae0614..19e1291ac4d5 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -2,6 +2,7 @@ nandcore-objs := core.o bbt.o obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o +obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o obj-y += onenand/ obj-y += raw/ diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/ecc-mtk.c similarity index 99% rename from drivers/mtd/nand/raw/mtk_ecc.c rename to drivers/mtd/nand/ecc-mtk.c index 49ab3448b9b1..74ddaa46ba7c 100644 --- a/drivers/mtd/nand/raw/mtk_ecc.c +++ b/drivers/mtd/nand/ecc-mtk.c @@ -15,8 +15,7 @@ #include #include #include - -#include "mtk_ecc.h" +#include #define ECC_IDLE_MASK BIT(0) #define ECC_IRQ_EN BIT(0) diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 9b078e78f3fa..8b6d7a515445 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -374,6 +374,7 @@ config MTD_NAND_QCOM config MTD_NAND_MTK tristate "MTK NAND controller" + depends on MTD_NAND_ECC_MEDIATEK depends on ARCH_MEDIATEK || COMPILE_TEST depends on HAS_IOMEM help diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 88a566513c56..fa1d00120310 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -48,7 +48,7 @@ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o -obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o +obj-$(CONFIG_MTD_NAND_MTK) += mtk_nand.o obj-$(CONFIG_MTD_NAND_MXIC) += mxic_nand.o obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index 66f04c693c87..d540454cbbdf 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -17,7 +17,7 @@ #include #include #include -#include "mtk_ecc.h" +#include /* NAND controller register definition */ #define NFI_CNFG (0x00) diff --git a/drivers/mtd/nand/raw/mtk_ecc.h b/include/linux/mtd/nand-ecc-mtk.h similarity index 100% rename from drivers/mtd/nand/raw/mtk_ecc.h rename to include/linux/mtd/nand-ecc-mtk.h From patchwork Sun Apr 24 03:25:24 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chuanhong Guo X-Patchwork-Id: 12824754 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id A8BBEC433EF for ; Sun, 24 Apr 2022 03:27:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=sk6BQA64nXLI9EKUMHVgq4kKQJQQZd5O2MMHdXV7A+Q=; b=NQWpSTRKnf61kd 6Uh/csEuUBO3cUTlIWwOOvcypiglhLa1zFjwTRQzrzDmJ1wFZia8NLRO2U9ZulFOX6yrUzSbbcfxi qwhP68JY1RXYdTlOfGTy6orYpnS/Zlii4icJtzbU/bB1gGRmx9+XtH9Lz1+DXPH8ohqefKBIH3H7q kIeJK5HgerKrIlOexZdWviqti9bIS+zjdXZIXbSIdQAcp7sp3J5l4g0FWEiFtZCsC4UieyedsD7IZ jQgdypnI6UL+XTEn5/JheLH4q+ieYjfUPEkvajcsQxWH8hTqZukH+KFs3a/VetkhOEbn7JvPY12+D GffTBWEQMY7WO4tKcDrQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1niStk-005eWL-Pi; Sun, 24 Apr 2022 03:26:56 +0000 Received: from mail-pg1-x529.google.com ([2607:f8b0:4864:20::529]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1niSt8-005eFz-Ha; Sun, 24 Apr 2022 03:26:23 +0000 Received: by mail-pg1-x529.google.com with SMTP id h5so10589510pgc.7; Sat, 23 Apr 2022 20:26:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=7H+kG9r0M7QsN7sjvm7fubOOiMiXJlA9+jxeVFg6sQ0=; b=NO4IbiYk9l366WYM7hEYrlMhLmaZHQLbqMJwojYqvLAtHNG/nLtcPrrA/Pjh81KnZg DmwhkHGtFtpJXloQdq0sm/XYC0xikVkT5pMLM2LwNECnOdwAaIEd5nyrYZKarPptHkXA ufxnkMI5UsF/SqK2rhnryjlKegWVGSWy9Z7O0VhbnaCSDu/Fmpy85lhpLSjKJrou2r0k p2J/9GAFvK9U+5PfuHegahomFjDA4vncpz8lV6tHTEQqlONbl+JqP9GyJ4EMdc5pJ9uo ynEs0JLWvoopBjEsXdgJ0Nocl4O8Vl0F9sLJDaMh9I5PM4KscxCVWWLImnSYaBl6qERr NMLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=7H+kG9r0M7QsN7sjvm7fubOOiMiXJlA9+jxeVFg6sQ0=; b=pHRXlPqFQLgWvI9ilnXnS7wOeM1Q+hPlGXZraCS9arBH1wFhexmHDDvjTocJv0ULZO F4U/rnrRyBE+wI2/e40lIt7/cR6XFgPhSe3z1d7VJMPPU7mbNZ+aWtRqj82FTnw6/IYX pVfkEK2O+DK/27qW7NvCKhX+8malG8LVGqOZUOlqRe82/q6drtAXswwktEcFOZP4yTVR AYFWhyPx/HBY/pObaOLG7ttOeo7Y8smBVefgTHHSDFoCCFstiVY1C23uzgDVPxapI/oH wWovZ02SHQyHXf0phi3ULa0wtsox83MA+lpdmzVDEqHrf7U7IHZ9xW6w2OBM2aCsp5Sq eStg== X-Gm-Message-State: AOAM531jSbhcudwW8gbMzglpNae06AhoVen+OwuOUST6QuSO+8k905do HL4RbAvz0FELp/ktzkNodmg= X-Google-Smtp-Source: ABdhPJz/YKwFLrGxZR5ThC9fXyTpM4BKnzygeygNsgDzapOF7QDfUDGrWRAiQ1SfyCjB7rRJ/FPSjQ== X-Received: by 2002:a65:60c1:0:b0:39d:9c28:909a with SMTP id r1-20020a6560c1000000b0039d9c28909amr9928518pgv.352.1650770777091; Sat, 23 Apr 2022 20:26:17 -0700 (PDT) Received: from guoguo-omen.lan ([222.201.153.219]) by smtp.gmail.com with ESMTPSA id r76-20020a632b4f000000b003820643e1c2sm5790724pgr.59.2022.04.23.20.26.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 23 Apr 2022 20:26:16 -0700 (PDT) From: Chuanhong Guo To: linux-spi@vger.kernel.org Cc: Chuanhong Guo , Daniel Golle , Mark Brown , Rob Herring , Krzysztof Kozlowski , Matthias Brugger , Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Roger Quadros , Thomas Bogendoerfer , Cai Huoqing , Florian Fainelli , Colin Ian King , Wolfram Sang , Paul Cercueil , Pratyush Yadav , Yu Kuai , devicetree@vger.kernel.org (open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS), linux-arm-kernel@lists.infradead.org (moderated list:ARM/Mediatek SoC support), linux-mediatek@lists.infradead.org (moderated list:ARM/Mediatek SoC support), linux-kernel@vger.kernel.org (open list), linux-mtd@lists.infradead.org (open list:NAND FLASH SUBSYSTEM) Subject: [PATCH v6 2/5] spi: add driver for MTK SPI NAND Flash Interface Date: Sun, 24 Apr 2022 11:25:24 +0800 Message-Id: <20220424032527.673605-3-gch981213@gmail.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220424032527.673605-1-gch981213@gmail.com> References: <20220424032527.673605-1-gch981213@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220423_202618_885874_304EA89C X-CRM114-Status: GOOD ( 27.95 ) X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+linux-mediatek=archiver.kernel.org@lists.infradead.org This driver implements support for the SPI-NAND mode of MTK NAND Flash Interface as a SPI-MEM controller with pipelined ECC capability. Signed-off-by: Chuanhong Guo Tested-by: Daniel Golle --- Change since v1: fix CI warnings Changes since v2: use streamed DMA api to avoid an extra memory copy during read make ECC engine config a per-nand context take user-requested ECC strength into account Change since v3: none Changes since v4: fix missing OOB write print page format with dev_dbg replace uint*_t copied from vendor driver with u* Changes since v5: add missing nfi mode register configuration in probe fix an off-by-one bug in mtk_snand_mac_io drivers/spi/Kconfig | 10 + drivers/spi/Makefile | 1 + drivers/spi/spi-mtk-snfi.c | 1470 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1481 insertions(+) create mode 100644 drivers/spi/spi-mtk-snfi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 6c28ca232444..83c3c2b57a22 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -590,6 +590,16 @@ config SPI_MTK_NOR SPI interface as well as several SPI NOR specific instructions via SPI MEM interface. +config SPI_MTK_SNFI + tristate "MediaTek SPI NAND Flash Interface" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on MTD_NAND_ECC_MEDIATEK + help + This enables support for SPI-NAND mode on the MediaTek NAND + Flash Interface found on MediaTek ARM SoCs. This controller + is implemented as a SPI-MEM controller with pipelined ECC + capcability. + config SPI_NPCM_FIU tristate "Nuvoton NPCM FLASH Interface Unit" depends on ARCH_NPCM || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3aa28ed3f761..51541ff17e67 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o obj-$(CONFIG_SPI_MT7621) += spi-mt7621.o obj-$(CONFIG_SPI_MTK_NOR) += spi-mtk-nor.o +obj-$(CONFIG_SPI_MTK_SNFI) += spi-mtk-snfi.o obj-$(CONFIG_SPI_MXIC) += spi-mxic.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NPCM_FIU) += spi-npcm-fiu.o diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c new file mode 100644 index 000000000000..2c556e304673 --- /dev/null +++ b/drivers/spi/spi-mtk-snfi.c @@ -0,0 +1,1470 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for the SPI-NAND mode of Mediatek NAND Flash Interface +// +// Copyright (c) 2022 Chuanhong Guo +// +// This driver is based on the SPI-NAND mtd driver from Mediatek SDK: +// +// Copyright (C) 2020 MediaTek Inc. +// Author: Weijie Gao +// +// This controller organize the page data as several interleaved sectors +// like the following: (sizeof(FDM + ECC) = snf->nfi_cfg.spare_size) +// +---------+------+------+---------+------+------+-----+ +// | Sector1 | FDM1 | ECC1 | Sector2 | FDM2 | ECC2 | ... | +// +---------+------+------+---------+------+------+-----+ +// With auto-format turned on, DMA only returns this part: +// +---------+---------+-----+ +// | Sector1 | Sector2 | ... | +// +---------+---------+-----+ +// The FDM data will be filled to the registers, and ECC parity data isn't +// accessible. +// With auto-format off, all ((Sector+FDM+ECC)*nsectors) will be read over DMA +// in it's original order shown in the first table. ECC can't be turned on when +// auto-format is off. +// +// However, Linux SPI-NAND driver expects the data returned as: +// +------+-----+ +// | Page | OOB | +// +------+-----+ +// where the page data is continuously stored instead of interleaved. +// So we assume all instructions matching the page_op template between ECC +// prepare_io_req and finish_io_req are for page cache r/w. +// Here's how this spi-mem driver operates when reading: +// 1. Always set snf->autofmt = true in prepare_io_req (even when ECC is off). +// 2. Perform page ops and let the controller fill the DMA bounce buffer with +// de-interleaved sector data and set FDM registers. +// 3. Return the data as: +// +---------+---------+-----+------+------+-----+ +// | Sector1 | Sector2 | ... | FDM1 | FDM2 | ... | +// +---------+---------+-----+------+------+-----+ +// 4. For other matching spi_mem ops outside a prepare/finish_io_req pair, +// read the data with auto-format off into the bounce buffer and copy +// needed data to the buffer specified in the request. +// +// Write requests operates in a similar manner. +// As a limitation of this strategy, we won't be able to access any ECC parity +// data at all in Linux. +// +// Here's the bad block mark situation on MTK chips: +// In older chips like mt7622, MTK uses the first FDM byte in the first sector +// as the bad block mark. After de-interleaving, this byte appears at [pagesize] +// in the returned data, which is the BBM position expected by kernel. However, +// the conventional bad block mark is the first byte of the OOB, which is part +// of the last sector data in the interleaved layout. Instead of fixing their +// hardware, MTK decided to address this inconsistency in software. On these +// later chips, the BootROM expects the following: +// 1. The [pagesize] byte on a nand page is used as BBM, which will appear at +// (page_size - (nsectors - 1) * spare_size) in the DMA buffer. +// 2. The original byte stored at that position in the DMA buffer will be stored +// as the first byte of the FDM section in the last sector. +// We can't disagree with the BootROM, so after de-interleaving, we need to +// perform the following swaps in read: +// 1. Store the BBM at [page_size - (nsectors - 1) * spare_size] to [page_size], +// which is the expected BBM position by kernel. +// 2. Store the page data byte at [pagesize + (nsectors-1) * fdm] back to +// [page_size - (nsectors - 1) * spare_size] +// Similarly, when writing, we need to perform swaps in the other direction. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NFI registers +#define NFI_CNFG 0x000 +#define CNFG_OP_MODE_S 12 +#define CNFG_OP_MODE_CUST 6 +#define CNFG_OP_MODE_PROGRAM 3 +#define CNFG_AUTO_FMT_EN BIT(9) +#define CNFG_HW_ECC_EN BIT(8) +#define CNFG_DMA_BURST_EN BIT(2) +#define CNFG_READ_MODE BIT(1) +#define CNFG_DMA_MODE BIT(0) + +#define NFI_PAGEFMT 0x0004 +#define NFI_SPARE_SIZE_LS_S 16 +#define NFI_FDM_ECC_NUM_S 12 +#define NFI_FDM_NUM_S 8 +#define NFI_SPARE_SIZE_S 4 +#define NFI_SEC_SEL_512 BIT(2) +#define NFI_PAGE_SIZE_S 0 +#define NFI_PAGE_SIZE_512_2K 0 +#define NFI_PAGE_SIZE_2K_4K 1 +#define NFI_PAGE_SIZE_4K_8K 2 +#define NFI_PAGE_SIZE_8K_16K 3 + +#define NFI_CON 0x008 +#define CON_SEC_NUM_S 12 +#define CON_BWR BIT(9) +#define CON_BRD BIT(8) +#define CON_NFI_RST BIT(1) +#define CON_FIFO_FLUSH BIT(0) + +#define NFI_INTR_EN 0x010 +#define NFI_INTR_STA 0x014 +#define NFI_IRQ_INTR_EN BIT(31) +#define NFI_IRQ_CUS_READ BIT(8) +#define NFI_IRQ_CUS_PG BIT(7) + +#define NFI_CMD 0x020 +#define NFI_CMD_DUMMY_READ 0x00 +#define NFI_CMD_DUMMY_WRITE 0x80 + +#define NFI_STRDATA 0x040 +#define STR_DATA BIT(0) + +#define NFI_STA 0x060 +#define NFI_NAND_FSM GENMASK(28, 24) +#define NFI_FSM GENMASK(19, 16) +#define READ_EMPTY BIT(12) + +#define NFI_FIFOSTA 0x064 +#define FIFO_WR_REMAIN_S 8 +#define FIFO_RD_REMAIN_S 0 + +#define NFI_ADDRCNTR 0x070 +#define SEC_CNTR GENMASK(16, 12) +#define SEC_CNTR_S 12 +#define NFI_SEC_CNTR(val) (((val)&SEC_CNTR) >> SEC_CNTR_S) + +#define NFI_STRADDR 0x080 + +#define NFI_BYTELEN 0x084 +#define BUS_SEC_CNTR(val) (((val)&SEC_CNTR) >> SEC_CNTR_S) + +#define NFI_FDM0L 0x0a0 +#define NFI_FDM0M 0x0a4 +#define NFI_FDML(n) (NFI_FDM0L + (n)*8) +#define NFI_FDMM(n) (NFI_FDM0M + (n)*8) + +#define NFI_DEBUG_CON1 0x220 +#define WBUF_EN BIT(2) + +#define NFI_MASTERSTA 0x224 +#define MAS_ADDR GENMASK(11, 9) +#define MAS_RD GENMASK(8, 6) +#define MAS_WR GENMASK(5, 3) +#define MAS_RDDLY GENMASK(2, 0) +#define NFI_MASTERSTA_MASK_7622 (MAS_ADDR | MAS_RD | MAS_WR | MAS_RDDLY) + +// SNFI registers +#define SNF_MAC_CTL 0x500 +#define MAC_XIO_SEL BIT(4) +#define SF_MAC_EN BIT(3) +#define SF_TRIG BIT(2) +#define WIP_READY BIT(1) +#define WIP BIT(0) + +#define SNF_MAC_OUTL 0x504 +#define SNF_MAC_INL 0x508 + +#define SNF_RD_CTL2 0x510 +#define DATA_READ_DUMMY_S 8 +#define DATA_READ_MAX_DUMMY 0xf +#define DATA_READ_CMD_S 0 + +#define SNF_RD_CTL3 0x514 + +#define SNF_PG_CTL1 0x524 +#define PG_LOAD_CMD_S 8 + +#define SNF_PG_CTL2 0x528 + +#define SNF_MISC_CTL 0x538 +#define SW_RST BIT(28) +#define FIFO_RD_LTC_S 25 +#define PG_LOAD_X4_EN BIT(20) +#define DATA_READ_MODE_S 16 +#define DATA_READ_MODE GENMASK(18, 16) +#define DATA_READ_MODE_X1 0 +#define DATA_READ_MODE_X2 1 +#define DATA_READ_MODE_X4 2 +#define DATA_READ_MODE_DUAL 5 +#define DATA_READ_MODE_QUAD 6 +#define PG_LOAD_CUSTOM_EN BIT(7) +#define DATARD_CUSTOM_EN BIT(6) +#define CS_DESELECT_CYC_S 0 + +#define SNF_MISC_CTL2 0x53c +#define PROGRAM_LOAD_BYTE_NUM_S 16 +#define READ_DATA_BYTE_NUM_S 11 + +#define SNF_DLY_CTL3 0x548 +#define SFCK_SAM_DLY_S 0 + +#define SNF_STA_CTL1 0x550 +#define CUS_PG_DONE BIT(28) +#define CUS_READ_DONE BIT(27) +#define SPI_STATE_S 0 +#define SPI_STATE GENMASK(3, 0) + +#define SNF_CFG 0x55c +#define SPI_MODE BIT(0) + +#define SNF_GPRAM 0x800 +#define SNF_GPRAM_SIZE 0xa0 + +#define SNFI_POLL_INTERVAL 1000000 + +static const u8 mt7622_spare_sizes[] = { 16, 26, 27, 28 }; + +struct mtk_snand_caps { + u16 sector_size; + u16 max_sectors; + u16 fdm_size; + u16 fdm_ecc_size; + u16 fifo_size; + + bool bbm_swap; + bool empty_page_check; + u32 mastersta_mask; + + const u8 *spare_sizes; + u32 num_spare_size; +}; + +static const struct mtk_snand_caps mt7622_snand_caps = { + .sector_size = 512, + .max_sectors = 8, + .fdm_size = 8, + .fdm_ecc_size = 1, + .fifo_size = 32, + .bbm_swap = false, + .empty_page_check = false, + .mastersta_mask = NFI_MASTERSTA_MASK_7622, + .spare_sizes = mt7622_spare_sizes, + .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes) +}; + +static const struct mtk_snand_caps mt7629_snand_caps = { + .sector_size = 512, + .max_sectors = 8, + .fdm_size = 8, + .fdm_ecc_size = 1, + .fifo_size = 32, + .bbm_swap = true, + .empty_page_check = false, + .mastersta_mask = NFI_MASTERSTA_MASK_7622, + .spare_sizes = mt7622_spare_sizes, + .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes) +}; + +struct mtk_snand_conf { + size_t page_size; + size_t oob_size; + u8 nsectors; + u8 spare_size; +}; + +struct mtk_snand { + struct spi_controller *ctlr; + struct device *dev; + struct clk *nfi_clk; + struct clk *pad_clk; + void __iomem *nfi_base; + int irq; + struct completion op_done; + const struct mtk_snand_caps *caps; + struct mtk_ecc_config *ecc_cfg; + struct mtk_ecc *ecc; + struct mtk_snand_conf nfi_cfg; + struct mtk_ecc_stats ecc_stats; + struct nand_ecc_engine ecc_eng; + bool autofmt; + u8 *buf; + size_t buf_len; +}; + +static struct mtk_snand *nand_to_mtk_snand(struct nand_device *nand) +{ + struct nand_ecc_engine *eng = nand->ecc.engine; + + return container_of(eng, struct mtk_snand, ecc_eng); +} + +static inline int snand_prepare_bouncebuf(struct mtk_snand *snf, size_t size) +{ + if (snf->buf_len >= size) + return 0; + kfree(snf->buf); + snf->buf = kmalloc(size, GFP_KERNEL); + if (!snf->buf) + return -ENOMEM; + snf->buf_len = size; + memset(snf->buf, 0xff, snf->buf_len); + return 0; +} + +static inline u32 nfi_read32(struct mtk_snand *snf, u32 reg) +{ + return readl(snf->nfi_base + reg); +} + +static inline void nfi_write32(struct mtk_snand *snf, u32 reg, u32 val) +{ + writel(val, snf->nfi_base + reg); +} + +static inline void nfi_write16(struct mtk_snand *snf, u32 reg, u16 val) +{ + writew(val, snf->nfi_base + reg); +} + +static inline void nfi_rmw32(struct mtk_snand *snf, u32 reg, u32 clr, u32 set) +{ + u32 val; + + val = readl(snf->nfi_base + reg); + val &= ~clr; + val |= set; + writel(val, snf->nfi_base + reg); +} + +static void nfi_read_data(struct mtk_snand *snf, u32 reg, u8 *data, u32 len) +{ + u32 i, val = 0, es = sizeof(u32); + + for (i = reg; i < reg + len; i++) { + if (i == reg || i % es == 0) + val = nfi_read32(snf, i & ~(es - 1)); + + *data++ = (u8)(val >> (8 * (i % es))); + } +} + +static int mtk_nfi_reset(struct mtk_snand *snf) +{ + u32 val, fifo_mask; + int ret; + + nfi_write32(snf, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST); + + ret = readw_poll_timeout(snf->nfi_base + NFI_MASTERSTA, val, + !(val & snf->caps->mastersta_mask), 0, + SNFI_POLL_INTERVAL); + if (ret) { + dev_err(snf->dev, "NFI master is still busy after reset\n"); + return ret; + } + + ret = readl_poll_timeout(snf->nfi_base + NFI_STA, val, + !(val & (NFI_FSM | NFI_NAND_FSM)), 0, + SNFI_POLL_INTERVAL); + if (ret) { + dev_err(snf->dev, "Failed to reset NFI\n"); + return ret; + } + + fifo_mask = ((snf->caps->fifo_size - 1) << FIFO_RD_REMAIN_S) | + ((snf->caps->fifo_size - 1) << FIFO_WR_REMAIN_S); + ret = readw_poll_timeout(snf->nfi_base + NFI_FIFOSTA, val, + !(val & fifo_mask), 0, SNFI_POLL_INTERVAL); + if (ret) { + dev_err(snf->dev, "NFI FIFOs are not empty\n"); + return ret; + } + + return 0; +} + +static int mtk_snand_mac_reset(struct mtk_snand *snf) +{ + int ret; + u32 val; + + nfi_rmw32(snf, SNF_MISC_CTL, 0, SW_RST); + + ret = readl_poll_timeout(snf->nfi_base + SNF_STA_CTL1, val, + !(val & SPI_STATE), 0, SNFI_POLL_INTERVAL); + if (ret) + dev_err(snf->dev, "Failed to reset SNFI MAC\n"); + + nfi_write32(snf, SNF_MISC_CTL, + (2 << FIFO_RD_LTC_S) | (10 << CS_DESELECT_CYC_S)); + + return ret; +} + +static int mtk_snand_mac_trigger(struct mtk_snand *snf, u32 outlen, u32 inlen) +{ + int ret; + u32 val; + + nfi_write32(snf, SNF_MAC_CTL, SF_MAC_EN); + nfi_write32(snf, SNF_MAC_OUTL, outlen); + nfi_write32(snf, SNF_MAC_INL, inlen); + + nfi_write32(snf, SNF_MAC_CTL, SF_MAC_EN | SF_TRIG); + + ret = readl_poll_timeout(snf->nfi_base + SNF_MAC_CTL, val, + val & WIP_READY, 0, SNFI_POLL_INTERVAL); + if (ret) { + dev_err(snf->dev, "Timed out waiting for WIP_READY\n"); + goto cleanup; + } + + ret = readl_poll_timeout(snf->nfi_base + SNF_MAC_CTL, val, !(val & WIP), + 0, SNFI_POLL_INTERVAL); + if (ret) + dev_err(snf->dev, "Timed out waiting for WIP cleared\n"); + +cleanup: + nfi_write32(snf, SNF_MAC_CTL, 0); + + return ret; +} + +static int mtk_snand_mac_io(struct mtk_snand *snf, const struct spi_mem_op *op) +{ + u32 rx_len = 0; + u32 reg_offs = 0; + u32 val = 0; + const u8 *tx_buf = NULL; + u8 *rx_buf = NULL; + int i, ret; + u8 b; + + if (op->data.dir == SPI_MEM_DATA_IN) { + rx_len = op->data.nbytes; + rx_buf = op->data.buf.in; + } else { + tx_buf = op->data.buf.out; + } + + mtk_snand_mac_reset(snf); + + for (i = 0; i < op->cmd.nbytes; i++, reg_offs++) { + b = (op->cmd.opcode >> ((op->cmd.nbytes - i - 1) * 8)) & 0xff; + val |= b << (8 * (reg_offs % 4)); + if (reg_offs % 4 == 3) { + nfi_write32(snf, SNF_GPRAM + reg_offs - 3, val); + val = 0; + } + } + + for (i = 0; i < op->addr.nbytes; i++, reg_offs++) { + b = (op->addr.val >> ((op->addr.nbytes - i - 1) * 8)) & 0xff; + val |= b << (8 * (reg_offs % 4)); + if (reg_offs % 4 == 3) { + nfi_write32(snf, SNF_GPRAM + reg_offs - 3, val); + val = 0; + } + } + + for (i = 0; i < op->dummy.nbytes; i++, reg_offs++) { + if (reg_offs % 4 == 3) { + nfi_write32(snf, SNF_GPRAM + reg_offs - 3, val); + val = 0; + } + } + + if (op->data.dir == SPI_MEM_DATA_OUT) { + for (i = 0; i < op->data.nbytes; i++, reg_offs++) { + val |= tx_buf[i] << (8 * (reg_offs % 4)); + if (reg_offs % 4 == 3) { + nfi_write32(snf, SNF_GPRAM + reg_offs - 3, val); + val = 0; + } + } + } + + if (reg_offs % 4) + nfi_write32(snf, SNF_GPRAM + (reg_offs & ~3), val); + + for (i = 0; i < reg_offs; i += 4) + dev_dbg(snf->dev, "%d: %08X", i, + nfi_read32(snf, SNF_GPRAM + i)); + + dev_dbg(snf->dev, "SNF TX: %u RX: %u", reg_offs, rx_len); + + ret = mtk_snand_mac_trigger(snf, reg_offs, rx_len); + if (ret) + return ret; + + if (!rx_len) + return 0; + + nfi_read_data(snf, SNF_GPRAM + reg_offs, rx_buf, rx_len); + return 0; +} + +static int mtk_snand_setup_pagefmt(struct mtk_snand *snf, u32 page_size, + u32 oob_size) +{ + int spare_idx = -1; + u32 spare_size, spare_size_shift, pagesize_idx; + u32 sector_size_512; + u8 nsectors; + int i; + + // skip if it's already configured as required. + if (snf->nfi_cfg.page_size == page_size && + snf->nfi_cfg.oob_size == oob_size) + return 0; + + nsectors = page_size / snf->caps->sector_size; + if (nsectors > snf->caps->max_sectors) { + dev_err(snf->dev, "too many sectors required.\n"); + goto err; + } + + if (snf->caps->sector_size == 512) { + sector_size_512 = NFI_SEC_SEL_512; + spare_size_shift = NFI_SPARE_SIZE_S; + } else { + sector_size_512 = 0; + spare_size_shift = NFI_SPARE_SIZE_LS_S; + } + + switch (page_size) { + case SZ_512: + pagesize_idx = NFI_PAGE_SIZE_512_2K; + break; + case SZ_2K: + if (snf->caps->sector_size == 512) + pagesize_idx = NFI_PAGE_SIZE_2K_4K; + else + pagesize_idx = NFI_PAGE_SIZE_512_2K; + break; + case SZ_4K: + if (snf->caps->sector_size == 512) + pagesize_idx = NFI_PAGE_SIZE_4K_8K; + else + pagesize_idx = NFI_PAGE_SIZE_2K_4K; + break; + case SZ_8K: + if (snf->caps->sector_size == 512) + pagesize_idx = NFI_PAGE_SIZE_8K_16K; + else + pagesize_idx = NFI_PAGE_SIZE_4K_8K; + break; + case SZ_16K: + pagesize_idx = NFI_PAGE_SIZE_8K_16K; + break; + default: + dev_err(snf->dev, "unsupported page size.\n"); + goto err; + } + + spare_size = oob_size / nsectors; + // If we're using the 1KB sector size, HW will automatically double the + // spare size. We should only use half of the value in this case. + if (snf->caps->sector_size == 1024) + spare_size /= 2; + + for (i = snf->caps->num_spare_size - 1; i >= 0; i--) { + if (snf->caps->spare_sizes[i] <= spare_size) { + spare_size = snf->caps->spare_sizes[i]; + if (snf->caps->sector_size == 1024) + spare_size *= 2; + spare_idx = i; + break; + } + } + + if (spare_idx < 0) { + dev_err(snf->dev, "unsupported spare size: %u\n", spare_size); + goto err; + } + + nfi_write32(snf, NFI_PAGEFMT, + (snf->caps->fdm_ecc_size << NFI_FDM_ECC_NUM_S) | + (snf->caps->fdm_size << NFI_FDM_NUM_S) | + (spare_idx << spare_size_shift) | + (pagesize_idx << NFI_PAGE_SIZE_S) | + sector_size_512); + + snf->nfi_cfg.page_size = page_size; + snf->nfi_cfg.oob_size = oob_size; + snf->nfi_cfg.nsectors = nsectors; + snf->nfi_cfg.spare_size = spare_size; + + dev_dbg(snf->dev, "page format: (%u + %u) * %u\n", + snf->caps->sector_size, spare_size, nsectors); + return snand_prepare_bouncebuf(snf, page_size + oob_size); +err: + dev_err(snf->dev, "page size %u + %u is not supported\n", page_size, + oob_size); + return -EOPNOTSUPP; +} + +static int mtk_snand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobecc) +{ + // ECC area is not accessible + return -ERANGE; +} + +static int mtk_snand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobfree) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + struct mtk_snand *ms = nand_to_mtk_snand(nand); + + if (section >= ms->nfi_cfg.nsectors) + return -ERANGE; + + oobfree->length = ms->caps->fdm_size - 1; + oobfree->offset = section * ms->caps->fdm_size + 1; + return 0; +} + +static const struct mtd_ooblayout_ops mtk_snand_ooblayout = { + .ecc = mtk_snand_ooblayout_ecc, + .free = mtk_snand_ooblayout_free, +}; + +static int mtk_snand_ecc_init_ctx(struct nand_device *nand) +{ + struct mtk_snand *snf = nand_to_mtk_snand(nand); + struct nand_ecc_props *conf = &nand->ecc.ctx.conf; + struct nand_ecc_props *reqs = &nand->ecc.requirements; + struct nand_ecc_props *user = &nand->ecc.user_conf; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int step_size = 0, strength = 0, desired_correction = 0, steps; + bool ecc_user = false; + int ret; + u32 parity_bits, max_ecc_bytes; + struct mtk_ecc_config *ecc_cfg; + + ret = mtk_snand_setup_pagefmt(snf, nand->memorg.pagesize, + nand->memorg.oobsize); + if (ret) + return ret; + + ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL); + if (!ecc_cfg) + return -ENOMEM; + + nand->ecc.ctx.priv = ecc_cfg; + + if (user->step_size && user->strength) { + step_size = user->step_size; + strength = user->strength; + ecc_user = true; + } else if (reqs->step_size && reqs->strength) { + step_size = reqs->step_size; + strength = reqs->strength; + } + + if (step_size && strength) { + steps = mtd->writesize / step_size; + desired_correction = steps * strength; + strength = desired_correction / snf->nfi_cfg.nsectors; + } + + ecc_cfg->mode = ECC_NFI_MODE; + ecc_cfg->sectors = snf->nfi_cfg.nsectors; + ecc_cfg->len = snf->caps->sector_size + snf->caps->fdm_ecc_size; + + // calculate the max possible strength under current page format + parity_bits = mtk_ecc_get_parity_bits(snf->ecc); + max_ecc_bytes = snf->nfi_cfg.spare_size - snf->caps->fdm_size; + ecc_cfg->strength = max_ecc_bytes * 8 / parity_bits; + mtk_ecc_adjust_strength(snf->ecc, &ecc_cfg->strength); + + // if there's a user requested strength, find the minimum strength that + // meets the requirement. Otherwise use the maximum strength which is + // expected by BootROM. + if (ecc_user && strength) { + u32 s_next = ecc_cfg->strength - 1; + + while (1) { + mtk_ecc_adjust_strength(snf->ecc, &s_next); + if (s_next >= ecc_cfg->strength) + break; + if (s_next < strength) + break; + s_next = ecc_cfg->strength - 1; + } + } + + mtd_set_ooblayout(mtd, &mtk_snand_ooblayout); + + conf->step_size = snf->caps->sector_size; + conf->strength = ecc_cfg->strength; + + if (ecc_cfg->strength < strength) + dev_warn(snf->dev, "unable to fulfill ECC of %u bits.\n", + strength); + dev_info(snf->dev, "ECC strength: %u bits per %u bytes\n", + ecc_cfg->strength, snf->caps->sector_size); + + return 0; +} + +static void mtk_snand_ecc_cleanup_ctx(struct nand_device *nand) +{ + struct mtk_ecc_config *ecc_cfg = nand_to_ecc_ctx(nand); + + kfree(ecc_cfg); +} + +static int mtk_snand_ecc_prepare_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + struct mtk_snand *snf = nand_to_mtk_snand(nand); + struct mtk_ecc_config *ecc_cfg = nand_to_ecc_ctx(nand); + int ret; + + ret = mtk_snand_setup_pagefmt(snf, nand->memorg.pagesize, + nand->memorg.oobsize); + if (ret) + return ret; + snf->autofmt = true; + snf->ecc_cfg = ecc_cfg; + return 0; +} + +static int mtk_snand_ecc_finish_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + struct mtk_snand *snf = nand_to_mtk_snand(nand); + struct mtd_info *mtd = nanddev_to_mtd(nand); + + snf->ecc_cfg = NULL; + snf->autofmt = false; + if ((req->mode == MTD_OPS_RAW) || (req->type != NAND_PAGE_READ)) + return 0; + + if (snf->ecc_stats.failed) + mtd->ecc_stats.failed += snf->ecc_stats.failed; + mtd->ecc_stats.corrected += snf->ecc_stats.corrected; + return snf->ecc_stats.failed ? -EBADMSG : snf->ecc_stats.bitflips; +} + +static struct nand_ecc_engine_ops mtk_snfi_ecc_engine_ops = { + .init_ctx = mtk_snand_ecc_init_ctx, + .cleanup_ctx = mtk_snand_ecc_cleanup_ctx, + .prepare_io_req = mtk_snand_ecc_prepare_io_req, + .finish_io_req = mtk_snand_ecc_finish_io_req, +}; + +static void mtk_snand_read_fdm(struct mtk_snand *snf, u8 *buf) +{ + u32 vall, valm; + u8 *oobptr = buf; + int i, j; + + for (i = 0; i < snf->nfi_cfg.nsectors; i++) { + vall = nfi_read32(snf, NFI_FDML(i)); + valm = nfi_read32(snf, NFI_FDMM(i)); + + for (j = 0; j < snf->caps->fdm_size; j++) + oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8); + + oobptr += snf->caps->fdm_size; + } +} + +static void mtk_snand_write_fdm(struct mtk_snand *snf, const u8 *buf) +{ + u32 fdm_size = snf->caps->fdm_size; + const u8 *oobptr = buf; + u32 vall, valm; + int i, j; + + for (i = 0; i < snf->nfi_cfg.nsectors; i++) { + vall = 0; + valm = 0; + + for (j = 0; j < 8; j++) { + if (j < 4) + vall |= (j < fdm_size ? oobptr[j] : 0xff) + << (j * 8); + else + valm |= (j < fdm_size ? oobptr[j] : 0xff) + << ((j - 4) * 8); + } + + nfi_write32(snf, NFI_FDML(i), vall); + nfi_write32(snf, NFI_FDMM(i), valm); + + oobptr += fdm_size; + } +} + +static void mtk_snand_bm_swap(struct mtk_snand *snf, u8 *buf) +{ + u32 buf_bbm_pos, fdm_bbm_pos; + + if (!snf->caps->bbm_swap || snf->nfi_cfg.nsectors == 1) + return; + + // swap [pagesize] byte on nand with the first fdm byte + // in the last sector. + buf_bbm_pos = snf->nfi_cfg.page_size - + (snf->nfi_cfg.nsectors - 1) * snf->nfi_cfg.spare_size; + fdm_bbm_pos = snf->nfi_cfg.page_size + + (snf->nfi_cfg.nsectors - 1) * snf->caps->fdm_size; + + swap(snf->buf[fdm_bbm_pos], buf[buf_bbm_pos]); +} + +static void mtk_snand_fdm_bm_swap(struct mtk_snand *snf) +{ + u32 fdm_bbm_pos1, fdm_bbm_pos2; + + if (!snf->caps->bbm_swap || snf->nfi_cfg.nsectors == 1) + return; + + // swap the first fdm byte in the first and the last sector. + fdm_bbm_pos1 = snf->nfi_cfg.page_size; + fdm_bbm_pos2 = snf->nfi_cfg.page_size + + (snf->nfi_cfg.nsectors - 1) * snf->caps->fdm_size; + swap(snf->buf[fdm_bbm_pos1], snf->buf[fdm_bbm_pos2]); +} + +static int mtk_snand_read_page_cache(struct mtk_snand *snf, + const struct spi_mem_op *op) +{ + u8 *buf = snf->buf; + u8 *buf_fdm = buf + snf->nfi_cfg.page_size; + // the address part to be sent by the controller + u32 op_addr = op->addr.val; + // where to start copying data from bounce buffer + u32 rd_offset = 0; + u32 dummy_clk = (op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth); + u32 op_mode = 0; + u32 dma_len = snf->buf_len; + int ret = 0; + u32 rd_mode, rd_bytes, val; + dma_addr_t buf_dma; + + if (snf->autofmt) { + u32 last_bit; + u32 mask; + + dma_len = snf->nfi_cfg.page_size; + op_mode = CNFG_AUTO_FMT_EN; + if (op->data.ecc) + op_mode |= CNFG_HW_ECC_EN; + // extract the plane bit: + // Find the highest bit set in (pagesize+oobsize). + // Bits higher than that in op->addr are kept and sent over SPI + // Lower bits are used as an offset for copying data from DMA + // bounce buffer. + last_bit = fls(snf->nfi_cfg.page_size + snf->nfi_cfg.oob_size); + mask = (1 << last_bit) - 1; + rd_offset = op_addr & mask; + op_addr &= ~mask; + + // check if we can dma to the caller memory + if (rd_offset == 0 && op->data.nbytes >= snf->nfi_cfg.page_size) + buf = op->data.buf.in; + } + mtk_snand_mac_reset(snf); + mtk_nfi_reset(snf); + + // command and dummy cycles + nfi_write32(snf, SNF_RD_CTL2, + (dummy_clk << DATA_READ_DUMMY_S) | + (op->cmd.opcode << DATA_READ_CMD_S)); + + // read address + nfi_write32(snf, SNF_RD_CTL3, op_addr); + + // Set read op_mode + if (op->data.buswidth == 4) + rd_mode = op->addr.buswidth == 4 ? DATA_READ_MODE_QUAD : + DATA_READ_MODE_X4; + else if (op->data.buswidth == 2) + rd_mode = op->addr.buswidth == 2 ? DATA_READ_MODE_DUAL : + DATA_READ_MODE_X2; + else + rd_mode = DATA_READ_MODE_X1; + rd_mode <<= DATA_READ_MODE_S; + nfi_rmw32(snf, SNF_MISC_CTL, DATA_READ_MODE, + rd_mode | DATARD_CUSTOM_EN); + + // Set bytes to read + rd_bytes = (snf->nfi_cfg.spare_size + snf->caps->sector_size) * + snf->nfi_cfg.nsectors; + nfi_write32(snf, SNF_MISC_CTL2, + (rd_bytes << PROGRAM_LOAD_BYTE_NUM_S) | rd_bytes); + + // NFI read prepare + nfi_write16(snf, NFI_CNFG, + (CNFG_OP_MODE_CUST << CNFG_OP_MODE_S) | CNFG_DMA_BURST_EN | + CNFG_READ_MODE | CNFG_DMA_MODE | op_mode); + + nfi_write32(snf, NFI_CON, (snf->nfi_cfg.nsectors << CON_SEC_NUM_S)); + + buf_dma = dma_map_single(snf->dev, buf, dma_len, DMA_FROM_DEVICE); + if (dma_mapping_error(snf->dev, buf_dma)) { + dev_err(snf->dev, "DMA mapping failed.\n"); + goto cleanup; + } + nfi_write32(snf, NFI_STRADDR, buf_dma); + if (op->data.ecc) { + snf->ecc_cfg->op = ECC_DECODE; + ret = mtk_ecc_enable(snf->ecc, snf->ecc_cfg); + if (ret) + goto cleanup_dma; + } + // Prepare for custom read interrupt + nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_READ); + reinit_completion(&snf->op_done); + + // Trigger NFI into custom mode + nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_READ); + + // Start DMA read + nfi_rmw32(snf, NFI_CON, 0, CON_BRD); + nfi_write16(snf, NFI_STRDATA, STR_DATA); + + if (!wait_for_completion_timeout( + &snf->op_done, usecs_to_jiffies(SNFI_POLL_INTERVAL))) { + dev_err(snf->dev, "DMA timed out for reading from cache.\n"); + ret = -ETIMEDOUT; + goto cleanup; + } + + // Wait for BUS_SEC_CNTR returning expected value + ret = readl_poll_timeout(snf->nfi_base + NFI_BYTELEN, val, + BUS_SEC_CNTR(val) >= snf->nfi_cfg.nsectors, 0, + SNFI_POLL_INTERVAL); + if (ret) { + dev_err(snf->dev, "Timed out waiting for BUS_SEC_CNTR\n"); + goto cleanup2; + } + + // Wait for bus becoming idle + ret = readl_poll_timeout(snf->nfi_base + NFI_MASTERSTA, val, + !(val & snf->caps->mastersta_mask), 0, + SNFI_POLL_INTERVAL); + if (ret) { + dev_err(snf->dev, "Timed out waiting for bus becoming idle\n"); + goto cleanup2; + } + + if (op->data.ecc) { + ret = mtk_ecc_wait_done(snf->ecc, ECC_DECODE); + if (ret) { + dev_err(snf->dev, "wait ecc done timeout\n"); + goto cleanup2; + } + // save status before disabling ecc + mtk_ecc_get_stats(snf->ecc, &snf->ecc_stats, + snf->nfi_cfg.nsectors); + } + + dma_unmap_single(snf->dev, buf_dma, dma_len, DMA_FROM_DEVICE); + + if (snf->autofmt) { + mtk_snand_read_fdm(snf, buf_fdm); + if (snf->caps->bbm_swap) { + mtk_snand_bm_swap(snf, buf); + mtk_snand_fdm_bm_swap(snf); + } + } + + // copy data back + if (nfi_read32(snf, NFI_STA) & READ_EMPTY) { + memset(op->data.buf.in, 0xff, op->data.nbytes); + snf->ecc_stats.bitflips = 0; + snf->ecc_stats.failed = 0; + snf->ecc_stats.corrected = 0; + } else { + if (buf == op->data.buf.in) { + u32 cap_len = snf->buf_len - snf->nfi_cfg.page_size; + u32 req_left = op->data.nbytes - snf->nfi_cfg.page_size; + + if (req_left) + memcpy(op->data.buf.in + snf->nfi_cfg.page_size, + buf_fdm, + cap_len < req_left ? cap_len : req_left); + } else if (rd_offset < snf->buf_len) { + u32 cap_len = snf->buf_len - rd_offset; + + if (op->data.nbytes < cap_len) + cap_len = op->data.nbytes; + memcpy(op->data.buf.in, snf->buf + rd_offset, cap_len); + } + } +cleanup2: + if (op->data.ecc) + mtk_ecc_disable(snf->ecc); +cleanup_dma: + // unmap dma only if any error happens. (otherwise it's done before + // data copying) + if (ret) + dma_unmap_single(snf->dev, buf_dma, dma_len, DMA_FROM_DEVICE); +cleanup: + // Stop read + nfi_write32(snf, NFI_CON, 0); + nfi_write16(snf, NFI_CNFG, 0); + + // Clear SNF done flag + nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_READ_DONE); + nfi_write32(snf, SNF_STA_CTL1, 0); + + // Disable interrupt + nfi_read32(snf, NFI_INTR_STA); + nfi_write32(snf, NFI_INTR_EN, 0); + + nfi_rmw32(snf, SNF_MISC_CTL, DATARD_CUSTOM_EN, 0); + return ret; +} + +static int mtk_snand_write_page_cache(struct mtk_snand *snf, + const struct spi_mem_op *op) +{ + // the address part to be sent by the controller + u32 op_addr = op->addr.val; + // where to start copying data from bounce buffer + u32 wr_offset = 0; + u32 op_mode = 0; + int ret = 0; + u32 wr_mode = 0; + u32 dma_len = snf->buf_len; + u32 wr_bytes, val; + size_t cap_len; + dma_addr_t buf_dma; + + if (snf->autofmt) { + u32 last_bit; + u32 mask; + + dma_len = snf->nfi_cfg.page_size; + op_mode = CNFG_AUTO_FMT_EN; + if (op->data.ecc) + op_mode |= CNFG_HW_ECC_EN; + + last_bit = fls(snf->nfi_cfg.page_size + snf->nfi_cfg.oob_size); + mask = (1 << last_bit) - 1; + wr_offset = op_addr & mask; + op_addr &= ~mask; + } + mtk_snand_mac_reset(snf); + mtk_nfi_reset(snf); + + if (wr_offset) + memset(snf->buf, 0xff, wr_offset); + + cap_len = snf->buf_len - wr_offset; + if (op->data.nbytes < cap_len) + cap_len = op->data.nbytes; + memcpy(snf->buf + wr_offset, op->data.buf.out, cap_len); + if (snf->autofmt) { + if (snf->caps->bbm_swap) { + mtk_snand_fdm_bm_swap(snf); + mtk_snand_bm_swap(snf, snf->buf); + } + mtk_snand_write_fdm(snf, snf->buf + snf->nfi_cfg.page_size); + } + + // Command + nfi_write32(snf, SNF_PG_CTL1, (op->cmd.opcode << PG_LOAD_CMD_S)); + + // write address + nfi_write32(snf, SNF_PG_CTL2, op_addr); + + // Set read op_mode + if (op->data.buswidth == 4) + wr_mode = PG_LOAD_X4_EN; + + nfi_rmw32(snf, SNF_MISC_CTL, PG_LOAD_X4_EN, + wr_mode | PG_LOAD_CUSTOM_EN); + + // Set bytes to write + wr_bytes = (snf->nfi_cfg.spare_size + snf->caps->sector_size) * + snf->nfi_cfg.nsectors; + nfi_write32(snf, SNF_MISC_CTL2, + (wr_bytes << PROGRAM_LOAD_BYTE_NUM_S) | wr_bytes); + + // NFI write prepare + nfi_write16(snf, NFI_CNFG, + (CNFG_OP_MODE_PROGRAM << CNFG_OP_MODE_S) | + CNFG_DMA_BURST_EN | CNFG_DMA_MODE | op_mode); + + nfi_write32(snf, NFI_CON, (snf->nfi_cfg.nsectors << CON_SEC_NUM_S)); + buf_dma = dma_map_single(snf->dev, snf->buf, dma_len, DMA_TO_DEVICE); + if (dma_mapping_error(snf->dev, buf_dma)) { + dev_err(snf->dev, "DMA mapping failed.\n"); + goto cleanup; + } + nfi_write32(snf, NFI_STRADDR, buf_dma); + if (op->data.ecc) { + snf->ecc_cfg->op = ECC_ENCODE; + ret = mtk_ecc_enable(snf->ecc, snf->ecc_cfg); + if (ret) + goto cleanup_dma; + } + // Prepare for custom write interrupt + nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_PG); + reinit_completion(&snf->op_done); + ; + + // Trigger NFI into custom mode + nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_WRITE); + + // Start DMA write + nfi_rmw32(snf, NFI_CON, 0, CON_BWR); + nfi_write16(snf, NFI_STRDATA, STR_DATA); + + if (!wait_for_completion_timeout( + &snf->op_done, usecs_to_jiffies(SNFI_POLL_INTERVAL))) { + dev_err(snf->dev, "DMA timed out for program load.\n"); + ret = -ETIMEDOUT; + goto cleanup_ecc; + } + + // Wait for NFI_SEC_CNTR returning expected value + ret = readl_poll_timeout(snf->nfi_base + NFI_ADDRCNTR, val, + NFI_SEC_CNTR(val) >= snf->nfi_cfg.nsectors, 0, + SNFI_POLL_INTERVAL); + if (ret) + dev_err(snf->dev, "Timed out waiting for NFI_SEC_CNTR\n"); + +cleanup_ecc: + if (op->data.ecc) + mtk_ecc_disable(snf->ecc); +cleanup_dma: + dma_unmap_single(snf->dev, buf_dma, dma_len, DMA_TO_DEVICE); +cleanup: + // Stop write + nfi_write32(snf, NFI_CON, 0); + nfi_write16(snf, NFI_CNFG, 0); + + // Clear SNF done flag + nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_PG_DONE); + nfi_write32(snf, SNF_STA_CTL1, 0); + + // Disable interrupt + nfi_read32(snf, NFI_INTR_STA); + nfi_write32(snf, NFI_INTR_EN, 0); + + nfi_rmw32(snf, SNF_MISC_CTL, PG_LOAD_CUSTOM_EN, 0); + + return ret; +} + +/** + * mtk_snand_is_page_ops() - check if the op is a controller supported page op. + * @op spi-mem op to check + * + * Check whether op can be executed with read_from_cache or program_load + * mode in the controller. + * This controller can execute typical Read From Cache and Program Load + * instructions found on SPI-NAND with 2-byte address. + * DTR and cmd buswidth & nbytes should be checked before calling this. + * + * Return: true if the op matches the instruction template + */ +static bool mtk_snand_is_page_ops(const struct spi_mem_op *op) +{ + if (op->addr.nbytes != 2) + return false; + + if (op->addr.buswidth != 1 && op->addr.buswidth != 2 && + op->addr.buswidth != 4) + return false; + + // match read from page instructions + if (op->data.dir == SPI_MEM_DATA_IN) { + // check dummy cycle first + if (op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth > + DATA_READ_MAX_DUMMY) + return false; + // quad io / quad out + if ((op->addr.buswidth == 4 || op->addr.buswidth == 1) && + op->data.buswidth == 4) + return true; + + // dual io / dual out + if ((op->addr.buswidth == 2 || op->addr.buswidth == 1) && + op->data.buswidth == 2) + return true; + + // standard spi + if (op->addr.buswidth == 1 && op->data.buswidth == 1) + return true; + } else if (op->data.dir == SPI_MEM_DATA_OUT) { + // check dummy cycle first + if (op->dummy.nbytes) + return false; + // program load quad out + if (op->addr.buswidth == 1 && op->data.buswidth == 4) + return true; + // standard spi + if (op->addr.buswidth == 1 && op->data.buswidth == 1) + return true; + } + return false; +} + +static bool mtk_snand_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + if (!spi_mem_default_supports_op(mem, op)) + return false; + if (op->cmd.nbytes != 1 || op->cmd.buswidth != 1) + return false; + if (mtk_snand_is_page_ops(op)) + return true; + return ((op->addr.nbytes == 0 || op->addr.buswidth == 1) && + (op->dummy.nbytes == 0 || op->dummy.buswidth == 1) && + (op->data.nbytes == 0 || op->data.buswidth == 1)); +} + +static int mtk_snand_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + struct mtk_snand *ms = spi_controller_get_devdata(mem->spi->master); + // page ops transfer size must be exactly ((sector_size + spare_size) * + // nsectors). Limit the op size if the caller requests more than that. + // exec_op will read more than needed and discard the leftover if the + // caller requests less data. + if (mtk_snand_is_page_ops(op)) { + size_t l; + // skip adjust_op_size for page ops + if (ms->autofmt) + return 0; + l = ms->caps->sector_size + ms->nfi_cfg.spare_size; + l *= ms->nfi_cfg.nsectors; + if (op->data.nbytes > l) + op->data.nbytes = l; + } else { + size_t hl = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; + + if (hl >= SNF_GPRAM_SIZE) + return -EOPNOTSUPP; + if (op->data.nbytes > SNF_GPRAM_SIZE - hl) + op->data.nbytes = SNF_GPRAM_SIZE - hl; + } + return 0; +} + +static int mtk_snand_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct mtk_snand *ms = spi_controller_get_devdata(mem->spi->master); + + dev_dbg(ms->dev, "OP %02x ADDR %08llX@%d:%u DATA %d:%u", op->cmd.opcode, + op->addr.val, op->addr.buswidth, op->addr.nbytes, + op->data.buswidth, op->data.nbytes); + if (mtk_snand_is_page_ops(op)) { + if (op->data.dir == SPI_MEM_DATA_IN) + return mtk_snand_read_page_cache(ms, op); + else + return mtk_snand_write_page_cache(ms, op); + } else { + return mtk_snand_mac_io(ms, op); + } +} + +static const struct spi_controller_mem_ops mtk_snand_mem_ops = { + .adjust_op_size = mtk_snand_adjust_op_size, + .supports_op = mtk_snand_supports_op, + .exec_op = mtk_snand_exec_op, +}; + +static const struct spi_controller_mem_caps mtk_snand_mem_caps = { + .ecc = true, +}; + +static irqreturn_t mtk_snand_irq(int irq, void *id) +{ + struct mtk_snand *snf = id; + u32 sta, ien; + + sta = nfi_read32(snf, NFI_INTR_STA); + ien = nfi_read32(snf, NFI_INTR_EN); + + if (!(sta & ien)) + return IRQ_NONE; + + nfi_write32(snf, NFI_INTR_EN, 0); + complete(&snf->op_done); + return IRQ_HANDLED; +} + +static const struct of_device_id mtk_snand_ids[] = { + { .compatible = "mediatek,mt7622-snand", .data = &mt7622_snand_caps }, + { .compatible = "mediatek,mt7629-snand", .data = &mt7629_snand_caps }, + {}, +}; + +MODULE_DEVICE_TABLE(of, mtk_snand_ids); + +static int mtk_snand_enable_clk(struct mtk_snand *ms) +{ + int ret; + + ret = clk_prepare_enable(ms->nfi_clk); + if (ret) { + dev_err(ms->dev, "unable to enable nfi clk\n"); + return ret; + } + ret = clk_prepare_enable(ms->pad_clk); + if (ret) { + dev_err(ms->dev, "unable to enable pad clk\n"); + goto err1; + } + return 0; +err1: + clk_disable_unprepare(ms->nfi_clk); + return ret; +} + +static void mtk_snand_disable_clk(struct mtk_snand *ms) +{ + clk_disable_unprepare(ms->pad_clk); + clk_disable_unprepare(ms->nfi_clk); +} + +static int mtk_snand_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *dev_id; + struct spi_controller *ctlr; + struct mtk_snand *ms; + int ret; + + dev_id = of_match_node(mtk_snand_ids, np); + if (!dev_id) + return -EINVAL; + + ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*ms)); + if (!ctlr) + return -ENOMEM; + platform_set_drvdata(pdev, ctlr); + + ms = spi_controller_get_devdata(ctlr); + + ms->ctlr = ctlr; + ms->caps = dev_id->data; + + ms->ecc = of_mtk_ecc_get(np); + if (IS_ERR(ms->ecc)) + return PTR_ERR(ms->ecc); + else if (!ms->ecc) + return -ENODEV; + + ms->nfi_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ms->nfi_base)) { + ret = PTR_ERR(ms->nfi_base); + goto release_ecc; + } + + ms->dev = &pdev->dev; + + ms->nfi_clk = devm_clk_get(&pdev->dev, "nfi_clk"); + if (IS_ERR(ms->nfi_clk)) { + ret = PTR_ERR(ms->nfi_clk); + dev_err(&pdev->dev, "unable to get nfi_clk, err = %d\n", ret); + goto release_ecc; + } + + ms->pad_clk = devm_clk_get(&pdev->dev, "pad_clk"); + if (IS_ERR(ms->pad_clk)) { + ret = PTR_ERR(ms->pad_clk); + dev_err(&pdev->dev, "unable to get pad_clk, err = %d\n", ret); + goto release_ecc; + } + + ret = mtk_snand_enable_clk(ms); + if (ret) + goto release_ecc; + + init_completion(&ms->op_done); + + ms->irq = platform_get_irq(pdev, 0); + if (ms->irq < 0) { + ret = ms->irq; + goto disable_clk; + } + ret = devm_request_irq(ms->dev, ms->irq, mtk_snand_irq, 0x0, + "mtk-snand", ms); + if (ret) { + dev_err(ms->dev, "failed to request snfi irq\n"); + goto disable_clk; + } + + ret = dma_set_mask(ms->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(ms->dev, "failed to set dma mask\n"); + goto disable_clk; + } + + // switch to SNFI mode + nfi_write32(ms, SNF_CFG, SPI_MODE); + + // setup an initial page format for ops matching page_cache_op template + // before ECC is called. + ret = mtk_snand_setup_pagefmt(ms, ms->caps->sector_size, + ms->caps->spare_sizes[0]); + if (ret) { + dev_err(ms->dev, "failed to set initial page format\n"); + goto disable_clk; + } + + // setup ECC engine + ms->ecc_eng.dev = &pdev->dev; + ms->ecc_eng.integration = NAND_ECC_ENGINE_INTEGRATION_PIPELINED; + ms->ecc_eng.ops = &mtk_snfi_ecc_engine_ops; + ms->ecc_eng.priv = ms; + + ret = nand_ecc_register_on_host_hw_engine(&ms->ecc_eng); + if (ret) { + dev_err(&pdev->dev, "failed to register ecc engine.\n"); + goto disable_clk; + } + + ctlr->num_chipselect = 1; + ctlr->mem_ops = &mtk_snand_mem_ops; + ctlr->mem_caps = &mtk_snand_mem_caps; + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; + ctlr->dev.of_node = pdev->dev.of_node; + ret = spi_register_controller(ctlr); + if (ret) { + dev_err(&pdev->dev, "spi_register_controller failed.\n"); + goto disable_clk; + } + + return 0; +disable_clk: + mtk_snand_disable_clk(ms); +release_ecc: + mtk_ecc_release(ms->ecc); + return ret; +} + +static int mtk_snand_remove(struct platform_device *pdev) +{ + struct spi_controller *ctlr = platform_get_drvdata(pdev); + struct mtk_snand *ms = spi_controller_get_devdata(ctlr); + + spi_unregister_controller(ctlr); + mtk_snand_disable_clk(ms); + mtk_ecc_release(ms->ecc); + kfree(ms->buf); + return 0; +} + +static struct platform_driver mtk_snand_driver = { + .probe = mtk_snand_probe, + .remove = mtk_snand_remove, + .driver = { + .name = "mtk-snand", + .of_match_table = mtk_snand_ids, + }, +}; + +module_platform_driver(mtk_snand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Chuanhong Guo "); +MODULE_DESCRIPTION("MeidaTek SPI-NAND Flash Controller Driver"); From patchwork Sun Apr 24 03:25:25 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chuanhong Guo X-Patchwork-Id: 12824755 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id BDF1EC433F5 for ; Sun, 24 Apr 2022 03:27:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=a8QT+jc6qqnYJKv8rFBydMXPi/uOYh1kFp5etojcxSY=; b=13wSa40YqfYuJ/ 6sa0C3O7DDw2Y3bYLUz1ASW0SMEOIxN3rPyZNU8Ykj4VFrMboXAHoSxIliUMbEXqaFyhPiku3lIZb ITu7R23rtrzju8nhHDkDIjo6gwd5jJrgP3KvVB/dazaEfT1uio320mdqXDcP0Sy9nxgx2yMEunZe3 aiZDdDC9iFKyP37YIjOdgps3f9fJQ+n+U71iBmhZWtUodOmZnzitNC5/m39r9XRJLJHAlYFYzR5Yu 9+seo3UZzBZWf3KDcV/WtNfw44BwiRG2vjJnHtnOx3126kbopzS+tvSDC+DIoMNnSrlaZeJd445Sx eycy0qv5KDcW3IB2tOog==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1niSuN-005erS-PI; Sun, 24 Apr 2022 03:27:35 +0000 Received: from mail-pl1-x632.google.com ([2607:f8b0:4864:20::632]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1niStE-005eIA-Cf; Sun, 24 Apr 2022 03:26:25 +0000 Received: by mail-pl1-x632.google.com with SMTP id c12so19605854plr.6; Sat, 23 Apr 2022 20:26:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=bx17EcXivOefNPVT0GwtSAGebJ/STejXXqwturRr7B8=; b=VnD8Me67cwwcUoKVkBFwr8tkQZRjrLohIPKApXMS5qmJsmyo9z8ww2EzrbiPKDMaZr 3ClNSEpE/w6BkiUPzrTInNz2DZsTGew31xuANhhq1dTdmDx7LfMzRxF2jOe3rZm94gE2 rPcSAolPOBcwUttbFm7CD0pYCt/EjAJeYLvJloINhrMk1D/m31lP2ybrzD8Us0EbO+3z F8o2ppl2ngetgUHRpxfVlBvbFXRlN+LvJK/H18UuyqNhBcr3a36R3dUSJYECgE9bmtls Z3yoTiqZF58OHFM7u7PQRH7ZO39FE/lbxjM1OB4YSQ/k1QXTvIUT65iKDx+M/228156L 15EQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=bx17EcXivOefNPVT0GwtSAGebJ/STejXXqwturRr7B8=; b=sm7Jj9c7Op1AtX0dr54a63Z9v5LuKwG63RhEQRgBZUDpfCuheH7EFRyx/rmDj+AFMw IIGK4CGUf/1bdcPXE9YGRfUN9e4iEVOs8/UWclx3/tWGUYmHLS1WnIqyju/DWUp3I78N h3okyXg7mWWePRzK6mtD20SDWJNVYClZkc4Twx4JJ17ZO4UX5DZBBE4Jyus7/QjnSlSx nc3yxZo3OfbwUkat7MleYI6lg/F+d/DD9rK6uRorFkpypLKIRxYAlVnDzh6TapoqZrZH iduNpDgThgtjWreuroeFeSBrIdXkQK4D3t5H4DWJqnJ0kmGD+xUBPQEp7SUYo5LADFE6 Vkqw== X-Gm-Message-State: AOAM531k+8g+R/rNUVS9ouj2u2K6uL7uYDSBETSir18/3ctrmRnuI+VJ fO5IohH/sCXyVhahxf7fSQ9fy7KKQVtvsf4n X-Google-Smtp-Source: ABdhPJwfQtqF2Kt/eCQMX/DK0J/qN+ecwIyk4oOsUZX9DIKzFbWhe21C3yXTZt0kur6BmqCQPnjRCA== X-Received: by 2002:a17:90b:388a:b0:1d2:a380:e622 with SMTP id mu10-20020a17090b388a00b001d2a380e622mr13663216pjb.22.1650770783261; Sat, 23 Apr 2022 20:26:23 -0700 (PDT) Received: from guoguo-omen.lan ([222.201.153.219]) by smtp.gmail.com with ESMTPSA id r76-20020a632b4f000000b003820643e1c2sm5790724pgr.59.2022.04.23.20.26.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 23 Apr 2022 20:26:22 -0700 (PDT) From: Chuanhong Guo To: linux-spi@vger.kernel.org Cc: Chuanhong Guo , Mark Brown , Rob Herring , Krzysztof Kozlowski , Matthias Brugger , Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Roger Quadros , Thomas Bogendoerfer , Cai Huoqing , Florian Fainelli , Colin Ian King , Wolfram Sang , Paul Cercueil , Pratyush Yadav , Yu Kuai , devicetree@vger.kernel.org (open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS), linux-arm-kernel@lists.infradead.org (moderated list:ARM/Mediatek SoC support), linux-mediatek@lists.infradead.org (moderated list:ARM/Mediatek SoC support), linux-kernel@vger.kernel.org (open list), linux-mtd@lists.infradead.org (open list:NAND FLASH SUBSYSTEM) Subject: [PATCH v6 3/5] mtd: nand: mtk-ecc: also parse nand-ecc-engine if available Date: Sun, 24 Apr 2022 11:25:25 +0800 Message-Id: <20220424032527.673605-4-gch981213@gmail.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220424032527.673605-1-gch981213@gmail.com> References: <20220424032527.673605-1-gch981213@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220423_202624_484562_695A6F99 X-CRM114-Status: GOOD ( 13.05 ) X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+linux-mediatek=archiver.kernel.org@lists.infradead.org The recently added ECC engine support introduced a generic property named nand-ecc-engine for ecc engine phandle. This patch adds support for this new property. Signed-off-by: Chuanhong Guo --- Change since v1: new patch Change in v2-v6: None drivers/mtd/nand/ecc-mtk.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c index 74ddaa46ba7c..9f9b201fe706 100644 --- a/drivers/mtd/nand/ecc-mtk.c +++ b/drivers/mtd/nand/ecc-mtk.c @@ -279,7 +279,10 @@ struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node) struct mtk_ecc *ecc = NULL; struct device_node *np; - np = of_parse_phandle(of_node, "ecc-engine", 0); + np = of_parse_phandle(of_node, "nand-ecc-engine", 0); + /* for backward compatibility */ + if (!np) + np = of_parse_phandle(of_node, "ecc-engine", 0); if (np) { ecc = mtk_ecc_get(np); of_node_put(np); From patchwork Sun Apr 24 03:25:26 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chuanhong Guo X-Patchwork-Id: 12824756 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 8A7E5C433EF for ; Sun, 24 Apr 2022 03:28:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=kqboRypg2uZ2TN6yKSPQ8e/BP9CCiUdglfyfB4IQXjc=; b=X2OZtaZOjd9pBI kQ/7DYj+qiyM09yiWIZkJbKK/pmB9UzbrOkrSiEDVf3K3MJRewVlN5Zo6ENT8Eh/ntHtQMlwUaE1n HF0VB3Ct0M6UPv7il4GlNJemg+4Yi7ExWrwKpf5/ATZqUwcyi/LtmZUeYPEmcKG6yBbJBROcHhF7A 4CS/3BLLFrouTCgMnG7VpQKKSjXNpD1/1wqJWXCegreJPV5xYzekas34ZaaMmSJTToxdqucvOz+y0 9l8R/ZertrDGxgsn2d81etIoCOUSSYc7uSwXXbYED4SkNq6jgxj6yQVijWAQjmT2jX28yY3nFFig6 1km7SWD4Bl46uBIhpdQQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1niSuy-005fEZ-PG; Sun, 24 Apr 2022 03:28:12 +0000 Received: from mail-pg1-x532.google.com ([2607:f8b0:4864:20::532]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1niStK-005eKL-Sp; Sun, 24 Apr 2022 03:26:32 +0000 Received: by mail-pg1-x532.google.com with SMTP id h5so10589714pgc.7; Sat, 23 Apr 2022 20:26:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=515OXkaaLsXBOTXwN+W2kLxRXcgyH7f1wIj3RDCO+iM=; b=GgklGDM6qZ3gecj3Y4PMhHIKiY52vuaTClV6/xRXB7+VYkSg12LeK8yXgdPpGUBqbs W7u7gV0TezjWnnrE/2yIyIqS3marQDgFypZmHS1fuZoXFeygJjjzAcoXHC2vAPvx3LVF M8mGeYSYSoVTJJcJorPuDFl3gGffkUAjfdgwp59PAtUR4AFmPEkyuUXnlhW2aaQfz1MX 3nuyS+YK9VTI6GRaJ5kOO+6ze5fiei427xP3DQ4qV0WMrpFDuoXTk5ahhLOGP6m8t328 mWfzBWkiwH/rDRRGOuCQLpbLTlW9bkDiXMkZn2WvVzCDtBUApNeeFQiBD+7TzEEVfMhN aZ4g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=515OXkaaLsXBOTXwN+W2kLxRXcgyH7f1wIj3RDCO+iM=; b=FVoU4u6PYB09rqzq3KTHNtU+S5YgKUGFYMs0pJvCrNix3SZEHndxjs/7ztdJZvVnm+ i+1BR9r6BVHVuk5y9D2zid2QhbfQ1UAYpAin3ooSeu6fkj84reZ2ZhV4vW9o6FGDNTwp rPufLu5RP4QDRIxF3EcUokzDUCAKdKnwCS3iFaZpO1ewEleAmnjsxxu2BarZtAyYLRVS 5VW5izqLxGIW5bppLa01oDSuEtiyylGn6l4QCzbvu2nw4WzkkQ58twKnxTwgpMyEOzVB bXdh7G+lKwSxHhV4q8e4H3DNpc2iiCVKlt4jrPD59xjm4QJ7Vxda0aOtRTJp5H34mC+a njpw== X-Gm-Message-State: AOAM531R9IOna7aDuJvKa3NHNVlk0fVThbwy7e0e/nsd13ytvTbB9/dx fV+yhCFoT8XhP2GBqO3KX/o= X-Google-Smtp-Source: ABdhPJxnUi/hi4CZA6156zTJSdh8sfTwaRohBb39mkA9j+vd6Pg/vHrI8ib1eX88yWsXuxsZu+0yXg== X-Received: by 2002:a05:6a00:134b:b0:50c:f10e:8eff with SMTP id k11-20020a056a00134b00b0050cf10e8effmr9674817pfu.79.1650770789721; Sat, 23 Apr 2022 20:26:29 -0700 (PDT) Received: from guoguo-omen.lan ([222.201.153.219]) by smtp.gmail.com with ESMTPSA id r76-20020a632b4f000000b003820643e1c2sm5790724pgr.59.2022.04.23.20.26.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 23 Apr 2022 20:26:29 -0700 (PDT) From: Chuanhong Guo To: linux-spi@vger.kernel.org Cc: Chuanhong Guo , Krzysztof Kozlowski , Mark Brown , Rob Herring , Krzysztof Kozlowski , Matthias Brugger , Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Roger Quadros , Thomas Bogendoerfer , Cai Huoqing , Florian Fainelli , Colin Ian King , Wolfram Sang , Paul Cercueil , Pratyush Yadav , Yu Kuai , devicetree@vger.kernel.org (open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS), linux-arm-kernel@lists.infradead.org (moderated list:ARM/Mediatek SoC support), linux-mediatek@lists.infradead.org (moderated list:ARM/Mediatek SoC support), linux-kernel@vger.kernel.org (open list), linux-mtd@lists.infradead.org (open list:NAND FLASH SUBSYSTEM) Subject: [PATCH v6 4/5] spi: dt-bindings: add binding doc for spi-mtk-snfi Date: Sun, 24 Apr 2022 11:25:26 +0800 Message-Id: <20220424032527.673605-5-gch981213@gmail.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220424032527.673605-1-gch981213@gmail.com> References: <20220424032527.673605-1-gch981213@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220423_202630_993670_2501723D X-CRM114-Status: GOOD ( 13.58 ) X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+linux-mediatek=archiver.kernel.org@lists.infradead.org Add device-tree binding documentation for Mediatek SPI-NAND Flash Interface. Signed-off-by: Chuanhong Guo Reviewed-by: Krzysztof Kozlowski --- Changes since v1: 1. add a blank line between properties in dt binding doc 2. rename ecc-engine to nand-ecc-engine for the generic properties Change since v2: none Change since v3: fix a missing rename in v1 Change since v4: fix typo: piplined -> pipelined Change since v5: none .../bindings/spi/mediatek,spi-mtk-snfi.yaml | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/mediatek,spi-mtk-snfi.yaml diff --git a/Documentation/devicetree/bindings/spi/mediatek,spi-mtk-snfi.yaml b/Documentation/devicetree/bindings/spi/mediatek,spi-mtk-snfi.yaml new file mode 100644 index 000000000000..6e6e02c91780 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/mediatek,spi-mtk-snfi.yaml @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/mediatek,spi-mtk-snfi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SPI-NAND flash controller for MediaTek ARM SoCs + +maintainers: + - Chuanhong Guo + +description: | + The Mediatek SPI-NAND flash controller is an extended version of + the Mediatek NAND flash controller. It can perform standard SPI + instructions with one continuous write and one read for up-to 0xa0 + bytes. It also supports typical SPI-NAND page cache operations + in single, dual or quad IO mode with pipelined ECC encoding/decoding + using the accompanying ECC engine. There should be only one spi + slave device following generic spi bindings. + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +properties: + compatible: + enum: + - mediatek,mt7622-snand + - mediatek,mt7629-snand + + reg: + items: + - description: core registers + + interrupts: + items: + - description: NFI interrupt + + clocks: + items: + - description: clock used for the controller + - description: clock used for the SPI bus + + clock-names: + items: + - const: nfi_clk + - const: pad_clk + + nand-ecc-engine: + description: device-tree node of the accompanying ECC engine. + $ref: /schemas/types.yaml#/definitions/phandle + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - nand-ecc-engine + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + soc { + #address-cells = <2>; + #size-cells = <2>; + snfi: spi@1100d000 { + compatible = "mediatek,mt7622-snand"; + reg = <0 0x1100d000 0 0x1000>; + interrupts = ; + clocks = <&pericfg CLK_PERI_NFI_PD>, <&pericfg CLK_PERI_SNFI_PD>; + clock-names = "nfi_clk", "pad_clk"; + nand-ecc-engine = <&bch>; + #address-cells = <1>; + #size-cells = <0>; + + flash@0 { + compatible = "spi-nand"; + reg = <0>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + nand-ecc-engine = <&snfi>; + }; + }; + }; From patchwork Sun Apr 24 03:25:27 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chuanhong Guo X-Patchwork-Id: 12824766 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 72482C433F5 for ; Sun, 24 Apr 2022 03:29:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=H/f4z31n7DKLkF1sk9JEUYHf9kJazl5D6HqN6Lt1Igk=; b=Gw7DAjlbsooyVQ RhNPEw166Ojxdj500LRl6cBZhUSUAvVPm9/lpa/YbiagGo9wULSApIhlr/U8vIsnvr/N0+HaNfA6N AJR76o5++sphtRlkHNrcRuQjBYe7MjVPNs61/8JAs36JstIz2aNWDPB+0h+G5m5QTLiHA5AhBfTdH A8m7vSfRQDlMh1/AFUDmzhvYeAAGofCiPKn6hI0DJJZ4pRBkYPnqgyaMb0/ClDi9Ye9h0u2wKB+AD zL0myBj5vWaguERa0CEkXaCv09LXY7ybK3I3gTMzcEtnwtEbfyCw8jr2V1z9Tm/D1PTlnVySzp00C cGalPvlNXK89WJLmY6yA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1niSvu-005fm9-Sr; Sun, 24 Apr 2022 03:29:10 +0000 Received: from mail-pf1-x42e.google.com ([2607:f8b0:4864:20::42e]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1niStR-005eMd-BD; Sun, 24 Apr 2022 03:26:38 +0000 Received: by mail-pf1-x42e.google.com with SMTP id j17so11748103pfi.9; Sat, 23 Apr 2022 20:26:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=dTSKZRhjg25O5C1jlBW1LRl5IUhwoxhhyCVs3jC3lXs=; b=NQEwgIsyvQM+8c91zHJp/VLeFL09cmvrN/vlUyahcrDgfjqF0SOWuHfyKAebhXKaGB 8CDfEF/W//rKPtjwmgE/i1SsC8KrAP0TsaHOjEGv/YFAhMkXwQkEcxo7FAaj51MDJciG C+BZE/k+XF1IPrsGNVcTf0u0VgX9jmXoYj7RMJ8gg7I1HtnT+C3y5pO/5gQbaWYWxr4u Mv+yHTv0SJwqegA4QhJW9D6NITCZiLRCumAsqrYAn7mFQnBOOXBd5pA69m4NV7ItlNPt AKTeCQ0KuwivsKU7Sj06o2fF5omHx5+c/CwcDOh/bqTgnjst0d1r6HB5Mz6G/roFBqXn 0kiw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=dTSKZRhjg25O5C1jlBW1LRl5IUhwoxhhyCVs3jC3lXs=; b=Vl4cV/yjNez3Tnrqn9PMVcBLUvF48QT4TRVg4CiwYblVmJQ0G8mwRrHYwMcfVbvDdl dO7UyHQ528zHy7XW1rl7GfLxwool/FXmawuWp1h22xevbHDutfWV3tgO+U0eQcyR2uES gUEhrMKz03A0yId9wvY2YbqdlC67Wsr0lSxgo4R1sZydnb0h6Ri/Wxahm82khu4jxa8v ol6CPocpJGc47A+vaxrPGXqhjMiMI9FjKm528xjlQdadpXZntQ4GXauo3KjbLNzJbCCt qI4ktvx1/zB63R/4fyYwfL1+2EDlLVKIV+9pkUj1dyN/z0DfBJSfdJlq8/Hysa4Y+2G4 uVUA== X-Gm-Message-State: AOAM53326fVZY2F7i06ZfIpijNsJm2A0blert/zFYWF8JAAoS5GHmLLH TwLFccaT6VdUGkSRqwpHdxQ= X-Google-Smtp-Source: ABdhPJwnCkDmNFP2ub0LUJ5XFBOw3WVX1bkBhe4XHDPGH7PdIKzQy9kZ9No2Rbpa1oUDlTX3Rbfi3g== X-Received: by 2002:a63:358c:0:b0:39c:c97a:ee80 with SMTP id c134-20020a63358c000000b0039cc97aee80mr9999124pga.7.1650770795849; Sat, 23 Apr 2022 20:26:35 -0700 (PDT) Received: from guoguo-omen.lan ([222.201.153.219]) by smtp.gmail.com with ESMTPSA id r76-20020a632b4f000000b003820643e1c2sm5790724pgr.59.2022.04.23.20.26.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 23 Apr 2022 20:26:35 -0700 (PDT) From: Chuanhong Guo To: linux-spi@vger.kernel.org Cc: Chuanhong Guo , Mark Brown , Rob Herring , Krzysztof Kozlowski , Matthias Brugger , Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Roger Quadros , Thomas Bogendoerfer , Cai Huoqing , Florian Fainelli , Colin Ian King , Wolfram Sang , Paul Cercueil , Pratyush Yadav , Yu Kuai , devicetree@vger.kernel.org (open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS), linux-arm-kernel@lists.infradead.org (moderated list:ARM/Mediatek SoC support), linux-mediatek@lists.infradead.org (moderated list:ARM/Mediatek SoC support), linux-kernel@vger.kernel.org (open list), linux-mtd@lists.infradead.org (open list:NAND FLASH SUBSYSTEM) Subject: [PATCH v6 5/5] arm64: dts: mediatek: add mtk-snfi for mt7622 Date: Sun, 24 Apr 2022 11:25:27 +0800 Message-Id: <20220424032527.673605-6-gch981213@gmail.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220424032527.673605-1-gch981213@gmail.com> References: <20220424032527.673605-1-gch981213@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220423_202637_456195_82C7C57C X-CRM114-Status: GOOD ( 10.07 ) X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+linux-mediatek=archiver.kernel.org@lists.infradead.org This patch adds a device-tree node for the MTK SPI-NAND Flash Interface for MT7622 device tree. Signed-off-by: Chuanhong Guo --- Changes since v1: 1. use the newly introduced nand-ecc-engine instead 2. reword commit message Change in v2-v6: none arch/arm64/boot/dts/mediatek/mt7622.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt7622.dtsi b/arch/arm64/boot/dts/mediatek/mt7622.dtsi index f232f8baf4e8..8c2563a3919a 100644 --- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi @@ -545,6 +545,18 @@ nandc: nfi@1100d000 { status = "disabled"; }; + snfi: spi@1100d000 { + compatible = "mediatek,mt7622-snand"; + reg = <0 0x1100d000 0 0x1000>; + interrupts = ; + clocks = <&pericfg CLK_PERI_NFI_PD>, <&pericfg CLK_PERI_SNFI_PD>; + clock-names = "nfi_clk", "pad_clk"; + nand-ecc-engine = <&bch>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + bch: ecc@1100e000 { compatible = "mediatek,mt7622-ecc"; reg = <0 0x1100e000 0 0x1000>;