From patchwork Fri Oct 12 07:01:06 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ryder Lee X-Patchwork-Id: 982884 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=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=mediatek.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 42WfJ84676z9s2P for ; Fri, 12 Oct 2018 18:15:52 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id 4E65AC21DB3; Fri, 12 Oct 2018 07:15:26 +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=1.3 required=5.0 tests=RCVD_IN_DNSWL_BLOCKED, RDNS_NONE, UNPARSEABLE_RELAY autolearn=no autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 7DCBCC21EDE; Fri, 12 Oct 2018 07:02:03 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 76498C21E16; Fri, 12 Oct 2018 07:01:37 +0000 (UTC) Received: from mailgw02.mediatek.com (unknown [210.61.82.184]) by lists.denx.de (Postfix) with ESMTPS id B4EDCC21E12 for ; Fri, 12 Oct 2018 07:01:32 +0000 (UTC) X-UUID: 44cbf0caac5146319070c5484bfcbbb7-20181012 X-UUID: 44cbf0caac5146319070c5484bfcbbb7-20181012 Received: from mtkcas09.mediatek.inc [(172.21.101.178)] by mailgw02.mediatek.com (envelope-from ) (mhqrelay.mediatek.com ESMTP with TLS) with ESMTP id 1730273193; Fri, 12 Oct 2018 15:01:22 +0800 Received: from mtkcas07.mediatek.inc (172.21.101.84) by mtkmbs03n2.mediatek.inc (172.21.101.182) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Fri, 12 Oct 2018 15:01:15 +0800 Received: from mtkslt306.mediatek.inc (10.21.14.136) by mtkcas07.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Fri, 12 Oct 2018 15:01:15 +0800 From: Ryder Lee To: Tom Rini , Simon Glass , Albert Aribaud Date: Fri, 12 Oct 2018 15:01:06 +0800 Message-ID: <9e2046940983cc9b16a86ad6cf7743c8e4e62811.1539326908.git.ryder.lee@mediatek.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: References: MIME-Version: 1.0 X-TM-SNTS-SMTP: 5284CE915E93D5E3F059457C5E925B5D7ACB00FE3C98EBCF80794945F44B82652000:8 X-MTK: N Cc: Steven Liu , Roy Luo , Sean Wang , u-boot@lists.denx.de, Jagan Teki , Guochun Mao Subject: [U-Boot] [PATCH v2 17/18] spi: mtk_qspi: add qspi driver for MT7629 SoC 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" From: Guochun Mao This patch adds MT7629 qspi driver for accessing SPI NOR flash. Cc: Jagan Teki Signed-off-by: Guochun Mao Reviewed-by: Simon Glass Reviewed-by: Jagan Teki --- change since v2: - Drop flash commands in the driver. --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/mtk_qspi.c | 359 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 367 insertions(+) create mode 100644 drivers/spi/mtk_qspi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 1df6876..f9cf4ba 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -124,6 +124,13 @@ config MT7621_SPI the SPI NOR flash on platforms embedding this Ralink / MediaTek SPI core, like MT7621/7628/7688. +config MTK_QSPI + bool "Mediatek QSPI driver" + help + Enable the Mediatek QSPI driver. This driver can be + used to access the SPI NOR flash on platforms embedding this + Mediatek QSPI IP core. + config MVEBU_A3700_SPI bool "Marvell Armada 3700 SPI driver" select CLK_ARMADA_3720 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 7242ea7..e5a78f5 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o obj-$(CONFIG_LPC32XX_SSP) += lpc32xx_ssp.o obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o +obj-$(CONFIG_MTK_QSPI) += mtk_qspi.o obj-$(CONFIG_MT7621_SPI) += mt7621_spi.o obj-$(CONFIG_MVEBU_A3700_SPI) += mvebu_a3700_spi.o obj-$(CONFIG_MXC_SPI) += mxc_spi.o diff --git a/drivers/spi/mtk_qspi.c b/drivers/spi/mtk_qspi.c new file mode 100644 index 0000000..b510733 --- /dev/null +++ b/drivers/spi/mtk_qspi.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 MediaTek, Inc. + * Author : Guochun.Mao@mediatek.com + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Register Offset */ +struct mtk_qspi_regs { + u32 cmd; + u32 cnt; + u32 rdsr; + u32 rdata; + u32 radr[3]; + u32 wdata; + u32 prgdata[6]; + u32 shreg[10]; + u32 cfg[2]; + u32 shreg10; + u32 mode_mon; + u32 status[4]; + u32 flash_time; + u32 flash_cfg; + u32 reserved_0[3]; + u32 sf_time; + u32 pp_dw_data; + u32 reserved_1; + u32 delsel_0[2]; + u32 intrstus; + u32 intren; + u32 reserved_2; + u32 cfg3; + u32 reserved_3; + u32 chksum; + u32 aaicmd; + u32 wrprot; + u32 radr3; + u32 dual; + u32 delsel_1[3]; +}; + +struct mtk_qspi_platdata { + fdt_addr_t reg_base; + fdt_addr_t mem_base; +}; + +struct mtk_qspi_priv { + struct mtk_qspi_regs *regs; + unsigned long *mem_base; + u8 op; + u8 tx[3]; /* only record max 3 bytes paras, when it's address. */ + u32 txlen; /* dout buffer length - op code length */ + u8 *rx; + u32 rxlen; +}; + +#define MTK_QSPI_CMD_POLLINGREG_US 500000 +#define MTK_QSPI_WRBUF_SIZE 256 +#define MTK_QSPI_COMMAND_ENABLE 0x30 + +/* NOR flash controller commands */ +#define MTK_QSPI_RD_TRIGGER BIT(0) +#define MTK_QSPI_READSTATUS BIT(1) +#define MTK_QSPI_PRG_CMD BIT(2) +#define MTK_QSPI_WR_TRIGGER BIT(4) +#define MTK_QSPI_WRITESTATUS BIT(5) +#define MTK_QSPI_AUTOINC BIT(7) + +#define MTK_QSPI_MAX_RX_TX_SHIFT 0x6 +#define MTK_QSPI_MAX_SHIFT 0x8 + +#define MTK_QSPI_WR_BUF_ENABLE 0x1 +#define MTK_QSPI_WR_BUF_DISABLE 0x0 + +static int mtk_qspi_execute_cmd(struct mtk_qspi_priv *priv, u8 cmd) +{ + u8 tmp; + u8 val = cmd & ~MTK_QSPI_AUTOINC; + + writeb(cmd, &priv->regs->cmd); + + return readb_poll_timeout(&priv->regs->cmd, tmp, !(val & tmp), + MTK_QSPI_CMD_POLLINGREG_US); +} + +static int mtk_qspi_tx_rx(struct mtk_qspi_priv *priv) +{ + int len = 1 + priv->txlen + priv->rxlen; + int i, ret, idx; + + if (len > MTK_QSPI_MAX_SHIFT) + return -ERR_INVAL; + + writeb(len * 8, &priv->regs->cnt); + + /* start at PRGDATA5, go down to PRGDATA0 */ + idx = MTK_QSPI_MAX_RX_TX_SHIFT - 1; + + /* opcode */ + writeb(priv->op, &priv->regs->prgdata[idx]); + idx--; + + /* program TX data */ + for (i = 0; i < priv->txlen; i++, idx--) + writeb(priv->tx[i], &priv->regs->prgdata[idx]); + + /* clear out rest of TX registers */ + while (idx >= 0) { + writeb(0, &priv->regs->prgdata[idx]); + idx--; + } + + ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_PRG_CMD); + if (ret) + return ret; + + /* restart at first RX byte */ + idx = priv->rxlen - 1; + + /* read out RX data */ + for (i = 0; i < priv->rxlen; i++, idx--) + priv->rx[i] = readb(&priv->regs->shreg[idx]); + + return 0; +} + +static int mtk_qspi_read(struct mtk_qspi_priv *priv, + u32 addr, u8 *buf, u32 len) +{ + memcpy(buf, (u8 *)priv->mem_base + addr, len); + return 0; +} + +static void mtk_qspi_set_addr(struct mtk_qspi_priv *priv, u32 addr) +{ + int i; + + for (i = 0; i < 3; i++) { + writeb(addr & 0xff, &priv->regs->radr[i]); + addr >>= 8; + } +} + +static int mtk_qspi_write_single_byte(struct mtk_qspi_priv *priv, + u32 addr, u32 length, const u8 *data) +{ + int i, ret; + + mtk_qspi_set_addr(priv, addr); + + for (i = 0; i < length; i++) { + writeb(*data++, &priv->regs->wdata); + ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER); + if (ret < 0) + return ret; + } + return 0; +} + +static int mtk_qspi_write_buffer(struct mtk_qspi_priv *priv, u32 addr, + const u8 *buf) +{ + int i, data; + + mtk_qspi_set_addr(priv, addr); + + for (i = 0; i < MTK_QSPI_WRBUF_SIZE; i += 4) { + data = buf[i + 3] << 24 | buf[i + 2] << 16 | + buf[i + 1] << 8 | buf[i]; + writel(data, &priv->regs->pp_dw_data); + } + + return mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER); +} + +static int mtk_qspi_write(struct mtk_qspi_priv *priv, + u32 addr, const u8 *buf, u32 len) +{ + int ret; + + /* setting pre-fetch buffer for page program */ + writel(MTK_QSPI_WR_BUF_ENABLE, &priv->regs->cfg[1]); + while (len >= MTK_QSPI_WRBUF_SIZE) { + ret = mtk_qspi_write_buffer(priv, addr, buf); + if (ret < 0) + return ret; + + len -= MTK_QSPI_WRBUF_SIZE; + addr += MTK_QSPI_WRBUF_SIZE; + buf += MTK_QSPI_WRBUF_SIZE; + } + /* disable pre-fetch buffer for page program */ + writel(MTK_QSPI_WR_BUF_DISABLE, &priv->regs->cfg[1]); + + if (len) + return mtk_qspi_write_single_byte(priv, addr, len, buf); + + return 0; +} + +static int mtk_qspi_claim_bus(struct udevice *dev) +{ + /* nothing to do */ + return 0; +} + +static int mtk_qspi_release_bus(struct udevice *dev) +{ + /* nothing to do */ + return 0; +} + +static int mtk_qspi_transfer(struct mtk_qspi_priv *priv, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + u32 bytes = DIV_ROUND_UP(bitlen, 8); + u32 addr; + + if (!bytes) + return -ERR_INVAL; + + if (dout) { + if (flags & SPI_XFER_BEGIN) { + /* parse op code and potential paras first */ + priv->op = *(u8 *)dout; + if (bytes > 1) + memcpy(priv->tx, (u8 *)dout + 1, + bytes <= 4 ? bytes - 1 : 3); + priv->txlen = bytes - 1; + } + + if (flags == SPI_XFER_ONCE) { + /* operations without receiving or sending data. + * for example: erase, write flash register or write + * enable... + */ + priv->rx = NULL; + priv->rxlen = 0; + return mtk_qspi_tx_rx(priv); + } + + if (flags & SPI_XFER_END) { + /* here, dout should be data to be written. + * and priv->tx should be filled 3Bytes address. + */ + addr = priv->tx[0] << 16 | priv->tx[1] << 8 | + priv->tx[2]; + return mtk_qspi_write(priv, addr, (u8 *)dout, bytes); + } + } + + if (din) { + if (priv->txlen >= 3) { + /* if run to here, priv->tx[] should be the address + * where read data from, + * and, din is the buf to receive data. + */ + addr = priv->tx[0] << 16 | priv->tx[1] << 8 | + priv->tx[2]; + return mtk_qspi_read(priv, addr, (u8 *)din, bytes); + } + + /* should be reading flash's register */ + priv->rx = (u8 *)din; + priv->rxlen = bytes; + return mtk_qspi_tx_rx(priv); + } + + return 0; +} + +static int mtk_qspi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct mtk_qspi_priv *priv = dev_get_priv(bus); + + return mtk_qspi_transfer(priv, bitlen, dout, din, flags); +} + +static int mtk_qspi_set_speed(struct udevice *bus, uint speed) +{ + /* nothing to do */ + return 0; +} + +static int mtk_qspi_set_mode(struct udevice *bus, uint mode) +{ + /* nothing to do */ + return 0; +} + +static int mtk_qspi_ofdata_to_platdata(struct udevice *bus) +{ + struct resource res_reg, res_mem; + struct mtk_qspi_platdata *plat = bus->platdata; + int ret; + + ret = dev_read_resource_byname(bus, "reg_base", &res_reg); + if (ret) { + debug("can't get reg_base resource(ret = %d)\n", ret); + return -ENOMEM; + } + + ret = dev_read_resource_byname(bus, "mem_base", &res_mem); + if (ret) { + debug("can't get map_base resource(ret = %d)\n", ret); + return -ENOMEM; + } + + plat->mem_base = res_mem.start; + plat->reg_base = res_reg.start; + + return 0; +} + +static int mtk_qspi_probe(struct udevice *bus) +{ + struct mtk_qspi_platdata *plat = dev_get_platdata(bus); + struct mtk_qspi_priv *priv = dev_get_priv(bus); + + priv->regs = (struct mtk_qspi_regs *)plat->reg_base; + priv->mem_base = (unsigned long *)plat->mem_base; + + writel(MTK_QSPI_COMMAND_ENABLE, &priv->regs->wrprot); + + return 0; +} + +static const struct dm_spi_ops mtk_qspi_ops = { + .claim_bus = mtk_qspi_claim_bus, + .release_bus = mtk_qspi_release_bus, + .xfer = mtk_qspi_xfer, + .set_speed = mtk_qspi_set_speed, + .set_mode = mtk_qspi_set_mode, +}; + +static const struct udevice_id mtk_qspi_ids[] = { + { .compatible = "mediatek,mt7629-qspi" }, + { } +}; + +U_BOOT_DRIVER(mtk_qspi) = { + .name = "mtk_qspi", + .id = UCLASS_SPI, + .of_match = mtk_qspi_ids, + .ops = &mtk_qspi_ops, + .ofdata_to_platdata = mtk_qspi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct mtk_qspi_platdata), + .priv_auto_alloc_size = sizeof(struct mtk_qspi_priv), + .probe = mtk_qspi_probe, +};