From patchwork Thu Dec 21 12:12:57 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Prabhakar Kushwaha X-Patchwork-Id: 851901 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 3z2VwT6Hz7z9s7B for ; Thu, 21 Dec 2017 23:15:53 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id B38BFC21FA8; Thu, 21 Dec 2017 12:15:03 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=BAD_ENC_HEADER, SPF_HELO_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id D4628C21F67; Thu, 21 Dec 2017 12:14:28 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id B4257C221B4; Thu, 21 Dec 2017 12:14:02 +0000 (UTC) Received: from NAM03-BY2-obe.outbound.protection.outlook.com (mail-by2nam03on0047.outbound.protection.outlook.com [104.47.42.47]) by lists.denx.de (Postfix) with ESMTPS id B86FFC221AC for ; Thu, 21 Dec 2017 12:13:57 +0000 (UTC) Received: from BLUPR0301CA0035.namprd03.prod.outlook.com (10.162.113.173) by CO2PR03MB2360.namprd03.prod.outlook.com (10.166.93.20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.323.15; Thu, 21 Dec 2017 12:13:54 +0000 Received: from BL2FFO11FD033.protection.gbl (2a01:111:f400:7c09::181) by BLUPR0301CA0035.outlook.office365.com (2a01:111:e400:5259::45) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.20.323.15 via Frontend Transport; Thu, 21 Dec 2017 12:13:54 +0000 Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=nxp.com; nxp.com; dkim=none (message not signed) header.d=none;nxp.com; dmarc=fail action=none header.from=nxp.com; Received-SPF: Fail (protection.outlook.com: domain of nxp.com does not designate 192.88.168.50 as permitted sender) receiver=protection.outlook.com; client-ip=192.88.168.50; helo=tx30smr01.am.freescale.net; Received: from tx30smr01.am.freescale.net (192.88.168.50) by BL2FFO11FD033.mail.protection.outlook.com (10.173.161.129) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.20.302.6 via Frontend Transport; Thu, 21 Dec 2017 12:13:40 +0000 Received: from VirtualBox.ap.freescale.net ([10.232.40.41]) by tx30smr01.am.freescale.net (8.14.3/8.14.0) with ESMTP id vBLCDbUJ018319; Thu, 21 Dec 2017 05:13:51 -0700 From: Prabhakar Kushwaha To: Date: Thu, 21 Dec 2017 17:42:57 +0530 Message-ID: <1513858377-20729-4-git-send-email-prabhakar.kushwaha@nxp.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1513858377-20729-1-git-send-email-prabhakar.kushwaha@nxp.com> References: <1513858377-20729-1-git-send-email-prabhakar.kushwaha@nxp.com> X-EOPAttributedMessage: 0 X-Matching-Connectors: 131583320203610329; (91ab9b29-cfa4-454e-5278-08d120cd25b8); () X-Forefront-Antispam-Report: CIP:192.88.168.50; IPV:NLI; CTRY:US; EFV:NLI; SFV:NSPM; SFS:(10009020)(336005)(39860400002)(376002)(396003)(346002)(39380400002)(2980300002)(1110001)(1109001)(339900001)(189003)(199004)(52314003)(377424004)(356003)(51416003)(316002)(4326008)(54906003)(106466001)(50226002)(81166006)(8676002)(104016004)(105606002)(498600001)(5660300001)(81156014)(16586007)(68736007)(2950100002)(6916009)(77096006)(59450400001)(305945005)(76176011)(86362001)(36756003)(97736004)(50466002)(48376002)(575784001)(2906002)(2351001)(47776003)(8656006)(39060400002)(53936002)(8936002)(85426001); DIR:OUT; SFP:1101; SCL:1; SRVR:CO2PR03MB2360; H:tx30smr01.am.freescale.net; FPR:; SPF:Fail; PTR:InfoDomainNonexistent; MX:1; A:1; LANG:en; X-Microsoft-Exchange-Diagnostics: 1; BL2FFO11FD033; 1:EpLShXPkQwGP00CB/g8UwsPl2J37474TT50dLkQx0/feLoB3JBACQFtaHVjssSeR0xi+CE7t6AGNCLdLQj+BBJsxHp+bgfYV9QJzlIZZNup9OxmLp4WnPE41CUZ3Um8K MIME-Version: 1.0 X-MS-Office365-Filtering-Correlation-Id: 4fc48239-36f9-42b1-48d9-08d5486c4591 X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(5600026)(4604075)(4534020)(4628075)(201703131517081)(2017052603307); SRVR:CO2PR03MB2360; X-Microsoft-Exchange-Diagnostics: 1; CO2PR03MB2360; 3:YiWkTjAiokpwxA5eNW2Tjh4cgLnHMs9uGplK+WrmaUZ02w3SV0uG0c2X1BHMfLGmYFSsXBH5Ojl/hLYoLcZ93zlsoO4koOBUyoKP0kfKOD+dROYhKjiZxxVGr99ucbvs7OZxUHqnxZqUihqEOEvJWkj9M5EYCxJxCNYXOlS4SS/cele6HyVTNdJZxrPA6/znfBd9Zi1MGTJdKFeTqeaZJNQwV/bBNl5RYU8M1r8SErRmPd0hyuVplSw72ej8hJUfv0amGCSy0CW62BA6vvCuctMpjJM5FS158ieDaOTX2iSf2mDYbK/66JbP/yrIhrO68qoixe9xCYwNtfzVqQOYJbFh8NcG0K6JEIMlZYKtvHE=; 25:ghZ1Q36vtvwo1BvgOvsZhlHXJQaGhqGwfhnEZuIwfLoEYtEXmZw8j77GpsiGqAR+qFBZRjteqjDNL53a6Ezn0pZuQbmD+viTHtL8uDa6PTmLldgyzBL/vN/gpMrh0VVIIeSlMMflCMGkE7Ex5UCRD2p1Ya/b1KnYfrMv0ycJXzm4jE2rJEH3VGkUjpoMjHK4TtRJ7/i7kOe157/cxUWs13BUOd5f2QQGw23PdF5aXUh6UF36fPkOQoiA5zdN6bUPhdCCNTRGauomL1bgm3xvxN+9K5D3chpwvonNT08KwLwMze0kqImoPgh53JqUb3cdqPk4q+H7z1JVKJsFS4MzJhKt07DsAEw/iOMmJ0juIUk= X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CO2PR03MB2360: X-Microsoft-Exchange-Diagnostics: 1; CO2PR03MB2360; 31:FXBaaRaPIMZ13kfVNAl4713eVv58d8GPrUnPoQ6ODRHRiRVrsgDXtB+kiB2SgqrNen45n89mcFQBZg5yw3t1NcMD/oKe+8WWYLNmv2j5gV90w6SYDMNTGwYT1obiwo9UGwsE0+So9CUOMk70Q178uuZQelO6tP5sLX7LKXFrHoyJ8wsV5Y1bJBTUH4CX+6wA9zqVa1rIQTdtKoAot6Fp9B8+sQMwYImJJvbOurt5LC4=; 4:ICi6ae3I2ObeR19kwDc5NpKq+vmnZqIsTJkkQ1bLldQarcTqRSUb0lXLwhzn1bgHvsoT0m7x8hGLA114acObQX4aWXiYoAB9Ml4xjJtNW3Ysi/k6RJzfYQgMyLxBefAqR1vZ1S/R3tFwsfZIwWJCjoiVYiliKjhb7mYRI9DpTZ6WsdMNrbXZphVpW/iVJEVShlX25HI8S5Sh4/wsbHxUSrJNHqSfbt2vsxELYEuyl4Ru4VeLWLPJ01p63cMua+y3OXAo2nOJrtmyCHUrmi839W7OELNb8TJtv7qgwG5Dxi83YJlRaT2dMFxs48WlpRlo X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(185117386973197); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(6095135)(2401047)(8121501046)(5005006)(3231023)(3002001)(10201501046)(93006095)(93001095)(6055026)(6096035)(20161123565025)(20161123556025)(20161123563025)(201703131430075)(201703131433075)(201703131448075)(201703161259150)(201703151042153)(20161123561025)(20161123559100)(201708071742011); SRVR:CO2PR03MB2360; BCL:0; PCL:0; RULEID:(100000803101)(100110400095)(400006); SRVR:CO2PR03MB2360; X-Forefront-PRVS: 0528942FD8 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; CO2PR03MB2360; 23:g3sLY39meBmFTQbHYWTwPlRBboBCh0HETaemnZdPQ?= bD6aUGDuRMmghecGExXAJTbe7jTjgf14AUeuNPlXFZAVb1+r9XGlW1ZbIntxmS1nPdN94Kg2WRGgFGWYY5uwBMJuIK9E1aHmd8IotyPjVluIpYcu3/GVFrhE6mnNQ2FtIYkoiJs4VBht9fLmvyflnLipc8DnBRkeSjQsv2bTmanx/w/Ku8T4640BNmRIZLYp90adsRGkXxqMpqKzjY8h14Cb11mX49EUbrJ08R2nvZrwKSWXzIELaKohQv1Mwo9ZX9JqIhLjw849beX5JKl2KAXZRuC26f30wNlWhdEI7UJXUNnUTDfjyauydwhr+hFi1otAyOwFhPbThA9jhT3o5Xl3rneDJ7mjuVdq1gYNkW30ZopEUUDR7ShZ7dngkb3EWyIlVEU2wWOZKtSo48MpQUfudT0AmcgQBXJkli59PGyfgyY5wbJ4q2hth24c5IzdPnTDTNoWHiqmJu+dmW8jMjZzP4F4GCxrFuhw3hjwNuv0dDnkNe1bhirNJsmr+BzdgJjw+txi6jZf7QT0ix6BmGytury/uiSwi4A/OLdeHliBoZCFERxhOBb2IVCe+KyTkMGcYf0yP+Q9OTQdcFX7f5bdKxDZzdrZjOSqVJxbW3JL/oTGriQv3zEzitBQPnDHIxucZdZ2qxC0ilEkrZhue+1r7/VtImPrKo6SdANqVWoNmqTc/Omk+6jOudOIAD3RxCtpJFFjx+f0VCBQWPPZFP7TAGtaA0IVaVSo4kjaSybxtqU43JPQMhQzkdHWKtx1hefob6MHsufVwEEt653+UI8C1KO3JUa5ciVZ5ls00UTH3GZhRycMiTqTu0GZ8uTT0Fx5T9FwmvsHDwC1fklF68SvXf79nv+ZL2BDOKtkanE41yXBphoqppEoCwKR7zWtLVnZgMCfEiH0p2t6gTNZr6MFSaSrwc1jAviH8gR5BJNqPzFQO+kpvANimNHKhMM7UWEN1nVH+mlwbjrQW3w5P50oIsYElrqxLVF144OdHIUAgY3bnWMmGM58z5U6Q1H1HySPFZPAFSD9I1dWQ4rBBD4/y9cnUXFwTqbYagOcLVZRrxQk3es77gkNV3SNIqs+e6WtItwD0fztgI7Cp1Lh9KrkCGGObhxKHOhybFS3e/r8JuXwlk2mwpri8pbo7jei8w= X-Microsoft-Exchange-Diagnostics: 1; CO2PR03MB2360; 6:VVncNGezmW8gJqfR+VunPtvso697CFf5EzRocQz9zkxrC8GHMq2exU90z4X1gN/908K23XGZUmK/ohl3hHTWErhYFZcWBYmrQ4zOsUlqV/mB7p4E8+JDjrqE+f6/Gnlvc9SzEWzxmSRnyD9UHpWYYuHbfgVr+5Cp/TLUn6vGyAUCVYy0bqmpEXFcyT7fTZGScCat1u4hnFByevdkD5LUvN1l0QAR5j5fiKC+JKN+RKwoJUZu7pcaZSq3KSy83Ay4jJRPWH6dgjD05hBwFtDRdhviMn6GSqB292S23AT9fxPLSaMr9OF0mmdtpFTdAg6STBqHMmgIzRZiMz0jnMvopnT5S7w5sUkRlQfSJAjwNvk=; 5:mdJuEPZyggv1fU+kdknbo+6nMkg5WFY4JqqOGGGY1yYGKwG3eW6TAqUBHBN5555a71QuZh0Ay7kdEfwkCzGa/kA3ujVOrep6pSv9MnqCuLr1exhuH19wY34yA6dX4jJ45CPlgFIaVxMJKSNiuOUpowsvGt4a8PnZb7F/jdnzyB0=; 24:NHYLruokpNE0nFYN3wqF7Euu5cxyrx8yKIxxvaNkwfoyqpUJGQdJHfwf1ZIvpiDrpoyPeod73ejWzv2Xv33p4D5Jxfk9EnSYR0SoWcs7Jqc=; 7:/b5aNriubCpEchqQq47gEzENe5kOTkGsbYGLG7BtPHrwLlCqHCxigWhGmzGDw9EIQCTqBsJ5oV9jKvOnTOuL4WQxLWLb1wgZ0Yn0M2nN1YG8ah6gAkoFXjnyBsvrxdMzKYfovVSqxB0ZWmqR+Xiw93oBedJm7uW+euQv0GnkaXO20TwxuYGk4t+2n3KDQZfxaKesJpE5GsTHwzfUnElr5iPeEqsGqXBXOIVWxMmfo7050uhUuOnGdVOlw1x0um/6 SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-MS-Exchange-CrossTenant-OriginalArrivalTime: 21 Dec 2017 12:13:40.1894 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 4fc48239-36f9-42b1-48d9-08d5486c4591 X-MS-Exchange-CrossTenant-Id: 5afe0b00-7697-4969-b663-5eab37d5f47e X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=5afe0b00-7697-4969-b663-5eab37d5f47e; Ip=[192.88.168.50]; Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: CO2PR03MB2360 Cc: suresh.gupta@nxp.com Subject: [U-Boot] [PATCH 3/3][v2] sf: parse Serial Flash Discoverable Parameters (SFDP) tables X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" This patch adds support to the JESD216 rev B standard and parses the SFDP tables to dynamically initialize the 'struct spi_nor_flash_parameter'. It has been ported from Linux commit "mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables". It Also ports all modifications done on top of the mentioned commit. This feature is enabled by defining FLASH_SFDP in spi_flash_info's flag field. Signed-off-by: Prabhakar Kushwaha CC: Cyrille Pitchen CC: Marek Vasut --- Changes for v2: Incorporated 'Cyrille Pitchen's comments - Fix typos in comments - corrected flash->dummy_byte value in spi_flash_read_sfdp - updated flash->dummy_byte calculation logic for READ commands - Used SPI_TX_QUAD, SPI_TX_DUAL condition check for x-4-4 and x-2-2 - Added fallback mechanism if SFDP read fails drivers/mtd/spi/sf_internal.h | 204 ++++++++++++++++++++ drivers/mtd/spi/spi_flash.c | 429 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 633 insertions(+) diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 15cad3d..eb606fa 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -12,6 +12,7 @@ #include #include +#include /* Dual SPI flash memories - see SPI_COMM_DUAL_... */ enum spi_dual_flash { @@ -83,6 +84,7 @@ enum spi_nor_option_flags { #define CMD_FLAG_STATUS 0x70 #define CMD_EN4B 0xB7 #define CMD_EX4B 0xE9 +#define CMD_READ_SFDP 0x5a /* Bank addr access commands */ #ifdef CONFIG_SPI_FLASH_BAR @@ -156,9 +158,211 @@ struct spi_flash_info { * Use dedicated 4byte address op codes * to support memory size above 128Mib. */ +#define FLASH_SFDP BIT(9) /* Parse SFDP tables */ #define RD_FULL (RD_QUAD | RD_DUAL | RD_QUADIO | RD_DUALIO) }; +struct sfdp_parameter_header { + u8 id_lsb; + u8 minor; + u8 major; + u8 length; /* in double words */ + u8 parameter_table_pointer[3]; /* byte address */ + u8 id_msb; +}; + +#define SFDP_PARAM_HEADER_ID(p) (((p)->id_msb << 8) | (p)->id_lsb) +#define SFDP_PARAM_HEADER_PTP(p) \ + (((p)->parameter_table_pointer[2] << 16) | \ + ((p)->parameter_table_pointer[1] << 8) | \ + ((p)->parameter_table_pointer[0] << 0)) + +#define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */ +#define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */ + +#define SFDP_SIGNATURE 0x50444653U +#define SFDP_JESD216_MAJOR 1 +#define SFDP_JESD216_MINOR 0 +#define SFDP_JESD216A_MINOR 5 +#define SFDP_JESD216B_MINOR 6 + +struct sfdp_header { + u32 signature; /* Ox50444653U <=> "SFDP" */ + u8 minor; + u8 major; + u8 nph; /* 0-base number of parameter headers */ + u8 unused; + + /* Basic Flash Parameter Table. */ + struct sfdp_parameter_header bfpt_header; +}; + +/* Basic Flash Parameter Table */ + +/* + * JESD216 rev B defines a Basic Flash Parameter Table of 16 DWORDs. + * They are indexed from 1 but C arrays are indexed from 0. + */ +#define BFPT_DWORD(i) ((i) - 1) +#define BFPT_DWORD_MAX 16 + +/* The first version of JESB216 defined only 9 DWORDs. */ +#define BFPT_DWORD_MAX_JESD216 9 + +/* 1st DWORD. */ +#define BFPT_DWORD1_FAST_READ_1_1_2 BIT(16) +#define BFPT_DWORD1_ADDRESS_BYTES_MASK GENMASK(18, 17) +#define BFPT_DWORD1_ADDRESS_BYTES_3_ONLY (0x0UL << 17) +#define BFPT_DWORD1_ADDRESS_BYTES_3_OR_4 (0x1UL << 17) +#define BFPT_DWORD1_ADDRESS_BYTES_4_ONLY (0x2UL << 17) +#define BFPT_DWORD1_DTR BIT(19) +#define BFPT_DWORD1_FAST_READ_1_2_2 BIT(20) +#define BFPT_DWORD1_FAST_READ_1_4_4 BIT(21) +#define BFPT_DWORD1_FAST_READ_1_1_4 BIT(22) + +/* 5th DWORD. */ +#define BFPT_DWORD5_FAST_READ_2_2_2 BIT(0) +#define BFPT_DWORD5_FAST_READ_4_4_4 BIT(4) + +/* 11th DWORD. */ +#define BFPT_DWORD11_PAGE_SIZE_SHIFT 4 +#define BFPT_DWORD11_PAGE_SIZE_MASK GENMASK(7, 4) + +/* 15th DWORD. */ + +/* + * (from JESD216 rev B) + * Quad Enable Requirements (QER): + * - 000b: Device does not have a QE bit. Device detects 1-1-4 and 1-4-4 + * reads based on instruction. DQ3/HOLD# functions are hold during + * instruction phase. + * - 001b: QE is bit 1 of status register 2. It is set via Write Status with + * two data bytes where bit 1 of the second byte is one. + * [...] + * Writing only one byte to the status register has the side-effect of + * clearing status register 2, including the QE bit. The 100b code is + * used if writing one byte to the status register does not modify + * status register 2. + * - 010b: QE is bit 6 of status register 1. It is set via Write Status with + * one data byte where bit 6 is one. + * [...] + * - 011b: QE is bit 7 of status register 2. It is set via Write status + * register 2 instruction 3Eh with one data byte where bit 7 is one. + * [...] + * The status register 2 is read using instruction 3Fh. + * - 100b: QE is bit 1 of status register 2. It is set via Write Status with + * two data bytes where bit 1 of the second byte is one. + * [...] + * In contrast to the 001b code, writing one byte to the status + * register does not modify status register 2. + * - 101b: QE is bit 1 of status register 2. Status register 1 is read using + * Read Status instruction 05h. Status register2 is read using + * instruction 35h. QE is set via Writ Status instruction 01h with + * two data bytes where bit 1 of the second byte is one. + * [...] + */ +#define BFPT_DWORD15_QER_MASK GENMASK(22, 20) +#define BFPT_DWORD15_QER_NONE (0x0UL << 20) /* Micron */ +#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY (0x1UL << 20) +#define BFPT_DWORD15_QER_SR1_BIT6 (0x2UL << 20) /* Macronix */ +#define BFPT_DWORD15_QER_SR2_BIT7 (0x3UL << 20) +#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) +#define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ + +struct sfdp_bfpt { + u32 dwords[BFPT_DWORD_MAX]; +}; + +/* Fast Read settings. */ + +struct sfdp_bfpt_read { + /* + * The bit in BFPT DWORD tells us + * whether the Fast Read x-y-z command is supported. + */ + u32 supported_dword; + u32 supported_bit; + + /* + * The half-word at offset in BFPT + * DWORD encodes the op code, the number of mode clocks and the number + * of wait states to be used by Fast Read x-y-z command. + */ + u32 settings_dword; + u32 settings_shift; + + /* The SPI Read x-y-z command. */ + u8 read_cmd; + +}; + +static const struct sfdp_bfpt_read sfdp_bfpt_reads[] = { + /* Fast Read 1-1-2 */ + { + BFPT_DWORD(1), BIT(16), /* Supported bit */ + BFPT_DWORD(4), 0, /* Settings */ + CMD_READ_DUAL_OUTPUT_FAST, + }, + + /* Fast Read 1-2-2 */ + { + BFPT_DWORD(1), BIT(20), /* Supported bit */ + BFPT_DWORD(4), 16, /* Settings */ + CMD_READ_DUAL_IO_FAST, + }, + + /* Fast Read 2-2-2 */ + { + BFPT_DWORD(5), BIT(0), /* Supported bit */ + BFPT_DWORD(6), 16, /* Settings */ + 0xFF, /* Not supported cmd */ + }, + + /* Fast Read 1-1-4 */ + { + BFPT_DWORD(1), BIT(22), /* Supported bit */ + BFPT_DWORD(3), 16, /* Settings */ + CMD_READ_QUAD_OUTPUT_FAST, + }, + + /* Fast Read 1-4-4 */ + { + BFPT_DWORD(1), BIT(21), /* Supported bit */ + BFPT_DWORD(3), 0, /* Settings */ + CMD_READ_QUAD_IO_FAST, + }, + + /* Fast Read 4-4-4 */ + { + BFPT_DWORD(5), BIT(4), /* Supported bit */ + BFPT_DWORD(7), 16, /* Settings */ + 0xFF, /* Not supported cmd */ + }, +}; + +struct sfdp_bfpt_erase { + /* + * The half-word at offset in DWORD encodes the + * op code and erase sector size to be used by Sector Erase commands. + */ + u32 dword; + u32 shift; +}; + +static const struct sfdp_bfpt_erase sfdp_bfpt_erases[] = { + /* Erase Type 1 in DWORD8 bits[15:0] */ + {BFPT_DWORD(8), 0}, + + /* Erase Type 2 in DWORD8 bits[31:16] */ + {BFPT_DWORD(8), 16}, + + /* Erase Type 3 in DWORD9 bits[15:0] */ + {BFPT_DWORD(9), 0}, + + /* Erase Type 4 in DWORD9 bits[31:16] */ + {BFPT_DWORD(9), 16}, +}; + extern const struct spi_flash_info spi_flash_ids[]; /* Send a single-byte command to the device and read the response */ diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index f385221..68d715f 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -1082,6 +1082,426 @@ int spi_flash_decode_fdt(struct spi_flash *flash) } #endif /* CONFIG_IS_ENABLED(OF_CONTROL) */ +/* + * Serial Flash Discoverable Parameters (SFDP) parsing. + */ + +/** + * spi_flash_read_sfdp() - read Serial Flash Discoverable Parameters. + * @flash: pointer to a 'struct spi_flash' + * @addr: offset in the SFDP area to start reading data from + * @len: number of bytes to read + * @buf: buffer where the SFDP data are copied into (dma-safe memory) + * + * Whatever the actual numbers of bytes for address and dummy cycles are + * for (Fast) Read commands, the Read SFDP (5Ah) instruction is always + * followed by a 3-byte address and 8 dummy clock cycles. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_flash_read_sfdp(struct spi_flash *flash, u32 addr, + size_t len, void *buf) +{ + u8 addr_width, read_cmd, dummy_byte; + int ret; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + read_cmd = flash->read_cmd; + addr_width = flash->addr_width; + dummy_byte = flash->dummy_byte; + + flash->read_cmd = CMD_READ_SFDP; + flash->addr_width = 3; + flash->dummy_byte = 1; + + ret = spi_flash_cmd_read_ops(flash, addr, len, (u8 *)buf); + if (ret < 0) + goto read_err; + ret = 0; + +read_err: + flash->read_cmd = read_cmd; + flash->addr_width = addr_width; + flash->dummy_byte = dummy_byte; + + spi_release_bus(flash->spi); + return ret; +} + +/** + * spi_flash_read_sfdp_dma_unsafe() - read Serial Flash Discoverable Parameters. + * @flash: pointer to a 'struct spi_flash' + * @addr: offset in the SFDP area to start reading data from + * @len: number of bytes to read + * @buf: buffer where the SFDP data are copied into + * + * Wrap spi_flash_read_sfdp() using a kmalloc'ed bounce buffer as @buf is now + * not guaranteed to be dma-safe. + * + * Return: -ENOMEM if kmalloc() fails, the return code of spi_flash_read_sfdp() + * otherwise. + */ +static int spi_flash_read_sfdp_dma_unsafe(struct spi_flash *flash, u32 addr, + size_t len, void *buf) +{ + void *dma_safe_buf; + int ret; + + dma_safe_buf = kmalloc(len, GFP_KERNEL); + if (!dma_safe_buf) + return -ENOMEM; + + ret = spi_flash_read_sfdp(flash, addr, len, dma_safe_buf); + memcpy(buf, dma_safe_buf, len); + kfree(dma_safe_buf); + + return ret; +} + +/** + * spi_flash_parse_bfpt() - read and parse the Basic Flash Parameter Table. + * @flash: pointer to a 'struct spi_flash' + * @info: pointer to a 'struct spi_flash_info' + * @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing + * the Basic Flash Parameter Table length and version + * + * The Basic Flash Parameter Table is the main and only mandatory table as + * defined by the SFDP (JESD216) specification. + * It provides us with the total size (memory density) of the data array and + * the number of address bytes for Fast Read, Page Program and Sector Erase + * commands. + * For Fast READ commands, it also gives the number of mode clock cycles and + * wait states (regrouped in the number of dummy clock cycles) for each + * supported instruction op code. + * For Page Program, the page size is now available since JESD216 rev A, however + * the supported instruction op codes are still not provided. + * For Sector Erase commands, this table stores the supported instruction op + * codes and the associated sector sizes. + * Finally, the Quad Enable Requirements (QER) are also available since JESD216 + * rev A. The QER bits encode the manufacturer dependent procedure to be + * executed to set the Quad Enable (QE) bit in some internal register of the + * Quad SPI memory. Indeed the QE bit, when it exists, must be set before + * sending any Quad SPI command to the memory. Actually, setting the QE bit + * tells the memory to reassign its WP# and HOLD#/RESET# pins to functions IO2 + * and IO3 hence enabling 4 (Quad) I/O lines. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_flash_parse_bfpt(struct spi_flash *flash, + const struct sfdp_parameter_header *bfpt_header) +{ + struct spi_slave *spi = flash->spi; + struct sfdp_bfpt bfpt; + size_t len; + int i, err; + u32 addr; + u16 half; + + /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */ + if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) + return -EINVAL; + + /* Read the Basic Flash Parameter Table. */ + len = min_t(size_t, sizeof(bfpt), + bfpt_header->length * sizeof(u32)); + addr = SFDP_PARAM_HEADER_PTP(bfpt_header); + memset(&bfpt, 0, sizeof(bfpt)); + err = spi_flash_read_sfdp_dma_unsafe(flash, addr, len, &bfpt); + if (err < 0) + return err; + + /* Fix endianness of the BFPT DWORDs. */ + for (i = 0; i < BFPT_DWORD_MAX; i++) + bfpt.dwords[i] = le32_to_cpu(bfpt.dwords[i]); + + /* Number of address bytes. */ + switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { + case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: + flash->addr_width = 3; + break; + + case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY: + flash->addr_width = 4; + break; + + case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4: + flash->addr_width = 3; + break; + + default: /* Reserved */ + break; + } + + /* Flash Memory Density (in bits). */ + flash->size = bfpt.dwords[BFPT_DWORD(2)]; + if (flash->size & BIT(31)) { + flash->size &= ~BIT(31); + + /* + * Prevent overflows on params->size. Anyway, a NOR of 2^64 + * bits is unlikely to exist so this error probably means + * the BFPT we are reading is corrupted/wrong. + */ + if (flash->size > 63) + return -EINVAL; + + flash->size = 1ULL << flash->size; + } else { + flash->size++; + } + flash->size >>= 3; /* Convert to bytes. */ + flash->size <<= flash->shift; + + flash->read_cmd = CMD_READ_ARRAY_FAST; + + /* Fast Read settings. */ + for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_reads); i++) { + const struct sfdp_bfpt_read *rd = &sfdp_bfpt_reads[i]; + u32 mode_dummy_cycle; + + if (!(bfpt.dwords[rd->supported_dword] & rd->supported_bit)) + continue; + + half = bfpt.dwords[rd->settings_dword] >> rd->settings_shift; + mode_dummy_cycle = ((half >> 5) & 0x07) + ((half >> 0) & 0x1f); + + switch (rd->read_cmd) { + case CMD_READ_QUAD_IO_FAST: + /* x-4-4 */ + if ((spi->mode & SPI_RX_QUAD) && + (spi->mode & SPI_TX_QUAD)) { + flash->read_cmd = rd->read_cmd; + flash->dummy_byte = (mode_dummy_cycle * 4) / 8; + } + break; + + case CMD_READ_QUAD_OUTPUT_FAST: + /* x-1-4 */ + if (spi->mode & SPI_RX_QUAD) { + flash->read_cmd = rd->read_cmd; + flash->dummy_byte = mode_dummy_cycle / 8; + } + break; + + case CMD_READ_DUAL_IO_FAST: + /* x-2-2 */ + if (((spi->mode & SPI_RX_DUAL) && + (spi->mode & SPI_TX_DUAL)) && + !(spi->mode & (SPI_RX_QUAD | SPI_TX_QUAD))) { + flash->read_cmd = rd->read_cmd; + flash->dummy_byte = (mode_dummy_cycle * 2) / 8; + } + break; + + case CMD_READ_DUAL_OUTPUT_FAST: + /* x-1-2*/ + if ((spi->mode & SPI_RX_DUAL) && + !(spi->mode & (SPI_RX_QUAD | SPI_TX_QUAD))) { + flash->read_cmd = rd->read_cmd; + flash->dummy_byte = mode_dummy_cycle / 8; + } + break; + + default: + debug("read_cmd=0x%x is not supported\n", rd->read_cmd); + break; + } + } + + if (flash->read_cmd == CMD_READ_ARRAY_FAST && spi->mode & SPI_RX_SLOW) + flash->read_cmd = CMD_READ_ARRAY_SLOW; + + /* Sector Erase settings. */ + for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) { + const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i]; + u32 erasesize; + u8 opcode; + + half = bfpt.dwords[er->dword] >> er->shift; + erasesize = half & 0xff; + + /* erasesize == 0 means this Erase Type is not supported. */ + if (!erasesize) + continue; + + erasesize = 1U << erasesize; + opcode = (half >> 8) & 0xff; +#ifdef CONFIG_SPI_FLASH_USE_4K_SECTORS + if (erasesize == SZ_4K) { + flash->erase_cmd = opcode; + flash->erase_size = erasesize; + break; + } +#endif + if (!flash->erase_size || flash->erase_size < erasesize) { + flash->erase_cmd = opcode; + flash->erase_size = erasesize; + } + } + + flash->erase_size <<= flash->shift; + + flash->sector_size = flash->erase_size; + + /* Stop here if not JESD216 rev A or later. */ + if (bfpt_header->length < BFPT_DWORD_MAX) + return 0; + + /* Page size: this field specifies 'N' so the page size = 2^N bytes. */ + flash->page_size = bfpt.dwords[BFPT_DWORD(11)]; + flash->page_size &= BFPT_DWORD11_PAGE_SIZE_MASK; + flash->page_size >>= BFPT_DWORD11_PAGE_SIZE_SHIFT; + flash->page_size = 1U << flash->page_size; + flash->page_size <<= flash->shift; + + if ((flash->read_cmd == CMD_READ_QUAD_OUTPUT_FAST) || + (flash->read_cmd == CMD_READ_QUAD_IO_FAST) || + (flash->write_cmd == CMD_QUAD_PAGE_PROGRAM)) { + /* Quad Enable Requirements. */ + switch (bfpt.dwords[BFPT_DWORD(15)] & BFPT_DWORD15_QER_MASK) { + case BFPT_DWORD15_QER_NONE: + break; + + case BFPT_DWORD15_QER_SR2_BIT1_BUGGY: + case BFPT_DWORD15_QER_SR2_BIT1_NO_RD: + break; + + case BFPT_DWORD15_QER_SR1_BIT6: +#ifdef CONFIG_SPI_FLASH_MACRONIX + macronix_quad_enable(flash); +#endif + break; + + case BFPT_DWORD15_QER_SR2_BIT7: + + break; + + case BFPT_DWORD15_QER_SR2_BIT1: +#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND) + spansion_quad_enable(flash); +#endif + break; + + default: + return -EINVAL; + } + } + + return 0; +} + +/** + * spi_flash_parse_sfdp() - parse the Serial Flash Discoverable Parameters. + * @flash: pointer to a 'struct spi_flash' + * + * The Serial Flash Discoverable Parameters are described by the JEDEC JESD216 + * specification. This is a standard which tends to supported by almost all + * (Q)SPI memory manufacturers. Those hard-coded tables allow us to learn at + * runtime the main parameters needed to perform basic SPI flash operations such + * as Fast Read, Page Program or Sector Erase commands. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_flash_parse_sfdp(struct spi_flash *flash) +{ + const struct sfdp_parameter_header *param_header, *bfpt_header; + struct sfdp_parameter_header *param_headers = NULL; + struct sfdp_header header; + size_t psize; + int i, err; + + /* Get the SFDP header. */ + err = spi_flash_read_sfdp_dma_unsafe(flash, 0, sizeof(header), &header); + if (err < 0) + return err; + + /* Check the SFDP header version. */ + if (le32_to_cpu(header.signature) != SFDP_SIGNATURE || + header.major != SFDP_JESD216_MAJOR || + header.minor < SFDP_JESD216_MINOR) + return -EINVAL; + + /* + * Verify that the first and only mandatory parameter header is a + * Basic Flash Parameter Table header as specified in JESD216. + */ + bfpt_header = &header.bfpt_header; + if (SFDP_PARAM_HEADER_ID(bfpt_header) != SFDP_BFPT_ID || + bfpt_header->major != SFDP_JESD216_MAJOR) + return -EINVAL; + + /* + * Allocate memory then read all parameter headers with a single + * Read SFDP command. These parameter headers will actually be parsed + * twice: a first time to get the latest revision of the basic flash + * parameter table, then a second time to handle the supported optional + * tables. + * Hence we read the parameter headers once for all to reduce the + * processing time. Also we use kmalloc() instead of devm_kmalloc() + * because we don't need to keep these parameter headers: the allocated + * memory is always released with kfree() before exiting this function. + */ + if (header.nph) { + psize = header.nph * sizeof(*param_headers); + + param_headers = kmalloc(psize, GFP_KERNEL); + if (!param_headers) + return -ENOMEM; + + err = spi_flash_read_sfdp(flash, sizeof(header), + psize, param_headers); + if (err < 0) { + debug("SF: failed to read SFDP parameter headers\n"); + goto exit; + } + } + + /* + * Check other parameter headers to get the latest revision of + * the basic flash parameter table. + */ + for (i = 0; i < header.nph; i++) { + param_header = ¶m_headers[i]; + + if (SFDP_PARAM_HEADER_ID(param_header) == SFDP_BFPT_ID && + param_header->major == SFDP_JESD216_MAJOR && + (param_header->minor > bfpt_header->minor || + (param_header->minor == bfpt_header->minor && + param_header->length > bfpt_header->length))) + bfpt_header = param_header; + } + + err = spi_flash_parse_bfpt(flash, bfpt_header); + if (err) + goto exit; + + /* Parse other parameter headers. */ + for (i = 0; i < header.nph; i++) { + param_header = ¶m_headers[i]; + + switch (SFDP_PARAM_HEADER_ID(param_header)) { + case SFDP_SECTOR_MAP_ID: + debug("non-uniform erase sector maps not supported\n"); + break; + + default: + break; + } + + if (err) + goto exit; + } + +exit: + kfree(param_headers); + return err; +} + int spi_flash_scan(struct spi_flash *flash) { struct spi_slave *spi = flash->spi; @@ -1272,6 +1692,15 @@ int spi_flash_scan(struct spi_flash *flash) flash->addr_width = SPI_FLASH_3B_ADDR_LEN; + /* Override the parameters with data read from SFDP tables. */ + if ((info->flags & RD_FULL) && (info->flags & FLASH_SFDP)) { + struct spi_flash sfdp_flash; + + memcpy(&sfdp_flash, flash, sizeof(struct spi_flash)); + if (spi_flash_parse_sfdp(flash)) + memcpy(flash, &sfdp_flash, sizeof(struct spi_flash)); + } + if (flash->size > SPI_FLASH_16MB_BOUN) { /* enable 4-byte addressing if the device exceeds 16MiB */ flash->addr_width = SPI_FLASH_4B_ADDR_LEN;