From patchwork Thu Dec 28 06:12:09 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jagan Teki X-Patchwork-Id: 853337 X-Patchwork-Delegate: trini@ti.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=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=amarulasolutions-com.20150623.gappssmtp.com header.i=@amarulasolutions-com.20150623.gappssmtp.com header.b="Eo0UwTl/"; dkim-atps=neutral Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 3z6flY5hzhz9ryr for ; Thu, 28 Dec 2017 17:22:33 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id 3A14EC21C2B; Thu, 28 Dec 2017 06:16:02 +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=RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, T_DKIM_INVALID 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 8DB60C21DEF; Thu, 28 Dec 2017 06:14:28 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 954B6C21DA9; Thu, 28 Dec 2017 06:13:32 +0000 (UTC) Received: from mail-pl0-f66.google.com (mail-pl0-f66.google.com [209.85.160.66]) by lists.denx.de (Postfix) with ESMTPS id 05B0DC21DA9 for ; Thu, 28 Dec 2017 06:13:27 +0000 (UTC) Received: by mail-pl0-f66.google.com with SMTP id n13so20698843plp.11 for ; Wed, 27 Dec 2017 22:13:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amarulasolutions-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=c2cRQkNjJ1HvgtRO5Np1rzb0253jH6bIdHsD4bn5mmQ=; b=Eo0UwTl/vH2zYHzHbmbqdfPCzaEYH8zR0/TcaO9rgO6T5pE8fQZb8pY6SAEuDNTJ54 wPvjeMg60eAeFYCNCVvnb22VruVQ2jANusm3OBFSLTm1cBsODo402MmCbs/NnNqk0n3m 5FgnO+OHzi1UgzZBcqTNmjbjuhdny8CuNX2loFzeaWcBoz21XcKBe+HfrlfJOWe8QGhB wnrGGMtMM7qiZhv/u9EjuXWfXjiX3HZUfrZxNoZ3+0JT+QBClF1kpaTl5PL9/42JJ1jQ 9LLMlX4bHsxKPOJDTCZD4RG0zmF9OoC0ZPocgueIDY8CwV1u7/RFo0EFLtOE27ucVZ4a cZ8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=c2cRQkNjJ1HvgtRO5Np1rzb0253jH6bIdHsD4bn5mmQ=; b=JAkZI8TBkfqXFxqtKYlofbOtrTwl6fl5AjAN8PbDfEbQz90uXftDvQUd2agmcoGQn3 y13l20YvjNLHyEbfO45g+XhK95eBPjTa7ROvkiwMNQaJ/VDdSIDWhEu0BEeqFYmPtKM9 ordqzbVTu4urI7tuKSGwsJmWBurfI7Dq2bVh/96s8uPb3hEExqjU4RCHVVmdzBXbdZzZ 46crpko/kJGIQzJYUldzcFcKP8PHiUTUgvO/HWJ2336j6/MuOMgr8Umd2W5d7bHbi+CK OYAuAxkpWijxuAj+bLQ0LH5c1GBvS1f3VbJ2lV//J3hsB3lZMvTTO1mBDn+BjlcRdKt3 Gw3g== X-Gm-Message-State: AKGB3mIde87etfL7wvF0HqBV9wVtOBkvrEoHmgPjXDlLhjBlxbrFULdg tjK7EMEoiekVR26sJWFdxVeKS28U X-Google-Smtp-Source: ACJfBovYnRZvuHjx+feUFgJrZinLL1IPQs+skDQbNVpgldJZgNo2JS4d9vkA7RnaB8EdRi2N0RoYxw== X-Received: by 10.84.172.195 with SMTP id n61mr30916393plb.49.1514441604704; Wed, 27 Dec 2017 22:13:24 -0800 (PST) Received: from localhost.localdomain ([27.59.176.39]) by smtp.gmail.com with ESMTPSA id s189sm16789697pgc.5.2017.12.27.22.13.19 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 27 Dec 2017 22:13:24 -0800 (PST) From: Jagan Teki To: u-boot@lists.denx.de Date: Thu, 28 Dec 2017 11:42:09 +0530 Message-Id: <1514441553-27543-4-git-send-email-jagan@amarulasolutions.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1514441553-27543-1-git-send-email-jagan@amarulasolutions.com> References: <1514441553-27543-1-git-send-email-jagan@amarulasolutions.com> Cc: Tom Rini Subject: [U-Boot] [PATCH v10 03/27] mtd: add SPI-NOR core support 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: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver. The idea is taken from Linux spi-nor framework. ======================================= cmd/spinor.c ======================================= mtd-uclass.c ======================================= spi-nor-uclass.c ======================================= spi-nor.c ======================================= m25p80.c zynq_qspinor.c ======================================= spi-uclass.c ======================================= zynq_qspi.c ======================================= #####SPI NOR chip###### ======================================= Signed-off-by: Suneel Garapati Signed-off-by: Jagan Teki --- Makefile | 1 + drivers/mtd/spi-nor/Makefile | 7 + drivers/mtd/spi-nor/spi-nor-ids.c | 184 +++++++++++ drivers/mtd/spi-nor/spi-nor-uclass.c | 143 +++++++++ drivers/mtd/spi-nor/spi-nor.c | 569 +++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/linux/mtd/spi-nor.h | 217 +++++++++++++ 7 files changed, 1122 insertions(+) create mode 100644 drivers/mtd/spi-nor/Makefile create mode 100644 drivers/mtd/spi-nor/spi-nor-ids.c create mode 100644 drivers/mtd/spi-nor/spi-nor-uclass.c create mode 100644 drivers/mtd/spi-nor/spi-nor.c create mode 100644 include/linux/mtd/spi-nor.h diff --git a/Makefile b/Makefile index e6d309a..70b5202 100644 --- a/Makefile +++ b/Makefile @@ -662,6 +662,7 @@ libs-$(CONFIG_CMD_NAND) += drivers/mtd/nand/ libs-y += drivers/mtd/onenand/ libs-$(CONFIG_CMD_UBI) += drivers/mtd/ubi/ libs-y += drivers/mtd/spi/ +libs-y += drivers/mtd/spi-nor/ libs-y += drivers/net/ libs-y += drivers/net/phy/ libs-y += drivers/pci/ diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile new file mode 100644 index 0000000..c1212a8 --- /dev/null +++ b/drivers/mtd/spi-nor/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (C) 2016 Jagan Teki +# +# SPDX-License-Identifier: GPL-2.0+ + +## spi-nor core +obj-y += spi-nor.o spi-nor-uclass.o spi-nor-ids.o diff --git a/drivers/mtd/spi-nor/spi-nor-ids.c b/drivers/mtd/spi-nor/spi-nor-ids.c new file mode 100644 index 0000000..db95655 --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor-ids.c @@ -0,0 +1,184 @@ +/* + * SPI NOR IDs. + * + * Copyright (C) 2016 Jagan Teki + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +/* Used when the "_ext_id" is two bytes at most */ +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff, \ + ((_ext_id) >> 8) & 0xff, \ + (_ext_id) & 0xff, \ + }, \ + .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), + +#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff, \ + ((_ext_id) >> 16) & 0xff, \ + ((_ext_id) >> 8) & 0xff, \ + (_ext_id) & 0xff, \ + }, \ + .id_len = 6, \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), + +const struct spi_nor_info spi_nor_ids[] = { +#ifdef CONFIG_SPI_NOR_MACRONIX /* MACRONIX */ + {"mx25l2006e", INFO(0xc22012, 0x0, 64 * 1024, 4, 0) }, + {"mx25l4005", INFO(0xc22013, 0x0, 64 * 1024, 8, 0) }, + {"mx25l8005", INFO(0xc22014, 0x0, 64 * 1024, 16, 0) }, + {"mx25l1605d", INFO(0xc22015, 0x0, 64 * 1024, 32, 0) }, + {"mx25l3205d", INFO(0xc22016, 0x0, 64 * 1024, 64, 0) }, + {"mx25l6405d", INFO(0xc22017, 0x0, 64 * 1024, 128, 0) }, + {"mx25l12805", INFO(0xc22018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"mx25l25635f", INFO(0xc22019, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP) }, + {"mx25l51235f", INFO(0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP) }, + {"mx25u6435f", INFO(0xc22537, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP) }, + {"mx25l12855e", INFO(0xc22618, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"mx25u1635e", INFO(0xc22535, 0x0, 64 * 1024, 32, SECT_4K) }, + {"mx66u51235f", INFO(0xc2253a, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP) }, + {"mx66l1g45g", INFO(0xc2201b, 0x0, 64 * 1024, 2048, RD_FULL | WR_QPP) }, +#endif +#ifdef CONFIG_SPI_NOR_SPANSION /* SPANSION */ + {"s25fl008a", INFO(0x010213, 0x0, 64 * 1024, 16, 0) }, + {"s25fl016a", INFO(0x010214, 0x0, 64 * 1024, 32, 0) }, + {"s25fl032a", INFO(0x010215, 0x0, 64 * 1024, 64, 0) }, + {"s25fl064a", INFO(0x010216, 0x0, 64 * 1024, 128, 0) }, + {"s25fl116k", INFO(0x014015, 0x0, 64 * 1024, 32, 0) }, + {"s25fl164k", INFO(0x014017, 0x0140, 64 * 1024, 128, 0) }, + {"s25fl128p_256k", INFO(0x012018, 0x0300, 256 * 1024, 64, RD_FULL | WR_QPP) }, + {"s25fl128p_64k", INFO(0x012018, 0x0301, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"s25fl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, RD_FULL | WR_QPP) }, + {"s25fl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, RD_FULL | WR_QPP) }, + {"s25fl128s_256k", INFO(0x012018, 0x4d00, 256 * 1024, 64, RD_FULL | WR_QPP) }, + {"s25fl128s_64k", INFO(0x012018, 0x4d01, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"s25fl256s_256k", INFO(0x010219, 0x4d00, 256 * 1024, 128, RD_FULL | WR_QPP) }, + {"s25fs256s_64k", INFO6(0x010219, 0x4d0181, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"s25fl256s_64k", INFO(0x010219, 0x4d01, 64 * 1024, 512, RD_FULL | WR_QPP) }, + {"s25fs512s", INFO6(0x010220, 0x4d0081, 128 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"s25fl512s_256k", INFO(0x010220, 0x4d00, 256 * 1024, 256, RD_FULL | WR_QPP) }, + {"s25fl512s_64k", INFO(0x010220, 0x4d01, 64 * 1024, 1024, RD_FULL | WR_QPP) }, + {"s25fl512s_512k", INFO(0x010220, 0x4f00, 256 * 1024, 256, RD_FULL | WR_QPP) }, +#endif +#ifdef CONFIG_SPI_NOR_STMICRO /* STMICRO */ + {"m25p10", INFO(0x202011, 0x0, 32 * 1024, 4, 0) }, + {"m25p20", INFO(0x202012, 0x0, 64 * 1024, 4, 0) }, + {"m25p40", INFO(0x202013, 0x0, 64 * 1024, 8, 0) }, + {"m25p80", INFO(0x202014, 0x0, 64 * 1024, 16, 0) }, + {"m25p16", INFO(0x202015, 0x0, 64 * 1024, 32, 0) }, + {"m25pE16", INFO(0x208015, 0x1000, 64 * 1024, 32, 0) }, + {"m25pX16", INFO(0x207115, 0x1000, 64 * 1024, 32, RD_QUAD | RD_DUAL) }, + {"m25p32", INFO(0x202016, 0x0, 64 * 1024, 64, 0) }, + {"m25p64", INFO(0x202017, 0x0, 64 * 1024, 128, 0) }, + {"m25p128", INFO(0x202018, 0x0, 256 * 1024, 64, 0) }, + {"m25pX64", INFO(0x207117, 0x0, 64 * 1024, 128, SECT_4K) }, + {"n25q016a", INFO(0x20bb15, 0x0, 64 * 1024, 32, SECT_4K) }, + {"n25q32", INFO(0x20ba16, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q32a", INFO(0x20bb16, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q64", INFO(0x20ba17, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q64a", INFO(0x20bb17, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q128", INFO(0x20ba18, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"n25q128a", INFO(0x20bb18, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"n25q256", INFO(0x20ba19, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q256a", INFO(0x20bb19, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q512", INFO(0x20ba20, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"n25q512a", INFO(0x20bb20, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"n25q1024", INFO(0x20ba21, 0x0, 64 * 1024, 2048, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"n25q1024a", INFO(0x20bb21, 0x0, 64 * 1024, 2048, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"mt25qu02g", INFO(0x20bb22, 0x0, 64 * 1024, 4096, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"mt25ql02g", INFO(0x20ba22, 0x0, 64 * 1024, 4096, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"mt35xu512g", INFO6(0x2c5b1a, 0x104100, 128 * 1024, 512, E_FSR | SECT_4K) }, +#endif +#ifdef CONFIG_SPI_NOR_SST /* SST */ + {"sst25vf040b", INFO(0xbf258d, 0x0, 64 * 1024, 8, SECT_4K | SST_WR) }, + {"sst25vf080b", INFO(0xbf258e, 0x0, 64 * 1024, 16, SECT_4K | SST_WR) }, + {"sst25vf016b", INFO(0xbf2541, 0x0, 64 * 1024, 32, SECT_4K | SST_WR) }, + {"sst25vf032b", INFO(0xbf254a, 0x0, 64 * 1024, 64, SECT_4K | SST_WR) }, + {"sst25vf064c", INFO(0xbf254b, 0x0, 64 * 1024, 128, SECT_4K) }, + {"sst25wf512", INFO(0xbf2501, 0x0, 64 * 1024, 1, SECT_4K | SST_WR) }, + {"sst25wf010", INFO(0xbf2502, 0x0, 64 * 1024, 2, SECT_4K | SST_WR) }, + {"sst25wf020", INFO(0xbf2503, 0x0, 64 * 1024, 4, SECT_4K | SST_WR) }, + {"sst25wf040", INFO(0xbf2504, 0x0, 64 * 1024, 8, SECT_4K | SST_WR) }, + {"sst25wf040b", INFO(0x621613, 0x0, 64 * 1024, 8, SECT_4K) }, + {"sst25wf080", INFO(0xbf2505, 0x0, 64 * 1024, 16, SECT_4K | SST_WR) }, +#endif +#ifdef CONFIG_SPI_NOR_WINBOND /* WINBOND */ + {"w25p80", INFO(0xef2014, 0x0, 64 * 1024, 16, 0) }, + {"w25p16", INFO(0xef2015, 0x0, 64 * 1024, 32, 0) }, + {"w25p32", INFO(0xef2016, 0x0, 64 * 1024, 64, 0) }, + {"w25x40", INFO(0xef3013, 0x0, 64 * 1024, 8, SECT_4K) }, + {"w25x16", INFO(0xef3015, 0x0, 64 * 1024, 32, SECT_4K) }, + {"w25x32", INFO(0xef3016, 0x0, 64 * 1024, 64, SECT_4K) }, + {"w25x64", INFO(0xef3017, 0x0, 64 * 1024, 128, SECT_4K) }, + {"w25q80bl", INFO(0xef4014, 0x0, 64 * 1024, 16, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q16cl", INFO(0xef4015, 0x0, 64 * 1024, 32, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q32bv", INFO(0xef4016, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q64cv", INFO(0xef4017, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q128bv", INFO(0xef4018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q256", INFO(0xef4019, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q80bw", INFO(0xef5014, 0x0, 64 * 1024, 16, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q16dw", INFO(0xef6015, 0x0, 64 * 1024, 32, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q32dw", INFO(0xef6016, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q64dw", INFO(0xef6017, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q128fw", INFO(0xef6018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP | SECT_4K) }, +#endif +#ifdef CONFIG_SPI_NOR_MISC + /* ATMEL */ + {"at45db011d", INFO(0x1f2200, 0x0, 64 * 1024, 4, SECT_4K) }, + {"at45db021d", INFO(0x1f2300, 0x0, 64 * 1024, 8, SECT_4K) }, + {"at45db041d", INFO(0x1f2400, 0x0, 64 * 1024, 8, SECT_4K) }, + {"at45db081d", INFO(0x1f2500, 0x0, 64 * 1024, 16, SECT_4K) }, + {"at45db161d", INFO(0x1f2600, 0x0, 64 * 1024, 32, SECT_4K) }, + {"at45db321d", INFO(0x1f2700, 0x0, 64 * 1024, 64, SECT_4K) }, + {"at45db641d", INFO(0x1f2800, 0x0, 64 * 1024, 128, SECT_4K) }, + {"at25df321a", INFO(0x1f4701, 0x0, 64 * 1024, 64, SECT_4K) }, + {"at25df321", INFO(0x1f4700, 0x0, 64 * 1024, 64, SECT_4K) }, + {"at26df081a", INFO(0x1f4501, 0x0, 64 * 1024, 16, SECT_4K) }, + + /* EON */ + {"en25q32b", INFO(0x1c3016, 0x0, 64 * 1024, 64, 0) }, + {"en25q64", INFO(0x1c3017, 0x0, 64 * 1024, 128, SECT_4K) }, + {"en25q128b", INFO(0x1c3018, 0x0, 64 * 1024, 256, 0) }, + {"en25s64", INFO(0x1c3817, 0x0, 64 * 1024, 128, 0) }, + + /* GIGADEVICE */ + {"gd25q64b", INFO(0xc84017, 0x0, 64 * 1024, 128, SECT_4K) }, + {"gd25lq32", INFO(0xc86016, 0x0, 64 * 1024, 64, SECT_4K) }, + + /* ISSI */ + {"is25lq040b", INFO(0x9d4013, 0x0, 64 * 1024, 8, 0) }, + {"is25lp032", INFO(0x9d6016, 0x0, 64 * 1024, 64, 0) }, + {"is25lp064", INFO(0x9d6017, 0x0, 64 * 1024, 128, 0) }, + {"is25lp128", INFO(0x9d6018, 0x0, 64 * 1024, 256, 0) }, +#endif + {}, /* Empty entry to terminate the list */ + /* + * Note: + * Below paired flash devices has similar spi_nor params. + * (s25fl129p_64k, s25fl128s_64k) + * (w25q80bl, w25q80bv) + * (w25q16cl, w25q16dv) + * (w25q32bv, w25q32fv_spi) + * (w25q64cv, w25q64fv_spi) + * (w25q128bv, w25q128fv_spi) + * (w25q32dw, w25q32fv_qpi) + * (w25q64dw, w25q64fv_qpi) + * (w25q128fw, w25q128fv_qpi) + */ +}; diff --git a/drivers/mtd/spi-nor/spi-nor-uclass.c b/drivers/mtd/spi-nor/spi-nor-uclass.c new file mode 100644 index 0000000..919682d --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor-uclass.c @@ -0,0 +1,143 @@ +/* + * SPI NOR Core framework. + * + * Copyright (C) 2016 Jagan Teki + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct spi_nor *spi_nor_get_spi_nor_dev(struct udevice *dev) +{ + struct spi_nor_uclass_priv *upriv; + + if (!device_active(dev)) + return NULL; + upriv = dev_get_uclass_priv(dev); + return upriv->spi_nor; +} + +struct spi_nor *find_spi_nor_device(int dev_num) +{ + struct udevice *dev, *spi_nor_dev; + int ret; + + ret = mtd_find_device(MTD_IF_TYPE_SPI_NOR, dev_num, &dev); + if (ret) { + printf("SPI-NOR Device %d not found\n", dev_num); + return NULL; + } + + spi_nor_dev = dev_get_parent(dev); + + struct spi_nor *nor = spi_nor_get_spi_nor_dev(spi_nor_dev); + + return nor; +} + +int get_spi_nor_num(void) +{ + return max((mtd_find_max_devnum(MTD_IF_TYPE_SPI_NOR) + 1), 0); +} + +struct mtd_info *spi_nor_get_mtd_info(struct spi_nor *nor) +{ + struct mtd_info *mtd; + struct udevice *dev; + + device_find_first_child(nor->dev, &dev); + if (!dev) + return NULL; + mtd = dev_get_uclass_platdata(dev); + + return mtd; +} + +void print_spi_nor_devices(char separator) +{ + struct udevice *dev; + bool first = true; + + for (uclass_first_device(UCLASS_SPI_NOR, &dev); + dev; + uclass_next_device(&dev), first = false) { + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + + if (!first) { + printf("%c", separator); + if (separator != '\n') + puts(" "); + } + + printf("%s: %d", dev->name, spi_nor_get_mtd_info(nor)->devnum); + } + + printf("\n"); +} + +int spi_nor_bind(struct udevice *dev, struct spi_nor *nor) +{ + struct udevice *mdev; + int ret; + + if (!spi_nor_get_ops(dev)) + return -ENOSYS; + + ret = mtd_create_devicef(dev, "spinor_mtd", "mtd", MTD_IF_TYPE_SPI_NOR, + &mdev); + if (ret) { + debug("Cannot create mtd device\n"); + return ret; + } + nor->dev = dev; + + return 0; +} + +static int spi_nor_mtd_probe(struct udevice *dev) +{ + struct udevice *spi_nor_dev = dev_get_parent(dev); + struct spi_nor_uclass_priv *upriv = dev_get_uclass_priv(spi_nor_dev); + struct spi_nor *nor = upriv->spi_nor; + int ret; + + ret = spi_nor_scan(nor); + if (ret) { + debug("%s: spi_nor_scan() failed (err=%d)\n", __func__, ret); + return ret; + } + + return 0; +} + +static const struct mtd_ops spi_nor_mtd_ops = { + .read = spi_nor_mread, + .erase = spi_nor_merase, +}; + +U_BOOT_DRIVER(spinor_mtd) = { + .name = "spinor_mtd", + .id = UCLASS_MTD, + .ops = &spi_nor_mtd_ops, + .probe = spi_nor_mtd_probe, +}; + +U_BOOT_DRIVER(spinor) = { + .name = "spinor", + .id = UCLASS_SPI_NOR, +}; + +UCLASS_DRIVER(spinor) = { + .id = UCLASS_SPI_NOR, + .name = "spinor", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .per_device_auto_alloc_size = sizeof(struct spi_nor_uclass_priv), +}; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c new file mode 100644 index 0000000..09fb8ca --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -0,0 +1,569 @@ +/* + * SPI NOR Core framework. + * + * Copyright (C) 2016 Jagan Teki + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* Set write enable latch with Write Enable command */ +static inline int write_enable(struct udevice *dev) +{ + return spi_nor_get_ops(dev)->write_reg(dev, SNOR_OP_WREN, NULL, 0); +} + +/* Re-set write enable latch with Write Disable command */ +static inline int write_disable(struct udevice *dev) +{ + return spi_nor_get_ops(dev)->write_reg(dev, SNOR_OP_WRDI, NULL, 0); +} + +static int read_sr(struct udevice *dev) +{ + u8 sr; + int ret; + + ret = spi_nor_get_ops(dev)->read_reg(dev, SNOR_OP_RDSR, &sr, 1); + if (ret < 0) { + debug("spi-nor: fail to read status register\n"); + return ret; + } + + return sr; +} + +static int read_fsr(struct udevice *dev) +{ + u8 fsr; + int ret; + + ret = spi_nor_get_ops(dev)->read_reg(dev, SNOR_OP_RDFSR, &fsr, 1); + if (ret < 0) { + debug("spi-nor: fail to read flag status register\n"); + return ret; + } + + return fsr; +} + +static int write_sr(struct udevice *dev, u8 ws) +{ + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + const struct spi_nor_ops *ops = spi_nor_get_ops(dev); + + nor->cmd_buf[0] = ws; + return ops->write_reg(dev, SNOR_OP_WRSR, nor->cmd_buf, 1); +} + +#if defined(CONFIG_SPI_NOR_SPANSION) || defined(CONFIG_SPI_NOR_WINBOND) +static int read_cr(struct udevice *dev) +{ + u8 cr; + int ret; + + ret = spi_nor_get_ops(dev)->read_reg(dev, SNOR_OP_RDCR, &cr, 1); + if (ret < 0) { + debug("spi-nor: fail to read config register\n"); + return ret; + } + + return cr; +} + +/* + * Write status Register and configuration register with 2 bytes + * - First byte will be written to the status register. + * - Second byte will be written to the configuration register. + * Return negative if error occured. + */ +static int write_sr_cr(struct udevice *dev, u16 val) +{ + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + const struct spi_nor_ops *ops = spi_nor_get_ops(dev); + + nor->cmd_buf[0] = val & 0xff; + nor->cmd_buf[1] = (val >> 8); + + return ops->write_reg(dev, SNOR_OP_WRSR, nor->cmd_buf, 2); +} +#endif + +static int spi_nor_sr_ready(struct udevice *dev) +{ + int sr = read_sr(dev); + if (sr < 0) + return sr; + else + return !(sr & SR_WIP); +} + +static int spi_nor_fsr_ready(struct udevice *dev) +{ + int fsr = read_fsr(dev); + if (fsr < 0) + return fsr; + else + return fsr & FSR_READY; +} + +static int spi_nor_ready(struct udevice *dev) +{ + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + int sr, fsr; + + sr = spi_nor_sr_ready(dev); + if (sr < 0) + return sr; + + fsr = 1; + if (nor->flags & SNOR_F_USE_FSR) { + fsr = spi_nor_fsr_ready(dev); + if (fsr < 0) + return fsr; + } + + return sr && fsr; +} + +static int spi_nor_wait_till_ready(struct udevice *dev, unsigned long timeout) +{ + int timebase, ret; + + timebase = get_timer(0); + + while (get_timer(timebase) < timeout) { + ret = spi_nor_ready(dev); + if (ret < 0) + return ret; + if (ret) + return 0; + } + + printf("spi-nor: Timeout!\n"); + + return -ETIMEDOUT; +} + +static const struct spi_nor_info *spi_nor_id(struct udevice *dev) +{ + int tmp; + u8 id[SPI_NOR_MAX_ID_LEN]; + const struct spi_nor_info *info; + const struct spi_nor_ops *ops = spi_nor_get_ops(dev); + + tmp = ops->read_reg(dev, SNOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); + if (tmp < 0) { + printf("spi-nor: error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); + } + + info = spi_nor_ids; + for (; info->name != NULL; info++) { + if (info->id_len) { + if (!memcmp(info->id, id, info->id_len)) + return info; + } + } + + printf("spi-nor: unrecognized JEDEC id bytes: %02x, %2x, %2x\n", + id[0], id[1], id[2]); + return ERR_PTR(-ENODEV); +} + +int spi_nor_merase(struct udevice *dev, struct erase_info *instr) +{ + struct mtd_info *mtd = dev_get_uclass_platdata(dev); + int devnum = mtd->devnum; + struct spi_nor *nor; + const struct spi_nor_ops *ops; + u32 addr, len, erase_addr; + uint32_t rem; + int ret = -1; + + nor = find_spi_nor_device(devnum); + if (!nor) + return -ENODEV; + ops = spi_nor_get_ops(nor->dev); + + div_u64_rem(instr->len, mtd->erasesize, &rem); + if (rem) + return -EINVAL; + + addr = instr->addr; + len = instr->len; + + while (len) { + erase_addr = addr; + + write_enable(nor->dev); + + ret = ops->write(nor->dev, erase_addr, 0, NULL); + if (ret < 0) + goto erase_err; + + ret = spi_nor_wait_till_ready(nor->dev, SNOR_READY_WAIT_ERASE); + if (ret < 0) + goto erase_err; + + addr += mtd->erasesize; + len -= mtd->erasesize; + } + + write_disable(nor->dev); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return ret; + +erase_err: + instr->state = MTD_ERASE_FAILED; + return ret; +} + +static int spi_nor_mwrite(struct udevice *dev, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mtd_info *mtd = dev_get_uclass_platdata(dev); + int devnum = mtd->devnum; + struct spi_nor *nor; + const struct spi_nor_ops *ops; + size_t addr, byte_addr; + size_t chunk_len, actual; + uint32_t page_size; + int ret = -1; + + nor = find_spi_nor_device(devnum); + if (!nor) + return -ENODEV; + ops = spi_nor_get_ops(nor->dev); + + page_size = mtd->writebufsize; + + for (actual = 0; actual < len; actual += chunk_len) { + addr = to; + + byte_addr = addr % page_size; + chunk_len = min(len - actual, (size_t)(page_size - byte_addr)); + + if (nor->max_write_size) + chunk_len = min(chunk_len, + (size_t)nor->max_write_size); + + write_enable(nor->dev); + + ret = ops->write(nor->dev, addr, chunk_len, buf + actual); + if (ret < 0) + break; + + ret = spi_nor_wait_till_ready(nor->dev, SNOR_READY_WAIT_PROG); + if (ret < 0) + return ret; + + to += chunk_len; + *retlen += chunk_len; + } + + return ret; +} + +int spi_nor_mread(struct udevice *dev, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct mtd_info *mtd = dev_get_uclass_platdata(dev); + int devnum = mtd->devnum; + struct spi_nor *nor; + const struct spi_nor_ops *ops; + int ret; + + nor = find_spi_nor_device(devnum); + if (!nor) + return -ENODEV; + ops = spi_nor_get_ops(nor->dev); + + /* Handle memory-mapped SPI */ + if (nor->memory_map) { + ret = ops->read(nor->dev, from, len, buf); + if (ret) { + debug("spi-nor: mmap read failed\n"); + return ret; + } + + return ret; + } + + ret = ops->read(nor->dev, from, len, buf); + if (ret < 0) { + printf("%s ret = %d\n", __func__, ret); + return ret; + } + + *retlen += len; + + return ret; +} + +#ifdef CONFIG_SPI_NOR_MACRONIX +static int macronix_quad_enable(struct udevice *dev) +{ + int ret, val; + + val = read_sr(dev); + if (val < 0) + return val; + + if (val & SR_QUAD_EN_MX) + return 0; + + write_enable(dev); + + ret = write_sr(dev, val | SR_QUAD_EN_MX); + if (ret < 0) + return ret; + + if (spi_nor_wait_till_ready(dev, SNOR_READY_WAIT_PROG)) + return 1; + + ret = read_sr(dev); + if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { + printf("spi-nor: Macronix Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} +#endif + +#if defined(CONFIG_SPI_NOR_SPANSION) || defined(CONFIG_SPI_NOR_WINBOND) +static int spansion_quad_enable(struct udevice *dev) +{ + int ret, val; + + val = read_cr(dev); + if (val < 0) + return val; + + if (val & CR_QUAD_EN_SPAN) + return 0; + + write_enable(dev); + + ret = write_sr_cr(dev, val | CR_QUAD_EN_SPAN); + if (ret < 0) + return ret; + + if (spi_nor_wait_till_ready(dev, SNOR_READY_WAIT_PROG)) + return 1; + + /* read back and check it */ + ret = read_cr(dev); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { + printf("spi-nor: Spansion Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} +#endif + +static int set_quad_mode(struct udevice *dev, const struct spi_nor_info *info) +{ + switch (JEDEC_MFR(info)) { +#ifdef CONFIG_SPI_NOR_MACRONIX + case SNOR_MFR_MACRONIX: + return macronix_quad_enable(dev); +#endif +#if defined(CONFIG_SPI_NOR_SPANSION) || defined(CONFIG_SPI_NOR_WINBOND) + case SNOR_MFR_SPANSION: + case SNOR_MFR_WINBOND: + return spansion_quad_enable(dev); +#endif +#ifdef CONFIG_SPI_NOR_STMICRO + case SNOR_MFR_MICRON: + return 0; +#endif + default: + printf("spi-nor: Need set QEB func for %02x flash\n", + JEDEC_MFR(info)); + return -1; + } +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) +int spi_nor_decode_fdt(const void *blob, struct spi_nor *nor) +{ + struct udevice *dev = nor->dev; + struct mtd_info *mtd = mtd_get_info(dev); + fdt_addr_t addr; + fdt_size_t size; + int node; + + /* If there is no node, do nothing */ + node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH); + if (node < 0) + return 0; + + addr = fdtdec_get_addr_size(blob, node, "memory-map", &size); + if (addr == FDT_ADDR_T_NONE) { + debug("%s: Cannot decode address\n", __func__); + return 0; + } + + if (mtd->size != size) { + debug("%s: Memory map must cover entire device\n", __func__); + return -1; + } + nor->memory_map = map_sysmem(addr, size); + + return 0; +} +#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */ + +int spi_nor_scan(struct spi_nor *nor) +{ + struct mtd_info *mtd = spi_nor_get_mtd_info(nor); + struct mtd_ops *ops = mtd_get_ops(mtd->dev); + const struct spi_nor_info *info = NULL; + int ret; + + struct spi_nor_uclass_priv *upriv = dev_get_uclass_priv(nor->dev); + upriv->spi_nor = nor; + + if (nor->init_done) + return 0; + + info = spi_nor_id(nor->dev); + if (IS_ERR_OR_NULL(info)) { + ret = -ENOENT; + goto err; + } + + /* + * Flash powers up read-only, so clear BP# bits. + * + * Note on some flash (like Macronix), QE (quad enable) bit is in the + * same status register as BP# bits, and we need preserve its original + * value during a reboot cycle as this is required by some platforms + * (like Intel ICH SPI controller working under descriptor mode). + */ + if (JEDEC_MFR(info) == SNOR_MFR_ATMEL || + (JEDEC_MFR(info) == SNOR_MFR_SST) || + (JEDEC_MFR(info) == SNOR_MFR_MACRONIX)) { + u8 sr = 0; + + if (JEDEC_MFR(info) == SNOR_MFR_MACRONIX) + sr = read_sr(nor->dev) & SR_QUAD_EN_MX; + write_sr(nor->dev, sr); + } + + mtd->name = info->name; + mtd->priv = nor; + mtd->type = MTD_NORFLASH; + mtd->writesize = 1; + mtd->flags = MTD_CAP_NORFLASH; + + if (info->flags & E_FSR) + nor->flags |= SNOR_F_USE_FSR; + + if (info->flags & SST_WR) + nor->flags |= SNOR_F_SST_WRITE; + + ops->write = spi_nor_mwrite; + + /* compute the flash size */ + nor->page_size = info->page_size; + /* + * The Spansion S25FL032P and S25FL064P have 256b pages, yet use the + * 0x4d00 Extended JEDEC code. The rest of the Spansion flashes with + * the 0x4d00 Extended JEDEC code have 512b pages. All of the others + * have 256b pages. + */ + if (JEDEC_EXT(info) == 0x4d00) { + if ((JEDEC_ID(info) != 0x0215) && + (JEDEC_ID(info) != 0x0216)) + nor->page_size = 512; + } + mtd->writebufsize = nor->page_size; + mtd->size = info->sector_size * info->n_sectors; + +#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + /* prefer "small sector" erase if possible */ + if (info->flags & SECT_4K) { + nor->erase_opcode = SNOR_OP_BE_4K; + mtd->erasesize = 4096; + } else +#endif + { + nor->erase_opcode = SNOR_OP_SE; + mtd->erasesize = info->sector_size; + } + + /* Look for read opcode */ + nor->read_opcode = SNOR_OP_READ_FAST; + if (nor->mode & SNOR_READ) + nor->read_opcode = SNOR_OP_READ; + else if (nor->mode & SNOR_READ_1_1_4 && info->flags & RD_QUAD) + nor->read_opcode = SNOR_OP_READ_1_1_4; + else if (nor->mode & SNOR_READ_1_1_2 && info->flags & RD_DUAL) + nor->read_opcode = SNOR_OP_READ_1_1_2; + + /* Look for program opcode */ + if (info->flags & WR_QPP && nor->mode & SNOR_WRITE_1_1_4) + nor->program_opcode = SNOR_OP_QPP; + else + /* Go for default supported write cmd */ + nor->program_opcode = SNOR_OP_PP; + + /* Set the quad enable bit - only for quad commands */ + if ((nor->read_opcode == SNOR_OP_READ_1_1_4) || + (nor->read_opcode == SNOR_OP_READ_1_1_4_IO) || + (nor->program_opcode == SNOR_OP_QPP)) { + ret = set_quad_mode(nor->dev, info); + if (ret) { + debug("spi-nor: quad mode not supported for %02x\n", + JEDEC_MFR(info)); + goto err; + } + } + + nor->addr_width = 3; + + /* Dummy cycles for read */ + switch (nor->read_opcode) { + case SNOR_OP_READ_1_1_4_IO: + nor->read_dummy = 16; + break; + case SNOR_OP_READ: + nor->read_dummy = 0; + break; + default: + nor->read_dummy = 8; + } + +#if CONFIG_IS_ENABLED(OF_CONTROL) + ret = spi_nor_decode_fdt(gd->fdt_blob, nor); + if (ret) { + debug("spi-nor: FDT decode error\n"); + goto err; + } +#endif + + nor->init_done = 1; + return 0; +err: + nor->init_done = 0; + return ret; +} diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 3fc2083..06541a4 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -75,6 +75,7 @@ enum uclass_id { UCLASS_SERIAL, /* Serial UART */ UCLASS_SPI, /* SPI bus */ UCLASS_SPMI, /* System Power Management Interface bus */ + UCLASS_SPI_NOR, /* SPI NOR flash */ UCLASS_SPI_FLASH, /* SPI flash */ UCLASS_SPI_GENERIC, /* Generic SPI flash target */ UCLASS_SYSCON, /* System configuration device */ diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h new file mode 100644 index 0000000..e1688e2 --- /dev/null +++ b/include/linux/mtd/spi-nor.h @@ -0,0 +1,217 @@ +/* + * SPI NOR Core header file. + * + * Copyright (C) 2016 Jagan Teki + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __MTD_SPI_NOR_H +#define __MTD_SPI_NOR_H + +#include +#include + +/* + * Manufacturer IDs + * + * The first byte returned from the flash after sending opcode SPINOR_OP_RDID. + * Sometimes these are the same as CFI IDs, but sometimes they aren't. + */ +#define SNOR_MFR_ATMEL 0x1f +#define SNOR_MFR_MACRONIX 0xc2 +#define SNOR_MFR_MICRON 0x20 /* ST Micro <--> Micron */ +#define SNOR_MFR_SPANSION 0x01 +#define SNOR_MFR_SST 0xbf +#define SNOR_MFR_WINBOND 0xef + +/** + * SPI NOR opcodes. + * + * Note on opcode nomenclature: some opcodes have a format like + * SNOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number + * of I/O lines used for the opcode, address, and data (respectively). The + * FUNCTION has an optional suffix of '4', to represent an opcode which + * requires a 4-byte (32-bit) address. + */ +#define SNOR_OP_WRDI 0x04 /* Write disable */ +#define SNOR_OP_WREN 0x06 /* Write enable */ +#define SNOR_OP_RDSR 0x05 /* Read status register */ +#define SNOR_OP_WRSR 0x01 /* Write status register 1 byte */ +#define SNOR_OP_READ 0x03 /* Read data bytes (low frequency) */ +#define SNOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ +#define SNOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */ +#define SNOR_OP_READ_1_1_2_IO 0xbb /* Read data bytes (Dual IO SPI) */ +#define SNOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */ +#define SNOR_OP_READ_1_1_4_IO 0xeb /* Read data bytes (Quad IO SPI) */ +#define SNOR_OP_BRWR 0x17 /* Bank register write */ +#define SNOR_OP_BRRD 0x16 /* Bank register read */ +#define SNOR_OP_WREAR 0xC5 /* Write extended address register */ +#define SNOR_OP_RDEAR 0xC8 /* Read extended address register */ +#define SNOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ +#define SNOR_OP_QPP 0x32 /* Quad Page program */ +#define SNOR_OP_BE_4K 0x20 /* Erase 4KiB block */ +#define SNOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ +#define SNOR_OP_BE_32K 0x52 /* Erase 32KiB block */ +#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */ +#define SNOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define SNOR_OP_RDID 0x9f /* Read JEDEC ID */ +#define SNOR_OP_RDCR 0x35 /* Read configuration register */ +#define SNOR_OP_RDFSR 0x70 /* Read flag status register */ + +/* Used for SST flashes only. */ +#define SNOR_OP_BP 0x02 /* Byte program */ +#define SNOR_OP_AAI_WP 0xad /* Auto addr increment word program */ + +/* Status Register bits. */ +#define SR_WIP BIT(0) /* Write in progress */ +#define SR_WEL BIT(1) /* Write enable latch */ + +/* meaning of other SR_* bits may differ between vendors */ +#define SR_BP0 BIT(2) /* Block protect 0 */ +#define SR_BP1 BIT(3) /* Block protect 1 */ +#define SR_BP2 BIT(4) /* Block protect 2 */ +#define SR_SRWD BIT(7) /* SR write protect */ + +#define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */ + +/* Flag Status Register bits */ +#define FSR_READY BIT(7) + +/* Configuration Register bits. */ +#define CR_QUAD_EN_SPAN BIT(1) /* Spansion/Winbond Quad I/O */ + +/* Flash timeout values */ +#define SNOR_READY_WAIT_PROG (2 * CONFIG_SYS_HZ) +#define SNOR_READY_WAIT_ERASE (5 * CONFIG_SYS_HZ) +#define SNOR_MAX_CMD_SIZE 4 +#define SNOR_16MB_BOUN 0x1000000 + +/** + * struct spi_nor_uclass_priv - Holds information about a device used by the uclass + */ +struct spi_nor_uclass_priv { + struct spi_nor *spi_nor; +}; + +enum snor_option_flags { + SNOR_F_SST_WRITE = BIT(0), + SNOR_F_USE_FSR = BIT(1), + SNOR_F_U_PAGE = BIT(1), +}; + +enum mode { + SNOR_READ = BIT(0), + SNOR_READ_1_1_2 = BIT(1), + SNOR_READ_1_1_4 = BIT(2), + SNOR_READ_1_1_2_IO = BIT(3), + SNOR_READ_1_1_4_IO = BIT(4), + SNOR_WRITE_1_1_BYTE = BIT(5), + SNOR_WRITE_1_1_4 = BIT(6), +}; + +#define JEDEC_MFR(info) ((info)->id[0]) +#define JEDEC_ID(info) (((info)->id[1]) << 8 | ((info)->id[2])) +#define JEDEC_EXT(info) (((info)->id[3]) << 8 | ((info)->id[4])) +#define SPI_NOR_MAX_ID_LEN 6 + +struct spi_nor_info { + char *name; + + /* + * This array stores the ID bytes. + * The first three bytes are the JEDIC ID. + * JEDEC ID zero means "no ID" (mostly older chips). + */ + u8 id[SPI_NOR_MAX_ID_LEN]; + u8 id_len; + + /* The size listed here is what works with SNOR_OP_SE, which isn't + * necessarily called a "sector" by the vendor. + */ + unsigned sector_size; + u16 n_sectors; + + u16 page_size; + + u16 flags; +#define SECT_4K BIT(0) +#define E_FSR BIT(1) +#define SST_WR BIT(2) +#define WR_QPP BIT(3) +#define RD_QUAD BIT(4) +#define RD_DUAL BIT(5) +#define RD_QUADIO BIT(6) +#define RD_DUALIO BIT(7) +#define RD_FULL (RD_QUAD | RD_DUAL | RD_QUADIO | RD_DUALIO) +}; + +extern const struct spi_nor_info spi_nor_ids[]; + +/** + * struct spi_nor - Structure for defining a the SPI NOR layer + * + * @dev: SPI NOR device + * @name: name of the SPI NOR device + * @page_size: the page size of the SPI NOR + * @addr_width: number of address bytes + * @erase_opcode: the opcode for erasing a sector + * @read_opcode: the read opcode + * @read_dummy: the dummy bytes needed by the read operation + * @program_opcode: the program opcode + * @max_write_size: If non-zero, the maximum number of bytes which can + * be written at once, excluding command bytes. + * @flags: flag options for the current SPI-NOR (SNOR_F_*) + * @mode: read, write mode or any other mode bits. + * @read_mode: read mode. + * @cmd_buf: used by the write_reg + * @read_reg: [DRIVER-SPECIFIC] read out the register + * @write_reg: [DRIVER-SPECIFIC] write data to the register + * @read: [DRIVER-SPECIFIC] read data from the SPI NOR + * @write: [DRIVER-SPECIFIC] write data to the SPI NOR + * @memory_map: address of read-only SPI NOR access + */ +struct spi_nor { + struct udevice *dev; + const char *name; + u8 init_done; + u32 page_size; + u8 addr_width; + u8 erase_opcode; + u8 read_opcode; + u8 read_dummy; + u8 program_opcode; + u32 max_write_size; + u32 flags; + u8 mode; + u8 read_mode; + u8 cmd_buf[SNOR_MAX_CMD_SIZE]; + + void *memory_map; +}; + +struct spi_nor_ops { + int (*read_reg)(struct udevice *dev, u8 cmd, u8 *val, int len); + int (*write_reg)(struct udevice *dev, u8 cmd, u8 *data, int len); + + int (*read)(struct udevice *dev, loff_t from, size_t len, + u_char *buf); + int (*write)(struct udevice *dev, loff_t to, size_t len, + const u_char *buf); +}; + +#define spi_nor_get_ops(dev) ((struct spi_nor_ops *)(dev)->driver->ops) + +int spi_nor_merase(struct udevice *dev, struct erase_info *instr); +int spi_nor_mread(struct udevice *dev, loff_t from, size_t len, + size_t *retlen, u_char *buf); + +int spi_nor_scan(struct spi_nor *nor); +int spi_nor_bind(struct udevice *dev, struct spi_nor *nor); + +struct spi_nor *find_spi_nor_device(int dev_num); +int get_spi_nor_num(void); +struct spi_nor *spi_nor_get_spi_nor_dev(struct udevice *dev); +struct mtd_info *spi_nor_get_mtd_info(struct spi_nor *nor); + +#endif /* __MTD_SPI_NOR_H */