diff options
author | Patrick Turley <patrick.turley@freescale.com> | 2009-10-27 16:30:37 -0500 |
---|---|---|
committer | Patrick Turley <patrick.turley@freescale.com> | 2009-10-28 15:36:04 -0500 |
commit | b840602a6ec0ccf0d9bf6c213f28b3baf176d37f (patch) | |
tree | 626e50e8b7c9a5c11f166cda2e3c63ff6303a1fd /drivers/mtd | |
parent | ee0f7386a16fef256fde86548192232ec98ed95a (diff) |
ENGR00115074: [MX233_BSP] Comment, clarify and characterize the NAND Flash
New comments and some variable renaming to improve the understandability of
the code.
Signed-off-by: Patrick Turley <patrick.turley@freescale.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/gpmi/gpmi-base.c | 896 | ||||
-rw-r--r-- | drivers/mtd/nand/gpmi/gpmi-bbt.c | 306 | ||||
-rw-r--r-- | drivers/mtd/nand/gpmi/gpmi-bch.c | 88 | ||||
-rw-r--r-- | drivers/mtd/nand/gpmi/gpmi-ecc8.c | 143 | ||||
-rw-r--r-- | drivers/mtd/nand/gpmi/gpmi.h | 204 |
5 files changed, 1485 insertions, 152 deletions
diff --git a/drivers/mtd/nand/gpmi/gpmi-base.c b/drivers/mtd/nand/gpmi/gpmi-base.c index 8bfb1c677a4e..fb85d2e2098c 100644 --- a/drivers/mtd/nand/gpmi/gpmi-base.c +++ b/drivers/mtd/nand/gpmi/gpmi-base.c @@ -40,24 +40,90 @@ #include <mach/dma.h> #include "gpmi.h" +/* + * Set this variable to a value greater than zero to see varying levels of + * debugging output. + */ + static int debug; + +/* + * This variable counts the total number of times the driver has copied either + * page data or OOB data from/to a DMA buffer. + */ + static int copies; + +/* + * Indicates that this driver should attempt to perform DMA directly to/from + * buffers passed into this driver. If false, this driver will use its own + * buffer for DMA and copy data between this buffer and the buffers that are + * passed in. + */ + static int map_buffers = true; + static int ff_writes; + +/* + * Forces all OOB reads and writes to NOT use ECC. + */ + static int raw_mode; + +/* + * Indicates the driver should register an MTD that represents the entire + * medium. + */ + static int add_mtd_entire; + +/* + * Indicates the driver should register a separate MTD for every physical chip. + */ + static int add_mtd_chip; + +/* + * Indicates the driver should report that *all* blocks are good. + */ + static int ignorebad; + +/* + * The maximum number of chips for which the NAND Flash MTD system is allowed to + * scan. + */ + static int max_chips = 4; + +/* + * + */ + static long clk = -1; + +/* + * This variable is connected to the "bch" module parameter. If set, it + * indicates the driver should use the BCH hardware block instead of the ECC8 + * hardware block for error correction. + */ + static int bch /* = 0 */ ; +/* Forward references. */ + static int gpmi_nand_init_hw(struct platform_device *pdev, int request_pins); static void gpmi_nand_release_hw(struct platform_device *pdev); static int gpmi_dma_exchange(struct gpmi_nand_data *g, struct stmp3xxx_dma_descriptor *dma); static void gpmi_read_buf(struct mtd_info *mtd, uint8_t * buf, int len); +/* + * This structure contains the "safe" GPMI timings that should succeed with any + * NAND Flash device (although, with less-than-optimal performance). + */ + struct gpmi_nand_timing gpmi_safe_timing = { .address_setup = 25, .data_setup = 80, @@ -66,8 +132,9 @@ struct gpmi_nand_timing gpmi_safe_timing = { }; /* - * define OOB placement schemes for 4k and 2k page devices + * ECC layout descriptions for various device geometries. */ + static struct nand_ecclayout gpmi_oob_128 = { .oobfree = { { @@ -90,18 +157,35 @@ static struct nand_ecclayout gpmi_oob_64 = { }, }; +/** + * gpmi_cycles_ceil - Translates timings in nanoseconds to GPMI clock cycles. + * + * @ntime: The time in nanoseconds. + * @period: The GPMI clock period. + */ static inline u32 gpmi_cycles_ceil(u32 ntime, u32 period) { int k; + /* + * Compute the minimum number of clock periods that entirely contain the + * given time. + */ + k = (ntime + period - 1) / period; + + /* + * A cycle count of less than 1 can fatally confuse the hardware. + */ + if (k == 0) k++; + return k; } /** - * gpmi_timer_expiry - timer expiry handling + * gpmi_timer_expiry - Inactivity timer expiration handler. */ static void gpmi_timer_expiry(unsigned long d) { @@ -145,12 +229,10 @@ static void gpmi_self_wakeup(struct gpmi_nand_data *g) } /** - * gpmi_set_timings - set GPMI timings - * @pdev: pointer to GPMI platform device - * @tm: pointer to structure &gpmi_nand_timing with new timings + * gpmi_set_timings - Set GPMI timings. * - * During initialization, GPMI uses safe sub-optimal timings, which - * can be changed after reading boot control blocks + * @pdev: A pointer to the owning platform device. + * @tm: A pointer to the new timings. */ void gpmi_set_timings(struct platform_device *pdev, struct gpmi_nand_timing *tm) { @@ -203,6 +285,9 @@ void gpmi_set_timings(struct platform_device *pdev, struct gpmi_nand_timing *tm) g->use_count--; } +/** + * bch_mode - Return a hardware register value that selects BCH. + */ static inline u32 bch_mode(void) { u32 c1 = 0; @@ -215,8 +300,10 @@ static inline u32 bch_mode(void) } /** - * gpmi_nand_init_hw - initialize the hardware - * @pdev: pointer to platform device + * gpmi_nand_init_hw - Initialize the hardware. + * + * @pdev: A pointer to the owning platform device. + * @request_pins: Indicates this function should request GPMI pins. * * Initialize GPMI hardware and set default (safe) timings for NAND access. * Returns error code or 0 on success @@ -228,23 +315,43 @@ static int gpmi_nand_init_hw(struct platform_device *pdev, int request_pins) (struct gpmi_platform_data *)pdev->dev.platform_data; int err = 0; + /* Try to get the GPMI clock. */ + g->clk = clk_get(NULL, "gpmi"); if (IS_ERR(g->clk)) { err = PTR_ERR(g->clk); - dev_err(&pdev->dev, "cannot set failsafe clockrate\n"); + dev_err(&pdev->dev, "Can't get GPMI clock\n"); goto out; } + + /* Turn on the GPMI clock. */ + clk_enable(g->clk); + + /* + * Check the clock rate setting. We don't allow this value to go below + * 24KHz because some chips don't work in that regime. + */ + if (clk <= 0) - clk = 24000; /* safe setting, some chips do not work on - speeds >= 24kHz */ + clk = 24000; + + /* + * Set the GPMI clock rate and record the value that was actually + * implemented. + */ + clk_set_rate(g->clk, clk); clk = clk_get_rate(g->clk); + /* Check if we're supposed to ask for our pins. */ + if (request_pins) gpd->pinmux(1); + /* Reset the GPMI block. */ + stmp3xxx_reset_block(HW_GPMI_CTRL0 + REGS_GPMI_BASE, 1); /* this CLEARS reset, despite of its name */ @@ -254,10 +361,13 @@ static int gpmi_nand_init_hw(struct platform_device *pdev, int request_pins) stmp3xxx_setl(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, REGS_GPMI_BASE + HW_GPMI_CTRL1); - /* ...and ECC module */ + /* + * Select the ECC to use. The bch_mode() function returns a value that + * selects whichever hardware is appropriate (q.v.). + */ stmp3xxx_setl(bch_mode(), REGS_GPMI_BASE + HW_GPMI_CTRL1); - /* choose NAND mode (1 means ATA, 0 - NAND */ + /* Choose NAND mode (1 means ATA, 0 - NAND */ stmp3xxx_clearl(BM_GPMI_CTRL1_GPMI_MODE, REGS_GPMI_BASE + HW_GPMI_CTRL1); @@ -267,9 +377,10 @@ out: /** * gpmi_nand_release_hw - free the hardware + * * @pdev: pointer to platform device * - * In opposite to gpmi_nand_init_hw, release all acquired resources + * In opposite to gpmi_nand_init_hw, release all acquired resources. */ static void gpmi_nand_release_hw(struct platform_device *pdev) { @@ -284,6 +395,11 @@ static void gpmi_nand_release_hw(struct platform_device *pdev) gpd->pinmux(0); } +/** + * gpmi_dma_is_error - + * + * @g: Per-device data structure. + */ static int gpmi_dma_is_error(struct gpmi_nand_data *g) { /* u32 n = __raw_readl(g->dma_ch); */ @@ -301,9 +417,10 @@ static int gpmi_dma_is_error(struct gpmi_nand_data *g) } /** - * gpmi_dma_exchange - run DMA to exchange with NAND chip + * gpmi_dma_exchange - Run DMA to exchange with NAND chip * - * @g: structure associated with NAND chip + * @g: Per-device data structure. + * @d: DMA descriptor. * * Run DMA and wait for completion */ @@ -353,7 +470,7 @@ static int gpmi_dma_exchange(struct gpmi_nand_data *g, } /** - * gpmi_ecc_read_page - replacement for nand_read_page + * gpmi_ecc_read_page - Replacement for nand_read_page * * @mtd: mtd info structure * @chip: nand chip info structure @@ -424,6 +541,12 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, return err; } +/** + * is_ff - Checks if all the bits in a buffer are set. + * + * @buffer: The buffer of interest. + * @size: The size of the buffer. + */ static inline int is_ff(const u8 * buffer, size_t size) { while (size--) { @@ -520,7 +643,7 @@ static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t * buf, int len) copies++; } - /* write plain data */ + /* Write plain data */ chain->command->cmd = BF(len, APBH_CHn_CMD_XFER_COUNT) | BF(4, APBH_CHn_CMD_CMDWORDS) | @@ -641,7 +764,7 @@ static u8 gpmi_read_byte(struct mtd_info *mtd) /** * gpmi_read_word - replacement for nand_read_word - * @mtd: MTD device + * @mtd: The owning MTD. * * Uses gpmi_read_buf to read 2 bytes from device */ @@ -654,7 +777,17 @@ static u16 gpmi_read_word(struct mtd_info *mtd) } /** - * gpmi_erase - erase a block and update BBT table + * gpmi_erase - Hook for erase operations at the MTD level. + * + * We install this function in the "erase" function pointer of the owning + * struct mtd_info. Thus, this function will get called *instead* of the + * function that the NAND Flash MTD system installed (see nand_erase()). + * + * We do this because, if an erase operation fails, then the block should be + * marked bad. Unfortunately, the NAND Flash MTD code doesn't do this. Since + * we've "hooked" the call, we can "override" the base NAND Flash MTD behavior + * and make sure the proper marking gets done before we return to the + * original caller. * * @mtd: MTD device * @instr: erase instruction @@ -683,9 +816,13 @@ int gpmi_erase(struct mtd_info *mtd, struct erase_info *instr) } /** - * gpmi_dev_ready - poll the RDY pin + * gpmi_dev_ready - Wait until the medium is ready. * - * @mtd: MTD device + * This function is supposed to return the instantaneous state of the medium. + * Instead, it actually waits for the medium to be ready. This is mostly + * harmless, but isn't actually correct. + * + * @mtd: The owning MTD. */ static int gpmi_dev_ready(struct mtd_info *mtd) { @@ -721,11 +858,20 @@ static int gpmi_dev_ready(struct mtd_info *mtd) } /** - * gpmi_hwcontrol - set command/address byte to the device + * gpmi_hwcontrol - Send command/address byte to the NAND Flash. * - * @mtd: MTD device - * @cmd: command byte - * @ctrl: control flags + * This is the function that we install in the cmd_ctrl function pointer of the + * owning struct nand_chip. The only functions in the reference implementation + * that use these functions pointers are cmdfunc and select_chip. + * + * In this driver, we implement our own select_chip, so this function will only + * be called by the reference implementation's cmdfunc. For this reason, we can + * ignore the chip enable bit and concentrate only on sending bytes to the + * NAND Flash. + * + * @mtd: The owning MTD. + * @cmd: The command byte. + * @ctrl: Control flags. */ static void gpmi_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { @@ -734,12 +880,30 @@ static void gpmi_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) struct stmp3xxx_dma_descriptor *chain = g->cchip->d; int ret; + /* + * Every operation begins with a series of command and address bytes, + * which are distinguished by either the Address Latch Enable (ALE) or + * Command Latch Enable (CLE) being asserted. Finally, when the caller + * is actually ready to execute the command, he will deassert both latch + * enables. + * + * Rather than run a separate DMA operation for every single byte, we + * queue them up and run a single DMA operation for the entire series + * of command and data bytes. + */ + if ((ctrl & (NAND_ALE | NAND_CLE))) { if (cmd != NAND_CMD_NONE) g->cmd_buffer[g->cmd_buffer_sz++] = cmd; return; } + /* + * If control arrives here, the caller has deasserted both the ALE and + * CLE, which means he's ready to run an operation. Check if we actually + * have any bytes to send. + */ + if (g->cmd_buffer_sz == 0) return; @@ -1042,7 +1206,7 @@ static int gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, /** * gpmi_irq - IRQ handler * - * @irq: irq no + * @irq: Interrupt number. * @context: IRQ context, pointer to gpmi_nand_data */ static irqreturn_t gpmi_irq(int irq, void *context) @@ -1058,6 +1222,12 @@ static irqreturn_t gpmi_irq(int irq, void *context) return IRQ_HANDLED; } +/** + * gpmi_select_chip() - NAND Flash MTD Interface select_chip() + * + * @mtd: A pointer to the owning MTD. + * @chipnr: The chip number to select, or -1 to select no chip. + */ static void gpmi_select_chip(struct mtd_info *mtd, int chipnr) { struct nand_chip *chip = mtd->priv; @@ -1075,6 +1245,19 @@ static void gpmi_select_chip(struct mtd_info *mtd, int chipnr) g->cchip = g->chips + chipnr; } +/** + * gpmi_command() - NAND Flash MTD Interface cmdfunc() + * + * This function is a veneer that calls the function originally installed by the + * NAND Flash MTD code. + * + * @mtd: A pointer to the owning MTD. + * @command: The command code. + * @column: The column address associated with this command code, or -1 if + * no column address applies. + * @page_addr: The page address associated with this command code, or -1 if no + * page address applies. + */ static void gpmi_command(struct mtd_info *mtd, unsigned int command, int column, int page_addr) { @@ -1084,6 +1267,16 @@ static void gpmi_command(struct mtd_info *mtd, unsigned int command, g->saved_command(mtd, command, column, page_addr); } +/** + * gpmi_read_oob() - MTD Interface read_oob(). + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. + * + * @mtd: A pointer to the MTD. + * @from: The starting address to read. + * @ops: Describes the operation. + */ static int gpmi_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { @@ -1097,6 +1290,16 @@ static int gpmi_read_oob(struct mtd_info *mtd, loff_t from, return ret; } +/** + * gpmi_read_oob() - MTD Interface write_oob(). + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. + * + * @mtd: A pointer to the MTD. + * @to: The starting address to write. + * @ops: Describes the operation. + */ static int gpmi_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { @@ -1111,7 +1314,12 @@ static int gpmi_write_oob(struct mtd_info *mtd, loff_t to, } /** - * perform the needed steps between nand_scan_ident and nand_scan_tail + * gpmi_scan_middle - Intermediate initialization. + * + * @g: Per-device data structure. + * + * Rather than call nand_scan(), this function makes the same calls, but + * inserts this function into the initialization pathway. */ static int gpmi_scan_middle(struct gpmi_nand_data *g) { @@ -1123,7 +1331,15 @@ static int gpmi_scan_middle(struct gpmi_nand_data *g) g->chip.chipsize = do_div(g->mtd.size, g->chip.numchips); } + /* + * In all currently-supported geometries, the number of ECC bytes that + * apply to the OOB bytes is the same. + */ + g->ecc_oob_bytes = 9; + + /* Look at the page size and configure appropriately. */ + switch (g->mtd.writesize) { case 2048: /* 2K page */ g->chip.ecc.layout = &gpmi_oob_64; @@ -1157,19 +1373,31 @@ static int gpmi_scan_middle(struct gpmi_nand_data *g) return -ERANGE; } + /* + * Hook the command function provided by the reference implementation. + * This has to be done here, rather than at initialization time, because + * the NAND Flash MTD installed the reference implementation only just + * now. + */ + g->saved_command = g->chip.cmdfunc; g->chip.cmdfunc = gpmi_command; + /* Install the ECC. */ + if (oobsize > 0) { g->mtd.oobsize = oobsize; /* otherwise error; oobsize should be set in valid cases */ - g->hc = gpmi_hwecc_chip_find("ecc8"); + g->hc = gpmi_ecc_find("ecc8"); g->hc->setup(g->hc, 0, g->mtd.writesize, g->mtd.oobsize); return 0; } + /* If control arrives here, something has gone wrong. */ + return -ENXIO; + } /** @@ -1308,6 +1536,14 @@ static void gpmi_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } +/** + * gpmi_init_chip - Sets up the driver to control the given chip. + * + * @pdev: A pointer to the owning struct platform_device. + * @g: Per-device data. + * @n: The chip number. + * @dma_ch: The DMA channel to use with this chip. + */ static int gpmi_init_chip(struct platform_device *pdev, struct gpmi_nand_data *g, int n, unsigned dma_ch) { @@ -1343,6 +1579,13 @@ out_all: return err; } +/** + * gpmi_deinit_chip - Tears down this driver's control of the given chip. + * + * @pdev: A pointer to the owning struct platform_device. + * @g: Per-device data. + * @n: The chip number. + */ static void gpmi_deinit_chip(struct platform_device *pdev, struct gpmi_nand_data *g, int n) { @@ -1364,33 +1607,43 @@ static void gpmi_deinit_chip(struct platform_device *pdev, stmp3xxx_dma_release(dma_ch); } -#if 0 -static int gpmi_to_concat(struct mtd_partition *part, char **list) -{ - while (list && *list) { - if (strcmp(part->name, *list) == 0) { - pr_debug("Partition '%s' will be concatenated\n", - part->name); - return true; - } - list++; - } - pr_debug("Partition '%s' is left as-is", part->name); - return false; -} -#endif - +/** + * gpmi_create_partitions - Create platform-driven partitions. + * + * This function creates partitions based on the platform data. + * + * @g: Per-device data. + * @gpd: Per-device platform data. + * @chipsize: The size of a single, physical chip in bytes. + */ static void gpmi_create_partitions(struct gpmi_nand_data *g, struct gpmi_platform_data *gpd, uint64_t chipsize) { + #ifdef CONFIG_MTD_PARTITIONS int chip, p; char chipname[20]; + /* + * We have a single MTD now that represents the entire medium. We want + * an MTD for each physical chip in the medium. + * + * If there's only one chip, then we can simply use the medium MTD. + * + * If there are multiple chips, we need to create partition MTDs that + * represent the subsets of the medium occupied by each physical chip. + */ + if (g->numchips == 1) - g->masters[0] = &g->mtd; + g->chip_mtds[0] = &g->mtd; else { + + /* + * Construct an array of partition descriptions, one for each + * physical chip. + */ + for (chip = 0; chip < g->numchips; chip++) { memset(g->chip_partitions + chip, 0, sizeof(g->chip_partitions[chip])); @@ -1402,23 +1655,68 @@ static void gpmi_create_partitions(struct gpmi_nand_data *g, g->chip_partitions[chip].offset = chipsize * chip; g->chip_partitions[chip].mask_flags = 0; } + + /* + * Derive a partition MTD for each physical chip. + * + * This function will register each partition MTD it creates + * that doesn't have the corresponding "mtdp" field set. Since + * we've explicitly set the "mtdp" field for each, *none* of + * these partitions will be registered. + */ + add_mtd_partitions(&g->mtd, g->chip_partitions, g->numchips); + } + g->n_concat = 0; memset(g->concat, 0, sizeof(g->concat)); + + /* + * Loop over physical chips, handling sub-partitions for each in turn. + */ + for (chip = 0; chip < g->numchips; chip++) { + + /* + * If the module parameter "add_mtd_chip" is set, then we want + * to register the partition MTD we made for this chip. Among + * other things, this will make it visible to user space. + */ + if (add_mtd_chip) { - printk(KERN_NOTICE "Adding MTD for the chip %d\n", + printk(KERN_NOTICE "Registering an MTD for chip %d\n", chip); - add_mtd_device(g->masters[chip]); + add_mtd_device(g->chip_mtds[chip]); } - if (chip >= gpd->items) + + /* + * Check if the platform data includes partition descriptions + * for this chip. If not, move to the next one. + */ + + if (chip >= gpd->chip_count) continue; - add_mtd_partitions(g->masters[chip], - gpd->parts[chip].partitions, - gpd->parts[chip].nr_partitions); + + /* + * If control arrives here, the platform data includes + * partition descriptions for this chip. Loop over all the + * partition descriptions for this chip, checking if any appear + * on the list to be concatenated. + * + * For each partition that is to be concatenated, set its "mtdp" + * pointer such that the new MTD will be added to an array + * rather than registered in the public MTD list. Later, after + * we've concatenated all these MTDs, we will register the + * final result. + */ + + add_mtd_partitions(g->chip_mtds[chip], + gpd->chip_partitions[chip].partitions, + gpd->chip_partitions[chip].nr_partitions); } if (g->n_concat > 0) { + #ifdef CONFIG_MTD_CONCAT if (g->n_concat == 1) #endif @@ -1434,38 +1732,115 @@ static void gpmi_create_partitions(struct gpmi_nand_data *g, } #endif } + + /* + * Set a flag in the per-device data structure to show that that we + * created custom partitions. This is important because, when we need + * to disassemble them, we need to be aware of how we created them. + */ + g->custom_partitions = true; + #endif + } +/** + * gpmi_delete_partitions - Remove the partitions created by this driver. + * + * @g: The per-device data structure. + */ static void gpmi_delete_partitions(struct gpmi_nand_data *g) { + + /* + * First, check if MTD partitioning is even available. If not, then + * we don't have to do any special work. + */ + #ifdef CONFIG_MTD_PARTITIONS int chip, p; + /* + * If control arrives here, MTD partitioning is available. But, if we + * didn't construct any custom partitions, then we don't have to do any + * special work. + */ + if (!g->custom_partitions) return; + #ifdef CONFIG_MTD_CONCAT + + /* + * If control arrives here, we constructed some custom partitions, and + * we have to disassemble them carefully. + * + * If we concatenated any MTDs, delete the synthetic MTD first. + */ + if (g->concat_mtd) del_mtd_device(g->concat_mtd); + + /* + * Check if we concatenated any partitions, which now must be + * disassembled. + * + * This process is complicated by the fact that MTD concatenation may or + * may not be available. Here are the cases: + * + * * If concatenation is not available: + * + * * De-register all the partition MTDs that would have been + * concatenated. + * + * * If concatenation is available: + * + * * If there is only one partition: + * + * * De-register that one partition MTD. + */ + if (g->n_concat == 1) #endif for (p = 0; p < g->n_concat; p++) del_mtd_device(g->concat[p]); + /* Handle the partitions related to each physical chip. */ + for (chip = 0; chip < g->numchips; chip++) { - del_mtd_partitions(g->masters[chip]); + + /* + * De-register and destroy any partition MTDs that may have been + * derived from the MTD that represents this chip. + */ + + del_mtd_partitions(g->chip_mtds[chip]); + + /* + * If the module variable 'add_mtd_chip' is set, then we + * registered the MTD that represents this chip. De-register it + * now. + */ + if (add_mtd_chip) - del_mtd_device(g->masters[chip]); + del_mtd_device(g->chip_mtds[chip]); + + /* + * Free the memory we used to hold the name of the MTD that + * represented this chip. + */ + kfree(g->chip_partitions[chip].name); + } #endif } /** - * gpmi_nand_probe - probe for the GPMI device + * gpmi_nand_probe - Probes for a GPMI device and, if possible, takes ownership. * - * Probe for GPMI device and discover NAND chips + * @pdev: A pointer to the platform device. */ static int __init gpmi_nand_probe(struct platform_device *pdev) { @@ -1477,7 +1852,7 @@ static int __init gpmi_nand_probe(struct platform_device *pdev) int dma; unsigned long long chipsize; - /* Allocate memory for the device structure (and zero it) */ + /* Allocate memory for the per-device structure (and zero it). */ g = kzalloc(sizeof(*g), GFP_KERNEL); if (!g) { dev_err(&pdev->dev, "failed to allocate gpmi_nand_data\n"); @@ -1569,63 +1944,145 @@ static int __init gpmi_nand_probe(struct platform_device *pdev) g->selected_chip = -1; g->ignorebad = ignorebad; /* copy global setting */ + /* Initialize the MTD object. */ + g->mtd.priv = &g->chip; g->mtd.name = dev_name(&pdev->dev); g->mtd.owner = THIS_MODULE; + /* + * Signal Control + */ + g->chip.cmd_ctrl = gpmi_hwcontrol; - g->chip.read_word = gpmi_read_word; - g->chip.read_byte = gpmi_read_byte; - g->chip.read_buf = gpmi_read_buf; - g->chip.write_buf = gpmi_write_buf; + + /* + * Chip Control + * + * The cmdfunc pointer is assigned elsewhere. + * We use the reference implementation of waitfunc. + */ + + g->chip.dev_ready = gpmi_dev_ready; g->chip.select_chip = gpmi_select_chip; + + /* + * Low-level I/O + */ + + g->chip.read_byte = gpmi_read_byte; + g->chip.read_word = gpmi_read_word; + g->chip.read_buf = gpmi_read_buf; + g->chip.write_buf = gpmi_write_buf; g->chip.verify_buf = gpmi_verify_buf; - g->chip.dev_ready = gpmi_dev_ready; - g->chip.ecc.mode = NAND_ECC_HW_SYNDROME; - g->chip.ecc.write_oob = gpmi_ecc_write_oob; - g->chip.ecc.read_oob = gpmi_ecc_read_oob; - g->chip.ecc.write_page = gpmi_ecc_write_page; - g->chip.ecc.read_page = gpmi_ecc_read_page; - g->chip.ecc.read_page_raw = gpmi_read_page_raw; + /* + * ECC Control + * + * None of these functions are necessary: + * - ecc.hwctl + * - ecc.calculate + * - ecc.correct + */ + + /* + * ECC-aware I/O + */ + + g->chip.ecc.read_page = gpmi_ecc_read_page; + g->chip.ecc.read_page_raw = gpmi_read_page_raw; + g->chip.ecc.write_page = gpmi_ecc_write_page; g->chip.ecc.write_page_raw = gpmi_write_page_raw; - g->chip.ecc.size = 512; - g->chip.write_page = gpmi_write_page; + /* + * High-level I/O + * + * This driver doesn't assign the erase_cmd pointer at the NAND Flash + * chip level. Instead, it intercepts the erase operation at the MTD + * level (see the assignment to mtd.erase below). + */ + + g->chip.write_page = gpmi_write_page; + g->chip.ecc.read_oob = gpmi_ecc_read_oob; + g->chip.ecc.write_oob = gpmi_ecc_write_oob; + + /* + * Bad Block Management + * + * We use the reference implementation of block_markbad. + */ - g->chip.scan_bbt = gpmi_scan_bbt; g->chip.block_bad = gpmi_block_bad; + g->chip.scan_bbt = gpmi_scan_bbt; - g->cmd_buffer_sz = 0; + g->chip.ecc.mode = NAND_ECC_HW_SYNDROME; + g->chip.ecc.size = 512; + + g->cmd_buffer_sz = 0; + + /* + * At this point, most drivers would call nand_scan(). Instead, this + * driver directly performs most of the same operations nand_scan() + * would, and introduces more initialization work in the "middle." + */ - /* first scan to find the device and get the page size */ if (nand_scan_ident(&g->mtd, max_chips) - || gpmi_scan_middle(g) - || nand_scan_tail(&g->mtd)) { - dev_err(&pdev->dev, "No NAND found\n"); + || gpmi_scan_middle(g) + || nand_scan_tail(&g->mtd)) { + dev_err(&pdev->dev, "No NAND Flash chips found\n"); /* errors found on some step */ goto out7; } - g->chip.options |= NAND_NO_SUBPAGE_WRITE; - g->chip.subpagesize = g->mtd.writesize; - g->mtd.subpage_sft = 0; + /* Completely disallow partial page writes. */ + + g->chip.options |= NAND_NO_SUBPAGE_WRITE; + g->chip.subpagesize = g->mtd.writesize; + g->mtd.subpage_sft = 0; + + /* Hook erase operations at the MTD level. */ g->mtd.erase = gpmi_erase; - g->saved_read_oob = g->mtd.read_oob; + /* Hook OOB read and write operations at the MTD level. */ + + g->saved_read_oob = g->mtd.read_oob; g->saved_write_oob = g->mtd.write_oob; - g->mtd.read_oob = gpmi_read_oob; - g->mtd.write_oob = gpmi_write_oob; + g->mtd.read_oob = gpmi_read_oob; + g->mtd.write_oob = gpmi_write_oob; #ifdef CONFIG_MTD_PARTITIONS + + /* + * Check if we got any information about the platform. If not, then we + * have no guidance about how to set up MTD partitions, so we should + * just leave now. + */ + if (gpd == NULL) goto out_all; - if (gpd->parts[0].part_probe_types) { + /* + * Check if the platform has specified a list of partition description + * parsers. If so, look for partitioning information from the parsers + * associated with the *first* partition set. We ignore any parsers + * attached to the second partition set (which makes having them rather + * silly, doesn't it?). + * + * Notice that any discovered partitions are recorded in the per-device + * data. + * + * Notice that, if the parsers find some partitions, we set the + * partition type to "command line". This isn't strictly accurate + * because we don't know which parsers were used, so we don't know if + * these partitions are *really* coming from a command line. + */ + + if (gpd->chip_partitions[0].part_probe_types) { g->nr_parts = parse_mtd_partitions(&g->mtd, - gpd->parts[0]. - part_probe_types, &g->parts, + gpd->chip_partitions[0]. + part_probe_types, + &g->chip_partitions, 0); if (g->nr_parts > 0) part_type = "command line"; @@ -1633,41 +2090,103 @@ static int __init gpmi_nand_probe(struct platform_device *pdev) g->nr_parts = 0; } - if (g->nr_parts == 0 && gpd->parts[0].partitions) { - g->parts = gpd->parts[0].partitions; - g->nr_parts = gpd->parts[0].nr_partitions; + /* + * If no partition descriptions have been discovered, but the platform + * data has partitions for us, then adopt the partitions that came from + * the platform. + */ + + if (g->nr_parts == 0 && gpd->chip_partitions[0].partitions) { + g->parts = gpd->chip_partitions[0].partitions; + g->nr_parts = gpd->chip_partitions[0].nr_partitions; part_type = "static"; } + /* + * At this point, we should have partition information either from the + * a parser or the platform. If we have nothing yet, then forget the + * whole partitioning thing. + */ + if (g->nr_parts == 0) { dev_err(&pdev->dev, "Neither part_probe_types nor " "partitions was specified in platform_data"); goto out_all; } + /* + * If control arrives here, we have some partitions to use. Announce + * what we've found. + */ + dev_info(&pdev->dev, "Using %s partition definition\n", part_type); + /* + * Transcribe the number of chips discovered by the scan to the + * per-device data structure. This assignment should NOT happen here, + * because it's conditional on whether partitioning is on. + */ + g->numchips = g->chip.numchips; + + /* + * Compute the number of bytes per chip by taking the size of the entire + * medium and dividing by the number of chips. + */ + chipsize = g->mtd.size; do_div(chipsize, (unsigned long)g->numchips); + /* + * Check if the partitions we're using came from a parser (in which case + * they should be used as-is), or came from the platform data (in which + * case we have some special processing we like to do). + */ + if (!strcmp(part_type, "command line")) - add_mtd_partitions(&g->mtd, g->parts, g->nr_parts); + add_mtd_partitions(&g->mtd, g->chip_partitions, g->nr_parts); else gpmi_create_partitions(g, gpd, chipsize); + /* + * Check if we're supposed to register the MTD that represents the + * entire medium. + */ + if (add_mtd_entire) { printk(KERN_NOTICE "Adding MTD covering the whole flash\n"); add_mtd_device(&g->mtd); } + #else + + /* + * If control arrives here, the MTD partitioning facility isn't + * available. We just register the MTD that represents the entire + * medium. + */ + add_mtd_device(&g->mtd); + #endif + + /* Initialize the Unique ID facility. */ + gpmi_uid_init("nand", &g->mtd, gpd->uid_offset, gpd->uid_size); + /* + * Check if we should export sysfs entries. + * + * This configuration variable should be destroyed, and this driver + * should *always* create the sysfs entries. + */ + #ifdef CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES gpmi_sysfs(pdev, true); #endif + + /* If control arrives here, everything worked. Return success. */ + return 0; out_all: @@ -1693,8 +2212,9 @@ out1: } /** - * gpmi_nand_remove - remove a GPMI device + * gpmi_nand_remove - Dissociates this driver from the given device. * + * @pdev: A pointer to the platform device. */ static int __devexit gpmi_nand_remove(struct platform_device *pdev) { @@ -1708,9 +2228,11 @@ static int __devexit gpmi_nand_remove(struct platform_device *pdev) gpmi_delete_partitions(g); del_timer_sync(&g->timer); gpmi_uid_remove("nand"); + #ifdef CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES gpmi_sysfs(pdev, false); #endif + nand_release(&g->mtd); gpmi_free_buffers(pdev, g); gpmi_deinit_chip(pdev, g, -1); @@ -1720,13 +2242,14 @@ static int __devexit gpmi_nand_remove(struct platform_device *pdev) regulator_put(g->regulator); #ifdef CONFIG_MTD_PARTITIONS - if (i < gpd->items && gpd->parts[i].partitions) - platf_parts = gpd->parts[i].partitions; + if (i < gpd->chip_count && gpd->chip_partitions[i].partitions) + platf_parts = gpd->chip_partitions[i].partitions; else platf_parts = NULL; - if (g->parts && g->parts != platf_parts) - kfree(g->parts); + if (g->chip_partitions && g->chip_partitions != platf_parts) + kfree(g->chip_partitions); #endif + iounmap(g->io_base); kfree(g); @@ -1734,40 +2257,87 @@ static int __devexit gpmi_nand_remove(struct platform_device *pdev) } #ifdef CONFIG_PM + +/** + * gpmi_nand_suspend() - Suspends this driver. + * + * @pdev: A pointer to the owning struct platform_device. + * @pm: For future use, currently unused. + */ static int gpmi_nand_suspend(struct platform_device *pdev, pm_message_t pm) { struct gpmi_nand_data *g = platform_get_drvdata(pdev); int r = 0; + /* If the driver suspended itself due to inactivity, wake it up. */ + if (g->self_suspended) gpmi_self_wakeup(g); + + /* Deactivate the inactivity timer. */ + del_timer_sync(&g->timer); + /* + * Suspend MTD's use of this device and, if that works, then shut down + * the actual hardware. + */ + r = g->mtd.suspend(&g->mtd); if (r == 0) gpmi_nand_release_hw(pdev); return r; + } +/** + * gpmi_nand_resume() - Resumes this driver from suspend. + * + * @pdev: A pointer to the owning struct platform_device. + */ static int gpmi_nand_resume(struct platform_device *pdev) { struct gpmi_nand_data *g = platform_get_drvdata(pdev); int r; + /* + * Spin up the hardware. + * + * Unfortunately, this code ignores the result of hardware + * initialization and spins up the driver unconditionally. + */ + r = gpmi_nand_init_hw(pdev, 1); gpmi_set_timings(pdev, &g->timing); + + /* Tell MTD it can use this device again. */ + g->mtd.resume(&g->mtd); + + /* Re-instate the inactivity timer. */ + g->timer.expires = jiffies + 4 * HZ; add_timer(&g->timer); + return r; + } + #else #define gpmi_nand_suspend NULL #define gpmi_nand_resume NULL #endif #ifdef CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES + +/** + * show_timings() - Shows the current NAND Flash 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_timings(struct device *d, struct device_attribute *attr, char *buf) { @@ -1781,6 +2351,14 @@ static ssize_t show_timings(struct device *d, struct device_attribute *attr, ptm->address_setup, ptm->dsample_time); } +/** + * store_timings() - Sets the current NAND Flash 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_timings(struct device *d, struct device_attribute *attr, const char *buf, size_t size) { @@ -1829,12 +2407,26 @@ static ssize_t store_timings(struct device *d, struct device_attribute *attr, return size; } +/** + * show_stat() - Shows current statistics. + * + * @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_stat(struct device *d, struct device_attribute *attr, char *buf) { return sprintf(buf, "copies\t\t%dff pages\t%d\n", copies, ff_writes); } +/** + * show_chips() - Shows the number of physical chips that were discovered. + * + * @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_chips(struct device *d, struct device_attribute *attr, char *buf) { @@ -1842,6 +2434,13 @@ static ssize_t show_chips(struct device *d, struct device_attribute *attr, return sprintf(buf, "%d\n", g->numchips); } +/** + * show_ignorebad() - Shows the value of the 'ignorebad' flag. + * + * @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_ignorebad(struct device *d, struct device_attribute *attr, char *buf) { @@ -1850,6 +2449,14 @@ static ssize_t show_ignorebad(struct device *d, struct device_attribute *attr, return sprintf(buf, "%d\n", g->ignorebad); } +/** + * store_ignorebad() - Sets the value of the 'ignorebad' flag. + * + * @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_ignorebad(struct device *d, struct device_attribute *attr, const char *buf, size_t size) { @@ -1887,6 +2494,12 @@ static struct device_attribute *gpmi_attrs[] = { NULL, }; +/** + * gpmi_sysfs() - Creates or removes sysfs nodes. + * + * @pdev: A pointer to the owning platform device. + * @create: Indicates the nodes are to be created (otherwise, removed). + */ int gpmi_sysfs(struct platform_device *pdev, int create) { int err = 0; @@ -1907,56 +2520,97 @@ int gpmi_sysfs(struct platform_device *pdev, int create) } return err; } + #endif -static struct platform_driver gpmi_nand_driver = { - .probe = gpmi_nand_probe, - .remove = __devexit_p(gpmi_nand_remove), - .driver = { - .name = "gpmi", - .owner = THIS_MODULE, - }, - .suspend = gpmi_nand_suspend, - .resume = gpmi_nand_resume, -}; +/* + * The global list of ECC descriptors. + * + * Each descriptor represents an ECC option that's available to the driver. + */ -static LIST_HEAD(gpmi_hwecc_chips); +static LIST_HEAD(gpmi_ecc_descriptor_list); -void gpmi_hwecc_chip_add(struct gpmi_hwecc_chip *chip) +/** + * gpmi_ecc_add() - Adds the given ECC descriptor. + * + * @name: The name of interest. + */ +void gpmi_ecc_add(struct gpmi_ecc_descriptor *chip) { - list_add(&chip->list, &gpmi_hwecc_chips); + list_add(&chip->list, &gpmi_ecc_descriptor_list); } +EXPORT_SYMBOL_GPL(gpmi_ecc_add); -EXPORT_SYMBOL_GPL(gpmi_hwecc_chip_add); - -void gpmi_hwecc_chip_remove(struct gpmi_hwecc_chip *chip) +/** + * gpmi_ecc_remove() - Removes an ECC descriptor with the given name. + * + * @name: The name of interest. + */ +void gpmi_ecc_remove(struct gpmi_ecc_descriptor *chip) { list_del(&chip->list); } +EXPORT_SYMBOL_GPL(gpmi_ecc_remove); -EXPORT_SYMBOL_GPL(gpmi_hwecc_chip_remove); - -struct gpmi_hwecc_chip *gpmi_hwecc_chip_find(char *name) +/** + * gpmi_ecc_find() - Tries to find an ECC descriptor with the given name. + * + * @name: The name of interest. + */ +struct gpmi_ecc_descriptor *gpmi_ecc_find(char *name) { - struct gpmi_hwecc_chip *c; + struct gpmi_ecc_descriptor *c; + + list_for_each_entry(c, &gpmi_ecc_descriptor_list, list) + if (strncmp(c->name, name, sizeof(c->name)) == 0) + return c; - list_for_each_entry(c, &gpmi_hwecc_chips, list) - if (strncmp(c->name, name, sizeof(c->name)) == 0) - return c; return NULL; + } +EXPORT_SYMBOL_GPL(gpmi_ecc_find); + +/* + * This structure represents this driver to the platform management system. + */ -EXPORT_SYMBOL_GPL(gpmi_hwecc_chip_find); +static struct platform_driver gpmi_nand_driver = { + .probe = gpmi_nand_probe, + .remove = __devexit_p(gpmi_nand_remove), + .driver = { + .name = "gpmi", + .owner = THIS_MODULE, + }, + .suspend = gpmi_nand_suspend, + .resume = gpmi_nand_resume, +}; static int __init gpmi_nand_init(void) { + int return_value; + + pr_info("GPMI NAND Flash driver\n"); + + /* Initialize the BCH and ECC8 hardware blocks. */ + bch_init(); ecc8_init(); - return platform_driver_register(&gpmi_nand_driver); + + /* Attempt to register this driver with the platform. */ + + return_value = platform_driver_register(&gpmi_nand_driver); + + if (return_value) + pr_err("GPMI NAND Flash driver registration failed\n"); + + return return_value; + } static void __exit gpmi_nand_exit(void) { + pr_info("GPMI NAND Flash driver exiting...\n"); platform_driver_unregister(&gpmi_nand_driver); } @@ -1964,7 +2618,7 @@ module_init(gpmi_nand_init); module_exit(gpmi_nand_exit); MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>"); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("GPMI NAND driver"); +MODULE_DESCRIPTION("GPMI NAND Flash driver"); module_param(max_chips, int, 0400); module_param(clk, long, 0400); module_param(bch, int, 0600); diff --git a/drivers/mtd/nand/gpmi/gpmi-bbt.c b/drivers/mtd/nand/gpmi/gpmi-bbt.c index 54755f870440..7b58f6a3a4e7 100644 --- a/drivers/mtd/nand/gpmi/gpmi-bbt.c +++ b/drivers/mtd/nand/gpmi/gpmi-bbt.c @@ -30,26 +30,71 @@ #include <mach/unique-id.h> #include "gpmi.h" +/* + * The equivalent of the BOOT_SEARCH_COUNT field in the OTP bits. That is, the + * logarithm to base 2 of the number of strides in a search area (a stride is + * 64 pages). + */ + static int boot_search_count; + +/* + * The size, in pages, of a search area stride. + * + * This number is dictated by the ROM, so it's not clear why it isn't at least + * const, or perhaps a macro. + */ + static int stride = 64; + +/* + * Indicates how NCBs are to be comprehended. + * + * A value of 0 indicates a format that is easy to develop and test with, but + * which the ROM can't actually boot. + * + * A value of 1 corresponds to the TA-1 ROM. + * + * A value of 3 corresponds to the TA-3 ROM. + */ + static int ncb_version = 3; + module_param(boot_search_count, int, 0400); module_param(ncb_version, int, 0400); +/* + * gpmi_read_page - + * + * @mtd: The owning MTD. + * @start: The offset at which to begin reading. + * @data: A pointer to a buff that will receive the data. This pointer may be + * NULL, in which case this function will allocate a buffer. + * @raw: If true, indicates that the caller doesn't want to use ECC. + */ void *gpmi_read_page(struct mtd_info *mtd, loff_t start, void *data, int raw) { int ret; struct mtd_oob_ops ops; + /* If the caller didn't send in his own buffer, allocate one. */ + if (!data) data = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); if (!data) return NULL; + /* Check if the caller wants to use ECC. */ + if (raw) ops.mode = MTD_OOB_RAW; else ops.mode = MTD_OOB_PLACE; + + /* + * Call nand_do_read_ops() to do the dirty work. + */ + ops.datbuf = data; ops.len = mtd->writesize; ops.oobbuf = data + mtd->writesize; @@ -62,6 +107,12 @@ void *gpmi_read_page(struct mtd_info *mtd, loff_t start, void *data, int raw) return data; } +/* + * gpmi_write_ncb - Writes an NCB to the medium. + * + * @mtd: The owning MTD. + * @b: Boot Control Block information. + */ int gpmi_write_ncb(struct mtd_info *mtd, struct gpmi_bcb_info *b) { struct gpmi_ncb *ncb = NULL, *unencoded_ncb = NULL; @@ -72,18 +123,31 @@ int gpmi_write_ncb(struct mtd_info *mtd, struct gpmi_bcb_info *b) struct erase_info instr; int ncb_count; + /* Allocate an I/O buffer for the NCB page, with OOB. */ + ncb = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); if (!ncb) { err = -ENOMEM; goto out; } + + /* Allocate a buffer within which we will construct the NCB. */ + unencoded_ncb = kzalloc(mtd->writesize, GFP_KERNEL); if (!unencoded_ncb) { err = -ENOMEM; goto out; } + ops.mode = -1; /* if the value is not set in switch below, this will cause BUG. Take care. */ + + /* + * Check if we got any Boot Control Block information and, specifically, + * if it contains an NCB for use to use. If not, we need to construct + * our own. + */ + if (b && b->pre_ncb) memcpy(unencoded_ncb, b->pre_ncb, b->pre_ncb_size); else { @@ -93,6 +157,8 @@ int gpmi_write_ncb(struct mtd_info *mtd, struct gpmi_bcb_info *b) unencoded_ncb->timing = b->timing; } + /* Construct the encoded NCB from the unencoded one. */ + switch (ncb_version) { case 0: ops.mode = MTD_OOB_AUTO; @@ -121,6 +187,8 @@ int gpmi_write_ncb(struct mtd_info *mtd, struct gpmi_bcb_info *b) goto out; } + /* Construct data structures for the write operation. */ + ops.datbuf = (u8 *)ncb; ops.len = mtd->writesize; ops.oobbuf = (u8 *)ncb + mtd->writesize; @@ -131,31 +199,62 @@ int gpmi_write_ncb(struct mtd_info *mtd, struct gpmi_bcb_info *b) do { printk(KERN_NOTICE"GPMI: Trying to store NCB at addr %lx\n", (unsigned long)start); + + /* + * Attempt to erase the block that will contain the current NCB. + */ + memset(&instr, 0, sizeof(instr)); instr.mtd = mtd; instr.addr = start; instr.len = (1 << chip->phys_erase_shift); err = nand_erase_nand(mtd, &instr, 0); + + /* + * Check if the erase worked and, if so, write the NCB. + */ + if (err == 0) { printk(KERN_NOTICE"GPMI: Erased, storing\n"); err = nand_do_write_ops(mtd, start, &ops); printk(KERN_NOTICE"GPMI: NCB update %s (%d).\n", err ? "failed" : "succeeded", err); } + + /* + * Move to the next block. + */ + start += (1 << chip->phys_erase_shift); ncb_count++; + } while (err != 0 && ncb_count < 100); + /* Tell the caller where we ended up putting the NCB. */ + if (b) b->ncbblock = start >> chip->bbt_erase_shift; out: + + /* Free our buffers. */ + kfree(ncb); kfree(unencoded_ncb); return 0; } +/* + * gpmi_redundancy_check_one - + * + * @pg: + * @dsize: + * @esize: + * @offset: + * @o1: + * @o2: + */ static int gpmi_redundancy_check_one(u8 *pg, int dsize, int esize, int offset, int o1, int o2) { @@ -181,6 +280,14 @@ static int gpmi_redundancy_check_one(u8 *pg, int dsize, int esize, int offset, return r; } +/* + * gpmi_redundancy_check - + * + * @pg: + * @dsize: + * @esize: + * @ecc_offset: + */ static int gpmi_redundancy_check(u8 *pg, int dsize, int esize, int ecc_offset) { if (gpmi_redundancy_check_one(pg, dsize, esize, ecc_offset, 0, 1) == 0) @@ -192,6 +299,16 @@ static int gpmi_redundancy_check(u8 *pg, int dsize, int esize, int ecc_offset) return -1; } +/* + * gpmi_ncb1_redundancy_check_one - + * + * @pg: + * @dsize: + * @esize: + * @offset: + * @o1: + * @o2: + */ static inline int gpmi_ncb1_redundancy_check(u8 *pg) { return gpmi_redundancy_check(pg, @@ -200,6 +317,12 @@ static inline int gpmi_ncb1_redundancy_check(u8 *pg) NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY); } +/* + * gpmi_scan_sigmatel_bbt - + * + * @mtd: The owning MTD. + * @nfo: + */ static int gpmi_scan_sigmatel_bbt( struct mtd_info *mtd, struct gpmi_bcb_info *nfo) { @@ -207,19 +330,43 @@ static int gpmi_scan_sigmatel_bbt( u8 *pg; struct gpmi_ncb *result = NULL; + /* + * If the boot search count is 0, make it 1. + * + * This is old TA1 behavior. The correct answer is to map 0 to 2. + */ + if (boot_search_count == 0) boot_search_count = 1; + + /* Check for nonsense. */ + if (nfo == NULL) return -EINVAL; + /* + * Loop through the medium, searching for the NCB. + * + * Note that this loop is wrong. Each stride is 64 pages, so it *should* + * be: + * + * for (page = 0; + * page < ((1<<boot_search_count) * 64); page += stride) { + */ + pg = NULL; printk(KERN_NOTICE"Scanning for NCB...\n"); for (page = 0; page < (1<<boot_search_count); page += stride) { + + /* Read the current page. */ + pg = gpmi_read_page(mtd, page * mtd->writesize, pg, ncb_version != 0); printk(KERN_NOTICE"GPMI: Checking page 0x%08X\n", page); + /* Check for NCB version 0. */ + if (ncb_version == 0) { if (memcmp(pg, SIG1, SIG_SIZE) != 0) continue; @@ -259,14 +406,54 @@ static int gpmi_scan_sigmatel_bbt( #endif #ifdef CONFIG_MTD_NAND_GPMI_TA3 + + /* Check for TA-3 NCB handling. */ + if (ncb_version == 3) { + /* + * A valid NCB page contains the following: + * + * +------------+ + * . + * . + * Don't Care + * . + * . + * +------------+ 1036 + * | | + * | NCB ECC | + * | | + * +------------+ 524 + * | | + * | NCB | + * | | + * +------------+ 12 + * | Don't Care | + * +------------+ 0 + * + * Within the NCB, there are three "fingerprints": + * + * +-----------+--------------------+ + * | Offset In | | + * | NCB Page | Fingerprint | + * +-----------+--------------------+ + * | 0x0c | "STMP" 0x504d5453 | + * | 0x38 | "NCB " 0x2042434E | + * | 0x8c | "RBIN" 0x4E494252 | + * +-----------+--------------------+ + */ + + /* Check for the first signature. */ + if (memcmp(pg + 12, SIG1, SIG_SIZE) != 0) continue; printk(KERN_NOTICE"GPMI: Signature found at 0x%08X\n", page); + /* Validate the NCB against the ECC stored with it. */ + if (gpmi_verify_hamming_13_8( pg + 12, pg + 524, @@ -274,9 +461,18 @@ static int gpmi_scan_sigmatel_bbt( printk(KERN_ERR"Verification failed.\n"); continue; } + + /* + * If control arrives here, we found what we were + * looking for. We want to return the address of the + * NCB itself. + */ + result = (struct gpmi_ncb *)(pg + 12); + } #endif + if (result) { printk(KERN_NOTICE"GPMI: Valid NCB found " "at 0x%08x\n", page); @@ -284,12 +480,21 @@ static int gpmi_scan_sigmatel_bbt( nfo->ncbblock = page * mtd->writesize; break; } + } + + /* Free the page buffer */ + kfree(pg); return result != NULL; } +/** + * gpmi_scan_bbt - Sets up to manage bad blocks. + * + * @mtd: The owning MTD. + */ int gpmi_scan_bbt(struct mtd_info *mtd) { struct gpmi_bcb_info stmp_bbt; @@ -302,21 +507,36 @@ int gpmi_scan_bbt(struct mtd_info *mtd) g->transcribe_bbmark = 0; /* - Since NCB uses the full page, including BB pattern bits, - driver has to ignore result of gpmi_block_bad when reading - these pages. + * The following code assumes the NCB uses the full page. This was true + * in the TA1 revision of the boot ROM, but not later versions. + */ + + /* + * Since NCB uses the full page, including BB pattern bits, + * driver has to ignore result of gpmi_block_bad when reading + * these pages. */ ign = g->ignorebad; g->ignorebad = true; /* strictly speaking, I'd have to hide * the BBT too. * But we still scanning it :) */ + r = gpmi_scan_sigmatel_bbt(mtd, &stmp_bbt); /* and then, driver has to restore the setting */ g->ignorebad = ign; + /* Check if we found the NCB. */ + if (r) { + + /* + * If control arrives here, the medium has an NCB and is + * therefore formatted for SigmaTel hardware. We want to use + * the timings from the NCB. + */ + printk(KERN_NOTICE"Setting discovered timings: %d:%d:%d:%d\n", stmp_bbt.timing.data_setup, stmp_bbt.timing.data_hold, @@ -327,8 +547,26 @@ int gpmi_scan_bbt(struct mtd_info *mtd) g->timing = stmp_bbt.timing; } else { + + /* + * If control arrives here, the medium has no NCB, so we + * presume it is in common format. This means we must transcribe + * the bad block marks. + */ + g->transcribe_bbmark = !0; + + /* + * Compute the number of blocks in the entire medium. + */ + numblocks = this->chipsize >> this->bbt_erase_shift; + + /* + * Loop over all the blocks in the medium, transcribing block + * marks as we go. + */ + from = 0; printk(KERN_NOTICE"Checking BB on common-formatted flash\n"); for (i = stmp_bbt.ncbblock + 1; i < numblocks; i++) { @@ -338,14 +576,35 @@ int gpmi_scan_bbt(struct mtd_info *mtd) } } + /* Use the reference implementation's BBT scan. */ + r = nand_default_bbt(mtd); + /* Check if we were transcribing block marks. */ + if (g->transcribe_bbmark) { - /* NCB has been not found, so create NCB now */ + + /* + * If control arrives here, we set the transcription flag + * because we didn't find an NCB. Now that we've + * transcribed all the bad block marks, we need to write an + * NCB to show that the medium is now formatted for SigmaTel + * Hardware. + */ + g->transcribe_bbmark = 0; + /* + * Store the timings we discovered from the NCB in the + * per-device data structure. + */ + stmp_bbt.timing = gpmi_safe_timing; + + /* Write an NCB to the medium. */ + r = gpmi_write_ncb(mtd, &stmp_bbt); + } else { /* NCB found, and its block should be marked as "good" */ gpmi_block_mark_as(this, stmp_bbt.ncbblock, 0x00); @@ -354,6 +613,13 @@ int gpmi_scan_bbt(struct mtd_info *mtd) return r; } +/** + * gpmi_block_bad - Checks if a block is bad, and may transcribes its mark. + * + * @mtd: The owning MTD. + * @ofs: + * @getchip: + */ int gpmi_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) { @@ -363,21 +629,42 @@ int gpmi_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) struct gpmi_nand_data *g = chip->priv; int chipnr; - /* badblockpos is an offset in OOB area */ + /* + * Compute the position of the block mark within the OOB (this code + * appears to be wrong). + */ + int badblockpos = chip->ecc.steps * chip->ecc.bytes; if (g->ignorebad) return 0; + /* + * Compute the page address of the first page in the block that contains + * the given offset. + */ + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + /* + * Compute the chip number that contains the given offset, and select + * it. + */ + chipnr = (int)(ofs >> chip->chip_shift); chip->select_chip(mtd, chipnr); + /* + * If we're transcribing block marks, set the position to that in a + * SigmaTel Hardware-format page. + */ + if (g->transcribe_bbmark) /* bad block marks still are on first byte of OOB */ badblockpos = 0; + /* Read the block mark. */ + if (chip->options & NAND_BUSWIDTH_16) { chip->cmdfunc(mtd, NAND_CMD_READOOB, badblockpos & 0xFE, page); @@ -392,9 +679,18 @@ int gpmi_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) res = 1; } + /* + * If we successfully read the block mark, and we're supposed to + * transcribe it, then do so. + */ + if (g->transcribe_bbmark && res) chip->block_markbad(mtd, ofs); + /* + * Deselect the chip. + */ + chip->select_chip(mtd, -1); return res; diff --git a/drivers/mtd/nand/gpmi/gpmi-bch.c b/drivers/mtd/nand/gpmi/gpmi-bch.c index d7b261778dcf..3d86178082dc 100644 --- a/drivers/mtd/nand/gpmi/gpmi-bch.c +++ b/drivers/mtd/nand/gpmi/gpmi-bch.c @@ -44,8 +44,20 @@ static int bch_read(void *context, static int bch_stat(void *ctx, int index, struct mtd_ecc_stats *r); static int bch_reset(void *context, int index); +/** + * bch_state_t - Describes the state of the BCH ECC. + * + * @chip: A descriptor the GPMI driver uses to track this ECC. + * @nands: An array of elements, each of which represents a physical chip. + * @stat: Used by the interrupt level to communicate ECC statistics to the + * base level. + * @done: A struct completion used to manage ECC interrupts. + * @writesize: The page data size. + * @oobsize: The page OOB size. + */ + struct bch_state_t { - struct gpmi_hwecc_chip chip; + struct gpmi_ecc_descriptor chip; struct { struct mtd_ecc_stats stat; struct completion done; @@ -53,6 +65,8 @@ struct bch_state_t { } nands[BCH_MAX_NANDS]; }; +/* The singleton struct bch_state_t for the BCH ECC. */ + static struct bch_state_t state = { .chip = { .name = "bch", @@ -63,6 +77,12 @@ static struct bch_state_t state = { }, }; +/** + * bch_reset - Resets the BCH. + * + * @context: Context data -- a pointer to a struct bch_state_t. + * @index: ?? + */ static int bch_reset(void *context, int index) { stmp3xxx_reset_block(REGS_BCH_BASE, true); @@ -70,6 +90,14 @@ static int bch_reset(void *context, int index) return 0; } +/** + * bch_stat - Gather statistics and clean up after a read operation. + * + * @context: Context data -- a pointer to a struct bch_state_t. + * @index: ?? + * @r: A statistics structure that will receive the results of the most + * recent operation. + */ static int bch_stat(void *context, int index, struct mtd_ecc_stats *r) { struct bch_state_t *state = context; @@ -80,6 +108,15 @@ static int bch_stat(void *context, int index, struct mtd_ecc_stats *r) return 0; } +/** + * bch_irq - Interrupt handler for the BCH hardware. + * + * This function gains control when the BCH hardware interrupts. It acknowledges + * the interrupt and gathers status information. + * + * @irq: The interrupt number. + * @context: Context data -- a pointer to a struct bch_state_t. + */ static irqreturn_t bch_irq(int irq, void *context) { u32 b0, s0; @@ -116,12 +153,28 @@ static irqreturn_t bch_irq(int irq, void *context) return IRQ_HANDLED; } +/** + * bch_available - Returns whether the BCH hardware is available. + * + * @context: Context data -- a pointer to a struct bch_state_t. + */ static int bch_available(void *context) { stmp3xxx_reset_block(REGS_BCH_BASE, 0); return __raw_readl(REGS_BCH_BASE + HW_BCH_BLOCKNAME) == 0x20484342; } +/** + * bch_setup - Set up BCH for use. + * + * If the GPMI driver decides to use this ECC, it will call this function once, + * before it starts any operations. + * + * @context: Context data -- a pointer to a struct bch_state_t. + * @index: ?? + * @writesize: The page data size. + * @oobsize: The page OOB size. + */ static int bch_setup(void *context, int index, int writesize, int oobsize) { struct bch_state_t *state = context; @@ -179,6 +232,18 @@ static int bch_setup(void *context, int index, int writesize, int oobsize) return 0; } +/** + * bch_read - Fill in a DMA chain to read a page. + * + * @context: Context data -- a pointer to a struct bch_state_t. + * @cs: The chip number to read. + * @chain: The main descriptor of the DMA chain to fill. + * @error: ?? + * @page: Physical address of the target page data buffer. + * @oob: Physical address of the target OOB data buffer. + * + * Return: status of operation -- 0 on success + */ static int bch_read(void *context, int index, struct stmp3xxx_dma_descriptor *chain, @@ -271,13 +336,27 @@ static int bch_read(void *context, return 0; } +/** + * bch_init - Initialize and register ECC. + * + * The GPMI driver calls this function once, at the beginning of time, whether + * or not it decides to use this ECC. + */ int __init bch_init(void) { int err; + /* Check if the BCH hardware is available. */ + if (!bch_available(&state.chip)) return -ENXIO; - gpmi_hwecc_chip_add(&state.chip); + + /* Give the GPMI driver a descriptor. */ + + gpmi_ecc_add(&state.chip); + + /* Attempt to acquire the BCH interrupt. */ + err = request_irq(IRQ_BCH, bch_irq, 0, state.chip.name, &state); if (err) return err; @@ -286,8 +365,11 @@ int __init bch_init(void) return 0; } +/** + * bch_exit - Shut down and de-register ECC. + */ void bch_exit(void) { free_irq(IRQ_BCH, &state); - gpmi_hwecc_chip_remove(&state.chip); + gpmi_ecc_remove(&state.chip); } diff --git a/drivers/mtd/nand/gpmi/gpmi-ecc8.c b/drivers/mtd/nand/gpmi/gpmi-ecc8.c index ead809cf0d54..d1b30c088328 100644 --- a/drivers/mtd/nand/gpmi/gpmi-ecc8.c +++ b/drivers/mtd/nand/gpmi/gpmi-ecc8.c @@ -52,8 +52,40 @@ static int ecc8_reset(void *context, int index); struct ecc8_nand { }; +/** + * ecc8_state_t - Describes the state of the Reed-Solomon ECC. + * + * @chip: A descriptor the GPMI driver uses to track this ECC. + * @done: A struct completion used to manage ECC interrupts. + * @stat: Used by the interrupt level to communicate ECC statistics to the + * base level. + * @writesize: The page data size. + * @oobsize: The page OOB size. + * @ecc_page: The number of ECC bytes associated with each 512-byte data + * block. + * @ecc_oob: The number of ECC bytes that cover the free bytes in the OOB. + * @oob_free: The number of free bytes in the OOB. That is, the number of + * OOB bytes that are *not* ECC bytes. + * @r: A bit mask that sets the ECC_CMD field of the GPMI_ECCCTRL + * register for reading. + * @w: A bit mask that sets the ECC_CMD field of the GPMI_ECCCTRL + * register for writing. + * @bits: The number of 512-byte data blocks in a page. By coincidence, + * this is also the strength of the ECC algorithm since the + * hardware is constrained to use ECC-4 with 2K pages and ECC-8 + * with 4K pages. The ECC strength may be the original source of + * this field's name, but the code actually uses this value in the + * sense of the number of 512-byte blocks. + * @s0_mask: This is presumably the mask that should be applied to the + * ECC8_STATUS0 register before checking for ECC errors. In fact, + * it is assigned but never used. + * @s1_mask: This is presumably the mask that should be applied to the + * ECC8_STATUS1 register before checking for ECC errors. In fact, + * it is assigned but never used. + */ + struct ecc8_state_t { - struct gpmi_hwecc_chip chip; + struct gpmi_ecc_descriptor chip; struct completion done; struct mtd_ecc_stats stat; u32 writesize, oobsize; @@ -63,6 +95,8 @@ struct ecc8_state_t { u32 s0_mask, s1_mask; }; +/* The singleton struct ecc8_state_t for the ECC8. */ + static struct ecc8_state_t state = { .chip = { .name = "ecc8", @@ -74,6 +108,12 @@ static struct ecc8_state_t state = { }, }; +/** + * ecc8_reset - Resets the ECC8. + * + * @context: Context data -- a pointer to a struct ecc8_state_t. + * @index: ?? + */ static int ecc8_reset(void *context, int index) { stmp3xxx_reset_block(REGS_ECC8_BASE, false); @@ -83,6 +123,14 @@ static int ecc8_reset(void *context, int index) return 0; } +/** + * ecc8_stat - Gather statistics and clean up after a read operation. + * + * @context: Context data -- a pointer to a struct ecc8_state_t. + * @index: ?? + * @r: A statistics structure that will receive the results of the most + * recent operation. + */ static int ecc8_stat(void *context, int index, struct mtd_ecc_stats *r) { struct ecc8_state_t *state = context; @@ -95,6 +143,15 @@ static int ecc8_stat(void *context, int index, struct mtd_ecc_stats *r) return 0; } +/** + * ecc8_irq - Interrupt handler for the ECC8 hardware. + * + * This function gains control when the ECC8 hardware interrupts. It + * acknowledges the interrupt and gathers status information. + * + * @irq: The interrupt number. + * @context: Context data -- a pointer to a struct ecc8_state_t. + */ static irqreturn_t ecc8_irq(int irq, void *context) { int r; @@ -104,20 +161,28 @@ static irqreturn_t ecc8_irq(int irq, void *context) u32 s0 = __raw_readl(REGS_ECC8_BASE + HW_ECC8_STATUS0), s1 = __raw_readl(REGS_ECC8_BASE + HW_ECC8_STATUS1); + /* Get the physical chip number to which this operation applied. */ + r = (s0 & BM_ECC8_STATUS0_COMPLETED_CE) >> 16; + /* Check if there were any errors. */ + if (s0 & BM_ECC8_STATUS0_CORRECTED || s0 & BM_ECC8_STATUS0_UNCORRECTABLE) { ecc_stats.failed = 0; ecc_stats.corrected = 0; + /* Check for errors in the OOB bytes. */ + s0 = (s0 & BM_ECC8_STATUS0_STATUS_AUX) >> 8; if (s0 <= 4) ecc_stats.corrected += s0; if (s0 == 0xE) ecc_stats.failed++; + /* Check for errors in the data bytes. */ + for ( ; s1 != 0; s1 >>= 4) { corr = s1 & 0xF; if (corr == 0x0C) @@ -128,21 +193,45 @@ static irqreturn_t ecc8_irq(int irq, void *context) ecc_stats.corrected += corr; s1 >>= 4; } + + /* Accumulate statistics. */ + state->stat.corrected += ecc_stats.corrected; state->stat.failed += ecc_stats.failed; + } + /* Acknowledge the interrupt. */ + complete(&state->done); stmp3xxx_clearl(BM_ECC8_CTRL_COMPLETE_IRQ, REGS_ECC8_BASE + HW_ECC8_CTRL); return IRQ_HANDLED; } +/** + * ecc8_available - Returns whether the Reed-Solomon ECC hardware is available. + * + * Note that this function always returns true. + * + * @context: Context data -- a pointer to a struct ecc8_state_t. + */ static int ecc8_available(void *context) { return 1; } +/** + * ecc8_setup - Set up ECC for use. + * + * If the GPMI driver decides to use this ECC, it will call this function once, + * before it starts any operations. + * + * @context: Context data -- a pointer to a struct ecc8_state_t. + * @index: ?? + * @writesize: The page data size. + * @oobsize: The page OOB size. + */ static int ecc8_setup(void *context, int index, int writesize, int oobsize) { struct ecc8_state_t *state = context; @@ -181,13 +270,14 @@ static int ecc8_setup(void *context, int index, int writesize, int oobsize) } /** - * ecc8_read - create dma chain to read nand page + * ecc8_read - Fill in a DMA chain to read a page. * - * @context: context data, pointer to ecc8_state - * @index: CS of nand to read - * @chain: dma chain to be filled - * @page: (physical) address of page data to be read to - * @oob: (physical) address of OOB data to be read to + * @context: Context data -- a pointer to a struct ecc8_state_t. + * @cs: The chip number to read. + * @chain: The main descriptor of the DMA chain to fill. + * @error: ?? + * @page: Physical address of the target page data buffer. + * @oob: Physical address of the target OOB data buffer. * * Return: status of operation -- 0 on success */ @@ -217,7 +307,6 @@ static int ecc8_read(void *context, int cs, page = oob; } - /* wait for ready */ chain->command->cmd = BF(1, APBH_CHn_CMD_CMDWORDS) | @@ -288,6 +377,18 @@ static int ecc8_read(void *context, int cs, return 0; } +/** + * ecc8_write - Fill in a DMA chain to write a page. + * + * @context: Context data -- a pointer to a struct ecc8_state_t. + * @cs: The chip number to read. + * @chain: The main descriptor of the DMA chain to fill. + * @error: ?? + * @page: Physical address of the source page data buffer. + * @oob: Physical address of the source OOB data buffer. + * + * Return: status of operation -- 0 on success + */ static int ecc8_write(void *context, int cs, struct stmp3xxx_dma_descriptor *chain, dma_addr_t error, @@ -364,14 +465,27 @@ static int ecc8_write(void *context, int cs, return 0; } +/** + * ecc8_init - Initialize and register ECC. + * + * The GPMI driver calls this function once, at the beginning of time, whether + * or not it decides to use this ECC. + */ int __init ecc8_init(void) { int err; + /* Check if the ECC8 hardware is available. */ + if (!ecc8_available(&state)) return -ENXIO; - gpmi_hwecc_chip_add(&state.chip); + /* Give the GPMI driver a descriptor. */ + + gpmi_ecc_add(&state.chip); + + /* Attempt to acquire the ECC interrupt. */ + err = request_irq(IRQ_ECC8_IRQ, ecc8_irq, 0, state.chip.name, &state); if (err) return err; @@ -380,8 +494,17 @@ int __init ecc8_init(void) return 0; } +/** + * ecc8_exit - Shut down and de-register ECC. + */ void ecc8_exit(void) { + + /* Relinquish the ECC interrupt. */ + free_irq(IRQ_ECC8_IRQ, &state); - gpmi_hwecc_chip_remove(&state.chip); + + /* Remove the descriptor for this ECC from the GPMI's list. */ + + gpmi_ecc_remove(&state.chip); } diff --git a/drivers/mtd/nand/gpmi/gpmi.h b/drivers/mtd/nand/gpmi/gpmi.h index 65de2e1a1c78..90e71db4cd9a 100644 --- a/drivers/mtd/nand/gpmi/gpmi.h +++ b/drivers/mtd/nand/gpmi/gpmi.h @@ -44,13 +44,28 @@ (BM_GPMI_ECCCTRL_ENABLE_ECC | \ BF(BV_GPMI_ECCCTRL_ECC_CMD__DECODE_8_BIT, GPMI_ECCCTRL_ECC_CMD)) -/* fingerprints of BCB that can be found on STMP-formatted flash */ +/* Fingerprints for Boot Control Blocks. */ + #define SIG1 "STMP" #define SIG_NCB "NCB " #define SIG_LDLB "LDLB" #define SIG_DBBT "DBBT" #define SIG_SIZE 4 +/** + * struct gpmi_nand_timing - NAND Flash timing parameters. + * + * This structure contains the four fundamental timing attributes for the + * NAND Flash bus. These values are expressed in GPMI clock cycles or half + * cycles, depending on how the GPMI clock is configured. See the hardware + * reference manual for details. + * + * @data_setup: The data setup time in nanoseconds. + * @data_hold: The data hold time in nanoseconds. + * @address_setup: The address setup time in nanoseconds. + * @dsample_time: The data sample delay in nanoseconds. + */ + struct gpmi_nand_timing { u8 data_setup; u8 data_hold; @@ -58,6 +73,15 @@ struct gpmi_nand_timing { u8 dsample_time; }; +/** + * struct gpmi_bcb_info - Information obtained from Boot Control Blocks. + * + * @timing: Timing values extracted from an NCB. + * @ncbblock: The offset within the MTD at which the NCB was found. + * @pre_ncb: + * @pre_ncb_size: + */ + struct gpmi_bcb_info { struct gpmi_nand_timing timing; loff_t ncbblock; @@ -98,7 +122,7 @@ int gpmi_verify_hamming_13_8(void *data, u8 *parity, size_t size); #define GPMI_DMA_MAX_CHAIN 20 /* max DMA commands in chain */ /* - * Sizes of data buffers to exchange commands/data with NAND chip + * Sizes of data buffers to exchange commands/data with NAND chip. * Default values cover 4K NAND page (4096 data bytes + 218 bytes OOB) */ #define GPMI_CMD_BUF_SZ 10 @@ -108,7 +132,23 @@ int gpmi_verify_hamming_13_8(void *data, u8 *parity, size_t size); #define GPMI_MAX_CHIPS 10 -struct gpmi_hwecc_chip { +/** + * struct gpmi_ecc_descriptor - Abstract description of ECC. + * + * @name: The name of the ECC represented by this structure. + * @list: Infrastructure for the list to which this structure belongs. + * @setup: A pointer to a function that prepares the ECC to function. + * @reset: A pointer to a function that resets the ECC to a known state. This + * pointer is currently never used, and probably should be removed. + * @read: A pointer to a function that fills in a given DMA chain such that + * a page read will pass through the owning ECC. + * @write: A pointer to a function that fills in a given DMA chain such that + * a page write will pass through the owning ECC. + * @stat: A pointer to a function that reports on ECC statistics for + * the preceding read operation. + */ + +struct gpmi_ecc_descriptor { char name[40]; struct list_head list; int (*setup)(void *ctx, int index, int writesize, int oobsize); @@ -124,15 +164,119 @@ struct gpmi_hwecc_chip { int (*stat)(void *ctx, int index, struct mtd_ecc_stats *r); }; -/* HWECC chips */ -struct gpmi_hwecc_chip *gpmi_hwecc_chip_find(char *name); -void gpmi_hwecc_chip_add(struct gpmi_hwecc_chip *chip); -void gpmi_hwecc_chip_remove(struct gpmi_hwecc_chip *chip); +/* ECC descriptor management. */ + +struct gpmi_ecc_descriptor *gpmi_ecc_find(char *name); +void gpmi_ecc_add(struct gpmi_ecc_descriptor *chip); +void gpmi_ecc_remove(struct gpmi_ecc_descriptor *chip); + +/* Housecleaning functions for the ECC hardware blocks. */ + int bch_init(void); int ecc8_init(void); void bch_exit(void); void ecc8_exit(void); +/** + * struct gpmi_nand_data - + * + * @io_base: The base I/O address of of the GPMI registers. + * @clk: A pointer to the structure that represents the GPMI + * clock. + * @irq: The GPMI interrupt request number. + * @inactivity_timer: A pointer to a timer the driver uses to shut itself + * down after periods of inactivity. + * @self_suspended: Indicates the driver suspended itself, rather than + * being suspended by higher layers of software. This is + * important because it effects how the driver wakes + * itself back up. + * @use_count: Used within the driver to hold off suspension until + * all operations are complete. + * @regulator: A pointer to the structure that represents the + * power regulator supplying power to the GPMI. + * @reg_uA: The GPMI current limit, in uA. + * @ignorebad: Forces the driver to report that all blocks are good. + * @bbt: Used to save a pointer to the in-memory NAND Flash MTD + * Bad Block Table if the "ignorebad" flag is turned on + * through the corresponding sysfs node. + * @nand: The data structure that represents this NAND Flash + * medium to the MTD NAND Flash system. + * @mtd: The data structure that represents this NAND Flash + * medium to MTD. + * @dev: A pointer to the owning struct device. + * @nr_parts: If the driver receives partition descriptions from an + * external parser (command line, etc.), then this is + * the number of discovered partitions. + * @parts: If the driver receives partition descriptions from an + * external parser (command line, etc.), then this is a + * pointer to the array of discovered partitions. + * @done: A struct completion used to manage GPMI interrupts. + * @cmd_buffer: + * @cmd_buffer_handle: + * @cmd_buffer_size: + * @cmd_buffer_sz: The number of command and address bytes queued up, + * waiting for transmission to the NAND Flash. + * @write_buffer: + * @write_buffer_handle: + * @write_buffer_size: + * @read_buffer: + * @read_buffer_handle: + * @data_buffer: + * @data_buffer_handle: + * @data_buffer_size: + * @oob_buffer: + * @oob_buffer_handle: + * @oob_buffer_size: + * @verify_buffer: + * @chips: An array of data structures, one for each physical + * chip. + * @cchip: A pointer to the element within the chips array that + * represents the currently selected chip. + * @selected_chip: The currently selectd chip number, or -1 if no chip + * is selected. + * @hwecc_type_read: + * @hwecc_type_write: + * @hwecc: Never used. + * @ecc_oob_bytes: The number of ECC bytes covering the OOB bytes alone. + * @oob_free: The total number of OOB bytes. + * @transcribe_bbmark: Used by the bad block management code to indicate + * that the medium is in common format and the bad block + * marks must be transcribed. + * @timing: The current timings installed in the hardware. + * @saved_command: Used to "hook" the NAND Flash MTD default + * implementation for the cmdfunc fuction pointer. + * @raw_oob_mode: + * @saved_read_oob: Used to "hook" the NAND Flash MTD interface function + * for the MTD read_oob fuction pointer. + * @saved_write_oob: Used to "hook" the NAND Flash MTD interface function + * for the MTD write_oob fuction pointer. + * @hc: A pointer to a structure that represents the ECC + * in use. + * @numchips: The number of physical chips. + * @custom_partitions: Indicates that the driver has applied driver-specific + * logic in partitioning the medium. This is important + * because similar logic must be applied in disassembling + * the partitioning when the driver shuts down. + * @chip_mtds: An array with an element for each physical chip. If + * there is only one physical chip, the first and only + * element in this array will be a copy of the pointer in + * the "mtd" field (that is, they will point to the same + * structure). If there is more than one physical chip, + * each element in this array is a pointer to a partition + * MTD derived from the "mtd" field, and representing the + * corresponding physical chip. + * @chip_partitions: An array of partition descriptions that each represent + * one of the physical chips in the medium. + * @n_concat: The number of partitions to be concatenated (pointers + * to the partition MTDs appear in the "concat" field). + * @concat: An array of pointers to partition MTDs to be + * concatenated. + * @concat_mtd: If "n_concat" is zero, this is NULL. If "n_concat" is + * one, this is a copy of the first and only element in + * the "concat" array. If "n_concat" is greater than one, + * this is a pointer to an MTD that represents the + * concatenation of all the MTDs appearing in "concat". + */ struct gpmi_nand_data { void __iomem *io_base; @@ -153,8 +297,7 @@ struct gpmi_nand_data { #ifdef CONFIG_MTD_PARTITIONS int nr_parts; - struct mtd_partition - *parts; + struct mtd_partition *parts; #endif struct completion done; @@ -206,11 +349,11 @@ struct gpmi_nand_data { int (*saved_write_oob)(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); - struct gpmi_hwecc_chip *hc; + struct gpmi_ecc_descriptor *hc; int numchips; int custom_partitions; - struct mtd_info *masters[GPMI_MAX_CHIPS]; + struct mtd_info *chip_mtds[GPMI_MAX_CHIPS]; struct mtd_partition chip_partitions[GPMI_MAX_CHIPS]; int n_concat; struct mtd_info *concat[GPMI_MAX_CHIPS]; @@ -219,6 +362,20 @@ struct gpmi_nand_data { extern struct gpmi_nand_timing gpmi_safe_timing; +/** + * struct gpmi_ncb - + * + * @fingerprint1: + * @timing: + * @pagesize: + * @page_plus_oob_size: + * @sectors_per_block: + * @sector_in_page_mask: + * @sector_to_page_shift: + * @num_nands: + * @fingerprint2: + */ + struct gpmi_ncb { u32 fingerprint1; struct gpmi_nand_timing timing; @@ -232,6 +389,28 @@ struct gpmi_ncb { u32 fingerprint2; /* offset 0x2C */ }; +/** + * struct gpmi_ldlb - + * + * @fingerprint1: + * @major: + * @minor: + * @sub: + * @nand_bitmap: + * @fingerprint2: + * @fw: + * @fw_starting_nand: + * @fw_starting_sector: + * @fw_sector_stride: + * @fw_sectors_total: + * @fw_major: + * @fw_minor: + * @fw_sub: + * @fw_reserved: + * @bbt_blk: + * @bbt_blk_backup: + */ + struct gpmi_ldlb { u32 fingerprint1; u16 major, minor, sub, reserved; @@ -266,8 +445,7 @@ static inline void gpmi_block_mark_as(struct nand_chip *chip, } } -static inline int gpmi_block_badness(struct nand_chip *chip, - int block) +static inline int gpmi_block_badness(struct nand_chip *chip, int block) { u32 o; int shift = (block & 0x03) << 1, |