From mboxrd@z Thu Jan 1 00:00:00 1970 From: marek.vasut@gmail.com (Marek Vasut) Date: Sat, 20 Aug 2011 13:46:21 +0200 Subject: [PATCH v9 2/3] MTD : add helper functions library and header files for GPMI NAND driver In-Reply-To: <1313581828-16625-3-git-send-email-b32955@freescale.com> References: <1313581828-16625-1-git-send-email-b32955@freescale.com> <1313581828-16625-3-git-send-email-b32955@freescale.com> Message-ID: <201108201346.21647.marek.vasut@gmail.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org 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 > --- > 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