Message ID | 1313581828-16625-3-git-send-email-b32955@freescale.com |
---|---|
State | New, archived |
Headers | show |
On Wednesday, August 17, 2011 01:50:27 PM Huang Shijie wrote: > bch-regs.h : registers file for BCH module > gpmi-regs.h: registers file for GPMI module > gpmi-lib.c: helper functions library. > > Signed-off-by: Huang Shijie <b32955@freescale.com> > --- > drivers/mtd/nand/gpmi-nand/bch-regs.h | 88 +++ > drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 978 > ++++++++++++++++++++++++++++++++ drivers/mtd/nand/gpmi-nand/gpmi-regs.h | > 174 ++++++ > 3 files changed, 1240 insertions(+), 0 deletions(-) > create mode 100644 drivers/mtd/nand/gpmi-nand/bch-regs.h > create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-lib.c > create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-regs.h > > diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h > b/drivers/mtd/nand/gpmi-nand/bch-regs.h new file mode 100644 > index 0000000..cec1dfa > --- /dev/null > +++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h > @@ -0,0 +1,88 @@ > +/* > + * Freescale GPMI NAND Flash Driver > + * > + * Copyright 2008-2011 Freescale Semiconductor, Inc. > + * Copyright 2008 Embedded Alley Solutions, Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License along > + * with this program; if not, write to the Free Software Foundation, Inc., > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > + */ > +#ifndef __GPMI_NAND_BCH_REGS_H > +#define __GPMI_NAND_BCH_REGS_H > + Aaargh, please remove these separators. > +/*======================================================================== > ====*/ +#define HW_BCH_CTRL 0x00000000 > +#define HW_BCH_CTRL_SET 0x00000004 > +#define HW_BCH_CTRL_CLR 0x00000008 > +#define HW_BCH_CTRL_TOG 0x0000000c > + > +#define BM_BCH_CTRL_COMPLETE_IRQ_EN (1 << 8) > +#define BM_BCH_CTRL_COMPLETE_IRQ (1 << 0) > + > +/*======================================================================== > ====*/ +#define HW_BCH_STATUS0 0x00000010 > +#define HW_BCH_MODE 0x00000020 > +#define HW_BCH_ENCODEPTR 0x00000030 > +#define HW_BCH_DATAPTR 0x00000040 > +#define HW_BCH_METAPTR 0x00000050 > +#define HW_BCH_LAYOUTSELECT 0x00000070 > + > +/*======================================================================== > ====*/ +#define HW_BCH_FLASH0LAYOUT0 0x00000080 > + > +#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24 > +#define BM_BCH_FLASH0LAYOUT0_NBLOCKS (0xff << > BP_BCH_FLASH0LAYOUT0_NBLOCKS) +#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \ > + (((v) << BP_BCH_FLASH0LAYOUT0_NBLOCKS) & BM_BCH_FLASH0LAYOUT0_NBLOCKS) > + > +#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16 > +#define BM_BCH_FLASH0LAYOUT0_META_SIZE (0xff << > BP_BCH_FLASH0LAYOUT0_META_SIZE) +#define > BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \ > + (((v) << BP_BCH_FLASH0LAYOUT0_META_SIZE)\ > + & BM_BCH_FLASH0LAYOUT0_META_SIZE) > + > +#define BP_BCH_FLASH0LAYOUT0_ECC0 12 > +#define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0) > +#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \ > + (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) & BM_BCH_FLASH0LAYOUT0_ECC0) > + > +#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 > +#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ > + (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) > +#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \ > + (((v) << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)\ > + & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) > + > +/*======================================================================== > ====*/ +#define HW_BCH_FLASH0LAYOUT1 0x00000090 > + > +#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16 > +#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE \ > + (0xffff << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) > +#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \ > + (((v) << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) \ > + & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE) > + > +#define BP_BCH_FLASH0LAYOUT1_ECCN 12 > +#define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN) > +#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \ > + (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) & BM_BCH_FLASH0LAYOUT1_ECCN) > + > +#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 > +#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ > + (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) > +#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \ > + (((v) << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ > + & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) > +#endif > diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c > b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c new file mode 100644 > index 0000000..1368842 > --- /dev/null > +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c > @@ -0,0 +1,978 @@ > +/* > + * Freescale GPMI NAND Flash Driver > + * > + * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. > + * Copyright (C) 2008 Embedded Alley Solutions, Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License along > + * with this program; if not, write to the Free Software Foundation, Inc., > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > + */ > +#include "gpmi-nand.h" > +#include "gpmi-regs.h" > +#include "bch-regs.h" > + > +struct timing_threshod timing_default_threshold = { > + .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >> > + BP_GPMI_TIMING0_DATA_SETUP), > + .internal_data_setup_in_ns = 0, > + .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >> > + BP_GPMI_CTRL1_RDN_DELAY), > + .max_dll_clock_period_in_ns = 32, > + .max_dll_delay_in_ns = 16, > +}; > + > +int gpmi_init(struct gpmi_nand_data *this) > +{ > + struct resources *r = &this->resources; > + int ret; > + > + ret = clk_enable(r->clock); > + if (ret) > + goto err_out; > + ret = mxs_reset_block(r->gpmi_regs); > + if (ret) > + goto err_out; > + > + /* Choose NAND mode. */ > + writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR); > + > + /* Set the IRQ polarity. */ > + writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, > + r->gpmi_regs + HW_GPMI_CTRL1_SET); > + > + /* Disable Write-Protection. */ > + writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET); > + > + /* Select BCH ECC. */ > + writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); > + > + clk_disable(r->clock); > + return 0; > +err_out: > + return ret; > +} > + > +/* This is very useful! */ Really? Cool, what for ? btw. this should really be enclosed in some #ifdef debug or whatnot. > +void gpmi_show_regs(struct gpmi_nand_data *this) > +{ > + struct resources *r = &this->resources; > + u32 reg; > + int i; > + int n; > + > + n = HW_GPMI_DEBUG / 0x10 + 1; > + > + pr_info("-------------- Show GPMI registers ----------\n"); > + for (i = 0; i <= n; i++) { > + reg = readl(r->gpmi_regs + i * 0x10); > + pr_info("offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); > + } > + pr_info("-------------- Show GPMI registers end ----------\n"); > +} > + > +/* Configures the geometry for BCH. */ > +int bch_set_geometry(struct gpmi_nand_data *this) > +{ > + struct resources *r = &this->resources; > + struct bch_geometry *bch_geo = &this->bch_geometry; > + unsigned int block_count; > + unsigned int block_size; > + unsigned int metadata_size; > + unsigned int ecc_strength; > + unsigned int page_size; > + int ret; > + > + if (common_nfc_set_geometry(this)) > + return !0; > + > + block_count = bch_geo->ecc_chunk_count - 1; > + block_size = bch_geo->ecc_chunk_size_in_bytes; > + metadata_size = bch_geo->metadata_size_in_bytes; > + ecc_strength = bch_geo->ecc_strength >> 1; > + page_size = bch_geo->page_size_in_bytes; > + > + ret = clk_enable(r->clock); > + if (ret) > + goto err_out; > + ret = mxs_reset_block(r->bch_regs); > + if (ret) > + goto err_out; > + > + /* Configure layout 0. */ > + writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) > + | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) > + | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) > + | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size), > + r->bch_regs + HW_BCH_FLASH0LAYOUT0); > + > + writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) > + | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) > + | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size), > + r->bch_regs + HW_BCH_FLASH0LAYOUT1); > + > + /* Set *all* chip selects to use layout 0. */ > + writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT); > + > + /* Enable interrupts. */ > + writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, > + r->bch_regs + HW_BCH_CTRL_SET); > + > + clk_disable(r->clock); > + return 0; > +err_out: > + return ret; > +} > + > +/* > + * ns_to_cycles - Converts time in nanoseconds to cycles. > + * > + * @ntime: The time, in nanoseconds. > + * @period: The cycle period, in nanoseconds. > + * @min: The minimum allowable number of cycles. > + */ > +static unsigned int ns_to_cycles(unsigned int time, > + unsigned int period, unsigned int min) > +{ > + unsigned int k; > + > + /* > + * Compute the minimum number of cycles that entirely contain the > + * given time. > + */ > + k = (time + period - 1) / period; > + return max(k, min); > +} > + > +/* > + * gpmi_compute_hardware_timing - Apply timing to current hardware > conditions. + * > + * @this: Per-device data. > + * @hardware_timing: A pointer to a hardware timing structure that will > receive + * the results of our calculations. > + */ > +static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, > + struct gpmi_nfc_hardware_timing *hw) > +{ > + struct gpmi_nand_platform_data *pdata = this->pdata; > + struct timing_threshod *nfc = &timing_default_threshold; > + struct nand_chip *nand = &this->mil.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; > + unsigned int dll_delay_shift; > + unsigned int max_sample_delay_in_ns; > + unsigned int address_setup_in_cycles; > + unsigned int data_setup_in_ns; > + unsigned int data_setup_in_cycles; > + unsigned int data_hold_in_cycles; > + int ideal_sample_delay_in_ns; > + unsigned int sample_delay_factor; > + int tEYE; > + unsigned int min_prop_delay_in_ns = pdata->min_prop_delay_in_ns; > + unsigned int max_prop_delay_in_ns = pdata->max_prop_delay_in_ns; > + > + /* > + * If there are multiple chips, we need to relax the timings to allow > + * for signal distortion due to higher capacitance. > + */ > + if (nand->numchips > 2) { > + target.data_setup_in_ns += 10; > + target.data_hold_in_ns += 10; > + target.address_setup_in_ns += 10; > + } else if (nand->numchips > 1) { > + target.data_setup_in_ns += 5; > + target.data_hold_in_ns += 5; > + 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. */ > + clock_frequency_in_hz = nfc->clock_frequency_in_hz; > + clock_period_in_ns = 1000000000 / clock_frequency_in_hz; > + > + /* > + * The NFC quantizes setup and hold parameters in terms of clock cycles. > + * Here, we quantize the setup and hold timing parameters to the > + * next-highest clock period to make sure we apply at least the > + * specified times. > + * > + * For data setup and data hold, the hardware interprets a value of zero > + * as the largest possible delay. This is not what's intended by a zero > + * in the input parameter, so we impose a minimum of one cycle. > + */ > + data_setup_in_cycles = ns_to_cycles(target.data_setup_in_ns, > + clock_period_in_ns, 1); > + data_hold_in_cycles = ns_to_cycles(target.data_hold_in_ns, > + clock_period_in_ns, 1); > + address_setup_in_cycles = ns_to_cycles(target.address_setup_in_ns, > + clock_period_in_ns, 0); > + > + /* > + * The clock's period affects the sample delay in a number of ways: > + * > + * (1) The NFC HAL tells us the maximum clock period the sample delay > + * DLL can tolerate. If the clock period is greater than half that > + * maximum, we must configure the DLL to be driven by half periods. > + * > + * (2) We need to convert from an ideal sample delay, in ns, to a > + * "sample delay factor," which the NFC uses. This factor depends on > + * whether we're driving the DLL with full or half periods. > + * Paraphrasing the reference manual: > + * > + * AD = SDF x 0.125 x RP > + * > + * where: > + * > + * AD is the applied delay, in ns. > + * SDF is the sample delay factor, which is dimensionless. > + * RP is the reference period, in ns, which is a full clock period > + * if the DLL is being driven by full periods, or half that if > + * the DLL is being driven by half periods. > + * > + * Let's re-arrange this in a way that's more useful to us: > + * > + * 8 > + * SDF = AD x ---- > + * RP > + * > + * The reference period is either the clock period or half that, so this > + * is: > + * > + * 8 AD x DDF > + * SDF = AD x ----- = -------- > + * f x P P > + * > + * where: > + * > + * f is 1 or 1/2, depending on how we're driving the DLL. > + * P is the clock period. > + * DDF is the DLL Delay Factor, a dimensionless value that > + * incorporates all the constants in the conversion. > + * > + * DDF will be either 8 or 16, both of which are powers of two. We can > + * reduce the cost of this conversion by using bit shifts instead of > + * multiplication or division. Thus: > + * > + * AD << DDS > + * SDF = --------- > + * P > + * > + * or > + * > + * AD = (SDF >> DDS) x P > + * > + * where: > + * > + * DDS is the DLL Delay Shift, the logarithm to base 2 of the DDF. > + */ > + if (clock_period_in_ns > (nfc->max_dll_clock_period_in_ns >> 1)) { > + dll_use_half_periods = true; > + dll_delay_shift = 3 + 1; > + } else { > + dll_use_half_periods = false; > + dll_delay_shift = 3; > + } > + > + /* > + * Compute the maximum sample delay the NFC allows, under current > + * conditions. If the clock is running too slowly, no sample delay is > + * possible. > + */ > + if (clock_period_in_ns > nfc->max_dll_clock_period_in_ns) > + max_sample_delay_in_ns = 0; > + else { > + /* > + * Compute the delay implied by the largest sample delay factor > + * the NFC allows. > + */ > + max_sample_delay_in_ns = > + (nfc->max_sample_delay_factor * clock_period_in_ns) >> > + dll_delay_shift; > + > + /* > + * Check if the implied sample delay larger than the NFC > + * actually allows. > + */ > + if (max_sample_delay_in_ns > nfc->max_dll_delay_in_ns) > + max_sample_delay_in_ns = nfc->max_dll_delay_in_ns; > + } > + > + /* > + * 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. > + */ > + max_prop_delay_in_ns += nfc->internal_data_setup_in_ns; > + > + /* > + * Earlier, we computed the number of clock cycles required to satisfy > + * the data setup time. Now, we need to know the actual nanoseconds. > + */ > + data_setup_in_ns = clock_period_in_ns * data_setup_in_cycles; > + > + /* > + * Compute tEYE, the width of the data eye when reading from the NAND > + * Flash. The eye width is fundamentally determined by the data setup > + * time, perturbed by propagation delays and some characteristics of the > + * NAND Flash device. > + * > + * start of the eye = max_prop_delay + tREA > + * end of the eye = min_prop_delay + tRHOH + data_setup > + */ > + tEYE = (int)min_prop_delay_in_ns + (int)target.tRHOH_in_ns + > + (int)data_setup_in_ns; > + > + tEYE -= (int)max_prop_delay_in_ns + (int)target.tREA_in_ns; > + > + /* > + * The eye must be open. If it's not, we can try to open it by > + * increasing its main forcer, the data setup time. > + * > + * In each iteration of the following loop, we increase the data setup > + * time by a single clock cycle. We do this until either the eye is > + * open or we run into NFC limits. > + */ > + while ((tEYE <= 0) && > + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { > + /* Give a cycle to data setup. */ > + data_setup_in_cycles++; > + /* Synchronize the data setup time with the cycles. */ > + data_setup_in_ns += clock_period_in_ns; > + /* Adjust tEYE accordingly. */ > + tEYE += clock_period_in_ns; > + } > + > + /* > + * When control arrives here, the eye is open. The ideal time to sample > + * the data is in the center of the eye: > + * > + * end of the eye + start of the eye > + * --------------------------------- - data_setup > + * 2 > + * > + * After some algebra, this simplifies to the code immediately below. > + */ > + ideal_sample_delay_in_ns = > + ((int)max_prop_delay_in_ns + > + (int)target.tREA_in_ns + > + (int)min_prop_delay_in_ns + > + (int)target.tRHOH_in_ns - > + (int)data_setup_in_ns) >> 1; > + > + /* > + * The following figure illustrates some aspects of a NAND Flash read: > + * > + * > + * __ _____________________________________ > + * RDN \_________________/ > + * > + * <---- tEYE -----> > + * /-----------------\ > + * Read Data ----------------------------< >--------- > + * \-----------------/ > + * ^ ^ ^ ^ > + * | | | | > + * |<--Data Setup -->|<--Delay Time -->| | > + * | | | | > + * | | | > + * | |<-- Quantized Delay Time -->| > + * | | | > + * > + * > + * We have some issues we must now address: > + * > + * (1) The *ideal* sample delay time must not be negative. If it is, we > + * jam it to zero. > + * > + * (2) The *ideal* sample delay time must not be greater than that > + * allowed by the NFC. If it is, we can increase the data setup > + * time, which will reduce the delay between the end of the data > + * setup and the center of the eye. It will also make the eye > + * larger, which might help with the next issue... > + * > + * (3) The *quantized* sample delay time must not fall either before the > + * eye opens or after it closes (the latter is the problem > + * illustrated in the above figure). > + */ > + > + /* Jam a negative ideal sample delay to zero. */ > + if (ideal_sample_delay_in_ns < 0) > + ideal_sample_delay_in_ns = 0; > + > + /* > + * Extend the data setup as needed to reduce the ideal sample delay > + * below the maximum permitted by the NFC. > + */ > + while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && > + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { > + > + /* Give a cycle to data setup. */ > + data_setup_in_cycles++; > + /* Synchronize the data setup time with the cycles. */ > + data_setup_in_ns += clock_period_in_ns; > + /* Adjust tEYE accordingly. */ > + tEYE += clock_period_in_ns; > + > + /* > + * Decrease the ideal sample delay by one half cycle, to keep it > + * in the middle of the eye. > + */ > + ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); > + > + /* Jam a negative ideal sample delay to zero. */ > + if (ideal_sample_delay_in_ns < 0) > + ideal_sample_delay_in_ns = 0; > + } > + > + /* > + * Compute the sample delay factor that corresponds to the ideal sample > + * delay. If the result is too large, then use the maximum allowed > + * 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; > + > + /* > + * These macros conveniently encapsulate a computation we'll use to > + * continuously evaluate whether or not the data sample delay is inside > + * the eye. > + */ > + #define IDEAL_DELAY ((int) ideal_sample_delay_in_ns) > + > + #define QUANTIZED_DELAY \ > + ((int) ((sample_delay_factor * clock_period_in_ns) >> \ > + dll_delay_shift)) > + > + #define DELAY_ERROR (abs(QUANTIZED_DELAY - IDEAL_DELAY)) > + > + #define SAMPLE_IS_NOT_WITHIN_THE_EYE (DELAY_ERROR > (tEYE >> 1)) > + > + /* > + * While the quantized sample time falls outside the eye, reduce the > + * sample delay or extend the data setup to move the sampling point back > + * toward the eye. Do not allow the number of data setup cycles to > + * exceed the maximum allowed by the NFC. > + */ > + while (SAMPLE_IS_NOT_WITHIN_THE_EYE && > + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { > + /* > + * If control arrives here, the quantized sample delay falls > + * outside the eye. Check if it's before the eye opens, or after > + * the eye closes. > + */ > + if (QUANTIZED_DELAY > IDEAL_DELAY) { > + /* > + * If control arrives here, the quantized sample delay > + * falls after the eye closes. Decrease the quantized > + * delay time and then go back to re-evaluate. > + */ > + if (sample_delay_factor != 0) > + sample_delay_factor--; > + continue; > + } > + > + /* > + * If control arrives here, the quantized sample delay falls > + * before the eye opens. Shift the sample point by increasing > + * data setup time. This will also make the eye larger. > + */ > + > + /* Give a cycle to data setup. */ > + data_setup_in_cycles++; > + /* Synchronize the data setup time with the cycles. */ > + data_setup_in_ns += clock_period_in_ns; > + /* Adjust tEYE accordingly. */ > + tEYE += clock_period_in_ns; > + > + /* > + * Decrease the ideal sample delay by one half cycle, to keep it > + * in the middle of the eye. > + */ > + ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); > + > + /* ...and one less period for the delay time. */ > + ideal_sample_delay_in_ns -= clock_period_in_ns; > + > + /* Jam a negative ideal sample delay to zero. */ > + if (ideal_sample_delay_in_ns < 0) > + ideal_sample_delay_in_ns = 0; > + > + /* > + * We have a new ideal sample delay, so re-compute the quantized > + * delay. > + */ > + 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; > + } > + > + /* 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; > + hw->use_half_periods = dll_use_half_periods; > + hw->sample_delay_factor = sample_delay_factor; > + > + /* Return success. */ > + return 0; > +} > + > +/* Begin the I/O */ > +void gpmi_begin(struct gpmi_nand_data *this) > +{ > + struct resources *r = &this->resources; > + struct timing_threshod *nfc = &timing_default_threshold; > + unsigned char *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 = clk_enable(r->clock); > + if (ret) { > + pr_info("We failed in enable the clk\n"); > + goto err_out; > + } > + > + /* set ready/busy timeout */ > + writel(0x500 << 16, gpmi_regs + HW_GPMI_TIMING1); > + > + /* Get the timing information we need. */ > + nfc->clock_frequency_in_hz = clk_get_rate(r->clock); > + clock_period_in_ns = 1000000000 / nfc->clock_frequency_in_hz; > + > + gpmi_nfc_compute_hardware_timing(this, &hw); > + > + /* Set up all the simple timing parameters. */ > + 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); > + > + /* > + * HEY - PAY ATTENTION! Please fix this comment and pay attention to other comments ;-) > + * > + * DLL_ENABLE must be set to zero when setting RDN_DELAY or HALF_PERIOD. > + */ > + writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR); > + > + /* Clear out the DLL control fields. */ > + writel(BM_GPMI_CTRL1_RDN_DELAY, gpmi_regs + HW_GPMI_CTRL1_CLR); > + writel(BM_GPMI_CTRL1_HALF_PERIOD, gpmi_regs + HW_GPMI_CTRL1_CLR); > + > + /* If no sample delay is called for, return immediately. */ > + if (!hw.sample_delay_factor) > + return; > + > + /* Configure the HALF_PERIOD flag. */ > + if (hw.use_half_periods) > + writel(BM_GPMI_CTRL1_HALF_PERIOD, > + gpmi_regs + HW_GPMI_CTRL1_SET); > + > + /* Set the delay factor. */ > + writel(BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor), > + gpmi_regs + HW_GPMI_CTRL1_SET); > + > + /* Enable the DLL. */ > + writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET); > + > + /* > + * After we enable the GPMI DLL, we have to wait 64 clock cycles before > + * we can use the GPMI. > + * > + * Calculate the amount of time we need to wait, in microseconds. > + */ > + dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000; > + > + if (!dll_wait_time_in_us) > + dll_wait_time_in_us = 1; > + > + /* Wait for the DLL to settle. */ > + udelay(dll_wait_time_in_us); > + > +err_out: > + return; > +} > + > +void gpmi_end(struct gpmi_nand_data *this) > +{ > + struct resources *r = &this->resources; > + clk_disable(r->clock); > +} > + > +/* Clears a BCH interrupt. */ > +void gpmi_clear_bch(struct gpmi_nand_data *this) > +{ > + struct resources *r = &this->resources; > + writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR); > +} > + > +/* Returns the Ready/Busy status of the given chip. */ > +int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) > +{ > + struct resources *r = &this->resources; > + uint32_t mask; > + uint32_t reg; > + > + if (GPMI_IS_MX23(this)) { > + mask = MX23_BM_GPMI_DEBUG_READY0 << chip; > + reg = readl(r->gpmi_regs + HW_GPMI_DEBUG); > + } else if (GPMI_IS_MX28(this)) { > + mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); > + reg = readl(r->gpmi_regs + HW_GPMI_STAT); > + } else > + BUG(); > + return !!(reg & mask); > +} > + > +static inline void set_dma_type(struct gpmi_nand_data *this, > + enum dma_ops_type type) > +{ > + this->last_dma_type = this->dma_type; > + this->dma_type = type; > +} > + > +int gpmi_send_command(struct gpmi_nand_data *this) > +{ > + struct dma_chan *channel = get_dma_chan(this); > + struct mil *mil = &this->mil; > + struct dma_async_tx_descriptor *desc; > + struct scatterlist *sgl; > + int chip = mil->current_chip; > + u32 pio[3]; > + > + /* [1] send out the PIO words */ > + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) > + | BM_GPMI_CTRL0_WORD_LENGTH > + | BF_GPMI_CTRL0_CS(chip, this) > + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) > + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) > + | BM_GPMI_CTRL0_ADDRESS_INCREMENT > + | BF_GPMI_CTRL0_XFER_COUNT(mil->command_length); > + pio[1] = pio[2] = 0; > + desc = channel->device->device_prep_slave_sg(channel, > + (struct scatterlist *)pio, > + ARRAY_SIZE(pio), DMA_NONE, 0); > + if (!desc) { > + pr_info("step 1 error\n"); > + return -1; > + } > + > + /* [2] send out the COMMAND + ADDRESS string stored in @buffer */ > + sgl = &mil->cmd_sgl; > + > + sg_init_one(sgl, mil->cmd_buffer, mil->command_length); > + dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); > + desc = channel->device->device_prep_slave_sg(channel, > + sgl, 1, DMA_TO_DEVICE, 1); > + if (!desc) { > + pr_info("step 2 error\n"); > + return -1; > + } > + > + /* [3] submit the DMA */ > + set_dma_type(this, DMA_FOR_COMMAND); > + return start_dma_without_bch_irq(this, desc); > +} > + > +int gpmi_send_data(struct gpmi_nand_data *this) > +{ > + struct dma_async_tx_descriptor *desc; > + struct dma_chan *channel = get_dma_chan(this); > + struct mil *mil = &this->mil; > + int chip = mil->current_chip; > + uint32_t command_mode; > + uint32_t address; > + u32 pio[2]; > + > + /* [1] PIO */ > + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; > + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; > + > + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) > + | BM_GPMI_CTRL0_WORD_LENGTH > + | BF_GPMI_CTRL0_CS(chip, this) > + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) > + | BF_GPMI_CTRL0_ADDRESS(address) > + | BF_GPMI_CTRL0_XFER_COUNT(mil->upper_len); > + pio[1] = 0; > + desc = channel->device->device_prep_slave_sg(channel, > + (struct scatterlist *)pio, > + ARRAY_SIZE(pio), DMA_NONE, 0); > + if (!desc) { > + pr_info("step 1 error\n"); > + return -1; > + } > + > + /* [2] send DMA request */ > + prepare_data_dma(this, DMA_TO_DEVICE); > + desc = channel->device->device_prep_slave_sg(channel, &mil->data_sgl, > + 1, DMA_TO_DEVICE, 1); > + if (!desc) { > + pr_info("step 2 error\n"); > + return -1; > + } > + /* [3] submit the DMA */ > + set_dma_type(this, DMA_FOR_WRITE_DATA); > + return start_dma_without_bch_irq(this, desc); > +} > + > +int gpmi_read_data(struct gpmi_nand_data *this) > +{ > + struct dma_async_tx_descriptor *desc; > + struct dma_chan *channel = get_dma_chan(this); > + struct mil *mil = &this->mil; > + int chip = mil->current_chip; > + u32 pio[2]; > + > + /* [1] : send PIO */ > + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) > + | BM_GPMI_CTRL0_WORD_LENGTH > + | BF_GPMI_CTRL0_CS(chip, this) > + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) > + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) > + | BF_GPMI_CTRL0_XFER_COUNT(mil->upper_len); > + pio[1] = 0; > + desc = channel->device->device_prep_slave_sg(channel, > + (struct scatterlist *)pio, > + ARRAY_SIZE(pio), DMA_NONE, 0); > + if (!desc) { > + pr_info("step 1 error\n"); > + return -1; > + } > + > + /* [2] : send DMA request */ > + prepare_data_dma(this, DMA_FROM_DEVICE); > + desc = channel->device->device_prep_slave_sg(channel, &mil->data_sgl, > + 1, DMA_FROM_DEVICE, 1); > + if (!desc) { > + pr_info("step 2 error\n"); > + return -1; > + } > + > + /* [3] : submit the DMA */ > + set_dma_type(this, DMA_FOR_READ_DATA); > + return start_dma_without_bch_irq(this, desc); > +} > + > +int gpmi_send_page(struct gpmi_nand_data *this, > + dma_addr_t payload, dma_addr_t auxiliary) > +{ > + struct bch_geometry *geo = &this->bch_geometry; > + uint32_t command_mode; > + uint32_t address; > + uint32_t ecc_command; > + uint32_t buffer_mask; > + struct dma_async_tx_descriptor *desc; > + struct dma_chan *channel = get_dma_chan(this); > + struct mil *mil = &this->mil; > + int chip = mil->current_chip; > + u32 pio[6]; > + > + /* A DMA descriptor that does an ECC page read. */ > + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; > + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; > + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE; > + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | > + BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; > + > + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) > + | BM_GPMI_CTRL0_WORD_LENGTH > + | BF_GPMI_CTRL0_CS(chip, this) > + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) > + | BF_GPMI_CTRL0_ADDRESS(address) > + | BF_GPMI_CTRL0_XFER_COUNT(0); > + pio[1] = 0; > + pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC > + | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) > + | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); > + pio[3] = geo->page_size_in_bytes; > + pio[4] = payload; > + pio[5] = auxiliary; > + > + desc = channel->device->device_prep_slave_sg(channel, > + (struct scatterlist *)pio, > + ARRAY_SIZE(pio), DMA_NONE, 0); > + if (!desc) { > + pr_info("step 2 error\n"); > + return -1; > + } > + set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE); > + return start_dma_with_bch_irq(this, desc); > +} > + > +int gpmi_read_page(struct gpmi_nand_data *this, > + dma_addr_t payload, dma_addr_t auxiliary) > +{ > + struct bch_geometry *geo = &this->bch_geometry; > + uint32_t command_mode; > + uint32_t address; > + uint32_t ecc_command; > + uint32_t buffer_mask; > + struct dma_async_tx_descriptor *desc; > + struct dma_chan *channel = get_dma_chan(this); > + struct mil *mil = &this->mil; > + int chip = mil->current_chip; > + u32 pio[6]; > + > + /* [1] Wait for the chip to report ready. */ > + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; > + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; > + > + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) > + | BM_GPMI_CTRL0_WORD_LENGTH > + | BF_GPMI_CTRL0_CS(chip, this) > + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) > + | BF_GPMI_CTRL0_ADDRESS(address) > + | BF_GPMI_CTRL0_XFER_COUNT(0); > + pio[1] = 0; > + desc = channel->device->device_prep_slave_sg(channel, > + (struct scatterlist *)pio, 2, DMA_NONE, 0); > + if (!desc) { > + pr_info("step 1 error\n"); > + return -1; > + } > + > + /* [2] Enable the BCH block and read. */ > + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ; > + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; > + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE; > + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE > + | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; > + > + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) > + | BM_GPMI_CTRL0_WORD_LENGTH > + | BF_GPMI_CTRL0_CS(chip, this) > + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) > + | BF_GPMI_CTRL0_ADDRESS(address) > + | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size_in_bytes); > + > + pio[1] = 0; > + pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC > + | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) > + | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); > + pio[3] = geo->page_size_in_bytes; > + pio[4] = payload; > + pio[5] = auxiliary; > + desc = channel->device->device_prep_slave_sg(channel, > + (struct scatterlist *)pio, > + ARRAY_SIZE(pio), DMA_NONE, 1); > + if (!desc) { > + pr_info("step 2 error\n"); > + return -1; > + } > + > + /* [3] Disable the BCH block */ > + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; > + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; > + > + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) > + | BM_GPMI_CTRL0_WORD_LENGTH > + | BF_GPMI_CTRL0_CS(chip, this) > + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) > + | BF_GPMI_CTRL0_ADDRESS(address) > + | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size_in_bytes); > + pio[1] = 0; > + desc = channel->device->device_prep_slave_sg(channel, > + (struct scatterlist *)pio, 2, DMA_NONE, 1); > + if (!desc) { > + pr_info("step 3 error\n"); > + return -1; > + } > + > + /* [4] submit the DMA */ > + set_dma_type(this, DMA_FOR_READ_ECC_PAGE); > + return start_dma_with_bch_irq(this, desc); > +} > diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h > b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h new file mode 100644 > index 0000000..c0381cd > --- /dev/null > +++ b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h > @@ -0,0 +1,174 @@ > +/* > + * Freescale GPMI NAND Flash Driver > + * > + * Copyright 2008-2011 Freescale Semiconductor, Inc. > + * Copyright 2008 Embedded Alley Solutions, Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License along > + * with this program; if not, write to the Free Software Foundation, Inc., > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > + */ > +#ifndef __GPMI_NAND_GPMI_REGS_H > +#define __GPMI_NAND_GPMI_REGS_H > + > +/*======================================================================== > ====*/ +#define HW_GPMI_CTRL0 0x00000000 > +#define HW_GPMI_CTRL0_SET 0x00000004 > +#define HW_GPMI_CTRL0_CLR 0x00000008 > +#define HW_GPMI_CTRL0_TOG 0x0000000c > + > +#define BP_GPMI_CTRL0_COMMAND_MODE 24 > +#define BM_GPMI_CTRL0_COMMAND_MODE (3 << BP_GPMI_CTRL0_COMMAND_MODE) > +#define BF_GPMI_CTRL0_COMMAND_MODE(v) \ > + (((v) << BP_GPMI_CTRL0_COMMAND_MODE) & BM_GPMI_CTRL0_COMMAND_MODE) > +#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0 > +#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1 > +#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2 > +#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3 > + > +#define BM_GPMI_CTRL0_WORD_LENGTH (1 << 23) > +#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0 > +#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1 > + > +/* > + * Difference in LOCK_CS between imx23 and imx28 : > + * This bit may impact the _POWER_ consumption. So some chips > + * do not set it. > + */ > +#define MX23_BP_GPMI_CTRL0_LOCK_CS 22 > +#define MX28_BP_GPMI_CTRL0_LOCK_CS 27 > +#define LOCK_CS_ENABLE 0x1 > +#define BF_GPMI_CTRL0_LOCK_CS(v, x) 0x0 > + > +/* Difference in CS between imx23 and imx28 */ > +#define BP_GPMI_CTRL0_CS 20 > +#define MX23_BM_GPMI_CTRL0_CS (3 << BP_GPMI_CTRL0_CS) > +#define MX28_BM_GPMI_CTRL0_CS (7 << BP_GPMI_CTRL0_CS) > +#define BF_GPMI_CTRL0_CS(v, x) (((v) << BP_GPMI_CTRL0_CS) & \ > + (GPMI_IS_MX23((x)) \ > + ? MX23_BM_GPMI_CTRL0_CS \ > + : MX28_BM_GPMI_CTRL0_CS)) > + > +#define BP_GPMI_CTRL0_ADDRESS 17 > +#define BM_GPMI_CTRL0_ADDRESS (3 << BP_GPMI_CTRL0_ADDRESS) > +#define BF_GPMI_CTRL0_ADDRESS(v) \ > + (((v) << BP_GPMI_CTRL0_ADDRESS) & BM_GPMI_CTRL0_ADDRESS) > +#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0 > +#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1 > +#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2 > + > +#define BM_GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16) > +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0 > +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1 > + > +#define BP_GPMI_CTRL0_XFER_COUNT 0 > +#define BM_GPMI_CTRL0_XFER_COUNT (0xffff << BP_GPMI_CTRL0_XFER_COUNT) > +#define BF_GPMI_CTRL0_XFER_COUNT(v) \ > + (((v) << BP_GPMI_CTRL0_XFER_COUNT) & BM_GPMI_CTRL0_XFER_COUNT) > + > +/*======================================================================== > ====*/ +#define HW_GPMI_COMPARE 0x00000010 > +/*======================================================================== > ====*/ +#define HW_GPMI_ECCCTRL 0x00000020 > +#define HW_GPMI_ECCCTRL_SET 0x00000024 > +#define HW_GPMI_ECCCTRL_CLR 0x00000028 > +#define HW_GPMI_ECCCTRL_TOG 0x0000002c > + > +#define BP_GPMI_ECCCTRL_ECC_CMD 13 > +#define BM_GPMI_ECCCTRL_ECC_CMD (3 << BP_GPMI_ECCCTRL_ECC_CMD) > +#define BF_GPMI_ECCCTRL_ECC_CMD(v) \ > + (((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD) > +#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE 0x0 > +#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE 0x1 > + > +#define BM_GPMI_ECCCTRL_ENABLE_ECC (1 << 12) > +#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1 > +#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0 > + > +#define BP_GPMI_ECCCTRL_BUFFER_MASK 0 > +#define BM_GPMI_ECCCTRL_BUFFER_MASK (0x1ff << BP_GPMI_ECCCTRL_BUFFER_MASK) > +#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \ > + (((v) << BP_GPMI_ECCCTRL_BUFFER_MASK) & BM_GPMI_ECCCTRL_BUFFER_MASK) > +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100 > +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF > + > +/*======================================================================== > ====*/ +#define HW_GPMI_ECCCOUNT 0x00000030 > +#define HW_GPMI_PAYLOAD 0x00000040 > +#define HW_GPMI_AUXILIARY 0x00000050 > +/*======================================================================== > ====*/ +#define HW_GPMI_CTRL1 0x00000060 > +#define HW_GPMI_CTRL1_SET 0x00000064 > +#define HW_GPMI_CTRL1_CLR 0x00000068 > +#define HW_GPMI_CTRL1_TOG 0x0000006c > + > +#define BM_GPMI_CTRL1_BCH_MODE (1 << 18) > + > +#define BP_GPMI_CTRL1_DLL_ENABLE 17 > +#define BM_GPMI_CTRL1_DLL_ENABLE (1 << BP_GPMI_CTRL1_DLL_ENABLE) > + > +#define BP_GPMI_CTRL1_HALF_PERIOD 16 > +#define BM_GPMI_CTRL1_HALF_PERIOD (1 << BP_GPMI_CTRL1_HALF_PERIOD) > + > +#define BP_GPMI_CTRL1_RDN_DELAY 12 > +#define BM_GPMI_CTRL1_RDN_DELAY (0xf << BP_GPMI_CTRL1_RDN_DELAY) > +#define BF_GPMI_CTRL1_RDN_DELAY(v) \ > + (((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY) > + > +#define BM_GPMI_CTRL1_DEV_RESET (1 << 3) > +#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0 > +#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1 > + > +#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2) > +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0 > +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1 > + > +#define BM_GPMI_CTRL1_CAMERA_MODE (1 << 1) > +#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0 > +#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1 > + > +#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0) > + > +/*======================================================================== > ====*/ +#define HW_GPMI_TIMING0 0x00000070 > + > +#define BP_GPMI_TIMING0_ADDRESS_SETUP 16 > +#define BM_GPMI_TIMING0_ADDRESS_SETUP (0xff << > BP_GPMI_TIMING0_ADDRESS_SETUP) +#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \ > + (((v) << BP_GPMI_TIMING0_ADDRESS_SETUP) & BM_GPMI_TIMING0_ADDRESS_SETUP) > + > +#define BP_GPMI_TIMING0_DATA_HOLD 8 > +#define BM_GPMI_TIMING0_DATA_HOLD (0xff << BP_GPMI_TIMING0_DATA_HOLD) > +#define BF_GPMI_TIMING0_DATA_HOLD(v) \ > + (((v) << BP_GPMI_TIMING0_DATA_HOLD) & BM_GPMI_TIMING0_DATA_HOLD) > + > +#define BP_GPMI_TIMING0_DATA_SETUP 0 > +#define BM_GPMI_TIMING0_DATA_SETUP (0xff << BP_GPMI_TIMING0_DATA_SETUP) > +#define BF_GPMI_TIMING0_DATA_SETUP(v) \ > + (((v) << BP_GPMI_TIMING0_DATA_SETUP) & BM_GPMI_TIMING0_DATA_SETUP) > + > +/*======================================================================== > ====*/ +#define HW_GPMI_TIMING1 0x00000080 > +#define HW_GPMI_TIMING2 0x00000090 > +#define HW_GPMI_DATA 0x000000a0 > +/*============================ MX28 uses this to detect READY > ================*/ +#define HW_GPMI_STAT 0x000000b0 > +#define MX28_BP_GPMI_STAT_READY_BUSY 24 > +#define MX28_BM_GPMI_STAT_READY_BUSY (0xff << > MX28_BP_GPMI_STAT_READY_BUSY) +#define MX28_BF_GPMI_STAT_READY_BUSY(v) \ > + (((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY) > +/*============================ MX23 uses this to detect READY > ================*/ +#define HW_GPMI_DEBUG 0x000000c0 > +#define MX23_BP_GPMI_DEBUG_READY0 28 > +#define MX23_BM_GPMI_DEBUG_READY0 (1 << MX23_BP_GPMI_DEBUG_READY0) > +#endif
Hi, > On Wednesday, August 17, 2011 01:50:27 PM Huang Shijie wrote: >> bch-regs.h : registers file for BCH module >> gpmi-regs.h: registers file for GPMI module >> gpmi-lib.c: helper functions library. >> >> Signed-off-by: Huang Shijie<b32955@freescale.com> >> --- >> drivers/mtd/nand/gpmi-nand/bch-regs.h | 88 +++ >> drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 978 >> ++++++++++++++++++++++++++++++++ drivers/mtd/nand/gpmi-nand/gpmi-regs.h | >> 174 ++++++ >> 3 files changed, 1240 insertions(+), 0 deletions(-) >> create mode 100644 drivers/mtd/nand/gpmi-nand/bch-regs.h >> create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-lib.c >> create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-regs.h >> >> diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h >> b/drivers/mtd/nand/gpmi-nand/bch-regs.h new file mode 100644 >> index 0000000..cec1dfa >> --- /dev/null >> +++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h >> @@ -0,0 +1,88 @@ >> +/* >> + * Freescale GPMI NAND Flash Driver >> + * >> + * Copyright 2008-2011 Freescale Semiconductor, Inc. >> + * Copyright 2008 Embedded Alley Solutions, Inc. >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License as published by >> + * the Free Software Foundation; either version 2 of the License, or >> + * (at your option) any later version. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License along >> + * with this program; if not, write to the Free Software Foundation, Inc., >> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >> + */ >> +#ifndef __GPMI_NAND_BCH_REGS_H >> +#define __GPMI_NAND_BCH_REGS_H >> + > Aaargh, please remove these separators. > ok. >> +/*======================================================================== >> ====*/ +#define HW_BCH_CTRL 0x00000000 >> +#define HW_BCH_CTRL_SET 0x00000004 >> +#define HW_BCH_CTRL_CLR 0x00000008 >> +#define HW_BCH_CTRL_TOG 0x0000000c >> + >> +#define BM_BCH_CTRL_COMPLETE_IRQ_EN (1<< 8) >> +#define BM_BCH_CTRL_COMPLETE_IRQ (1<< 0) >> + >> +/*======================================================================== >> ====*/ +#define HW_BCH_STATUS0 0x00000010 >> +#define HW_BCH_MODE 0x00000020 >> +#define HW_BCH_ENCODEPTR 0x00000030 >> +#define HW_BCH_DATAPTR 0x00000040 >> +#define HW_BCH_METAPTR 0x00000050 >> +#define HW_BCH_LAYOUTSELECT 0x00000070 >> + >> +/*======================================================================== >> ====*/ +#define HW_BCH_FLASH0LAYOUT0 0x00000080 >> + >> +#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24 >> +#define BM_BCH_FLASH0LAYOUT0_NBLOCKS (0xff<< >> BP_BCH_FLASH0LAYOUT0_NBLOCKS) +#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \ >> + (((v)<< BP_BCH_FLASH0LAYOUT0_NBLOCKS)& BM_BCH_FLASH0LAYOUT0_NBLOCKS) >> + >> +#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16 >> +#define BM_BCH_FLASH0LAYOUT0_META_SIZE (0xff<< >> BP_BCH_FLASH0LAYOUT0_META_SIZE) +#define >> BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \ >> + (((v)<< BP_BCH_FLASH0LAYOUT0_META_SIZE)\ >> + & BM_BCH_FLASH0LAYOUT0_META_SIZE) >> + >> +#define BP_BCH_FLASH0LAYOUT0_ECC0 12 >> +#define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf<< BP_BCH_FLASH0LAYOUT0_ECC0) >> +#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \ >> + (((v)<< BP_BCH_FLASH0LAYOUT0_ECC0)& BM_BCH_FLASH0LAYOUT0_ECC0) >> + >> +#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 >> +#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ >> + (0xfff<< BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) >> +#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \ >> + (((v)<< BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)\ >> + & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) >> + >> +/*======================================================================== >> ====*/ +#define HW_BCH_FLASH0LAYOUT1 0x00000090 >> + >> +#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16 >> +#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE \ >> + (0xffff<< BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) >> +#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \ >> + (((v)<< BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) \ >> + & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE) >> + >> +#define BP_BCH_FLASH0LAYOUT1_ECCN 12 >> +#define BM_BCH_FLASH0LAYOUT1_ECCN (0xf<< BP_BCH_FLASH0LAYOUT1_ECCN) >> +#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \ >> + (((v)<< BP_BCH_FLASH0LAYOUT1_ECCN)& BM_BCH_FLASH0LAYOUT1_ECCN) >> + >> +#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 >> +#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ >> + (0xfff<< BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) >> +#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \ >> + (((v)<< BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ >> + & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) >> +#endif >> diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c >> b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c new file mode 100644 >> index 0000000..1368842 >> --- /dev/null >> +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c >> @@ -0,0 +1,978 @@ >> +/* >> + * Freescale GPMI NAND Flash Driver >> + * >> + * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. >> + * Copyright (C) 2008 Embedded Alley Solutions, Inc. >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License as published by >> + * the Free Software Foundation; either version 2 of the License, or >> + * (at your option) any later version. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License along >> + * with this program; if not, write to the Free Software Foundation, Inc., >> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >> + */ >> +#include "gpmi-nand.h" >> +#include "gpmi-regs.h" >> +#include "bch-regs.h" >> + >> +struct timing_threshod timing_default_threshold = { >> + .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP>> >> + BP_GPMI_TIMING0_DATA_SETUP), >> + .internal_data_setup_in_ns = 0, >> + .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY>> >> + BP_GPMI_CTRL1_RDN_DELAY), >> + .max_dll_clock_period_in_ns = 32, >> + .max_dll_delay_in_ns = 16, >> +}; >> + >> +int gpmi_init(struct gpmi_nand_data *this) >> +{ >> + struct resources *r =&this->resources; >> + int ret; >> + >> + ret = clk_enable(r->clock); >> + if (ret) >> + goto err_out; >> + ret = mxs_reset_block(r->gpmi_regs); >> + if (ret) >> + goto err_out; >> + >> + /* Choose NAND mode. */ >> + writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR); >> + >> + /* Set the IRQ polarity. */ >> + writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, >> + r->gpmi_regs + HW_GPMI_CTRL1_SET); >> + >> + /* Disable Write-Protection. */ >> + writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET); >> + >> + /* Select BCH ECC. */ >> + writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); >> + >> + clk_disable(r->clock); >> + return 0; >> +err_out: >> + return ret; >> +} >> + >> +/* This is very useful! */ > Really? Cool, what for ? > btw. this should really be enclosed in some #ifdef debug or whatnot. > Yes, it shows the register contents when DMA timeout occurs. this routine is called only when error occurs. >> +void gpmi_show_regs(struct gpmi_nand_data *this) >> +{ >> + struct resources *r =&this->resources; >> + u32 reg; >> + int i; >> + int n; >> + >> + n = HW_GPMI_DEBUG / 0x10 + 1; >> + >> + pr_info("-------------- Show GPMI registers ----------\n"); >> + for (i = 0; i<= n; i++) { >> + reg = readl(r->gpmi_regs + i * 0x10); >> + pr_info("offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); >> + } >> + pr_info("-------------- Show GPMI registers end ----------\n"); >> +} >> + >> +/* Configures the geometry for BCH. */ >> +int bch_set_geometry(struct gpmi_nand_data *this) >> +{ >> + struct resources *r =&this->resources; >> + struct bch_geometry *bch_geo =&this->bch_geometry; >> + unsigned int block_count; >> + unsigned int block_size; >> + unsigned int metadata_size; >> + unsigned int ecc_strength; >> + unsigned int page_size; >> + int ret; >> + >> + if (common_nfc_set_geometry(this)) >> + return !0; >> + >> + block_count = bch_geo->ecc_chunk_count - 1; >> + block_size = bch_geo->ecc_chunk_size_in_bytes; >> + metadata_size = bch_geo->metadata_size_in_bytes; >> + ecc_strength = bch_geo->ecc_strength>> 1; >> + page_size = bch_geo->page_size_in_bytes; >> + >> + ret = clk_enable(r->clock); >> + if (ret) >> + goto err_out; >> + ret = mxs_reset_block(r->bch_regs); >> + if (ret) >> + goto err_out; >> + >> + /* Configure layout 0. */ >> + writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) >> + | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) >> + | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) >> + | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size), >> + r->bch_regs + HW_BCH_FLASH0LAYOUT0); >> + >> + writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) >> + | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) >> + | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size), >> + r->bch_regs + HW_BCH_FLASH0LAYOUT1); >> + >> + /* Set *all* chip selects to use layout 0. */ >> + writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT); >> + >> + /* Enable interrupts. */ >> + writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, >> + r->bch_regs + HW_BCH_CTRL_SET); >> + >> + clk_disable(r->clock); >> + return 0; >> +err_out: >> + return ret; >> +} >> + >> +/* >> + * ns_to_cycles - Converts time in nanoseconds to cycles. >> + * >> + * @ntime: The time, in nanoseconds. >> + * @period: The cycle period, in nanoseconds. >> + * @min: The minimum allowable number of cycles. >> + */ >> +static unsigned int ns_to_cycles(unsigned int time, >> + unsigned int period, unsigned int min) >> +{ >> + unsigned int k; >> + >> + /* >> + * Compute the minimum number of cycles that entirely contain the >> + * given time. >> + */ >> + k = (time + period - 1) / period; >> + return max(k, min); >> +} >> + >> +/* >> + * gpmi_compute_hardware_timing - Apply timing to current hardware >> conditions. + * >> + * @this: Per-device data. >> + * @hardware_timing: A pointer to a hardware timing structure that will >> receive + * the results of our calculations. >> + */ >> +static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, >> + struct gpmi_nfc_hardware_timing *hw) >> +{ >> + struct gpmi_nand_platform_data *pdata = this->pdata; >> + struct timing_threshod *nfc =&timing_default_threshold; >> + struct nand_chip *nand =&this->mil.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; >> + unsigned int dll_delay_shift; >> + unsigned int max_sample_delay_in_ns; >> + unsigned int address_setup_in_cycles; >> + unsigned int data_setup_in_ns; >> + unsigned int data_setup_in_cycles; >> + unsigned int data_hold_in_cycles; >> + int ideal_sample_delay_in_ns; >> + unsigned int sample_delay_factor; >> + int tEYE; >> + unsigned int min_prop_delay_in_ns = pdata->min_prop_delay_in_ns; >> + unsigned int max_prop_delay_in_ns = pdata->max_prop_delay_in_ns; >> + >> + /* >> + * If there are multiple chips, we need to relax the timings to allow >> + * for signal distortion due to higher capacitance. >> + */ >> + if (nand->numchips> 2) { >> + target.data_setup_in_ns += 10; >> + target.data_hold_in_ns += 10; >> + target.address_setup_in_ns += 10; >> + } else if (nand->numchips> 1) { >> + target.data_setup_in_ns += 5; >> + target.data_hold_in_ns += 5; >> + 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. */ >> + clock_frequency_in_hz = nfc->clock_frequency_in_hz; >> + clock_period_in_ns = 1000000000 / clock_frequency_in_hz; >> + >> + /* >> + * The NFC quantizes setup and hold parameters in terms of clock cycles. >> + * Here, we quantize the setup and hold timing parameters to the >> + * next-highest clock period to make sure we apply at least the >> + * specified times. >> + * >> + * For data setup and data hold, the hardware interprets a value of zero >> + * as the largest possible delay. This is not what's intended by a zero >> + * in the input parameter, so we impose a minimum of one cycle. >> + */ >> + data_setup_in_cycles = ns_to_cycles(target.data_setup_in_ns, >> + clock_period_in_ns, 1); >> + data_hold_in_cycles = ns_to_cycles(target.data_hold_in_ns, >> + clock_period_in_ns, 1); >> + address_setup_in_cycles = ns_to_cycles(target.address_setup_in_ns, >> + clock_period_in_ns, 0); >> + >> + /* >> + * The clock's period affects the sample delay in a number of ways: >> + * >> + * (1) The NFC HAL tells us the maximum clock period the sample delay >> + * DLL can tolerate. If the clock period is greater than half that >> + * maximum, we must configure the DLL to be driven by half periods. >> + * >> + * (2) We need to convert from an ideal sample delay, in ns, to a >> + * "sample delay factor," which the NFC uses. This factor depends on >> + * whether we're driving the DLL with full or half periods. >> + * Paraphrasing the reference manual: >> + * >> + * AD = SDF x 0.125 x RP >> + * >> + * where: >> + * >> + * AD is the applied delay, in ns. >> + * SDF is the sample delay factor, which is dimensionless. >> + * RP is the reference period, in ns, which is a full clock period >> + * if the DLL is being driven by full periods, or half that if >> + * the DLL is being driven by half periods. >> + * >> + * Let's re-arrange this in a way that's more useful to us: >> + * >> + * 8 >> + * SDF = AD x ---- >> + * RP >> + * >> + * The reference period is either the clock period or half that, so this >> + * is: >> + * >> + * 8 AD x DDF >> + * SDF = AD x ----- = -------- >> + * f x P P >> + * >> + * where: >> + * >> + * f is 1 or 1/2, depending on how we're driving the DLL. >> + * P is the clock period. >> + * DDF is the DLL Delay Factor, a dimensionless value that >> + * incorporates all the constants in the conversion. >> + * >> + * DDF will be either 8 or 16, both of which are powers of two. We can >> + * reduce the cost of this conversion by using bit shifts instead of >> + * multiplication or division. Thus: >> + * >> + * AD<< DDS >> + * SDF = --------- >> + * P >> + * >> + * or >> + * >> + * AD = (SDF>> DDS) x P >> + * >> + * where: >> + * >> + * DDS is the DLL Delay Shift, the logarithm to base 2 of the DDF. >> + */ >> + if (clock_period_in_ns> (nfc->max_dll_clock_period_in_ns>> 1)) { >> + dll_use_half_periods = true; >> + dll_delay_shift = 3 + 1; >> + } else { >> + dll_use_half_periods = false; >> + dll_delay_shift = 3; >> + } >> + >> + /* >> + * Compute the maximum sample delay the NFC allows, under current >> + * conditions. If the clock is running too slowly, no sample delay is >> + * possible. >> + */ >> + if (clock_period_in_ns> nfc->max_dll_clock_period_in_ns) >> + max_sample_delay_in_ns = 0; >> + else { >> + /* >> + * Compute the delay implied by the largest sample delay factor >> + * the NFC allows. >> + */ >> + max_sample_delay_in_ns = >> + (nfc->max_sample_delay_factor * clock_period_in_ns)>> >> + dll_delay_shift; >> + >> + /* >> + * Check if the implied sample delay larger than the NFC >> + * actually allows. >> + */ >> + if (max_sample_delay_in_ns> nfc->max_dll_delay_in_ns) >> + max_sample_delay_in_ns = nfc->max_dll_delay_in_ns; >> + } >> + >> + /* >> + * 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. >> + */ >> + max_prop_delay_in_ns += nfc->internal_data_setup_in_ns; >> + >> + /* >> + * Earlier, we computed the number of clock cycles required to satisfy >> + * the data setup time. Now, we need to know the actual nanoseconds. >> + */ >> + data_setup_in_ns = clock_period_in_ns * data_setup_in_cycles; >> + >> + /* >> + * Compute tEYE, the width of the data eye when reading from the NAND >> + * Flash. The eye width is fundamentally determined by the data setup >> + * time, perturbed by propagation delays and some characteristics of the >> + * NAND Flash device. >> + * >> + * start of the eye = max_prop_delay + tREA >> + * end of the eye = min_prop_delay + tRHOH + data_setup >> + */ >> + tEYE = (int)min_prop_delay_in_ns + (int)target.tRHOH_in_ns + >> + (int)data_setup_in_ns; >> + >> + tEYE -= (int)max_prop_delay_in_ns + (int)target.tREA_in_ns; >> + >> + /* >> + * The eye must be open. If it's not, we can try to open it by >> + * increasing its main forcer, the data setup time. >> + * >> + * In each iteration of the following loop, we increase the data setup >> + * time by a single clock cycle. We do this until either the eye is >> + * open or we run into NFC limits. >> + */ >> + while ((tEYE<= 0)&& >> + (data_setup_in_cycles< nfc->max_data_setup_cycles)) { >> + /* Give a cycle to data setup. */ >> + data_setup_in_cycles++; >> + /* Synchronize the data setup time with the cycles. */ >> + data_setup_in_ns += clock_period_in_ns; >> + /* Adjust tEYE accordingly. */ >> + tEYE += clock_period_in_ns; >> + } >> + >> + /* >> + * When control arrives here, the eye is open. The ideal time to sample >> + * the data is in the center of the eye: >> + * >> + * end of the eye + start of the eye >> + * --------------------------------- - data_setup >> + * 2 >> + * >> + * After some algebra, this simplifies to the code immediately below. >> + */ >> + ideal_sample_delay_in_ns = >> + ((int)max_prop_delay_in_ns + >> + (int)target.tREA_in_ns + >> + (int)min_prop_delay_in_ns + >> + (int)target.tRHOH_in_ns - >> + (int)data_setup_in_ns)>> 1; >> + >> + /* >> + * The following figure illustrates some aspects of a NAND Flash read: >> + * >> + * >> + * __ _____________________________________ >> + * RDN \_________________/ >> + * >> + *<---- tEYE -----> >> + * /-----------------\ >> + * Read Data ----------------------------< >--------- >> + * \-----------------/ >> + * ^ ^ ^ ^ >> + * | | | | >> + * |<--Data Setup -->|<--Delay Time -->| | >> + * | | | | >> + * | | | >> + * | |<-- Quantized Delay Time -->| >> + * | | | >> + * >> + * >> + * We have some issues we must now address: >> + * >> + * (1) The *ideal* sample delay time must not be negative. If it is, we >> + * jam it to zero. >> + * >> + * (2) The *ideal* sample delay time must not be greater than that >> + * allowed by the NFC. If it is, we can increase the data setup >> + * time, which will reduce the delay between the end of the data >> + * setup and the center of the eye. It will also make the eye >> + * larger, which might help with the next issue... >> + * >> + * (3) The *quantized* sample delay time must not fall either before the >> + * eye opens or after it closes (the latter is the problem >> + * illustrated in the above figure). >> + */ >> + >> + /* Jam a negative ideal sample delay to zero. */ >> + if (ideal_sample_delay_in_ns< 0) >> + ideal_sample_delay_in_ns = 0; >> + >> + /* >> + * Extend the data setup as needed to reduce the ideal sample delay >> + * below the maximum permitted by the NFC. >> + */ >> + while ((ideal_sample_delay_in_ns> max_sample_delay_in_ns)&& >> + (data_setup_in_cycles< nfc->max_data_setup_cycles)) { >> + >> + /* Give a cycle to data setup. */ >> + data_setup_in_cycles++; >> + /* Synchronize the data setup time with the cycles. */ >> + data_setup_in_ns += clock_period_in_ns; >> + /* Adjust tEYE accordingly. */ >> + tEYE += clock_period_in_ns; >> + >> + /* >> + * Decrease the ideal sample delay by one half cycle, to keep it >> + * in the middle of the eye. >> + */ >> + ideal_sample_delay_in_ns -= (clock_period_in_ns>> 1); >> + >> + /* Jam a negative ideal sample delay to zero. */ >> + if (ideal_sample_delay_in_ns< 0) >> + ideal_sample_delay_in_ns = 0; >> + } >> + >> + /* >> + * Compute the sample delay factor that corresponds to the ideal sample >> + * delay. If the result is too large, then use the maximum allowed >> + * 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; >> + >> + /* >> + * These macros conveniently encapsulate a computation we'll use to >> + * continuously evaluate whether or not the data sample delay is inside >> + * the eye. >> + */ >> + #define IDEAL_DELAY ((int) ideal_sample_delay_in_ns) >> + >> + #define QUANTIZED_DELAY \ >> + ((int) ((sample_delay_factor * clock_period_in_ns)>> \ >> + dll_delay_shift)) >> + >> + #define DELAY_ERROR (abs(QUANTIZED_DELAY - IDEAL_DELAY)) >> + >> + #define SAMPLE_IS_NOT_WITHIN_THE_EYE (DELAY_ERROR> (tEYE>> 1)) >> + >> + /* >> + * While the quantized sample time falls outside the eye, reduce the >> + * sample delay or extend the data setup to move the sampling point back >> + * toward the eye. Do not allow the number of data setup cycles to >> + * exceed the maximum allowed by the NFC. >> + */ >> + while (SAMPLE_IS_NOT_WITHIN_THE_EYE&& >> + (data_setup_in_cycles< nfc->max_data_setup_cycles)) { >> + /* >> + * If control arrives here, the quantized sample delay falls >> + * outside the eye. Check if it's before the eye opens, or after >> + * the eye closes. >> + */ >> + if (QUANTIZED_DELAY> IDEAL_DELAY) { >> + /* >> + * If control arrives here, the quantized sample delay >> + * falls after the eye closes. Decrease the quantized >> + * delay time and then go back to re-evaluate. >> + */ >> + if (sample_delay_factor != 0) >> + sample_delay_factor--; >> + continue; >> + } >> + >> + /* >> + * If control arrives here, the quantized sample delay falls >> + * before the eye opens. Shift the sample point by increasing >> + * data setup time. This will also make the eye larger. >> + */ >> + >> + /* Give a cycle to data setup. */ >> + data_setup_in_cycles++; >> + /* Synchronize the data setup time with the cycles. */ >> + data_setup_in_ns += clock_period_in_ns; >> + /* Adjust tEYE accordingly. */ >> + tEYE += clock_period_in_ns; >> + >> + /* >> + * Decrease the ideal sample delay by one half cycle, to keep it >> + * in the middle of the eye. >> + */ >> + ideal_sample_delay_in_ns -= (clock_period_in_ns>> 1); >> + >> + /* ...and one less period for the delay time. */ >> + ideal_sample_delay_in_ns -= clock_period_in_ns; >> + >> + /* Jam a negative ideal sample delay to zero. */ >> + if (ideal_sample_delay_in_ns< 0) >> + ideal_sample_delay_in_ns = 0; >> + >> + /* >> + * We have a new ideal sample delay, so re-compute the quantized >> + * delay. >> + */ >> + 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; >> + } >> + >> + /* 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; >> + hw->use_half_periods = dll_use_half_periods; >> + hw->sample_delay_factor = sample_delay_factor; >> + >> + /* Return success. */ >> + return 0; >> +} >> + >> +/* Begin the I/O */ >> +void gpmi_begin(struct gpmi_nand_data *this) >> +{ >> + struct resources *r =&this->resources; >> + struct timing_threshod *nfc =&timing_default_threshold; >> + unsigned char *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 = clk_enable(r->clock); >> + if (ret) { >> + pr_info("We failed in enable the clk\n"); >> + goto err_out; >> + } >> + >> + /* set ready/busy timeout */ >> + writel(0x500<< 16, gpmi_regs + HW_GPMI_TIMING1); >> + >> + /* Get the timing information we need. */ >> + nfc->clock_frequency_in_hz = clk_get_rate(r->clock); >> + clock_period_in_ns = 1000000000 / nfc->clock_frequency_in_hz; >> + >> + gpmi_nfc_compute_hardware_timing(this,&hw); >> + >> + /* Set up all the simple timing parameters. */ >> + 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); >> + >> + /* >> + * HEY - PAY ATTENTION! > Please fix this comment and pay attention to other comments ;-) > ok :) thanks Huang Shijie
On Monday, August 22, 2011 07:03:56 AM Huang Shijie wrote: > Hi, > > > On Wednesday, August 17, 2011 01:50:27 PM Huang Shijie wrote: > >> bch-regs.h : registers file for BCH module > >> gpmi-regs.h: registers file for GPMI module > >> gpmi-lib.c: helper functions library. > >> > >> Signed-off-by: Huang Shijie<b32955@freescale.com> > >> --- [...] > >> +err_out: > >> + return ret; > >> +} > >> + > >> +/* This is very useful! */ > > > > Really? Cool, what for ? > > btw. this should really be enclosed in some #ifdef debug or whatnot. > > Yes, it shows the register contents when DMA timeout occurs. > this routine is called only when error occurs. > Hi, I'd expect exactly this explanation to be in the comment ;-) Cheers
Hi, On Mon, Aug 22, 2011 at 7:20 AM, Marek Vasut <marek.vasut@gmail.com> wrote: > On Monday, August 22, 2011 07:03:56 AM Huang Shijie wrote: >> Hi, >> >> > On Wednesday, August 17, 2011 01:50:27 PM Huang Shijie wrote: >> >> bch-regs.h : registers file for BCH module >> >> gpmi-regs.h: registers file for GPMI module >> >> gpmi-lib.c: helper functions library. >> >> >> >> Signed-off-by: Huang Shijie<b32955@freescale.com> >> >> --- > > [...] > >> >> +err_out: >> >> + Â return ret; >> >> +} >> >> + >> >> +/* This is very useful! */ >> > >> > Really? Cool, what for ? >> > btw. this should really be enclosed in some #ifdef debug or whatnot. >> >> Yes, it shows the register contents when DMA timeout occurs. >> this routine is called only when error occurs. >> > Hi, > > I'd expect exactly this explanation to be in the comment ;-) > ok, i will do it. thanks Huang Shijie > Cheers >
diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h new file mode 100644 index 0000000..cec1dfa --- /dev/null +++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h @@ -0,0 +1,88 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef __GPMI_NAND_BCH_REGS_H +#define __GPMI_NAND_BCH_REGS_H + +/*============================================================================*/ +#define HW_BCH_CTRL 0x00000000 +#define HW_BCH_CTRL_SET 0x00000004 +#define HW_BCH_CTRL_CLR 0x00000008 +#define HW_BCH_CTRL_TOG 0x0000000c + +#define BM_BCH_CTRL_COMPLETE_IRQ_EN (1 << 8) +#define BM_BCH_CTRL_COMPLETE_IRQ (1 << 0) + +/*============================================================================*/ +#define HW_BCH_STATUS0 0x00000010 +#define HW_BCH_MODE 0x00000020 +#define HW_BCH_ENCODEPTR 0x00000030 +#define HW_BCH_DATAPTR 0x00000040 +#define HW_BCH_METAPTR 0x00000050 +#define HW_BCH_LAYOUTSELECT 0x00000070 + +/*============================================================================*/ +#define HW_BCH_FLASH0LAYOUT0 0x00000080 + +#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH0LAYOUT0_NBLOCKS (0xff << BP_BCH_FLASH0LAYOUT0_NBLOCKS) +#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_NBLOCKS) & BM_BCH_FLASH0LAYOUT0_NBLOCKS) + +#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH0LAYOUT0_META_SIZE (0xff << BP_BCH_FLASH0LAYOUT0_META_SIZE) +#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_META_SIZE)\ + & BM_BCH_FLASH0LAYOUT0_META_SIZE) + +#define BP_BCH_FLASH0LAYOUT0_ECC0 12 +#define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0) +#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) & BM_BCH_FLASH0LAYOUT0_ECC0) + +#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ + (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)\ + & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) + +/*============================================================================*/ +#define HW_BCH_FLASH0LAYOUT1 0x00000090 + +#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE \ + (0xffff << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) +#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \ + (((v) << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) \ + & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE) + +#define BP_BCH_FLASH0LAYOUT1_ECCN 12 +#define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN) +#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \ + (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) & BM_BCH_FLASH0LAYOUT1_ECCN) + +#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ + (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \ + (((v) << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#endif diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c new file mode 100644 index 0000000..1368842 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -0,0 +1,978 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "gpmi-nand.h" +#include "gpmi-regs.h" +#include "bch-regs.h" + +struct timing_threshod timing_default_threshold = { + .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >> + BP_GPMI_TIMING0_DATA_SETUP), + .internal_data_setup_in_ns = 0, + .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >> + BP_GPMI_CTRL1_RDN_DELAY), + .max_dll_clock_period_in_ns = 32, + .max_dll_delay_in_ns = 16, +}; + +int gpmi_init(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + int ret; + + ret = clk_enable(r->clock); + if (ret) + goto err_out; + ret = mxs_reset_block(r->gpmi_regs); + if (ret) + goto err_out; + + /* Choose NAND mode. */ + writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* Set the IRQ polarity. */ + writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, + r->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Disable Write-Protection. */ + writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Select BCH ECC. */ + writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); + + clk_disable(r->clock); + return 0; +err_out: + return ret; +} + +/* This is very useful! */ +void gpmi_show_regs(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + u32 reg; + int i; + int n; + + n = HW_GPMI_DEBUG / 0x10 + 1; + + pr_info("-------------- Show GPMI registers ----------\n"); + for (i = 0; i <= n; i++) { + reg = readl(r->gpmi_regs + i * 0x10); + pr_info("offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); + } + pr_info("-------------- Show GPMI registers end ----------\n"); +} + +/* Configures the geometry for BCH. */ +int bch_set_geometry(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + struct bch_geometry *bch_geo = &this->bch_geometry; + unsigned int block_count; + unsigned int block_size; + unsigned int metadata_size; + unsigned int ecc_strength; + unsigned int page_size; + int ret; + + if (common_nfc_set_geometry(this)) + return !0; + + block_count = bch_geo->ecc_chunk_count - 1; + block_size = bch_geo->ecc_chunk_size_in_bytes; + metadata_size = bch_geo->metadata_size_in_bytes; + ecc_strength = bch_geo->ecc_strength >> 1; + page_size = bch_geo->page_size_in_bytes; + + ret = clk_enable(r->clock); + if (ret) + goto err_out; + ret = mxs_reset_block(r->bch_regs); + if (ret) + goto err_out; + + /* Configure layout 0. */ + writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) + | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) + | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) + | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size), + r->bch_regs + HW_BCH_FLASH0LAYOUT0); + + writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) + | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) + | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size), + r->bch_regs + HW_BCH_FLASH0LAYOUT1); + + /* Set *all* chip selects to use layout 0. */ + writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT); + + /* Enable interrupts. */ + writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, + r->bch_regs + HW_BCH_CTRL_SET); + + clk_disable(r->clock); + return 0; +err_out: + return ret; +} + +/* + * ns_to_cycles - Converts time in nanoseconds to cycles. + * + * @ntime: The time, in nanoseconds. + * @period: The cycle period, in nanoseconds. + * @min: The minimum allowable number of cycles. + */ +static unsigned int ns_to_cycles(unsigned int time, + unsigned int period, unsigned int min) +{ + unsigned int k; + + /* + * Compute the minimum number of cycles that entirely contain the + * given time. + */ + k = (time + period - 1) / period; + return max(k, min); +} + +/* + * gpmi_compute_hardware_timing - Apply timing to current hardware conditions. + * + * @this: Per-device data. + * @hardware_timing: A pointer to a hardware timing structure that will receive + * the results of our calculations. + */ +static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, + struct gpmi_nfc_hardware_timing *hw) +{ + struct gpmi_nand_platform_data *pdata = this->pdata; + struct timing_threshod *nfc = &timing_default_threshold; + struct nand_chip *nand = &this->mil.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; + unsigned int dll_delay_shift; + unsigned int max_sample_delay_in_ns; + unsigned int address_setup_in_cycles; + unsigned int data_setup_in_ns; + unsigned int data_setup_in_cycles; + unsigned int data_hold_in_cycles; + int ideal_sample_delay_in_ns; + unsigned int sample_delay_factor; + int tEYE; + unsigned int min_prop_delay_in_ns = pdata->min_prop_delay_in_ns; + unsigned int max_prop_delay_in_ns = pdata->max_prop_delay_in_ns; + + /* + * If there are multiple chips, we need to relax the timings to allow + * for signal distortion due to higher capacitance. + */ + if (nand->numchips > 2) { + target.data_setup_in_ns += 10; + target.data_hold_in_ns += 10; + target.address_setup_in_ns += 10; + } else if (nand->numchips > 1) { + target.data_setup_in_ns += 5; + target.data_hold_in_ns += 5; + 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. */ + clock_frequency_in_hz = nfc->clock_frequency_in_hz; + clock_period_in_ns = 1000000000 / clock_frequency_in_hz; + + /* + * The NFC quantizes setup and hold parameters in terms of clock cycles. + * Here, we quantize the setup and hold timing parameters to the + * next-highest clock period to make sure we apply at least the + * specified times. + * + * For data setup and data hold, the hardware interprets a value of zero + * as the largest possible delay. This is not what's intended by a zero + * in the input parameter, so we impose a minimum of one cycle. + */ + data_setup_in_cycles = ns_to_cycles(target.data_setup_in_ns, + clock_period_in_ns, 1); + data_hold_in_cycles = ns_to_cycles(target.data_hold_in_ns, + clock_period_in_ns, 1); + address_setup_in_cycles = ns_to_cycles(target.address_setup_in_ns, + clock_period_in_ns, 0); + + /* + * The clock's period affects the sample delay in a number of ways: + * + * (1) The NFC HAL tells us the maximum clock period the sample delay + * DLL can tolerate. If the clock period is greater than half that + * maximum, we must configure the DLL to be driven by half periods. + * + * (2) We need to convert from an ideal sample delay, in ns, to a + * "sample delay factor," which the NFC uses. This factor depends on + * whether we're driving the DLL with full or half periods. + * Paraphrasing the reference manual: + * + * AD = SDF x 0.125 x RP + * + * where: + * + * AD is the applied delay, in ns. + * SDF is the sample delay factor, which is dimensionless. + * RP is the reference period, in ns, which is a full clock period + * if the DLL is being driven by full periods, or half that if + * the DLL is being driven by half periods. + * + * Let's re-arrange this in a way that's more useful to us: + * + * 8 + * SDF = AD x ---- + * RP + * + * The reference period is either the clock period or half that, so this + * is: + * + * 8 AD x DDF + * SDF = AD x ----- = -------- + * f x P P + * + * where: + * + * f is 1 or 1/2, depending on how we're driving the DLL. + * P is the clock period. + * DDF is the DLL Delay Factor, a dimensionless value that + * incorporates all the constants in the conversion. + * + * DDF will be either 8 or 16, both of which are powers of two. We can + * reduce the cost of this conversion by using bit shifts instead of + * multiplication or division. Thus: + * + * AD << DDS + * SDF = --------- + * P + * + * or + * + * AD = (SDF >> DDS) x P + * + * where: + * + * DDS is the DLL Delay Shift, the logarithm to base 2 of the DDF. + */ + if (clock_period_in_ns > (nfc->max_dll_clock_period_in_ns >> 1)) { + dll_use_half_periods = true; + dll_delay_shift = 3 + 1; + } else { + dll_use_half_periods = false; + dll_delay_shift = 3; + } + + /* + * Compute the maximum sample delay the NFC allows, under current + * conditions. If the clock is running too slowly, no sample delay is + * possible. + */ + if (clock_period_in_ns > nfc->max_dll_clock_period_in_ns) + max_sample_delay_in_ns = 0; + else { + /* + * Compute the delay implied by the largest sample delay factor + * the NFC allows. + */ + max_sample_delay_in_ns = + (nfc->max_sample_delay_factor * clock_period_in_ns) >> + dll_delay_shift; + + /* + * Check if the implied sample delay larger than the NFC + * actually allows. + */ + if (max_sample_delay_in_ns > nfc->max_dll_delay_in_ns) + max_sample_delay_in_ns = nfc->max_dll_delay_in_ns; + } + + /* + * 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. + */ + max_prop_delay_in_ns += nfc->internal_data_setup_in_ns; + + /* + * Earlier, we computed the number of clock cycles required to satisfy + * the data setup time. Now, we need to know the actual nanoseconds. + */ + data_setup_in_ns = clock_period_in_ns * data_setup_in_cycles; + + /* + * Compute tEYE, the width of the data eye when reading from the NAND + * Flash. The eye width is fundamentally determined by the data setup + * time, perturbed by propagation delays and some characteristics of the + * NAND Flash device. + * + * start of the eye = max_prop_delay + tREA + * end of the eye = min_prop_delay + tRHOH + data_setup + */ + tEYE = (int)min_prop_delay_in_ns + (int)target.tRHOH_in_ns + + (int)data_setup_in_ns; + + tEYE -= (int)max_prop_delay_in_ns + (int)target.tREA_in_ns; + + /* + * The eye must be open. If it's not, we can try to open it by + * increasing its main forcer, the data setup time. + * + * In each iteration of the following loop, we increase the data setup + * time by a single clock cycle. We do this until either the eye is + * open or we run into NFC limits. + */ + while ((tEYE <= 0) && + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { + /* Give a cycle to data setup. */ + data_setup_in_cycles++; + /* Synchronize the data setup time with the cycles. */ + data_setup_in_ns += clock_period_in_ns; + /* Adjust tEYE accordingly. */ + tEYE += clock_period_in_ns; + } + + /* + * When control arrives here, the eye is open. The ideal time to sample + * the data is in the center of the eye: + * + * end of the eye + start of the eye + * --------------------------------- - data_setup + * 2 + * + * After some algebra, this simplifies to the code immediately below. + */ + ideal_sample_delay_in_ns = + ((int)max_prop_delay_in_ns + + (int)target.tREA_in_ns + + (int)min_prop_delay_in_ns + + (int)target.tRHOH_in_ns - + (int)data_setup_in_ns) >> 1; + + /* + * The following figure illustrates some aspects of a NAND Flash read: + * + * + * __ _____________________________________ + * RDN \_________________/ + * + * <---- tEYE -----> + * /-----------------\ + * Read Data ----------------------------< >--------- + * \-----------------/ + * ^ ^ ^ ^ + * | | | | + * |<--Data Setup -->|<--Delay Time -->| | + * | | | | + * | | | + * | |<-- Quantized Delay Time -->| + * | | | + * + * + * We have some issues we must now address: + * + * (1) The *ideal* sample delay time must not be negative. If it is, we + * jam it to zero. + * + * (2) The *ideal* sample delay time must not be greater than that + * allowed by the NFC. If it is, we can increase the data setup + * time, which will reduce the delay between the end of the data + * setup and the center of the eye. It will also make the eye + * larger, which might help with the next issue... + * + * (3) The *quantized* sample delay time must not fall either before the + * eye opens or after it closes (the latter is the problem + * illustrated in the above figure). + */ + + /* Jam a negative ideal sample delay to zero. */ + if (ideal_sample_delay_in_ns < 0) + ideal_sample_delay_in_ns = 0; + + /* + * Extend the data setup as needed to reduce the ideal sample delay + * below the maximum permitted by the NFC. + */ + while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { + + /* Give a cycle to data setup. */ + data_setup_in_cycles++; + /* Synchronize the data setup time with the cycles. */ + data_setup_in_ns += clock_period_in_ns; + /* Adjust tEYE accordingly. */ + tEYE += clock_period_in_ns; + + /* + * Decrease the ideal sample delay by one half cycle, to keep it + * in the middle of the eye. + */ + ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); + + /* Jam a negative ideal sample delay to zero. */ + if (ideal_sample_delay_in_ns < 0) + ideal_sample_delay_in_ns = 0; + } + + /* + * Compute the sample delay factor that corresponds to the ideal sample + * delay. If the result is too large, then use the maximum allowed + * 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; + + /* + * These macros conveniently encapsulate a computation we'll use to + * continuously evaluate whether or not the data sample delay is inside + * the eye. + */ + #define IDEAL_DELAY ((int) ideal_sample_delay_in_ns) + + #define QUANTIZED_DELAY \ + ((int) ((sample_delay_factor * clock_period_in_ns) >> \ + dll_delay_shift)) + + #define DELAY_ERROR (abs(QUANTIZED_DELAY - IDEAL_DELAY)) + + #define SAMPLE_IS_NOT_WITHIN_THE_EYE (DELAY_ERROR > (tEYE >> 1)) + + /* + * While the quantized sample time falls outside the eye, reduce the + * sample delay or extend the data setup to move the sampling point back + * toward the eye. Do not allow the number of data setup cycles to + * exceed the maximum allowed by the NFC. + */ + while (SAMPLE_IS_NOT_WITHIN_THE_EYE && + (data_setup_in_cycles < nfc->max_data_setup_cycles)) { + /* + * If control arrives here, the quantized sample delay falls + * outside the eye. Check if it's before the eye opens, or after + * the eye closes. + */ + if (QUANTIZED_DELAY > IDEAL_DELAY) { + /* + * If control arrives here, the quantized sample delay + * falls after the eye closes. Decrease the quantized + * delay time and then go back to re-evaluate. + */ + if (sample_delay_factor != 0) + sample_delay_factor--; + continue; + } + + /* + * If control arrives here, the quantized sample delay falls + * before the eye opens. Shift the sample point by increasing + * data setup time. This will also make the eye larger. + */ + + /* Give a cycle to data setup. */ + data_setup_in_cycles++; + /* Synchronize the data setup time with the cycles. */ + data_setup_in_ns += clock_period_in_ns; + /* Adjust tEYE accordingly. */ + tEYE += clock_period_in_ns; + + /* + * Decrease the ideal sample delay by one half cycle, to keep it + * in the middle of the eye. + */ + ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); + + /* ...and one less period for the delay time. */ + ideal_sample_delay_in_ns -= clock_period_in_ns; + + /* Jam a negative ideal sample delay to zero. */ + if (ideal_sample_delay_in_ns < 0) + ideal_sample_delay_in_ns = 0; + + /* + * We have a new ideal sample delay, so re-compute the quantized + * delay. + */ + 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; + } + + /* 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; + hw->use_half_periods = dll_use_half_periods; + hw->sample_delay_factor = sample_delay_factor; + + /* Return success. */ + return 0; +} + +/* Begin the I/O */ +void gpmi_begin(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + struct timing_threshod *nfc = &timing_default_threshold; + unsigned char *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 = clk_enable(r->clock); + if (ret) { + pr_info("We failed in enable the clk\n"); + goto err_out; + } + + /* set ready/busy timeout */ + writel(0x500 << 16, gpmi_regs + HW_GPMI_TIMING1); + + /* Get the timing information we need. */ + nfc->clock_frequency_in_hz = clk_get_rate(r->clock); + clock_period_in_ns = 1000000000 / nfc->clock_frequency_in_hz; + + gpmi_nfc_compute_hardware_timing(this, &hw); + + /* Set up all the simple timing parameters. */ + 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); + + /* + * HEY - PAY ATTENTION! + * + * DLL_ENABLE must be set to zero when setting RDN_DELAY or HALF_PERIOD. + */ + writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* Clear out the DLL control fields. */ + writel(BM_GPMI_CTRL1_RDN_DELAY, gpmi_regs + HW_GPMI_CTRL1_CLR); + writel(BM_GPMI_CTRL1_HALF_PERIOD, gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* If no sample delay is called for, return immediately. */ + if (!hw.sample_delay_factor) + return; + + /* Configure the HALF_PERIOD flag. */ + if (hw.use_half_periods) + writel(BM_GPMI_CTRL1_HALF_PERIOD, + gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Set the delay factor. */ + writel(BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor), + gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Enable the DLL. */ + writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET); + + /* + * After we enable the GPMI DLL, we have to wait 64 clock cycles before + * we can use the GPMI. + * + * Calculate the amount of time we need to wait, in microseconds. + */ + dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000; + + if (!dll_wait_time_in_us) + dll_wait_time_in_us = 1; + + /* Wait for the DLL to settle. */ + udelay(dll_wait_time_in_us); + +err_out: + return; +} + +void gpmi_end(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + clk_disable(r->clock); +} + +/* Clears a BCH interrupt. */ +void gpmi_clear_bch(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR); +} + +/* Returns the Ready/Busy status of the given chip. */ +int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) +{ + struct resources *r = &this->resources; + uint32_t mask; + uint32_t reg; + + if (GPMI_IS_MX23(this)) { + mask = MX23_BM_GPMI_DEBUG_READY0 << chip; + reg = readl(r->gpmi_regs + HW_GPMI_DEBUG); + } else if (GPMI_IS_MX28(this)) { + mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); + reg = readl(r->gpmi_regs + HW_GPMI_STAT); + } else + BUG(); + return !!(reg & mask); +} + +static inline void set_dma_type(struct gpmi_nand_data *this, + enum dma_ops_type type) +{ + this->last_dma_type = this->dma_type; + this->dma_type = type; +} + +int gpmi_send_command(struct gpmi_nand_data *this) +{ + struct dma_chan *channel = get_dma_chan(this); + struct mil *mil = &this->mil; + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl; + int chip = mil->current_chip; + u32 pio[3]; + + /* [1] send out the PIO words */ + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) + | BM_GPMI_CTRL0_ADDRESS_INCREMENT + | BF_GPMI_CTRL0_XFER_COUNT(mil->command_length); + pio[1] = pio[2] = 0; + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_NONE, 0); + if (!desc) { + pr_info("step 1 error\n"); + return -1; + } + + /* [2] send out the COMMAND + ADDRESS string stored in @buffer */ + sgl = &mil->cmd_sgl; + + sg_init_one(sgl, mil->cmd_buffer, mil->command_length); + dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); + desc = channel->device->device_prep_slave_sg(channel, + sgl, 1, DMA_TO_DEVICE, 1); + if (!desc) { + pr_info("step 2 error\n"); + return -1; + } + + /* [3] submit the DMA */ + set_dma_type(this, DMA_FOR_COMMAND); + return start_dma_without_bch_irq(this, desc); +} + +int gpmi_send_data(struct gpmi_nand_data *this) +{ + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + struct mil *mil = &this->mil; + int chip = mil->current_chip; + uint32_t command_mode; + uint32_t address; + u32 pio[2]; + + /* [1] PIO */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(mil->upper_len); + pio[1] = 0; + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_NONE, 0); + if (!desc) { + pr_info("step 1 error\n"); + return -1; + } + + /* [2] send DMA request */ + prepare_data_dma(this, DMA_TO_DEVICE); + desc = channel->device->device_prep_slave_sg(channel, &mil->data_sgl, + 1, DMA_TO_DEVICE, 1); + if (!desc) { + pr_info("step 2 error\n"); + return -1; + } + /* [3] submit the DMA */ + set_dma_type(this, DMA_FOR_WRITE_DATA); + return start_dma_without_bch_irq(this, desc); +} + +int gpmi_read_data(struct gpmi_nand_data *this) +{ + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + struct mil *mil = &this->mil; + int chip = mil->current_chip; + u32 pio[2]; + + /* [1] : send PIO */ + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) + | BF_GPMI_CTRL0_XFER_COUNT(mil->upper_len); + pio[1] = 0; + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_NONE, 0); + if (!desc) { + pr_info("step 1 error\n"); + return -1; + } + + /* [2] : send DMA request */ + prepare_data_dma(this, DMA_FROM_DEVICE); + desc = channel->device->device_prep_slave_sg(channel, &mil->data_sgl, + 1, DMA_FROM_DEVICE, 1); + if (!desc) { + pr_info("step 2 error\n"); + return -1; + } + + /* [3] : submit the DMA */ + set_dma_type(this, DMA_FOR_READ_DATA); + return start_dma_without_bch_irq(this, desc); +} + +int gpmi_send_page(struct gpmi_nand_data *this, + dma_addr_t payload, dma_addr_t auxiliary) +{ + struct bch_geometry *geo = &this->bch_geometry; + uint32_t command_mode; + uint32_t address; + uint32_t ecc_command; + uint32_t buffer_mask; + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + struct mil *mil = &this->mil; + int chip = mil->current_chip; + u32 pio[6]; + + /* A DMA descriptor that does an ECC page read. */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE; + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | + BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(0); + pio[1] = 0; + pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC + | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) + | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); + pio[3] = geo->page_size_in_bytes; + pio[4] = payload; + pio[5] = auxiliary; + + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_NONE, 0); + if (!desc) { + pr_info("step 2 error\n"); + return -1; + } + set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE); + return start_dma_with_bch_irq(this, desc); +} + +int gpmi_read_page(struct gpmi_nand_data *this, + dma_addr_t payload, dma_addr_t auxiliary) +{ + struct bch_geometry *geo = &this->bch_geometry; + uint32_t command_mode; + uint32_t address; + uint32_t ecc_command; + uint32_t buffer_mask; + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + struct mil *mil = &this->mil; + int chip = mil->current_chip; + u32 pio[6]; + + /* [1] Wait for the chip to report ready. */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(0); + pio[1] = 0; + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, 2, DMA_NONE, 0); + if (!desc) { + pr_info("step 1 error\n"); + return -1; + } + + /* [2] Enable the BCH block and read. */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE; + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE + | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size_in_bytes); + + pio[1] = 0; + pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC + | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) + | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); + pio[3] = geo->page_size_in_bytes; + pio[4] = payload; + pio[5] = auxiliary; + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_NONE, 1); + if (!desc) { + pr_info("step 2 error\n"); + return -1; + } + + /* [3] Disable the BCH block */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size_in_bytes); + pio[1] = 0; + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, 2, DMA_NONE, 1); + if (!desc) { + pr_info("step 3 error\n"); + return -1; + } + + /* [4] submit the DMA */ + set_dma_type(this, DMA_FOR_READ_ECC_PAGE); + return start_dma_with_bch_irq(this, desc); +} diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h new file mode 100644 index 0000000..c0381cd --- /dev/null +++ b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h @@ -0,0 +1,174 @@ +/* + * Freescale GPMI NAND Flash Driver + * + * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef __GPMI_NAND_GPMI_REGS_H +#define __GPMI_NAND_GPMI_REGS_H + +/*============================================================================*/ +#define HW_GPMI_CTRL0 0x00000000 +#define HW_GPMI_CTRL0_SET 0x00000004 +#define HW_GPMI_CTRL0_CLR 0x00000008 +#define HW_GPMI_CTRL0_TOG 0x0000000c + +#define BP_GPMI_CTRL0_COMMAND_MODE 24 +#define BM_GPMI_CTRL0_COMMAND_MODE (3 << BP_GPMI_CTRL0_COMMAND_MODE) +#define BF_GPMI_CTRL0_COMMAND_MODE(v) \ + (((v) << BP_GPMI_CTRL0_COMMAND_MODE) & BM_GPMI_CTRL0_COMMAND_MODE) +#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0 +#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1 +#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2 +#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3 + +#define BM_GPMI_CTRL0_WORD_LENGTH (1 << 23) +#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0 +#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1 + +/* + * Difference in LOCK_CS between imx23 and imx28 : + * This bit may impact the _POWER_ consumption. So some chips + * do not set it. + */ +#define MX23_BP_GPMI_CTRL0_LOCK_CS 22 +#define MX28_BP_GPMI_CTRL0_LOCK_CS 27 +#define LOCK_CS_ENABLE 0x1 +#define BF_GPMI_CTRL0_LOCK_CS(v, x) 0x0 + +/* Difference in CS between imx23 and imx28 */ +#define BP_GPMI_CTRL0_CS 20 +#define MX23_BM_GPMI_CTRL0_CS (3 << BP_GPMI_CTRL0_CS) +#define MX28_BM_GPMI_CTRL0_CS (7 << BP_GPMI_CTRL0_CS) +#define BF_GPMI_CTRL0_CS(v, x) (((v) << BP_GPMI_CTRL0_CS) & \ + (GPMI_IS_MX23((x)) \ + ? MX23_BM_GPMI_CTRL0_CS \ + : MX28_BM_GPMI_CTRL0_CS)) + +#define BP_GPMI_CTRL0_ADDRESS 17 +#define BM_GPMI_CTRL0_ADDRESS (3 << BP_GPMI_CTRL0_ADDRESS) +#define BF_GPMI_CTRL0_ADDRESS(v) \ + (((v) << BP_GPMI_CTRL0_ADDRESS) & BM_GPMI_CTRL0_ADDRESS) +#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0 +#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1 +#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2 + +#define BM_GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16) +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0 +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1 + +#define BP_GPMI_CTRL0_XFER_COUNT 0 +#define BM_GPMI_CTRL0_XFER_COUNT (0xffff << BP_GPMI_CTRL0_XFER_COUNT) +#define BF_GPMI_CTRL0_XFER_COUNT(v) \ + (((v) << BP_GPMI_CTRL0_XFER_COUNT) & BM_GPMI_CTRL0_XFER_COUNT) + +/*============================================================================*/ +#define HW_GPMI_COMPARE 0x00000010 +/*============================================================================*/ +#define HW_GPMI_ECCCTRL 0x00000020 +#define HW_GPMI_ECCCTRL_SET 0x00000024 +#define HW_GPMI_ECCCTRL_CLR 0x00000028 +#define HW_GPMI_ECCCTRL_TOG 0x0000002c + +#define BP_GPMI_ECCCTRL_ECC_CMD 13 +#define BM_GPMI_ECCCTRL_ECC_CMD (3 << BP_GPMI_ECCCTRL_ECC_CMD) +#define BF_GPMI_ECCCTRL_ECC_CMD(v) \ + (((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD) +#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE 0x0 +#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE 0x1 + +#define BM_GPMI_ECCCTRL_ENABLE_ECC (1 << 12) +#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1 +#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0 + +#define BP_GPMI_ECCCTRL_BUFFER_MASK 0 +#define BM_GPMI_ECCCTRL_BUFFER_MASK (0x1ff << BP_GPMI_ECCCTRL_BUFFER_MASK) +#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \ + (((v) << BP_GPMI_ECCCTRL_BUFFER_MASK) & BM_GPMI_ECCCTRL_BUFFER_MASK) +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF + +/*============================================================================*/ +#define HW_GPMI_ECCCOUNT 0x00000030 +#define HW_GPMI_PAYLOAD 0x00000040 +#define HW_GPMI_AUXILIARY 0x00000050 +/*============================================================================*/ +#define HW_GPMI_CTRL1 0x00000060 +#define HW_GPMI_CTRL1_SET 0x00000064 +#define HW_GPMI_CTRL1_CLR 0x00000068 +#define HW_GPMI_CTRL1_TOG 0x0000006c + +#define BM_GPMI_CTRL1_BCH_MODE (1 << 18) + +#define BP_GPMI_CTRL1_DLL_ENABLE 17 +#define BM_GPMI_CTRL1_DLL_ENABLE (1 << BP_GPMI_CTRL1_DLL_ENABLE) + +#define BP_GPMI_CTRL1_HALF_PERIOD 16 +#define BM_GPMI_CTRL1_HALF_PERIOD (1 << BP_GPMI_CTRL1_HALF_PERIOD) + +#define BP_GPMI_CTRL1_RDN_DELAY 12 +#define BM_GPMI_CTRL1_RDN_DELAY (0xf << BP_GPMI_CTRL1_RDN_DELAY) +#define BF_GPMI_CTRL1_RDN_DELAY(v) \ + (((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY) + +#define BM_GPMI_CTRL1_DEV_RESET (1 << 3) +#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0 +#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1 + +#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2) +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0 +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1 + +#define BM_GPMI_CTRL1_CAMERA_MODE (1 << 1) +#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0 +#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1 + +#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0) + +/*============================================================================*/ +#define HW_GPMI_TIMING0 0x00000070 + +#define BP_GPMI_TIMING0_ADDRESS_SETUP 16 +#define BM_GPMI_TIMING0_ADDRESS_SETUP (0xff << BP_GPMI_TIMING0_ADDRESS_SETUP) +#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \ + (((v) << BP_GPMI_TIMING0_ADDRESS_SETUP) & BM_GPMI_TIMING0_ADDRESS_SETUP) + +#define BP_GPMI_TIMING0_DATA_HOLD 8 +#define BM_GPMI_TIMING0_DATA_HOLD (0xff << BP_GPMI_TIMING0_DATA_HOLD) +#define BF_GPMI_TIMING0_DATA_HOLD(v) \ + (((v) << BP_GPMI_TIMING0_DATA_HOLD) & BM_GPMI_TIMING0_DATA_HOLD) + +#define BP_GPMI_TIMING0_DATA_SETUP 0 +#define BM_GPMI_TIMING0_DATA_SETUP (0xff << BP_GPMI_TIMING0_DATA_SETUP) +#define BF_GPMI_TIMING0_DATA_SETUP(v) \ + (((v) << BP_GPMI_TIMING0_DATA_SETUP) & BM_GPMI_TIMING0_DATA_SETUP) + +/*============================================================================*/ +#define HW_GPMI_TIMING1 0x00000080 +#define HW_GPMI_TIMING2 0x00000090 +#define HW_GPMI_DATA 0x000000a0 +/*============================ MX28 uses this to detect READY ================*/ +#define HW_GPMI_STAT 0x000000b0 +#define MX28_BP_GPMI_STAT_READY_BUSY 24 +#define MX28_BM_GPMI_STAT_READY_BUSY (0xff << MX28_BP_GPMI_STAT_READY_BUSY) +#define MX28_BF_GPMI_STAT_READY_BUSY(v) \ + (((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY) +/*============================ MX23 uses this to detect READY ================*/ +#define HW_GPMI_DEBUG 0x000000c0 +#define MX23_BP_GPMI_DEBUG_READY0 28 +#define MX23_BM_GPMI_DEBUG_READY0 (1 << MX23_BP_GPMI_DEBUG_READY0) +#endif
bch-regs.h : registers file for BCH module gpmi-regs.h: registers file for GPMI module gpmi-lib.c: helper functions library. Signed-off-by: Huang Shijie <b32955@freescale.com> --- drivers/mtd/nand/gpmi-nand/bch-regs.h | 88 +++ drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 978 ++++++++++++++++++++++++++++++++ drivers/mtd/nand/gpmi-nand/gpmi-regs.h | 174 ++++++ 3 files changed, 1240 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/gpmi-nand/bch-regs.h create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-lib.c create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-regs.h