summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
authorPatrick Turley <patrick.turley@freescale.com>2010-05-07 17:50:18 -0500
committerPatrick Turley <patrick.turley@freescale.com>2010-05-26 11:00:58 -0500
commitf2c1da9854814027459e778c67e25553db1150ee (patch)
treefe79aa5d7717befd6a346988319a8162c5532be5 /drivers/mtd
parent93908befbbcef4ffcebdf202878b0800ac295ca4 (diff)
ENGR00123828 Improved timing for the i.MX23/i.MX28 NAND Flash driver
Added improved timing calculations to the driver, and various other support work. Signed-off-by: Patrick Turley <patrick.turley@freescale.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v1.h1
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-common.c515
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v0.c181
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v1.c129
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-main.c586
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-mil.c54
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v0.c1
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h236
-rw-r--r--drivers/mtd/nand/nand_device_info.c8
9 files changed, 1491 insertions, 220 deletions
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v1.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v1.h
index 8c1bc7b2fb7a..dcb3b7d3fc88 100644
--- a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v1.h
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v1.h
@@ -183,6 +183,7 @@
#define BM_GPMI_CTRL1_TIMEOUT_IRQ_EN 0x00100000
#define BM_GPMI_CTRL1_GANGED_RDYBUSY 0x00080000
#define BM_GPMI_CTRL1_BCH_MODE 0x00040000
+#define BP_GPMI_CTRL1_DLL_ENABLE 17
#define BM_GPMI_CTRL1_DLL_ENABLE 0x00020000
#define BP_GPMI_CTRL1_HALF_PERIOD 16
#define BM_GPMI_CTRL1_HALF_PERIOD 0x00010000
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-common.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-common.c
index c210203adfca..b38d653a21fd 100644
--- a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-common.c
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-common.c
@@ -520,3 +520,518 @@ int gpmi_nfc_dma_go(struct gpmi_nfc_data *this, int dma_channel)
return error;
}
+
+/**
+ * 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.
+ */
+int gpmi_nfc_compute_hardware_timing(struct gpmi_nfc_data *this,
+ struct gpmi_nfc_hardware_timing *hw)
+{
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct nfc_hal *nfc = this->nfc;
+ struct gpmi_nfc_timing target = nfc->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 (physical->chip_count > 2) {
+ target.data_setup_in_ns += 10;
+ target.data_hold_in_ns += 10;
+ target.address_setup_in_ns += 10;
+ } else if (physical->chip_count > 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;
+
+}
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v0.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v0.c
index f80e1c9fc1f0..294bb9409581 100644
--- a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v0.c
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v0.c
@@ -151,7 +151,7 @@ static int set_geometry(struct gpmi_nfc_data *this)
}
/**
- * set_set_timing() - Configures the NFC timing.
+ * set_timing() - Configures the NFC timing.
*
* @this: Per-device data.
* @timing: The timing of interest.
@@ -172,6 +172,69 @@ static int set_timing(struct gpmi_nfc_data *this,
}
/**
+ * get_timing() - Retrieves the NFC hardware timing.
+ *
+ * @this: Per-device data.
+ * @clock_frequency_in_hz: The clock frequency, in Hz, during the current
+ * I/O transaction. If no I/O transaction is in
+ * progress, this is the clock frequency during the
+ * most recent I/O transaction.
+ * @hardware_timing: The hardware timing configuration in effect during
+ * the current I/O transaction. If no I/O transaction
+ * is in progress, this is the hardware timing
+ * configuration during the most recent I/O
+ * transaction.
+ */
+static void get_timing(struct gpmi_nfc_data *this,
+ unsigned long *clock_frequency_in_hz,
+ struct gpmi_nfc_hardware_timing *hardware_timing)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ unsigned char *gpmi_regs = resources->gpmi_regs;
+ uint32_t register_image;
+
+ /* Return the clock frequency. */
+
+ *clock_frequency_in_hz = nfc->clock_frequency_in_hz;
+
+ /* We'll be reading the hardware, so let's enable the clock. */
+
+ clk_enable(resources->clock);
+
+ /* Retrieve the hardware timing. */
+
+ register_image = __raw_readl(gpmi_regs + HW_GPMI_TIMING0);
+
+ hardware_timing->data_setup_in_cycles =
+ (register_image & BM_GPMI_TIMING0_DATA_SETUP) >>
+ BP_GPMI_TIMING0_DATA_SETUP;
+
+ hardware_timing->data_hold_in_cycles =
+ (register_image & BM_GPMI_TIMING0_DATA_HOLD) >>
+ BP_GPMI_TIMING0_DATA_HOLD;
+
+ hardware_timing->address_setup_in_cycles =
+ (register_image & BM_GPMI_TIMING0_ADDRESS_SETUP) >>
+ BP_GPMI_TIMING0_ADDRESS_SETUP;
+
+ register_image = __raw_readl(gpmi_regs + HW_GPMI_CTRL1);
+
+ hardware_timing->use_half_periods =
+ (register_image & BM_GPMI_CTRL1_HALF_PERIOD) >>
+ BP_GPMI_CTRL1_HALF_PERIOD;
+
+ hardware_timing->sample_delay_factor =
+ (register_image & BM_GPMI_CTRL1_RDN_DELAY) >>
+ BP_GPMI_CTRL1_RDN_DELAY;
+
+ /* We're done reading the hardware, so disable the clock. */
+
+ clk_disable(resources->clock);
+
+}
+
+/**
* exit() - Shuts down the NFC hardware.
*
* @this: Per-device data.
@@ -188,12 +251,83 @@ static void exit(struct gpmi_nfc_data *this)
*/
static void begin(struct gpmi_nfc_data *this)
{
- struct resources *resources = &this->resources;
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct gpmi_nfc_hardware_timing hw;
+ unsigned char *gpmi_regs = resources->gpmi_regs;
+ unsigned int clock_period_in_ns;
+ uint32_t register_image;
+ unsigned int dll_wait_time_in_us;
/* Enable the clock. */
clk_enable(resources->clock);
+ /* Get the timing information we need. */
+
+ nfc->clock_frequency_in_hz = clk_get_rate(resources->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. */
+
+ register_image =
+ 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) ;
+
+ __raw_writel(register_image, gpmi_regs + HW_GPMI_TIMING0);
+
+ /*
+ * HEY - PAY ATTENTION!
+ *
+ * DLL_ENABLE must be set to zero when setting RDN_DELAY or HALF_PERIOD.
+ */
+
+ __raw_writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+ /* Clear out the DLL control fields. */
+
+ __raw_writel(BM_GPMI_CTRL1_RDN_DELAY, gpmi_regs + HW_GPMI_CTRL1_CLR);
+ __raw_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)
+ __raw_writel(BM_GPMI_CTRL1_HALF_PERIOD,
+ gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Set the delay factor. */
+
+ __raw_writel(BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor),
+ gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Enable the DLL. */
+
+ __raw_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);
+
}
/**
@@ -676,7 +810,7 @@ static int read_page(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.pio_words[1] = 0;
(*d)->cmd.pio_words[2] =
- BM_GPMI_ECCCTRL_ENABLE_ECC |
+ BM_GPMI_ECCCTRL_ENABLE_ECC |
BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) |
BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ;
(*d)->cmd.pio_words[3] = nfc_geo->page_size_in_bytes;
@@ -763,25 +897,28 @@ static int read_page(struct gpmi_nfc_data *this, unsigned chip,
/* This structure represents the NFC HAL for this version of the hardware. */
struct nfc_hal gpmi_nfc_hal_v0 = {
- .version = 0,
- .description = "4-chip GPMI and BCH",
- .max_chip_count = 4,
- .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
+ .version = 0,
+ .description = "4-chip GPMI and BCH",
+ .max_chip_count = 4,
+ .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
BP_GPMI_TIMING0_DATA_SETUP),
- .max_data_sample_delay_cycles = (BM_GPMI_CTRL1_RDN_DELAY >>
+ .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,
- .init = init,
- .set_geometry = set_geometry,
- .set_timing = set_timing,
- .exit = exit,
- .begin = begin,
- .end = end,
- .clear_bch = clear_bch,
- .is_ready = is_ready,
- .send_command = send_command,
- .send_data = send_data,
- .read_data = read_data,
- .send_page = send_page,
- .read_page = read_page,
+ .max_dll_clock_period_in_ns = 32,
+ .max_dll_delay_in_ns = 16,
+ .init = init,
+ .set_geometry = set_geometry,
+ .set_timing = set_timing,
+ .get_timing = get_timing,
+ .exit = exit,
+ .begin = begin,
+ .end = end,
+ .clear_bch = clear_bch,
+ .is_ready = is_ready,
+ .send_command = send_command,
+ .send_data = send_data,
+ .read_data = read_data,
+ .send_page = send_page,
+ .read_page = read_page,
};
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v1.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v1.c
index 545fbbc9ba4d..962efe686853 100644
--- a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v1.c
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v1.c
@@ -153,7 +153,7 @@ static int set_geometry(struct gpmi_nfc_data *this)
}
/**
- * set_set_timing() - Configures the NFC timing.
+ * set_timing() - Configures the NFC timing.
*
* @this: Per-device data.
* @timing: The timing of interest.
@@ -174,6 +174,69 @@ static int set_timing(struct gpmi_nfc_data *this,
}
/**
+ * get_timing() - Retrieves the NFC hardware timing.
+ *
+ * @this: Per-device data.
+ * @clock_frequency_in_hz: The clock frequency, in Hz, during the current
+ * I/O transaction. If no I/O transaction is in
+ * progress, this is the clock frequency during the
+ * most recent I/O transaction.
+ * @hardware_timing: The hardware timing configuration in effect during
+ * the current I/O transaction. If no I/O transaction
+ * is in progress, this is the hardware timing
+ * configuration during the most recent I/O
+ * transaction.
+ */
+static void get_timing(struct gpmi_nfc_data *this,
+ unsigned long *clock_frequency_in_hz,
+ struct gpmi_nfc_hardware_timing *hardware_timing)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ unsigned char *gpmi_regs = resources->gpmi_regs;
+ uint32_t register_image;
+
+ /* Return the clock frequency. */
+
+ *clock_frequency_in_hz = nfc->clock_frequency_in_hz;
+
+ /* We'll be reading the hardware, so let's enable the clock. */
+
+ clk_enable(resources->clock);
+
+ /* Retrieve the hardware timing. */
+
+ register_image = __raw_readl(gpmi_regs + HW_GPMI_TIMING0);
+
+ hardware_timing->data_setup_in_cycles =
+ (register_image & BM_GPMI_TIMING0_DATA_SETUP) >>
+ BP_GPMI_TIMING0_DATA_SETUP;
+
+ hardware_timing->data_hold_in_cycles =
+ (register_image & BM_GPMI_TIMING0_DATA_HOLD) >>
+ BP_GPMI_TIMING0_DATA_HOLD;
+
+ hardware_timing->address_setup_in_cycles =
+ (register_image & BM_GPMI_TIMING0_ADDRESS_SETUP) >>
+ BP_GPMI_TIMING0_ADDRESS_SETUP;
+
+ register_image = __raw_readl(gpmi_regs + HW_GPMI_CTRL1);
+
+ hardware_timing->use_half_periods =
+ (register_image & BM_GPMI_CTRL1_HALF_PERIOD) >>
+ BP_GPMI_CTRL1_HALF_PERIOD;
+
+ hardware_timing->sample_delay_factor =
+ (register_image & BM_GPMI_CTRL1_RDN_DELAY) >>
+ BP_GPMI_CTRL1_RDN_DELAY;
+
+ /* We're done reading the hardware, so disable the clock. */
+
+ clk_disable(resources->clock);
+
+}
+
+/**
* exit() - Shuts down the NFC hardware.
*
* @this: Per-device data.
@@ -190,12 +253,23 @@ static void exit(struct gpmi_nfc_data *this)
*/
static void begin(struct gpmi_nfc_data *this)
{
- struct resources *resources = &this->resources;
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct gpmi_nfc_hardware_timing hw;
/* Enable the clock. */
clk_enable(resources->clock);
+ /* Get the timing information we need. */
+
+ nfc->clock_frequency_in_hz = clk_get_rate(resources->clock);
+ gpmi_nfc_compute_hardware_timing(this, &hw);
+
+ /* Apply the hardware timing. */
+
+ /* Coming soon - the clock handling code isn't ready yet. */
+
}
/**
@@ -294,7 +368,6 @@ static int send_command(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.address = buffer;
(*d)->cmd.pio_words[0] =
- BM_GPMI_CTRL0_LOCK_CS |
BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
BM_GPMI_CTRL0_WORD_LENGTH |
BF_GPMI_CTRL0_CS(chip) |
@@ -366,7 +439,6 @@ static int send_data(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.address = buffer;
(*d)->cmd.pio_words[0] =
- BM_GPMI_CTRL0_LOCK_CS |
BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
BM_GPMI_CTRL0_WORD_LENGTH |
BF_GPMI_CTRL0_CS(chip) |
@@ -437,7 +509,6 @@ static int read_data(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.address = buffer;
(*d)->cmd.pio_words[0] =
- BM_GPMI_CTRL0_LOCK_CS |
BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
BM_GPMI_CTRL0_WORD_LENGTH |
BF_GPMI_CTRL0_CS(chip) |
@@ -476,7 +547,6 @@ static int read_data(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.pio_words[0] =
BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
- BM_GPMI_CTRL0_LOCK_CS |
BM_GPMI_CTRL0_WORD_LENGTH |
BF_GPMI_CTRL0_CS(chip) |
BF_GPMI_CTRL0_ADDRESS(address) |
@@ -552,7 +622,6 @@ static int send_page(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.address = 0;
(*d)->cmd.pio_words[0] =
- BM_GPMI_CTRL0_LOCK_CS |
BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
BM_GPMI_CTRL0_WORD_LENGTH |
BF_GPMI_CTRL0_CS(chip) |
@@ -643,7 +712,6 @@ static int read_page(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.pio_words[0] =
BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
- BM_GPMI_CTRL0_LOCK_CS |
BM_GPMI_CTRL0_WORD_LENGTH |
BF_GPMI_CTRL0_CS(chip) |
BF_GPMI_CTRL0_ADDRESS(address) |
@@ -676,7 +744,6 @@ static int read_page(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.address = 0;
(*d)->cmd.pio_words[0] =
- BM_GPMI_CTRL0_LOCK_CS |
BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
BM_GPMI_CTRL0_WORD_LENGTH |
BF_GPMI_CTRL0_CS(chip) |
@@ -685,7 +752,7 @@ static int read_page(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.pio_words[1] = 0;
(*d)->cmd.pio_words[2] =
- BM_GPMI_ECCCTRL_ENABLE_ECC |
+ BM_GPMI_ECCCTRL_ENABLE_ECC |
BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) |
BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ;
(*d)->cmd.pio_words[3] = nfc_geo->page_size_in_bytes;
@@ -717,7 +784,6 @@ static int read_page(struct gpmi_nfc_data *this, unsigned chip,
(*d)->cmd.pio_words[0] =
BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
- BM_GPMI_CTRL0_LOCK_CS |
BM_GPMI_CTRL0_WORD_LENGTH |
BF_GPMI_CTRL0_CS(chip) |
BF_GPMI_CTRL0_ADDRESS(address) |
@@ -773,25 +839,28 @@ static int read_page(struct gpmi_nfc_data *this, unsigned chip,
/* This structure represents the NFC HAL for this version of the hardware. */
struct nfc_hal gpmi_nfc_hal_v1 = {
- .version = 1,
- .description = "8-chip GPMI and BCH",
- .max_chip_count = 8,
- .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
+ .version = 1,
+ .description = "8-chip GPMI and BCH",
+ .max_chip_count = 8,
+ .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
BP_GPMI_TIMING0_DATA_SETUP),
- .max_data_sample_delay_cycles = (BM_GPMI_CTRL1_RDN_DELAY >>
+ .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,
- .init = init,
- .set_geometry = set_geometry,
- .set_timing = set_timing,
- .exit = exit,
- .begin = begin,
- .end = end,
- .clear_bch = clear_bch,
- .is_ready = is_ready,
- .send_command = send_command,
- .send_data = send_data,
- .read_data = read_data,
- .send_page = send_page,
- .read_page = read_page,
+ .max_dll_clock_period_in_ns = 32,
+ .max_dll_delay_in_ns = 16,
+ .init = init,
+ .set_geometry = set_geometry,
+ .set_timing = set_timing,
+ .get_timing = get_timing,
+ .exit = exit,
+ .begin = begin,
+ .end = end,
+ .clear_bch = clear_bch,
+ .is_ready = is_ready,
+ .send_command = send_command,
+ .send_data = send_data,
+ .read_data = read_data,
+ .send_page = send_page,
+ .read_page = read_page,
};
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-main.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-main.c
index d88215246bab..0143f1c358ff 100644
--- a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-main.c
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-main.c
@@ -58,6 +58,54 @@ static struct boot_rom_helper *(boot_rom_helpers[]) = {
};
/**
+ * show_device_report() - Contains a shell script that creates a handy report.
+ *
+ * @d: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_report(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ static const char *script =
+ "GPMISysDirectory=/sys/bus/platform/devices/gpmi-nfc.0\n"
+ "\n"
+ "NodeList='\n"
+ "physical_geometry\n"
+ "nfc_info\n"
+ "nfc_geometry\n"
+ "timing\n"
+ "timing_diagram\n"
+ "rom_geometry\n"
+ "mtd_nand_info\n"
+ "mtd_info\n"
+ "'\n"
+ "\n"
+ "cd ${GPMISysDirectory}\n"
+ "\n"
+ "printf '\\n'\n"
+ "\n"
+ "for NodeName in ${NodeList}\n"
+ "do\n"
+ "\n"
+ " printf '--------------------------------------------\\n'\n"
+ " printf '%s\\n' ${NodeName}\n"
+ " printf '--------------------------------------------\\n'\n"
+ " printf '\\n'\n"
+ "\n"
+ " cat ${NodeName}\n"
+ "\n"
+ " printf '\\n'\n"
+ "\n"
+ "done\n"
+ ;
+
+ return sprintf(buf, "%s", script);
+
+}
+
+/**
* show_device_numchips() - Shows the number of physical chips.
*
* This node is made obsolete by the physical_geometry node, but we keep it for
@@ -88,15 +136,18 @@ static ssize_t show_device_physical_geometry(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct nand_device_info *info = &this->device_info;
struct physical_geometry *physical = &this->physical_geometry;
return sprintf(buf,
+ "Description : %s\n"
"Chip Count : %u\n"
"Chip Size in Bytes : %llu\n"
"Block Size in Bytes : %u\n"
"Page Data Size in Bytes: %u\n"
"Page OOB Size in Bytes : %u\n"
,
+ info->description,
physical->chip_count,
physical->chip_size_in_bytes,
physical->block_size_in_bytes,
@@ -116,47 +167,27 @@ static ssize_t show_device_physical_geometry(struct device *dev,
static ssize_t show_device_nfc_info(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct gpmi_nfc_data *this = dev_get_drvdata(dev);
- struct resources *resources = &this->resources;
- struct nfc_hal *nfc = this->nfc;
- struct clk *parent_clock;
- unsigned long parent_clock_rate_in_hz;
- unsigned long clock_rate_in_hz;
- int clock_use_count;
-
- parent_clock = clk_get_parent(resources->clock);
- parent_clock_rate_in_hz = clk_get_rate(parent_clock);
- clock_rate_in_hz = clk_get_rate(resources->clock);
- clock_use_count = clk_get_usecount(resources->clock);
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct nfc_hal *nfc = this->nfc;
return sprintf(buf,
- "Version : %u\n"
- "Description : %s\n"
- "Max Chip Count : %u\n"
- "Parent Clock Rate in Hz: %lu\n"
- "Clock Rate in Hz : %lu\n"
- "Clock Use Count : %u\n"
- "Data Setup in ns : %d\n"
- "Data Hold in ns : %d\n"
- "Address Setup in ns : %d\n"
- "Sample Delay in ns : %d\n"
- "tREA in ns : %d\n"
- "tRLOH in ns : %d\n"
- "tRHOH in ns : %d\n"
+ "Version : %u\n"
+ "Description : %s\n"
+ "Max Chip Count : %u\n"
+ "Max Data Setup Cycles : 0x%x\n"
+ "Internal Data Setup in ns : %u\n"
+ "Max Sample Delay Factor : 0x%x\n"
+ "Max DLL Clock Period in ns: %u\n"
+ "Max DLL Delay in ns : %u\n"
,
nfc->version,
nfc->description,
nfc->max_chip_count,
- parent_clock_rate_in_hz,
- clock_rate_in_hz,
- clock_use_count,
- nfc->timing.data_setup_in_ns,
- nfc->timing.data_hold_in_ns,
- nfc->timing.address_setup_in_ns,
- nfc->timing.gpmi_sample_delay_in_ns,
- nfc->timing.tREA_in_ns,
- nfc->timing.tRLOH_in_ns,
- nfc->timing.tRHOH_in_ns
+ nfc->max_data_setup_cycles,
+ nfc->internal_data_setup_in_ns,
+ nfc->max_sample_delay_factor,
+ nfc->max_dll_clock_period_in_ns,
+ nfc->max_dll_delay_in_ns
);
}
@@ -422,6 +453,288 @@ static ssize_t show_device_mtd_info(struct device *dev,
}
/**
+ * show_device_timing_diagram() - Shows a timing diagram.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_timing_diagram(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct nfc_hal *nfc = this->nfc;
+ struct gpmi_nfc_timing timing = nfc->timing;
+ struct gpmi_nfc_hardware_timing hardware_timing;
+ unsigned long clock_frequency_in_hz;
+ unsigned long clock_period_in_ns;
+ unsigned int data_setup_in_ns;
+ unsigned int dll_delay_shift;
+ unsigned int sample_delay_in_ns;
+ unsigned int tDS_in_ns;
+ unsigned int tOPEN_in_ns;
+ unsigned int tCLOSE_in_ns;
+ unsigned int tEYE_in_ns;
+ unsigned int tDELAY_in_ns;
+ unsigned int tDS;
+ unsigned int tOPEN;
+ unsigned int tCLOSE;
+ unsigned int tEYE;
+ unsigned int tDELAY;
+ const unsigned int diagram_width_in_chars = 55;
+ unsigned int diagram_width_in_ns;
+ int o = 0;
+ unsigned int i;
+
+ /*
+ * If there are any timing characteristics we need, but don't know, we
+ * pretend they're zero.
+ */
+
+ if (timing.tREA_in_ns < 0)
+ timing.tREA_in_ns = 0;
+
+ if (timing.tRHOH_in_ns < 0)
+ timing.tRHOH_in_ns = 0;
+
+ /* Get information about the current/last I/O transaction. */
+
+ nfc->get_timing(this, &clock_frequency_in_hz, &hardware_timing);
+
+ clock_period_in_ns = 1000000000 / clock_frequency_in_hz;
+
+ /* Compute basic timing facts. */
+
+ data_setup_in_ns =
+ hardware_timing.data_setup_in_cycles * clock_period_in_ns;
+
+ /* Compute data sample delay facts. */
+
+ dll_delay_shift = 3;
+
+ if (hardware_timing.use_half_periods)
+ dll_delay_shift++;
+
+ sample_delay_in_ns =
+ (hardware_timing.sample_delay_factor * clock_period_in_ns) >>
+ dll_delay_shift;
+
+ /* Compute the basic metrics in the diagram, in nanoseconds. */
+
+ tDS_in_ns = data_setup_in_ns;
+ tOPEN_in_ns = pdata->max_prop_delay_in_ns + timing.tREA_in_ns;
+ tCLOSE_in_ns = pdata->min_prop_delay_in_ns + timing.tRHOH_in_ns;
+ tEYE_in_ns = tDS_in_ns + tCLOSE_in_ns - tOPEN_in_ns;
+ tDELAY_in_ns = sample_delay_in_ns;
+
+ /*
+ * We need to translate nanosecond timings into character widths in the
+ * diagram. The first step is to discover how "wide" the diagram is in
+ * nanoseconds. That depends on which happens latest: the sample point
+ * or the close of the eye.
+ */
+
+ if (tCLOSE_in_ns >= tDELAY_in_ns)
+ diagram_width_in_ns = tDS_in_ns + tCLOSE_in_ns;
+ else
+ diagram_width_in_ns = tDS_in_ns + tDELAY_in_ns;
+
+ /* Convert the metrics that appear in the diagram. */
+
+ tDS = (tDS_in_ns * diagram_width_in_chars) / diagram_width_in_ns;
+ tOPEN = (tOPEN_in_ns * diagram_width_in_chars) / diagram_width_in_ns;
+ tCLOSE = (tCLOSE_in_ns * diagram_width_in_chars) / diagram_width_in_ns;
+ tEYE = (tEYE_in_ns * diagram_width_in_chars) / diagram_width_in_ns;
+ tDELAY = (tDELAY_in_ns * diagram_width_in_chars) / diagram_width_in_ns;
+
+ /*
+ * Show the results.
+ *
+ * This code is really ugly, but it draws a pretty picture :)
+ */
+
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, "Sample ______");
+ for (i = 0; i < tDS; i++)
+ o += sprintf(buf + o, "_");
+ if (tDELAY > 0)
+ for (i = 0; i < (tDELAY - 1); i++)
+ o += sprintf(buf + o, "_");
+ o += sprintf(buf + o, "|");
+ for (i = 0; i < (diagram_width_in_chars - (tDS + tDELAY)); i++)
+ o += sprintf(buf + o, "_");
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, "Strobe ");
+ for (i = 0; i < tDS; i++)
+ o += sprintf(buf + o, " ");
+ o += sprintf(buf + o, "|");
+ if (tDELAY > 1) {
+ for (i = 2; i < tDELAY; i++)
+ o += sprintf(buf + o, "-");
+ o += sprintf(buf + o, "|");
+ }
+ o += sprintf(buf + o, " tDELAY\n");
+
+
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, " tDS ");
+ o += sprintf(buf + o, "|");
+ if (tDS > 1) {
+ for (i = 2; i < tDS; i++)
+ o += sprintf(buf + o, "-");
+ o += sprintf(buf + o, "|");
+ }
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, " ______");
+ for (i = 0; i < tDS; i++)
+ o += sprintf(buf + o, " ");
+ for (i = 0; i < (diagram_width_in_chars - tDS); i++)
+ o += sprintf(buf + o, "_");
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, "RDN ");
+ if (tDS > 0) {
+ if (tDS == 1)
+ o += sprintf(buf + o, "V");
+ else {
+ o += sprintf(buf + o, "\\");
+ for (i = 2; i < tDS; i++)
+ o += sprintf(buf + o, "_");
+ o += sprintf(buf + o, "/");
+ }
+ }
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, " tOPEN ");
+ o += sprintf(buf + o, "|");
+ if (tOPEN > 1) {
+ for (i = 2; i < tOPEN; i++)
+ o += sprintf(buf + o, "-");
+ o += sprintf(buf + o, "|");
+ }
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, " ");
+ for (i = 0; i < tDS; i++)
+ o += sprintf(buf + o, " ");
+ o += sprintf(buf + o, "|");
+ if (tCLOSE > 1) {
+ for (i = 2; i < tCLOSE; i++)
+ o += sprintf(buf + o, "-");
+ o += sprintf(buf + o, "|");
+ }
+ o += sprintf(buf + o, " tCLOSE\n");
+
+
+ o += sprintf(buf + o, " ");
+ for (i = 0; i < tOPEN; i++)
+ o += sprintf(buf + o, " ");
+ if (tEYE > 2) {
+ o += sprintf(buf + o, " ");
+ for (i = 2; i < tEYE; i++)
+ o += sprintf(buf + o, "_");
+ }
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, "Data ______");
+ for (i = 0; i < tOPEN; i++)
+ o += sprintf(buf + o, "_");
+ if (tEYE > 0) {
+ if (tEYE == 1)
+ o += sprintf(buf + o, "|");
+ else {
+ o += sprintf(buf + o, "/");
+ for (i = 2; i < tEYE; i++)
+ o += sprintf(buf + o, " ");
+ o += sprintf(buf + o, "\\");
+ }
+ }
+ for (i = 0; i < (diagram_width_in_chars - (tOPEN + tEYE)); i++)
+ o += sprintf(buf + o, "_");
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, " ");
+ for (i = 0; i < tOPEN; i++)
+ o += sprintf(buf + o, " ");
+ if (tEYE > 0) {
+ if (tEYE == 1)
+ o += sprintf(buf + o, "|");
+ else {
+ o += sprintf(buf + o, "\\");
+ for (i = 2; i < tEYE; i++)
+ o += sprintf(buf + o, "_");
+ o += sprintf(buf + o, "/");
+ }
+ }
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, " ");
+ for (i = 0; i < tOPEN; i++)
+ o += sprintf(buf + o, " ");
+ o += sprintf(buf + o, "|");
+ if (tEYE > 1) {
+ for (i = 2; i < tEYE; i++)
+ o += sprintf(buf + o, "-");
+ o += sprintf(buf + o, "|");
+ }
+ o += sprintf(buf + o, " tEYE\n");
+
+
+ o += sprintf(buf + o, "\n");
+ o += sprintf(buf + o, "tDS : %u ns\n", tDS_in_ns);
+ o += sprintf(buf + o, "tOPEN : %u ns\n", tOPEN_in_ns);
+ o += sprintf(buf + o, "tCLOSE: %u ns\n", tCLOSE_in_ns);
+ o += sprintf(buf + o, "tEYE : %u ns\n", tEYE_in_ns);
+ o += sprintf(buf + o, "tDELAY: %u ns\n", tDELAY_in_ns);
+ o += sprintf(buf + o, "\n");
+
+
+ return o;
+
+}
+
+/**
+ * store_device_invalidate_page_cache() - Invalidates the device's page cache.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer containing a new attribute value.
+ * @size: The size of the buffer.
+ */
+static ssize_t store_device_invalidate_page_cache(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+
+ /* Invalidate the page cache. */
+
+ this->mil.nand.pagebuf = -1;
+
+ /* Return success. */
+
+ return size;
+
+}
+
+/**
* store_device_mark_block_bad() - Marks a block as bad.
*
* @dev: The device of interest.
@@ -600,21 +913,198 @@ static ssize_t store_device_inject_ecc_error(struct device *dev,
}
/**
- * store_device_invalidate_page_cache() - Invalidates the device's page cache.
+ * show_device_timing_help() - Show help for setting timing.
+ *
+ * @d: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_timing_help(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ static const char *help =
+ "<Data Setup>,<Data Hold>,<Address Setup>,<Sample Delay>,"
+ "<tREA>,<tRLOH>,<tRHOH>\n";
+
+ return sprintf(buf, "%s", help);
+
+}
+
+/**
+ * show_device_timing() - Shows the current timing.
+ *
+ * @d: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_timing(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct nfc_hal *nfc = this->nfc;
+ struct gpmi_nfc_timing *recorded = &nfc->timing;
+ unsigned long clock_frequency_in_hz;
+ unsigned long clock_period_in_ns;
+ struct gpmi_nfc_hardware_timing hardware;
+ unsigned int effective_data_setup_in_ns;
+ unsigned int effective_data_hold_in_ns;
+ unsigned int effective_address_setup_in_ns;
+ unsigned int dll_delay_shift;
+ unsigned int effective_sample_delay_in_ns;
+
+ /* Get information about the current/last I/O transaction. */
+
+ nfc->get_timing(this, &clock_frequency_in_hz, &hardware);
+
+ clock_period_in_ns = 1000000000 / clock_frequency_in_hz;
+
+ /* Compute basic timing facts. */
+
+ effective_data_setup_in_ns =
+ hardware.data_setup_in_cycles * clock_period_in_ns;
+ effective_data_hold_in_ns =
+ hardware.data_hold_in_cycles * clock_period_in_ns;
+ effective_address_setup_in_ns =
+ hardware.address_setup_in_cycles * clock_period_in_ns;
+
+ /* Compute data sample delay facts. */
+
+ dll_delay_shift = 3;
+
+ if (hardware.use_half_periods)
+ dll_delay_shift++;
+
+ effective_sample_delay_in_ns =
+ (hardware.sample_delay_factor * clock_period_in_ns) >>
+ dll_delay_shift;
+
+ /* Show the results. */
+
+ return sprintf(buf,
+ "Minimum Propagation Delay in ns : %u\n"
+ "Maximum Propagation Delay in ns : %u\n"
+ "Clock Frequency in Hz : %lu\n"
+ "Clock Period in ns : %lu\n"
+ "Recorded Data Setup in ns : %d\n"
+ "Hardware Data Setup in cycles : %u\n"
+ "Effective Data Setup in ns : %u\n"
+ "Recorded Data Hold in ns : %d\n"
+ "Hardware Data Hold in cycles : %u\n"
+ "Effective Data Hold in ns : %u\n"
+ "Recorded Address Setup in ns : %d\n"
+ "Hardware Address Setup in cycles: %u\n"
+ "Effective Address Setup in ns : %u\n"
+ "Using Half Period : %s\n"
+ "Recorded Sample Delay in ns : %d\n"
+ "Hardware Sample Delay Factor : %u\n"
+ "Effective Sample Delay in ns : %u\n"
+ "Recorded tREA in ns : %d\n"
+ "Recorded tRLOH in ns : %d\n"
+ "Recorded tRHOH in ns : %d\n"
+ ,
+ pdata->min_prop_delay_in_ns,
+ pdata->max_prop_delay_in_ns,
+ clock_frequency_in_hz,
+ clock_period_in_ns,
+ recorded->data_setup_in_ns,
+ hardware .data_setup_in_cycles,
+ effective_data_setup_in_ns,
+ recorded->data_hold_in_ns,
+ hardware .data_hold_in_cycles,
+ effective_data_hold_in_ns,
+ recorded->address_setup_in_ns,
+ hardware .address_setup_in_cycles,
+ effective_address_setup_in_ns,
+ hardware .use_half_periods ? "Yes" : "No",
+ recorded->gpmi_sample_delay_in_ns,
+ hardware .sample_delay_factor,
+ effective_sample_delay_in_ns,
+ recorded->tREA_in_ns,
+ recorded->tRLOH_in_ns,
+ recorded->tRHOH_in_ns);
+
+}
+
+/**
+ * store_device_timing() - Sets the current timing.
*
* @dev: The device of interest.
* @attr: The attribute of interest.
* @buf: A buffer containing a new attribute value.
* @size: The size of the buffer.
*/
-static ssize_t store_device_invalidate_page_cache(struct device *dev,
+static ssize_t store_device_timing(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
- struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct nfc_hal *nfc = this->nfc;
+ const char *p = buf;
+ const char *q;
+ char tmps[20];
+ long t;
+ struct gpmi_nfc_timing new;
+
+ int8_t *field_pointers[] = {
+ &new.data_setup_in_ns,
+ &new.data_hold_in_ns,
+ &new.address_setup_in_ns,
+ &new.gpmi_sample_delay_in_ns,
+ &new.tREA_in_ns,
+ &new.tRLOH_in_ns,
+ &new.tRHOH_in_ns,
+ NULL,
+ };
+
+ int8_t **field_pointer = field_pointers;
- /* Invalidate the page cache. */
+ /*
+ * Loop over comma-separated timing values in the incoming buffer,
+ * assigning them to fields in the timing structure as we go along.
+ */
- this->mil.nand.pagebuf = -1;
+ while (*field_pointer != NULL) {
+
+ /* Clear out the temporary buffer. */
+
+ memset(tmps, 0, sizeof(tmps));
+
+ /* Copy the timing value into the temporary buffer. */
+
+ q = strchr(p, ',');
+ if (q)
+ strncpy(tmps, p, min_t(int, sizeof(tmps) - 1, q - p));
+ else
+ strncpy(tmps, p, sizeof(tmps) - 1);
+
+ /* Attempt to convert the current timing value. */
+
+ if (strict_strtol(tmps, 0, &t) < 0)
+ return -EINVAL;
+
+ if ((t > 127) || (t < -128))
+ return -EINVAL;
+
+ /* Assign this value to the current field. */
+
+ **field_pointer = (int8_t) t;
+ field_pointer++;
+
+ /* Check if we ran out of input too soon. */
+
+ if (!q && *field_pointer)
+ return -EINVAL;
+
+ /* Move past the comma to the next timing value. */
+
+ p = q + 1;
+
+ }
+
+ /* Hand over the timing to the NFC. */
+
+ nfc->set_timing(this, &new);
/* Return success. */
@@ -624,6 +1114,7 @@ static ssize_t store_device_invalidate_page_cache(struct device *dev,
/* Device attributes that appear in sysfs. */
+static DEVICE_ATTR(report , 0555, show_device_report , 0);
static DEVICE_ATTR(numchips , 0444, show_device_numchips , 0);
static DEVICE_ATTR(physical_geometry, 0444, show_device_physical_geometry, 0);
static DEVICE_ATTR(nfc_info , 0444, show_device_nfc_info , 0);
@@ -631,6 +1122,11 @@ static DEVICE_ATTR(nfc_geometry , 0444, show_device_nfc_geometry , 0);
static DEVICE_ATTR(rom_geometry , 0444, show_device_rom_geometry , 0);
static DEVICE_ATTR(mtd_nand_info , 0444, show_device_mtd_nand_info , 0);
static DEVICE_ATTR(mtd_info , 0444, show_device_mtd_info , 0);
+static DEVICE_ATTR(timing_diagram , 0444, show_device_timing_diagram , 0);
+static DEVICE_ATTR(timing_help , 0444, show_device_timing_help , 0);
+
+static DEVICE_ATTR(invalidate_page_cache, 0644,
+ 0, store_device_invalidate_page_cache);
static DEVICE_ATTR(mark_block_bad, 0200,
0, store_device_mark_block_bad);
@@ -641,10 +1137,11 @@ static DEVICE_ATTR(ignorebad, 0644,
static DEVICE_ATTR(inject_ecc_error, 0644,
show_device_inject_ecc_error, store_device_inject_ecc_error);
-static DEVICE_ATTR(invalidate_page_cache, 0644,
- 0, store_device_invalidate_page_cache);
+static DEVICE_ATTR(timing, 0644,
+ show_device_timing, store_device_timing);
static struct device_attribute *device_attributes[] = {
+ &dev_attr_report,
&dev_attr_numchips,
&dev_attr_physical_geometry,
&dev_attr_nfc_info,
@@ -652,10 +1149,13 @@ static struct device_attribute *device_attributes[] = {
&dev_attr_rom_geometry,
&dev_attr_mtd_nand_info,
&dev_attr_mtd_info,
+ &dev_attr_invalidate_page_cache,
&dev_attr_mark_block_bad,
&dev_attr_ignorebad,
&dev_attr_inject_ecc_error,
- &dev_attr_invalidate_page_cache,
+ &dev_attr_timing,
+ &dev_attr_timing_help,
+ &dev_attr_timing_diagram,
};
/**
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-mil.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-mil.c
index 85d5a12ac59a..34505b8e6546 100644
--- a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-mil.c
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-mil.c
@@ -1682,12 +1682,10 @@ static int mil_scan_bbt(struct mtd_info *mtd)
/*
* Copy the device info into the per-device data. We can't just keep
* the pointer because that storage is reclaimed after initialization.
- * Notice that we null out the pointer to the device description, which
- * will also be reclaimed.
*/
this->device_info = *info;
- this->device_info.description = 0;
+ this->device_info.description = kstrdup(info->description, GFP_KERNEL);
/* Set up geometry. */
@@ -1845,8 +1843,8 @@ static int mil_boot_areas_init(struct gpmi_nfc_data *this)
*
* The code that handles "more than one" boot area actually only handles
* two. We *could* write the general case, but that would take a lot of
- * time to both write and test -- and, at this writing, we don't have a
- * chip that cares.
+ * time to both write and test -- and, right now, we don't have a chip
+ * that cares.
*/
/* Check if a boot area is larger than a single chip. */
@@ -1889,13 +1887,25 @@ static int mil_boot_areas_init(struct gpmi_nfc_data *this)
/* Find the general use MTD. */
for (i = 0; i < MAX_MTD_DEVICES; i++) {
+
+ /* Get the current MTD so we can examine it. */
+
search_mtd = get_mtd_device(0, i);
- if (!search_mtd)
- continue;
- if (search_mtd == ERR_PTR(-ENODEV))
+
+ /* Check if we got nonsense. */
+
+ if ((!search_mtd) || (search_mtd == ERR_PTR(-ENODEV)))
continue;
+
+ /* Check if the current MTD is one of our remainders. */
+
if (search_mtd->name == general_use_name)
mil->general_use_mtd = search_mtd;
+
+ /* Put the MTD back. We only wanted a quick look. */
+
+ put_mtd_device(search_mtd);
+
}
if (!mil->general_use_mtd) {
@@ -1910,8 +1920,9 @@ static int mil_boot_areas_init(struct gpmi_nfc_data *this)
#if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT)
/*
- * If control arrives here, there is more than one chip. We
- * partition the medium and concatenate the remainders like so:
+ * If control arrives here, there is more than one boot area.
+ * We partition the medium and concatenate the remainders like
+ * so:
*
* --- Chip 0 --- --- Chip 1 --- ... ------- Chip N -------
* / \ / \
@@ -1924,29 +1935,18 @@ static int mil_boot_areas_init(struct gpmi_nfc_data *this)
* | |/ /
* +----------+----------- ... ----------------------+
* | General Use |
- * +----------+----------- ... ----------------------+
+ * +---------------------- ... ----------------------+
*
* Notice that the results we leave in the master MTD table
- * are a little bit goofy:
+ * look like this:
*
* * Chip 0 Boot Area
* * Chip 1 Boot Area
- * * Chip 0 Remainder
- * * Medium Remainder
- *
- * There are two reasons:
- *
- * 1) Since the remainder partitions aren't very useful, we'd
- * actually prefer to "hide" (create them but not register
- * them). This was possible before 2.6.31, but now the
- * partitioning code automatically registers anything it
- * creates (thanks :(). It might be possible to "unregister"
- * these MTDs after the fact, but I don't have time to look
- * into the other effects that might have.
+ * * General Use
*
- * 2) Some user space programs expect the boot partitions to
- * appear first. This is naive, but let's try not to cause
- * any trouble, where we can avoid it.
+ * Some user space programs expect the boot partitions to
+ * appear first. This is naive, but let's try not to cause
+ * any trouble, where we can avoid it.
*/
/* Chip 0 Boot */
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v0.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v0.c
index a2a459835934..35321cc25546 100644
--- a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v0.c
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v0.c
@@ -88,7 +88,6 @@ static int set_geometry(struct gpmi_nfc_data *this)
static int check_transcription_stamp(struct gpmi_nfc_data *this)
{
struct physical_geometry *physical = &this->physical_geometry;
- struct nfc_geometry *nfc_geo = &this->nfc_geometry;
struct boot_rom_geometry *rom_geo = &this->rom_geometry;
struct mil *mil = &this->mil;
struct mtd_info *mtd = &mil->mtd;
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h
index 08afbcf2d943..6f14b73dd93d 100644
--- a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h
@@ -30,6 +30,7 @@
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/mtd/mtd.h>
@@ -362,36 +363,58 @@ struct gpmi_nfc_data {
* This structure contains the fundamental timing attributes for the NAND Flash
* bus and the GPMI NFC hardware.
*
- * @data_setup_in_ns: The data setup time, in nanoseconds. Usually the
- * maximum of tDS and tWP. A negative value
- * indicates this characteristic isn't known.
- * @data_hold_in_ns: The data hold time, in nanoseconds. Usually the
- * maximum of tDH, tWH and tREH. A negative value
- * indicates this characteristic isn't known.
- * @address_setup_in_ns: The address setup time, in nanoseconds. Usually
- * the maximum of tCLS, tCS and tALS. A negative
- * value indicates this characteristic isn't known.
- * @gpmi_sample_time_in_ns: A GPMI-specific timing parameter. A negative
- * value indicates this characteristic isn't known.
- * @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A
- * negative value indicates this characteristic
- * isn't known.
- * @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A
- * negative value indicates this characteristic
- * isn't known.
- * @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A
- * negative value indicates this characteristic
- * isn't known.
+ * @data_setup_in_ns: The data setup time, in nanoseconds. Usually the
+ * maximum of tDS and tWP. A negative value
+ * indicates this characteristic isn't known.
+ * @data_hold_in_ns: The data hold time, in nanoseconds. Usually the
+ * maximum of tDH, tWH and tREH. A negative value
+ * indicates this characteristic isn't known.
+ * @address_setup_in_ns: The address setup time, in nanoseconds. Usually
+ * the maximum of tCLS, tCS and tALS. A negative
+ * value indicates this characteristic isn't known.
+ * @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative value
+ * indicates this characteristic isn't known.
+ * @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A
+ * negative value indicates this characteristic isn't
+ * known.
+ * @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A
+ * negative value indicates this characteristic isn't
+ * known.
+ * @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A
+ * negative value indicates this characteristic isn't
+ * known.
*/
struct gpmi_nfc_timing {
- int8_t data_setup_in_ns;
- int8_t data_hold_in_ns;
- int8_t address_setup_in_ns;
- int8_t gpmi_sample_delay_in_ns;
- int8_t tREA_in_ns;
- int8_t tRLOH_in_ns;
- int8_t tRHOH_in_ns;
+ int8_t data_setup_in_ns;
+ int8_t data_hold_in_ns;
+ int8_t address_setup_in_ns;
+ int8_t gpmi_sample_delay_in_ns;
+ int8_t tREA_in_ns;
+ int8_t tRLOH_in_ns;
+ int8_t tRHOH_in_ns;
+};
+
+/**
+ * struct gpmi_nfc_hardware_timing - GPMI NFC hardware timing parameters.
+ *
+ * This structure contains timing information expressed in a form directly
+ * usable by the GPMI NFC hardware.
+ *
+ * @data_setup_in_cycles: The data setup time, in cycles.
+ * @data_hold_in_cycles: The data hold time, in cycles.
+ * @address_setup_in_cycles: The address setup time, in cycles.
+ * @use_half_periods: Indicates the clock is running slowly, so the
+ * NFC DLL should use half-periods.
+ * @sample_delay_factor: The sample delay factor.
+ */
+
+struct gpmi_nfc_hardware_timing {
+ uint8_t data_setup_in_cycles;
+ uint8_t data_hold_in_cycles;
+ uint8_t address_setup_in_cycles;
+ bool use_half_periods;
+ uint8_t sample_delay_factor;
};
/**
@@ -399,70 +422,89 @@ struct gpmi_nfc_timing {
*
* This structure embodies an abstract interface to the underlying NFC hardware.
*
- * @version: The NFC hardware version.
- * @description: A pointer to a human-readable description of
- * the NFC hardware.
- * @max_chip_count: The maximum number of chips the NFC can
- * possibly support (this value is a constant
- * for each NFC version). This may *not* be the
- * actual number of chips connected.
- * @max_data_setup_cycles: The maximum number of data setup cycles
- * that can be expressed in the hardware.
- * @max_data_sample_delay_cycles: The maximum number of data sample delay
- * cycles that can be expressed in the hardware.
- * @max_dll_clock_period_in_ns: The maximum period of the GPMI clock that the
- * sample delay DLL hardware can possibly work
- * with (the DLL is unusable with longer
- * periods). At HALF this value, the DLL must be
- * configured to use half-periods.
- * @dma_descriptors: A pool of DMA descriptors.
- * @isr_dma_channel: The DMA channel with which the NFC HAL is
- * working. We record this here so the ISR knows
- * which DMA channel to acknowledge.
- * @dma_done: The completion structure used for DMA
- * interrupts.
- * @bch_done: The completion structure used for BCH
- * interrupts.
- * @timing: The current timing configuration.
- * @init: Initializes the NFC hardware and data
- * structures. This function will be called
- * after everything has been set up for
- * communication with the NFC itself, but before
- * the platform has set up off-chip
- * communication. Thus, this function must not
- * attempt to communicate with the NAND Flash
- * hardware.
- * @set_geometry: Configures the NFC hardware and data
- * structures to match the physical NAND Flash
- * geometry.
- * @set_geometry: Configures the NFC hardware and data
- * structures to match the physical NAND Flash
- * geometry.
- * @exit: Shuts down the NFC hardware and data
- * structures. This function will be called
- * after the platform has shut down off-chip
- * communication but while communication with
- * the NFC itself still works.
- * @clear_bch: Clears a BCH interrupt (intended to be called
- * by a more general interrupt handler to do
- * device-specific clearing).
- * @is_ready: Returns true if the given chip is ready.
- * @begin: Begins an interaction with the NFC. This
- * function must be called before *any* of the
- * following functions so the NFC can prepare
- * itself.
- * @end: Ends interaction with the NFC. This function
- * should be called to give the NFC a chance to,
- * among other things, enter a lower-power
- * state.
- * @send_command: Sends the given buffer of command bytes.
- * @send_data: Sends the given buffer of data bytes.
- * @read_data: Reads data bytes into the given buffer.
- * @send_page: Sends the given given data and OOB bytes,
- * using the ECC engine.
- * @read_page: Reads a page through the ECC engine and
- * delivers the data and OOB bytes to the given
- * buffers.
+ * @version: The NFC hardware version.
+ * @description: A pointer to a human-readable description of
+ * the NFC hardware.
+ * @max_chip_count: The maximum number of chips the NFC can
+ * possibly support (this value is a constant for
+ * each NFC version). This may *not* be the actual
+ * number of chips connected.
+ * @max_data_setup_cycles: The maximum number of data setup cycles that
+ * can be expressed in the hardware.
+ * @internal_data_setup_in_ns: The time, in ns, that the NFC hardware requires
+ * for data read internal setup. In the Reference
+ * Manual, see the chapter "High-Speed NAND
+ * Timing" for more details.
+ * @max_sample_delay_factor: The maximum sample delay factor that can be
+ * expressed in the hardware.
+ * @max_dll_clock_period_in_ns: The maximum period of the GPMI clock that the
+ * sample delay DLL hardware can possibly work
+ * with (the DLL is unusable with longer periods).
+ * If the full-cycle period is greater than HALF
+ * this value, the DLL must be configured to use
+ * half-periods.
+ * @max_dll_delay_in_ns: The maximum amount of delay, in ns, that the
+ * DLL can implement.
+ * @dma_descriptors: A pool of DMA descriptors.
+ * @isr_dma_channel: The DMA channel with which the NFC HAL is
+ * working. We record this here so the ISR knows
+ * which DMA channel to acknowledge.
+ * @dma_done: The completion structure used for DMA
+ * interrupts.
+ * @bch_done: The completion structure used for BCH
+ * interrupts.
+ * @timing: The current timing configuration.
+ * @clock_frequency_in_hz: The clock frequency, in Hz, during the current
+ * I/O transaction. If no I/O transaction is in
+ * progress, this is the clock frequency during
+ * the most recent I/O transaction.
+ * @hardware_timing: The hardware timing configuration in effect
+ * during the current I/O transaction. If no I/O
+ * transaction is in progress, this is the
+ * hardware timing configuration during the most
+ * recent I/O transaction.
+ * @init: Initializes the NFC hardware and data
+ * structures. This function will be called after
+ * everything has been set up for communication
+ * with the NFC itself, but before the platform
+ * has set up off-chip communication. Thus, this
+ * function must not attempt to communicate with
+ * the NAND Flash hardware.
+ * @set_geometry: Configures the NFC hardware and data structures
+ * to match the physical NAND Flash geometry.
+ * @set_geometry: Configures the NFC hardware and data structures
+ * to match the physical NAND Flash geometry.
+ * @set_timing: Configures the NFC hardware and data structures
+ * to match the given NAND Flash bus timing.
+ * @get_timing: Returns the the clock frequency, in Hz, and
+ * the hardware timing configuration during the
+ * current I/O transaction. If no I/O transaction
+ * is in progress, this is the timing state during
+ * the most recent I/O transaction.
+ * @exit: Shuts down the NFC hardware and data
+ * structures. This function will be called after
+ * the platform has shut down off-chip
+ * communication but while communication with the
+ * NFC itself still works.
+ * @clear_bch: Clears a BCH interrupt (intended to be called
+ * by a more general interrupt handler to do
+ * device-specific clearing).
+ * @is_ready: Returns true if the given chip is ready.
+ * @begin: Begins an interaction with the NFC. This
+ * function must be called before *any* of the
+ * following functions so the NFC can prepare
+ * itself.
+ * @end: Ends interaction with the NFC. This function
+ * should be called to give the NFC a chance to,
+ * among other things, enter a lower-power state.
+ * @send_command: Sends the given buffer of command bytes.
+ * @send_data: Sends the given buffer of data bytes.
+ * @read_data: Reads data bytes into the given buffer.
+ * @send_page: Sends the given given data and OOB bytes,
+ * using the ECC engine.
+ * @read_page: Reads a page through the ECC engine and
+ * delivers the data and OOB bytes to the given
+ * buffers.
*/
#define NFC_DMA_DESCRIPTOR_COUNT (4)
@@ -475,8 +517,10 @@ struct nfc_hal {
const char *description;
const unsigned int max_chip_count;
const unsigned int max_data_setup_cycles;
- const unsigned int max_data_sample_delay_cycles;
+ const unsigned int internal_data_setup_in_ns;
+ const unsigned int max_sample_delay_factor;
const unsigned int max_dll_clock_period_in_ns;
+ const unsigned int max_dll_delay_in_ns;
/* Working variables. */
@@ -485,13 +529,17 @@ struct nfc_hal {
struct completion dma_done;
struct completion bch_done;
struct gpmi_nfc_timing timing;
+ unsigned long clock_frequency_in_hz;
/* Configuration functions. */
int (*init) (struct gpmi_nfc_data *);
int (*set_geometry)(struct gpmi_nfc_data *);
int (*set_timing) (struct gpmi_nfc_data *,
- const struct gpmi_nfc_timing *);
+ const struct gpmi_nfc_timing *);
+ void (*get_timing) (struct gpmi_nfc_data *,
+ unsigned long *clock_frequency_in_hz,
+ struct gpmi_nfc_hardware_timing *);
void (*exit) (struct gpmi_nfc_data *);
/* Call these functions to begin and end I/O. */
@@ -570,6 +618,8 @@ extern int gpmi_nfc_dma_init(struct gpmi_nfc_data *this);
extern void gpmi_nfc_dma_exit(struct gpmi_nfc_data *this);
extern int gpmi_nfc_set_geometry(struct gpmi_nfc_data *this);
extern int gpmi_nfc_dma_go(struct gpmi_nfc_data *this, int dma_channel);
+extern int gpmi_nfc_compute_hardware_timing(struct gpmi_nfc_data *this,
+ struct gpmi_nfc_hardware_timing *hw);
/* NFC HAL Structures */
diff --git a/drivers/mtd/nand/nand_device_info.c b/drivers/mtd/nand/nand_device_info.c
index ecd5b21189cc..1ab1d1d21811 100644
--- a/drivers/mtd/nand/nand_device_info.c
+++ b/drivers/mtd/nand/nand_device_info.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -1284,9 +1284,9 @@ static struct nand_device_info nand_device_info_table_type_9[] __initdata =
.data_hold_in_ns = 10,
.address_setup_in_ns = 25,
.gpmi_sample_delay_in_ns = 6,
- .tREA_in_ns = -1,
- .tRLOH_in_ns = -1,
- .tRHOH_in_ns = -1,
+ .tREA_in_ns = 20,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
"K9LBG08U0D",
},
{