Merge tag 'for-linus-3.4' of git://git.infradead.org/mtd-2.6
Pull MTD changes from David Woodhouse: - Artem's cleanup of the MTD API continues apace. - Fixes and improvements for ST FSMC and SuperH FLCTL NAND, amongst others. - More work on DiskOnChip G3, new driver for DiskOnChip G4. - Clean up debug/warning printks in JFFS2 to use pr_<level>. Fix up various trivial conflicts, largely due to changes in calling conventions for things like dmaengine_prep_slave_sg() (new inline wrapper to hide new parameter, clashing with rewrite of previously last parameter that used to be an 'append' flag, and is now a bitmap of 'unsigned long flags'). (Also some header file fallout - like so many merges this merge window - and silly conflicts with sparse fixes) * tag 'for-linus-3.4' of git://git.infradead.org/mtd-2.6: (120 commits) mtd: docg3 add protection against concurrency mtd: docg3 refactor cascade floors structure mtd: docg3 increase write/erase timeout mtd: docg3 fix inbound calculations mtd: nand: gpmi: fix function annotations mtd: phram: fix section mismatch for phram_setup mtd: unify initialization of erase_info->fail_addr mtd: support ONFI multi lun NAND mtd: sm_ftl: fix typo in major number. mtd: add device-tree support to spear_smi mtd: spear_smi: Remove default partition information from driver mtd: Add device-tree support to fsmc_nand mtd: fix section mismatch for doc_probe_device mtd: nand/fsmc: Remove sparse warnings and errors mtd: nand/fsmc: Add DMA support mtd: nand/fsmc: Access the NAND device word by word whenever possible mtd: nand/fsmc: Use dev_err to report error scenario mtd: nand/fsmc: Use devm routines mtd: nand/fsmc: Modify fsmc driver to accept nand timing parameters via platform mtd: fsmc_nand: add pm callbacks to support hibernation ...
This commit is contained in:
33
Documentation/devicetree/bindings/mtd/fsmc-nand.txt
Normal file
33
Documentation/devicetree/bindings/mtd/fsmc-nand.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
* FSMC NAND
|
||||
|
||||
Required properties:
|
||||
- compatible : "st,spear600-fsmc-nand"
|
||||
- reg : Address range of the mtd chip
|
||||
- reg-names: Should contain the reg names "fsmc_regs" and "nand_data"
|
||||
- st,ale-off : Chip specific offset to ALE
|
||||
- st,cle-off : Chip specific offset to CLE
|
||||
|
||||
Optional properties:
|
||||
- bank-width : Width (in bytes) of the device. If not present, the width
|
||||
defaults to 1 byte
|
||||
- nand-skip-bbtscan: Indicates the the BBT scanning should be skipped
|
||||
|
||||
Example:
|
||||
|
||||
fsmc: flash@d1800000 {
|
||||
compatible = "st,spear600-fsmc-nand";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0xd1800000 0x1000 /* FSMC Register */
|
||||
0xd2000000 0x4000>; /* NAND Base */
|
||||
reg-names = "fsmc_regs", "nand_data";
|
||||
st,ale-off = <0x20000>;
|
||||
st,cle-off = <0x10000>;
|
||||
|
||||
bank-width = <1>;
|
||||
nand-skip-bbtscan;
|
||||
|
||||
partition@0 {
|
||||
...
|
||||
};
|
||||
};
|
31
Documentation/devicetree/bindings/mtd/spear_smi.txt
Normal file
31
Documentation/devicetree/bindings/mtd/spear_smi.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
* SPEAr SMI
|
||||
|
||||
Required properties:
|
||||
- compatible : "st,spear600-smi"
|
||||
- reg : Address range of the mtd chip
|
||||
- #address-cells, #size-cells : Must be present if the device has sub-nodes
|
||||
representing partitions.
|
||||
- interrupt-parent: Should be the phandle for the interrupt controller
|
||||
that services interrupts for this device
|
||||
- interrupts: Should contain the STMMAC interrupts
|
||||
- clock-rate : Functional clock rate of SMI in Hz
|
||||
|
||||
Optional properties:
|
||||
- st,smi-fast-mode : Flash supports read in fast mode
|
||||
|
||||
Example:
|
||||
|
||||
smi: flash@fc000000 {
|
||||
compatible = "st,spear600-smi";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0xfc000000 0x1000>;
|
||||
interrupt-parent = <&vic1>;
|
||||
interrupts = <12>;
|
||||
clock-rate = <50000000>; /* 50MHz */
|
||||
|
||||
flash@f8000000 {
|
||||
st,smi-fast-mode;
|
||||
...
|
||||
};
|
||||
};
|
@@ -17,20 +17,12 @@
|
||||
|
||||
void omap1_set_vpp(struct platform_device *pdev, int enable)
|
||||
{
|
||||
static int count;
|
||||
u32 l;
|
||||
|
||||
if (enable) {
|
||||
if (count++ == 0) {
|
||||
l = omap_readl(EMIFS_CONFIG);
|
||||
l |= OMAP_EMIFS_CONFIG_WP;
|
||||
omap_writel(l, EMIFS_CONFIG);
|
||||
}
|
||||
} else {
|
||||
if (count && (--count == 0)) {
|
||||
l = omap_readl(EMIFS_CONFIG);
|
||||
l &= ~OMAP_EMIFS_CONFIG_WP;
|
||||
omap_writel(l, EMIFS_CONFIG);
|
||||
}
|
||||
}
|
||||
l = omap_readl(EMIFS_CONFIG);
|
||||
if (enable)
|
||||
l |= OMAP_EMIFS_CONFIG_WP;
|
||||
else
|
||||
l &= ~OMAP_EMIFS_CONFIG_WP;
|
||||
omap_writel(l, EMIFS_CONFIG);
|
||||
}
|
||||
|
@@ -35,9 +35,7 @@
|
||||
static void simtec_nor_vpp(struct platform_device *pdev, int vpp)
|
||||
{
|
||||
unsigned int val;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
val = __raw_readb(BAST_VA_CTRL3);
|
||||
|
||||
printk(KERN_DEBUG "%s(%d)\n", __func__, vpp);
|
||||
@@ -48,7 +46,6 @@ static void simtec_nor_vpp(struct platform_device *pdev, int vpp)
|
||||
val &= ~BAST_CPLD_CTRL3_ROMWEN;
|
||||
|
||||
__raw_writeb(val, BAST_VA_CTRL3);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static struct physmap_flash_data simtec_nor_pdata = {
|
||||
|
@@ -39,6 +39,7 @@
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/mtd/sh_flctl.h>
|
||||
#include <linux/pm_clock.h>
|
||||
#include <linux/smsc911x.h>
|
||||
#include <linux/sh_intc.h>
|
||||
@@ -956,6 +957,50 @@ static struct platform_device fsi_ak4643_device = {
|
||||
},
|
||||
};
|
||||
|
||||
/* FLCTL */
|
||||
static struct mtd_partition nand_partition_info[] = {
|
||||
{
|
||||
.name = "system",
|
||||
.offset = 0,
|
||||
.size = 128 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
.name = "userdata",
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.size = 256 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
.name = "cache",
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.size = 128 * 1024 * 1024,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource nand_flash_resources[] = {
|
||||
[0] = {
|
||||
.start = 0xe6a30000,
|
||||
.end = 0xe6a3009b,
|
||||
.flags = IORESOURCE_MEM,
|
||||
}
|
||||
};
|
||||
|
||||
static struct sh_flctl_platform_data nand_flash_data = {
|
||||
.parts = nand_partition_info,
|
||||
.nr_parts = ARRAY_SIZE(nand_partition_info),
|
||||
.flcmncr_val = CLK_16B_12L_4H | TYPESEL_SET
|
||||
| SHBUSSEL | SEL_16BIT | SNAND_E,
|
||||
.use_holden = 1,
|
||||
};
|
||||
|
||||
static struct platform_device nand_flash_device = {
|
||||
.name = "sh_flctl",
|
||||
.resource = nand_flash_resources,
|
||||
.num_resources = ARRAY_SIZE(nand_flash_resources),
|
||||
.dev = {
|
||||
.platform_data = &nand_flash_data,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* The card detect pin of the top SD/MMC slot (CN7) is active low and is
|
||||
* connected to GPIO A22 of SH7372 (GPIO_PORT41).
|
||||
@@ -1259,6 +1304,7 @@ static struct platform_device *mackerel_devices[] __initdata = {
|
||||
&fsi_device,
|
||||
&fsi_ak4643_device,
|
||||
&fsi_hdmi_device,
|
||||
&nand_flash_device,
|
||||
&sdhi0_device,
|
||||
#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
|
||||
&sdhi1_device,
|
||||
@@ -1488,6 +1534,30 @@ static void __init mackerel_init(void)
|
||||
gpio_request(GPIO_FN_MMCCMD0, NULL);
|
||||
gpio_request(GPIO_FN_MMCCLK0, NULL);
|
||||
|
||||
/* FLCTL */
|
||||
gpio_request(GPIO_FN_D0_NAF0, NULL);
|
||||
gpio_request(GPIO_FN_D1_NAF1, NULL);
|
||||
gpio_request(GPIO_FN_D2_NAF2, NULL);
|
||||
gpio_request(GPIO_FN_D3_NAF3, NULL);
|
||||
gpio_request(GPIO_FN_D4_NAF4, NULL);
|
||||
gpio_request(GPIO_FN_D5_NAF5, NULL);
|
||||
gpio_request(GPIO_FN_D6_NAF6, NULL);
|
||||
gpio_request(GPIO_FN_D7_NAF7, NULL);
|
||||
gpio_request(GPIO_FN_D8_NAF8, NULL);
|
||||
gpio_request(GPIO_FN_D9_NAF9, NULL);
|
||||
gpio_request(GPIO_FN_D10_NAF10, NULL);
|
||||
gpio_request(GPIO_FN_D11_NAF11, NULL);
|
||||
gpio_request(GPIO_FN_D12_NAF12, NULL);
|
||||
gpio_request(GPIO_FN_D13_NAF13, NULL);
|
||||
gpio_request(GPIO_FN_D14_NAF14, NULL);
|
||||
gpio_request(GPIO_FN_D15_NAF15, NULL);
|
||||
gpio_request(GPIO_FN_FCE0, NULL);
|
||||
gpio_request(GPIO_FN_WE0_FWE, NULL);
|
||||
gpio_request(GPIO_FN_FRB, NULL);
|
||||
gpio_request(GPIO_FN_A4_FOE, NULL);
|
||||
gpio_request(GPIO_FN_A5_FCDE, NULL);
|
||||
gpio_request(GPIO_FN_RD_FSC, NULL);
|
||||
|
||||
/* enable GPS module (GT-720F) */
|
||||
gpio_request(GPIO_FN_SCIFA2_TXD1, NULL);
|
||||
gpio_request(GPIO_FN_SCIFA2_RXD1, NULL);
|
||||
@@ -1532,6 +1602,7 @@ static void __init mackerel_init(void)
|
||||
sh7372_add_device_to_domain(&sh7372_a4mp, &fsi_device);
|
||||
sh7372_add_device_to_domain(&sh7372_a3sp, &usbhs0_device);
|
||||
sh7372_add_device_to_domain(&sh7372_a3sp, &usbhs1_device);
|
||||
sh7372_add_device_to_domain(&sh7372_a3sp, &nand_flash_device);
|
||||
sh7372_add_device_to_domain(&sh7372_a3sp, &sh_mmcif_device);
|
||||
sh7372_add_device_to_domain(&sh7372_a3sp, &sdhi0_device);
|
||||
#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
|
||||
|
@@ -511,7 +511,7 @@ enum { MSTP001, MSTP000,
|
||||
MSTP223,
|
||||
MSTP218, MSTP217, MSTP216, MSTP214, MSTP208, MSTP207,
|
||||
MSTP206, MSTP205, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200,
|
||||
MSTP328, MSTP323, MSTP322, MSTP314, MSTP313, MSTP312,
|
||||
MSTP328, MSTP323, MSTP322, MSTP315, MSTP314, MSTP313, MSTP312,
|
||||
MSTP423, MSTP415, MSTP413, MSTP411, MSTP410, MSTP407, MSTP406,
|
||||
MSTP405, MSTP404, MSTP403, MSTP400,
|
||||
MSTP_NR };
|
||||
@@ -553,6 +553,7 @@ static struct clk mstp_clks[MSTP_NR] = {
|
||||
[MSTP328] = MSTP(&div6_clks[DIV6_SPU], SMSTPCR3, 28, 0), /* FSI2 */
|
||||
[MSTP323] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 23, 0), /* IIC1 */
|
||||
[MSTP322] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 22, 0), /* USB0 */
|
||||
[MSTP315] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 15, 0), /* FLCTL*/
|
||||
[MSTP314] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 14, 0), /* SDHI0 */
|
||||
[MSTP313] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 13, 0), /* SDHI1 */
|
||||
[MSTP312] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 12, 0), /* MMC */
|
||||
@@ -653,6 +654,7 @@ static struct clk_lookup lookups[] = {
|
||||
CLKDEV_DEV_ID("r8a66597_hcd.0", &mstp_clks[MSTP322]), /* USB0 */
|
||||
CLKDEV_DEV_ID("r8a66597_udc.0", &mstp_clks[MSTP322]), /* USB0 */
|
||||
CLKDEV_DEV_ID("renesas_usbhs.0", &mstp_clks[MSTP322]), /* USB0 */
|
||||
CLKDEV_DEV_ID("sh_flctl.0", &mstp_clks[MSTP315]), /* FLCTL */
|
||||
CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP314]), /* SDHI0 */
|
||||
CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP313]), /* SDHI1 */
|
||||
CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[MSTP312]), /* MMC */
|
||||
|
@@ -1544,6 +1544,8 @@ static struct fsmc_nand_platform_data nand_platform_data = {
|
||||
.nr_partitions = ARRAY_SIZE(u300_partitions),
|
||||
.options = NAND_SKIP_BBTSCAN,
|
||||
.width = FSMC_NAND_BW8,
|
||||
.ale_off = PLAT_NAND_ALE,
|
||||
.cle_off = PLAT_NAND_CLE,
|
||||
};
|
||||
|
||||
static struct platform_device nand_device = {
|
||||
|
@@ -24,6 +24,11 @@
|
||||
/* NFIF */
|
||||
#define U300_NAND_IF_PHYS_BASE 0x9f800000
|
||||
|
||||
/* ALE, CLE offset for FSMC NAND */
|
||||
#define PLAT_NAND_CLE (1 << 16)
|
||||
#define PLAT_NAND_ALE (1 << 17)
|
||||
|
||||
|
||||
/* AHB Peripherals */
|
||||
#define U300_AHB_PER_PHYS_BASE 0xa0000000
|
||||
#define U300_AHB_PER_VIRT_BASE 0xff010000
|
||||
|
@@ -60,7 +60,7 @@ static int __init flash_init(void)
|
||||
if (mymtd) {
|
||||
mymtd->owner = THIS_MODULE;
|
||||
mtd_device_parse_register(mymtd, part_probe_types,
|
||||
0, NULL, 0);
|
||||
NULL, NULL, 0);
|
||||
} else {
|
||||
pr_err("Failed to register MTD device for flash\n");
|
||||
}
|
||||
|
@@ -22,10 +22,10 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fsl/mxs-dma.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <mach/mxs.h>
|
||||
#include <mach/dma.h>
|
||||
#include <mach/common.h>
|
||||
|
||||
#include "dmaengine.h"
|
||||
@@ -337,10 +337,32 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan)
|
||||
clk_disable_unprepare(mxs_dma->clk);
|
||||
}
|
||||
|
||||
/*
|
||||
* How to use the flags for ->device_prep_slave_sg() :
|
||||
* [1] If there is only one DMA command in the DMA chain, the code should be:
|
||||
* ......
|
||||
* ->device_prep_slave_sg(DMA_CTRL_ACK);
|
||||
* ......
|
||||
* [2] If there are two DMA commands in the DMA chain, the code should be
|
||||
* ......
|
||||
* ->device_prep_slave_sg(0);
|
||||
* ......
|
||||
* ->device_prep_slave_sg(DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
* ......
|
||||
* [3] If there are more than two DMA commands in the DMA chain, the code
|
||||
* should be:
|
||||
* ......
|
||||
* ->device_prep_slave_sg(0); // First
|
||||
* ......
|
||||
* ->device_prep_slave_sg(DMA_PREP_INTERRUPT [| DMA_CTRL_ACK]);
|
||||
* ......
|
||||
* ->device_prep_slave_sg(DMA_PREP_INTERRUPT | DMA_CTRL_ACK); // Last
|
||||
* ......
|
||||
*/
|
||||
static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
|
||||
struct dma_chan *chan, struct scatterlist *sgl,
|
||||
unsigned int sg_len, enum dma_transfer_direction direction,
|
||||
unsigned long append, void *context)
|
||||
unsigned long flags, void *context)
|
||||
{
|
||||
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
|
||||
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
|
||||
@@ -348,6 +370,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
|
||||
struct scatterlist *sg;
|
||||
int i, j;
|
||||
u32 *pio;
|
||||
bool append = flags & DMA_PREP_INTERRUPT;
|
||||
int idx = append ? mxs_chan->desc_count : 0;
|
||||
|
||||
if (mxs_chan->status == DMA_IN_PROGRESS && !append)
|
||||
@@ -374,7 +397,6 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
|
||||
ccw->bits |= CCW_CHAIN;
|
||||
ccw->bits &= ~CCW_IRQ;
|
||||
ccw->bits &= ~CCW_DEC_SEM;
|
||||
ccw->bits &= ~CCW_WAIT4END;
|
||||
} else {
|
||||
idx = 0;
|
||||
}
|
||||
@@ -389,7 +411,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
|
||||
ccw->bits = 0;
|
||||
ccw->bits |= CCW_IRQ;
|
||||
ccw->bits |= CCW_DEC_SEM;
|
||||
ccw->bits |= CCW_WAIT4END;
|
||||
if (flags & DMA_CTRL_ACK)
|
||||
ccw->bits |= CCW_WAIT4END;
|
||||
ccw->bits |= CCW_HALT_ON_TERM;
|
||||
ccw->bits |= CCW_TERM_FLUSH;
|
||||
ccw->bits |= BF_CCW(sg_len, PIO_NUM);
|
||||
@@ -420,7 +443,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
|
||||
ccw->bits &= ~CCW_CHAIN;
|
||||
ccw->bits |= CCW_IRQ;
|
||||
ccw->bits |= CCW_DEC_SEM;
|
||||
ccw->bits |= CCW_WAIT4END;
|
||||
if (flags & DMA_CTRL_ACK)
|
||||
ccw->bits |= CCW_WAIT4END;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -38,10 +38,10 @@
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fsl/mxs-dma.h>
|
||||
|
||||
#include <mach/mxs.h>
|
||||
#include <mach/common.h>
|
||||
#include <mach/dma.h>
|
||||
#include <mach/mmc.h>
|
||||
|
||||
#define DRIVER_NAME "mxs-mmc"
|
||||
@@ -305,7 +305,7 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
|
||||
struct mxs_mmc_host *host, unsigned int append)
|
||||
struct mxs_mmc_host *host, unsigned long flags)
|
||||
{
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct mmc_data *data = host->data;
|
||||
@@ -325,7 +325,7 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
|
||||
}
|
||||
|
||||
desc = dmaengine_prep_slave_sg(host->dmach,
|
||||
sgl, sg_len, host->slave_dirn, append);
|
||||
sgl, sg_len, host->slave_dirn, flags);
|
||||
if (desc) {
|
||||
desc->callback = mxs_mmc_dma_irq_callback;
|
||||
desc->callback_param = host;
|
||||
@@ -358,7 +358,7 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host)
|
||||
host->ssp_pio_words[2] = cmd1;
|
||||
host->dma_dir = DMA_NONE;
|
||||
host->slave_dirn = DMA_TRANS_NONE;
|
||||
desc = mxs_mmc_prep_dma(host, 0);
|
||||
desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
|
||||
if (!desc)
|
||||
goto out;
|
||||
|
||||
@@ -398,7 +398,7 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host)
|
||||
host->ssp_pio_words[2] = cmd1;
|
||||
host->dma_dir = DMA_NONE;
|
||||
host->slave_dirn = DMA_TRANS_NONE;
|
||||
desc = mxs_mmc_prep_dma(host, 0);
|
||||
desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
|
||||
if (!desc)
|
||||
goto out;
|
||||
|
||||
@@ -526,7 +526,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
|
||||
host->data = data;
|
||||
host->dma_dir = dma_data_dir;
|
||||
host->slave_dirn = slave_dirn;
|
||||
desc = mxs_mmc_prep_dma(host, 1);
|
||||
desc = mxs_mmc_prep_dma(host, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc)
|
||||
goto out;
|
||||
|
||||
|
@@ -304,9 +304,6 @@ config MTD_OOPS
|
||||
buffer in a flash partition where it can be read back at some
|
||||
later point.
|
||||
|
||||
To use, add console=ttyMTDx to the kernel command line,
|
||||
where x is the MTD device number to use.
|
||||
|
||||
config MTD_SWAP
|
||||
tristate "Swap on MTD device support"
|
||||
depends on MTD && SWAP
|
||||
|
@@ -87,7 +87,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *, struct cfi_private **
|
||||
|
||||
static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, void **virt, resource_size_t *phys);
|
||||
static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len);
|
||||
static int cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len);
|
||||
|
||||
static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
|
||||
static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
|
||||
@@ -262,9 +262,9 @@ static void fixup_st_m28w320cb(struct mtd_info *mtd)
|
||||
static void fixup_use_point(struct mtd_info *mtd)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
if (!mtd->point && map_is_linear(map)) {
|
||||
mtd->point = cfi_intelext_point;
|
||||
mtd->unpoint = cfi_intelext_unpoint;
|
||||
if (!mtd->_point && map_is_linear(map)) {
|
||||
mtd->_point = cfi_intelext_point;
|
||||
mtd->_unpoint = cfi_intelext_unpoint;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,8 +274,8 @@ static void fixup_use_write_buffers(struct mtd_info *mtd)
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
if (cfi->cfiq->BufWriteTimeoutTyp) {
|
||||
printk(KERN_INFO "Using buffer write method\n" );
|
||||
mtd->write = cfi_intelext_write_buffers;
|
||||
mtd->writev = cfi_intelext_writev;
|
||||
mtd->_write = cfi_intelext_write_buffers;
|
||||
mtd->_writev = cfi_intelext_writev;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,15 +443,15 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
|
||||
mtd->type = MTD_NORFLASH;
|
||||
|
||||
/* Fill in the default mtd operations */
|
||||
mtd->erase = cfi_intelext_erase_varsize;
|
||||
mtd->read = cfi_intelext_read;
|
||||
mtd->write = cfi_intelext_write_words;
|
||||
mtd->sync = cfi_intelext_sync;
|
||||
mtd->lock = cfi_intelext_lock;
|
||||
mtd->unlock = cfi_intelext_unlock;
|
||||
mtd->is_locked = cfi_intelext_is_locked;
|
||||
mtd->suspend = cfi_intelext_suspend;
|
||||
mtd->resume = cfi_intelext_resume;
|
||||
mtd->_erase = cfi_intelext_erase_varsize;
|
||||
mtd->_read = cfi_intelext_read;
|
||||
mtd->_write = cfi_intelext_write_words;
|
||||
mtd->_sync = cfi_intelext_sync;
|
||||
mtd->_lock = cfi_intelext_lock;
|
||||
mtd->_unlock = cfi_intelext_unlock;
|
||||
mtd->_is_locked = cfi_intelext_is_locked;
|
||||
mtd->_suspend = cfi_intelext_suspend;
|
||||
mtd->_resume = cfi_intelext_resume;
|
||||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
mtd->name = map->name;
|
||||
mtd->writesize = 1;
|
||||
@@ -600,12 +600,12 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_OTP
|
||||
mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
|
||||
mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
|
||||
mtd->write_user_prot_reg = cfi_intelext_write_user_prot_reg;
|
||||
mtd->lock_user_prot_reg = cfi_intelext_lock_user_prot_reg;
|
||||
mtd->get_fact_prot_info = cfi_intelext_get_fact_prot_info;
|
||||
mtd->get_user_prot_info = cfi_intelext_get_user_prot_info;
|
||||
mtd->_read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
|
||||
mtd->_read_user_prot_reg = cfi_intelext_read_user_prot_reg;
|
||||
mtd->_write_user_prot_reg = cfi_intelext_write_user_prot_reg;
|
||||
mtd->_lock_user_prot_reg = cfi_intelext_lock_user_prot_reg;
|
||||
mtd->_get_fact_prot_info = cfi_intelext_get_fact_prot_info;
|
||||
mtd->_get_user_prot_info = cfi_intelext_get_user_prot_info;
|
||||
#endif
|
||||
|
||||
/* This function has the potential to distort the reality
|
||||
@@ -1017,8 +1017,6 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
|
||||
case FL_READY:
|
||||
case FL_STATUS:
|
||||
case FL_JEDEC_QUERY:
|
||||
/* We should really make set_vpp() count, rather than doing this */
|
||||
DISABLE_VPP(map);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: put_chip() called with oldstate %d!!\n", map->name, chip->oldstate);
|
||||
@@ -1324,7 +1322,7 @@ static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
int chipnum;
|
||||
int ret = 0;
|
||||
|
||||
if (!map->virt || (from + len > mtd->size))
|
||||
if (!map->virt)
|
||||
return -EINVAL;
|
||||
|
||||
/* Now lock the chip(s) to POINT state */
|
||||
@@ -1334,7 +1332,6 @@ static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
ofs = from - (chipnum << cfi->chipshift);
|
||||
|
||||
*virt = map->virt + cfi->chips[chipnum].start + ofs;
|
||||
*retlen = 0;
|
||||
if (phys)
|
||||
*phys = map->phys + cfi->chips[chipnum].start + ofs;
|
||||
|
||||
@@ -1369,12 +1366,12 @@ static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
static int cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long ofs;
|
||||
int chipnum;
|
||||
int chipnum, err = 0;
|
||||
|
||||
/* Now unlock the chip(s) POINT state */
|
||||
|
||||
@@ -1382,7 +1379,7 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
chipnum = (from >> cfi->chipshift);
|
||||
ofs = from - (chipnum << cfi->chipshift);
|
||||
|
||||
while (len) {
|
||||
while (len && !err) {
|
||||
unsigned long thislen;
|
||||
struct flchip *chip;
|
||||
|
||||
@@ -1400,8 +1397,10 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
chip->ref_point_counter--;
|
||||
if(chip->ref_point_counter == 0)
|
||||
chip->state = FL_READY;
|
||||
} else
|
||||
printk(KERN_ERR "%s: Warning: unpoint called on non pointed region\n", map->name); /* Should this give an error? */
|
||||
} else {
|
||||
printk(KERN_ERR "%s: Error: unpoint called on non pointed region\n", map->name);
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
put_chip(map, chip, chip->start);
|
||||
mutex_unlock(&chip->mutex);
|
||||
@@ -1410,6 +1409,8 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
ofs = 0;
|
||||
chipnum++;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
|
||||
@@ -1456,8 +1457,6 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz
|
||||
chipnum = (from >> cfi->chipshift);
|
||||
ofs = from - (chipnum << cfi->chipshift);
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
while (len) {
|
||||
unsigned long thislen;
|
||||
|
||||
@@ -1551,7 +1550,8 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
|
||||
}
|
||||
|
||||
xip_enable(map, chip, adr);
|
||||
out: put_chip(map, chip, adr);
|
||||
out: DISABLE_VPP(map);
|
||||
put_chip(map, chip, adr);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
@@ -1565,10 +1565,6 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
|
||||
int chipnum;
|
||||
unsigned long ofs;
|
||||
|
||||
*retlen = 0;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
chipnum = to >> cfi->chipshift;
|
||||
ofs = to - (chipnum << cfi->chipshift);
|
||||
|
||||
@@ -1794,7 +1790,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||
}
|
||||
|
||||
xip_enable(map, chip, cmd_adr);
|
||||
out: put_chip(map, chip, cmd_adr);
|
||||
out: DISABLE_VPP(map);
|
||||
put_chip(map, chip, cmd_adr);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
@@ -1813,7 +1810,6 @@ static int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs,
|
||||
for (i = 0; i < count; i++)
|
||||
len += vecs[i].iov_len;
|
||||
|
||||
*retlen = 0;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
@@ -1932,6 +1928,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
||||
ret = -EIO;
|
||||
} else if (chipstatus & 0x20 && retries--) {
|
||||
printk(KERN_DEBUG "block erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus);
|
||||
DISABLE_VPP(map);
|
||||
put_chip(map, chip, adr);
|
||||
mutex_unlock(&chip->mutex);
|
||||
goto retry;
|
||||
@@ -1944,7 +1941,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
||||
}
|
||||
|
||||
xip_enable(map, chip, adr);
|
||||
out: put_chip(map, chip, adr);
|
||||
out: DISABLE_VPP(map);
|
||||
put_chip(map, chip, adr);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
@@ -2086,7 +2084,8 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
|
||||
}
|
||||
|
||||
xip_enable(map, chip, adr);
|
||||
out: put_chip(map, chip, adr);
|
||||
out: DISABLE_VPP(map);
|
||||
put_chip(map, chip, adr);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
@@ -2483,7 +2482,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd)
|
||||
allowed to. Or should we return -EAGAIN, because the upper layers
|
||||
ought to have already shut down anything which was using the device
|
||||
anyway? The latter for now. */
|
||||
printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->oldstate);
|
||||
printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->state);
|
||||
ret = -EAGAIN;
|
||||
case FL_PM_SUSPENDED:
|
||||
break;
|
||||
|
@@ -59,6 +59,9 @@ static void cfi_amdstd_resume (struct mtd_info *);
|
||||
static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *);
|
||||
static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
|
||||
static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf);
|
||||
|
||||
static void cfi_amdstd_destroy(struct mtd_info *);
|
||||
|
||||
struct mtd_info *cfi_cmdset_0002(struct map_info *, int);
|
||||
@@ -189,7 +192,7 @@ static void fixup_use_write_buffers(struct mtd_info *mtd)
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
if (cfi->cfiq->BufWriteTimeoutTyp) {
|
||||
pr_debug("Using buffer write method\n" );
|
||||
mtd->write = cfi_amdstd_write_buffers;
|
||||
mtd->_write = cfi_amdstd_write_buffers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,8 +231,8 @@ static void fixup_convert_atmel_pri(struct mtd_info *mtd)
|
||||
static void fixup_use_secsi(struct mtd_info *mtd)
|
||||
{
|
||||
/* Setup for chips with a secsi area */
|
||||
mtd->read_user_prot_reg = cfi_amdstd_secsi_read;
|
||||
mtd->read_fact_prot_reg = cfi_amdstd_secsi_read;
|
||||
mtd->_read_user_prot_reg = cfi_amdstd_secsi_read;
|
||||
mtd->_read_fact_prot_reg = cfi_amdstd_secsi_read;
|
||||
}
|
||||
|
||||
static void fixup_use_erase_chip(struct mtd_info *mtd)
|
||||
@@ -238,7 +241,7 @@ static void fixup_use_erase_chip(struct mtd_info *mtd)
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
if ((cfi->cfiq->NumEraseRegions == 1) &&
|
||||
((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) {
|
||||
mtd->erase = cfi_amdstd_erase_chip;
|
||||
mtd->_erase = cfi_amdstd_erase_chip;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -249,8 +252,8 @@ static void fixup_use_erase_chip(struct mtd_info *mtd)
|
||||
*/
|
||||
static void fixup_use_atmel_lock(struct mtd_info *mtd)
|
||||
{
|
||||
mtd->lock = cfi_atmel_lock;
|
||||
mtd->unlock = cfi_atmel_unlock;
|
||||
mtd->_lock = cfi_atmel_lock;
|
||||
mtd->_unlock = cfi_atmel_unlock;
|
||||
mtd->flags |= MTD_POWERUP_LOCK;
|
||||
}
|
||||
|
||||
@@ -429,12 +432,12 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
|
||||
mtd->type = MTD_NORFLASH;
|
||||
|
||||
/* Fill in the default mtd operations */
|
||||
mtd->erase = cfi_amdstd_erase_varsize;
|
||||
mtd->write = cfi_amdstd_write_words;
|
||||
mtd->read = cfi_amdstd_read;
|
||||
mtd->sync = cfi_amdstd_sync;
|
||||
mtd->suspend = cfi_amdstd_suspend;
|
||||
mtd->resume = cfi_amdstd_resume;
|
||||
mtd->_erase = cfi_amdstd_erase_varsize;
|
||||
mtd->_write = cfi_amdstd_write_words;
|
||||
mtd->_read = cfi_amdstd_read;
|
||||
mtd->_sync = cfi_amdstd_sync;
|
||||
mtd->_suspend = cfi_amdstd_suspend;
|
||||
mtd->_resume = cfi_amdstd_resume;
|
||||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
mtd->name = map->name;
|
||||
mtd->writesize = 1;
|
||||
@@ -443,6 +446,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
|
||||
pr_debug("MTD %s(): write buffer size %d\n", __func__,
|
||||
mtd->writebufsize);
|
||||
|
||||
mtd->_panic_write = cfi_amdstd_panic_write;
|
||||
mtd->reboot_notifier.notifier_call = cfi_amdstd_reboot;
|
||||
|
||||
if (cfi->cfi_mode==CFI_MODE_CFI){
|
||||
@@ -770,8 +774,6 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
|
||||
|
||||
case FL_READY:
|
||||
case FL_STATUS:
|
||||
/* We should really make set_vpp() count, rather than doing this */
|
||||
DISABLE_VPP(map);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate);
|
||||
@@ -1013,13 +1015,9 @@ static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_
|
||||
int ret = 0;
|
||||
|
||||
/* ofs: offset within the first chip that the first read should start */
|
||||
|
||||
chipnum = (from >> cfi->chipshift);
|
||||
ofs = from - (chipnum << cfi->chipshift);
|
||||
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
while (len) {
|
||||
unsigned long thislen;
|
||||
|
||||
@@ -1097,16 +1095,11 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
int chipnum;
|
||||
int ret = 0;
|
||||
|
||||
|
||||
/* ofs: offset within the first chip that the first read should start */
|
||||
|
||||
/* 8 secsi bytes per chip */
|
||||
chipnum=from>>3;
|
||||
ofs=from & 7;
|
||||
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
while (len) {
|
||||
unsigned long thislen;
|
||||
|
||||
@@ -1234,6 +1227,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
|
||||
xip_enable(map, chip, adr);
|
||||
op_done:
|
||||
chip->state = FL_READY;
|
||||
DISABLE_VPP(map);
|
||||
put_chip(map, chip, adr);
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
||||
@@ -1251,10 +1245,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
unsigned long ofs, chipstart;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
*retlen = 0;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
chipnum = to >> cfi->chipshift;
|
||||
ofs = to - (chipnum << cfi->chipshift);
|
||||
chipstart = cfi->chips[chipnum].start;
|
||||
@@ -1476,6 +1466,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||
ret = -EIO;
|
||||
op_done:
|
||||
chip->state = FL_READY;
|
||||
DISABLE_VPP(map);
|
||||
put_chip(map, chip, adr);
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
||||
@@ -1493,10 +1484,6 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
int chipnum;
|
||||
unsigned long ofs;
|
||||
|
||||
*retlen = 0;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
chipnum = to >> cfi->chipshift;
|
||||
ofs = to - (chipnum << cfi->chipshift);
|
||||
|
||||
@@ -1562,6 +1549,238 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for the flash chip to become ready to write data
|
||||
*
|
||||
* This is only called during the panic_write() path. When panic_write()
|
||||
* is called, the kernel is in the process of a panic, and will soon be
|
||||
* dead. Therefore we don't take any locks, and attempt to get access
|
||||
* to the chip as soon as possible.
|
||||
*/
|
||||
static int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip,
|
||||
unsigned long adr)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
int retries = 10;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* If the driver thinks the chip is idle, and no toggle bits
|
||||
* are changing, then the chip is actually idle for sure.
|
||||
*/
|
||||
if (chip->state == FL_READY && chip_ready(map, adr))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Try several times to reset the chip and then wait for it
|
||||
* to become idle. The upper limit of a few milliseconds of
|
||||
* delay isn't a big problem: the kernel is dying anyway. It
|
||||
* is more important to save the messages.
|
||||
*/
|
||||
while (retries > 0) {
|
||||
const unsigned long timeo = (HZ / 1000) + 1;
|
||||
|
||||
/* send the reset command */
|
||||
map_write(map, CMD(0xF0), chip->start);
|
||||
|
||||
/* wait for the chip to become ready */
|
||||
for (i = 0; i < jiffies_to_usecs(timeo); i++) {
|
||||
if (chip_ready(map, adr))
|
||||
return 0;
|
||||
|
||||
udelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* the chip never became ready */
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write out one word of data to a single flash chip during a kernel panic
|
||||
*
|
||||
* This is only called during the panic_write() path. When panic_write()
|
||||
* is called, the kernel is in the process of a panic, and will soon be
|
||||
* dead. Therefore we don't take any locks, and attempt to get access
|
||||
* to the chip as soon as possible.
|
||||
*
|
||||
* The implementation of this routine is intentionally similar to
|
||||
* do_write_oneword(), in order to ease code maintenance.
|
||||
*/
|
||||
static int do_panic_write_oneword(struct map_info *map, struct flchip *chip,
|
||||
unsigned long adr, map_word datum)
|
||||
{
|
||||
const unsigned long uWriteTimeout = (HZ / 1000) + 1;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
int retry_cnt = 0;
|
||||
map_word oldd;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
adr += chip->start;
|
||||
|
||||
ret = cfi_amdstd_panic_wait(map, chip, adr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pr_debug("MTD %s(): PANIC WRITE 0x%.8lx(0x%.8lx)\n",
|
||||
__func__, adr, datum.x[0]);
|
||||
|
||||
/*
|
||||
* Check for a NOP for the case when the datum to write is already
|
||||
* present - it saves time and works around buggy chips that corrupt
|
||||
* data at other locations when 0xff is written to a location that
|
||||
* already contains 0xff.
|
||||
*/
|
||||
oldd = map_read(map, adr);
|
||||
if (map_word_equal(map, oldd, datum)) {
|
||||
pr_debug("MTD %s(): NOP\n", __func__);
|
||||
goto op_done;
|
||||
}
|
||||
|
||||
ENABLE_VPP(map);
|
||||
|
||||
retry:
|
||||
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
map_write(map, datum, adr);
|
||||
|
||||
for (i = 0; i < jiffies_to_usecs(uWriteTimeout); i++) {
|
||||
if (chip_ready(map, adr))
|
||||
break;
|
||||
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
if (!chip_good(map, adr, datum)) {
|
||||
/* reset on all failures. */
|
||||
map_write(map, CMD(0xF0), chip->start);
|
||||
/* FIXME - should have reset delay before continuing */
|
||||
|
||||
if (++retry_cnt <= MAX_WORD_RETRIES)
|
||||
goto retry;
|
||||
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
op_done:
|
||||
DISABLE_VPP(map);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write out some data during a kernel panic
|
||||
*
|
||||
* This is used by the mtdoops driver to save the dying messages from a
|
||||
* kernel which has panic'd.
|
||||
*
|
||||
* This routine ignores all of the locking used throughout the rest of the
|
||||
* driver, in order to ensure that the data gets written out no matter what
|
||||
* state this driver (and the flash chip itself) was in when the kernel crashed.
|
||||
*
|
||||
* The implementation of this routine is intentionally similar to
|
||||
* cfi_amdstd_write_words(), in order to ease code maintenance.
|
||||
*/
|
||||
static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long ofs, chipstart;
|
||||
int ret = 0;
|
||||
int chipnum;
|
||||
|
||||
chipnum = to >> cfi->chipshift;
|
||||
ofs = to - (chipnum << cfi->chipshift);
|
||||
chipstart = cfi->chips[chipnum].start;
|
||||
|
||||
/* If it's not bus aligned, do the first byte write */
|
||||
if (ofs & (map_bankwidth(map) - 1)) {
|
||||
unsigned long bus_ofs = ofs & ~(map_bankwidth(map) - 1);
|
||||
int i = ofs - bus_ofs;
|
||||
int n = 0;
|
||||
map_word tmp_buf;
|
||||
|
||||
ret = cfi_amdstd_panic_wait(map, &cfi->chips[chipnum], bus_ofs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Load 'tmp_buf' with old contents of flash */
|
||||
tmp_buf = map_read(map, bus_ofs + chipstart);
|
||||
|
||||
/* Number of bytes to copy from buffer */
|
||||
n = min_t(int, len, map_bankwidth(map) - i);
|
||||
|
||||
tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n);
|
||||
|
||||
ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
|
||||
bus_ofs, tmp_buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ofs += n;
|
||||
buf += n;
|
||||
(*retlen) += n;
|
||||
len -= n;
|
||||
|
||||
if (ofs >> cfi->chipshift) {
|
||||
chipnum++;
|
||||
ofs = 0;
|
||||
if (chipnum == cfi->numchips)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* We are now aligned, write as much as possible */
|
||||
while (len >= map_bankwidth(map)) {
|
||||
map_word datum;
|
||||
|
||||
datum = map_word_load(map, buf);
|
||||
|
||||
ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
|
||||
ofs, datum);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ofs += map_bankwidth(map);
|
||||
buf += map_bankwidth(map);
|
||||
(*retlen) += map_bankwidth(map);
|
||||
len -= map_bankwidth(map);
|
||||
|
||||
if (ofs >> cfi->chipshift) {
|
||||
chipnum++;
|
||||
ofs = 0;
|
||||
if (chipnum == cfi->numchips)
|
||||
return 0;
|
||||
|
||||
chipstart = cfi->chips[chipnum].start;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the trailing bytes if any */
|
||||
if (len & (map_bankwidth(map) - 1)) {
|
||||
map_word tmp_buf;
|
||||
|
||||
ret = cfi_amdstd_panic_wait(map, &cfi->chips[chipnum], ofs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tmp_buf = map_read(map, ofs + chipstart);
|
||||
|
||||
tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
|
||||
|
||||
ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
|
||||
ofs, tmp_buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
(*retlen) += len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle devices with one erase region, that only implement
|
||||
@@ -1649,6 +1868,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
|
||||
|
||||
chip->state = FL_READY;
|
||||
xip_enable(map, chip, adr);
|
||||
DISABLE_VPP(map);
|
||||
put_chip(map, chip, adr);
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
||||
@@ -1739,6 +1959,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
||||
}
|
||||
|
||||
chip->state = FL_READY;
|
||||
DISABLE_VPP(map);
|
||||
put_chip(map, chip, adr);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return ret;
|
||||
|
@@ -228,15 +228,15 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
|
||||
}
|
||||
|
||||
/* Also select the correct geometry setup too */
|
||||
mtd->erase = cfi_staa_erase_varsize;
|
||||
mtd->read = cfi_staa_read;
|
||||
mtd->write = cfi_staa_write_buffers;
|
||||
mtd->writev = cfi_staa_writev;
|
||||
mtd->sync = cfi_staa_sync;
|
||||
mtd->lock = cfi_staa_lock;
|
||||
mtd->unlock = cfi_staa_unlock;
|
||||
mtd->suspend = cfi_staa_suspend;
|
||||
mtd->resume = cfi_staa_resume;
|
||||
mtd->_erase = cfi_staa_erase_varsize;
|
||||
mtd->_read = cfi_staa_read;
|
||||
mtd->_write = cfi_staa_write_buffers;
|
||||
mtd->_writev = cfi_staa_writev;
|
||||
mtd->_sync = cfi_staa_sync;
|
||||
mtd->_lock = cfi_staa_lock;
|
||||
mtd->_unlock = cfi_staa_unlock;
|
||||
mtd->_suspend = cfi_staa_suspend;
|
||||
mtd->_resume = cfi_staa_resume;
|
||||
mtd->flags = MTD_CAP_NORFLASH & ~MTD_BIT_WRITEABLE;
|
||||
mtd->writesize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
|
||||
mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
|
||||
@@ -394,8 +394,6 @@ static int cfi_staa_read (struct mtd_info *mtd, loff_t from, size_t len, size_t
|
||||
chipnum = (from >> cfi->chipshift);
|
||||
ofs = from - (chipnum << cfi->chipshift);
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
while (len) {
|
||||
unsigned long thislen;
|
||||
|
||||
@@ -617,10 +615,6 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to,
|
||||
int chipnum;
|
||||
unsigned long ofs;
|
||||
|
||||
*retlen = 0;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
chipnum = to >> cfi->chipshift;
|
||||
ofs = to - (chipnum << cfi->chipshift);
|
||||
|
||||
@@ -904,12 +898,6 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd,
|
||||
int i, first;
|
||||
struct mtd_erase_region_info *regions = mtd->eraseregions;
|
||||
|
||||
if (instr->addr > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
if ((instr->len + instr->addr) > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check that both start and end of the requested erase are
|
||||
* aligned with the erasesize at the appropriate addresses.
|
||||
*/
|
||||
@@ -1155,9 +1143,6 @@ static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
if (len & (mtd->erasesize -1))
|
||||
return -EINVAL;
|
||||
|
||||
if ((len + ofs) > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
chipnum = ofs >> cfi->chipshift;
|
||||
adr = ofs - (chipnum << cfi->chipshift);
|
||||
|
||||
|
@@ -173,12 +173,6 @@ int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
|
||||
int i, first;
|
||||
struct mtd_erase_region_info *regions = mtd->eraseregions;
|
||||
|
||||
if (ofs > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
if ((len + ofs) > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check that both start and end of the requested erase are
|
||||
* aligned with the erasesize at the appropriate addresses.
|
||||
*/
|
||||
|
@@ -101,7 +101,7 @@ static void fixup_use_fwh_lock(struct mtd_info *mtd)
|
||||
{
|
||||
printk(KERN_NOTICE "using fwh lock/unlock method\n");
|
||||
/* Setup for the chips with the fwh lock method */
|
||||
mtd->lock = fwh_lock_varsize;
|
||||
mtd->unlock = fwh_unlock_varsize;
|
||||
mtd->_lock = fwh_lock_varsize;
|
||||
mtd->_unlock = fwh_unlock_varsize;
|
||||
}
|
||||
#endif /* FWH_LOCK_H */
|
||||
|
@@ -55,10 +55,10 @@ static struct mtd_info *map_absent_probe(struct map_info *map)
|
||||
mtd->name = map->name;
|
||||
mtd->type = MTD_ABSENT;
|
||||
mtd->size = map->size;
|
||||
mtd->erase = map_absent_erase;
|
||||
mtd->read = map_absent_read;
|
||||
mtd->write = map_absent_write;
|
||||
mtd->sync = map_absent_sync;
|
||||
mtd->_erase = map_absent_erase;
|
||||
mtd->_read = map_absent_read;
|
||||
mtd->_write = map_absent_write;
|
||||
mtd->_sync = map_absent_sync;
|
||||
mtd->flags = 0;
|
||||
mtd->erasesize = PAGE_SIZE;
|
||||
mtd->writesize = 1;
|
||||
@@ -70,13 +70,11 @@ static struct mtd_info *map_absent_probe(struct map_info *map)
|
||||
|
||||
static int map_absent_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
*retlen = 0;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int map_absent_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
|
||||
{
|
||||
*retlen = 0;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@@ -64,11 +64,11 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
|
||||
mtd->name = map->name;
|
||||
mtd->type = MTD_RAM;
|
||||
mtd->size = map->size;
|
||||
mtd->erase = mapram_erase;
|
||||
mtd->get_unmapped_area = mapram_unmapped_area;
|
||||
mtd->read = mapram_read;
|
||||
mtd->write = mapram_write;
|
||||
mtd->sync = mapram_nop;
|
||||
mtd->_erase = mapram_erase;
|
||||
mtd->_get_unmapped_area = mapram_unmapped_area;
|
||||
mtd->_read = mapram_read;
|
||||
mtd->_write = mapram_write;
|
||||
mtd->_sync = mapram_nop;
|
||||
mtd->flags = MTD_CAP_RAM;
|
||||
mtd->writesize = 1;
|
||||
|
||||
@@ -122,14 +122,10 @@ static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr)
|
||||
unsigned long i;
|
||||
|
||||
allff = map_word_ff(map);
|
||||
|
||||
for (i=0; i<instr->len; i += map_bankwidth(map))
|
||||
map_write(map, allff, instr->addr + i);
|
||||
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
|
||||
mtd_erase_callback(instr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -41,11 +41,11 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
|
||||
mtd->name = map->name;
|
||||
mtd->type = MTD_ROM;
|
||||
mtd->size = map->size;
|
||||
mtd->get_unmapped_area = maprom_unmapped_area;
|
||||
mtd->read = maprom_read;
|
||||
mtd->write = maprom_write;
|
||||
mtd->sync = maprom_nop;
|
||||
mtd->erase = maprom_erase;
|
||||
mtd->_get_unmapped_area = maprom_unmapped_area;
|
||||
mtd->_read = maprom_read;
|
||||
mtd->_write = maprom_write;
|
||||
mtd->_sync = maprom_nop;
|
||||
mtd->_erase = maprom_erase;
|
||||
mtd->flags = MTD_CAP_ROM;
|
||||
mtd->erasesize = map->size;
|
||||
mtd->writesize = 1;
|
||||
@@ -85,8 +85,7 @@ static void maprom_nop(struct mtd_info *mtd)
|
||||
|
||||
static int maprom_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
|
||||
{
|
||||
printk(KERN_NOTICE "maprom_write called\n");
|
||||
return -EIO;
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
static int maprom_erase (struct mtd_info *mtd, struct erase_info *info)
|
||||
|
@@ -103,6 +103,13 @@ config M25PXX_USE_FAST_READ
|
||||
help
|
||||
This option enables FAST_READ access supported by ST M25Pxx.
|
||||
|
||||
config MTD_SPEAR_SMI
|
||||
tristate "SPEAR MTD NOR Support through SMI controller"
|
||||
depends on PLAT_SPEAR
|
||||
default y
|
||||
help
|
||||
This enable SNOR support on SPEAR platforms using SMI controller
|
||||
|
||||
config MTD_SST25L
|
||||
tristate "Support SST25L (non JEDEC) SPI Flash chips"
|
||||
depends on SPI_MASTER
|
||||
|
@@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_LART) += lart.o
|
||||
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
|
||||
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
|
||||
obj-$(CONFIG_MTD_M25P80) += m25p80.o
|
||||
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
|
||||
obj-$(CONFIG_MTD_SST25L) += sst25l.o
|
||||
|
||||
CFLAGS_docg3.o += -I$(src)
|
@@ -104,14 +104,6 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
int offset = from & (PAGE_SIZE-1);
|
||||
int cpylen;
|
||||
|
||||
if (from > mtd->size)
|
||||
return -EINVAL;
|
||||
if (from + len > mtd->size)
|
||||
len = mtd->size - from;
|
||||
|
||||
if (retlen)
|
||||
*retlen = 0;
|
||||
|
||||
while (len) {
|
||||
if ((offset + len) > PAGE_SIZE)
|
||||
cpylen = PAGE_SIZE - offset; // multiple pages
|
||||
@@ -148,8 +140,6 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
|
||||
int offset = to & ~PAGE_MASK; // page offset
|
||||
int cpylen;
|
||||
|
||||
if (retlen)
|
||||
*retlen = 0;
|
||||
while (len) {
|
||||
if ((offset+len) > PAGE_SIZE)
|
||||
cpylen = PAGE_SIZE - offset; // multiple pages
|
||||
@@ -188,13 +178,6 @@ static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
struct block2mtd_dev *dev = mtd->priv;
|
||||
int err;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
if (to >= mtd->size)
|
||||
return -ENOSPC;
|
||||
if (to + len > mtd->size)
|
||||
len = mtd->size - to;
|
||||
|
||||
mutex_lock(&dev->write_mutex);
|
||||
err = _block2mtd_write(dev, buf, to, len, retlen);
|
||||
mutex_unlock(&dev->write_mutex);
|
||||
@@ -283,13 +266,14 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
||||
dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
|
||||
dev->mtd.erasesize = erase_size;
|
||||
dev->mtd.writesize = 1;
|
||||
dev->mtd.writebufsize = PAGE_SIZE;
|
||||
dev->mtd.type = MTD_RAM;
|
||||
dev->mtd.flags = MTD_CAP_RAM;
|
||||
dev->mtd.erase = block2mtd_erase;
|
||||
dev->mtd.write = block2mtd_write;
|
||||
dev->mtd.writev = mtd_writev;
|
||||
dev->mtd.sync = block2mtd_sync;
|
||||
dev->mtd.read = block2mtd_read;
|
||||
dev->mtd._erase = block2mtd_erase;
|
||||
dev->mtd._write = block2mtd_write;
|
||||
dev->mtd._writev = mtd_writev;
|
||||
dev->mtd._sync = block2mtd_sync;
|
||||
dev->mtd._read = block2mtd_read;
|
||||
dev->mtd.priv = dev;
|
||||
dev->mtd.owner = THIS_MODULE;
|
||||
|
||||
|
@@ -562,14 +562,15 @@ void DoC2k_init(struct mtd_info *mtd)
|
||||
|
||||
mtd->type = MTD_NANDFLASH;
|
||||
mtd->flags = MTD_CAP_NANDFLASH;
|
||||
mtd->writesize = 512;
|
||||
mtd->writebufsize = mtd->writesize = 512;
|
||||
mtd->oobsize = 16;
|
||||
mtd->ecc_strength = 2;
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->erase = doc_erase;
|
||||
mtd->read = doc_read;
|
||||
mtd->write = doc_write;
|
||||
mtd->read_oob = doc_read_oob;
|
||||
mtd->write_oob = doc_write_oob;
|
||||
mtd->_erase = doc_erase;
|
||||
mtd->_read = doc_read;
|
||||
mtd->_write = doc_write;
|
||||
mtd->_read_oob = doc_read_oob;
|
||||
mtd->_write_oob = doc_write_oob;
|
||||
this->curfloor = -1;
|
||||
this->curchip = -1;
|
||||
mutex_init(&this->lock);
|
||||
@@ -602,13 +603,7 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
int i, len256 = 0, ret=0;
|
||||
size_t left = len;
|
||||
|
||||
/* Don't allow read past end of device */
|
||||
if (from >= this->totlen)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&this->lock);
|
||||
|
||||
*retlen = 0;
|
||||
while (left) {
|
||||
len = left;
|
||||
|
||||
@@ -748,13 +743,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t left = len;
|
||||
int status;
|
||||
|
||||
/* Don't allow write past end of device */
|
||||
if (to >= this->totlen)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&this->lock);
|
||||
|
||||
*retlen = 0;
|
||||
while (left) {
|
||||
len = left;
|
||||
|
||||
|
@@ -346,14 +346,15 @@ void DoCMil_init(struct mtd_info *mtd)
|
||||
|
||||
/* FIXME: erase size is not always 8KiB */
|
||||
mtd->erasesize = 0x2000;
|
||||
mtd->writesize = 512;
|
||||
mtd->writebufsize = mtd->writesize = 512;
|
||||
mtd->oobsize = 16;
|
||||
mtd->ecc_strength = 2;
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->erase = doc_erase;
|
||||
mtd->read = doc_read;
|
||||
mtd->write = doc_write;
|
||||
mtd->read_oob = doc_read_oob;
|
||||
mtd->write_oob = doc_write_oob;
|
||||
mtd->_erase = doc_erase;
|
||||
mtd->_read = doc_read;
|
||||
mtd->_write = doc_write;
|
||||
mtd->_read_oob = doc_read_oob;
|
||||
mtd->_write_oob = doc_write_oob;
|
||||
this->curfloor = -1;
|
||||
this->curchip = -1;
|
||||
|
||||
@@ -383,10 +384,6 @@ static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
void __iomem *docptr = this->virtadr;
|
||||
struct Nand *mychip = &this->chips[from >> (this->chipshift)];
|
||||
|
||||
/* Don't allow read past end of device */
|
||||
if (from >= this->totlen)
|
||||
return -EINVAL;
|
||||
|
||||
/* Don't allow a single read to cross a 512-byte block boundary */
|
||||
if (from + len > ((from | 0x1ff) + 1))
|
||||
len = ((from | 0x1ff) + 1) - from;
|
||||
@@ -494,10 +491,6 @@ static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
|
||||
void __iomem *docptr = this->virtadr;
|
||||
struct Nand *mychip = &this->chips[to >> (this->chipshift)];
|
||||
|
||||
/* Don't allow write past end of device */
|
||||
if (to >= this->totlen)
|
||||
return -EINVAL;
|
||||
|
||||
#if 0
|
||||
/* Don't allow a single write to cross a 512-byte block boundary */
|
||||
if (to + len > ( (to | 0x1ff) + 1))
|
||||
@@ -599,7 +592,6 @@ static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
|
||||
printk("Error programming flash\n");
|
||||
/* Error in programming
|
||||
FIXME: implement Bad Block Replacement (in nftl.c ??) */
|
||||
*retlen = 0;
|
||||
ret = -EIO;
|
||||
}
|
||||
dummy = ReadDOC(docptr, LastDataRead);
|
||||
|
@@ -467,14 +467,15 @@ void DoCMilPlus_init(struct mtd_info *mtd)
|
||||
|
||||
mtd->type = MTD_NANDFLASH;
|
||||
mtd->flags = MTD_CAP_NANDFLASH;
|
||||
mtd->writesize = 512;
|
||||
mtd->writebufsize = mtd->writesize = 512;
|
||||
mtd->oobsize = 16;
|
||||
mtd->ecc_strength = 2;
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->erase = doc_erase;
|
||||
mtd->read = doc_read;
|
||||
mtd->write = doc_write;
|
||||
mtd->read_oob = doc_read_oob;
|
||||
mtd->write_oob = doc_write_oob;
|
||||
mtd->_erase = doc_erase;
|
||||
mtd->_read = doc_read;
|
||||
mtd->_write = doc_write;
|
||||
mtd->_read_oob = doc_read_oob;
|
||||
mtd->_write_oob = doc_write_oob;
|
||||
this->curfloor = -1;
|
||||
this->curchip = -1;
|
||||
|
||||
@@ -581,10 +582,6 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
void __iomem * docptr = this->virtadr;
|
||||
struct Nand *mychip = &this->chips[from >> (this->chipshift)];
|
||||
|
||||
/* Don't allow read past end of device */
|
||||
if (from >= this->totlen)
|
||||
return -EINVAL;
|
||||
|
||||
/* Don't allow a single read to cross a 512-byte block boundary */
|
||||
if (from + len > ((from | 0x1ff) + 1))
|
||||
len = ((from | 0x1ff) + 1) - from;
|
||||
@@ -700,10 +697,6 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
void __iomem * docptr = this->virtadr;
|
||||
struct Nand *mychip = &this->chips[to >> (this->chipshift)];
|
||||
|
||||
/* Don't allow write past end of device */
|
||||
if (to >= this->totlen)
|
||||
return -EINVAL;
|
||||
|
||||
/* Don't allow writes which aren't exactly one block (512 bytes) */
|
||||
if ((to & 0x1ff) || (len != 0x200))
|
||||
return -EINVAL;
|
||||
@@ -800,7 +793,6 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to);
|
||||
/* Error in programming
|
||||
FIXME: implement Bad Block Replacement (in nftl.c ??) */
|
||||
*retlen = 0;
|
||||
ret = -EIO;
|
||||
}
|
||||
dummy = ReadDOC(docptr, Mplus_LastDataRead);
|
||||
|
@@ -80,14 +80,9 @@ static struct nand_ecclayout docg3_oobinfo = {
|
||||
.oobavail = 8,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct docg3_bch - BCH engine
|
||||
*/
|
||||
static struct bch_control *docg3_bch;
|
||||
|
||||
static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
|
||||
{
|
||||
u8 val = readb(docg3->base + reg);
|
||||
u8 val = readb(docg3->cascade->base + reg);
|
||||
|
||||
trace_docg3_io(0, 8, reg, (int)val);
|
||||
return val;
|
||||
@@ -95,7 +90,7 @@ static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
|
||||
|
||||
static inline u16 doc_readw(struct docg3 *docg3, u16 reg)
|
||||
{
|
||||
u16 val = readw(docg3->base + reg);
|
||||
u16 val = readw(docg3->cascade->base + reg);
|
||||
|
||||
trace_docg3_io(0, 16, reg, (int)val);
|
||||
return val;
|
||||
@@ -103,13 +98,13 @@ static inline u16 doc_readw(struct docg3 *docg3, u16 reg)
|
||||
|
||||
static inline void doc_writeb(struct docg3 *docg3, u8 val, u16 reg)
|
||||
{
|
||||
writeb(val, docg3->base + reg);
|
||||
writeb(val, docg3->cascade->base + reg);
|
||||
trace_docg3_io(1, 8, reg, val);
|
||||
}
|
||||
|
||||
static inline void doc_writew(struct docg3 *docg3, u16 val, u16 reg)
|
||||
{
|
||||
writew(val, docg3->base + reg);
|
||||
writew(val, docg3->cascade->base + reg);
|
||||
trace_docg3_io(1, 16, reg, val);
|
||||
}
|
||||
|
||||
@@ -643,7 +638,8 @@ static int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc)
|
||||
|
||||
for (i = 0; i < DOC_ECC_BCH_SIZE; i++)
|
||||
ecc[i] = bitrev8(hwecc[i]);
|
||||
numerrs = decode_bch(docg3_bch, NULL, DOC_ECC_BCH_COVERED_BYTES,
|
||||
numerrs = decode_bch(docg3->cascade->bch, NULL,
|
||||
DOC_ECC_BCH_COVERED_BYTES,
|
||||
NULL, ecc, NULL, errorpos);
|
||||
BUG_ON(numerrs == -EINVAL);
|
||||
if (numerrs < 0)
|
||||
@@ -734,7 +730,7 @@ err:
|
||||
* doc_read_page_getbytes - Reads bytes from a prepared page
|
||||
* @docg3: the device
|
||||
* @len: the number of bytes to be read (must be a multiple of 4)
|
||||
* @buf: the buffer to be filled in
|
||||
* @buf: the buffer to be filled in (or NULL is forget bytes)
|
||||
* @first: 1 if first time read, DOC_READADDRESS should be set
|
||||
*
|
||||
*/
|
||||
@@ -849,7 +845,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct docg3 *docg3 = mtd->priv;
|
||||
int block0, block1, page, ret, ofs = 0;
|
||||
int block0, block1, page, ret, skip, ofs = 0;
|
||||
u8 *oobbuf = ops->oobbuf;
|
||||
u8 *buf = ops->datbuf;
|
||||
size_t len, ooblen, nbdata, nboob;
|
||||
@@ -869,34 +865,36 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
|
||||
doc_dbg("doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n",
|
||||
from, ops->mode, buf, len, oobbuf, ooblen);
|
||||
if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % DOC_LAYOUT_OOB_SIZE) ||
|
||||
(from % DOC_LAYOUT_PAGE_SIZE))
|
||||
if (ooblen % DOC_LAYOUT_OOB_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
ret = -EINVAL;
|
||||
calc_block_sector(from + len, &block0, &block1, &page, &ofs,
|
||||
docg3->reliable);
|
||||
if (block1 > docg3->max_block)
|
||||
goto err;
|
||||
if (from + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
ops->oobretlen = 0;
|
||||
ops->retlen = 0;
|
||||
ret = 0;
|
||||
skip = from % DOC_LAYOUT_PAGE_SIZE;
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
while (!ret && (len > 0 || ooblen > 0)) {
|
||||
calc_block_sector(from, &block0, &block1, &page, &ofs,
|
||||
calc_block_sector(from - skip, &block0, &block1, &page, &ofs,
|
||||
docg3->reliable);
|
||||
nbdata = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE);
|
||||
nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip);
|
||||
nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE);
|
||||
ret = doc_read_page_prepare(docg3, block0, block1, page, ofs);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
goto out;
|
||||
ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES);
|
||||
if (ret < 0)
|
||||
goto err_in_read;
|
||||
ret = doc_read_page_getbytes(docg3, nbdata, buf, 1);
|
||||
ret = doc_read_page_getbytes(docg3, skip, NULL, 1);
|
||||
if (ret < skip)
|
||||
goto err_in_read;
|
||||
ret = doc_read_page_getbytes(docg3, nbdata, buf, 0);
|
||||
if (ret < nbdata)
|
||||
goto err_in_read;
|
||||
doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE - nbdata,
|
||||
doc_read_page_getbytes(docg3,
|
||||
DOC_LAYOUT_PAGE_SIZE - nbdata - skip,
|
||||
NULL, 0);
|
||||
ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0);
|
||||
if (ret < nboob)
|
||||
@@ -950,13 +948,15 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
len -= nbdata;
|
||||
ooblen -= nboob;
|
||||
from += DOC_LAYOUT_PAGE_SIZE;
|
||||
skip = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&docg3->cascade->lock);
|
||||
return ret;
|
||||
err_in_read:
|
||||
doc_read_page_finish(docg3);
|
||||
err:
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1114,10 +1114,10 @@ static int doc_get_op_status(struct docg3 *docg3)
|
||||
*/
|
||||
static int doc_write_erase_wait_status(struct docg3 *docg3)
|
||||
{
|
||||
int status, ret = 0;
|
||||
int i, status, ret = 0;
|
||||
|
||||
if (!doc_is_ready(docg3))
|
||||
usleep_range(3000, 3000);
|
||||
for (i = 0; !doc_is_ready(docg3) && i < 5; i++)
|
||||
msleep(20);
|
||||
if (!doc_is_ready(docg3)) {
|
||||
doc_dbg("Timeout reached and the chip is still not ready\n");
|
||||
ret = -EAGAIN;
|
||||
@@ -1196,18 +1196,19 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
|
||||
int block0, block1, page, ret, ofs = 0;
|
||||
|
||||
doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len);
|
||||
doc_set_device_id(docg3, docg3->device_id);
|
||||
|
||||
info->state = MTD_ERASE_PENDING;
|
||||
calc_block_sector(info->addr + info->len, &block0, &block1, &page,
|
||||
&ofs, docg3->reliable);
|
||||
ret = -EINVAL;
|
||||
if (block1 > docg3->max_block || page || ofs)
|
||||
if (info->addr + info->len > mtd->size || page || ofs)
|
||||
goto reset_err;
|
||||
|
||||
ret = 0;
|
||||
calc_block_sector(info->addr, &block0, &block1, &page, &ofs,
|
||||
docg3->reliable);
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
doc_set_device_id(docg3, docg3->device_id);
|
||||
doc_set_reliable_mode(docg3);
|
||||
for (len = info->len; !ret && len > 0; len -= mtd->erasesize) {
|
||||
info->state = MTD_ERASING;
|
||||
@@ -1215,6 +1216,7 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
|
||||
block0 += 2;
|
||||
block1 += 2;
|
||||
}
|
||||
mutex_unlock(&docg3->cascade->lock);
|
||||
|
||||
if (ret)
|
||||
goto reset_err;
|
||||
@@ -1401,7 +1403,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct docg3 *docg3 = mtd->priv;
|
||||
int block0, block1, page, ret, pofs = 0, autoecc, oobdelta;
|
||||
int ret, autoecc, oobdelta;
|
||||
u8 *oobbuf = ops->oobbuf;
|
||||
u8 *buf = ops->datbuf;
|
||||
size_t len, ooblen;
|
||||
@@ -1438,12 +1440,8 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
if (len && ooblen &&
|
||||
(len / DOC_LAYOUT_PAGE_SIZE) != (ooblen / oobdelta))
|
||||
return -EINVAL;
|
||||
|
||||
ret = -EINVAL;
|
||||
calc_block_sector(ofs + len, &block0, &block1, &page, &pofs,
|
||||
docg3->reliable);
|
||||
if (block1 > docg3->max_block)
|
||||
goto err;
|
||||
if (ofs + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
ops->oobretlen = 0;
|
||||
ops->retlen = 0;
|
||||
@@ -1457,6 +1455,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
if (autoecc < 0)
|
||||
return autoecc;
|
||||
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
while (!ret && len > 0) {
|
||||
memset(oob, 0, sizeof(oob));
|
||||
if (ofs == docg3->oob_write_ofs)
|
||||
@@ -1477,8 +1476,9 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
}
|
||||
ops->retlen += DOC_LAYOUT_PAGE_SIZE;
|
||||
}
|
||||
err:
|
||||
|
||||
doc_set_device_id(docg3, 0);
|
||||
mutex_unlock(&docg3->cascade->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1535,9 +1535,11 @@ static ssize_t dps0_is_key_locked(struct device *dev,
|
||||
struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
|
||||
int dps0;
|
||||
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
doc_set_device_id(docg3, docg3->device_id);
|
||||
dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS);
|
||||
doc_set_device_id(docg3, 0);
|
||||
mutex_unlock(&docg3->cascade->lock);
|
||||
|
||||
return sprintf(buf, "%d\n", !(dps0 & DOC_DPS_KEY_OK));
|
||||
}
|
||||
@@ -1548,9 +1550,11 @@ static ssize_t dps1_is_key_locked(struct device *dev,
|
||||
struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
|
||||
int dps1;
|
||||
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
doc_set_device_id(docg3, docg3->device_id);
|
||||
dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS);
|
||||
doc_set_device_id(docg3, 0);
|
||||
mutex_unlock(&docg3->cascade->lock);
|
||||
|
||||
return sprintf(buf, "%d\n", !(dps1 & DOC_DPS_KEY_OK));
|
||||
}
|
||||
@@ -1565,10 +1569,12 @@ static ssize_t dps0_insert_key(struct device *dev,
|
||||
if (count != DOC_LAYOUT_DPS_KEY_LENGTH)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
doc_set_device_id(docg3, docg3->device_id);
|
||||
for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++)
|
||||
doc_writeb(docg3, buf[i], DOC_DPS0_KEY);
|
||||
doc_set_device_id(docg3, 0);
|
||||
mutex_unlock(&docg3->cascade->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -1582,10 +1588,12 @@ static ssize_t dps1_insert_key(struct device *dev,
|
||||
if (count != DOC_LAYOUT_DPS_KEY_LENGTH)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
doc_set_device_id(docg3, docg3->device_id);
|
||||
for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++)
|
||||
doc_writeb(docg3, buf[i], DOC_DPS1_KEY);
|
||||
doc_set_device_id(docg3, 0);
|
||||
mutex_unlock(&docg3->cascade->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -1601,13 +1609,13 @@ static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = {
|
||||
};
|
||||
|
||||
static int doc_register_sysfs(struct platform_device *pdev,
|
||||
struct mtd_info **floors)
|
||||
struct docg3_cascade *cascade)
|
||||
{
|
||||
int ret = 0, floor, i = 0;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS && floors[floor];
|
||||
floor++)
|
||||
for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS &&
|
||||
cascade->floors[floor]; floor++)
|
||||
for (i = 0; !ret && i < 4; i++)
|
||||
ret = device_create_file(dev, &doc_sys_attrs[floor][i]);
|
||||
if (!ret)
|
||||
@@ -1621,12 +1629,12 @@ static int doc_register_sysfs(struct platform_device *pdev,
|
||||
}
|
||||
|
||||
static void doc_unregister_sysfs(struct platform_device *pdev,
|
||||
struct mtd_info **floors)
|
||||
struct docg3_cascade *cascade)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int floor, i;
|
||||
|
||||
for (floor = 0; floor < DOC_MAX_NBFLOORS && floors[floor];
|
||||
for (floor = 0; floor < DOC_MAX_NBFLOORS && cascade->floors[floor];
|
||||
floor++)
|
||||
for (i = 0; i < 4; i++)
|
||||
device_remove_file(dev, &doc_sys_attrs[floor][i]);
|
||||
@@ -1640,7 +1648,11 @@ static int dbg_flashctrl_show(struct seq_file *s, void *p)
|
||||
struct docg3 *docg3 = (struct docg3 *)s->private;
|
||||
|
||||
int pos = 0;
|
||||
u8 fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
|
||||
u8 fctrl;
|
||||
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
|
||||
mutex_unlock(&docg3->cascade->lock);
|
||||
|
||||
pos += seq_printf(s,
|
||||
"FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n",
|
||||
@@ -1658,9 +1670,12 @@ static int dbg_asicmode_show(struct seq_file *s, void *p)
|
||||
{
|
||||
struct docg3 *docg3 = (struct docg3 *)s->private;
|
||||
|
||||
int pos = 0;
|
||||
int pctrl = doc_register_readb(docg3, DOC_ASICMODE);
|
||||
int mode = pctrl & 0x03;
|
||||
int pos = 0, pctrl, mode;
|
||||
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
pctrl = doc_register_readb(docg3, DOC_ASICMODE);
|
||||
mode = pctrl & 0x03;
|
||||
mutex_unlock(&docg3->cascade->lock);
|
||||
|
||||
pos += seq_printf(s,
|
||||
"%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (",
|
||||
@@ -1692,7 +1707,11 @@ static int dbg_device_id_show(struct seq_file *s, void *p)
|
||||
{
|
||||
struct docg3 *docg3 = (struct docg3 *)s->private;
|
||||
int pos = 0;
|
||||
int id = doc_register_readb(docg3, DOC_DEVICESELECT);
|
||||
int id;
|
||||
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
id = doc_register_readb(docg3, DOC_DEVICESELECT);
|
||||
mutex_unlock(&docg3->cascade->lock);
|
||||
|
||||
pos += seq_printf(s, "DeviceId = %d\n", id);
|
||||
return pos;
|
||||
@@ -1705,6 +1724,7 @@ static int dbg_protection_show(struct seq_file *s, void *p)
|
||||
int pos = 0;
|
||||
int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high;
|
||||
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
protect = doc_register_readb(docg3, DOC_PROTECTION);
|
||||
dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS);
|
||||
dps0_low = doc_register_readw(docg3, DOC_DPS0_ADDRLOW);
|
||||
@@ -1712,6 +1732,7 @@ static int dbg_protection_show(struct seq_file *s, void *p)
|
||||
dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS);
|
||||
dps1_low = doc_register_readw(docg3, DOC_DPS1_ADDRLOW);
|
||||
dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH);
|
||||
mutex_unlock(&docg3->cascade->lock);
|
||||
|
||||
pos += seq_printf(s, "Protection = 0x%02x (",
|
||||
protect);
|
||||
@@ -1804,7 +1825,7 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
|
||||
|
||||
switch (chip_id) {
|
||||
case DOC_CHIPID_G3:
|
||||
mtd->name = kasprintf(GFP_KERNEL, "DiskOnChip G3 floor %d",
|
||||
mtd->name = kasprintf(GFP_KERNEL, "docg3.%d",
|
||||
docg3->device_id);
|
||||
docg3->max_block = 2047;
|
||||
break;
|
||||
@@ -1817,16 +1838,17 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
|
||||
mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES;
|
||||
if (docg3->reliable == 2)
|
||||
mtd->erasesize /= 2;
|
||||
mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
|
||||
mtd->writebufsize = mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
|
||||
mtd->oobsize = DOC_LAYOUT_OOB_SIZE;
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->erase = doc_erase;
|
||||
mtd->read = doc_read;
|
||||
mtd->write = doc_write;
|
||||
mtd->read_oob = doc_read_oob;
|
||||
mtd->write_oob = doc_write_oob;
|
||||
mtd->block_isbad = doc_block_isbad;
|
||||
mtd->_erase = doc_erase;
|
||||
mtd->_read = doc_read;
|
||||
mtd->_write = doc_write;
|
||||
mtd->_read_oob = doc_read_oob;
|
||||
mtd->_write_oob = doc_write_oob;
|
||||
mtd->_block_isbad = doc_block_isbad;
|
||||
mtd->ecclayout = &docg3_oobinfo;
|
||||
mtd->ecc_strength = DOC_ECC_BCH_T;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1834,6 +1856,7 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
|
||||
* @base: the io space where the device is probed
|
||||
* @floor: the floor of the probed device
|
||||
* @dev: the device
|
||||
* @cascade: the cascade of chips this devices will belong to
|
||||
*
|
||||
* Checks whether a device at the specified IO range, and floor is available.
|
||||
*
|
||||
@@ -1841,8 +1864,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
|
||||
* if a memory allocation failed. If floor 0 is checked, a reset of the ASIC is
|
||||
* launched.
|
||||
*/
|
||||
static struct mtd_info *doc_probe_device(void __iomem *base, int floor,
|
||||
struct device *dev)
|
||||
static struct mtd_info * __init
|
||||
doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
|
||||
{
|
||||
int ret, bbt_nbpages;
|
||||
u16 chip_id, chip_id_inv;
|
||||
@@ -1865,7 +1888,7 @@ static struct mtd_info *doc_probe_device(void __iomem *base, int floor,
|
||||
|
||||
docg3->dev = dev;
|
||||
docg3->device_id = floor;
|
||||
docg3->base = base;
|
||||
docg3->cascade = cascade;
|
||||
doc_set_device_id(docg3, docg3->device_id);
|
||||
if (!floor)
|
||||
doc_set_asic_mode(docg3, DOC_ASICMODE_RESET);
|
||||
@@ -1882,7 +1905,7 @@ static struct mtd_info *doc_probe_device(void __iomem *base, int floor,
|
||||
switch (chip_id) {
|
||||
case DOC_CHIPID_G3:
|
||||
doc_info("Found a G3 DiskOnChip at addr %p, floor %d\n",
|
||||
base, floor);
|
||||
docg3->cascade->base, floor);
|
||||
break;
|
||||
default:
|
||||
doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id);
|
||||
@@ -1927,10 +1950,12 @@ static void doc_release_device(struct mtd_info *mtd)
|
||||
static int docg3_resume(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct docg3_cascade *cascade;
|
||||
struct mtd_info **docg3_floors, *mtd;
|
||||
struct docg3 *docg3;
|
||||
|
||||
docg3_floors = platform_get_drvdata(pdev);
|
||||
cascade = platform_get_drvdata(pdev);
|
||||
docg3_floors = cascade->floors;
|
||||
mtd = docg3_floors[0];
|
||||
docg3 = mtd->priv;
|
||||
|
||||
@@ -1952,11 +1977,13 @@ static int docg3_resume(struct platform_device *pdev)
|
||||
static int docg3_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
int floor, i;
|
||||
struct docg3_cascade *cascade;
|
||||
struct mtd_info **docg3_floors, *mtd;
|
||||
struct docg3 *docg3;
|
||||
u8 ctrl, pwr_down;
|
||||
|
||||
docg3_floors = platform_get_drvdata(pdev);
|
||||
cascade = platform_get_drvdata(pdev);
|
||||
docg3_floors = cascade->floors;
|
||||
for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) {
|
||||
mtd = docg3_floors[floor];
|
||||
if (!mtd)
|
||||
@@ -2006,7 +2033,7 @@ static int __init docg3_probe(struct platform_device *pdev)
|
||||
struct resource *ress;
|
||||
void __iomem *base;
|
||||
int ret, floor, found = 0;
|
||||
struct mtd_info **docg3_floors;
|
||||
struct docg3_cascade *cascade;
|
||||
|
||||
ret = -ENXIO;
|
||||
ress = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
@@ -2017,17 +2044,19 @@ static int __init docg3_probe(struct platform_device *pdev)
|
||||
base = ioremap(ress->start, DOC_IOSPACE_SIZE);
|
||||
|
||||
ret = -ENOMEM;
|
||||
docg3_floors = kzalloc(sizeof(*docg3_floors) * DOC_MAX_NBFLOORS,
|
||||
GFP_KERNEL);
|
||||
if (!docg3_floors)
|
||||
cascade = kzalloc(sizeof(*cascade) * DOC_MAX_NBFLOORS,
|
||||
GFP_KERNEL);
|
||||
if (!cascade)
|
||||
goto nomem1;
|
||||
docg3_bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
|
||||
cascade->base = base;
|
||||
mutex_init(&cascade->lock);
|
||||
cascade->bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
|
||||
DOC_ECC_BCH_PRIMPOLY);
|
||||
if (!docg3_bch)
|
||||
if (!cascade->bch)
|
||||
goto nomem2;
|
||||
|
||||
for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) {
|
||||
mtd = doc_probe_device(base, floor, dev);
|
||||
mtd = doc_probe_device(cascade, floor, dev);
|
||||
if (IS_ERR(mtd)) {
|
||||
ret = PTR_ERR(mtd);
|
||||
goto err_probe;
|
||||
@@ -2038,7 +2067,7 @@ static int __init docg3_probe(struct platform_device *pdev)
|
||||
else
|
||||
continue;
|
||||
}
|
||||
docg3_floors[floor] = mtd;
|
||||
cascade->floors[floor] = mtd;
|
||||
ret = mtd_device_parse_register(mtd, part_probes, NULL, NULL,
|
||||
0);
|
||||
if (ret)
|
||||
@@ -2046,26 +2075,26 @@ static int __init docg3_probe(struct platform_device *pdev)
|
||||
found++;
|
||||
}
|
||||
|
||||
ret = doc_register_sysfs(pdev, docg3_floors);
|
||||
ret = doc_register_sysfs(pdev, cascade);
|
||||
if (ret)
|
||||
goto err_probe;
|
||||
if (!found)
|
||||
goto notfound;
|
||||
|
||||
platform_set_drvdata(pdev, docg3_floors);
|
||||
doc_dbg_register(docg3_floors[0]->priv);
|
||||
platform_set_drvdata(pdev, cascade);
|
||||
doc_dbg_register(cascade->floors[0]->priv);
|
||||
return 0;
|
||||
|
||||
notfound:
|
||||
ret = -ENODEV;
|
||||
dev_info(dev, "No supported DiskOnChip found\n");
|
||||
err_probe:
|
||||
free_bch(docg3_bch);
|
||||
kfree(cascade->bch);
|
||||
for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
|
||||
if (docg3_floors[floor])
|
||||
doc_release_device(docg3_floors[floor]);
|
||||
if (cascade->floors[floor])
|
||||
doc_release_device(cascade->floors[floor]);
|
||||
nomem2:
|
||||
kfree(docg3_floors);
|
||||
kfree(cascade);
|
||||
nomem1:
|
||||
iounmap(base);
|
||||
noress:
|
||||
@@ -2080,19 +2109,19 @@ noress:
|
||||
*/
|
||||
static int __exit docg3_release(struct platform_device *pdev)
|
||||
{
|
||||
struct mtd_info **docg3_floors = platform_get_drvdata(pdev);
|
||||
struct docg3 *docg3 = docg3_floors[0]->priv;
|
||||
void __iomem *base = docg3->base;
|
||||
struct docg3_cascade *cascade = platform_get_drvdata(pdev);
|
||||
struct docg3 *docg3 = cascade->floors[0]->priv;
|
||||
void __iomem *base = cascade->base;
|
||||
int floor;
|
||||
|
||||
doc_unregister_sysfs(pdev, docg3_floors);
|
||||
doc_unregister_sysfs(pdev, cascade);
|
||||
doc_dbg_unregister(docg3);
|
||||
for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
|
||||
if (docg3_floors[floor])
|
||||
doc_release_device(docg3_floors[floor]);
|
||||
if (cascade->floors[floor])
|
||||
doc_release_device(cascade->floors[floor]);
|
||||
|
||||
kfree(docg3_floors);
|
||||
free_bch(docg3_bch);
|
||||
free_bch(docg3->cascade->bch);
|
||||
kfree(cascade);
|
||||
iounmap(base);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -22,6 +22,8 @@
|
||||
#ifndef _MTD_DOCG3_H
|
||||
#define _MTD_DOCG3_H
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
||||
/*
|
||||
* Flash memory areas :
|
||||
* - 0x0000 .. 0x07ff : IPL
|
||||
@@ -266,10 +268,24 @@
|
||||
*/
|
||||
#define DOC_LAYOUT_DPS_KEY_LENGTH 8
|
||||
|
||||
/**
|
||||
* struct docg3_cascade - Cascade of 1 to 4 docg3 chips
|
||||
* @floors: floors (ie. one physical docg3 chip is one floor)
|
||||
* @base: IO space to access all chips in the cascade
|
||||
* @bch: the BCH correcting control structure
|
||||
* @lock: lock to protect docg3 IO space from concurrent accesses
|
||||
*/
|
||||
struct docg3_cascade {
|
||||
struct mtd_info *floors[DOC_MAX_NBFLOORS];
|
||||
void __iomem *base;
|
||||
struct bch_control *bch;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct docg3 - DiskOnChip driver private data
|
||||
* @dev: the device currently under control
|
||||
* @base: mapped IO space
|
||||
* @cascade: the cascade this device belongs to
|
||||
* @device_id: number of the cascaded DoCG3 device (0, 1, 2 or 3)
|
||||
* @if_cfg: if true, reads are on 16bits, else reads are on 8bits
|
||||
|
||||
@@ -287,7 +303,7 @@
|
||||
*/
|
||||
struct docg3 {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct docg3_cascade *cascade;
|
||||
unsigned int device_id:4;
|
||||
unsigned int if_cfg:1;
|
||||
unsigned int reliable:2;
|
||||
|
@@ -367,9 +367,6 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
|
||||
printk (KERN_DEBUG "%s(addr = 0x%.8x, len = %d)\n", __func__, instr->addr, instr->len);
|
||||
#endif
|
||||
|
||||
/* sanity checks */
|
||||
if (instr->addr + instr->len > mtd->size) return (-EINVAL);
|
||||
|
||||
/*
|
||||
* check that both start and end of the requested erase are
|
||||
* aligned with the erasesize at the appropriate addresses.
|
||||
@@ -440,10 +437,6 @@ static int flash_read (struct mtd_info *mtd,loff_t from,size_t len,size_t *retle
|
||||
printk (KERN_DEBUG "%s(from = 0x%.8x, len = %d)\n", __func__, (__u32)from, len);
|
||||
#endif
|
||||
|
||||
/* sanity checks */
|
||||
if (!len) return (0);
|
||||
if (from + len > mtd->size) return (-EINVAL);
|
||||
|
||||
/* we always read len bytes */
|
||||
*retlen = len;
|
||||
|
||||
@@ -522,11 +515,8 @@ static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen
|
||||
printk (KERN_DEBUG "%s(to = 0x%.8x, len = %d)\n", __func__, (__u32)to, len);
|
||||
#endif
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
/* sanity checks */
|
||||
if (!len) return (0);
|
||||
if (to + len > mtd->size) return (-EINVAL);
|
||||
|
||||
/* first, we write a 0xFF.... padded byte until we reach a dword boundary */
|
||||
if (to & (BUSWIDTH - 1))
|
||||
@@ -630,14 +620,15 @@ static int __init lart_flash_init (void)
|
||||
mtd.name = module_name;
|
||||
mtd.type = MTD_NORFLASH;
|
||||
mtd.writesize = 1;
|
||||
mtd.writebufsize = 4;
|
||||
mtd.flags = MTD_CAP_NORFLASH;
|
||||
mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN;
|
||||
mtd.erasesize = FLASH_BLOCKSIZE_MAIN;
|
||||
mtd.numeraseregions = ARRAY_SIZE(erase_regions);
|
||||
mtd.eraseregions = erase_regions;
|
||||
mtd.erase = flash_erase;
|
||||
mtd.read = flash_read;
|
||||
mtd.write = flash_write;
|
||||
mtd._erase = flash_erase;
|
||||
mtd._read = flash_read;
|
||||
mtd._write = flash_write;
|
||||
mtd.owner = THIS_MODULE;
|
||||
|
||||
#ifdef LART_DEBUG
|
||||
|
@@ -288,9 +288,6 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
__func__, (long long)instr->addr,
|
||||
(long long)instr->len);
|
||||
|
||||
/* sanity checks */
|
||||
if (instr->addr + instr->len > flash->mtd.size)
|
||||
return -EINVAL;
|
||||
div_u64_rem(instr->len, mtd->erasesize, &rem);
|
||||
if (rem)
|
||||
return -EINVAL;
|
||||
@@ -349,13 +346,6 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
|
||||
__func__, (u32)from, len);
|
||||
|
||||
/* sanity checks */
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (from + len > flash->mtd.size)
|
||||
return -EINVAL;
|
||||
|
||||
spi_message_init(&m);
|
||||
memset(t, 0, (sizeof t));
|
||||
|
||||
@@ -371,9 +361,6 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
t[1].len = len;
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
/* Byte count starts at zero. */
|
||||
*retlen = 0;
|
||||
|
||||
mutex_lock(&flash->lock);
|
||||
|
||||
/* Wait till previous write/erase is done. */
|
||||
@@ -417,15 +404,6 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
|
||||
__func__, (u32)to, len);
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
/* sanity checks */
|
||||
if (!len)
|
||||
return(0);
|
||||
|
||||
if (to + len > flash->mtd.size)
|
||||
return -EINVAL;
|
||||
|
||||
spi_message_init(&m);
|
||||
memset(t, 0, (sizeof t));
|
||||
|
||||
@@ -509,15 +487,6 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
|
||||
__func__, (u32)to, len);
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
/* sanity checks */
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (to + len > flash->mtd.size)
|
||||
return -EINVAL;
|
||||
|
||||
spi_message_init(&m);
|
||||
memset(t, 0, (sizeof t));
|
||||
|
||||
@@ -908,14 +877,14 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
||||
flash->mtd.writesize = 1;
|
||||
flash->mtd.flags = MTD_CAP_NORFLASH;
|
||||
flash->mtd.size = info->sector_size * info->n_sectors;
|
||||
flash->mtd.erase = m25p80_erase;
|
||||
flash->mtd.read = m25p80_read;
|
||||
flash->mtd._erase = m25p80_erase;
|
||||
flash->mtd._read = m25p80_read;
|
||||
|
||||
/* sst flash chips use AAI word program */
|
||||
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST)
|
||||
flash->mtd.write = sst_write;
|
||||
flash->mtd._write = sst_write;
|
||||
else
|
||||
flash->mtd.write = m25p80_write;
|
||||
flash->mtd._write = m25p80_write;
|
||||
|
||||
/* prefer "small sector" erase if possible */
|
||||
if (info->flags & SECT_4K) {
|
||||
@@ -932,6 +901,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
||||
ppdata.of_node = spi->dev.of_node;
|
||||
flash->mtd.dev.parent = &spi->dev;
|
||||
flash->page_size = info->page_size;
|
||||
flash->mtd.writebufsize = flash->page_size;
|
||||
|
||||
if (info->addr_width)
|
||||
flash->addr_width = info->addr_width;
|
||||
@@ -1004,21 +974,7 @@ static struct spi_driver m25p80_driver = {
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
static int __init m25p80_init(void)
|
||||
{
|
||||
return spi_register_driver(&m25p80_driver);
|
||||
}
|
||||
|
||||
|
||||
static void __exit m25p80_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&m25p80_driver);
|
||||
}
|
||||
|
||||
|
||||
module_init(m25p80_init);
|
||||
module_exit(m25p80_exit);
|
||||
module_spi_driver(m25p80_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Mike Lavender");
|
||||
|
@@ -59,12 +59,8 @@ static int ms02nv_read(struct mtd_info *mtd, loff_t from,
|
||||
{
|
||||
struct ms02nv_private *mp = mtd->priv;
|
||||
|
||||
if (from + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(buf, mp->uaddr + from, len);
|
||||
*retlen = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -73,12 +69,8 @@ static int ms02nv_write(struct mtd_info *mtd, loff_t to,
|
||||
{
|
||||
struct ms02nv_private *mp = mtd->priv;
|
||||
|
||||
if (to + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(mp->uaddr + to, buf, len);
|
||||
*retlen = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -215,8 +207,8 @@ static int __init ms02nv_init_one(ulong addr)
|
||||
mtd->size = fixsize;
|
||||
mtd->name = (char *)ms02nv_name;
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->read = ms02nv_read;
|
||||
mtd->write = ms02nv_write;
|
||||
mtd->_read = ms02nv_read;
|
||||
mtd->_write = ms02nv_write;
|
||||
mtd->writesize = 1;
|
||||
|
||||
ret = -EIO;
|
||||
|
@@ -164,9 +164,6 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
dev_name(&spi->dev), (long long)instr->addr,
|
||||
(long long)instr->len);
|
||||
|
||||
/* Sanity checks */
|
||||
if (instr->addr + instr->len > mtd->size)
|
||||
return -EINVAL;
|
||||
div_u64_rem(instr->len, priv->page_size, &rem);
|
||||
if (rem)
|
||||
return -EINVAL;
|
||||
@@ -252,14 +249,6 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
pr_debug("%s: read 0x%x..0x%x\n", dev_name(&priv->spi->dev),
|
||||
(unsigned)from, (unsigned)(from + len));
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
/* Sanity checks */
|
||||
if (!len)
|
||||
return 0;
|
||||
if (from + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Calculate flash page/byte address */
|
||||
addr = (((unsigned)from / priv->page_size) << priv->page_offset)
|
||||
+ ((unsigned)from % priv->page_size);
|
||||
@@ -328,14 +317,6 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
pr_debug("%s: write 0x%x..0x%x\n",
|
||||
dev_name(&spi->dev), (unsigned)to, (unsigned)(to + len));
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
/* Sanity checks */
|
||||
if (!len)
|
||||
return 0;
|
||||
if ((to + len) > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
x[0].tx_buf = command = priv->command;
|
||||
@@ -490,8 +471,6 @@ static ssize_t otp_read(struct spi_device *spi, unsigned base,
|
||||
|
||||
if ((off + len) > 64)
|
||||
len = 64 - off;
|
||||
if (len == 0)
|
||||
return len;
|
||||
|
||||
spi_message_init(&m);
|
||||
|
||||
@@ -611,16 +590,16 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,
|
||||
|
||||
static char *otp_setup(struct mtd_info *device, char revision)
|
||||
{
|
||||
device->get_fact_prot_info = dataflash_get_otp_info;
|
||||
device->read_fact_prot_reg = dataflash_read_fact_otp;
|
||||
device->get_user_prot_info = dataflash_get_otp_info;
|
||||
device->read_user_prot_reg = dataflash_read_user_otp;
|
||||
device->_get_fact_prot_info = dataflash_get_otp_info;
|
||||
device->_read_fact_prot_reg = dataflash_read_fact_otp;
|
||||
device->_get_user_prot_info = dataflash_get_otp_info;
|
||||
device->_read_user_prot_reg = dataflash_read_user_otp;
|
||||
|
||||
/* rev c parts (at45db321c and at45db1281 only!) use a
|
||||
* different write procedure; not (yet?) implemented.
|
||||
*/
|
||||
if (revision > 'c')
|
||||
device->write_user_prot_reg = dataflash_write_user_otp;
|
||||
device->_write_user_prot_reg = dataflash_write_user_otp;
|
||||
|
||||
return ", OTP";
|
||||
}
|
||||
@@ -672,9 +651,9 @@ add_dataflash_otp(struct spi_device *spi, char *name,
|
||||
device->owner = THIS_MODULE;
|
||||
device->type = MTD_DATAFLASH;
|
||||
device->flags = MTD_WRITEABLE;
|
||||
device->erase = dataflash_erase;
|
||||
device->read = dataflash_read;
|
||||
device->write = dataflash_write;
|
||||
device->_erase = dataflash_erase;
|
||||
device->_read = dataflash_read;
|
||||
device->_write = dataflash_write;
|
||||
device->priv = priv;
|
||||
|
||||
device->dev.parent = &spi->dev;
|
||||
@@ -946,18 +925,7 @@ static struct spi_driver dataflash_driver = {
|
||||
/* FIXME: investigate suspend and resume... */
|
||||
};
|
||||
|
||||
static int __init dataflash_init(void)
|
||||
{
|
||||
return spi_register_driver(&dataflash_driver);
|
||||
}
|
||||
module_init(dataflash_init);
|
||||
|
||||
static void __exit dataflash_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&dataflash_driver);
|
||||
}
|
||||
module_exit(dataflash_exit);
|
||||
|
||||
module_spi_driver(dataflash_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Andrew Victor, David Brownell");
|
||||
|
@@ -34,34 +34,23 @@ static struct mtd_info *mtd_info;
|
||||
|
||||
static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
if (instr->addr + instr->len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
|
||||
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
mtd_erase_callback(instr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ram_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, void **virt, resource_size_t *phys)
|
||||
{
|
||||
if (from + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
/* can we return a physical address with this driver? */
|
||||
if (phys)
|
||||
return -EINVAL;
|
||||
|
||||
*virt = mtd->priv + from;
|
||||
*retlen = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
static int ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -80,11 +69,7 @@ static unsigned long ram_get_unmapped_area(struct mtd_info *mtd,
|
||||
static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
if (from + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(buf, mtd->priv + from, len);
|
||||
|
||||
*retlen = len;
|
||||
return 0;
|
||||
}
|
||||
@@ -92,11 +77,7 @@ static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
static int ram_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
if (to + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy((char *)mtd->priv + to, buf, len);
|
||||
|
||||
*retlen = len;
|
||||
return 0;
|
||||
}
|
||||
@@ -126,12 +107,12 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
|
||||
mtd->priv = mapped_address;
|
||||
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->erase = ram_erase;
|
||||
mtd->point = ram_point;
|
||||
mtd->unpoint = ram_unpoint;
|
||||
mtd->get_unmapped_area = ram_get_unmapped_area;
|
||||
mtd->read = ram_read;
|
||||
mtd->write = ram_write;
|
||||
mtd->_erase = ram_erase;
|
||||
mtd->_point = ram_point;
|
||||
mtd->_unpoint = ram_unpoint;
|
||||
mtd->_get_unmapped_area = ram_get_unmapped_area;
|
||||
mtd->_read = ram_read;
|
||||
mtd->_write = ram_write;
|
||||
|
||||
if (mtd_device_register(mtd, NULL, 0))
|
||||
return -EIO;
|
||||
|
@@ -33,45 +33,33 @@ struct phram_mtd_list {
|
||||
|
||||
static LIST_HEAD(phram_list);
|
||||
|
||||
|
||||
static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
u_char *start = mtd->priv;
|
||||
|
||||
if (instr->addr + instr->len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
memset(start + instr->addr, 0xff, instr->len);
|
||||
|
||||
/* This'll catch a few races. Free the thing before returning :)
|
||||
/*
|
||||
* This'll catch a few races. Free the thing before returning :)
|
||||
* I don't feel at all ashamed. This kind of thing is possible anyway
|
||||
* with flash, but unlikely.
|
||||
*/
|
||||
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
|
||||
mtd_erase_callback(instr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, void **virt, resource_size_t *phys)
|
||||
{
|
||||
if (from + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
/* can we return a physical address with this driver? */
|
||||
if (phys)
|
||||
return -EINVAL;
|
||||
|
||||
*virt = mtd->priv + from;
|
||||
*retlen = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
static int phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
@@ -79,14 +67,7 @@ static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
{
|
||||
u_char *start = mtd->priv;
|
||||
|
||||
if (from >= mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
if (len > mtd->size - from)
|
||||
len = mtd->size - from;
|
||||
|
||||
memcpy(buf, start + from, len);
|
||||
|
||||
*retlen = len;
|
||||
return 0;
|
||||
}
|
||||
@@ -96,20 +77,11 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
{
|
||||
u_char *start = mtd->priv;
|
||||
|
||||
if (to >= mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
if (len > mtd->size - to)
|
||||
len = mtd->size - to;
|
||||
|
||||
memcpy(start + to, buf, len);
|
||||
|
||||
*retlen = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void unregister_devices(void)
|
||||
{
|
||||
struct phram_mtd_list *this, *safe;
|
||||
@@ -142,11 +114,11 @@ static int register_device(char *name, unsigned long start, unsigned long len)
|
||||
new->mtd.name = name;
|
||||
new->mtd.size = len;
|
||||
new->mtd.flags = MTD_CAP_RAM;
|
||||
new->mtd.erase = phram_erase;
|
||||
new->mtd.point = phram_point;
|
||||
new->mtd.unpoint = phram_unpoint;
|
||||
new->mtd.read = phram_read;
|
||||
new->mtd.write = phram_write;
|
||||
new->mtd._erase = phram_erase;
|
||||
new->mtd._point = phram_point;
|
||||
new->mtd._unpoint = phram_unpoint;
|
||||
new->mtd._read = phram_read;
|
||||
new->mtd._write = phram_write;
|
||||
new->mtd.owner = THIS_MODULE;
|
||||
new->mtd.type = MTD_RAM;
|
||||
new->mtd.erasesize = PAGE_SIZE;
|
||||
@@ -233,7 +205,17 @@ static inline void kill_final_newline(char *str)
|
||||
return 1; \
|
||||
} while (0)
|
||||
|
||||
static int phram_setup(const char *val, struct kernel_param *kp)
|
||||
/*
|
||||
* This shall contain the module parameter if any. It is of the form:
|
||||
* - phram=<device>,<address>,<size> for module case
|
||||
* - phram.phram=<device>,<address>,<size> for built-in case
|
||||
* We leave 64 bytes for the device name, 12 for the address and 12 for the
|
||||
* size.
|
||||
* Example: phram.phram=rootfs,0xa0000000,512Mi
|
||||
*/
|
||||
static __initdata char phram_paramline[64+12+12];
|
||||
|
||||
static int __init phram_setup(const char *val)
|
||||
{
|
||||
char buf[64+12+12], *str = buf;
|
||||
char *token[3];
|
||||
@@ -282,12 +264,28 @@ static int phram_setup(const char *val, struct kernel_param *kp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
module_param_call(phram, phram_setup, NULL, NULL, 000);
|
||||
static int __init phram_param_call(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
/*
|
||||
* This function is always called before 'init_phram()', whether
|
||||
* built-in or module.
|
||||
*/
|
||||
if (strlen(val) >= sizeof(phram_paramline))
|
||||
return -ENOSPC;
|
||||
strcpy(phram_paramline, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_param_call(phram, phram_param_call, NULL, NULL, 000);
|
||||
MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\"");
|
||||
|
||||
|
||||
static int __init init_phram(void)
|
||||
{
|
||||
if (phram_paramline[0])
|
||||
return phram_setup(phram_paramline);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -94,12 +94,48 @@
|
||||
#include <linux/ioctl.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/pmc551.h>
|
||||
|
||||
#define PMC551_VERSION \
|
||||
"Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n"
|
||||
|
||||
#define PCI_VENDOR_ID_V3_SEMI 0x11b0
|
||||
#define PCI_DEVICE_ID_V3_SEMI_V370PDC 0x0200
|
||||
|
||||
#define PMC551_PCI_MEM_MAP0 0x50
|
||||
#define PMC551_PCI_MEM_MAP1 0x54
|
||||
#define PMC551_PCI_MEM_MAP_MAP_ADDR_MASK 0x3ff00000
|
||||
#define PMC551_PCI_MEM_MAP_APERTURE_MASK 0x000000f0
|
||||
#define PMC551_PCI_MEM_MAP_REG_EN 0x00000002
|
||||
#define PMC551_PCI_MEM_MAP_ENABLE 0x00000001
|
||||
|
||||
#define PMC551_SDRAM_MA 0x60
|
||||
#define PMC551_SDRAM_CMD 0x62
|
||||
#define PMC551_DRAM_CFG 0x64
|
||||
#define PMC551_SYS_CTRL_REG 0x78
|
||||
|
||||
#define PMC551_DRAM_BLK0 0x68
|
||||
#define PMC551_DRAM_BLK1 0x6c
|
||||
#define PMC551_DRAM_BLK2 0x70
|
||||
#define PMC551_DRAM_BLK3 0x74
|
||||
#define PMC551_DRAM_BLK_GET_SIZE(x) (524288 << ((x >> 4) & 0x0f))
|
||||
#define PMC551_DRAM_BLK_SET_COL_MUX(x, v) (((x) & ~0x00007000) | (((v) & 0x7) << 12))
|
||||
#define PMC551_DRAM_BLK_SET_ROW_MUX(x, v) (((x) & ~0x00000f00) | (((v) & 0xf) << 8))
|
||||
|
||||
struct mypriv {
|
||||
struct pci_dev *dev;
|
||||
u_char *start;
|
||||
u32 base_map0;
|
||||
u32 curr_map0;
|
||||
u32 asize;
|
||||
struct mtd_info *nextpmc551;
|
||||
};
|
||||
|
||||
static struct mtd_info *pmc551list;
|
||||
|
||||
static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, void **virt, resource_size_t *phys);
|
||||
|
||||
static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
struct mypriv *priv = mtd->priv;
|
||||
@@ -115,16 +151,6 @@ static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
#endif
|
||||
|
||||
end = instr->addr + instr->len - 1;
|
||||
|
||||
/* Is it past the end? */
|
||||
if (end > mtd->size) {
|
||||
#ifdef CONFIG_MTD_PMC551_DEBUG
|
||||
printk(KERN_DEBUG "pmc551_erase() out of bounds (%ld > %ld)\n",
|
||||
(long)end, (long)mtd->size);
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
eoff_hi = end & ~(priv->asize - 1);
|
||||
soff_hi = instr->addr & ~(priv->asize - 1);
|
||||
eoff_lo = end & (priv->asize - 1);
|
||||
@@ -178,18 +204,6 @@ static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
printk(KERN_DEBUG "pmc551_point(%ld, %ld)\n", (long)from, (long)len);
|
||||
#endif
|
||||
|
||||
if (from + len > mtd->size) {
|
||||
#ifdef CONFIG_MTD_PMC551_DEBUG
|
||||
printk(KERN_DEBUG "pmc551_point() out of bounds (%ld > %ld)\n",
|
||||
(long)from + len, (long)mtd->size);
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* can we return a physical address with this driver? */
|
||||
if (phys)
|
||||
return -EINVAL;
|
||||
|
||||
soff_hi = from & ~(priv->asize - 1);
|
||||
soff_lo = from & (priv->asize - 1);
|
||||
|
||||
@@ -205,11 +219,12 @@ static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pmc551_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
static int pmc551_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
{
|
||||
#ifdef CONFIG_MTD_PMC551_DEBUG
|
||||
printk(KERN_DEBUG "pmc551_unpoint()\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
@@ -228,16 +243,6 @@ static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
#endif
|
||||
|
||||
end = from + len - 1;
|
||||
|
||||
/* Is it past the end? */
|
||||
if (end > mtd->size) {
|
||||
#ifdef CONFIG_MTD_PMC551_DEBUG
|
||||
printk(KERN_DEBUG "pmc551_read() out of bounds (%ld > %ld)\n",
|
||||
(long)end, (long)mtd->size);
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
soff_hi = from & ~(priv->asize - 1);
|
||||
eoff_hi = end & ~(priv->asize - 1);
|
||||
soff_lo = from & (priv->asize - 1);
|
||||
@@ -295,16 +300,6 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
#endif
|
||||
|
||||
end = to + len - 1;
|
||||
/* Is it past the end? or did the u32 wrap? */
|
||||
if (end > mtd->size) {
|
||||
#ifdef CONFIG_MTD_PMC551_DEBUG
|
||||
printk(KERN_DEBUG "pmc551_write() out of bounds (end: %ld, "
|
||||
"size: %ld, to: %ld)\n", (long)end, (long)mtd->size,
|
||||
(long)to);
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
soff_hi = to & ~(priv->asize - 1);
|
||||
eoff_hi = end & ~(priv->asize - 1);
|
||||
soff_lo = to & (priv->asize - 1);
|
||||
@@ -358,7 +353,7 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
* mechanism
|
||||
* returns the size of the memory region found.
|
||||
*/
|
||||
static u32 fixup_pmc551(struct pci_dev *dev)
|
||||
static int fixup_pmc551(struct pci_dev *dev)
|
||||
{
|
||||
#ifdef CONFIG_MTD_PMC551_BUGFIX
|
||||
u32 dram_data;
|
||||
@@ -668,7 +663,7 @@ static int __init init_pmc551(void)
|
||||
struct mypriv *priv;
|
||||
int found = 0;
|
||||
struct mtd_info *mtd;
|
||||
u32 length = 0;
|
||||
int length = 0;
|
||||
|
||||
if (msize) {
|
||||
msize = (1 << (ffs(msize) - 1)) << 20;
|
||||
@@ -786,11 +781,11 @@ static int __init init_pmc551(void)
|
||||
|
||||
mtd->size = msize;
|
||||
mtd->flags = MTD_CAP_RAM;
|
||||
mtd->erase = pmc551_erase;
|
||||
mtd->read = pmc551_read;
|
||||
mtd->write = pmc551_write;
|
||||
mtd->point = pmc551_point;
|
||||
mtd->unpoint = pmc551_unpoint;
|
||||
mtd->_erase = pmc551_erase;
|
||||
mtd->_read = pmc551_read;
|
||||
mtd->_write = pmc551_write;
|
||||
mtd->_point = pmc551_point;
|
||||
mtd->_unpoint = pmc551_unpoint;
|
||||
mtd->type = MTD_RAM;
|
||||
mtd->name = "PMC551 RAM board";
|
||||
mtd->erasesize = 0x10000;
|
||||
|
@@ -75,7 +75,7 @@ static slram_mtd_list_t *slram_mtdlist = NULL;
|
||||
static int slram_erase(struct mtd_info *, struct erase_info *);
|
||||
static int slram_point(struct mtd_info *, loff_t, size_t, size_t *, void **,
|
||||
resource_size_t *);
|
||||
static void slram_unpoint(struct mtd_info *, loff_t, size_t);
|
||||
static int slram_unpoint(struct mtd_info *, loff_t, size_t);
|
||||
static int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
static int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
|
||||
|
||||
@@ -83,21 +83,13 @@ static int slram_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
slram_priv_t *priv = mtd->priv;
|
||||
|
||||
if (instr->addr + instr->len > mtd->size) {
|
||||
return(-EINVAL);
|
||||
}
|
||||
|
||||
memset(priv->start + instr->addr, 0xff, instr->len);
|
||||
|
||||
/* This'll catch a few races. Free the thing before returning :)
|
||||
* I don't feel at all ashamed. This kind of thing is possible anyway
|
||||
* with flash, but unlikely.
|
||||
*/
|
||||
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
|
||||
mtd_erase_callback(instr);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
@@ -106,20 +98,14 @@ static int slram_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
{
|
||||
slram_priv_t *priv = mtd->priv;
|
||||
|
||||
/* can we return a physical address with this driver? */
|
||||
if (phys)
|
||||
return -EINVAL;
|
||||
|
||||
if (from + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
*virt = priv->start + from;
|
||||
*retlen = len;
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void slram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
static int slram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int slram_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
@@ -127,14 +113,7 @@ static int slram_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
{
|
||||
slram_priv_t *priv = mtd->priv;
|
||||
|
||||
if (from > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
if (from + len > mtd->size)
|
||||
len = mtd->size - from;
|
||||
|
||||
memcpy(buf, priv->start + from, len);
|
||||
|
||||
*retlen = len;
|
||||
return(0);
|
||||
}
|
||||
@@ -144,11 +123,7 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
{
|
||||
slram_priv_t *priv = mtd->priv;
|
||||
|
||||
if (to + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(priv->start + to, buf, len);
|
||||
|
||||
*retlen = len;
|
||||
return(0);
|
||||
}
|
||||
@@ -199,11 +174,11 @@ static int register_device(char *name, unsigned long start, unsigned long length
|
||||
(*curmtd)->mtdinfo->name = name;
|
||||
(*curmtd)->mtdinfo->size = length;
|
||||
(*curmtd)->mtdinfo->flags = MTD_CAP_RAM;
|
||||
(*curmtd)->mtdinfo->erase = slram_erase;
|
||||
(*curmtd)->mtdinfo->point = slram_point;
|
||||
(*curmtd)->mtdinfo->unpoint = slram_unpoint;
|
||||
(*curmtd)->mtdinfo->read = slram_read;
|
||||
(*curmtd)->mtdinfo->write = slram_write;
|
||||
(*curmtd)->mtdinfo->_erase = slram_erase;
|
||||
(*curmtd)->mtdinfo->_point = slram_point;
|
||||
(*curmtd)->mtdinfo->_unpoint = slram_unpoint;
|
||||
(*curmtd)->mtdinfo->_read = slram_read;
|
||||
(*curmtd)->mtdinfo->_write = slram_write;
|
||||
(*curmtd)->mtdinfo->owner = THIS_MODULE;
|
||||
(*curmtd)->mtdinfo->type = MTD_RAM;
|
||||
(*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ;
|
||||
|
1147
drivers/mtd/devices/spear_smi.c
Normal file
1147
drivers/mtd/devices/spear_smi.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -175,9 +175,6 @@ static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
int err;
|
||||
|
||||
/* Sanity checks */
|
||||
if (instr->addr + instr->len > flash->mtd.size)
|
||||
return -EINVAL;
|
||||
|
||||
if ((uint32_t)instr->len % mtd->erasesize)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -223,16 +220,6 @@ static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
unsigned char command[4];
|
||||
int ret;
|
||||
|
||||
/* Sanity checking */
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
if (from + len > flash->mtd.size)
|
||||
return -EINVAL;
|
||||
|
||||
if (retlen)
|
||||
*retlen = 0;
|
||||
|
||||
spi_message_init(&message);
|
||||
memset(&transfer, 0, sizeof(transfer));
|
||||
|
||||
@@ -274,13 +261,6 @@ static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
int i, j, ret, bytes, copied = 0;
|
||||
unsigned char command[5];
|
||||
|
||||
/* Sanity checks */
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (to + len > flash->mtd.size)
|
||||
return -EINVAL;
|
||||
|
||||
if ((uint32_t)to % mtd->writesize)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -402,10 +382,11 @@ static int __devinit sst25l_probe(struct spi_device *spi)
|
||||
flash->mtd.flags = MTD_CAP_NORFLASH;
|
||||
flash->mtd.erasesize = flash_info->erase_size;
|
||||
flash->mtd.writesize = flash_info->page_size;
|
||||
flash->mtd.writebufsize = flash_info->page_size;
|
||||
flash->mtd.size = flash_info->page_size * flash_info->nr_pages;
|
||||
flash->mtd.erase = sst25l_erase;
|
||||
flash->mtd.read = sst25l_read;
|
||||
flash->mtd.write = sst25l_write;
|
||||
flash->mtd._erase = sst25l_erase;
|
||||
flash->mtd._read = sst25l_read;
|
||||
flash->mtd._write = sst25l_write;
|
||||
|
||||
dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name,
|
||||
(long long)flash->mtd.size >> 10);
|
||||
@@ -418,9 +399,9 @@ static int __devinit sst25l_probe(struct spi_device *spi)
|
||||
flash->mtd.numeraseregions);
|
||||
|
||||
|
||||
ret = mtd_device_parse_register(&flash->mtd, NULL, 0,
|
||||
data ? data->parts : NULL,
|
||||
data ? data->nr_parts : 0);
|
||||
ret = mtd_device_parse_register(&flash->mtd, NULL, NULL,
|
||||
data ? data->parts : NULL,
|
||||
data ? data->nr_parts : 0);
|
||||
if (ret) {
|
||||
kfree(flash);
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
@@ -450,18 +431,7 @@ static struct spi_driver sst25l_driver = {
|
||||
.remove = __devexit_p(sst25l_remove),
|
||||
};
|
||||
|
||||
static int __init sst25l_init(void)
|
||||
{
|
||||
return spi_register_driver(&sst25l_driver);
|
||||
}
|
||||
|
||||
static void __exit sst25l_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&sst25l_driver);
|
||||
}
|
||||
|
||||
module_init(sst25l_init);
|
||||
module_exit(sst25l_exit);
|
||||
module_spi_driver(sst25l_driver);
|
||||
|
||||
MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips");
|
||||
MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, "
|
||||
|
@@ -56,7 +56,7 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
||||
if (memcmp(mtd->name, "DiskOnChip", 10))
|
||||
return;
|
||||
|
||||
if (!mtd->block_isbad) {
|
||||
if (!mtd->_block_isbad) {
|
||||
printk(KERN_ERR
|
||||
"INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
|
||||
"Please use the new diskonchip driver under the NAND subsystem.\n");
|
||||
|
@@ -40,7 +40,7 @@ static int lpddr_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
static int lpddr_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
||||
static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
|
||||
size_t *retlen, void **mtdbuf, resource_size_t *phys);
|
||||
static void lpddr_unpoint(struct mtd_info *mtd, loff_t adr, size_t len);
|
||||
static int lpddr_unpoint(struct mtd_info *mtd, loff_t adr, size_t len);
|
||||
static int get_chip(struct map_info *map, struct flchip *chip, int mode);
|
||||
static int chip_ready(struct map_info *map, struct flchip *chip, int mode);
|
||||
static void put_chip(struct map_info *map, struct flchip *chip);
|
||||
@@ -63,18 +63,18 @@ struct mtd_info *lpddr_cmdset(struct map_info *map)
|
||||
mtd->type = MTD_NORFLASH;
|
||||
|
||||
/* Fill in the default mtd operations */
|
||||
mtd->read = lpddr_read;
|
||||
mtd->_read = lpddr_read;
|
||||
mtd->type = MTD_NORFLASH;
|
||||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
mtd->flags &= ~MTD_BIT_WRITEABLE;
|
||||
mtd->erase = lpddr_erase;
|
||||
mtd->write = lpddr_write_buffers;
|
||||
mtd->writev = lpddr_writev;
|
||||
mtd->lock = lpddr_lock;
|
||||
mtd->unlock = lpddr_unlock;
|
||||
mtd->_erase = lpddr_erase;
|
||||
mtd->_write = lpddr_write_buffers;
|
||||
mtd->_writev = lpddr_writev;
|
||||
mtd->_lock = lpddr_lock;
|
||||
mtd->_unlock = lpddr_unlock;
|
||||
if (map_is_linear(map)) {
|
||||
mtd->point = lpddr_point;
|
||||
mtd->unpoint = lpddr_unpoint;
|
||||
mtd->_point = lpddr_point;
|
||||
mtd->_unpoint = lpddr_unpoint;
|
||||
}
|
||||
mtd->size = 1 << lpddr->qinfo->DevSizeShift;
|
||||
mtd->erasesize = 1 << lpddr->qinfo->UniformBlockSizeShift;
|
||||
@@ -530,14 +530,12 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
|
||||
struct flchip *chip = &lpddr->chips[chipnum];
|
||||
int ret = 0;
|
||||
|
||||
if (!map->virt || (adr + len > mtd->size))
|
||||
if (!map->virt)
|
||||
return -EINVAL;
|
||||
|
||||
/* ofs: offset within the first chip that the first read should start */
|
||||
ofs = adr - (chipnum << lpddr->chipshift);
|
||||
|
||||
*mtdbuf = (void *)map->virt + chip->start + ofs;
|
||||
*retlen = 0;
|
||||
|
||||
while (len) {
|
||||
unsigned long thislen;
|
||||
@@ -575,11 +573,11 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
|
||||
static int lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct lpddr_private *lpddr = map->fldrv_priv;
|
||||
int chipnum = adr >> lpddr->chipshift;
|
||||
int chipnum = adr >> lpddr->chipshift, err = 0;
|
||||
unsigned long ofs;
|
||||
|
||||
/* ofs: offset within the first chip that the first read should start */
|
||||
@@ -603,9 +601,11 @@ static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
|
||||
chip->ref_point_counter--;
|
||||
if (chip->ref_point_counter == 0)
|
||||
chip->state = FL_READY;
|
||||
} else
|
||||
} else {
|
||||
printk(KERN_WARNING "%s: Warning: unpoint called on non"
|
||||
"pointed region\n", map->name);
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
put_chip(map, chip);
|
||||
mutex_unlock(&chip->mutex);
|
||||
@@ -614,6 +614,8 @@ static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
|
||||
ofs = 0;
|
||||
chipnum++;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int lpddr_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
@@ -637,13 +639,11 @@ static int lpddr_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
int chipnum;
|
||||
unsigned long ofs, vec_seek, i;
|
||||
int wbufsize = 1 << lpddr->qinfo->BufSizeShift;
|
||||
|
||||
size_t len = 0;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
len += vecs[i].iov_len;
|
||||
|
||||
*retlen = 0;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
@@ -688,9 +688,6 @@ static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
ofs = instr->addr;
|
||||
len = instr->len;
|
||||
|
||||
if (ofs > mtd->size || (len + ofs) > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
while (len > 0) {
|
||||
ret = do_erase_oneblock(mtd, ofs);
|
||||
if (ret)
|
||||
|
@@ -164,8 +164,8 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
mtd_device_parse_register(state->mtd, part_probe_types, 0,
|
||||
pdata->parts, pdata->nr_parts);
|
||||
mtd_device_parse_register(state->mtd, part_probe_types, NULL,
|
||||
pdata->parts, pdata->nr_parts);
|
||||
|
||||
platform_set_drvdata(pdev, state);
|
||||
|
||||
|
@@ -196,7 +196,7 @@ static int __init init_dc21285(void)
|
||||
|
||||
dc21285_mtd->owner = THIS_MODULE;
|
||||
|
||||
mtd_device_parse_register(dc21285_mtd, probes, 0, NULL, 0);
|
||||
mtd_device_parse_register(dc21285_mtd, probes, NULL, NULL, 0);
|
||||
|
||||
if(machine_is_ebsa285()) {
|
||||
/*
|
||||
|
@@ -252,8 +252,8 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
|
||||
mtd_device_parse_register(state->mtd, part_probe_types, 0,
|
||||
pdata->parts, pdata->nr_parts);
|
||||
mtd_device_parse_register(state->mtd, part_probe_types, NULL,
|
||||
pdata->parts, pdata->nr_parts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -85,8 +85,8 @@ static int __init h720x_mtd_init(void)
|
||||
if (mymtd) {
|
||||
mymtd->owner = THIS_MODULE;
|
||||
|
||||
mtd_device_parse_register(mymtd, NULL, 0,
|
||||
h720x_partitions, NUM_PARTITIONS);
|
||||
mtd_device_parse_register(mymtd, NULL, NULL,
|
||||
h720x_partitions, NUM_PARTITIONS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -91,7 +91,7 @@ static int __init init_impa7(void)
|
||||
if (impa7_mtd[i]) {
|
||||
impa7_mtd[i]->owner = THIS_MODULE;
|
||||
devicesfound++;
|
||||
mtd_device_parse_register(impa7_mtd[i], NULL, 0,
|
||||
mtd_device_parse_register(impa7_mtd[i], NULL, NULL,
|
||||
partitions,
|
||||
ARRAY_SIZE(partitions));
|
||||
}
|
||||
|
@@ -72,7 +72,7 @@ static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p)
|
||||
{
|
||||
/* register the flash bank */
|
||||
/* partition the flash bank */
|
||||
return mtd_device_parse_register(p->info, NULL, 0, NULL, 0);
|
||||
return mtd_device_parse_register(p->info, NULL, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
|
||||
|
@@ -226,7 +226,7 @@ static int ixp2000_flash_probe(struct platform_device *dev)
|
||||
}
|
||||
info->mtd->owner = THIS_MODULE;
|
||||
|
||||
err = mtd_device_parse_register(info->mtd, probes, 0, NULL, 0);
|
||||
err = mtd_device_parse_register(info->mtd, probes, NULL, NULL, 0);
|
||||
if (err)
|
||||
goto Error;
|
||||
|
||||
|
@@ -182,6 +182,9 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
|
||||
{
|
||||
struct flash_platform_data *plat = dev->dev.platform_data;
|
||||
struct ixp4xx_flash_info *info;
|
||||
struct mtd_part_parser_data ppdata = {
|
||||
.origin = dev->resource->start,
|
||||
};
|
||||
int err = -1;
|
||||
|
||||
if (!plat)
|
||||
@@ -247,7 +250,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
|
||||
/* Use the fast version */
|
||||
info->map.write = ixp4xx_write16;
|
||||
|
||||
err = mtd_device_parse_register(info->mtd, probes, dev->resource->start,
|
||||
err = mtd_device_parse_register(info->mtd, probes, &ppdata,
|
||||
plat->parts, plat->nr_parts);
|
||||
if (err) {
|
||||
printk(KERN_ERR "Could not parse partitions\n");
|
||||
|
@@ -27,17 +27,21 @@ static struct mtd_info *mymtd;
|
||||
|
||||
|
||||
/* Is this really the vpp port? */
|
||||
static DEFINE_SPINLOCK(l440gx_vpp_lock);
|
||||
static int l440gx_vpp_refcnt;
|
||||
static void l440gx_set_vpp(struct map_info *map, int vpp)
|
||||
{
|
||||
unsigned long l;
|
||||
unsigned long flags;
|
||||
|
||||
l = inl(VPP_PORT);
|
||||
spin_lock_irqsave(&l440gx_vpp_lock, flags);
|
||||
if (vpp) {
|
||||
l |= 1;
|
||||
if (++l440gx_vpp_refcnt == 1) /* first nested 'on' */
|
||||
outl(inl(VPP_PORT) | 1, VPP_PORT);
|
||||
} else {
|
||||
l &= ~1;
|
||||
if (--l440gx_vpp_refcnt == 0) /* last nested 'off' */
|
||||
outl(inl(VPP_PORT) & ~1, VPP_PORT);
|
||||
}
|
||||
outl(l, VPP_PORT);
|
||||
spin_unlock_irqrestore(&l440gx_vpp_lock, flags);
|
||||
}
|
||||
|
||||
static struct map_info l440gx_map = {
|
||||
|
@@ -45,6 +45,7 @@ struct ltq_mtd {
|
||||
};
|
||||
|
||||
static char ltq_map_name[] = "ltq_nor";
|
||||
static const char *ltq_probe_types[] __devinitconst = { "cmdlinepart", NULL };
|
||||
|
||||
static map_word
|
||||
ltq_read16(struct map_info *map, unsigned long adr)
|
||||
@@ -168,8 +169,9 @@ ltq_mtd_probe(struct platform_device *pdev)
|
||||
cfi->addr_unlock1 ^= 1;
|
||||
cfi->addr_unlock2 ^= 1;
|
||||
|
||||
err = mtd_device_parse_register(ltq_mtd->mtd, NULL, 0,
|
||||
ltq_mtd_data->parts, ltq_mtd_data->nr_parts);
|
||||
err = mtd_device_parse_register(ltq_mtd->mtd, ltq_probe_types, NULL,
|
||||
ltq_mtd_data->parts,
|
||||
ltq_mtd_data->nr_parts);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to add partitions\n");
|
||||
goto err_destroy;
|
||||
|
@@ -199,8 +199,9 @@ static int __devinit latch_addr_flash_probe(struct platform_device *dev)
|
||||
}
|
||||
info->mtd->owner = THIS_MODULE;
|
||||
|
||||
mtd_device_parse_register(info->mtd, NULL, 0,
|
||||
latch_addr_data->parts, latch_addr_data->nr_parts);
|
||||
mtd_device_parse_register(info->mtd, NULL, NULL,
|
||||
latch_addr_data->parts,
|
||||
latch_addr_data->nr_parts);
|
||||
return 0;
|
||||
|
||||
iounmap:
|
||||
|
@@ -294,13 +294,24 @@ static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *f
|
||||
}
|
||||
|
||||
|
||||
static DEFINE_SPINLOCK(pcmcia_vpp_lock);
|
||||
static int pcmcia_vpp_refcnt;
|
||||
static void pcmciamtd_set_vpp(struct map_info *map, int on)
|
||||
{
|
||||
struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
|
||||
struct pcmcia_device *link = dev->p_dev;
|
||||
unsigned long flags;
|
||||
|
||||
pr_debug("dev = %p on = %d vpp = %d\n\n", dev, on, dev->vpp);
|
||||
pcmcia_fixup_vpp(link, on ? dev->vpp : 0);
|
||||
spin_lock_irqsave(&pcmcia_vpp_lock, flags);
|
||||
if (on) {
|
||||
if (++pcmcia_vpp_refcnt == 1) /* first nested 'on' */
|
||||
pcmcia_fixup_vpp(link, dev->vpp);
|
||||
} else {
|
||||
if (--pcmcia_vpp_refcnt == 0) /* last nested 'off' */
|
||||
pcmcia_fixup_vpp(link, 0);
|
||||
}
|
||||
spin_unlock_irqrestore(&pcmcia_vpp_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -27,6 +27,8 @@ struct physmap_flash_info {
|
||||
struct mtd_info *mtd[MAX_RESOURCES];
|
||||
struct mtd_info *cmtd;
|
||||
struct map_info map[MAX_RESOURCES];
|
||||
spinlock_t vpp_lock;
|
||||
int vpp_refcnt;
|
||||
};
|
||||
|
||||
static int physmap_flash_remove(struct platform_device *dev)
|
||||
@@ -63,12 +65,26 @@ static void physmap_set_vpp(struct map_info *map, int state)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct physmap_flash_data *physmap_data;
|
||||
struct physmap_flash_info *info;
|
||||
unsigned long flags;
|
||||
|
||||
pdev = (struct platform_device *)map->map_priv_1;
|
||||
physmap_data = pdev->dev.platform_data;
|
||||
|
||||
if (physmap_data->set_vpp)
|
||||
physmap_data->set_vpp(pdev, state);
|
||||
if (!physmap_data->set_vpp)
|
||||
return;
|
||||
|
||||
info = platform_get_drvdata(pdev);
|
||||
|
||||
spin_lock_irqsave(&info->vpp_lock, flags);
|
||||
if (state) {
|
||||
if (++info->vpp_refcnt == 1) /* first nested 'on' */
|
||||
physmap_data->set_vpp(pdev, 1);
|
||||
} else {
|
||||
if (--info->vpp_refcnt == 0) /* last nested 'off' */
|
||||
physmap_data->set_vpp(pdev, 0);
|
||||
}
|
||||
spin_unlock_irqrestore(&info->vpp_lock, flags);
|
||||
}
|
||||
|
||||
static const char *rom_probe_types[] = {
|
||||
@@ -172,9 +188,11 @@ static int physmap_flash_probe(struct platform_device *dev)
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
spin_lock_init(&info->vpp_lock);
|
||||
|
||||
part_types = physmap_data->part_probe_types ? : part_probe_types;
|
||||
|
||||
mtd_device_parse_register(info->cmtd, part_types, 0,
|
||||
mtd_device_parse_register(info->cmtd, part_types, NULL,
|
||||
physmap_data->parts, physmap_data->nr_parts);
|
||||
return 0;
|
||||
|
||||
|
@@ -222,8 +222,9 @@ static int platram_probe(struct platform_device *pdev)
|
||||
/* check to see if there are any available partitions, or wether
|
||||
* to add this device whole */
|
||||
|
||||
err = mtd_device_parse_register(info->mtd, pdata->probes, 0,
|
||||
pdata->partitions, pdata->nr_partitions);
|
||||
err = mtd_device_parse_register(info->mtd, pdata->probes, NULL,
|
||||
pdata->partitions,
|
||||
pdata->nr_partitions);
|
||||
if (!err)
|
||||
dev_info(&pdev->dev, "registered mtd device\n");
|
||||
|
||||
|
@@ -98,7 +98,8 @@ static int __devinit pxa2xx_flash_probe(struct platform_device *pdev)
|
||||
}
|
||||
info->mtd->owner = THIS_MODULE;
|
||||
|
||||
mtd_device_parse_register(info->mtd, probes, 0, flash->parts, flash->nr_parts);
|
||||
mtd_device_parse_register(info->mtd, probes, NULL, flash->parts,
|
||||
flash->nr_parts);
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
return 0;
|
||||
|
@@ -102,8 +102,8 @@ static int rbtx4939_flash_probe(struct platform_device *dev)
|
||||
info->mtd->owner = THIS_MODULE;
|
||||
if (err)
|
||||
goto err_out;
|
||||
err = mtd_device_parse_register(info->mtd, NULL, 0,
|
||||
pdata->parts, pdata->nr_parts);
|
||||
err = mtd_device_parse_register(info->mtd, NULL, NULL, pdata->parts,
|
||||
pdata->nr_parts);
|
||||
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
@@ -36,10 +36,22 @@ struct sa_info {
|
||||
struct sa_subdev_info subdev[0];
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(sa1100_vpp_lock);
|
||||
static int sa1100_vpp_refcnt;
|
||||
static void sa1100_set_vpp(struct map_info *map, int on)
|
||||
{
|
||||
struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
|
||||
subdev->plat->set_vpp(on);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sa1100_vpp_lock, flags);
|
||||
if (on) {
|
||||
if (++sa1100_vpp_refcnt == 1) /* first nested 'on' */
|
||||
subdev->plat->set_vpp(1);
|
||||
} else {
|
||||
if (--sa1100_vpp_refcnt == 0) /* last nested 'off' */
|
||||
subdev->plat->set_vpp(0);
|
||||
}
|
||||
spin_unlock_irqrestore(&sa1100_vpp_lock, flags);
|
||||
}
|
||||
|
||||
static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
|
||||
@@ -252,8 +264,8 @@ static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
|
||||
/*
|
||||
* Partition selection stuff.
|
||||
*/
|
||||
mtd_device_parse_register(info->mtd, part_probes, 0,
|
||||
plat->parts, plat->nr_parts);
|
||||
mtd_device_parse_register(info->mtd, part_probes, NULL, plat->parts,
|
||||
plat->nr_parts);
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
err = 0;
|
||||
|
@@ -92,8 +92,8 @@ static int __init init_soleng_maps(void)
|
||||
mtd_device_register(eprom_mtd, NULL, 0);
|
||||
}
|
||||
|
||||
mtd_device_parse_register(flash_mtd, probes, 0,
|
||||
superh_se_partitions, NUM_PARTITIONS);
|
||||
mtd_device_parse_register(flash_mtd, probes, NULL,
|
||||
superh_se_partitions, NUM_PARTITIONS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -85,7 +85,7 @@ static int __init uclinux_mtd_init(void)
|
||||
}
|
||||
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->point = uclinux_point;
|
||||
mtd->_point = uclinux_point;
|
||||
mtd->priv = mapp;
|
||||
|
||||
uclinux_ram_mtdinfo = mtd;
|
||||
|
@@ -360,9 +360,6 @@ static int vmu_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
int index = 0, retval, partition, leftover, numblocks;
|
||||
unsigned char cx;
|
||||
|
||||
if (len < 1)
|
||||
return -EIO;
|
||||
|
||||
mpart = mtd->priv;
|
||||
mdev = mpart->mdev;
|
||||
partition = mpart->partition;
|
||||
@@ -434,11 +431,6 @@ static int vmu_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
partition = mpart->partition;
|
||||
card = maple_get_drvdata(mdev);
|
||||
|
||||
/* simple sanity checks */
|
||||
if (len < 1) {
|
||||
error = -EIO;
|
||||
goto failed;
|
||||
}
|
||||
numblocks = card->parts[partition].numblocks;
|
||||
if (to + len > numblocks * card->blocklen)
|
||||
len = numblocks * card->blocklen - to;
|
||||
@@ -544,9 +536,9 @@ static void vmu_queryblocks(struct mapleq *mq)
|
||||
mtd_cur->flags = MTD_WRITEABLE|MTD_NO_ERASE;
|
||||
mtd_cur->size = part_cur->numblocks * card->blocklen;
|
||||
mtd_cur->erasesize = card->blocklen;
|
||||
mtd_cur->write = vmu_flash_write;
|
||||
mtd_cur->read = vmu_flash_read;
|
||||
mtd_cur->sync = vmu_flash_sync;
|
||||
mtd_cur->_write = vmu_flash_write;
|
||||
mtd_cur->_read = vmu_flash_read;
|
||||
mtd_cur->_sync = vmu_flash_sync;
|
||||
mtd_cur->writesize = card->blocklen;
|
||||
|
||||
mpart = kmalloc(sizeof(struct mdev_part), GFP_KERNEL);
|
||||
|
@@ -142,7 +142,7 @@ static int __init init_sbc82xx_flash(void)
|
||||
nr_parts = ARRAY_SIZE(smallflash_parts);
|
||||
}
|
||||
|
||||
mtd_device_parse_register(sbcmtd[i], part_probes, 0,
|
||||
mtd_device_parse_register(sbcmtd[i], part_probes, NULL,
|
||||
defparts, nr_parts);
|
||||
}
|
||||
return 0;
|
||||
|
@@ -233,6 +233,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
|
||||
ret = __get_mtd_device(dev->mtd);
|
||||
if (ret)
|
||||
goto error_release;
|
||||
dev->file_mode = mode;
|
||||
|
||||
unlock:
|
||||
dev->open++;
|
||||
|
@@ -321,8 +321,12 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd)
|
||||
mutex_unlock(&mtdblk->cache_mutex);
|
||||
|
||||
if (!--mtdblk->count) {
|
||||
/* It was the last usage. Free the cache */
|
||||
mtd_sync(mbd->mtd);
|
||||
/*
|
||||
* It was the last usage. Free the cache, but only sync if
|
||||
* opened for writing.
|
||||
*/
|
||||
if (mbd->file_mode & FMODE_WRITE)
|
||||
mtd_sync(mbd->mtd);
|
||||
vfree(mtdblk->cache_data);
|
||||
}
|
||||
|
||||
|
@@ -405,7 +405,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd,
|
||||
if (length > 4096)
|
||||
return -EINVAL;
|
||||
|
||||
if (!mtd->write_oob)
|
||||
if (!mtd->_write_oob)
|
||||
ret = -EOPNOTSUPP;
|
||||
else
|
||||
ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT;
|
||||
@@ -576,7 +576,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
|
||||
!access_ok(VERIFY_READ, req.usr_data, req.len) ||
|
||||
!access_ok(VERIFY_READ, req.usr_oob, req.ooblen))
|
||||
return -EFAULT;
|
||||
if (!mtd->write_oob)
|
||||
if (!mtd->_write_oob)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ops.mode = req.mode;
|
||||
|
@@ -72,8 +72,6 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
int ret = 0, err;
|
||||
int i;
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
size_t size, retsize;
|
||||
@@ -126,11 +124,6 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
int err = -EINVAL;
|
||||
int i;
|
||||
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
size_t size, retsize;
|
||||
@@ -145,11 +138,7 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
else
|
||||
size = len;
|
||||
|
||||
if (!(subdev->flags & MTD_WRITEABLE))
|
||||
err = -EROFS;
|
||||
else
|
||||
err = mtd_write(subdev, to, size, &retsize, buf);
|
||||
|
||||
err = mtd_write(subdev, to, size, &retsize, buf);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@@ -176,19 +165,10 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
int i;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
/* Calculate total length of data */
|
||||
for (i = 0; i < count; i++)
|
||||
total_len += vecs[i].iov_len;
|
||||
|
||||
/* Do not allow write past end of device */
|
||||
if ((to + total_len) > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check alignment */
|
||||
if (mtd->writesize > 1) {
|
||||
uint64_t __to = to;
|
||||
@@ -224,12 +204,8 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
old_iov_len = vecs_copy[entry_high].iov_len;
|
||||
vecs_copy[entry_high].iov_len = size;
|
||||
|
||||
if (!(subdev->flags & MTD_WRITEABLE))
|
||||
err = -EROFS;
|
||||
else
|
||||
err = mtd_writev(subdev, &vecs_copy[entry_low],
|
||||
entry_high - entry_low + 1, to,
|
||||
&retsize);
|
||||
err = mtd_writev(subdev, &vecs_copy[entry_low],
|
||||
entry_high - entry_low + 1, to, &retsize);
|
||||
|
||||
vecs_copy[entry_high].iov_len = old_iov_len - size;
|
||||
vecs_copy[entry_high].iov_base += size;
|
||||
@@ -403,15 +379,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
uint64_t length, offset = 0;
|
||||
struct erase_info *erase;
|
||||
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
|
||||
if (instr->addr > concat->mtd.size)
|
||||
return -EINVAL;
|
||||
|
||||
if (instr->len + instr->addr > concat->mtd.size)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Check for proper erase block alignment of the to-be-erased area.
|
||||
* It is easier to do this based on the super device's erase
|
||||
@@ -459,8 +426,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
||||
|
||||
/* make a local copy of instr to avoid modifying the caller's struct */
|
||||
erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
|
||||
|
||||
@@ -499,10 +464,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
else
|
||||
erase->len = length;
|
||||
|
||||
if (!(subdev->flags & MTD_WRITEABLE)) {
|
||||
err = -EROFS;
|
||||
break;
|
||||
}
|
||||
length -= erase->len;
|
||||
if ((err = concat_dev_erase(subdev, erase))) {
|
||||
/* sanity check: should never happen since
|
||||
@@ -538,9 +499,6 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int i, err = -EINVAL;
|
||||
|
||||
if ((len + ofs) > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
uint64_t size;
|
||||
@@ -575,9 +533,6 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int i, err = 0;
|
||||
|
||||
if ((len + ofs) > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
uint64_t size;
|
||||
@@ -650,9 +605,6 @@ static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
|
||||
if (!mtd_can_have_bb(concat->subdev[0]))
|
||||
return res;
|
||||
|
||||
if (ofs > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
|
||||
@@ -673,12 +625,6 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int i, err = -EINVAL;
|
||||
|
||||
if (!mtd_can_have_bb(concat->subdev[0]))
|
||||
return 0;
|
||||
|
||||
if (ofs > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
|
||||
@@ -716,10 +662,6 @@ static unsigned long concat_get_unmapped_area(struct mtd_info *mtd,
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we've found the subdev over which the mapping will reside */
|
||||
if (offset + len > subdev->size)
|
||||
return (unsigned long) -EINVAL;
|
||||
|
||||
return mtd_get_unmapped_area(subdev, len, offset, flags);
|
||||
}
|
||||
|
||||
@@ -777,16 +719,16 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
concat->mtd.subpage_sft = subdev[0]->subpage_sft;
|
||||
concat->mtd.oobsize = subdev[0]->oobsize;
|
||||
concat->mtd.oobavail = subdev[0]->oobavail;
|
||||
if (subdev[0]->writev)
|
||||
concat->mtd.writev = concat_writev;
|
||||
if (subdev[0]->read_oob)
|
||||
concat->mtd.read_oob = concat_read_oob;
|
||||
if (subdev[0]->write_oob)
|
||||
concat->mtd.write_oob = concat_write_oob;
|
||||
if (subdev[0]->block_isbad)
|
||||
concat->mtd.block_isbad = concat_block_isbad;
|
||||
if (subdev[0]->block_markbad)
|
||||
concat->mtd.block_markbad = concat_block_markbad;
|
||||
if (subdev[0]->_writev)
|
||||
concat->mtd._writev = concat_writev;
|
||||
if (subdev[0]->_read_oob)
|
||||
concat->mtd._read_oob = concat_read_oob;
|
||||
if (subdev[0]->_write_oob)
|
||||
concat->mtd._write_oob = concat_write_oob;
|
||||
if (subdev[0]->_block_isbad)
|
||||
concat->mtd._block_isbad = concat_block_isbad;
|
||||
if (subdev[0]->_block_markbad)
|
||||
concat->mtd._block_markbad = concat_block_markbad;
|
||||
|
||||
concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
|
||||
|
||||
@@ -833,8 +775,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
if (concat->mtd.writesize != subdev[i]->writesize ||
|
||||
concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
|
||||
concat->mtd.oobsize != subdev[i]->oobsize ||
|
||||
!concat->mtd.read_oob != !subdev[i]->read_oob ||
|
||||
!concat->mtd.write_oob != !subdev[i]->write_oob) {
|
||||
!concat->mtd._read_oob != !subdev[i]->_read_oob ||
|
||||
!concat->mtd._write_oob != !subdev[i]->_write_oob) {
|
||||
kfree(concat);
|
||||
printk("Incompatible OOB or ECC data on \"%s\"\n",
|
||||
subdev[i]->name);
|
||||
@@ -849,15 +791,15 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
concat->num_subdev = num_devs;
|
||||
concat->mtd.name = name;
|
||||
|
||||
concat->mtd.erase = concat_erase;
|
||||
concat->mtd.read = concat_read;
|
||||
concat->mtd.write = concat_write;
|
||||
concat->mtd.sync = concat_sync;
|
||||
concat->mtd.lock = concat_lock;
|
||||
concat->mtd.unlock = concat_unlock;
|
||||
concat->mtd.suspend = concat_suspend;
|
||||
concat->mtd.resume = concat_resume;
|
||||
concat->mtd.get_unmapped_area = concat_get_unmapped_area;
|
||||
concat->mtd._erase = concat_erase;
|
||||
concat->mtd._read = concat_read;
|
||||
concat->mtd._write = concat_write;
|
||||
concat->mtd._sync = concat_sync;
|
||||
concat->mtd._lock = concat_lock;
|
||||
concat->mtd._unlock = concat_unlock;
|
||||
concat->mtd._suspend = concat_suspend;
|
||||
concat->mtd._resume = concat_resume;
|
||||
concat->mtd._get_unmapped_area = concat_get_unmapped_area;
|
||||
|
||||
/*
|
||||
* Combine the erase block size info of the subdevices:
|
||||
|
@@ -107,7 +107,7 @@ static LIST_HEAD(mtd_notifiers);
|
||||
*/
|
||||
static void mtd_release(struct device *dev)
|
||||
{
|
||||
struct mtd_info *mtd = dev_get_drvdata(dev);
|
||||
struct mtd_info __maybe_unused *mtd = dev_get_drvdata(dev);
|
||||
dev_t index = MTD_DEVT(mtd->index);
|
||||
|
||||
/* remove /dev/mtdXro node if needed */
|
||||
@@ -126,7 +126,7 @@ static int mtd_cls_resume(struct device *dev)
|
||||
{
|
||||
struct mtd_info *mtd = dev_get_drvdata(dev);
|
||||
|
||||
if (mtd && mtd->resume)
|
||||
if (mtd)
|
||||
mtd_resume(mtd);
|
||||
return 0;
|
||||
}
|
||||
@@ -610,8 +610,8 @@ int __get_mtd_device(struct mtd_info *mtd)
|
||||
if (!try_module_get(mtd->owner))
|
||||
return -ENODEV;
|
||||
|
||||
if (mtd->get_device) {
|
||||
err = mtd->get_device(mtd);
|
||||
if (mtd->_get_device) {
|
||||
err = mtd->_get_device(mtd);
|
||||
|
||||
if (err) {
|
||||
module_put(mtd->owner);
|
||||
@@ -675,13 +675,266 @@ void __put_mtd_device(struct mtd_info *mtd)
|
||||
--mtd->usecount;
|
||||
BUG_ON(mtd->usecount < 0);
|
||||
|
||||
if (mtd->put_device)
|
||||
mtd->put_device(mtd);
|
||||
if (mtd->_put_device)
|
||||
mtd->_put_device(mtd);
|
||||
|
||||
module_put(mtd->owner);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__put_mtd_device);
|
||||
|
||||
/*
|
||||
* Erase is an asynchronous operation. Device drivers are supposed
|
||||
* to call instr->callback() whenever the operation completes, even
|
||||
* if it completes with a failure.
|
||||
* Callers are supposed to pass a callback function and wait for it
|
||||
* to be called before writing to the block.
|
||||
*/
|
||||
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr)
|
||||
return -EINVAL;
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
||||
if (!instr->len) {
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
mtd_erase_callback(instr);
|
||||
return 0;
|
||||
}
|
||||
return mtd->_erase(mtd, instr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_erase);
|
||||
|
||||
/*
|
||||
* This stuff for eXecute-In-Place. phys is optional and may be set to NULL.
|
||||
*/
|
||||
int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
|
||||
void **virt, resource_size_t *phys)
|
||||
{
|
||||
*retlen = 0;
|
||||
*virt = NULL;
|
||||
if (phys)
|
||||
*phys = 0;
|
||||
if (!mtd->_point)
|
||||
return -EOPNOTSUPP;
|
||||
if (from < 0 || from > mtd->size || len > mtd->size - from)
|
||||
return -EINVAL;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_point(mtd, from, len, retlen, virt, phys);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_point);
|
||||
|
||||
/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
|
||||
int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
{
|
||||
if (!mtd->_point)
|
||||
return -EOPNOTSUPP;
|
||||
if (from < 0 || from > mtd->size || len > mtd->size - from)
|
||||
return -EINVAL;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_unpoint(mtd, from, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_unpoint);
|
||||
|
||||
/*
|
||||
* Allow NOMMU mmap() to directly map the device (if not NULL)
|
||||
* - return the address to which the offset maps
|
||||
* - return -ENOSYS to indicate refusal to do the mapping
|
||||
*/
|
||||
unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len,
|
||||
unsigned long offset, unsigned long flags)
|
||||
{
|
||||
if (!mtd->_get_unmapped_area)
|
||||
return -EOPNOTSUPP;
|
||||
if (offset > mtd->size || len > mtd->size - offset)
|
||||
return -EINVAL;
|
||||
return mtd->_get_unmapped_area(mtd, len, offset, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_get_unmapped_area);
|
||||
|
||||
int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
|
||||
u_char *buf)
|
||||
{
|
||||
*retlen = 0;
|
||||
if (from < 0 || from > mtd->size || len > mtd->size - from)
|
||||
return -EINVAL;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_read(mtd, from, len, retlen, buf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_read);
|
||||
|
||||
int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
|
||||
const u_char *buf)
|
||||
{
|
||||
*retlen = 0;
|
||||
if (to < 0 || to > mtd->size || len > mtd->size - to)
|
||||
return -EINVAL;
|
||||
if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_write(mtd, to, len, retlen, buf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_write);
|
||||
|
||||
/*
|
||||
* In blackbox flight recorder like scenarios we want to make successful writes
|
||||
* in interrupt context. panic_write() is only intended to be called when its
|
||||
* known the kernel is about to panic and we need the write to succeed. Since
|
||||
* the kernel is not going to be running for much longer, this function can
|
||||
* break locks and delay to ensure the write succeeds (but not sleep).
|
||||
*/
|
||||
int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
|
||||
const u_char *buf)
|
||||
{
|
||||
*retlen = 0;
|
||||
if (!mtd->_panic_write)
|
||||
return -EOPNOTSUPP;
|
||||
if (to < 0 || to > mtd->size || len > mtd->size - to)
|
||||
return -EINVAL;
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_panic_write(mtd, to, len, retlen, buf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_panic_write);
|
||||
|
||||
/*
|
||||
* Method to access the protection register area, present in some flash
|
||||
* devices. The user data is one time programmable but the factory data is read
|
||||
* only.
|
||||
*/
|
||||
int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len)
|
||||
{
|
||||
if (!mtd->_get_fact_prot_info)
|
||||
return -EOPNOTSUPP;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_get_fact_prot_info(mtd, buf, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info);
|
||||
|
||||
int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
*retlen = 0;
|
||||
if (!mtd->_read_fact_prot_reg)
|
||||
return -EOPNOTSUPP;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg);
|
||||
|
||||
int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len)
|
||||
{
|
||||
if (!mtd->_get_user_prot_info)
|
||||
return -EOPNOTSUPP;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_get_user_prot_info(mtd, buf, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_get_user_prot_info);
|
||||
|
||||
int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
*retlen = 0;
|
||||
if (!mtd->_read_user_prot_reg)
|
||||
return -EOPNOTSUPP;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg);
|
||||
|
||||
int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
*retlen = 0;
|
||||
if (!mtd->_write_user_prot_reg)
|
||||
return -EOPNOTSUPP;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg);
|
||||
|
||||
int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
{
|
||||
if (!mtd->_lock_user_prot_reg)
|
||||
return -EOPNOTSUPP;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_lock_user_prot_reg(mtd, from, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg);
|
||||
|
||||
/* Chip-supported device locking */
|
||||
int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
if (!mtd->_lock)
|
||||
return -EOPNOTSUPP;
|
||||
if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
|
||||
return -EINVAL;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_lock(mtd, ofs, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_lock);
|
||||
|
||||
int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
if (!mtd->_unlock)
|
||||
return -EOPNOTSUPP;
|
||||
if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
|
||||
return -EINVAL;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_unlock(mtd, ofs, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_unlock);
|
||||
|
||||
int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
if (!mtd->_is_locked)
|
||||
return -EOPNOTSUPP;
|
||||
if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
|
||||
return -EINVAL;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_is_locked(mtd, ofs, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_is_locked);
|
||||
|
||||
int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
if (!mtd->_block_isbad)
|
||||
return 0;
|
||||
if (ofs < 0 || ofs > mtd->size)
|
||||
return -EINVAL;
|
||||
return mtd->_block_isbad(mtd, ofs);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_block_isbad);
|
||||
|
||||
int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
if (!mtd->_block_markbad)
|
||||
return -EOPNOTSUPP;
|
||||
if (ofs < 0 || ofs > mtd->size)
|
||||
return -EINVAL;
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
return mtd->_block_markbad(mtd, ofs);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_block_markbad);
|
||||
|
||||
/*
|
||||
* default_mtd_writev - the default writev method
|
||||
* @mtd: mtd device description object pointer
|
||||
@@ -729,9 +982,11 @@ int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen)
|
||||
{
|
||||
*retlen = 0;
|
||||
if (!mtd->writev)
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
if (!mtd->_writev)
|
||||
return default_mtd_writev(mtd, vecs, count, to, retlen);
|
||||
return mtd->writev(mtd, vecs, count, to, retlen);
|
||||
return mtd->_writev(mtd, vecs, count, to, retlen);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_writev);
|
||||
|
||||
|
@@ -169,7 +169,7 @@ static void mtdoops_workfunc_erase(struct work_struct *work)
|
||||
cxt->nextpage = 0;
|
||||
}
|
||||
|
||||
while (mtd_can_have_bb(mtd)) {
|
||||
while (1) {
|
||||
ret = mtd_block_isbad(mtd, cxt->nextpage * record_size);
|
||||
if (!ret)
|
||||
break;
|
||||
@@ -199,9 +199,9 @@ badblock:
|
||||
return;
|
||||
}
|
||||
|
||||
if (mtd_can_have_bb(mtd) && ret == -EIO) {
|
||||
if (ret == -EIO) {
|
||||
ret = mtd_block_markbad(mtd, cxt->nextpage * record_size);
|
||||
if (ret < 0) {
|
||||
if (ret < 0 && ret != -EOPNOTSUPP) {
|
||||
printk(KERN_ERR "mtdoops: block_markbad failed, aborting\n");
|
||||
return;
|
||||
}
|
||||
@@ -257,8 +257,7 @@ static void find_next_position(struct mtdoops_context *cxt)
|
||||
size_t retlen;
|
||||
|
||||
for (page = 0; page < cxt->oops_pages; page++) {
|
||||
if (mtd_can_have_bb(mtd) &&
|
||||
mtd_block_isbad(mtd, page * record_size))
|
||||
if (mtd_block_isbad(mtd, page * record_size))
|
||||
continue;
|
||||
/* Assume the page is used */
|
||||
mark_page_used(cxt, page);
|
||||
|
@@ -65,12 +65,8 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
int res;
|
||||
|
||||
stats = part->master->ecc_stats;
|
||||
|
||||
if (from >= mtd->size)
|
||||
len = 0;
|
||||
else if (from + len > mtd->size)
|
||||
len = mtd->size - from;
|
||||
res = mtd_read(part->master, from + part->offset, len, retlen, buf);
|
||||
res = part->master->_read(part->master, from + part->offset, len,
|
||||
retlen, buf);
|
||||
if (unlikely(res)) {
|
||||
if (mtd_is_bitflip(res))
|
||||
mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
|
||||
@@ -84,19 +80,16 @@ static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, void **virt, resource_size_t *phys)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (from >= mtd->size)
|
||||
len = 0;
|
||||
else if (from + len > mtd->size)
|
||||
len = mtd->size - from;
|
||||
return mtd_point(part->master, from + part->offset, len, retlen,
|
||||
virt, phys);
|
||||
|
||||
return part->master->_point(part->master, from + part->offset, len,
|
||||
retlen, virt, phys);
|
||||
}
|
||||
|
||||
static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
|
||||
mtd_unpoint(part->master, from + part->offset, len);
|
||||
return part->master->_unpoint(part->master, from + part->offset, len);
|
||||
}
|
||||
|
||||
static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
|
||||
@@ -107,7 +100,8 @@ static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
|
||||
struct mtd_part *part = PART(mtd);
|
||||
|
||||
offset += part->offset;
|
||||
return mtd_get_unmapped_area(part->master, len, offset, flags);
|
||||
return part->master->_get_unmapped_area(part->master, len, offset,
|
||||
flags);
|
||||
}
|
||||
|
||||
static int part_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
@@ -138,7 +132,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = mtd_read_oob(part->master, from + part->offset, ops);
|
||||
res = part->master->_read_oob(part->master, from + part->offset, ops);
|
||||
if (unlikely(res)) {
|
||||
if (mtd_is_bitflip(res))
|
||||
mtd->ecc_stats.corrected++;
|
||||
@@ -152,55 +146,46 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_read_user_prot_reg(part->master, from, len, retlen, buf);
|
||||
return part->master->_read_user_prot_reg(part->master, from, len,
|
||||
retlen, buf);
|
||||
}
|
||||
|
||||
static int part_get_user_prot_info(struct mtd_info *mtd,
|
||||
struct otp_info *buf, size_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_get_user_prot_info(part->master, buf, len);
|
||||
return part->master->_get_user_prot_info(part->master, buf, len);
|
||||
}
|
||||
|
||||
static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_read_fact_prot_reg(part->master, from, len, retlen, buf);
|
||||
return part->master->_read_fact_prot_reg(part->master, from, len,
|
||||
retlen, buf);
|
||||
}
|
||||
|
||||
static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_get_fact_prot_info(part->master, buf, len);
|
||||
return part->master->_get_fact_prot_info(part->master, buf, len);
|
||||
}
|
||||
|
||||
static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
if (to >= mtd->size)
|
||||
len = 0;
|
||||
else if (to + len > mtd->size)
|
||||
len = mtd->size - to;
|
||||
return mtd_write(part->master, to + part->offset, len, retlen, buf);
|
||||
return part->master->_write(part->master, to + part->offset, len,
|
||||
retlen, buf);
|
||||
}
|
||||
|
||||
static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
if (to >= mtd->size)
|
||||
len = 0;
|
||||
else if (to + len > mtd->size)
|
||||
len = mtd->size - to;
|
||||
return mtd_panic_write(part->master, to + part->offset, len, retlen,
|
||||
buf);
|
||||
return part->master->_panic_write(part->master, to + part->offset, len,
|
||||
retlen, buf);
|
||||
}
|
||||
|
||||
static int part_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
@@ -208,50 +193,43 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
|
||||
if (to >= mtd->size)
|
||||
return -EINVAL;
|
||||
if (ops->datbuf && to + ops->len > mtd->size)
|
||||
return -EINVAL;
|
||||
return mtd_write_oob(part->master, to + part->offset, ops);
|
||||
return part->master->_write_oob(part->master, to + part->offset, ops);
|
||||
}
|
||||
|
||||
static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_write_user_prot_reg(part->master, from, len, retlen, buf);
|
||||
return part->master->_write_user_prot_reg(part->master, from, len,
|
||||
retlen, buf);
|
||||
}
|
||||
|
||||
static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_lock_user_prot_reg(part->master, from, len);
|
||||
return part->master->_lock_user_prot_reg(part->master, from, len);
|
||||
}
|
||||
|
||||
static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
return mtd_writev(part->master, vecs, count, to + part->offset,
|
||||
retlen);
|
||||
return part->master->_writev(part->master, vecs, count,
|
||||
to + part->offset, retlen);
|
||||
}
|
||||
|
||||
static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
int ret;
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
if (instr->addr >= mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
instr->addr += part->offset;
|
||||
ret = mtd_erase(part->master, instr);
|
||||
ret = part->master->_erase(part->master, instr);
|
||||
if (ret) {
|
||||
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
|
||||
instr->fail_addr -= part->offset;
|
||||
@@ -262,7 +240,7 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
|
||||
void mtd_erase_callback(struct erase_info *instr)
|
||||
{
|
||||
if (instr->mtd->erase == part_erase) {
|
||||
if (instr->mtd->_erase == part_erase) {
|
||||
struct mtd_part *part = PART(instr->mtd);
|
||||
|
||||
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
|
||||
@@ -277,52 +255,44 @@ EXPORT_SYMBOL_GPL(mtd_erase_callback);
|
||||
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if ((len + ofs) > mtd->size)
|
||||
return -EINVAL;
|
||||
return mtd_lock(part->master, ofs + part->offset, len);
|
||||
return part->master->_lock(part->master, ofs + part->offset, len);
|
||||
}
|
||||
|
||||
static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if ((len + ofs) > mtd->size)
|
||||
return -EINVAL;
|
||||
return mtd_unlock(part->master, ofs + part->offset, len);
|
||||
return part->master->_unlock(part->master, ofs + part->offset, len);
|
||||
}
|
||||
|
||||
static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if ((len + ofs) > mtd->size)
|
||||
return -EINVAL;
|
||||
return mtd_is_locked(part->master, ofs + part->offset, len);
|
||||
return part->master->_is_locked(part->master, ofs + part->offset, len);
|
||||
}
|
||||
|
||||
static void part_sync(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
mtd_sync(part->master);
|
||||
part->master->_sync(part->master);
|
||||
}
|
||||
|
||||
static int part_suspend(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return mtd_suspend(part->master);
|
||||
return part->master->_suspend(part->master);
|
||||
}
|
||||
|
||||
static void part_resume(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
mtd_resume(part->master);
|
||||
part->master->_resume(part->master);
|
||||
}
|
||||
|
||||
static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (ofs >= mtd->size)
|
||||
return -EINVAL;
|
||||
ofs += part->offset;
|
||||
return mtd_block_isbad(part->master, ofs);
|
||||
return part->master->_block_isbad(part->master, ofs);
|
||||
}
|
||||
|
||||
static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
@@ -330,12 +300,8 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
struct mtd_part *part = PART(mtd);
|
||||
int res;
|
||||
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
if (ofs >= mtd->size)
|
||||
return -EINVAL;
|
||||
ofs += part->offset;
|
||||
res = mtd_block_markbad(part->master, ofs);
|
||||
res = part->master->_block_markbad(part->master, ofs);
|
||||
if (!res)
|
||||
mtd->ecc_stats.badblocks++;
|
||||
return res;
|
||||
@@ -410,54 +376,55 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
|
||||
*/
|
||||
slave->mtd.dev.parent = master->dev.parent;
|
||||
|
||||
slave->mtd.read = part_read;
|
||||
slave->mtd.write = part_write;
|
||||
slave->mtd._read = part_read;
|
||||
slave->mtd._write = part_write;
|
||||
|
||||
if (master->panic_write)
|
||||
slave->mtd.panic_write = part_panic_write;
|
||||
if (master->_panic_write)
|
||||
slave->mtd._panic_write = part_panic_write;
|
||||
|
||||
if (master->point && master->unpoint) {
|
||||
slave->mtd.point = part_point;
|
||||
slave->mtd.unpoint = part_unpoint;
|
||||
if (master->_point && master->_unpoint) {
|
||||
slave->mtd._point = part_point;
|
||||
slave->mtd._unpoint = part_unpoint;
|
||||
}
|
||||
|
||||
if (master->get_unmapped_area)
|
||||
slave->mtd.get_unmapped_area = part_get_unmapped_area;
|
||||
if (master->read_oob)
|
||||
slave->mtd.read_oob = part_read_oob;
|
||||
if (master->write_oob)
|
||||
slave->mtd.write_oob = part_write_oob;
|
||||
if (master->read_user_prot_reg)
|
||||
slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
|
||||
if (master->read_fact_prot_reg)
|
||||
slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
|
||||
if (master->write_user_prot_reg)
|
||||
slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
|
||||
if (master->lock_user_prot_reg)
|
||||
slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
|
||||
if (master->get_user_prot_info)
|
||||
slave->mtd.get_user_prot_info = part_get_user_prot_info;
|
||||
if (master->get_fact_prot_info)
|
||||
slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
|
||||
if (master->sync)
|
||||
slave->mtd.sync = part_sync;
|
||||
if (!partno && !master->dev.class && master->suspend && master->resume) {
|
||||
slave->mtd.suspend = part_suspend;
|
||||
slave->mtd.resume = part_resume;
|
||||
if (master->_get_unmapped_area)
|
||||
slave->mtd._get_unmapped_area = part_get_unmapped_area;
|
||||
if (master->_read_oob)
|
||||
slave->mtd._read_oob = part_read_oob;
|
||||
if (master->_write_oob)
|
||||
slave->mtd._write_oob = part_write_oob;
|
||||
if (master->_read_user_prot_reg)
|
||||
slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
|
||||
if (master->_read_fact_prot_reg)
|
||||
slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
|
||||
if (master->_write_user_prot_reg)
|
||||
slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
|
||||
if (master->_lock_user_prot_reg)
|
||||
slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
|
||||
if (master->_get_user_prot_info)
|
||||
slave->mtd._get_user_prot_info = part_get_user_prot_info;
|
||||
if (master->_get_fact_prot_info)
|
||||
slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
|
||||
if (master->_sync)
|
||||
slave->mtd._sync = part_sync;
|
||||
if (!partno && !master->dev.class && master->_suspend &&
|
||||
master->_resume) {
|
||||
slave->mtd._suspend = part_suspend;
|
||||
slave->mtd._resume = part_resume;
|
||||
}
|
||||
if (master->writev)
|
||||
slave->mtd.writev = part_writev;
|
||||
if (master->lock)
|
||||
slave->mtd.lock = part_lock;
|
||||
if (master->unlock)
|
||||
slave->mtd.unlock = part_unlock;
|
||||
if (master->is_locked)
|
||||
slave->mtd.is_locked = part_is_locked;
|
||||
if (master->block_isbad)
|
||||
slave->mtd.block_isbad = part_block_isbad;
|
||||
if (master->block_markbad)
|
||||
slave->mtd.block_markbad = part_block_markbad;
|
||||
slave->mtd.erase = part_erase;
|
||||
if (master->_writev)
|
||||
slave->mtd._writev = part_writev;
|
||||
if (master->_lock)
|
||||
slave->mtd._lock = part_lock;
|
||||
if (master->_unlock)
|
||||
slave->mtd._unlock = part_unlock;
|
||||
if (master->_is_locked)
|
||||
slave->mtd._is_locked = part_is_locked;
|
||||
if (master->_block_isbad)
|
||||
slave->mtd._block_isbad = part_block_isbad;
|
||||
if (master->_block_markbad)
|
||||
slave->mtd._block_markbad = part_block_markbad;
|
||||
slave->mtd._erase = part_erase;
|
||||
slave->master = master;
|
||||
slave->offset = part->offset;
|
||||
|
||||
@@ -549,7 +516,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
|
||||
}
|
||||
|
||||
slave->mtd.ecclayout = master->ecclayout;
|
||||
if (master->block_isbad) {
|
||||
slave->mtd.ecc_strength = master->ecc_strength;
|
||||
if (master->_block_isbad) {
|
||||
uint64_t offs = 0;
|
||||
|
||||
while (offs < slave->mtd.size) {
|
||||
@@ -761,7 +729,7 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types,
|
||||
for ( ; ret <= 0 && *types; types++) {
|
||||
parser = get_partition_parser(*types);
|
||||
if (!parser && !request_module("%s", *types))
|
||||
parser = get_partition_parser(*types);
|
||||
parser = get_partition_parser(*types);
|
||||
if (!parser)
|
||||
continue;
|
||||
ret = (*parser->parse_fn)(master, pparts, data);
|
||||
|
@@ -314,6 +314,26 @@ config MTD_NAND_DISKONCHIP_BBTWRITE
|
||||
load time (assuming you build diskonchip as a module) with the module
|
||||
parameter "inftl_bbt_write=1".
|
||||
|
||||
config MTD_NAND_DOCG4
|
||||
tristate "Support for DiskOnChip G4 (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL
|
||||
select BCH
|
||||
select BITREVERSE
|
||||
help
|
||||
Support for diskonchip G4 nand flash, found in various smartphones and
|
||||
PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba
|
||||
Portege G900, Asus P526, and O2 XDA Zinc.
|
||||
|
||||
With this driver you will be able to use UBI and create a ubifs on the
|
||||
device, so you may wish to consider enabling UBI and UBIFS as well.
|
||||
|
||||
These devices ship with the Mys/Sandisk SAFTL formatting, for which
|
||||
there is currently no mtd parser, so you may want to use command line
|
||||
partitioning to segregate write-protected blocks. On the Treo680, the
|
||||
first five erase blocks (256KiB each) are write-protected, followed
|
||||
by the block containing the saftl partition table. This is probably
|
||||
typical.
|
||||
|
||||
config MTD_NAND_SHARPSL
|
||||
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
|
||||
depends on ARCH_PXA
|
||||
@@ -421,7 +441,6 @@ config MTD_NAND_NANDSIM
|
||||
config MTD_NAND_GPMI_NAND
|
||||
bool "GPMI NAND Flash Controller driver"
|
||||
depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28)
|
||||
select MTD_CMDLINE_PARTS
|
||||
help
|
||||
Enables NAND Flash support for IMX23 or IMX28.
|
||||
The GPMI controller is very powerful, with the help of BCH
|
||||
|
@@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
|
||||
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
|
||||
obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
|
||||
obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o
|
||||
obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_H1900) += h1910.o
|
||||
obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
|
||||
|
@@ -585,12 +585,13 @@ static int alauda_init_media(struct alauda *al)
|
||||
mtd->writesize = 1<<card->pageshift;
|
||||
mtd->type = MTD_NANDFLASH;
|
||||
mtd->flags = MTD_CAP_NANDFLASH;
|
||||
mtd->read = alauda_read;
|
||||
mtd->write = alauda_write;
|
||||
mtd->erase = alauda_erase;
|
||||
mtd->block_isbad = alauda_isbad;
|
||||
mtd->_read = alauda_read;
|
||||
mtd->_write = alauda_write;
|
||||
mtd->_erase = alauda_erase;
|
||||
mtd->_block_isbad = alauda_isbad;
|
||||
mtd->priv = al;
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->ecc_strength = 1;
|
||||
|
||||
err = mtd_device_register(mtd, NULL, 0);
|
||||
if (err) {
|
||||
|
@@ -603,6 +603,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
|
||||
nand_chip->ecc.hwctl = atmel_nand_hwctl;
|
||||
nand_chip->ecc.read_page = atmel_nand_read_page;
|
||||
nand_chip->ecc.bytes = 4;
|
||||
nand_chip->ecc.strength = 1;
|
||||
}
|
||||
|
||||
nand_chip->chip_delay = 20; /* 20us command delay time */
|
||||
|
@@ -475,6 +475,14 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
|
||||
largepage_bbt.options = NAND_BBT_SCAN2NDPAGE;
|
||||
this->badblock_pattern = &largepage_bbt;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: ecc strength value of 6 bits per 512 bytes of data is a
|
||||
* conservative guess, given 13 ecc bytes and using bch alg.
|
||||
* (Assume Galois field order m=15 to allow a margin of error.)
|
||||
*/
|
||||
this->ecc.strength = 6;
|
||||
|
||||
#endif
|
||||
|
||||
/* Now finish off the scan, now that ecc.layout has been initialized. */
|
||||
@@ -487,7 +495,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
|
||||
|
||||
/* Register the partitions */
|
||||
board_mtd->name = "bcm_umi-nand";
|
||||
mtd_device_parse_register(board_mtd, NULL, 0, NULL, 0);
|
||||
mtd_device_parse_register(board_mtd, NULL, NULL, NULL, 0);
|
||||
|
||||
/* Return happy */
|
||||
return 0;
|
||||
|
@@ -702,9 +702,11 @@ static int bf5xx_nand_scan(struct mtd_info *mtd)
|
||||
if (likely(mtd->writesize >= 512)) {
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 6;
|
||||
chip->ecc.strength = 2;
|
||||
} else {
|
||||
chip->ecc.size = 256;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.strength = 1;
|
||||
bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET));
|
||||
SSYNC();
|
||||
}
|
||||
|
@@ -783,6 +783,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
|
||||
cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
cafe->nand.ecc.size = mtd->writesize;
|
||||
cafe->nand.ecc.bytes = 14;
|
||||
cafe->nand.ecc.strength = 4;
|
||||
cafe->nand.ecc.hwctl = (void *)cafe_nand_bug;
|
||||
cafe->nand.ecc.calculate = (void *)cafe_nand_bug;
|
||||
cafe->nand.ecc.correct = (void *)cafe_nand_bug;
|
||||
@@ -799,7 +800,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
|
||||
pci_set_drvdata(pdev, mtd);
|
||||
|
||||
mtd->name = "cafe_nand";
|
||||
mtd_device_parse_register(mtd, part_probes, 0, NULL, 0);
|
||||
mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
|
||||
|
||||
goto out;
|
||||
|
||||
|
@@ -219,7 +219,7 @@ static int __init cmx270_init(void)
|
||||
}
|
||||
|
||||
/* Register the partitions */
|
||||
ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, 0,
|
||||
ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL,
|
||||
partition_info, NUM_PARTITIONS);
|
||||
if (ret)
|
||||
goto err_scan;
|
||||
|
@@ -248,6 +248,8 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
|
||||
goto out_ior;
|
||||
}
|
||||
|
||||
this->ecc.strength = 1;
|
||||
|
||||
new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
|
||||
|
||||
cs553x_mtd[cs] = new_mtd;
|
||||
@@ -313,7 +315,7 @@ static int __init cs553x_init(void)
|
||||
for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
|
||||
if (cs553x_mtd[i]) {
|
||||
/* If any devices registered, return success. Else the last error. */
|
||||
mtd_device_parse_register(cs553x_mtd[i], NULL, 0,
|
||||
mtd_device_parse_register(cs553x_mtd[i], NULL, NULL,
|
||||
NULL, 0);
|
||||
err = 0;
|
||||
}
|
||||
|
@@ -641,6 +641,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||
info->chip.ecc.bytes = 3;
|
||||
}
|
||||
info->chip.ecc.size = 512;
|
||||
info->chip.ecc.strength = pdata->ecc_bits;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
@@ -752,8 +753,8 @@ syndrome_done:
|
||||
if (ret < 0)
|
||||
goto err_scan;
|
||||
|
||||
ret = mtd_device_parse_register(&info->mtd, NULL, 0,
|
||||
pdata->parts, pdata->nr_parts);
|
||||
ret = mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts,
|
||||
pdata->nr_parts);
|
||||
|
||||
if (ret < 0)
|
||||
goto err_scan;
|
||||
|
@@ -1590,6 +1590,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
ECC_15BITS * (denali->mtd.writesize /
|
||||
ECC_SECTOR_SIZE)))) {
|
||||
/* if MLC OOB size is large enough, use 15bit ECC*/
|
||||
denali->nand.ecc.strength = 15;
|
||||
denali->nand.ecc.layout = &nand_15bit_oob;
|
||||
denali->nand.ecc.bytes = ECC_15BITS;
|
||||
iowrite32(15, denali->flash_reg + ECC_CORRECTION);
|
||||
@@ -1600,12 +1601,14 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
" contain 8bit ECC correction codes");
|
||||
goto failed_req_irq;
|
||||
} else {
|
||||
denali->nand.ecc.strength = 8;
|
||||
denali->nand.ecc.layout = &nand_8bit_oob;
|
||||
denali->nand.ecc.bytes = ECC_8BITS;
|
||||
iowrite32(8, denali->flash_reg + ECC_CORRECTION);
|
||||
}
|
||||
|
||||
denali->nand.ecc.bytes *= denali->devnum;
|
||||
denali->nand.ecc.strength *= denali->devnum;
|
||||
denali->nand.ecc.layout->eccbytes *=
|
||||
denali->mtd.writesize / ECC_SECTOR_SIZE;
|
||||
denali->nand.ecc.layout->oobfree[0].offset =
|
||||
|
@@ -1653,6 +1653,7 @@ static int __init doc_probe(unsigned long physadr)
|
||||
nand->ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
nand->ecc.size = 512;
|
||||
nand->ecc.bytes = 6;
|
||||
nand->ecc.strength = 2;
|
||||
nand->bbt_options = NAND_BBT_USE_FLASH;
|
||||
|
||||
doc->physadr = physadr;
|
||||
|
1377
drivers/mtd/nand/docg4.c
Normal file
1377
drivers/mtd/nand/docg4.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -813,6 +813,12 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
|
||||
&fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.strength = 1;
|
||||
/*
|
||||
* FIXME: can hardware ecc correct 4 bitflips if page size is
|
||||
* 2k? Then does hardware report number of corrections for this
|
||||
* case? If so, ecc_stats reporting needs to be fixed as well.
|
||||
*/
|
||||
} else {
|
||||
/* otherwise fall back to default software ECC */
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -848,7 +848,10 @@ int gpmi_send_command(struct gpmi_nand_data *this)
|
||||
|
||||
sg_init_one(sgl, this->cmd_buffer, this->command_length);
|
||||
dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE);
|
||||
desc = dmaengine_prep_slave_sg(channel, sgl, 1, DMA_MEM_TO_DEV, 1);
|
||||
desc = dmaengine_prep_slave_sg(channel,
|
||||
sgl, 1, DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
|
||||
if (!desc) {
|
||||
pr_err("step 2 error\n");
|
||||
return -1;
|
||||
@@ -889,7 +892,8 @@ int gpmi_send_data(struct gpmi_nand_data *this)
|
||||
/* [2] send DMA request */
|
||||
prepare_data_dma(this, DMA_TO_DEVICE);
|
||||
desc = dmaengine_prep_slave_sg(channel, &this->data_sgl,
|
||||
1, DMA_MEM_TO_DEV, 1);
|
||||
1, DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
pr_err("step 2 error\n");
|
||||
return -1;
|
||||
@@ -925,7 +929,8 @@ int gpmi_read_data(struct gpmi_nand_data *this)
|
||||
/* [2] : send DMA request */
|
||||
prepare_data_dma(this, DMA_FROM_DEVICE);
|
||||
desc = dmaengine_prep_slave_sg(channel, &this->data_sgl,
|
||||
1, DMA_DEV_TO_MEM, 1);
|
||||
1, DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
pr_err("step 2 error\n");
|
||||
return -1;
|
||||
@@ -970,8 +975,10 @@ int gpmi_send_page(struct gpmi_nand_data *this,
|
||||
pio[4] = payload;
|
||||
pio[5] = auxiliary;
|
||||
|
||||
desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio,
|
||||
ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
|
||||
desc = dmaengine_prep_slave_sg(channel,
|
||||
(struct scatterlist *)pio,
|
||||
ARRAY_SIZE(pio), DMA_TRANS_NONE,
|
||||
DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
pr_err("step 2 error\n");
|
||||
return -1;
|
||||
@@ -1035,7 +1042,8 @@ int gpmi_read_page(struct gpmi_nand_data *this,
|
||||
pio[5] = auxiliary;
|
||||
desc = dmaengine_prep_slave_sg(channel,
|
||||
(struct scatterlist *)pio,
|
||||
ARRAY_SIZE(pio), DMA_TRANS_NONE, 1);
|
||||
ARRAY_SIZE(pio), DMA_TRANS_NONE,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
pr_err("step 2 error\n");
|
||||
return -1;
|
||||
@@ -1052,9 +1060,11 @@ int gpmi_read_page(struct gpmi_nand_data *this,
|
||||
| BF_GPMI_CTRL0_ADDRESS(address)
|
||||
| BF_GPMI_CTRL0_XFER_COUNT(geo->page_size);
|
||||
pio[1] = 0;
|
||||
pio[2] = 0; /* clear GPMI_HW_GPMI_ECCCTRL, disable the BCH. */
|
||||
desc = dmaengine_prep_slave_sg(channel,
|
||||
(struct scatterlist *)pio, 2,
|
||||
DMA_TRANS_NONE, 1);
|
||||
(struct scatterlist *)pio, 3,
|
||||
DMA_TRANS_NONE,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
pr_err("step 3 error\n");
|
||||
return -1;
|
||||
|
@@ -1124,7 +1124,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
|
||||
|
||||
/* Do we have a flash based bad block table ? */
|
||||
if (chip->options & NAND_BBT_USE_FLASH)
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||
ret = nand_update_bbt(mtd, ofs);
|
||||
else {
|
||||
chipnr = (int)(ofs >> chip->chip_shift);
|
||||
@@ -1155,7 +1155,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit nand_boot_set_geometry(struct gpmi_nand_data *this)
|
||||
static int nand_boot_set_geometry(struct gpmi_nand_data *this)
|
||||
{
|
||||
struct boot_rom_geometry *geometry = &this->rom_geometry;
|
||||
|
||||
@@ -1182,7 +1182,7 @@ static int __devinit nand_boot_set_geometry(struct gpmi_nand_data *this)
|
||||
}
|
||||
|
||||
static const char *fingerprint = "STMP";
|
||||
static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this)
|
||||
static int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
|
||||
{
|
||||
struct boot_rom_geometry *rom_geo = &this->rom_geometry;
|
||||
struct device *dev = this->dev;
|
||||
@@ -1239,7 +1239,7 @@ static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this)
|
||||
}
|
||||
|
||||
/* Writes a transcription stamp. */
|
||||
static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this)
|
||||
static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
|
||||
{
|
||||
struct device *dev = this->dev;
|
||||
struct boot_rom_geometry *rom_geo = &this->rom_geometry;
|
||||
@@ -1322,7 +1322,7 @@ static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit mx23_boot_init(struct gpmi_nand_data *this)
|
||||
static int mx23_boot_init(struct gpmi_nand_data *this)
|
||||
{
|
||||
struct device *dev = this->dev;
|
||||
struct nand_chip *chip = &this->nand;
|
||||
@@ -1391,7 +1391,7 @@ static int __devinit mx23_boot_init(struct gpmi_nand_data *this)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit nand_boot_init(struct gpmi_nand_data *this)
|
||||
static int nand_boot_init(struct gpmi_nand_data *this)
|
||||
{
|
||||
nand_boot_set_geometry(this);
|
||||
|
||||
@@ -1401,7 +1401,7 @@ static int __devinit nand_boot_init(struct gpmi_nand_data *this)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit gpmi_set_geometry(struct gpmi_nand_data *this)
|
||||
static int gpmi_set_geometry(struct gpmi_nand_data *this)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@@ -20,7 +20,7 @@
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <mach/dma.h>
|
||||
#include <linux/fsl/mxs-dma.h>
|
||||
|
||||
struct resources {
|
||||
void *gpmi_regs;
|
||||
|
@@ -135,8 +135,8 @@ static int __init h1910_init(void)
|
||||
}
|
||||
|
||||
/* Register the partitions */
|
||||
mtd_device_parse_register(h1910_nand_mtd, NULL, 0,
|
||||
partition_info, NUM_PARTITIONS);
|
||||
mtd_device_parse_register(h1910_nand_mtd, NULL, NULL, partition_info,
|
||||
NUM_PARTITIONS);
|
||||
|
||||
/* Return happy */
|
||||
return 0;
|
||||
|
@@ -332,6 +332,11 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
|
||||
chip->ecc.mode = NAND_ECC_HW_OOB_FIRST;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 9;
|
||||
chip->ecc.strength = 2;
|
||||
/*
|
||||
* FIXME: ecc_strength value of 2 bits per 512 bytes of data is a
|
||||
* conservative guess, given 9 ecc bytes and reed-solomon alg.
|
||||
*/
|
||||
|
||||
if (pdata)
|
||||
chip->ecc.layout = pdata->ecc_layout;
|
||||
@@ -367,9 +372,9 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
|
||||
goto err_gpio_free;
|
||||
}
|
||||
|
||||
ret = mtd_device_parse_register(mtd, NULL, 0,
|
||||
pdata ? pdata->partitions : NULL,
|
||||
pdata ? pdata->num_partitions : 0);
|
||||
ret = mtd_device_parse_register(mtd, NULL, NULL,
|
||||
pdata ? pdata->partitions : NULL,
|
||||
pdata ? pdata->num_partitions : 0);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to add mtd device\n");
|
||||
|
@@ -1225,9 +1225,16 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
||||
goto escan;
|
||||
}
|
||||
|
||||
if (this->ecc.mode == NAND_ECC_HW) {
|
||||
if (nfc_is_v1())
|
||||
this->ecc.strength = 1;
|
||||
else
|
||||
this->ecc.strength = (host->eccsize == 4) ? 4 : 8;
|
||||
}
|
||||
|
||||
/* Register the partitions */
|
||||
mtd_device_parse_register(mtd, part_probes, 0,
|
||||
pdata->parts, pdata->nr_parts);
|
||||
mtd_device_parse_register(mtd, part_probes, NULL, pdata->parts,
|
||||
pdata->nr_parts);
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
|
@@ -123,12 +123,6 @@ static int check_offs_len(struct mtd_info *mtd,
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
/* Do not allow past end of device */
|
||||
if (ofs + len > mtd->size) {
|
||||
pr_debug("%s: past end of device\n", __func__);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -338,7 +332,7 @@ static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||
*/
|
||||
static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
|
||||
{
|
||||
int page, chipnr, res = 0;
|
||||
int page, chipnr, res = 0, i = 0;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
u16 bad;
|
||||
|
||||
@@ -356,23 +350,29 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
|
||||
chip->select_chip(mtd, chipnr);
|
||||
}
|
||||
|
||||
if (chip->options & NAND_BUSWIDTH_16) {
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE,
|
||||
page);
|
||||
bad = cpu_to_le16(chip->read_word(mtd));
|
||||
if (chip->badblockpos & 0x1)
|
||||
bad >>= 8;
|
||||
else
|
||||
bad &= 0xFF;
|
||||
} else {
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
|
||||
bad = chip->read_byte(mtd);
|
||||
}
|
||||
do {
|
||||
if (chip->options & NAND_BUSWIDTH_16) {
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB,
|
||||
chip->badblockpos & 0xFE, page);
|
||||
bad = cpu_to_le16(chip->read_word(mtd));
|
||||
if (chip->badblockpos & 0x1)
|
||||
bad >>= 8;
|
||||
else
|
||||
bad &= 0xFF;
|
||||
} else {
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos,
|
||||
page);
|
||||
bad = chip->read_byte(mtd);
|
||||
}
|
||||
|
||||
if (likely(chip->badblockbits == 8))
|
||||
res = bad != 0xFF;
|
||||
else
|
||||
res = hweight8(bad) < chip->badblockbits;
|
||||
if (likely(chip->badblockbits == 8))
|
||||
res = bad != 0xFF;
|
||||
else
|
||||
res = hweight8(bad) < chip->badblockbits;
|
||||
ofs += mtd->writesize;
|
||||
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
|
||||
i++;
|
||||
} while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
|
||||
|
||||
if (getchip)
|
||||
nand_release_device(mtd);
|
||||
@@ -386,51 +386,79 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
|
||||
* @ofs: offset from device start
|
||||
*
|
||||
* This is the default implementation, which can be overridden by a hardware
|
||||
* specific driver.
|
||||
* specific driver. We try operations in the following order, according to our
|
||||
* bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH):
|
||||
* (1) erase the affected block, to allow OOB marker to be written cleanly
|
||||
* (2) update in-memory BBT
|
||||
* (3) write bad block marker to OOB area of affected block
|
||||
* (4) update flash-based BBT
|
||||
* Note that we retain the first error encountered in (3) or (4), finish the
|
||||
* procedures, and dump the error in the end.
|
||||
*/
|
||||
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
uint8_t buf[2] = { 0, 0 };
|
||||
int block, ret, i = 0;
|
||||
int block, res, ret = 0, i = 0;
|
||||
int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM);
|
||||
|
||||
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
|
||||
ofs += mtd->erasesize - mtd->writesize;
|
||||
if (write_oob) {
|
||||
struct erase_info einfo;
|
||||
|
||||
/* Attempt erase before marking OOB */
|
||||
memset(&einfo, 0, sizeof(einfo));
|
||||
einfo.mtd = mtd;
|
||||
einfo.addr = ofs;
|
||||
einfo.len = 1 << chip->phys_erase_shift;
|
||||
nand_erase_nand(mtd, &einfo, 0);
|
||||
}
|
||||
|
||||
/* Get block number */
|
||||
block = (int)(ofs >> chip->bbt_erase_shift);
|
||||
/* Mark block bad in memory-based BBT */
|
||||
if (chip->bbt)
|
||||
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
|
||||
|
||||
/* Do we have a flash based bad block table? */
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||
ret = nand_update_bbt(mtd, ofs);
|
||||
else {
|
||||
/* Write bad block marker to OOB */
|
||||
if (write_oob) {
|
||||
struct mtd_oob_ops ops;
|
||||
loff_t wr_ofs = ofs;
|
||||
|
||||
nand_get_device(chip, mtd, FL_WRITING);
|
||||
|
||||
/*
|
||||
* Write to first two pages if necessary. If we write to more
|
||||
* than one location, the first error encountered quits the
|
||||
* procedure. We write two bytes per location, so we dont have
|
||||
* to mess with 16 bit access.
|
||||
*/
|
||||
ops.len = ops.ooblen = 2;
|
||||
ops.datbuf = NULL;
|
||||
ops.oobbuf = buf;
|
||||
ops.ooboffs = chip->badblockpos & ~0x01;
|
||||
ops.ooboffs = chip->badblockpos;
|
||||
if (chip->options & NAND_BUSWIDTH_16) {
|
||||
ops.ooboffs &= ~0x01;
|
||||
ops.len = ops.ooblen = 2;
|
||||
} else {
|
||||
ops.len = ops.ooblen = 1;
|
||||
}
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
|
||||
/* Write to first/last page(s) if necessary */
|
||||
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
|
||||
wr_ofs += mtd->erasesize - mtd->writesize;
|
||||
do {
|
||||
ret = nand_do_write_oob(mtd, ofs, &ops);
|
||||
res = nand_do_write_oob(mtd, wr_ofs, &ops);
|
||||
if (!ret)
|
||||
ret = res;
|
||||
|
||||
i++;
|
||||
ofs += mtd->writesize;
|
||||
} while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) &&
|
||||
i < 2);
|
||||
wr_ofs += mtd->writesize;
|
||||
} while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
|
||||
|
||||
nand_release_device(mtd);
|
||||
}
|
||||
|
||||
/* Update flash-based bad block table */
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH) {
|
||||
res = nand_update_bbt(mtd, ofs);
|
||||
if (!ret)
|
||||
ret = res;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
mtd->ecc_stats.badblocks++;
|
||||
|
||||
@@ -1586,25 +1614,14 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
struct mtd_oob_ops ops;
|
||||
int ret;
|
||||
|
||||
/* Do not allow reads past end of device */
|
||||
if ((from + len) > mtd->size)
|
||||
return -EINVAL;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
nand_get_device(chip, mtd, FL_READING);
|
||||
|
||||
ops.len = len;
|
||||
ops.datbuf = buf;
|
||||
ops.oobbuf = NULL;
|
||||
ops.mode = 0;
|
||||
|
||||
ret = nand_do_read_ops(mtd, from, &ops);
|
||||
|
||||
*retlen = ops.retlen;
|
||||
|
||||
nand_release_device(mtd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2293,12 +2310,6 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
struct mtd_oob_ops ops;
|
||||
int ret;
|
||||
|
||||
/* Do not allow reads past end of device */
|
||||
if ((to + len) > mtd->size)
|
||||
return -EINVAL;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
/* Wait for the device to get ready */
|
||||
panic_nand_wait(mtd, chip, 400);
|
||||
|
||||
@@ -2333,25 +2344,14 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
struct mtd_oob_ops ops;
|
||||
int ret;
|
||||
|
||||
/* Do not allow reads past end of device */
|
||||
if ((to + len) > mtd->size)
|
||||
return -EINVAL;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
nand_get_device(chip, mtd, FL_WRITING);
|
||||
|
||||
ops.len = len;
|
||||
ops.datbuf = (uint8_t *)buf;
|
||||
ops.oobbuf = NULL;
|
||||
ops.mode = 0;
|
||||
|
||||
ret = nand_do_write_ops(mtd, to, &ops);
|
||||
|
||||
*retlen = ops.retlen;
|
||||
|
||||
nand_release_device(mtd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2550,8 +2550,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||
if (check_offs_len(mtd, instr->addr, instr->len))
|
||||
return -EINVAL;
|
||||
|
||||
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
||||
|
||||
/* Grab the lock and see if the device is available */
|
||||
nand_get_device(chip, mtd, FL_ERASING);
|
||||
|
||||
@@ -2715,10 +2713,6 @@ static void nand_sync(struct mtd_info *mtd)
|
||||
*/
|
||||
static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
|
||||
{
|
||||
/* Check for invalid offset */
|
||||
if (offs > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
return nand_block_checkbad(mtd, offs, 1, 0);
|
||||
}
|
||||
|
||||
@@ -2857,7 +2851,6 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
|
||||
return 0;
|
||||
|
||||
pr_info("ONFI flash detected\n");
|
||||
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
|
||||
for (i = 0; i < 3; i++) {
|
||||
chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
|
||||
@@ -2898,7 +2891,8 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
mtd->writesize = le32_to_cpu(p->byte_per_page);
|
||||
mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
|
||||
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
|
||||
chip->chipsize = (uint64_t)le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
|
||||
chip->chipsize = le32_to_cpu(p->blocks_per_lun);
|
||||
chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
|
||||
*busw = 0;
|
||||
if (le16_to_cpu(p->features) & 1)
|
||||
*busw = NAND_BUSWIDTH_16;
|
||||
@@ -2907,6 +2901,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
chip->options |= (NAND_NO_READRDY |
|
||||
NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
|
||||
|
||||
pr_info("ONFI flash detected\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -3238,6 +3233,10 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
int i;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
|
||||
BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
|
||||
!(chip->bbt_options & NAND_BBT_USE_FLASH));
|
||||
|
||||
if (!(chip->options & NAND_OWN_BUFFERS))
|
||||
chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
|
||||
if (!chip->buffers)
|
||||
@@ -3350,6 +3349,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
if (!chip->ecc.size)
|
||||
chip->ecc.size = 256;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.strength = 1;
|
||||
break;
|
||||
|
||||
case NAND_ECC_SOFT_BCH:
|
||||
@@ -3384,6 +3384,8 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
pr_warn("BCH ECC initialization failed!\n");
|
||||
BUG();
|
||||
}
|
||||
chip->ecc.strength =
|
||||
chip->ecc.bytes*8 / fls(8*chip->ecc.size);
|
||||
break;
|
||||
|
||||
case NAND_ECC_NONE:
|
||||
@@ -3397,6 +3399,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
chip->ecc.write_oob = nand_write_oob_std;
|
||||
chip->ecc.size = mtd->writesize;
|
||||
chip->ecc.bytes = 0;
|
||||
chip->ecc.strength = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -3461,25 +3464,26 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
mtd->type = MTD_NANDFLASH;
|
||||
mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
|
||||
MTD_CAP_NANDFLASH;
|
||||
mtd->erase = nand_erase;
|
||||
mtd->point = NULL;
|
||||
mtd->unpoint = NULL;
|
||||
mtd->read = nand_read;
|
||||
mtd->write = nand_write;
|
||||
mtd->panic_write = panic_nand_write;
|
||||
mtd->read_oob = nand_read_oob;
|
||||
mtd->write_oob = nand_write_oob;
|
||||
mtd->sync = nand_sync;
|
||||
mtd->lock = NULL;
|
||||
mtd->unlock = NULL;
|
||||
mtd->suspend = nand_suspend;
|
||||
mtd->resume = nand_resume;
|
||||
mtd->block_isbad = nand_block_isbad;
|
||||
mtd->block_markbad = nand_block_markbad;
|
||||
mtd->_erase = nand_erase;
|
||||
mtd->_point = NULL;
|
||||
mtd->_unpoint = NULL;
|
||||
mtd->_read = nand_read;
|
||||
mtd->_write = nand_write;
|
||||
mtd->_panic_write = panic_nand_write;
|
||||
mtd->_read_oob = nand_read_oob;
|
||||
mtd->_write_oob = nand_write_oob;
|
||||
mtd->_sync = nand_sync;
|
||||
mtd->_lock = NULL;
|
||||
mtd->_unlock = NULL;
|
||||
mtd->_suspend = nand_suspend;
|
||||
mtd->_resume = nand_resume;
|
||||
mtd->_block_isbad = nand_block_isbad;
|
||||
mtd->_block_markbad = nand_block_markbad;
|
||||
mtd->writebufsize = mtd->writesize;
|
||||
|
||||
/* propagate ecc.layout to mtd_info */
|
||||
/* propagate ecc info to mtd_info */
|
||||
mtd->ecclayout = chip->ecc.layout;
|
||||
mtd->ecc_strength = chip->ecc.strength * chip->ecc.steps;
|
||||
|
||||
/* Check, if we should skip the bad block table scan */
|
||||
if (chip->options & NAND_SKIP_BBTSCAN)
|
||||
|
@@ -179,6 +179,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = 256;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.strength = 1;
|
||||
chip->priv = ndfc;
|
||||
|
||||
ndfc->mtd.priv = chip;
|
||||
|
@@ -1058,6 +1058,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
|
||||
(pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) {
|
||||
info->nand.ecc.bytes = 3;
|
||||
info->nand.ecc.size = 512;
|
||||
info->nand.ecc.strength = 1;
|
||||
info->nand.ecc.calculate = omap_calculate_ecc;
|
||||
info->nand.ecc.hwctl = omap_enable_hwecc;
|
||||
info->nand.ecc.correct = omap_correct_data;
|
||||
@@ -1101,8 +1102,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
|
||||
goto out_release_mem_region;
|
||||
}
|
||||
|
||||
mtd_device_parse_register(&info->mtd, NULL, 0,
|
||||
pdata->parts, pdata->nr_parts);
|
||||
mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts,
|
||||
pdata->nr_parts);
|
||||
|
||||
platform_set_drvdata(pdev, &info->mtd);
|
||||
|
||||
|
@@ -129,8 +129,8 @@ static int __init orion_nand_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
mtd->name = "orion_nand";
|
||||
ret = mtd_device_parse_register(mtd, NULL, 0,
|
||||
board->parts, board->nr_parts);
|
||||
ret = mtd_device_parse_register(mtd, NULL, NULL, board->parts,
|
||||
board->nr_parts);
|
||||
if (ret) {
|
||||
nand_release(mtd);
|
||||
goto no_dev;
|
||||
|
@@ -99,8 +99,9 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
err = mtd_device_parse_register(&data->mtd,
|
||||
pdata->chip.part_probe_types, 0,
|
||||
pdata->chip.partitions, pdata->chip.nr_partitions);
|
||||
pdata->chip.part_probe_types, NULL,
|
||||
pdata->chip.partitions,
|
||||
pdata->chip.nr_partitions);
|
||||
|
||||
if (!err)
|
||||
return err;
|
||||
|
@@ -275,11 +275,10 @@ static int __init ppchameleonevb_init(void)
|
||||
ppchameleon_mtd->name = "ppchameleon-nand";
|
||||
|
||||
/* Register the partitions */
|
||||
mtd_device_parse_register(ppchameleon_mtd, NULL, 0,
|
||||
ppchameleon_mtd->size == NAND_SMALL_SIZE ?
|
||||
partition_info_me :
|
||||
partition_info_hi,
|
||||
NUM_PARTITIONS);
|
||||
mtd_device_parse_register(ppchameleon_mtd, NULL, NULL,
|
||||
ppchameleon_mtd->size == NAND_SMALL_SIZE ?
|
||||
partition_info_me : partition_info_hi,
|
||||
NUM_PARTITIONS);
|
||||
|
||||
nand_evb_init:
|
||||
/****************************
|
||||
@@ -365,11 +364,10 @@ static int __init ppchameleonevb_init(void)
|
||||
ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME;
|
||||
|
||||
/* Register the partitions */
|
||||
mtd_device_parse_register(ppchameleonevb_mtd, NULL, 0,
|
||||
ppchameleon_mtd->size == NAND_SMALL_SIZE ?
|
||||
partition_info_me :
|
||||
partition_info_hi,
|
||||
NUM_PARTITIONS);
|
||||
mtd_device_parse_register(ppchameleonevb_mtd, NULL, NULL,
|
||||
ppchameleon_mtd->size == NAND_SMALL_SIZE ?
|
||||
partition_info_me : partition_info_hi,
|
||||
NUM_PARTITIONS);
|
||||
|
||||
/* Return happy */
|
||||
return 0;
|
||||
|
@@ -1002,6 +1002,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
||||
KEEP_CONFIG:
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = host->page_size;
|
||||
chip->ecc.strength = 1;
|
||||
|
||||
chip->options = NAND_NO_AUTOINCR;
|
||||
chip->options |= NAND_NO_READRDY;
|
||||
@@ -1228,8 +1229,9 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
|
||||
pdata->parts[cs], pdata->nr_parts[cs]);
|
||||
ret = mtd_device_parse_register(info->host[cs]->mtd, NULL,
|
||||
NULL, pdata->parts[cs],
|
||||
pdata->nr_parts[cs]);
|
||||
if (!ret)
|
||||
probe_success = 1;
|
||||
}
|
||||
|
@@ -891,6 +891,7 @@ int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
|
||||
chip->ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
chip->ecc.size = R852_DMA_LEN;
|
||||
chip->ecc.bytes = SM_OOB_SIZE;
|
||||
chip->ecc.strength = 2;
|
||||
chip->ecc.hwctl = r852_ecc_hwctl;
|
||||
chip->ecc.calculate = r852_ecc_calculate;
|
||||
chip->ecc.correct = r852_ecc_correct;
|
||||
|
@@ -527,6 +527,7 @@ static int __init rtc_from4_init(void)
|
||||
this->ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
this->ecc.size = 512;
|
||||
this->ecc.bytes = 8;
|
||||
this->ecc.strength = 3;
|
||||
/* return the status of extra status and ECC checks */
|
||||
this->errstat = rtc_from4_errstat;
|
||||
/* set the nand_oobinfo to support FPGA H/W error detection */
|
||||
|
@@ -751,8 +751,8 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
|
||||
if (set)
|
||||
mtd->mtd.name = set->name;
|
||||
|
||||
return mtd_device_parse_register(&mtd->mtd, NULL, 0,
|
||||
set->partitions, set->nr_partitions);
|
||||
return mtd_device_parse_register(&mtd->mtd, NULL, NULL,
|
||||
set->partitions, set->nr_partitions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -823,6 +823,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
||||
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
|
||||
chip->ecc.correct = s3c2410_nand_correct_data;
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.strength = 1;
|
||||
|
||||
switch (info->cpu_type) {
|
||||
case TYPE_S3C2410:
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
@@ -283,7 +284,7 @@ static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
|
||||
static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val)
|
||||
{
|
||||
struct sh_flctl *flctl = mtd_to_flctl(mtd);
|
||||
uint32_t flcmncr_val = readl(FLCMNCR(flctl)) & ~SEL_16BIT;
|
||||
uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT;
|
||||
uint32_t flcmdcr_val, addr_len_bytes = 0;
|
||||
|
||||
/* Set SNAND bit if page size is 2048byte */
|
||||
@@ -303,6 +304,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va
|
||||
break;
|
||||
case NAND_CMD_READ0:
|
||||
case NAND_CMD_READOOB:
|
||||
case NAND_CMD_RNDOUT:
|
||||
addr_len_bytes = flctl->rw_ADRCNT;
|
||||
flcmdcr_val |= CDSRC_E;
|
||||
if (flctl->chip.options & NAND_BUSWIDTH_16)
|
||||
@@ -320,6 +322,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va
|
||||
break;
|
||||
case NAND_CMD_READID:
|
||||
flcmncr_val &= ~SNAND_E;
|
||||
flcmdcr_val |= CDSRC_E;
|
||||
addr_len_bytes = ADRCNT_1;
|
||||
break;
|
||||
case NAND_CMD_STATUS:
|
||||
@@ -513,6 +516,8 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
struct sh_flctl *flctl = mtd_to_flctl(mtd);
|
||||
uint32_t read_cmd = 0;
|
||||
|
||||
pm_runtime_get_sync(&flctl->pdev->dev);
|
||||
|
||||
flctl->read_bytes = 0;
|
||||
if (command != NAND_CMD_PAGEPROG)
|
||||
flctl->index = 0;
|
||||
@@ -525,7 +530,6 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
execmd_read_page_sector(mtd, page_addr);
|
||||
break;
|
||||
}
|
||||
empty_fifo(flctl);
|
||||
if (flctl->page_size)
|
||||
set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
|
||||
| command);
|
||||
@@ -547,7 +551,6 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
break;
|
||||
}
|
||||
|
||||
empty_fifo(flctl);
|
||||
if (flctl->page_size) {
|
||||
set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
|
||||
| NAND_CMD_READ0);
|
||||
@@ -559,15 +562,35 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
flctl->read_bytes = mtd->oobsize;
|
||||
goto read_normal_exit;
|
||||
|
||||
case NAND_CMD_READID:
|
||||
empty_fifo(flctl);
|
||||
set_cmd_regs(mtd, command, command);
|
||||
set_addr(mtd, 0, 0);
|
||||
case NAND_CMD_RNDOUT:
|
||||
if (flctl->hwecc)
|
||||
break;
|
||||
|
||||
flctl->read_bytes = 4;
|
||||
if (flctl->page_size)
|
||||
set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8)
|
||||
| command);
|
||||
else
|
||||
set_cmd_regs(mtd, command, command);
|
||||
|
||||
set_addr(mtd, column, 0);
|
||||
|
||||
flctl->read_bytes = mtd->writesize + mtd->oobsize - column;
|
||||
goto read_normal_exit;
|
||||
|
||||
case NAND_CMD_READID:
|
||||
set_cmd_regs(mtd, command, command);
|
||||
|
||||
/* READID is always performed using an 8-bit bus */
|
||||
if (flctl->chip.options & NAND_BUSWIDTH_16)
|
||||
column <<= 1;
|
||||
set_addr(mtd, column, 0);
|
||||
|
||||
flctl->read_bytes = 8;
|
||||
writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
|
||||
empty_fifo(flctl);
|
||||
start_translation(flctl);
|
||||
read_datareg(flctl, 0); /* read and end */
|
||||
read_fiforeg(flctl, flctl->read_bytes, 0);
|
||||
wait_completion(flctl);
|
||||
break;
|
||||
|
||||
case NAND_CMD_ERASE1:
|
||||
@@ -650,29 +673,55 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
goto runtime_exit;
|
||||
|
||||
read_normal_exit:
|
||||
writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
|
||||
empty_fifo(flctl);
|
||||
start_translation(flctl);
|
||||
read_fiforeg(flctl, flctl->read_bytes, 0);
|
||||
wait_completion(flctl);
|
||||
runtime_exit:
|
||||
pm_runtime_put_sync(&flctl->pdev->dev);
|
||||
return;
|
||||
}
|
||||
|
||||
static void flctl_select_chip(struct mtd_info *mtd, int chipnr)
|
||||
{
|
||||
struct sh_flctl *flctl = mtd_to_flctl(mtd);
|
||||
uint32_t flcmncr_val = readl(FLCMNCR(flctl));
|
||||
int ret;
|
||||
|
||||
switch (chipnr) {
|
||||
case -1:
|
||||
flcmncr_val &= ~CE0_ENABLE;
|
||||
writel(flcmncr_val, FLCMNCR(flctl));
|
||||
flctl->flcmncr_base &= ~CE0_ENABLE;
|
||||
|
||||
pm_runtime_get_sync(&flctl->pdev->dev);
|
||||
writel(flctl->flcmncr_base, FLCMNCR(flctl));
|
||||
|
||||
if (flctl->qos_request) {
|
||||
dev_pm_qos_remove_request(&flctl->pm_qos);
|
||||
flctl->qos_request = 0;
|
||||
}
|
||||
|
||||
pm_runtime_put_sync(&flctl->pdev->dev);
|
||||
break;
|
||||
case 0:
|
||||
flcmncr_val |= CE0_ENABLE;
|
||||
writel(flcmncr_val, FLCMNCR(flctl));
|
||||
flctl->flcmncr_base |= CE0_ENABLE;
|
||||
|
||||
if (!flctl->qos_request) {
|
||||
ret = dev_pm_qos_add_request(&flctl->pdev->dev,
|
||||
&flctl->pm_qos, 100);
|
||||
if (ret < 0)
|
||||
dev_err(&flctl->pdev->dev,
|
||||
"PM QoS request failed: %d\n", ret);
|
||||
flctl->qos_request = 1;
|
||||
}
|
||||
|
||||
if (flctl->holden) {
|
||||
pm_runtime_get_sync(&flctl->pdev->dev);
|
||||
writel(HOLDEN, FLHOLDCR(flctl));
|
||||
pm_runtime_put_sync(&flctl->pdev->dev);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
@@ -730,11 +779,6 @@ static int flctl_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flctl_register_init(struct sh_flctl *flctl, unsigned long val)
|
||||
{
|
||||
writel(val, FLCMNCR(flctl));
|
||||
}
|
||||
|
||||
static int flctl_chip_init_tail(struct mtd_info *mtd)
|
||||
{
|
||||
struct sh_flctl *flctl = mtd_to_flctl(mtd);
|
||||
@@ -781,13 +825,13 @@ static int flctl_chip_init_tail(struct mtd_info *mtd)
|
||||
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 10;
|
||||
chip->ecc.strength = 4;
|
||||
chip->ecc.read_page = flctl_read_page_hwecc;
|
||||
chip->ecc.write_page = flctl_write_page_hwecc;
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
|
||||
/* 4 symbols ECC enabled */
|
||||
writel(readl(FLCMNCR(flctl)) | _4ECCEN | ECCPOS2 | ECCPOS_02,
|
||||
FLCMNCR(flctl));
|
||||
flctl->flcmncr_base |= _4ECCEN | ECCPOS2 | ECCPOS_02;
|
||||
} else {
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
}
|
||||
@@ -819,13 +863,13 @@ static int __devinit flctl_probe(struct platform_device *pdev)
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to get I/O memory\n");
|
||||
goto err;
|
||||
goto err_iomap;
|
||||
}
|
||||
|
||||
flctl->reg = ioremap(res->start, resource_size(res));
|
||||
if (flctl->reg == NULL) {
|
||||
dev_err(&pdev->dev, "failed to remap I/O memory\n");
|
||||
goto err;
|
||||
goto err_iomap;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, flctl);
|
||||
@@ -833,9 +877,9 @@ static int __devinit flctl_probe(struct platform_device *pdev)
|
||||
nand = &flctl->chip;
|
||||
flctl_mtd->priv = nand;
|
||||
flctl->pdev = pdev;
|
||||
flctl->flcmncr_base = pdata->flcmncr_val;
|
||||
flctl->hwecc = pdata->has_hwecc;
|
||||
|
||||
flctl_register_init(flctl, pdata->flcmncr_val);
|
||||
flctl->holden = pdata->use_holden;
|
||||
|
||||
nand->options = NAND_NO_AUTOINCR;
|
||||
|
||||
@@ -855,23 +899,28 @@ static int __devinit flctl_probe(struct platform_device *pdev)
|
||||
nand->read_word = flctl_read_word;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_resume(&pdev->dev);
|
||||
|
||||
ret = nand_scan_ident(flctl_mtd, 1, NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
goto err_chip;
|
||||
|
||||
ret = flctl_chip_init_tail(flctl_mtd);
|
||||
if (ret)
|
||||
goto err;
|
||||
goto err_chip;
|
||||
|
||||
ret = nand_scan_tail(flctl_mtd);
|
||||
if (ret)
|
||||
goto err;
|
||||
goto err_chip;
|
||||
|
||||
mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
err_chip:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
err_iomap:
|
||||
kfree(flctl);
|
||||
return ret;
|
||||
}
|
||||
@@ -881,6 +930,7 @@ static int __devexit flctl_remove(struct platform_device *pdev)
|
||||
struct sh_flctl *flctl = platform_get_drvdata(pdev);
|
||||
|
||||
nand_release(&flctl->mtd);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
kfree(flctl);
|
||||
|
||||
return 0;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user