From patchwork Tue Mar 25 08:19:31 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lee Jones X-Patchwork-Id: 27006 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ob0-f197.google.com (mail-ob0-f197.google.com [209.85.214.197]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 511B820062 for ; Tue, 25 Mar 2014 08:44:50 +0000 (UTC) Received: by mail-ob0-f197.google.com with SMTP id wp18sf592811obc.0 for ; Tue, 25 Mar 2014 01:44:49 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:from:to:subject:date:message-id :in-reply-to:references:cc:precedence:list-id:list-unsubscribe :list-archive:list-post:list-help:list-subscribe:mime-version:sender :errors-to:x-original-sender:x-original-authentication-results :mailing-list:content-type:content-transfer-encoding; bh=twFyq0ESJ0aiZkgqbG5a6i1HwxNuQkYnY3+sFRIeZUs=; b=YGeLVWfuInUmXnpbjf1Kwy+tO6KqsIq2XP5/xs+Jd2kU5F/N/KIewwIyHGQmV1r8t4 Awe+Dco3ybswmj2LlKh/fT2wULRXrygJ+LG6UFCHA9QaFuj4QBwV+3g8wgGwtw3gzfN8 G0fvrambVPJDlJRft+8m1hodD12qr5klBD4kUtjuUYYRYAfn1omQcXzL7aJopQJG/36G LqdcP+xg6sm5xrdVNdNjwv5eGKlwgPbNPAIbk3vFTvH+FCSjL02k3xRP5LUimR1xbNLu UyDFAr42TDwIAdPq+M1gw2Rp0iCFQwIYIErr80Hgf6jiM9KkqgpWA8l2Il2Kp3YC8mAB 2DwQ== X-Gm-Message-State: ALoCoQm7vfatxbUEuvUIHkjjkgF9I0dxcw7cdScw6vm0bzUZ8zPfEp7mMzuUGizEzigFnXWzB7Xx X-Received: by 10.182.16.199 with SMTP id i7mr14353111obd.42.1395737089892; Tue, 25 Mar 2014 01:44:49 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.85.203 with SMTP id n69ls137531qgd.96.gmail; Tue, 25 Mar 2014 01:44:49 -0700 (PDT) X-Received: by 10.220.161.8 with SMTP id p8mr54356829vcx.4.1395737089769; Tue, 25 Mar 2014 01:44:49 -0700 (PDT) Received: from mail-vc0-f182.google.com (mail-vc0-f182.google.com [209.85.220.182]) by mx.google.com with ESMTPS id xo2si3585302vec.170.2014.03.25.01.44.49 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 25 Mar 2014 01:44:49 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.220.182 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=209.85.220.182; Received: by mail-vc0-f182.google.com with SMTP id ks9so159793vcb.13 for ; Tue, 25 Mar 2014 01:44:49 -0700 (PDT) X-Received: by 10.220.12.66 with SMTP id w2mr9142701vcw.15.1395737089676; Tue, 25 Mar 2014 01:44:49 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.220.78.9 with SMTP id i9csp286593vck; Tue, 25 Mar 2014 01:44:48 -0700 (PDT) X-Received: by 10.180.12.233 with SMTP id b9mr21311285wic.8.1395737088216; Tue, 25 Mar 2014 01:44:48 -0700 (PDT) Received: from casper.infradead.org (casper.infradead.org. [2001:770:15f::2]) by mx.google.com with ESMTPS id hh7si12291415wib.65.2014.03.25.01.44.47 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 25 Mar 2014 01:44:48 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-mtd-bounces+patch=linaro.org@lists.infradead.org designates 2001:770:15f::2 as permitted sender) client-ip=2001:770:15f::2; Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WSMxK-0002b0-NK; Tue, 25 Mar 2014 08:43:35 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WSMwq-0003lL-BA; Tue, 25 Mar 2014 08:43:04 +0000 Received: from bombadil.infradead.org ([2001:1868:205::9]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WSMsN-0003B8-KL for linux-mtd@merlin.infradead.org; Tue, 25 Mar 2014 08:38:27 +0000 Received: from mail-we0-f181.google.com ([74.125.82.181]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WSMba-00025T-Rk for linux-mtd@lists.infradead.org; Tue, 25 Mar 2014 08:21:07 +0000 Received: by mail-we0-f181.google.com with SMTP id q58so80096wes.12 for ; Tue, 25 Mar 2014 01:20:44 -0700 (PDT) X-Received: by 10.181.11.169 with SMTP id ej9mr21559954wid.18.1395735644541; Tue, 25 Mar 2014 01:20:44 -0700 (PDT) Received: from lee--X1.home (host109-148-113-193.range109-148.btcentralplus.com. [109.148.113.193]) by mx.google.com with ESMTPSA id k4sm5567676wib.19.2014.03.25.01.20.42 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 25 Mar 2014 01:20:43 -0700 (PDT) From: Lee Jones To: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [RFC 14/47] mtd: nand: stm_nand_bch: configure BCH and FLEX by ONFI timing mode Date: Tue, 25 Mar 2014 08:19:31 +0000 Message-Id: <1395735604-26706-15-git-send-email-lee.jones@linaro.org> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1395735604-26706-1-git-send-email-lee.jones@linaro.org> References: <1395735604-26706-1-git-send-email-lee.jones@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140325_012107_223661_5E2647DF X-CRM114-Status: GOOD ( 18.53 ) X-Spam-Score: -0.7 (/) X-Spam-Report: SpamAssassin version 3.3.2 on bombadil.infradead.org summary: Content analysis details: (-0.7 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [74.125.82.181 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record Cc: angus.clark@st.com, kernel@stlinux.com, lee.jones@linaro.org, linux-mtd@lists.infradead.org, pekon@ti.com, computersforpeace@gmail.com, dwmw2@infradead.org X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: , List-Help: , List-Subscribe: , MIME-Version: 1.0 Sender: "linux-mtd" Errors-To: linux-mtd-bounces+patch=linaro.org@lists.infradead.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: lee.jones@linaro.org X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.220.182 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 This patch adds support for configuring the NAND Controller timing registers according to 'nand_timing_spec' data. Since the stm_nand_bch driver falls back to Hamming FLEX mode for certain operations, it is necessary to configure the timing registers of both the Hamming Controller and the BCH Controller. The device initialisation is now performed in two stages, nand_scan_ident() and nand_scan_tail(), rather than a single call to nand_scan(). This allows information obtained during device discovery (e.g. ONFI parameter data) to help optimise the timing configuration prior to performing some of the more data intensive tasks found in nand_scan_tail() (e.g. bad block scanning). If 'nand_timing_spec' is supplied via platform data, then this is used in preference for configuring the timing registers. Failing that, we test for the timing mode advertised by ONFI-compliant NAND, and select one of the predefined timing specifications. Finally, if no timing data is available, the timing registers are left unmodified (i.e. reset values, or as programmed by the boot-loader). Signed-off-by: Lee Jones --- drivers/mtd/nand/stm_nand_bch.c | 311 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) diff --git a/drivers/mtd/nand/stm_nand_bch.c b/drivers/mtd/nand/stm_nand_bch.c index 3115fb4..fbcd1b9 100644 --- a/drivers/mtd/nand/stm_nand_bch.c +++ b/drivers/mtd/nand/stm_nand_bch.c @@ -172,6 +172,10 @@ static void nandi_set_mtd_defaults(struct nandi_controller *nandi, mtd->_resume = nand_resume; } +/* + * Timing and Clocks + */ + static void nandi_clk_enable(struct nandi_controller *nandi) { if (nandi->emi_clk) @@ -210,6 +214,291 @@ static struct clk *nandi_clk_setup(struct nandi_controller *nandi, return clk; } +/* Derive Hamming-FLEX timing register values from 'nand_timing_spec' data */ +static void flex_calc_timing_registers(struct nand_timing_spec *spec, + int tCLK, int relax, + uint32_t *ctl_timing, + uint32_t *wen_timing, + uint32_t *ren_timing) +{ + int tMAX_HOLD; + int n_ctl_setup; + int n_ctl_hold; + int n_ctl_wb; + + int tMAX_WEN_OFF; + int n_wen_on; + int n_wen_off; + + int tMAX_REN_OFF; + int n_ren_on; + int n_ren_off; + + /* + * CTL_TIMING + */ + + /* - SETUP */ + n_ctl_setup = (spec->tCLS - spec->tWP + tCLK - 1)/tCLK; + if (n_ctl_setup < 1) + n_ctl_setup = 1; + n_ctl_setup += relax; + + /* - HOLD */ + tMAX_HOLD = spec->tCLH; + if (spec->tCH > tMAX_HOLD) + tMAX_HOLD = spec->tCH; + if (spec->tALH > tMAX_HOLD) + tMAX_HOLD = spec->tALH; + if (spec->tDH > tMAX_HOLD) + tMAX_HOLD = spec->tDH; + n_ctl_hold = (tMAX_HOLD + tCLK - 1)/tCLK + relax; + + /* - CE_deassert_hold = 0 */ + + /* - WE_high_to_RBn_low */ + n_ctl_wb = (spec->tWB + tCLK - 1)/tCLK; + + *ctl_timing = ((n_ctl_setup & 0xff) | + (n_ctl_hold & 0xff) << 8 | + (n_ctl_wb & 0xff) << 24); + + /* + * WEN_TIMING + */ + + /* - ON */ + n_wen_on = (spec->tWH + tCLK - 1)/tCLK + relax; + + /* - OFF */ + tMAX_WEN_OFF = spec->tWC - spec->tWH; + if (spec->tWP > tMAX_WEN_OFF) + tMAX_WEN_OFF = spec->tWP; + n_wen_off = (tMAX_WEN_OFF + tCLK - 1)/tCLK + relax; + + *wen_timing = ((n_wen_on & 0xff) | + (n_wen_off & 0xff) << 8); + + /* + * REN_TIMING + */ + + /* - ON */ + n_ren_on = (spec->tREH + tCLK - 1)/tCLK + relax; + + /* - OFF */ + tMAX_REN_OFF = spec->tRC - spec->tREH; + if (spec->tRP > tMAX_REN_OFF) + tMAX_REN_OFF = spec->tRP; + if (spec->tREA > tMAX_REN_OFF) + tMAX_REN_OFF = spec->tREA; + n_ren_off = (tMAX_REN_OFF + tCLK - 1)/tCLK + 1 + relax; + + *ren_timing = ((n_ren_on & 0xff) | + (n_ren_off & 0xff) << 8); +} + +/* Derive BCH timing register values from 'nand_timing_spec' data */ +static void bch_calc_timing_registers(struct nand_timing_spec *spec, + int tCLK, int relax, + uint32_t *ctl_timing, + uint32_t *wen_timing, + uint32_t *ren_timing) +{ + int tMAX_HOLD; + int n_ctl_setup; + int n_ctl_hold; + int n_ctl_wb; + + int n_wen_on; + int n_wen_off; + int wen_half_on; + int wen_half_off; + + int tMAX_REN_ON; + int tMAX_CS_DEASSERT; + int n_d_latch; + int n_telqv; + int n_ren_on; + int n_ren_off; + int ren_half_on; + int ren_half_off; + + /* + * CTL_TIMING + */ + + /* - SETUP */ + if (spec->tCLS > spec->tWP) + n_ctl_setup = (spec->tCLS - spec->tWP + tCLK - 1)/tCLK; + else + n_ctl_setup = 0; + n_ctl_setup += relax; + + /* - HOLD */ + tMAX_HOLD = spec->tCLH; + if (spec->tCH > tMAX_HOLD) + tMAX_HOLD = spec->tCH; + if (spec->tALH > tMAX_HOLD) + tMAX_HOLD = spec->tALH; + if (spec->tDH > tMAX_HOLD) + tMAX_HOLD = spec->tDH; + n_ctl_hold = (tMAX_HOLD + tCLK - 1)/tCLK + relax; + /* - CE_deassert_hold = 0 */ + + /* - WE_high_to_RBn_low */ + n_ctl_wb = (spec->tWB + tCLK - 1)/tCLK; + + *ctl_timing = ((n_ctl_setup & 0xff) | + (n_ctl_hold & 0xff) << 8 | + (n_ctl_wb & 0xff) << 24); + + /* + * WEN_TIMING + */ + + /* - ON */ + n_wen_on = (2 * spec->tWH + tCLK - 1)/tCLK; + wen_half_on = n_wen_on % 2; + n_wen_on /= 2; + n_wen_on += relax; + + /* - OFF */ + n_wen_off = (2 * spec->tWP + tCLK - 1)/tCLK; + wen_half_off = n_wen_off % 2; + n_wen_off /= 2; + n_wen_off += relax; + + *wen_timing = ((n_wen_on & 0xff) | + (n_wen_off & 0xff) << 8 | + (wen_half_on << 16) | + (wen_half_off << 17)); + + /* + * REN_TIMING + */ + + /* - ON */ + tMAX_REN_ON = spec->tRC - spec->tRP; + if (spec->tREH > tMAX_REN_ON) + tMAX_REN_ON = spec->tREH; + + n_ren_on = (2 * tMAX_REN_ON + tCLK - 1)/tCLK; + ren_half_on = n_ren_on % 2; + n_ren_on /= 2; + n_ren_on += relax; + + /* - OFF */ + n_ren_off = (2 * spec->tREA + tCLK - 1)/tCLK; + ren_half_off = n_ren_off % 2; + n_ren_off /= 2; + n_ren_off += relax; + + /* - DATA_LATCH */ + if (spec->tREA <= (spec->tRP - (2 * tCLK))) + n_d_latch = 0; + else if (spec->tREA <= (spec->tRP - tCLK)) + n_d_latch = 1; + else if ((spec->tREA <= spec->tRP) && (spec->tRHOH >= 2 * tCLK)) + n_d_latch = 2; + else + n_d_latch = 3; + + /* - TELQV */ + tMAX_CS_DEASSERT = spec->tCOH; + if (spec->tCHZ > tMAX_CS_DEASSERT) + tMAX_CS_DEASSERT = spec->tCHZ; + if (spec->tCSD > tMAX_CS_DEASSERT) + tMAX_CS_DEASSERT = spec->tCSD; + + n_telqv = (tMAX_CS_DEASSERT + tCLK - 1)/tCLK; + + *ren_timing = ((n_ren_on & 0xff) | + (n_ren_off & 0xff) << 8 | + (n_d_latch & 0x3) << 16 | + (wen_half_on << 18) | + (wen_half_off << 19) | + (n_telqv & 0xff) << 24); +} + +static void flex_configure_timing_registers(struct nandi_controller *nandi, + struct nand_timing_spec *spec, + int relax) +{ + uint32_t ctl_timing; + uint32_t wen_timing; + uint32_t ren_timing; + int emi_t_ns; + + /* Select Hamming Controller */ + emiss_nandi_select(STM_NANDI_HAMMING); + + /* Get EMI clock (default 100MHz) */ + if (nandi->emi_clk) + emi_t_ns = 1000000000UL / clk_get_rate(nandi->emi_clk); + else { + dev_warn(nandi->dev, + "No EMI clock available; assuming default 100MHz\n"); + emi_t_ns = 10; + } + + /* Derive timing register values from specification */ + flex_calc_timing_registers(spec, emi_t_ns, relax, + &ctl_timing, &wen_timing, &ren_timing); + + dev_dbg(nandi->dev, + "updating FLEX timing configuration [0x%08x, 0x%08x, 0x%08x]\n", + ctl_timing, wen_timing, ren_timing); + + /* Program timing registers */ + writel(ctl_timing, nandi->base + NANDHAM_CTL_TIMING); + writel(wen_timing, nandi->base + NANDHAM_WEN_TIMING); + writel(ren_timing, nandi->base + NANDHAM_REN_TIMING); +} + +static void bch_configure_timing_registers(struct nandi_controller *nandi, + struct nand_timing_spec *spec, + int relax) +{ + uint32_t ctl_timing; + uint32_t wen_timing; + uint32_t ren_timing; + int bch_t_ns; + + /* Select BCH Controller */ + emiss_nandi_select(STM_NANDI_BCH); + + /* Get BCH clock (default 200MHz) */ + if (nandi->bch_clk) + bch_t_ns = 1000000000UL / clk_get_rate(nandi->bch_clk); + else { + dev_warn(nandi->dev, + "No BCH clock available; assuming default 200MHz\n"); + bch_t_ns = 5; + } + + /* Derive timing register values from specification */ + bch_calc_timing_registers(spec, bch_t_ns, relax, + &ctl_timing, &wen_timing, &ren_timing); + + dev_dbg(nandi->dev, + "updating BCH timing configuration [0x%08x, 0x%08x, 0x%08x]\n", + ctl_timing, wen_timing, ren_timing); + + /* Program timing registers */ + writel(ctl_timing, nandi->base + NANDBCH_CTL_TIMING); + writel(wen_timing, nandi->base + NANDBCH_WEN_TIMING); + writel(ren_timing, nandi->base + NANDBCH_REN_TIMING); +} + +static void nandi_configure_timing_registers(struct nandi_controller *nandi, + struct nand_timing_spec *spec, + int relax) +{ + bch_configure_timing_registers(nandi, spec, relax); + flex_configure_timing_registers(nandi, spec, relax); +} + static void nandi_init_hamming(struct nandi_controller *nandi, int emi_bank) { dev_dbg(nandi->dev, "%s\n", __func__); @@ -463,6 +752,28 @@ static int stm_nand_bch_probe(struct platform_device *pdev) if (err) return err; + /* + * Configure timing registers + */ + if (bank && bank->timing_spec) { + dev_info(&pdev->dev, "Using platform timing data\n"); + nandi_configure_timing_registers(nandi, bank->timing_spec, + bank->timing_relax); + } else if (chip->onfi_version) { + int mode = fls(onfi_get_async_timing_mode(chip) - 1); + + /* Modes 4 and 5 (EDO) are not supported on our H/W */ + if (mode > 3) + mode = 3; + + dev_info(&pdev->dev, "Using ONFI Timing Mode %d\n", mode); + nandi_configure_timing_registers(nandi, + &nand_onfi_timing_specs[mode], + bank ? bank->timing_relax : 0); + } else { + dev_warn(&pdev->dev, "No timing data available\n"); + } + return 0; }