From patchwork Fri May 14 06:20:57 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Haojian Zhuang X-Patchwork-Id: 52559 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [18.85.46.34]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id C2653B7E7D for ; Fri, 14 May 2010 16:30:46 +1000 (EST) Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.69 #1 (Red Hat Linux)) id 1OCoO2-0005PM-Lq; Fri, 14 May 2010 06:28:42 +0000 Received: from mail-px0-f177.google.com ([209.85.212.177]) by bombadil.infradead.org with esmtp (Exim 4.69 #1 (Red Hat Linux)) id 1OCoGa-0007Xr-Se; Fri, 14 May 2010 06:21:03 +0000 Received: by pxi1 with SMTP id 1so1196977pxi.36 for ; Thu, 13 May 2010 23:20:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:received:date:message-id :subject:from:to:content-type; bh=rkGmFSi9aHeWtbQhHHk4GfVXYT4Hc4jtMaETAy3kP8k=; b=iNLgGXjoMOzhw/kAqcqBG0I4B++x4uw4Add3RNT/7G40o1wcZIxb0ebBsNy4ebD/Cd EZQzmvxbuzA6GaGca2bCXRYEN/xFfrPIxn/VsNkRgAEReTgrFCRUCX8xZNU83jGf9aBM O+SgxJu8ULIFAPwpazjd9No6eHYNXlgfRy8bs= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:date:message-id:subject:from:to:content-type; b=JHHsm4hI0+DJp4nZMdb6JF/pCqTMwsw3RB4UuD/JeguWWPl9IBhdsZ1Jg4xMO0mm4w uVRi1EhCBut7Bik7BS2mpykjjBY0gIFCqYbkn+CCB5RicX7m5V2POVZAxxGo998Us5B8 UFpMJnsgxCBlx0kT5POUFHMhPRNFVrnK++rlc= MIME-Version: 1.0 Received: by 10.142.74.8 with SMTP id w8mr385642wfa.274.1273818057904; Thu, 13 May 2010 23:20:57 -0700 (PDT) Received: by 10.142.252.16 with HTTP; Thu, 13 May 2010 23:20:57 -0700 (PDT) Date: Fri, 14 May 2010 14:20:57 +0800 Message-ID: Subject: [PATCH 13/20] mtd: pxa3xx_nand: show the real time used by the controller From: Haojian Zhuang To: Marc Kleine-Budde , Eric Miao , David Woodhouse , David Woodhouse , linux-mtd@lists.infradead.org, linux-arm-kernel X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20100514_022102_344379_7C50A043 X-CRM114-Status: GOOD ( 26.86 ) X-Spam-Score: -0.1 (/) X-Spam-Report: SpamAssassin version 3.3.1 on bombadil.infradead.org summary: Content analysis details: (-0.1 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.212.177 listed in list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is freemail (haojian.zhuang[at]gmail.com) -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-mtd-bounces@lists.infradead.org Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org From 8764712bd7ef73efc556b8c5f6d6ea585e8727b3 Mon Sep 17 00:00:00 2001 From: Lei Wen Date: Mon, 29 Mar 2010 09:18:09 +0800 Subject: [PATCH] mtd: pxa3xx_nand: show the real time used by the controller The electronic signal timing is not that simple that we set to timing register. Sometimes it is difficult to find current timing is not what NAND chip spec want. So we add show real timing function to give intuitive check whether current timing meet the need. Signed-off-by: Lei Wen Signed-off-by: Haojian Zhuang --- arch/arm/plat-pxa/include/plat/pxa3xx_nand.h | 1 + drivers/mtd/nand/pxa3xx_nand.c | 239 ++++++++++++++++++++------ 2 files changed, 189 insertions(+), 51 deletions(-) static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info) @@ -1060,7 +1196,7 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) } static void pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, - const struct pxa3xx_nand_flash *f) + struct pxa3xx_nand_flash *f, int show_timing) { struct pxa3xx_nand *nand = info->nand_data; struct platform_device *pdev = nand->pdev; @@ -1125,7 +1261,7 @@ static void pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, info->reg_ndcr = ndcr; - pxa3xx_nand_set_timing(info, &f->timing); + pxa3xx_nand_set_timing(info, &f->timing, show_timing); } /* the maximum possible buffer size for large page with OOB data @@ -1227,7 +1363,7 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand *nand) struct pxa3xx_nand_info *info = nand->info[nand->chip_select]; struct mtd_info *mtd = get_mtd_by_info(info); /* use the common timing to make a try */ - pxa3xx_nand_config_flash(info, &builtin_flash_types[0]); + pxa3xx_nand_config_flash(info, &builtin_flash_types[0], 0); pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0); if (nand->state & STATE_READY) return 1; @@ -1270,7 +1406,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) /* find the chip in default list */ if (f->chip_id == id) { - pxa3xx_nand_config_flash(info, f); + pxa3xx_nand_config_flash(info, f, 1); chip->cellinfo = info->data_buff[2]; mtd->writesize = f->page_size; mtd->writesize_shift = ffs(mtd->writesize) - 1; @@ -1556,6 +1692,7 @@ static int __devinit pxa3xx_nand_probe(struct platform_device *pdev) return ret; nand = platform_get_drvdata(pdev); + nand->RD_CNT_DEL = pdata->RD_CNT_DEL; for (cs = 0; cs < NUM_CHIP_SELECT; cs++) { info = nand->info[cs]; mtd = get_mtd_by_info(info); diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h index 7c99390..c428897 100644 --- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h @@ -15,6 +15,7 @@ struct pxa3xx_nand_platform_data { /* Whether the controller support using naked command set */ int naked_cmd_support; + unsigned int RD_CNT_DEL; const struct mtd_partition *parts[NUM_CHIP_SELECT]; unsigned int nr_parts[NUM_CHIP_SELECT]; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 94702df..56559e1 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -90,6 +90,27 @@ #define NDCR_WRCMDREQM (0x1) #define NDCR_INT_MASK (0xFFF) +/* Data Controller Timing Paramter x Register For CSx */ +#define NDTR0_tADL(c) (min_t(uint32_t, (c), 31) << 27) +#define NDTR0_SELCNTR (0x1 << 26) +#define NDTR0_RD_CNT_DEL_MASK (0xF << 22) +#define NDTR0_RD_CNT_DEL(x) (((x) << 22) & NDTR0_RD_CNT_DEL_MASK) +#define NDTR0_tCH(c) (min_t(uint32_t, (c), 7) << 19) +#define NDTR0_tCS(c) (min_t(uint32_t, (c), 7) << 16) +#define NDTR0_tWH(c) (min_t(uint32_t, (c), 7) << 11) +#define NDTR0_tWP(c) (min_t(uint32_t, (c), 7) << 8) +#define NDTR0_sel_NRE_EDGE (0x1 << 7) +#define NDTR0_ETRP (0x1 << 6) +#define NDTR0_tRH(c) (min_t(uint32_t, (c), 7) << 3) +#define NDTR0_tRP(c) (min_t(uint32_t, (c), 7) << 0) + +#define NDTR1_tR(c) (min_t(uint32_t, (c), 65535) << 16) +#define NDTR1_WAIT_MODE (0x1 << 15) +#define NDTR1_PRESCALE (0x1 << 14) +#define NDTR1_tRHW(c) (min_t(uint32_t, (c), 3) << 8) +#define NDTR1_tWHR(c) (min_t(uint32_t, (c), 15) << 4) +#define NDTR1_tAR(c) (min_t(uint32_t, (c), 15) << 0) + #define NDSR_MASK (0xfff) #define NDSR_RDY (0x1 << 12) #define NDSR_FLASH_RDY (0x1 << 11) @@ -166,6 +187,7 @@ enum { }; struct pxa3xx_nand_timing { + uint32_t tADL; /* Adress to Write Data delay */ uint32_t tCH; /* Enable signal hold time */ uint32_t tCS; /* Enable signal setup time */ uint32_t tWH; /* ND_nWE high duration */ @@ -173,6 +195,7 @@ struct pxa3xx_nand_timing { uint32_t tRH; /* ND_nRE high duration */ uint32_t tRP; /* ND_nRE pulse width */ uint32_t tR; /* ND_nWE high to ND_nRE low for read */ + uint32_t tRHW; /* delay for next command issue */ uint32_t tWHR; /* ND_nWE high to ND_nRE low for status read */ uint32_t tAR; /* ND_ALE low to ND_nRE low delay */ }; @@ -254,6 +277,7 @@ struct pxa3xx_nand { uint8_t use_ecc; uint8_t use_dma; uint8_t wait_mode; + uint32_t RD_CNT_DEL; /* relate to the command */ uint32_t state; @@ -297,82 +321,194 @@ const static struct pxa3xx_nand_cmdset cmdset = { * detect the chip id before we know how to optimize further */ static struct pxa3xx_nand_flash __devinitdata builtin_flash_types[] = { -{ 0, 0, 0, 0, 0, 0, 0, { 40, 80, 60, 100, 80, 100, 90000, 400, 40, }, }, +{ 0, 0, 0, 0, 0, 0, 0, { 0, 40, 80, 60, 100, 80, 100, 90000, 0, 400, 40, }, }, { 0x46ec, 32, 512, 16, 16, ECC_HAMMIN, 4096, \ - { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, }, + { 0, 10, 0, 20, 40, 30, 40, 11123, 0, 110, 10, }, }, { 0xdaec, 64, 2048, 8, 8, ECC_HAMMIN, 2048, \ - { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, }, + { 0, 10, 0, 20, 40, 30, 40, 11123, 0, 110, 10, }, }, { 0xd3ec, 128, 2048, 8, 8, ECC_BCH, 4096, \ - { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, }, + { 0, 10, 0, 20, 40, 30, 40, 11123, 0, 110, 10, }, }, { 0xd7ec, 128, 4096, 8, 8, ECC_BCH, 8192, \ - { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, }, + { 0, 10, 0, 20, 40, 30, 40, 11123, 0, 110, 10, }, }, { 0xa12c, 64, 2048, 8, 8, ECC_HAMMIN, 1024, \ - { 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, }, + { 0, 10, 25, 15, 25, 15, 30, 25000, 0, 60, 10, }, }, { 0xb12c, 64, 2048, 16, 16, ECC_HAMMIN, 1024, \ - { 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, }, + { 0, 10, 25, 15, 25, 15, 30, 25000, 0, 60, 10, }, }, { 0xdc2c, 64, 2048, 8, 8, ECC_HAMMIN, 4096, \ - { 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, }, + { 0, 10, 25, 15, 25, 15, 30, 25000, 0, 60, 10, }, }, { 0xcc2c, 64, 2048, 16, 16, ECC_HAMMIN, 4096, \ - { 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, }, + { 0, 10, 25, 15, 25, 15, 30, 25000, 0, 60, 10, }, }, { 0x382c, 128, 4096, 8, 8, ECC_BCH, 2048, \ - { 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, }, + { 120, 8, 55, 15, 30, 30, 30, 25000, 60, 170, 15, }, }, { 0xba20, 64, 2048, 16, 16, ECC_HAMMIN, 2048, \ - { 10, 35, 15, 25, 15, 25, 25000, 60, 10, }, }, + { 0, 10, 35, 15, 25, 15, 25, 25000, 0, 60, 10, }, }, }; static const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL}; -#define NDTR0_tCH(c) (min((c), 7) << 19) -#define NDTR0_tCS(c) (min((c), 7) << 16) -#define NDTR0_tWH(c) (min((c), 7) << 11) -#define NDTR0_tWP(c) (min((c), 7) << 8) -#define NDTR0_tRH(c) (min((c), 7) << 3) -#define NDTR0_tRP(c) (min((c), 7) << 0) - -#define NDTR1_tR(c) (min((c), 65535) << 16) -#define NDTR1_tWHR(c) (min((c), 15) << 4) -#define NDTR1_tAR(c) (min((c), 15) << 0) +/* convert nano-seconds to nand flash controller clock cycles */ +#define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) + 1) -#define tCH_NDTR0(r) (((r) >> 19) & 0x7) -#define tCS_NDTR0(r) (((r) >> 16) & 0x7) -#define tWH_NDTR0(r) (((r) >> 11) & 0x7) -#define tWP_NDTR0(r) (((r) >> 8) & 0x7) -#define tRH_NDTR0(r) (((r) >> 3) & 0x7) -#define tRP_NDTR0(r) (((r) >> 0) & 0x7) +/* convert nand flash controller clock cycles to nano-seconds */ +#define cycle2ns(cycle, clk) (cycle * 1000 / (clk / 1000000)) -#define tR_NDTR1(r) (((r) >> 16) & 0xffff) -#define tWHR_NDTR1(r) (((r) >> 4) & 0xf) -#define tAR_NDTR1(r) (((r) >> 0) & 0xf) +/* + * This function shows the real timing when NAND controller + * send signal to the NAND chip. + */ +static void show_real_timing(uint32_t ndtr0, uint32_t ndtr1, unsigned long nand_clk) +{ + uint32_t rtADL, rtCH, rtCS, rtWH, rtWP, rtRH, rtRP; + uint32_t rtR, rtRHW, rtWHR, rtAR, tmp; + + rtCH = ((ndtr0 >> 19) & 0x7) + 1; + rtCS = ((ndtr0 >> 16) & 0x7) + 1; + rtWH = ((ndtr0 >> 11) & 0x7) + 1; + rtWP = ((ndtr0 >> 8) & 0x7) + 1; + rtADL= (ndtr0 >> 27) & 0x1f; + rtRH = ((ndtr0 >> 3) & 0x7) + 1; + rtRP = (ndtr0 & NDTR0_ETRP) ? ((0x8 | (ndtr0 & 0x7)) + 1) + : ((ndtr0 & 0x7) + 1); + rtRHW = (ndtr1 >> 8) & 0x3; + rtWHR = (ndtr1 >> 4) & 0xf; + rtAR = ndtr1 & 0xf; + rtR = (ndtr1 >> 16) & 0xffff; + + if (ndtr1 & NDTR1_PRESCALE) + rtR *= 16; + + rtR += rtCH + 2; + switch(rtRHW) { + case 0: + rtRHW = 0; + break; + case 1: + rtRHW = 16; + break; + case 2: + rtRHW = 32; + break; + case 3: + rtRHW = 48; + break; + } -/* convert nano-seconds to nand flash controller clock cycles */ -#define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) - 1) + /* + * TWHR delay=max(tAR, max(0, tWHR-max(tWH, tCH))) + * TAR delay=max(tAR, max(0, tWHR-max(tWH, tCH))) + 2 + */ + if (rtWH > rtCH) + tmp = rtWH - 1; + else + tmp = rtCH - 1; + if (rtADL != 0) { + rtADL = rtADL - 3 - rtWP; + rtADL = rtADL > 0 ? rtADL : 0; + rtADL = rtADL + tmp + rtWP + 8; + } + if (rtWHR < tmp) + rtWHR = rtAR; + else { + if (rtAR > (rtWHR - tmp)) + rtWHR = rtAR; + else + rtWHR = rtWHR - tmp; + } + rtAR = rtWHR + 2; + printk("Shows real timing(ns):\n"); + if (ndtr0 & NDTR0_SELCNTR) + printk("NDTR0 SELCNTR is set\n"); + else + printk("NDTR0 SELCNTR is not set\n"); + if (ndtr0 & NDTR0_RD_CNT_DEL_MASK) + printk("Read Strobe delay is %d\n", + (ndtr0 & NDTR0_RD_CNT_DEL_MASK) >> 22); + else + printk("No Read Stobe delay\n"); + if (ndtr0 & NDTR0_sel_NRE_EDGE) + printk("Controller is using falling edge to detect RE\n"); + else + printk("Controller is using rising edge to detect RE\n"); -/* convert nand flash controller clock cycles to nano-seconds */ -#define cycle2ns(c, clk) ((((c) + 1) * 1000000 + clk / 500) / (clk / 1000)) + if (ndtr1 & NDTR1_WAIT_MODE) + printk("NDTR1 wait mode is set\n"); + else + printk("NDTR1 wait mode is not set\n"); + + printk("TADL is %ld TCH is %ld TCS is %ld TWH is %ld TWP is %ld TRH is %ld " + "TRP is %ld TR is %ld TRHW is %ld TWHR is %ld TAR is %ld\n", + cycle2ns(rtADL, nand_clk), cycle2ns(rtCH, nand_clk), + cycle2ns(rtCS, nand_clk), cycle2ns(rtWH, nand_clk), + cycle2ns(rtWP, nand_clk), cycle2ns(rtRH, nand_clk), + cycle2ns(rtRP, nand_clk), cycle2ns(rtR, nand_clk), + cycle2ns(rtRHW, nand_clk), cycle2ns(rtWHR, nand_clk), + cycle2ns(rtAR, nand_clk)); +} static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, - const struct pxa3xx_nand_timing *t) + struct pxa3xx_nand_timing *t, int show_timing) { struct pxa3xx_nand *nand = info->nand_data; unsigned long nand_clk; - uint32_t ndtr0, ndtr1; + uint32_t ndtr0, ndtr1, tRP, tR, tRHW, tADL; nand_clk = clk_get_rate(nand->clk); - ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) | - NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) | - NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) | - NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) | - NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) | - NDTR0_tRP(ns2cycle(t->tRP, nand_clk)); + ndtr0 = ndtr1 = 0; + tRP = ns2cycle(t->tRP, nand_clk); + tRP = (tRP > 0xf) ? 0xf : tRP; + if (tRP > 0x7) { + ndtr0 |= NDTR0_ETRP; + tRP -= 0x7; + } + tR = ns2cycle(t->tR, nand_clk); + if (tR > 0xffff) { + ndtr1 |= NDTR1_PRESCALE; + tR /= 16; + } + if (t->tRHW > 0) { + tRHW = ns2cycle(t->tRHW, nand_clk); + if (tRHW < 16) + tRHW = 1; + else { + if (tRHW < 32) + tRHW = 2; + else + tRHW = 3; + } + } + else + tRHW = 0; + tADL = (t->tADL > 0) ? ns2cycle(t->tADL, nand_clk) : 0; + + if (nand->RD_CNT_DEL > 0) + ndtr0 |= NDTR0_SELCNTR + | (NDTR0_RD_CNT_DEL(nand->RD_CNT_DEL - 1)); + + ndtr0 |= NDTR0_tADL(tADL) + | NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) + | NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) + | NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) + | NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) + | NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) + | NDTR0_tRP(tRP) + | NDTR0_SELCNTR; + + if (nand->wait_mode) + ndtr1 |= NDTR1_WAIT_MODE; + + ndtr1 |= NDTR1_tR(tR) + | NDTR1_tRHW(tRHW) + | NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) + | NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); - ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) | - NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | - NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); - info->ndtr0cs0 = ndtr0; - info->ndtr1cs0 = ndtr1; nand_writel(nand, NDTR0CS0, ndtr0); nand_writel(nand, NDTR1CS0, ndtr1); + nand_writel(nand, NDREDEL, 0x0); + info->ndtr0cs0 = ndtr0; + info->ndtr1cs0 = ndtr1; + if (show_timing) + show_real_timing(ndtr0, ndtr1, nand_clk); }