diff options
author | Patrick Turley <patrick.turley@freescale.com> | 2009-10-28 14:42:59 -0500 |
---|---|---|
committer | Alejandro Gonzalez <alex.gonzalez@digi.com> | 2010-02-12 17:19:29 +0100 |
commit | 25562a38de3aabb9dbed1cc1885e8783d8eccfa2 (patch) | |
tree | f96b0b5a2a75cb8d4747c70907ee1219f3a8e076 /drivers/mtd | |
parent | 5513751db6dc6b34f778a8ce03613c166a244022 (diff) |
ENGR00116542 [MX233_BSP] Simplify the GPMI NAND Flash driver's partitioning
Simplified the partitioning mechanism and, as a side effect, made the
GPMI NAND Flash driver usable (it was always crashing the kernel).
Signed-off-by: Patrick Turley <patrick.turley@freescale.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/gpmi/gpmi-base.c | 998 | ||||
-rw-r--r-- | drivers/mtd/nand/gpmi/gpmi.h | 68 |
2 files changed, 535 insertions, 531 deletions
diff --git a/drivers/mtd/nand/gpmi/gpmi-base.c b/drivers/mtd/nand/gpmi/gpmi-base.c index fb85d2e2098c..7d2ee994c815 100644 --- a/drivers/mtd/nand/gpmi/gpmi-base.c +++ b/drivers/mtd/nand/gpmi/gpmi-base.c @@ -348,7 +348,7 @@ static int gpmi_nand_init_hw(struct platform_device *pdev, int request_pins) /* Check if we're supposed to ask for our pins. */ if (request_pins) - gpd->pinmux(1); + gpd->pinmux_handler(1); /* Reset the GPMI block. */ @@ -392,7 +392,7 @@ static void gpmi_nand_release_hw(struct platform_device *pdev) clk_disable(g->clk); clk_put(g->clk); - gpd->pinmux(0); + gpd->pinmux_handler(0); } /** @@ -1314,93 +1314,6 @@ static int gpmi_write_oob(struct mtd_info *mtd, loff_t to, } /** - * 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) -{ - int oobsize = 0; - - /* Limit to 2G size due to Kernel larger 4G space support */ - if (g->mtd.size == 0) { - g->mtd.size = 1 << 31; - 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; - g->chip.ecc.bytes = 9; - g->oob_free = 19; - g->hwecc_type_read = GPMI_ECC4_RD; - g->hwecc_type_write = GPMI_ECC4_WR; - oobsize = 64; - break; - case 4096: - g->chip.ecc.layout = &gpmi_oob_128; - g->chip.ecc.bytes = 18; - g->oob_free = 65; - g->hwecc_type_read = GPMI_ECC8_RD; - g->hwecc_type_write = GPMI_ECC8_WR; - oobsize = 218; - break; - default: - printk(KERN_ERR "Unsupported write_size %d.", g->mtd.writesize); - break; - } - - g->mtd.ecclayout = g->chip.ecc.layout; - /* sanity check */ - if (oobsize > NAND_MAX_OOBSIZE || g->mtd.writesize > NAND_MAX_PAGESIZE) { - printk(KERN_ERR "Internal error. Either page size " - "(%d) > max (%d) " - "or oob size (%d) > max(%d). Sorry.\n", - oobsize, NAND_MAX_OOBSIZE, - g->mtd.writesize, NAND_MAX_PAGESIZE); - 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_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; - -} - -/** * gpmi_write_page - [REPLACEABLE] write one page * @mtd: MTD device structure * @chip: NAND chip descriptor @@ -1608,233 +1521,586 @@ static void gpmi_deinit_chip(struct platform_device *pdev, } /** - * gpmi_create_partitions - Create platform-driven partitions. + * gpmi_scan_middle - Intermediate initialization. * - * This function creates partitions based on the platform data. + * @g: Per-device data structure. * - * @g: Per-device data. - * @gpd: Per-device platform data. - * @chipsize: The size of a single, physical chip in bytes. + * Rather than call nand_scan(), this function makes the same calls, but + * inserts this function into the initialization pathway. */ -static void gpmi_create_partitions(struct gpmi_nand_data *g, - struct gpmi_platform_data *gpd, - uint64_t chipsize) +static int gpmi_scan_middle(struct gpmi_nand_data *g) { + int oobsize = 0; -#ifdef CONFIG_MTD_PARTITIONS - int chip, p; - char chipname[20]; + /* Limit to 2G size due to Kernel larger 4G space support */ + if (g->mtd.size == 0) { + g->mtd.size = 1 << 31; + g->nand.chipsize = do_div(g->mtd.size, g->nand.numchips); + } /* - * We have a single MTD now that represents the entire medium. We want - * an MTD for each physical chip in the medium. + * 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->nand.ecc.layout = &gpmi_oob_64; + g->nand.ecc.bytes = 9; + g->oob_free = 19; + g->hwecc_type_read = GPMI_ECC4_RD; + g->hwecc_type_write = GPMI_ECC4_WR; + oobsize = 64; + break; + case 4096: + g->nand.ecc.layout = &gpmi_oob_128; + g->nand.ecc.bytes = 18; + g->oob_free = 65; + g->hwecc_type_read = GPMI_ECC8_RD; + g->hwecc_type_write = GPMI_ECC8_WR; + oobsize = 218; + break; + default: + printk(KERN_ERR "Unsupported writesize %d.", g->mtd.writesize); + break; + } + + g->mtd.ecclayout = g->nand.ecc.layout; + /* sanity check */ + if (oobsize > NAND_MAX_OOBSIZE || + g->mtd.writesize > NAND_MAX_PAGESIZE) { + printk(KERN_ERR "Internal error. Either page size " + "(%d) > max (%d) " + "or oob size (%d) > max(%d). Sorry.\n", + oobsize, NAND_MAX_OOBSIZE, + g->mtd.writesize, NAND_MAX_PAGESIZE); + 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->nand.cmdfunc; + g->nand.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_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; + +} + +/** + * gpmi_register_with_mtd - Registers devices with MTD. + * + * @g: Per-device data. + */ +static int gpmi_register_with_mtd(struct gpmi_nand_data *g) +{ +#if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT) + int r; + unsigned i; + struct gpmi_platform_data *gpd = g->gpd; + struct mtd_info *mtd = &g->mtd; + struct nand_chip *nand = &g->nand; + struct mtd_partition partitions[4]; + struct mtd_info *search_mtd; + struct mtd_info *gpmi_0_remainder_mtd = 0; + struct mtd_info *gpmi_remainder_mtd = 0; + struct mtd_info *concatenate[2]; + + /* + * Here we declare the static strings we use to name partitions. We use + * static strings because, as of 2.6.31, the partitioning code *always* + * registers the partition MTDs it creates and leaves behind *no* other + * trace of its work. So, once we've created a partition, we must search + * the master table to find the MTDs we created. Since we're using + * static strings, we can search the master table for an MTD with a name + * field pointing to a known address. + */ + + static char *gpmi_0_boot_name = "gpmi-0-boot"; + static char *gpmi_0_remainder_name = "gpmi-0-remainder"; + static char *gpmi_1_boot_name = "gpmi-1-boot"; + static char *gpmi_remainder_name = "gpmi-remainder"; + static char *gpmi_general_use_name = "gpmi-general-use"; +#endif + + /* Initialize the MTD object. */ + + mtd->priv = &g->nand; + mtd->name = "gpmi-medium"; + mtd->owner = THIS_MODULE; + + /* + * Signal Control + */ + + g->nand.cmd_ctrl = gpmi_hwcontrol; + + /* + * Chip Control * - * If there's only one chip, then we can simply use the medium MTD. + * The cmdfunc pointer is assigned elsewhere. + * We use the reference implementation of waitfunc. + */ + + g->nand.dev_ready = gpmi_dev_ready; + g->nand.select_chip = gpmi_select_chip; + + /* + * Low-level I/O + */ + + g->nand.read_byte = gpmi_read_byte; + g->nand.read_word = gpmi_read_word; + g->nand.read_buf = gpmi_read_buf; + g->nand.write_buf = gpmi_write_buf; + g->nand.verify_buf = gpmi_verify_buf; + + /* + * ECC Control * - * If there are multiple chips, we need to create partition MTDs that - * represent the subsets of the medium occupied by each physical chip. + * None of these functions are necessary: + * - ecc.hwctl + * - ecc.calculate + * - ecc.correct */ - if (g->numchips == 1) - g->chip_mtds[0] = &g->mtd; - else { + /* + * ECC-aware I/O + */ - /* - * Construct an array of partition descriptions, one for each - * physical chip. - */ + g->nand.ecc.read_page = gpmi_ecc_read_page; + g->nand.ecc.read_page_raw = gpmi_read_page_raw; + g->nand.ecc.write_page = gpmi_ecc_write_page; + g->nand.ecc.write_page_raw = gpmi_write_page_raw; - for (chip = 0; chip < g->numchips; chip++) { - memset(g->chip_partitions + chip, - 0, sizeof(g->chip_partitions[chip])); - snprintf(chipname, sizeof(chipname), - "gpmi-chip-%d", chip); - g->chip_partitions[chip].name = - kstrdup(chipname, GFP_KERNEL); - g->chip_partitions[chip].size = chipsize; - g->chip_partitions[chip].offset = chipsize * chip; - g->chip_partitions[chip].mask_flags = 0; - } + /* + * 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->nand.write_page = gpmi_write_page; + g->nand.ecc.read_oob = gpmi_ecc_read_oob; + g->nand.ecc.write_oob = gpmi_ecc_write_oob; + + /* + * Bad Block Management + * + * We use the reference implementation of block_markbad. + */ + + g->nand.block_bad = gpmi_block_bad; + g->nand.scan_bbt = gpmi_scan_bbt; + + g->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + g->nand.ecc.size = 512; + + g->cmd_buffer_sz = 0; + + /* + * We now want the NAND Flash MTD system to scan for chips and create + * the MTD data structure that represents the medium. + * + * At this point, most drivers would call nand_scan(). Instead, this + * driver directly performs most of the same operations nand_scan() + * would, and introduces some additional initialization work in the + * "middle." + */ + + pr_info("Scanning for NAND Flash chips...\n"); + + if (nand_scan_ident(&g->mtd, max_chips) + || gpmi_scan_middle(g) + || nand_scan_tail(&g->mtd)) { /* - * 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. + * If control arrives here, something went wrong. */ - add_mtd_partitions(&g->mtd, g->chip_partitions, g->numchips); + dev_err(&g->dev->dev, "No NAND Flash chips found\n"); + return !0; } - g->n_concat = 0; - memset(g->concat, 0, sizeof(g->concat)); + /* Completely disallow partial page writes. */ + + g->nand.options |= NAND_NO_SUBPAGE_WRITE; + g->nand.subpagesize = g->mtd.writesize; + g->mtd.subpage_sft = 0; + + /* Hook erase operations at the MTD level. */ + + g->mtd.erase = gpmi_erase; + + /* 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; + +#if !defined(CONFIG_MTD_PARTITIONS) || !defined(CONFIG_MTD_CONCAT) + + /* + * If control arrives here, we're missing support for either or both of + * MTD partitioning and concatenation. Do the simple thing and register + * the entire medium. + */ + + pr_info("MTD partitioning and/or concatenation are disabled.\n" + "Registering the entire GPMI medium...\n"); + + add_mtd_device(g->mtd); + +#else /* - * Loop over physical chips, handling sub-partitions for each in turn. + * Our goal here is to partition the medium in a way that protects the + * boot area. First, check if the platform data says we need to + * protect it. */ - for (chip = 0; chip < g->numchips; chip++) { + if (!gpd->boot_area_size_in_bytes) { /* - * 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 control arrives here, we don't need to protect the boot + * area. Make the entire medium available for general use. */ - if (add_mtd_chip) { - printk(KERN_NOTICE "Registering an MTD for chip %d\n", - chip); - add_mtd_device(g->chip_mtds[chip]); - } + pr_info("Boot area protection disabled.\n" + "Opening the entire medium for general use.\n"); + + g->general_use_mtd = mtd; + + } else { + + pr_info("Boot area protection enabled: 0x%x bytes.\n", + gpd->boot_area_size_in_bytes); /* - * Check if the platform data includes partition descriptions - * for this chip. If not, move to the next one. + * If control arrives here, we need to protect the boot area. + * First, check if the area we're supposed to protect is larger + * than a single chip. */ - if (chip >= gpd->chip_count) - continue; + if (gpd->boot_area_size_in_bytes > nand->chipsize) { + dev_emerg(&g->dev->dev, "Protected boot area size is " + "larger than a single chip"); + BUG(); + } /* - * 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. + * Recall that the boot ROM uses the first part of chip one and, + * if it exists, also the first part of chip two. We check now + * to see how many chips there are, and adjust our partitioning + * accordingly. */ - add_mtd_partitions(g->chip_mtds[chip], - gpd->chip_partitions[chip].partitions, - gpd->chip_partitions[chip].nr_partitions); + g->chip0_boot_mtd = 0; + g->chip1_boot_mtd = 0; + + if (nand->numchips == 1) { + + pr_info("Partitioning for one chip.\n"); + + /* + * If control arrives here, there's only one chip. We + * partition the medium like so: + * + * +------+-------------------------------------------+ + * | Boot | General Use | + * +------+-------------------------------------------+ + */ + + /* Chip 0 Boot */ + + partitions[0].name = gpmi_0_boot_name; + partitions[0].offset = 0; + partitions[0].size = gpd->boot_area_size_in_bytes; + partitions[0].mask_flags = 0; + + /* General Use */ + + partitions[1].name = gpmi_general_use_name; + partitions[1].offset = gpd->boot_area_size_in_bytes; + partitions[1].size = MTDPART_SIZ_FULL; + partitions[1].mask_flags = 0; + + /* Construct and register the partitions. */ + + add_mtd_partitions(mtd, partitions, 2); + + /* Find the general use MTD. */ + + for (i = 0; i < MAX_MTD_DEVICES; i++) { + search_mtd = get_mtd_device(0, i); + if (!search_mtd) + continue; + if (search_mtd->name == gpmi_general_use_name) + g->general_use_mtd = search_mtd; + } + + if (!g->general_use_mtd) { + dev_emerg(&g->dev->dev, "Can't find general " + "use MTD"); + BUG(); + } + + } else { + + pr_info("Partitioning for multiple chips.\n"); + + /* + * If control arrives here, there is more than one chip. + * We partition the medium and concatenate the + * remainders like so: + * + * --- Chip 0 --- --- Chip 1 --- ... -- Chip N --- + * / \ / \ + * +----+----------+----+---------- ... --------------+ + * |Boot|Remainder |Boot| Remainder | + * +----+----------+----+---------- ... --------------+ + * | | / / + * | | / / + * | | / / + * | |/ / + * +----------+---------- ... --------------+ + * | General Use | + * +----------+---------- ... --------------+ + * + * Notice we do something just a little goofy here. + * Instead of numbering these partitions in the order + * they appear in the medium, we have them in this + * order: + * + * * Chip 0 Boot Area + * * Chip 1 Boot Area + * * Chip 0 Remainder + * * Medium Remainder + * + * Before 2.6.31, it was possible to "hide" partitions + * (to create them without registering them), which made + * it possible to hide the remainders. In the future, it + * may become possible to do so again. Also, 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 */ + + partitions[0].name = gpmi_0_boot_name; + partitions[0].offset = 0; + partitions[0].size = gpd->boot_area_size_in_bytes; + partitions[0].mask_flags = 0; + + /* Chip 1 Boot */ + + partitions[1].name = gpmi_1_boot_name; + partitions[1].offset = nand->chipsize; + partitions[1].size = gpd->boot_area_size_in_bytes; + partitions[1].mask_flags = 0; + + /* Chip 0 Remainder */ + + partitions[2].name = gpmi_0_remainder_name; + partitions[2].offset = gpd->boot_area_size_in_bytes; + partitions[2].size = nand->chipsize - + gpd->boot_area_size_in_bytes; + partitions[2].mask_flags = 0; + + /* Medium Remainder */ + + partitions[3].name = gpmi_remainder_name; + partitions[3].offset = nand->chipsize + + gpd->boot_area_size_in_bytes; + partitions[3].size = MTDPART_SIZ_FULL; + partitions[3].mask_flags = 0; + + /* Construct and register the partitions. */ + + add_mtd_partitions(mtd, partitions, 4); + + /* Find the remainder partitions. */ + + for (i = 0; i < MAX_MTD_DEVICES; i++) { + search_mtd = get_mtd_device(0, i); + if (!search_mtd) + continue; + if (search_mtd == ERR_PTR(-ENODEV)) + continue; + if (search_mtd->name == gpmi_0_remainder_name) + gpmi_0_remainder_mtd = search_mtd; + if (search_mtd->name == gpmi_remainder_name) + gpmi_remainder_mtd = search_mtd; + } + + if (!gpmi_0_remainder_mtd || !gpmi_remainder_mtd) { + dev_emerg(&g->dev->dev, "Can't find remainder " + "partitions"); + BUG(); + } + + /* Concatenate the remainders and register. */ + + concatenate[0] = gpmi_0_remainder_mtd; + concatenate[1] = gpmi_remainder_mtd; + + g->general_use_mtd = mtd_concat_create(concatenate, + 2, "gpmi-general-use"); + + add_mtd_device(g->general_use_mtd); + + } + } - if (g->n_concat > 0) { -#ifdef CONFIG_MTD_CONCAT - if (g->n_concat == 1) -#endif - for (p = 0; p < g->n_concat; p++) - add_mtd_device(g->concat[p]); -#ifdef CONFIG_MTD_CONCAT - if (g->n_concat > 1) { - g->concat_mtd = mtd_concat_create(g->concat, - g->n_concat, - gpd->concat_name); - if (g->concat_mtd) - add_mtd_device(g->concat_mtd); + /* + * When control arrives here, we've done whatever partitioning and + * concatenation we needed to protect the boot area, and we have + * identified a single MTD that represents the "general use" portion of + * the medium. Check if the user wants to partition the general use MTD + * further. + */ + + /* Check for dynamic partitioning information. */ + + if (gpd->partition_source_types) { + r = parse_mtd_partitions(mtd, gpd->partition_source_types, + &g->partitions, 0); + if (r > 0) + g->partition_count = r; + else { + g->partitions = 0; + g->partition_count = 0; } -#endif + } + + /* Fall back to platform partitions? */ + + if (!g->partition_count && gpd->partitions && gpd->partition_count) { + g->partitions = gpd->partitions; + g->partition_count = gpd->partition_count; + } + + /* If we have partitions, implement them. */ + + if (g->partitions) { + pr_info("Applying partitions to the general use area.\n"); + add_mtd_partitions(g->general_use_mtd, + g->partitions, g->partition_count); } /* - * 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. + * Check if we're supposed to register the MTD that represents + * the entire medium. */ - g->custom_partitions = true; + if (add_mtd_entire) { + pr_info("Registering the full NAND Flash medium MTD.\n"); + add_mtd_device(mtd); + } #endif + /* If control arrives here, everything went well. */ + + return 0; + } /** - * gpmi_delete_partitions - Remove the partitions created by this driver. + * gpmi_unregister_with_mtd - Unregisters devices with MTD. * - * @g: The per-device data structure. + * @g: Per-device data. */ -static void gpmi_delete_partitions(struct gpmi_nand_data *g) +static void gpmi_unregister_with_mtd(struct gpmi_nand_data *g) { +#if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT) + struct gpmi_platform_data *gpd = g->gpd; + struct mtd_info *mtd = &g->mtd; + struct nand_chip *nand = &g->nand; +#endif /* - * 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. + * This function mirrors, in reverse, the structure of + * gpmi_register_with_mtd(). See that function for details about how we + * partition the medium. */ - if (!g->custom_partitions) - return; +#if !defined(CONFIG_MTD_PARTITIONS) || !defined(CONFIG_MTD_CONCAT) -#ifdef CONFIG_MTD_CONCAT + del_mtd_device(mtd); - /* - * 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); +#else /* - * 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 we registered the MTD that represents the entire medium, + * unregister it now. Note that this does *not* "destroy" the MTD - it + * merely unregisters it. That's important because all our other MTDs + * depend on this one. */ - if (g->n_concat == 1) -#endif - for (p = 0; p < g->n_concat; p++) - del_mtd_device(g->concat[p]); + if (add_mtd_entire) + del_mtd_device(mtd); - /* Handle the partitions related to each physical chip. */ + /* If we partitioned the general use MTD, destroy the partitions. */ - for (chip = 0; chip < g->numchips; chip++) { + if (g->partitions) + del_mtd_partitions(g->general_use_mtd); - /* - * De-register and destroy any partition MTDs that may have been - * derived from the MTD that represents this chip. - */ + /* + * If we're protecting the boot area, we have some additional MTDs to + * tear down. + */ - del_mtd_partitions(g->chip_mtds[chip]); + if (gpd->boot_area_size_in_bytes) { /* - * If the module variable 'add_mtd_chip' is set, then we - * registered the MTD that represents this chip. De-register it - * now. + * If we have more than one chip, then we concatenated two + * "remainder" MTDs to produce the "general use" MTD. + * Unregister the concatenation MTD, and then destroy it. */ - if (add_mtd_chip) - del_mtd_device(g->chip_mtds[chip]); + if (nand->numchips > 1) { + del_mtd_device(g->general_use_mtd); + mtd_concat_destroy(g->general_use_mtd); + } /* - * Free the memory we used to hold the name of the MTD that - * represented this chip. + * Destroy all the partition MTDs based directly on the medium + * MTD. */ - kfree(g->chip_partitions[chip].name); + del_mtd_partitions(mtd); } + #endif + } /** @@ -1846,11 +2112,9 @@ static int __init gpmi_nand_probe(struct platform_device *pdev) { struct gpmi_nand_data *g; struct gpmi_platform_data *gpd; - const char *part_type = 0; int err = 0; struct resource *r; int dma; - unsigned long long chipsize; /* Allocate memory for the per-device structure (and zero it). */ g = kzalloc(sizeof(*g), GFP_KERNEL); @@ -1881,6 +2145,7 @@ static int __init gpmi_nand_probe(struct platform_device *pdev) } gpd = (struct gpmi_platform_data *)pdev->dev.platform_data; + g->gpd = gpd; platform_set_drvdata(pdev, g); err = gpmi_nand_init_hw(pdev, 1); if (err) @@ -1939,236 +2204,17 @@ static int __init gpmi_nand_probe(struct platform_device *pdev) } g->dev = pdev; - g->chip.priv = g; + g->nand.priv = g; g->timing = gpmi_safe_timing; 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; - - /* - * 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; - - /* - * 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; - - /* - * 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.block_bad = gpmi_block_bad; - g->chip.scan_bbt = gpmi_scan_bbt; - - 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." - */ + /* Register with MTD. */ - if (nand_scan_ident(&g->mtd, max_chips) - || gpmi_scan_middle(g) - || nand_scan_tail(&g->mtd)) { - dev_err(&pdev->dev, "No NAND Flash chips found\n"); - /* errors found on some step */ + if (gpmi_register_with_mtd(g)) goto out7; - } - - /* 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; - - /* 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; - -#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; - - /* - * 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->chip_partitions[0]. - part_probe_types, - &g->chip_partitions, - 0); - if (g->nr_parts > 0) - part_type = "command line"; - else - g->nr_parts = 0; - } - - /* - * 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->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 + /* Stay away from Unique ID -- It's going away soon. */ /* Initialize the Unique ID facility. */ @@ -2189,7 +2235,6 @@ static int __init gpmi_nand_probe(struct platform_device *pdev) return 0; -out_all: ecc8_exit(); bch_exit(); out7: @@ -2219,13 +2264,8 @@ out1: static int __devexit gpmi_nand_remove(struct platform_device *pdev) { struct gpmi_nand_data *g = platform_get_drvdata(pdev); - int i = 0; -#ifdef CONFIG_MTD_PARTITIONS - struct gpmi_platform_data *gpd = pdev->dev.platform_data; - struct mtd_partition *platf_parts; -#endif - gpmi_delete_partitions(g); + gpmi_unregister_with_mtd(g); del_timer_sync(&g->timer); gpmi_uid_remove("nand"); @@ -2240,16 +2280,6 @@ static int __devexit gpmi_nand_remove(struct platform_device *pdev) free_irq(g->irq, g); if (g->regulator) regulator_put(g->regulator); - -#ifdef CONFIG_MTD_PARTITIONS - if (i < gpd->chip_count && gpd->chip_partitions[i].partitions) - platf_parts = gpd->chip_partitions[i].partitions; - else - platf_parts = NULL; - if (g->chip_partitions && g->chip_partitions != platf_parts) - kfree(g->chip_partitions); -#endif - iounmap(g->io_base); kfree(g); @@ -2431,7 +2461,7 @@ static ssize_t show_chips(struct device *d, struct device_attribute *attr, char *buf) { struct gpmi_nand_data *g = dev_get_drvdata(d); - return sprintf(buf, "%d\n", g->numchips); + return sprintf(buf, "%d\n", g->nand.numchips); } /** @@ -2470,11 +2500,11 @@ static ssize_t store_ignorebad(struct device *d, struct device_attribute *attr, v = 1; if (v != g->ignorebad) { if (v) { - g->bbt = g->chip.bbt; - g->chip.bbt = NULL; + g->bbt = g->nand.bbt; + g->nand.bbt = NULL; g->ignorebad = 1; } else { - g->chip.bbt = g->bbt; + g->nand.bbt = g->bbt; g->ignorebad = 0; } } diff --git a/drivers/mtd/nand/gpmi/gpmi.h b/drivers/mtd/nand/gpmi/gpmi.h index 90e71db4cd9a..3e273659274f 100644 --- a/drivers/mtd/nand/gpmi/gpmi.h +++ b/drivers/mtd/nand/gpmi/gpmi.h @@ -178,8 +178,10 @@ void bch_exit(void); void ecc8_exit(void); /** - * struct gpmi_nand_data - + * struct gpmi_nand_data - GPMI driver per-device data structure. * + * @dev: A pointer to the owning struct device. + * @gpd: GPMI-specific platform data. * @io_base: The base I/O address of of the GPMI registers. * @clk: A pointer to the structure that represents the GPMI * clock. @@ -199,17 +201,14 @@ void ecc8_exit(void); * @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. + * @nand: The data structure that represents this NAND Flash + * medium to the MTD NAND Flash system. + * @partitions: A pointer to an array of partition descriptions + * collected from the platform. If this member is NULL, + * then no such partitions were given. + * @partition_count: The number of elements in the partitions array. * @done: A struct completion used to manage GPMI interrupts. * @cmd_buffer: * @cmd_buffer_handle: @@ -252,33 +251,13 @@ void ecc8_exit(void); * 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 { + + struct platform_device *dev; + struct gpmi_platform_data *gpd; + void __iomem *io_base; struct clk *clk; int irq; @@ -291,13 +270,15 @@ struct gpmi_nand_data { int ignorebad; void *bbt; - struct nand_chip chip; - struct mtd_info mtd; - struct platform_device *dev; + struct mtd_info mtd; + struct nand_chip nand; -#ifdef CONFIG_MTD_PARTITIONS - int nr_parts; - struct mtd_partition *parts; +#if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT) + struct mtd_info *chip0_boot_mtd; + struct mtd_info *chip1_boot_mtd; + struct mtd_info *general_use_mtd; + struct mtd_partition *partitions; + unsigned partition_count; #endif struct completion done; @@ -351,13 +332,6 @@ struct gpmi_nand_data { struct gpmi_ecc_descriptor *hc; - int numchips; - int custom_partitions; - 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]; - struct mtd_info *concat_mtd; }; extern struct gpmi_nand_timing gpmi_safe_timing; |