From patchwork Sun Oct 30 18:23:44 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jagan Teki X-Patchwork-Id: 689018 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 3t6Qtt1Vl2z9t17 for ; Mon, 31 Oct 2016 05:26:46 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 2197FA75F1; Sun, 30 Oct 2016 19:26:04 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id yfq-6zQ8qJyW; Sun, 30 Oct 2016 19:26:04 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 1C441B3818; Sun, 30 Oct 2016 19:25:39 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id E59D2A752D for ; Sun, 30 Oct 2016 19:25:28 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id EYfhiqallwBa for ; Sun, 30 Oct 2016 19:25:28 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-pf0-f196.google.com (mail-pf0-f196.google.com [209.85.192.196]) by theia.denx.de (Postfix) with ESMTPS id B0883A75C9 for ; Sun, 30 Oct 2016 19:25:24 +0100 (CET) Received: by mail-pf0-f196.google.com with SMTP id s8so6642286pfj.2 for ; Sun, 30 Oct 2016 11:25:24 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=2pWH8oTMNktKrgGQOkY6N6n/6lh0EF4iOCcx0c59uBs=; b=VBkxxLAn2zT2G4Oe+L0FEwrseexytsQZuB/cH8QR/0byTYUDLdjaQGG/7LP3VHRoad eB2+F7th5qsPUxArtlBNGM7XFcqOfaNCE+XixctDsplgMoyoR5n6l1YKoeqxh0Gjt7G7 Z1sAuwjYVdzFrehJM2Ha7nEIPVvFjFqdl6G/OqPPM/XTrh0X5/9d7GcrAaHSqADf+WX7 r6QFN933Q7wAYLuEKtVBQw7M1fm2+Lgt4mPl+DktD2vtphoAHMxHvGWQUt5v82CGoAb4 pd5i77Nespp2l/zZLWh/UZtsNrcnXALT64TwxfV4qnvTi1yWPCXpIjVcalf8JtA3XFb6 7WRw== X-Gm-Message-State: ABUngvdg3Va9QUKpT7t0/vHlUQQKYZcjorhTQgJGbdvSiDyr5gT26eI1/0OiyDXSdJf6Hw== X-Received: by 10.98.93.83 with SMTP id r80mr42470236pfb.17.1477851923213; Sun, 30 Oct 2016 11:25:23 -0700 (PDT) Received: from Mr.J ([49.204.230.134]) by smtp.gmail.com with ESMTPSA id k7sm31357901pan.8.2016.10.30.11.25.19 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 30 Oct 2016 11:25:22 -0700 (PDT) From: Jagan Teki To: u-boot@lists.denx.de Date: Sun, 30 Oct 2016 23:53:44 +0530 Message-Id: <1477851833-23960-13-git-send-email-jagan@openedev.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1477851833-23960-1-git-send-email-jagan@openedev.com> References: <1477851833-23960-1-git-send-email-jagan@openedev.com> Cc: Tom Rini , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v9 12/21] mtd: spi-nor: Add m25p80 driver X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 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" This is MTD SPI-NOR driver for ST M25Pxx (and similar) serial flash chips which is written as MTD_UCLASS. Signed-off-by: Jagan Teki --- drivers/mtd/spi-nor/Makefile | 3 + drivers/mtd/spi-nor/m25p80.c | 217 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 drivers/mtd/spi-nor/m25p80.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 15e43ea..d11ccf4 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -7,3 +7,6 @@ ifdef CONFIG_MTD_SPI_NOR obj-y += spi-nor.o spi-nor-ids.o endif + +## spi-nor to spi interface driver +obj-$(CONFIG_MTD_M25P80) += m25p80.o diff --git a/drivers/mtd/spi-nor/m25p80.c b/drivers/mtd/spi-nor/m25p80.c new file mode 100644 index 0000000..740d3f6 --- /dev/null +++ b/drivers/mtd/spi-nor/m25p80.c @@ -0,0 +1,217 @@ +/* + * MTD SPI-NOR driver for ST M25Pxx (and similar) serial flash chips + * + * Copyright (C) 2016 Jagan Teki + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define MAX_CMD_SIZE 6 +struct m25p { + struct spi_slave *spi; + struct spi_nor spi_nor; + u8 command[MAX_CMD_SIZE]; +}; + +static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd) +{ + /* opcode is in cmd[0] */ + cmd[1] = addr >> (nor->addr_width * 8 - 8); + cmd[2] = addr >> (nor->addr_width * 8 - 16); + cmd[3] = addr >> (nor->addr_width * 8 - 24); +} + +static int m25p_cmdsz(struct spi_nor *nor) +{ + return 1 + nor->addr_width; +} + +static int m25p80_read_reg(struct spi_nor *nor, u8 opcode, u8 *val, int len) +{ + struct m25p *flash = nor->priv; + struct spi_slave *spi = flash->spi; + int ret; + + ret = spi_write_then_read(spi, &opcode, 1, NULL, val, len); + if (ret < 0) { + debug("m25p80: error %d reading register %x\n", ret, opcode); + return ret; + } + + return ret; +} + +static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +{ + struct m25p *flash = nor->priv; + struct spi_slave *spi = flash->spi; + int ret; + + ret = spi_write_then_read(spi, &opcode, 1, buf, NULL, len); + if (ret < 0) { + debug("m25p80: error %d writing register %x\n", ret, opcode); + return ret; + } + + return ret; +} + +/* + * TODO: remove the weak after all the other spi_flash_copy_mmap + * implementations removed from drivers + */ +void __weak flash_copy_mmap(void *data, void *offset, size_t len) +{ +#ifdef CONFIG_DMA + if (!dma_memcpy(data, offset, len)) + return; +#endif + memcpy(data, offset, len); +} + +static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, + u_char *buf) +{ + struct m25p *flash = nor->priv; + struct spi_slave *spi = flash->spi; + unsigned int dummy = nor->read_dummy; + int ret; + + if (nor->memory_map) { + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP); + flash_copy_mmap(buf, nor->memory_map + from, len); + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP_END); + spi_release_bus(spi); + return 0; + } + + /* convert the dummy cycles to the number of bytes */ + dummy /= 8; + + flash->command[0] = nor->read_opcode; + m25p_addr2cmd(nor, from, flash->command); + + ret = spi_write_then_read(spi, flash->command, m25p_cmdsz(nor) + dummy, + NULL, buf, len); + if (ret < 0) { + debug("m25p80: error %d reading %x\n", ret, flash->command[0]); + return ret; + } + + return ret; +} + +static int m25p80_write(struct spi_nor *nor, loff_t to, size_t len, + const u_char *buf) +{ + struct m25p *flash = nor->priv; + struct spi_slave *spi = flash->spi; + int cmd_sz = m25p_cmdsz(nor); + int ret; + + if ((nor->program_opcode == SNOR_OP_AAI_WP) && (buf != NULL)) + cmd_sz = 1; + + flash->command[0] = nor->program_opcode; + if (buf == NULL) + flash->command[0] = nor->erase_opcode; + m25p_addr2cmd(nor, to, flash->command); + + ret = spi_write_then_read(spi, flash->command, cmd_sz, buf, NULL, len); + if (ret < 0) { + debug("m25p80: error %d writing %x\n", ret, flash->command[0]); + return ret; + } + + return ret; +} + +static int m25p_probe(struct udevice *dev) +{ + struct mtd_info *mtd = mtd_get_info(dev); + struct spi_slave *spi = dev_get_parent_priv(dev); + struct m25p *flash = dev_get_priv(dev); + struct spi_nor *nor; + int ret; + + nor = &flash->spi_nor; + + flash->spi = spi; + nor->priv = flash; + mtd->priv = nor; + nor->dev = dev; + + /* install hooks */ + nor->read = m25p80_read; + nor->write = m25p80_write; + nor->read_reg = m25p80_read_reg; + nor->write_reg = m25p80_write_reg; + + /* claim spi bus */ + ret = spi_claim_bus(spi); + if (ret) { + debug("m25p80: failed to claim SPI bus: %d\n", ret); + return ret; + } + + if (spi->mode & SPI_RX_SLOW) + nor->mode = SNOR_READ; + else if (spi->mode & SPI_RX_DUAL) + nor->mode = SNOR_READ_1_1_2; + else if (spi->mode & SPI_RX_QUAD) + nor->mode = SNOR_READ_1_1_4; + + if (spi->mode & SPI_TX_BYTE) + nor->mode |= SNOR_WRITE_1_1_BYTE; + else if (spi->mode & SPI_TX_QUAD) + nor->mode |= SNOR_WRITE_1_1_4; + + nor->memory_map = spi->memory_map; + nor->max_write_size = spi->max_write_size; + + ret = spi_nor_scan(dev); + if (ret) + goto err_scan; + + ret = dm_add_mtd_device(dev); + if (ret) + goto err_mtd; + + return 0; + +err_mtd: + device_remove(dev); + spi_free_slave(spi); +err_scan: + spi_release_bus(spi); + return ret; +} + +static const struct udevice_id m25p_ids[] = { + /* + * Generic compatibility for SPI NOR that can be identified by the + * JEDEC READ ID opcode (0x9F). Use this, if possible. + */ + { .compatible = "jedec,spi-nor" }, + { } +}; + +U_BOOT_DRIVER(m25p80) = { + .name = "m25p80", + .id = UCLASS_MTD, + .of_match = m25p_ids, + .probe = m25p_probe, + .priv_auto_alloc_size = sizeof(struct m25p), +};