From patchwork Sat Jan 17 05:59:23 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peng Fan X-Patchwork-Id: 430097 X-Patchwork-Delegate: sjg@chromium.org 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 EE4DF1401DA for ; Sat, 17 Jan 2015 17:00:03 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 90B434B622; Sat, 17 Jan 2015 06:59:59 +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 JRNWN6MQBP9h; Sat, 17 Jan 2015 06:59:59 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id CD8CA4B61A; Sat, 17 Jan 2015 06:59:58 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 102954B61A for ; Sat, 17 Jan 2015 06:59:54 +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 aBCeAvqkcR+L for ; Sat, 17 Jan 2015 06:59:53 +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 na01-by2-obe.outbound.protection.outlook.com (mail-by2on0132.outbound.protection.outlook.com [207.46.100.132]) by theia.denx.de (Postfix) with ESMTPS id 26B094B616 for ; Sat, 17 Jan 2015 06:59:51 +0100 (CET) Received: from BY2PR03CA051.namprd03.prod.outlook.com (10.141.249.24) by DM2PR0301MB0686.namprd03.prod.outlook.com (25.160.96.24) with Microsoft SMTP Server (TLS) id 15.1.59.20; Sat, 17 Jan 2015 05:59:45 +0000 Received: from BN1BFFO11FD052.protection.gbl (2a01:111:f400:7c10::1:115) by BY2PR03CA051.outlook.office365.com (2a01:111:e400:2c5d::24) with Microsoft SMTP Server (TLS) id 15.1.59.20 via Frontend Transport; Sat, 17 Jan 2015 05:59:45 +0000 Received: from tx30smr01.am.freescale.net (192.88.168.50) by BN1BFFO11FD052.mail.protection.outlook.com (10.58.145.7) with Microsoft SMTP Server (TLS) id 15.1.59.14 via Frontend Transport; Sat, 17 Jan 2015 05:59:44 +0000 Received: from linux-jyl1.am.freescale.net ([10.192.185.219]) by tx30smr01.am.freescale.net (8.14.3/8.14.0) with ESMTP id t0H5xTJO001597; Fri, 16 Jan 2015 22:59:42 -0700 From: Peng Fan To: , Date: Sat, 17 Jan 2015 13:59:23 +0800 Message-ID: <1421474363-30453-1-git-send-email-Peng.Fan@freescale.com> X-Mailer: git-send-email 1.8.4 X-EOPAttributedMessage: 0 Received-SPF: Fail (protection.outlook.com: domain of freescale.com does not designate 192.88.168.50 as permitted sender) receiver=protection.outlook.com; client-ip=192.88.168.50; helo=tx30smr01.am.freescale.net; Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=Peng.Fan@freescale.com; X-Forefront-Antispam-Report: CIP:192.88.168.50; CTRY:US; IPV:NLI; EFV:NLI; SFV:NSPM; SFS:(10019020)(6009001)(339900001)(199003)(189002)(87936001)(46102003)(104016003)(48376002)(68736005)(47776003)(64706001)(97736003)(50466002)(77096005)(50986999)(50226001)(92566002)(229853001)(6806004)(19580405001)(19580395003)(86362001)(575784001)(85426001)(105606002)(106466001)(62966003)(77156002)(36756003)(473944003); DIR:OUT; SFP:1102; SCL:1; SRVR:DM2PR0301MB0686; H:tx30smr01.am.freescale.net; FPR:; SPF:Fail; MLV:sfv; PTR:InfoDomainNonexistent; A:1; MX:1; LANG:en; MIME-Version: 1.0 X-DmarcAction-Test: None X-Microsoft-Antispam: UriScan:; X-Microsoft-Antispam: BCL:0;PCL:0;RULEID:(3005004);SRVR:DM2PR0301MB0686; X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004); SRVR:DM2PR0301MB0686; X-Forefront-PRVS: 04599F3534 X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:; SRVR:DM2PR0301MB0686; X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Jan 2015 05:59:44.7744 (UTC) X-MS-Exchange-CrossTenant-Id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=710a03f5-10f6-4d38-9ff4-a80b81da590d; Ip=[192.88.168.50] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM2PR0301MB0686 Cc: u-boot@lists.denx.de Subject: [U-Boot] [RFC PATCH] dm:spi:fsl_qspi add DM support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.13 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de Hi Simon ,Jagan This patch is based on git://git.denx.de/u-boot-spi.git master branch, since some fsl_qspi's new feature is still in this git repo and have not been merged to mainline. I saw Simon sent out a new patch that remove the per_child_auto_alloc_size from the platforms' driver code and move it to spi uclass driver. In this patch I do not remove it, since this is a RFC version, and Jagan's spi git repo still has it. I can remove it in formal version if needed. Please take your time to review and comment this patch. Thanks. This patch adds DM support for fsl_qspi driver, the DM part needs device tree support. Partial of the original driver code is reused, such as the AHB part, the LUT initialization and etc. The driver now supports new DM and original driver by define "CONFIG_DM_SPI". Until device tree is integrated, the original part can be discarded. The driver code file is now as following: " Common code that needs both by DM or original driver code. #if defined(CONFIG_DM_SPI) DM part #else Original driver code #endif " In DM part, some reconstruction is done. A fsl_qspi_runcmd is included to simplify the code, but not the original qspi_op_xxxxs. fsl_qspi_get_seqid is included to get seqid, but not hardcoded in original qspi_op_xxxxs. Signed-off-by: Peng Fan --- drivers/spi/fsl_qspi.c | 420 +++++++++++++++++++++++++++++++++++++++++++++++-- drivers/spi/fsl_qspi.h | 1 + 2 files changed, 405 insertions(+), 16 deletions(-) diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index 5e0b069..ee151b3 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -13,6 +13,13 @@ #include #include "fsl_qspi.h" +#ifdef CONFIG_DM_SPI +#include +#include +#include +#include +#endif + #define RX_BUFFER_SIZE 0x80 #ifdef CONFIG_MX6SX #define TX_BUFFER_SIZE 0x200 @@ -71,27 +78,41 @@ #define qspi_write32 out_be32 #endif -static unsigned long spi_bases[] = { - QSPI0_BASE_ADDR, -#ifdef CONFIG_MX6SX - QSPI1_BASE_ADDR, -#endif -}; +#ifdef CONFIG_DM_SPI +DECLARE_GLOBAL_DATA_PTR; +#define QUADSPI_AHBMAP_BANK_MAXSIZE SZ_64M -static unsigned long amba_bases[] = { - QSPI0_AMBA_BASE, -#ifdef CONFIG_MX6SX - QSPI1_AMBA_BASE, -#endif +struct fsl_qspi_platdata { + u32 max_hz; + u32 reg_base; + u32 amba_base; + u32 flash_num; }; struct fsl_qspi { + u32 reg_base; + u32 amba_base; + size_t cmd_len; + u8 cmd_buf[32]; + size_t data_len; + int qspi_is_init; + size_t flash_size; + u32 bank_memmap_phy[4]; + int cs; + u32 sf_addr; + int flash_num; + u8 cur_seqid; + u32 freq; +}; +#else +struct fsl_qspi { struct spi_slave slave; unsigned long reg_base; unsigned long amba_base; u32 sf_addr; u8 cur_seqid; }; +#endif /* QSPI support swapping the flash read/write data * in hardware for LS102xA, but not for VF610 */ @@ -104,11 +125,6 @@ static inline u32 qspi_endian_xchg(u32 data) #endif } -static inline struct fsl_qspi *to_qspi_spi(struct spi_slave *slave) -{ - return container_of(slave, struct fsl_qspi, slave); -} - static void qspi_set_lut(struct fsl_qspi *qspi) { struct fsl_qspi_regs *regs = (struct fsl_qspi_regs *)qspi->reg_base; @@ -367,6 +383,377 @@ static void qspi_init_ahb_read(struct fsl_qspi_regs *regs) } #endif +#ifdef CONFIG_DM_SPI +/* Get the SEQID for the command */ +static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) +{ + switch (cmd) { + case QSPI_CMD_FAST_READ: + case QSPI_CMD_FAST_READ_4B: + return SEQID_FAST_READ; + case QSPI_CMD_WREN: + return SEQID_WREN; + case QSPI_CMD_RDSR: + return SEQID_RDSR; + case QSPI_CMD_SE: + return SEQID_SE; + case QSPI_CMD_PP: + case QSPI_CMD_PP_4B: + return SEQID_PP; + case QSPI_CMD_RDID: + return SEQID_RDID; + case QSPI_CMD_BE_4K: + return SEQID_BE_4K; +#ifdef CONFIG_SPI_FLASH_BAR + case QSPI_CMD_BRRD: + return SEQID_BRRD; + case QSPI_CMD_BRWR: + return SEQID_BRWR; + case QSPI_CMD_RDEAR: + return SEQID_RDEAR; + case QSPI_CMD_WREAR: + return SEQID_WREAR; +#endif + default: + break; + } + return -1; +} + +/* Read out the data from the QUADSPI_RBDR buffer registers. */ +static void fsl_qspi_ip_read(struct fsl_qspi *q, int len, u8 *rxbuf) +{ + struct fsl_qspi_regs *regs = (struct fsl_qspi_regs *)q->reg_base; + u32 tmp; + int i = 0; + + while (len > 0) { + tmp = qspi_read32(®s->rbdr[i]); + tmp = qspi_endian_xchg(tmp); + + if (len >= 4) { + memcpy(rxbuf, &tmp, 4); + rxbuf += 4; + } else { + memcpy(rxbuf, &tmp, len); + break; + } + + len -= 4; + i++; + } +} + +/* Write data to the QUADSPI_TBDR buffer registers. */ +static void fsl_qspi_write_data(struct fsl_qspi *q, int len, u8 *txbuf) +{ + struct fsl_qspi_regs *regs = (struct fsl_qspi_regs *)q->reg_base; + u32 tmp; + u32 t1, t2; + int j; + + tmp = qspi_read32(®s->mcr); + qspi_write32(®s->mcr, tmp | QSPI_MCR_CLR_RXF_MASK | + QSPI_MCR_CLR_TXF_MASK); + + /* fill the TX data to the FIFO */ + t2 = len % 4; + t1 = len >> 2; /* 4 Bytes aligned */ + + for (j = 0; j < t1; j++) { + memcpy(&tmp, txbuf, 4); + tmp = qspi_endian_xchg(tmp); + qspi_write32(®s->tbdr, tmp); + txbuf += 4; + } + + if (t2) { + tmp = 0; + memcpy(&tmp, txbuf, t2); + tmp = qspi_endian_xchg(tmp); + qspi_write32(®s->tbdr, tmp); + } +} + +static int fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, int len) +{ + struct fsl_qspi_regs *regs = (struct fsl_qspi_regs *)q->reg_base; + int seqid; + u32 reg; + + /* Wait previous cmd completed */ + while (qspi_read32(®s->sr) & QSPI_SR_BUSY_MASK) + ; + + /* save the reg */ + reg = qspi_read32(®s->mcr); + + qspi_write32(®s->sfar, q->amba_base + q->sf_addr); + qspi_write32(®s->rbct, QSPI_RBCT_WMRK_MASK | QSPI_RBCT_RXBRD_USEIPS); + qspi_write32(®s->mcr, reg | QSPI_MCR_CLR_RXF_MASK); + + /* trigger the LUT now */ + seqid = fsl_qspi_get_seqid(q, cmd); + qspi_write32(®s->ipcr, (seqid << QSPI_IPCR_SEQID_SHIFT) | len); + + /* Wait until completed */ + while (qspi_read32(®s->sr) & QSPI_SR_BUSY_MASK) + ; + + /* restore the MCR */ + qspi_write32(®s->mcr, reg); + +#ifdef CONFIG_SYS_FSL_QSPI_AHB + /* After switch BANK, AHB buffer should also be invalid. */ + if ((QSPI_CMD_SE == cmd) || (QSPI_CMD_PP == cmd) || + (QSPI_CMD_BE_4K == cmd) || (QSPI_CMD_WREAR == cmd) || + (QSPI_CMD_BRWR == cmd)) + qspi_ahb_invalid(q); +#endif + return 0; +} + +int fsl_qspi_init(struct fsl_qspi *qspi) +{ + struct fsl_qspi_regs *regs; + u32 smpr_val; + + regs = (struct fsl_qspi_regs *)qspi->reg_base; + + qspi_write32(®s->mcr, QSPI_MCR_RESERVED_MASK | QSPI_MCR_MDIS_MASK); + + smpr_val = qspi_read32(®s->smpr); + qspi_write32(®s->smpr, smpr_val & ~(QSPI_SMPR_FSDLY_MASK | + QSPI_SMPR_FSPHS_MASK | QSPI_SMPR_HSENA_MASK)); + qspi_write32(®s->mcr, QSPI_MCR_RESERVED_MASK); + + /* flash size is still using macro definition. */ + if (qspi->flash_num == 2) { + qspi_write32(®s->sfa1ad, qspi->amba_base + + FSL_QSPI_FLASH_SIZE); + qspi_write32(®s->sfa2ad, qspi->amba_base + + FSL_QSPI_FLASH_SIZE); + qspi_write32(®s->sfb1ad, qspi->amba_base + + FSL_QSPI_FLASH_SIZE * 2); + qspi_write32(®s->sfb2ad, qspi->amba_base + + FSL_QSPI_FLASH_SIZE * 2); + } else if (qspi->flash_num == 4) { + qspi_write32(®s->sfa1ad, qspi->amba_base + + FSL_QSPI_FLASH_SIZE); + qspi_write32(®s->sfa2ad, qspi->amba_base + + FSL_QSPI_FLASH_SIZE * 2); + qspi_write32(®s->sfb1ad, qspi->amba_base + + FSL_QSPI_FLASH_SIZE * 3); + qspi_write32(®s->sfb2ad, qspi->amba_base + + FSL_QSPI_FLASH_SIZE * 4); + } else { + qspi_write32(®s->sfa1ad, qspi->amba_base + + FSL_QSPI_FLASH_SIZE); + qspi_write32(®s->sfa2ad, qspi->amba_base + + FSL_QSPI_FLASH_SIZE); + qspi_write32(®s->sfb1ad, qspi->amba_base + + FSL_QSPI_FLASH_SIZE); + qspi_write32(®s->sfb2ad, qspi->amba_base + + FSL_QSPI_FLASH_SIZE); + } + + qspi_set_lut(qspi); + + smpr_val = qspi_read32(®s->smpr); + smpr_val &= ~QSPI_SMPR_DDRSMP_MASK; + qspi_write32(®s->smpr, smpr_val); + qspi_write32(®s->mcr, QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); + +#ifdef CONFIG_SYS_FSL_QSPI_AHB + qspi_init_ahb_read(regs); +#endif + return 0; +} + +static int fsl_qspi_probe(struct udevice *bus) +{ + struct fsl_qspi_platdata *plat = bus->platdata; + struct fsl_qspi *priv = dev_get_priv(bus); + + priv->reg_base = plat->reg_base; + priv->amba_base = plat->amba_base; + priv->flash_num = plat->flash_num; + + if (!priv->qspi_is_init) { + /* Initialization */ + fsl_qspi_init(priv); + priv->qspi_is_init = 1; + } + + return 0; +} + +static int fsl_qspi_child_pre_probe(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct fsl_qspi *priv = dev_get_priv(bus); + struct spi_slave *slave = dev_get_parentdata(dev); + int cs = spi_chip_select(dev); + + slave->max_write_size = TX_BUFFER_SIZE; + + priv->cs = cs; + priv->amba_base = priv->bank_memmap_phy[priv->cs]; + + return 0; +} + +static int fsl_qspi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct fsl_qspi *q = dev_get_priv(bus); + u32 len = DIV_ROUND_UP(bitlen, 8); + int ret = 0; + u8 *buf; + static u32 addr; + static u8 opcode; + + if (!opcode && (flags & SPI_XFER_BEGIN)) { + /* CMD PHASE */ + buf = (u8 *)dout; + opcode = buf[0]; + if (len > 1) { + addr = buf[1] << 16 | buf[2] << 8 | buf[3]; + q->sf_addr = addr; + } + + /* Only transfer CMD */ + if (flags & SPI_XFER_END) + ret = fsl_qspi_runcmd(q, opcode, 0); + + } else if (opcode) { + /* Data phase */ + if (din) { + /* read*/ + buf = (u8 *)din; +#ifdef CONFIG_SYS_FSL_QSPI_AHB + if (QSPI_CMD_FAST_READ == opcode) { + qspi_ahb_read(q, buf, len); + } else { + ret = fsl_qspi_runcmd(q, opcode, len); + if (!ret) + fsl_qspi_ip_read(q, len, buf); + } +#else + ret = fsl_qspi_runcmd(q, opcode, len); + if (!ret) + fsl_qspi_ip_read(q, len, buf); +#endif + } else if (dout) { + /* Write data, First prepare data */ + buf = (u8 *)dout; + fsl_qspi_write_data(q, len, buf); + /* Run page program cmd */ + ret = fsl_qspi_runcmd(q, opcode, len); + } + } + + if (ret || (flags & SPI_XFER_END)) { + opcode = 0; + addr = 0; + } + + return ret; +} + +static int fsl_qspi_ofdata_to_platdata(struct udevice *bus) +{ + struct fsl_qspi_platdata *plat = bus->platdata; + const void *blob = gd->fdt_blob; + int node = bus->of_offset; + int subnode, flash_num = 0; + u32 data[4]; + int ret; + + ret = fdtdec_get_int_array(blob, node, "reg", data, ARRAY_SIZE(data)); + if (ret) { + printf("Error: can't get base addresses (ret = %d)!\n", ret); + return -ENODEV; + } + + /* Count flash numbers */ + fdt_for_each_subnode(blob, subnode, node) + ++flash_num; + + if (flash_num == 0) { + printf("Error: Missing flashes!\n"); + return -ENODEV; + } + + plat->reg_base = data[0]; + plat->amba_base = data[2]; + plat->flash_num = flash_num; + + plat->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", + 50000000); + + debug("%s: reg_base=0x%x amba_base=0x%x max-frequency=%d\n", + __func__, plat->reg_base, plat->amba_base, plat->max_hz); + + return 0; +} + +static int fsl_qspi_set_speed(struct udevice *bus, u32 speed) +{ + return 0; +} + +static int fsl_qspi_set_mode(struct udevice *bus, u32 mode) +{ + return 0; +} + +static const struct dm_spi_ops fsl_qspi_ops = { + .xfer = fsl_qspi_xfer, + .set_speed = fsl_qspi_set_speed, + .set_mode = fsl_qspi_set_mode, +}; + +static const struct udevice_id fsl_qspi_ids[] = { + { .compatible = "fsl,vf610-qspi" }, + { .compatible = "fsl,imx6sx-qspi" }, + { } +}; + +U_BOOT_DRIVER(fsl_qspi) = { + .name = "fsl_qspi", + .id = UCLASS_SPI, + .of_match = fsl_qspi_ids, + .ops = &fsl_qspi_ops, + .ofdata_to_platdata = fsl_qspi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct fsl_qspi_platdata), + .priv_auto_alloc_size = sizeof(struct fsl_qspi), + .per_child_auto_alloc_size = sizeof(struct spi_slave), + .probe = fsl_qspi_probe, + .child_pre_probe = fsl_qspi_child_pre_probe, +}; + +#else +static unsigned long spi_bases[] = { + QSPI0_BASE_ADDR, +#ifdef CONFIG_MX6SX + QSPI1_BASE_ADDR, +#endif +}; + +static unsigned long amba_bases[] = { + QSPI0_AMBA_BASE, +#ifdef CONFIG_MX6SX + QSPI1_AMBA_BASE, +#endif +}; + +static inline struct fsl_qspi *to_qspi_spi(struct spi_slave *slave) +{ + return container_of(slave, struct fsl_qspi, slave); +} + void spi_init() { /* do nothing */ @@ -782,3 +1169,4 @@ void spi_release_bus(struct spi_slave *slave) { /* Nothing to do */ } +#endif diff --git a/drivers/spi/fsl_qspi.h b/drivers/spi/fsl_qspi.h index 6cb3610..ef4d4b5 100644 --- a/drivers/spi/fsl_qspi.h +++ b/drivers/spi/fsl_qspi.h @@ -104,6 +104,7 @@ struct fsl_qspi_regs { #define QSPI_RBCT_RXBRD_SHIFT 8 #define QSPI_RBCT_RXBRD_USEIPS (1 << QSPI_RBCT_RXBRD_SHIFT) +#define QSPI_RBCT_WMRK_MASK 0x1F #define QSPI_SR_BUSY_SHIFT 0 #define QSPI_SR_BUSY_MASK (1 << QSPI_SR_BUSY_SHIFT)