Received: by 2002:a05:6358:9144:b0:117:f937:c515 with SMTP id r4csp8448688rwr; Thu, 11 May 2023 01:07:05 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ6pxinzH81LncQ9DUfkgSTNf8MBwNflK8c/pYPxkGZlyg46EqY3jvAq/7YMGk+ufFhU4bcf X-Received: by 2002:a05:6a21:3299:b0:100:6863:8be7 with SMTP id yt25-20020a056a21329900b0010068638be7mr17062349pzb.62.1683792425616; Thu, 11 May 2023 01:07:05 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1683792425; cv=pass; d=google.com; s=arc-20160816; b=Xq2aM3GoqQ2FPCmFVwSvCkPA5HVsAvG9owXKtTsqFe3YSfwW0rpXTBAl1A6pA+3HbB fjKJKrUv14QlGeAg5VATDLa9cVZ7Epq3bM/IufkpQJ+g6y/4Jgrs2Sfj/SBzJEVn9ott PGp1sbwrr5yH49FLYQIxL0bKTfDgaxpyK3914UygY1kn3RuySUUVyt+PlSkIL2W9mKPK bzaiU+rFrB6YGIfQ5hYQWI99t4gnWKWR93QORAMdFFi+aMsCit934bc3J8fwtE6Zzb9y OaE/95WxvMbcf2u0VSl5BlXJuulrEk3w8SN12PFlj+3Fq/q0dDnK5r8jSo5un1cP7P9x wruA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:mime-version:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature; bh=CT2kXCIugqpqyHA9ppng2qcmbVe8qrLggSXJSAQsdq0=; b=wF2RwttZIkA0bwBO9WaxkKG0mhYl6FbQxMqSvSSdHpvd8hDuDdEeFaFi7J34P0sgWA hNtXp9TyrAX3kGAm2ApR9Zoy5tqlvi7SQ4q5aOM0m4pL5o9euV3QOhNx7eTlGQ0odAA/ UAK1s7anRN2j71pOfOXs10cv/7NM8RzvYAIgJWxQ3NEF+gRa7Rvv0n9EpNCdA8ncxRek OwV4viyaIhFewuWxzJiRyG1qwBqcOLAZDpRy+euCjQG8uNS72r3QV1xfUGX+061ollU1 X05KVgxBhZGXtWvwTCMoh+WI5e9lcxlZK9BSKJ/ro0zEIVUOR9FXy+lijm/5q6JvefRQ O/YQ== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@amd.com header.s=selector1 header.b=KEqFdbQ0; arc=pass (i=1 spf=pass spfdomain=amd.com dmarc=pass fromdomain=amd.com); spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=amd.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id k187-20020a633dc4000000b0050e7254402esi1385253pga.208.2023.05.11.01.06.53; Thu, 11 May 2023 01:07:05 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@amd.com header.s=selector1 header.b=KEqFdbQ0; arc=pass (i=1 spf=pass spfdomain=amd.com dmarc=pass fromdomain=amd.com); spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=amd.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237596AbjEKHeU (ORCPT + 99 others); Thu, 11 May 2023 03:34:20 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40710 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237550AbjEKHeB (ORCPT ); Thu, 11 May 2023 03:34:01 -0400 Received: from NAM10-BN7-obe.outbound.protection.outlook.com (mail-bn7nam10on2084.outbound.protection.outlook.com [40.107.92.84]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 593A3659D; Thu, 11 May 2023 00:33:20 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=SvDWcRFXOzklK99g0fvh0a8zwJVqEip/Hqa/bcCOCymACmP4Axm4EkH5lPA6dGowNLRlOHJcACBHgCo586LV8andD0zeMtqJp1nEvBoYYPLmhVG+ndb5+Zo8io7OfXU400RpmXVNO0UzGVhYXuYK6VGEHn5O9f9jMgynmdxOEEny3HBdHzZDmLFrGzM9Qy6oyOGJ4xAMPFwKWxu8PDcffcUeIS7vGzzJnb8GOabAB9UKkwAvoyjNgLHvj1rQgwIn1OrmV7qArns48VtSxz4Ebn4fQ1BvPUR7xL/paQ7gukk0oLlBi0MFL897HIOKLeG5+gWuhlo+u4yd3N431KFlvA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=CT2kXCIugqpqyHA9ppng2qcmbVe8qrLggSXJSAQsdq0=; b=XZdQFlKjp/7ulXu6V+6lXb5xcUwpQpA4bhu0JW7rZ9nsIkBooMUdK23xdKqNm/lEyYNpq3AY7GMwOmQVmHDtAoxY43KOxEeMlK+tkZyjPCfb6sjinsurIxxh8HR2ec7Ym18533wDArwvGJnHhcmTzK77/cSp2T0DdR8LBLKkEAO6vlQhmDSYVlDvpQRYr3vv3G+agYozRVvGNk2R9PyuTummXeXCgzoRHBLJTn8RN84lkQaqzfQyxPN2Lzc98r+SRe9nYeh0ckkyn4z1sreWCS9FzSG8dO0sLvSNigzFRamCF7nN0TzkjrWQAoJMb1AKSRBuXTSEwF9DdopcOP9qEA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 165.204.84.17) smtp.rcpttodomain=kernel.org smtp.mailfrom=amd.com; dmarc=pass (p=quarantine sp=quarantine pct=100) action=none header.from=amd.com; dkim=none (message not signed); arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amd.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=CT2kXCIugqpqyHA9ppng2qcmbVe8qrLggSXJSAQsdq0=; b=KEqFdbQ0G0sgLjDnShrFpH8gpd1Z4lw2rp0TwJQH1lNFI6R/PmbL1NC7uiwkzMVkxPrDoCakZf/OXUX3GhLoZm2PzJScMjJ1jU5Pr2zg9aFreBOYBJ8SPQBWEB9YyLAlMbBnL9fWBzEJ+QhovammBZgTESXO71bufBNl1AGEBcQ= Received: from BL1PR13CA0123.namprd13.prod.outlook.com (2603:10b6:208:2bb::8) by PH0PR12MB5403.namprd12.prod.outlook.com (2603:10b6:510:eb::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6363.29; Thu, 11 May 2023 07:33:17 +0000 Received: from BL02EPF000145B8.namprd05.prod.outlook.com (2603:10b6:208:2bb:cafe::42) by BL1PR13CA0123.outlook.office365.com (2603:10b6:208:2bb::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6411.6 via Frontend Transport; Thu, 11 May 2023 07:33:16 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 165.204.84.17) smtp.mailfrom=amd.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=amd.com; Received-SPF: Pass (protection.outlook.com: domain of amd.com designates 165.204.84.17 as permitted sender) receiver=protection.outlook.com; client-ip=165.204.84.17; helo=SATLEXMB03.amd.com; pr=C Received: from SATLEXMB03.amd.com (165.204.84.17) by BL02EPF000145B8.mail.protection.outlook.com (10.167.241.208) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.6387.21 via Frontend Transport; Thu, 11 May 2023 07:33:16 +0000 Received: from SATLEXMB03.amd.com (10.181.40.144) by SATLEXMB03.amd.com (10.181.40.144) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.34; Thu, 11 May 2023 02:33:15 -0500 Received: from xhdakumarma40u.xilinx.com (10.180.168.240) by SATLEXMB03.amd.com (10.181.40.144) with Microsoft SMTP Server id 15.1.2375.34 via Frontend Transport; Thu, 11 May 2023 02:33:11 -0500 From: Amit Kumar Mahapatra To: , , , , , , CC: , , , , , , , , , , , Amit Kumar Mahapatra Subject: [PATCH V8 6/7] mtd: spi-nor: Add parallel memories support in spi-nor Date: Thu, 11 May 2023 13:01:33 +0530 Message-ID: <20230511073134.41180-7-amit.kumar-mahapatra@amd.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230511073134.41180-1-amit.kumar-mahapatra@amd.com> References: <20230511073134.41180-1-amit.kumar-mahapatra@amd.com> MIME-Version: 1.0 Content-Type: text/plain X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: BL02EPF000145B8:EE_|PH0PR12MB5403:EE_ X-MS-Office365-Filtering-Correlation-Id: 958da3f9-e899-4196-b75c-08db51f1fc4e X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: hv2fzpM4MTvQJEE8YQVdYo+Vr/NaapMEuPj7Z47pPsSnBHsjfgmULH8NpKJbOWGPj3/x1nr9xGqdj7rou3EIZv+6LJ8gLFWKhAcHR9e1OEOM6N44WECVgZOYbvQFBZLD1cTQMoeGJy7GNfTpYVDbojm6Xtyurt/E8cztwouPd4n7VboRveUgvvJ365wa8uFLXeXpcDEljdaNJV/n+u5IhbDWoH7NYMs98LWWeMD47YdJZ7L7DzC6k9v5cOOxXWGosKXNSkuNekO68xE0+QR2yMQdW/OGOxtcnmvjfFZi+q0HpTKr7sbFQI5Q5LjnZfmHteTjb/WL3jS8+5E/iSiwx7DbhCDSFOfhOA0fAdsu0aB1nXJ/kPnTKYK9LZCY6HqZlBxuhXCW6boa0xLpXpdRHzk7j7zq9Hn/z/LHMpUZMjOhkE8BPy0aCUs7k7dSewTeUkj2OqZpcqbGJtUbh3wsYQ9P3ROm474ayzneohNWykQzVBZDXeaEoagsvW43KKs/Ow2GBO8dHQ78v5R1RvCMb59Dan4ecN8+XT4spfWQprl8tcpl5dNOx4s7JvnAOq6ZiGBi1C7MvW6ZCZU4uIvJK9RsU7aOSpSxLPLZ7NbBesd2pZiT+BV3k+6PrMZX4w/8x3GV651PNhCV4uoIv3aLHPkSIxh8CfJ9Eih8rjZa1SoTI7NRgnxqAj4o5OOKKfkUeiwOjcASh+kCy+vok6c+JXCSfI7WWQ290QL9UpxNG8s= X-Forefront-Antispam-Report: CIP:165.204.84.17;CTRY:US;LANG:en;SCL:1;SRV:;IPV:CAL;SFV:NSPM;H:SATLEXMB03.amd.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230028)(4636009)(376002)(396003)(39860400002)(136003)(346002)(451199021)(40470700004)(36840700001)(46966006)(40460700003)(82310400005)(8936002)(1076003)(426003)(2616005)(7416002)(26005)(8676002)(40480700001)(336012)(54906003)(2906002)(47076005)(83380400001)(5660300002)(30864003)(356005)(6666004)(41300700001)(110136005)(70586007)(478600001)(316002)(70206006)(4326008)(186003)(81166007)(36756003)(36860700001)(86362001)(82740400003)(36900700001);DIR:OUT;SFP:1101; X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 11 May 2023 07:33:16.4573 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 958da3f9-e899-4196-b75c-08db51f1fc4e X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=3dd8961f-e488-4e60-8e11-a82d994e183d;Ip=[165.204.84.17];Helo=[SATLEXMB03.amd.com] X-MS-Exchange-CrossTenant-AuthSource: BL02EPF000145B8.namprd05.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH0PR12MB5403 X-Spam-Status: No, score=-1.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FORGED_SPF_HELO, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_PASS,SPF_NONE, T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The current implementation assumes that a maximum of two flashes are connected in parallel mode. The QSPI controller splits the data evenly between both the flashes so, both the flashes that are connected in parallel mode should be identical. During each operation SPI-NOR sets 0th bit for CS0 & 1st bit for CS1 in nor->spimem->spi->cs_index_mask. The QSPI driver will then assert/de-assert CS0 & CS1. Write operation in parallel mode are performed in page size * 2 chunks as each write operation results in writing both the flashes. For doubling the address space each operation is performed at addr/2 flash offset, where addr is the address specified by the user. Signed-off-by: Amit Kumar Mahapatra --- drivers/mtd/spi-nor/core.c | 545 +++++++++++++++++++++++--------- drivers/mtd/spi-nor/core.h | 4 + drivers/mtd/spi-nor/micron-st.c | 5 + 3 files changed, 406 insertions(+), 148 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 87eea547910c..8d3027ac9e8d 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -464,17 +464,29 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) op.data.nbytes = 2; } + if (nor->flags & SNOR_F_HAS_PARALLEL) + op.data.nbytes = 2; + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDSR, sr, - 1); + if (nor->flags & SNOR_F_HAS_PARALLEL) + ret = spi_nor_controller_ops_read_reg(nor, + SPINOR_OP_RDSR, + sr, 2); + else + ret = spi_nor_controller_ops_read_reg(nor, + SPINOR_OP_RDSR, + sr, 1); } if (ret) dev_dbg(nor->dev, "error %d reading SR\n", ret); + if (nor->flags & SNOR_F_HAS_PARALLEL) + sr[0] |= sr[1]; + return ret; } @@ -1837,16 +1849,140 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) if (ret) return ret; - /* whole-chip erase? */ - if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { - unsigned long timeout; + if (!(nor->flags & SNOR_F_HAS_PARALLEL)) { + /* whole-chip erase? */ + if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { + unsigned long timeout; - ret = spi_nor_lock_device(nor); - if (ret) - goto erase_err; + ret = spi_nor_lock_device(nor); + if (ret) + goto erase_err; + + while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && params) { + nor->spimem->spi->cs_index_mask = 1 << cur_cs_num; + ret = spi_nor_write_enable(nor); + if (ret) { + spi_nor_unlock_device(nor); + goto erase_err; + } + + ret = spi_nor_erase_chip(nor); + spi_nor_unlock_device(nor); + if (ret) + goto erase_err; + + /* + * Scale the timeout linearly with the size of the flash, with + * a minimum calibrated to an old 2MB flash. We could try to + * pull these from CFI/SFDP, but these values should be good + * enough for now. + */ + timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES, + CHIP_ERASE_2MB_READY_WAIT_JIFFIES * + (unsigned long)(params->size / + SZ_2M)); + ret = spi_nor_wait_till_ready_with_timeout(nor, timeout); + if (ret) + goto erase_err; + + cur_cs_num++; + params = spi_nor_get_params(nor, cur_cs_num); + } + + /* REVISIT in some cases we could speed up erasing large regions + * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up + * to use "small sector erase", but that's not always optimal. + */ + + /* "sector"-at-a-time erase */ + } else if (spi_nor_has_uniform_erase(nor)) { + /* Determine the flash from which the operation need to start */ + while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && + (addr > sz - 1) && params) { + cur_cs_num++; + params = spi_nor_get_params(nor, cur_cs_num); + sz += params->size; + } + while (len) { + ret = spi_nor_lock_device(nor); + if (ret) + goto erase_err; + + nor->spimem->spi->cs_index_mask = 1 << cur_cs_num; + ret = spi_nor_write_enable(nor); + if (ret) { + spi_nor_unlock_device(nor); + goto erase_err; + } + + offset = addr; + if (nor->flags & SNOR_F_HAS_STACKED) { + params = spi_nor_get_params(nor, cur_cs_num); + offset -= (sz - params->size); + } + ret = spi_nor_erase_sector(nor, offset); + spi_nor_unlock_device(nor); + if (ret) + goto erase_err; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto erase_err; + + addr += mtd->erasesize; + len -= mtd->erasesize; + + /* + * Flash cross over condition in stacked mode. + */ + if ((nor->flags & SNOR_F_HAS_STACKED) && (addr > sz - 1)) { + cur_cs_num++; + params = spi_nor_get_params(nor, cur_cs_num); + sz += params->size; + } + } + + /* erase multiple sectors */ + } else { + u64 erase_len = 0; + + /* Determine the flash from which the operation need to start */ + while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && + (addr > sz - 1) && params) { + cur_cs_num++; + params = spi_nor_get_params(nor, cur_cs_num); + sz += params->size; + } + /* perform multi sector erase onec per Flash*/ + while (len) { + erase_len = (len > (sz - addr)) ? (sz - addr) : len; + offset = addr; + nor->spimem->spi->cs_index_mask = 1 << cur_cs_num; + if (nor->flags & SNOR_F_HAS_STACKED) { + params = spi_nor_get_params(nor, cur_cs_num); + offset -= (sz - params->size); + } + ret = spi_nor_erase_multi_sectors(nor, offset, erase_len); + if (ret) + goto erase_err; + len -= erase_len; + addr += erase_len; + params = spi_nor_get_params(nor, cur_cs_num); + sz += params->size; + } + } + } else { + nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS; + + /* whole-chip erase? */ + if (len == mtd->size && !(nor->flags & + SNOR_F_NO_OP_CHIP_ERASE)) { + unsigned long timeout; + + ret = spi_nor_lock_device(nor); + if (ret) + goto erase_err; - while (cur_cs_num < SNOR_FLASH_CNT_MAX && params) { - nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num; ret = spi_nor_write_enable(nor); if (ret) { spi_nor_unlock_device(nor); @@ -1865,96 +2001,51 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) */ timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES, CHIP_ERASE_2MB_READY_WAIT_JIFFIES * - (unsigned long)(params->size / SZ_2M)); + (unsigned long)(mtd->size / SZ_2M)); ret = spi_nor_wait_till_ready_with_timeout(nor, timeout); if (ret) goto erase_err; - cur_cs_num++; - } - /* REVISIT in some cases we could speed up erasing large regions - * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up - * to use "small sector erase", but that's not always optimal. - */ + /* REVISIT in some cases we could speed up erasing large regions + * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up + * to use "small sector erase", but that's not always optimal. + */ - /* "sector"-at-a-time erase */ - } else if (spi_nor_has_uniform_erase(nor)) { - /* Determine the flash from which the operation need to start */ - while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) { - cur_cs_num++; - params = spi_nor_get_params(nor, cur_cs_num); - sz += params->size; - } + /* "sector"-at-a-time erase */ + } else if (spi_nor_has_uniform_erase(nor)) { + while (len) { + ret = spi_nor_lock_device(nor); + if (ret) + goto erase_err; + ret = spi_nor_write_enable(nor); + if (ret) { + spi_nor_unlock_device(nor); + goto erase_err; + } - while (len) { - ret = spi_nor_lock_device(nor); - if (ret) - goto erase_err; + offset = addr / 2; - nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num; - ret = spi_nor_write_enable(nor); - if (ret) { + ret = spi_nor_erase_sector(nor, offset); spi_nor_unlock_device(nor); - goto erase_err; - } - offset = addr; - if (nor->flags & SNOR_F_HAS_STACKED) { - params = spi_nor_get_params(nor, cur_cs_num); - offset -= (sz - params->size); - } - - ret = spi_nor_erase_sector(nor, offset); - spi_nor_unlock_device(nor); - if (ret) - goto erase_err; - - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto erase_err; + if (ret) + goto erase_err; - addr += mtd->erasesize; - len -= mtd->erasesize; + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto erase_err; - /* - * Flash cross over condition in stacked mode. - */ - if ((nor->flags & SNOR_F_HAS_STACKED) && (addr > sz - 1)) { - cur_cs_num++; - params = spi_nor_get_params(nor, cur_cs_num); - sz += params->size; + addr += mtd->erasesize; + len -= mtd->erasesize; } - } - /* erase multiple sectors */ - } else { - u64 erase_len = 0; - - /* Determine the flash from which the operation need to start */ - while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) { - cur_cs_num++; - params = spi_nor_get_params(nor, cur_cs_num); - sz += params->size; - } - /* perform multi sector erase onec per Flash*/ - while (len) { - erase_len = (len > (sz - addr)) ? (sz - addr) : len; - offset = addr; - nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num; - if (nor->flags & SNOR_F_HAS_STACKED) { - params = spi_nor_get_params(nor, cur_cs_num); - offset -= (sz - params->size); - } - ret = spi_nor_erase_multi_sectors(nor, offset, erase_len); + /* erase multiple sectors */ + } else { + offset = addr / 2; + ret = spi_nor_erase_multi_sectors(nor, offset, len); if (ret) goto erase_err; - len -= erase_len; - addr += erase_len; - cur_cs_num++; - params = spi_nor_get_params(nor, cur_cs_num); - sz += params->size; } } - ret = spi_nor_write_disable(nor); erase_err: @@ -2155,7 +2246,9 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, ssize_t ret, read_len, len_lock = len; loff_t from_lock = from; u32 cur_cs_num = 0; - u64 sz; + u_char *readbuf; + bool is_ofst_odd = false; + u64 sz = 0; dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); @@ -2166,23 +2259,47 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, params = spi_nor_get_params(nor, 0); sz = params->size; - /* Determine the flash from which the operation need to start */ - while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (from > sz - 1) && params) { - cur_cs_num++; - params = spi_nor_get_params(nor, cur_cs_num); - sz += params->size; + /* + * Cannot read from odd offset in parallel mode, so read + * len + 1 from offset + 1 and ignore offset[0] data. + */ + if ((nor->flags & SNOR_F_HAS_PARALLEL) && (from & 0x01)) { + from = (loff_t)(from - 1); + len = (size_t)(len + 1); + is_ofst_odd = true; + readbuf = kmalloc(len, GFP_KERNEL); + if (!readbuf) + return -ENOMEM; + } else { + readbuf = buf; + } + + if (!(nor->flags & SNOR_F_HAS_PARALLEL)) { + /* Determine the flash from which the operation need to start */ + while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (from > sz - 1) && params) { + cur_cs_num++; + params = spi_nor_get_params(nor, cur_cs_num); + sz += params->size; + } } + while (len) { loff_t addr = from; - nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num; - read_len = (len > (sz - addr)) ? (sz - addr) : len; - params = spi_nor_get_params(nor, cur_cs_num); - addr -= (sz - params->size); + if (nor->flags & SNOR_F_HAS_PARALLEL) { + nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS; + read_len = len; + addr /= 2; + } else { + nor->spimem->spi->cs_index_mask = 1 << cur_cs_num; + read_len = (len > (sz - addr)) ? (sz - addr) : len; + params = spi_nor_get_params(nor, cur_cs_num); + addr -= (sz - params->size); + } addr = spi_nor_convert_addr(nor, addr); - ret = spi_nor_read_data(nor, addr, len, buf); + ret = spi_nor_read_data(nor, addr, read_len, readbuf); if (ret == 0) { /* We shouldn't see 0-length reads */ ret = -EIO; @@ -2192,8 +2309,20 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, goto read_err; WARN_ON(ret > read_len); - *retlen += ret; + if (is_ofst_odd) { + /* + * Cannot read from odd offset in parallel mode. + * So read len + 1 from offset + 1 from the flash + * and copy len data from readbuf[1]. + */ + memcpy(buf, (readbuf + 1), (len - 1)); + *retlen += (ret - 1); + } else { + *retlen += ret; + } buf += ret; + if (!is_ofst_odd) + readbuf += ret; from += ret; len -= ret; @@ -2211,8 +2340,10 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, ret = 0; read_err: - spi_nor_unlock_and_unprep_rd(nor, from_lock, len_lock); + if (is_ofst_odd) + kfree(readbuf); + spi_nor_unlock_and_unprep_rd(nor, from_lock, len_lock); return ret; } @@ -2241,11 +2372,37 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, page_size = params->page_size; sz = params->size; - /* Determine the flash from which the operation need to start */ - while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (to > sz - 1) && params) { - cur_cs_num++; - params = spi_nor_get_params(nor, cur_cs_num); - sz += params->size; + if (nor->flags & SNOR_F_HAS_PARALLEL) { + /* + * Cannot write to odd offset in parallel mode, + * so write 2 byte first. + */ + if (to & 0x01) { + u8 two[2] = {0xff, buf[0]}; + size_t written_len; + + ret = spi_nor_write(mtd, to & ~1, 2, &written_len, two); + if (ret < 0) + return ret; + *retlen += 1; /* We've written only one actual byte */ + ++buf; + --len; + ++to; + } + /* + * Write operation are performed in page size chunks and in + * parallel memories both the flashes are written simultaneously, + * hence doubled the page_size. + */ + page_size <<= 1; + + } else { + /* Determine the flash from which the operation need to start */ + while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (to > sz - 1) && params) { + cur_cs_num++; + params = spi_nor_get_params(nor, cur_cs_num); + sz += params->size; + } } for (i = 0; i < len; ) { @@ -2267,9 +2424,14 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, /* the size of data remaining on the first page */ page_remain = min_t(size_t, page_size - page_offset, len - i); - nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num; - params = spi_nor_get_params(nor, cur_cs_num); - addr -= (sz - params->size); + if (nor->flags & SNOR_F_HAS_PARALLEL) { + nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS; + addr /= 2; + } else { + nor->spimem->spi->cs_index_mask = 1 << cur_cs_num; + params = spi_nor_get_params(nor, cur_cs_num); + addr -= (sz - params->size); + } addr = spi_nor_convert_addr(nor, addr); @@ -2725,7 +2887,15 @@ static int spi_nor_select_erase(struct spi_nor *nor) if (!erase) return -EINVAL; nor->erase_opcode = erase->opcode; - mtd->erasesize = erase->size; + /* + * In parallel-memories the erase operation is + * performed on both the flashes simultaneously + * so, double the erasesize. + */ + if (nor->flags & SNOR_F_HAS_PARALLEL) + mtd->erasesize = erase->size * 2; + else + mtd->erasesize = erase->size; return 0; } @@ -2743,7 +2913,15 @@ static int spi_nor_select_erase(struct spi_nor *nor) if (!erase) return -EINVAL; - mtd->erasesize = erase->size; + /* + * In parallel-memories the erase operation is + * performed on both the flashes simultaneously + * so, double the erasesize. + */ + if (nor->flags & SNOR_F_HAS_PARALLEL) + mtd->erasesize = erase->size * 2; + else + mtd->erasesize = erase->size; return 0; } @@ -3071,7 +3249,22 @@ static void spi_nor_late_init_params(struct spi_nor *nor) nor->flags |= SNOR_F_HAS_STACKED; } } - if (nor->flags & SNOR_F_HAS_STACKED) { + i = 0; + idx = 0; + while (i < SNOR_FLASH_CNT_MAX) { + rc = of_property_read_u64_index(np, "parallel-memories", idx, &flash_size[i]); + if (rc == -EINVAL) { + break; + } else if (rc == -EOVERFLOW) { + idx++; + } else { + idx++; + i++; + if (!(nor->flags & SNOR_F_HAS_PARALLEL)) + nor->flags |= SNOR_F_HAS_PARALLEL; + } + } + if (nor->flags & (SNOR_F_HAS_STACKED | SNOR_F_HAS_PARALLEL)) { for (idx = 1; idx < SNOR_FLASH_CNT_MAX; idx++) { params = spi_nor_get_params(nor, idx); params = devm_kzalloc(nor->dev, sizeof(*params), GFP_KERNEL); @@ -3292,24 +3485,42 @@ static int spi_nor_quad_enable(struct spi_nor *nor) struct spi_nor_flash_parameter *params; int err, idx; - for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) { - params = spi_nor_get_params(nor, idx); - if (params) { - if (!params->quad_enable) - return 0; + if (nor->flags & SNOR_F_HAS_PARALLEL) { + params = spi_nor_get_params(nor, 0); + if (!params->quad_enable) + return 0; - if (!(spi_nor_get_protocol_width(nor->read_proto) == 4 || - spi_nor_get_protocol_width(nor->write_proto) == 4)) - return 0; - /* - * Set the appropriate CS index before - * issuing the command. - */ - nor->spimem->spi->cs_index_mask = 0x01 << idx; + if (!(spi_nor_get_protocol_width(nor->read_proto) == 4 || + spi_nor_get_protocol_width(nor->write_proto) == 4)) + return 0; + /* + * In parallel mode both chip selects i.e., CS0 & + * CS1 need to be asserted simulatneously. + */ + nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS; + err = params->quad_enable(nor); + if (err) + return err; + } else { + for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) { + params = spi_nor_get_params(nor, idx); + if (params) { + if (!params->quad_enable) + return 0; - err = params->quad_enable(nor); - if (err) - return err; + if (!(spi_nor_get_protocol_width(nor->read_proto) == 4 || + spi_nor_get_protocol_width(nor->write_proto) == 4)) + return 0; + /* + * Set the appropriate CS index before + * issuing the command. + */ + nor->spimem->spi->cs_index_mask = 1 << idx; + + err = params->quad_enable(nor); + if (err) + return err; + } } } return err; @@ -3385,16 +3596,27 @@ static int spi_nor_init(struct spi_nor *nor) */ WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, "enabling reset hack; may not recover from unexpected reboots\n"); - for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) { - if (spi_nor_get_params(nor, idx)) { - /* - * Select the appropriate CS index before - * issuing the command. - */ - nor->spimem->spi->cs_index_mask = 0x01 << idx; - err = spi_nor_set_4byte_addr_mode(nor, true); - if (err) - return err; + if (nor->flags & SNOR_F_HAS_PARALLEL) { + /* + * In parallel mode both chip selects i.e., CS0 & + * CS1 need to be asserted simulatneously. + */ + nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS; + err = spi_nor_set_4byte_addr_mode(nor, true); + if (err) + return err; + } else { + for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) { + if (spi_nor_get_params(nor, idx)) { + /* + * Select the appropriate CS index before + * issuing the command. + */ + nor->spimem->spi->cs_index_mask = 1 << idx; + err = spi_nor_set_4byte_addr_mode(nor, true); + if (err) + return err; + } } } } @@ -3516,23 +3738,41 @@ static void spi_nor_restore(struct spi_nor *nor) /* restore the addressing mode */ if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && nor->flags & SNOR_F_BROKEN_RESET) { - for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) { - if (spi_nor_get_params(nor, idx)) { + if (nor->flags & SNOR_F_HAS_PARALLEL) { + /* + * In parallel mode both chip selects i.e., CS0 & + * CS1 need to be asserted simulatneously. + */ + nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS; + spi_nor_set_4byte_addr_mode(nor, false); + if (ret) /* - * Select the appropriate CS index before - * issuing the command. + * Do not stop the execution in the hope that the flash + * will default to the 3-byte address mode after the + * software reset. */ - nor->spimem->spi->cs_index_mask = 1 << idx; - ret = spi_nor_set_4byte_addr_mode(nor, false); - if (ret) + dev_err(nor->dev, + "Failed to exit 4-byte address mode, err = %d\n", + ret); + } else { + for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) { + if (spi_nor_get_params(nor, idx)) { /* - * Do not stop the execution in the hope that the flash - * will default to the 3-byte address mode after the - * software reset. + * Select the appropriate CS index before + * issuing the command. */ - dev_err(nor->dev, - "Failed to exit 4-byte address mode, err = %d\n", - ret); + nor->spimem->spi->cs_index_mask = 1 << idx; + ret = spi_nor_set_4byte_addr_mode(nor, false); + if (ret) + /* + * Do not stop the execution in the hope that the + * flash will default to the 3-byte address mode + * after the software reset. + */ + dev_err(nor->dev, + "Failed to exit 4-byte address mode, err = %d\n", + ret); + } } } } @@ -3620,7 +3860,16 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor) else mtd->_erase = spi_nor_erase; mtd->writesize = params->writesize; - mtd->writebufsize = params->page_size; + /* + * In parallel-memories the write operation is + * performed on both the flashes simultaneously + * one page per flash, so double the writebufsize. + */ + if (nor->flags & SNOR_F_HAS_PARALLEL) + mtd->writebufsize = params->page_size << 1; + else + mtd->writebufsize = params->page_size; + for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) { params = spi_nor_get_params(nor, idx); if (params) diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 89dc65a697b8..52bc8041a4f9 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -14,6 +14,9 @@ /* In single configuration enable CS0 */ #define SPI_NOR_ENABLE_CS0 BIT(0) +/* In parallel configuration enable multiple CS */ +#define SPI_NOR_ENABLE_MULTI_CS (BIT(0) | BIT(1)) + /* Standard SPI NOR flash operations. */ #define SPI_NOR_READID_OP(naddr, ndummy, buf, len) \ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 0), \ @@ -136,6 +139,7 @@ enum spi_nor_option_flags { SNOR_F_RWW = BIT(14), SNOR_F_ECC = BIT(15), SNOR_F_HAS_STACKED = BIT(16), + SNOR_F_HAS_PARALLEL = BIT(17), }; struct spi_nor_read_command { diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index 92d6b0e7512c..26a871c4fe98 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -335,6 +335,9 @@ static int micron_st_nor_read_fsr(struct spi_nor *nor, u8 *fsr) op.data.nbytes = 2; } + if (nor->flags & SNOR_F_HAS_PARALLEL) + op.data.nbytes = 2; + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); @@ -346,6 +349,8 @@ static int micron_st_nor_read_fsr(struct spi_nor *nor, u8 *fsr) if (ret) dev_dbg(nor->dev, "error %d reading FSR\n", ret); + if (nor->flags & SNOR_F_HAS_PARALLEL) + fsr[0] &= fsr[1]; return ret; } -- 2.17.1