From patchwork Fri Jan 19 13:58:54 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 863525 X-Patchwork-Delegate: boris.brezillon@free-electrons.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.infradead.org (client-ip=65.50.211.133; helo=bombadil.infradead.org; envelope-from=linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="D/vICHk/"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zNMsS3z0qz9s7v for ; Sat, 20 Jan 2018 01:00:12 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=gKgGGIQyTBfSB1g5DplVaFDztcP+DBXgTB3Iql/WuqM=; b=D/vICHk/wnDs+lRS2riEdb98kK F/oJcILDmrw0+yiCmr7VLK9uD5vmuhaScAIhRYYTCmvCxeFLNSh2tkg9n7HI0TrnhTcHCq3Roz3ME zO0b0YBcjQogvyHi8KFZvrxmqIC3qlrAflvMByshRHxbYYtRN07TLqxUdRDtHBTTSGDqcQBEG5HJW 5tgTfxPvT/dgrx+meNsGnxUCVLRTuzkHVmFvMahNx2xto0pcg8OSlwvXaALrBX0F0YcYajCTu7xrv tJ2unYbeZFuJMH8+xU5WqlEvzY2W+644BJf3TG48Z3Wg8+NoyWuSIXT/2gGNoEG0LWSKkP5qax2As IWVGrTXA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1ecXDD-0001bQ-85; Fri, 19 Jan 2018 14:00:07 +0000 Received: from mail.free-electrons.com ([62.4.15.54]) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1ecXCw-00013l-AH for linux-mtd@lists.infradead.org; Fri, 19 Jan 2018 13:59:52 +0000 Received: by mail.free-electrons.com (Postfix, from userid 110) id 4DB9E2090D; Fri, 19 Jan 2018 14:59:39 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT, URIBL_BLOCKED shortcircuit=ham autolearn=disabled version=3.4.0 Received: from localhost.localdomain (LStLambert-657-1-97-87.w90-63.abo.wanadoo.fr [90.63.216.87]) by mail.free-electrons.com (Postfix) with ESMTPSA id 00024208F4; Fri, 19 Jan 2018 14:59:28 +0100 (CET) From: Miquel Raynal To: Boris Brezillon , Richard Weinberger , David Woodhouse , Brian Norris , Marek Vasut , Cyrille Pitchen Subject: [PATCH v3 1/2] mtd: nand: Check ONFI timings have been acked by the chip Date: Fri, 19 Jan 2018 14:58:54 +0100 Message-Id: <20180119135855.10673-2-miquel.raynal@free-electrons.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180119135855.10673-1-miquel.raynal@free-electrons.com> References: <20180119135855.10673-1-miquel.raynal@free-electrons.com> X-Spam-Note: CRM114 invocation failed X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-1.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Han Xu , linux-mtd@lists.infradead.org, Miquel Raynal MIME-Version: 1.0 Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Choosing ONFI timings when ->onfi_set/get_features() calls are supported by the NAND chip is a matter of reading the chip's ONFI parameter page and telling the chip the chosen mode (between all of the supported ones) with ->onfi_set_feature(). Add a check on whether the chip "acked" the timing mode or not. This can be a problem for NAND chips that do not follow entirely the ONFI specification. These chips actually support other modes than "mode 0", but do not update the parameter page once a timing mode has been selected. This issue will be addressed in another patch that will add the feature to overwrite NAND chips features within the parameter page, from the NAND chip driver. Signed-off-by: Miquel Raynal Tested-by: Han Xu --- drivers/mtd/nand/nand_base.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 96c97588e1ba..e209a65a17e0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1231,15 +1231,47 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) chip->onfi_timing_mode_default, }; + chip->select_chip(mtd, chipnr); ret = chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_TIMING_MODE, tmode_param); + chip->select_chip(mtd, -1); if (ret) - goto err; + return ret; } ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface); -err: + if (ret) + return ret; + + if (chip->onfi_version && + (le16_to_cpu(chip->onfi_params.opt_cmd) & + ONFI_OPT_CMD_SET_GET_FEATURES)) { + u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {}; + + chip->select_chip(mtd, chipnr); + ret = chip->onfi_get_features(mtd, chip, + ONFI_FEATURE_ADDR_TIMING_MODE, + tmode_param); + chip->select_chip(mtd, -1); + if (ret) + goto err_reset_chip; + + if (tmode_param[0] != chip->onfi_timing_mode_default) { + pr_warn("timings mode %d not acknowledged by the NAND chip\n", + chip->onfi_timing_mode_default); + goto err_reset_chip; + } + } + + return 0; + +err_reset_chip: + onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); + chip->select_chip(mtd, chipnr); + nand_reset_op(chip); + chip->select_chip(mtd, -1); + return ret; } @@ -2738,10 +2770,8 @@ int nand_reset(struct nand_chip *chip, int chipnr) if (ret) return ret; - chip->select_chip(mtd, chipnr); chip->data_interface = saved_data_intf; ret = nand_setup_data_interface(chip, chipnr); - chip->select_chip(mtd, -1); if (ret) return ret; @@ -6571,10 +6601,7 @@ int nand_scan_tail(struct mtd_info *mtd) /* Enter fastest possible mode on all dies. */ for (i = 0; i < chip->numchips; i++) { - chip->select_chip(mtd, i); ret = nand_setup_data_interface(chip, i); - chip->select_chip(mtd, -1); - if (ret) goto err_nand_manuf_cleanup; } From patchwork Fri Jan 19 13:58:55 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 863527 X-Patchwork-Delegate: boris.brezillon@free-electrons.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.infradead.org (client-ip=65.50.211.133; helo=bombadil.infradead.org; envelope-from=linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="MziWnWnQ"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zNMsk1Sglz9s7G for ; Sat, 20 Jan 2018 01:00:26 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=PoLdJZjMGAAiM+zG8fq/09WJP417ZrdeNtec2PwthlY=; b=MziWnWnQRtiGldccHhhK5vVVNS Eo3pUvzXicmImZSehb+qig42APG6PNbzeldSpThte1cNIw7+VPC21Hqy7DYglOsuWQb7ZKbZwXu9p AXdNE1t490lRfNaQQUK/ngeM7rCA+lmfAnazf3dxK4K3I7x22rYNoGQQh7rotdXcYUtbIjqCk3z+Q /Ko06Nr9Rw8JAZFzBFQKSuiEKqaSZGc9Vp1C9KCy/RorqQKGpBqiacjJlu8er/AzDOdf30Mw+nEjH 174aTX2aIY1sW8VP8ATiSSFFIVbcqFTO6ZPlWvOlOy8BUpQIjWYMoEN1JQ5PjD2vauKjNwHDJoWRl LVDHu9mw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1ecXDP-0002g1-Rj; Fri, 19 Jan 2018 14:00:19 +0000 Received: from mail.free-electrons.com ([62.4.15.54]) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1ecXCw-00013n-AH for linux-mtd@lists.infradead.org; Fri, 19 Jan 2018 13:59:54 +0000 Received: by mail.free-electrons.com (Postfix, from userid 110) id 94EF5208F4; Fri, 19 Jan 2018 14:59:39 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT, URIBL_BLOCKED shortcircuit=ham autolearn=disabled version=3.4.0 Received: from localhost.localdomain (LStLambert-657-1-97-87.w90-63.abo.wanadoo.fr [90.63.216.87]) by mail.free-electrons.com (Postfix) with ESMTPSA id 389E1208FF; Fri, 19 Jan 2018 14:59:29 +0100 (CET) From: Miquel Raynal To: Boris Brezillon , Richard Weinberger , David Woodhouse , Brian Norris , Marek Vasut , Cyrille Pitchen Subject: [PATCH v3 2/2] mtd: nand: gpmi: Support ->setup_data_interface() Date: Fri, 19 Jan 2018 14:58:55 +0100 Message-Id: <20180119135855.10673-3-miquel.raynal@free-electrons.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180119135855.10673-1-miquel.raynal@free-electrons.com> References: <20180119135855.10673-1-miquel.raynal@free-electrons.com> X-Spam-Note: CRM114 invocation failed X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-1.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Han Xu , linux-mtd@lists.infradead.org, Miquel Raynal MIME-Version: 1.0 Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Until now the GPMI driver had its own timings logic while the core already handles that and request the NAND controller drivers to support the ->setup_data_interface() hook. Implement that hook by reusing the already existing function. No real glue is necessary between core timing delays and GPMI registers because the driver already translates the ONFI timing modes into register values. Make use of the core's tREA, tRLOH and tRHOH values that allow computing more precise timings for mode [0-3] and get significantly better values (+20% with an i.MX6 Sabre Auto board). Otherwise use the existing logic. Signed-off-by: Miquel Raynal Tested-by: Han Xu --- drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 277 ++++++++++----------------------- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 42 +++-- drivers/mtd/nand/gpmi-nand/gpmi-nand.h | 82 +++++----- drivers/mtd/nand/nand_base.c | 3 +- 4 files changed, 152 insertions(+), 252 deletions(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 97787246af41..9a58661eb3b1 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -151,8 +151,15 @@ static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v) return ret; } -#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true) -#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false) +int gpmi_enable_clk(struct gpmi_nand_data *this) +{ + return __gpmi_enable_clk(this, true); +} + +int gpmi_disable_clk(struct gpmi_nand_data *this) +{ + return __gpmi_enable_clk(this, false); +} int gpmi_init(struct gpmi_nand_data *this) { @@ -326,14 +333,13 @@ static unsigned int ns_to_cycles(unsigned int time, #define DEF_MIN_PROP_DELAY 5 #define DEF_MAX_PROP_DELAY 9 /* Apply timing to current hardware conditions. */ -static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, - struct gpmi_nfc_hardware_timing *hw) +static void +gpmi_nfc_compute_hardware_timings(struct gpmi_nand_data *this) { + struct gpmi_nfc_hardware_timing *hw = &this->hw; struct timing_threshold *nfc = &timing_default_threshold; - struct resources *r = &this->resources; struct nand_chip *nand = &this->nand; struct nand_timing target = this->timing; - bool improved_timing_is_available; unsigned long clock_frequency_in_hz; unsigned int clock_period_in_ns; bool dll_use_half_periods; @@ -349,6 +355,9 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY; unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY; + /* Clock rate for non-EDO modes */ + hw->clk_rate = 22000000; + /* * If there are multiple chips, we need to relax the timings to allow * for signal distortion due to higher capacitance. @@ -363,14 +372,8 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, target.address_setup_in_ns += 5; } - /* Check if improved timing information is available. */ - improved_timing_is_available = - (target.tREA_in_ns >= 0) && - (target.tRLOH_in_ns >= 0) && - (target.tRHOH_in_ns >= 0); - /* Inspect the clock. */ - nfc->clock_frequency_in_hz = clk_get_rate(r->clock[0]); + nfc->clock_frequency_in_hz = hw->clk_rate; clock_frequency_in_hz = nfc->clock_frequency_in_hz; clock_period_in_ns = NSEC_PER_SEC / clock_frequency_in_hz; @@ -482,65 +485,6 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, } /* - * Check if improved timing information is available. If not, we have to - * use a less-sophisticated algorithm. - */ - if (!improved_timing_is_available) { - /* - * Fold the read setup time required by the NFC into the ideal - * sample delay. - */ - ideal_sample_delay_in_ns = target.gpmi_sample_delay_in_ns + - nfc->internal_data_setup_in_ns; - - /* - * The ideal sample delay may be greater than the maximum - * allowed by the NFC. If so, we can trade off sample delay time - * for more data setup time. - * - * In each iteration of the following loop, we add a cycle to - * the data setup time and subtract a corresponding amount from - * the sample delay until we've satisified the constraints or - * can't do any better. - */ - while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && - (data_setup_in_cycles < nfc->max_data_setup_cycles)) { - - data_setup_in_cycles++; - ideal_sample_delay_in_ns -= clock_period_in_ns; - - if (ideal_sample_delay_in_ns < 0) - ideal_sample_delay_in_ns = 0; - - } - - /* - * Compute the sample delay factor that corresponds most closely - * to the ideal sample delay. If the result is too large for the - * NFC, use the maximum value. - * - * Notice that we use the ns_to_cycles function to compute the - * sample delay factor. We do this because the form of the - * computation is the same as that for calculating cycles. - */ - sample_delay_factor = - ns_to_cycles( - ideal_sample_delay_in_ns << dll_delay_shift, - clock_period_in_ns, 0); - - if (sample_delay_factor > nfc->max_sample_delay_factor) - sample_delay_factor = nfc->max_sample_delay_factor; - - /* Skip to the part where we return our results. */ - goto return_results; - } - - /* - * If control arrives here, we have more detailed timing information, - * so we can use a better algorithm. - */ - - /* * Fold the read setup time required by the NFC into the maximum * propagation delay. */ @@ -760,8 +704,6 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, sample_delay_factor = nfc->max_sample_delay_factor; } - /* Control arrives here when we're ready to return our results. */ -return_results: hw->data_setup_in_cycles = data_setup_in_cycles; hw->data_hold_in_cycles = data_hold_in_cycles; hw->address_setup_in_cycles = address_setup_in_cycles; @@ -769,9 +711,6 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, hw->sample_delay_factor = sample_delay_factor; hw->device_busy_timeout = GPMI_DEFAULT_BUSY_TIMEOUT; hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS; - - /* Return success. */ - return 0; } /* @@ -857,19 +796,17 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, * So we only support the mode 4 and mode 5. It is no need to * support other modes. */ -static void gpmi_compute_edo_timing(struct gpmi_nand_data *this, - struct gpmi_nfc_hardware_timing *hw) +static void gpmi_nfc_compute_edo_timings(struct gpmi_nand_data *this, + int mode) { - struct resources *r = &this->resources; - unsigned long rate = clk_get_rate(r->clock[0]); - int mode = this->timing_mode; + struct gpmi_nfc_hardware_timing *hw = &this->hw; int dll_threshold = this->devdata->max_chain_delay; unsigned long delay; unsigned long clk_period; - int t_rea; - int c = 4; - int t_rp; - int rp; + int t_rp, t_rea, rp; + + /* Set the main clock to: 100MHz (mode 5) or 80MHz (mode 4) */ + hw->clk_rate = (mode == 5) ? 100000000 : 80000000; /* * [1] for GPMI_HW_GPMI_TIMING0: @@ -880,7 +817,7 @@ static void gpmi_compute_edo_timing(struct gpmi_nand_data *this, */ hw->data_setup_in_cycles = 1; hw->data_hold_in_cycles = 1; - hw->address_setup_in_cycles = ((mode == 5) ? 1 : 0); + hw->address_setup_in_cycles = (mode == 5) ? 1 : 0; /* [2] for GPMI_HW_GPMI_TIMING1 */ hw->device_busy_timeout = 0x9000; @@ -892,11 +829,9 @@ static void gpmi_compute_edo_timing(struct gpmi_nand_data *this, * Enlarge 10 times for the numerator and denominator in {3}. * This make us to get more accurate result. */ - clk_period = NSEC_PER_SEC / (rate / 10); + clk_period = NSEC_PER_SEC / (hw->clk_rate / 10); dll_threshold *= 10; - t_rea = ((mode == 5) ? 16 : 20) * 10; - c *= 10; - + t_rea = this->timing.tREA_in_ns * 10; t_rp = clk_period * 1; /* DATA_SETUP is 1 */ if (clk_period > dll_threshold) { @@ -911,129 +846,42 @@ static void gpmi_compute_edo_timing(struct gpmi_nand_data *this, * Multiply the numerator with 10, we could do a round off: * 7.8 round up to 8; 7.4 round down to 7. */ - delay = (((t_rea + c - t_rp) * 8) * 10) / rp; + delay = (((t_rea + 40 - t_rp) * 8) * 10) / rp; delay = (delay + 5) / 10; hw->sample_delay_factor = delay; } -static int enable_edo_mode(struct gpmi_nand_data *this, int mode) -{ - struct resources *r = &this->resources; - struct nand_chip *nand = &this->nand; - struct mtd_info *mtd = nand_to_mtd(nand); - uint8_t *feature; - unsigned long rate; - int ret; - - feature = kzalloc(ONFI_SUBFEATURE_PARAM_LEN, GFP_KERNEL); - if (!feature) - return -ENOMEM; - - nand->select_chip(mtd, 0); - - /* [1] send SET FEATURE command to NAND */ - feature[0] = mode; - ret = nand->onfi_set_features(mtd, nand, - ONFI_FEATURE_ADDR_TIMING_MODE, feature); - if (ret) - goto err_out; - - /* [2] send GET FEATURE command to double-check the timing mode */ - memset(feature, 0, ONFI_SUBFEATURE_PARAM_LEN); - ret = nand->onfi_get_features(mtd, nand, - ONFI_FEATURE_ADDR_TIMING_MODE, feature); - if (ret || feature[0] != mode) - goto err_out; - - nand->select_chip(mtd, -1); - - /* [3] set the main IO clock, 100MHz for mode 5, 80MHz for mode 4. */ - rate = (mode == 5) ? 100000000 : 80000000; - clk_set_rate(r->clock[0], rate); - - /* Let the gpmi_begin() re-compute the timing again. */ - this->flags &= ~GPMI_TIMING_INIT_OK; - - this->flags |= GPMI_ASYNC_EDO_ENABLED; - this->timing_mode = mode; - kfree(feature); - dev_info(this->dev, "enable the asynchronous EDO mode %d\n", mode); - return 0; - -err_out: - nand->select_chip(mtd, -1); - kfree(feature); - dev_err(this->dev, "mode:%d ,failed in set feature.\n", mode); - return -EINVAL; -} - -int gpmi_extra_init(struct gpmi_nand_data *this) -{ - struct nand_chip *chip = &this->nand; - - /* Enable the asynchronous EDO feature. */ - if (GPMI_IS_MX6(this) && chip->onfi_version) { - int mode = onfi_get_async_timing_mode(chip); - - /* We only support the timing mode 4 and mode 5. */ - if (mode & ONFI_TIMING_MODE_5) - mode = 5; - else if (mode & ONFI_TIMING_MODE_4) - mode = 4; - else - return 0; - - return enable_edo_mode(this, mode); - } - return 0; -} - /* Begin the I/O */ -void gpmi_begin(struct gpmi_nand_data *this) +void gpmi_nfc_apply_timings(struct gpmi_nand_data *this) { + struct gpmi_nfc_hardware_timing *hw = &this->hw; struct resources *r = &this->resources; void __iomem *gpmi_regs = r->gpmi_regs; unsigned int clock_period_in_ns; uint32_t reg; unsigned int dll_wait_time_in_us; - struct gpmi_nfc_hardware_timing hw; - int ret; - - /* Enable the clock. */ - ret = gpmi_enable_clk(this); - if (ret) { - dev_err(this->dev, "We failed in enable the clk\n"); - goto err_out; - } - /* Only initialize the timing once */ - if (this->flags & GPMI_TIMING_INIT_OK) - return; - this->flags |= GPMI_TIMING_INIT_OK; - - if (this->flags & GPMI_ASYNC_EDO_ENABLED) - gpmi_compute_edo_timing(this, &hw); - else - gpmi_nfc_compute_hardware_timing(this, &hw); + /* [0] Set the main clock rate */ + clk_set_rate(r->clock[0], hw->clk_rate); /* [1] Set HW_GPMI_TIMING0 */ - reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) | - BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) | - BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles); + reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw->address_setup_in_cycles) | + BF_GPMI_TIMING0_DATA_HOLD(hw->data_hold_in_cycles) | + BF_GPMI_TIMING0_DATA_SETUP(hw->data_setup_in_cycles); writel(reg, gpmi_regs + HW_GPMI_TIMING0); /* [2] Set HW_GPMI_TIMING1 */ - writel(BF_GPMI_TIMING1_BUSY_TIMEOUT(hw.device_busy_timeout), - gpmi_regs + HW_GPMI_TIMING1); + writel(BF_GPMI_TIMING1_BUSY_TIMEOUT(hw->device_busy_timeout), + gpmi_regs + HW_GPMI_TIMING1); /* [3] The following code is to set the HW_GPMI_CTRL1. */ /* Set the WRN_DLY_SEL */ writel(BM_GPMI_CTRL1_WRN_DLY_SEL, gpmi_regs + HW_GPMI_CTRL1_CLR); - writel(BF_GPMI_CTRL1_WRN_DLY_SEL(hw.wrn_dly_sel), - gpmi_regs + HW_GPMI_CTRL1_SET); + writel(BF_GPMI_CTRL1_WRN_DLY_SEL(hw->wrn_dly_sel), + gpmi_regs + HW_GPMI_CTRL1_SET); /* DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD. */ writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR); @@ -1043,12 +891,12 @@ void gpmi_begin(struct gpmi_nand_data *this) writel(reg, gpmi_regs + HW_GPMI_CTRL1_CLR); /* If no sample delay is called for, return immediately. */ - if (!hw.sample_delay_factor) + if (!hw->sample_delay_factor) return; /* Set RDN_DELAY or HALF_PERIOD. */ - reg = ((hw.use_half_periods) ? BM_GPMI_CTRL1_HALF_PERIOD : 0) - | BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor); + reg = ((hw->use_half_periods) ? BM_GPMI_CTRL1_HALF_PERIOD : 0) + | BF_GPMI_CTRL1_RDN_DELAY(hw->sample_delay_factor); writel(reg, gpmi_regs + HW_GPMI_CTRL1_SET); @@ -1060,7 +908,7 @@ void gpmi_begin(struct gpmi_nand_data *this) * we can use the GPMI. Calculate the amount of time we need to wait, * in microseconds. */ - clock_period_in_ns = NSEC_PER_SEC / clk_get_rate(r->clock[0]); + clock_period_in_ns = NSEC_PER_SEC / hw->clk_rate; dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000; if (!dll_wait_time_in_us) @@ -1068,14 +916,45 @@ void gpmi_begin(struct gpmi_nand_data *this) /* Wait for the DLL to settle. */ udelay(dll_wait_time_in_us); - -err_out: - return; } -void gpmi_end(struct gpmi_nand_data *this) +int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr, + const struct nand_data_interface *conf) { - gpmi_disable_clk(this); + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + const struct nand_sdr_timings *sdr; + bool edo_mode = false; + + /* Retrieve required NAND timings */ + sdr = nand_get_sdr_timings(conf); + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + if (sdr->tRC_min <= 25000) + edo_mode = true; + + /* Only MX6 GPMI controller can reach EDO timings */ + if (edo_mode && !GPMI_IS_MX6(this)) + return -ENOTSUPP; + + if (chipnr < 0) + return 0; + + this->timing.tREA_in_ns = sdr->tREA_max / 1000; + this->timing.tRLOH_in_ns = sdr->tRLOH_min / 1000; + this->timing.tRHOH_in_ns = sdr->tRHOH_min / 1000; + + /* Compute GPMI parameters depending on the mode */ + if (edo_mode) + gpmi_nfc_compute_edo_timings(this, + sdr->tRC_min > 20000 ? 4 : 5); + else + gpmi_nfc_compute_hardware_timings(this); + + this->hw.must_apply_timings = true; + + return 0; } /* Clears a BCH interrupt. */ diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index b51db8c85405..459b7fb5e15b 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -938,11 +938,32 @@ static void gpmi_select_chip(struct mtd_info *mtd, int chipnr) { struct nand_chip *chip = mtd_to_nand(mtd); struct gpmi_nand_data *this = nand_get_controller_data(chip); + int ret; - if ((this->current_chip < 0) && (chipnr >= 0)) - gpmi_begin(this); - else if ((this->current_chip >= 0) && (chipnr < 0)) - gpmi_end(this); + /* + * For power consumption matters, disable/enable the clock each time a + * die is selected/unselected. + */ + if (this->current_chip < 0 && chipnr >= 0) { + ret = gpmi_enable_clk(this); + if (ret) + dev_err(this->dev, "Failed to enable the clock\n"); + } else if (this->current_chip >= 0 && chipnr < 0) { + ret = gpmi_disable_clk(this); + if (ret) + dev_err(this->dev, "Failed to disable the clock\n"); + } + + /* + * This driver currently supports only one NAND chip. Plus, dies share + * the same configuration. So once timings have been applied on the + * controller side, they will not change anymore. When the time will + * come, the check on must_apply_timings will have to be dropped. + */ + if (chipnr >= 0 && this->hw.must_apply_timings) { + this->hw.must_apply_timings = false; + gpmi_nfc_apply_timings(this); + } this->current_chip = chipnr; } @@ -1947,14 +1968,6 @@ static int gpmi_init_last(struct gpmi_nand_data *this) chip->options |= NAND_SUBPAGE_READ; } - /* - * Can we enable the extra features? such as EDO or Sync mode. - * - * We do not check the return value now. That's means if we fail in - * enable the extra features, we still can run in the normal way. - */ - gpmi_extra_init(this); - return 0; } @@ -1975,6 +1988,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) nand_set_controller_data(chip, this); nand_set_flash_node(chip, this->pdev->dev.of_node); chip->select_chip = gpmi_select_chip; + chip->setup_data_interface = gpmi_setup_data_interface; chip->cmd_ctrl = gpmi_cmd_ctrl; chip->dev_ready = gpmi_dev_ready; chip->read_byte = gpmi_read_byte; @@ -2133,7 +2147,6 @@ static int gpmi_pm_resume(struct device *dev) return ret; /* re-init the GPMI registers */ - this->flags &= ~GPMI_TIMING_INIT_OK; ret = gpmi_init(this); if (ret) { dev_err(this->dev, "Error setting GPMI : %d\n", ret); @@ -2147,9 +2160,6 @@ static int gpmi_pm_resume(struct device *dev) return ret; } - /* re-init others */ - gpmi_extra_init(this); - return 0; } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index 06c1f993912c..23f9030cf6d2 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -135,11 +135,47 @@ struct gpmi_devdata { const int clks_count; }; +/** + * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters. + * @timing_mode: The timing mode to comply with. + * @must_apply_timings: Whether controller timings have already been + * applied or not (useful only while there is + * support for only one chip select) + * @clk_rate: The clock rate that must be used to derive the + * following parameters. + * @data_setup_in_cycles: The data setup time, in cycles. + * @data_hold_in_cycles: The data hold time, in cycles. + * @address_setup_in_cycles: The address setup time, in cycles. + * @device_busy_timeout: The timeout waiting for NAND Ready/Busy, + * this value is the number of cycles multiplied + * by 4096. + * @use_half_periods: Indicates the clock is running slowly, so the + * NFC DLL should use half-periods. + * @sample_delay_factor: The sample delay factor. + * @wrn_dly_sel: The delay on the GPMI write strobe. + */ +struct gpmi_nfc_hardware_timing { + unsigned int timing_mode; + bool must_apply_timings; + unsigned long int clk_rate; + + /* for HW_GPMI_TIMING0 */ + u8 data_setup_in_cycles; + u8 data_hold_in_cycles; + u8 address_setup_in_cycles; + + /* for HW_GPMI_TIMING1 */ + u16 device_busy_timeout; +#define GPMI_DEFAULT_BUSY_TIMEOUT 0x500 /* default busy timeout value.*/ + + /* for HW_GPMI_CTRL1 */ + bool use_half_periods; + u8 sample_delay_factor; + u8 wrn_dly_sel; +}; + struct gpmi_nand_data { - /* flags */ -#define GPMI_ASYNC_EDO_ENABLED (1 << 0) -#define GPMI_TIMING_INIT_OK (1 << 1) - int flags; + /* Devdata */ const struct gpmi_devdata *devdata; /* System Interface */ @@ -152,6 +188,7 @@ struct gpmi_nand_data { /* Flash Hardware */ struct nand_timing timing; int timing_mode; + struct gpmi_nfc_hardware_timing hw; /* BCH */ struct bch_geometry bch_geometry; @@ -205,35 +242,6 @@ struct gpmi_nand_data { }; /** - * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters. - * @data_setup_in_cycles: The data setup time, in cycles. - * @data_hold_in_cycles: The data hold time, in cycles. - * @address_setup_in_cycles: The address setup time, in cycles. - * @device_busy_timeout: The timeout waiting for NAND Ready/Busy, - * this value is the number of cycles multiplied - * by 4096. - * @use_half_periods: Indicates the clock is running slowly, so the - * NFC DLL should use half-periods. - * @sample_delay_factor: The sample delay factor. - * @wrn_dly_sel: The delay on the GPMI write strobe. - */ -struct gpmi_nfc_hardware_timing { - /* for HW_GPMI_TIMING0 */ - uint8_t data_setup_in_cycles; - uint8_t data_hold_in_cycles; - uint8_t address_setup_in_cycles; - - /* for HW_GPMI_TIMING1 */ - uint16_t device_busy_timeout; -#define GPMI_DEFAULT_BUSY_TIMEOUT 0x500 /* default busy timeout value.*/ - - /* for HW_GPMI_CTRL1 */ - bool use_half_periods; - uint8_t sample_delay_factor; - uint8_t wrn_dly_sel; -}; - -/** * struct timing_threshold - Timing threshold * @max_data_setup_cycles: The maximum number of data setup cycles that * can be expressed in the hardware. @@ -279,14 +287,16 @@ int start_dma_with_bch_irq(struct gpmi_nand_data *, /* GPMI-NAND helper function library */ int gpmi_init(struct gpmi_nand_data *); -int gpmi_extra_init(struct gpmi_nand_data *); void gpmi_clear_bch(struct gpmi_nand_data *); void gpmi_dump_info(struct gpmi_nand_data *); int bch_set_geometry(struct gpmi_nand_data *); int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip); int gpmi_send_command(struct gpmi_nand_data *); -void gpmi_begin(struct gpmi_nand_data *); -void gpmi_end(struct gpmi_nand_data *); +int gpmi_enable_clk(struct gpmi_nand_data *this); +int gpmi_disable_clk(struct gpmi_nand_data *this); +int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr, + const struct nand_data_interface *conf); +void gpmi_nfc_apply_timings(struct gpmi_nand_data *this); int gpmi_read_data(struct gpmi_nand_data *); int gpmi_send_data(struct gpmi_nand_data *); int gpmi_send_page(struct gpmi_nand_data *, diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e209a65a17e0..9602d17845e2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1267,7 +1267,8 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) return 0; err_reset_chip: - onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); + /* Fallback to timing mode 0 */ + nand_reset_data_interface(chip, chipnr); chip->select_chip(mtd, chipnr); nand_reset_op(chip); chip->select_chip(mtd, -1);