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>
This commit is contained in:
Patrick Turley
2010-05-07 17:50:18 -05:00
committed by Matt Sealey
parent 414ff9f16f
commit b422b1ca4e
10 changed files with 1520 additions and 220 deletions

View File

@@ -1259,6 +1259,9 @@ static int gpmi_set_rate(struct clk *clk, unsigned long rate)
int ret = -EINVAL;
u32 reg, div;
/* Make absolutely certain the clock is enabled. */
local_clk_enable(clk);
/* if the desired clock can be sourced from ref_xtal,
* use ref_xtal to save power
*/
@@ -1284,6 +1287,10 @@ static int gpmi_set_rate(struct clk *clk, unsigned long rate)
ret = clk_busy_wait(clk);
out:
/* Undo the enable above. */
local_clk_disable(clk);
if (ret != 0)
printk(KERN_ERR "%s: error %d\n", __func__, ret);
return ret;
@@ -1309,11 +1316,33 @@ static int gpmi_set_parent(struct clk *clk, struct clk *parent)
return ret;
}
/* handle peripheral clocks whose optimal parent dependent on
* system parameters such as cpu_clk rate. For now, this optimization
* only occurs to the peripheral clock when it's not in use to avoid
* handling more complex system clock coordination issues.
*/
static int gpmi_set_sys_dependent_parent(struct clk *clk)
{
if ((clk->ref & CLK_EN_MASK) == 0) {
if (clk_get_rate(&cpu_clk) > ref_xtal_get_rate(&ref_xtal_clk)) {
clk_set_parent(clk, &ref_io_clk);
clk_set_rate(clk, PLL_ENABLED_MAX_CLK_GPMI);
} else {
clk_set_parent(clk, &ref_xtal_clk);
clk_set_rate(clk, ref_xtal_get_rate(&ref_xtal_clk));
}
}
return 0;
}
static struct clk gpmi_clk = {
.parent = &ref_io_clk,
.secondary = 0,
.flags = 0,
.set_parent = gpmi_set_parent,
.set_sys_dependent_parent = gpmi_set_sys_dependent_parent,
.enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_GPMI,
.enable_bits = BM_CLKCTRL_GPMI_CLKGATE,

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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.
@@ -171,6 +171,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.
*
@@ -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,
};

View File

@@ -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.
@@ -173,6 +173,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.
*
@@ -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,
};

View File

@@ -57,6 +57,54 @@ static struct boot_rom_helper *(boot_rom_helpers[]) = {
&gpmi_nfc_boot_rom_helper_v1,
};
/**
* 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.
*
@@ -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
);
}
@@ -421,6 +452,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.
*
@@ -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;
/* Invalidate the page cache. */
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,
};
this->mil.nand.pagebuf = -1;
int8_t **field_pointer = field_pointers;
/*
* Loop over comma-separated timing values in the incoming buffer,
* assigning them to fields in the timing structure as we go along.
*/
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,
};
/**

View File

@@ -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
* * General Use
*
* 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.
*
* 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 */

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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",
},
{