summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/mtd/devices
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers/mtd/devices')
-rw-r--r--kernel/drivers/mtd/devices/Kconfig8
-rw-r--r--kernel/drivers/mtd/devices/Makefile1
-rw-r--r--kernel/drivers/mtd/devices/bcm47xxsflash.c7
-rw-r--r--kernel/drivers/mtd/devices/block2mtd.c1
-rw-r--r--kernel/drivers/mtd/devices/docg3.c42
-rw-r--r--kernel/drivers/mtd/devices/m25p80.c94
-rw-r--r--kernel/drivers/mtd/devices/mtd_dataflash.c3
-rw-r--r--kernel/drivers/mtd/devices/mtdram.c21
-rw-r--r--kernel/drivers/mtd/devices/powernv_flash.c285
-rw-r--r--kernel/drivers/mtd/devices/slram.c2
-rw-r--r--kernel/drivers/mtd/devices/spear_smi.c5
-rw-r--r--kernel/drivers/mtd/devices/sst25l.c4
12 files changed, 397 insertions, 76 deletions
diff --git a/kernel/drivers/mtd/devices/Kconfig b/kernel/drivers/mtd/devices/Kconfig
index c49d0b127..f73c41697 100644
--- a/kernel/drivers/mtd/devices/Kconfig
+++ b/kernel/drivers/mtd/devices/Kconfig
@@ -195,6 +195,14 @@ config MTD_BLOCK2MTD
Testing MTD users (eg JFFS2) on large media and media that might
be removed during a write (using the floppy drive).
+config MTD_POWERNV_FLASH
+ tristate "powernv flash MTD driver"
+ depends on PPC_POWERNV
+ help
+ This provides an MTD device to access flash on powernv OPAL
+ platforms from Linux. This device abstracts away the
+ firmware interface for flash access.
+
comment "Disk-On-Chip Device Drivers"
config MTD_DOCG3
diff --git a/kernel/drivers/mtd/devices/Makefile b/kernel/drivers/mtd/devices/Makefile
index f0b0e611d..7912d3a0e 100644
--- a/kernel/drivers/mtd/devices/Makefile
+++ b/kernel/drivers/mtd/devices/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o
+obj-$(CONFIG_MTD_POWERNV_FLASH) += powernv_flash.o
CFLAGS_docg3.o += -I$(src)
diff --git a/kernel/drivers/mtd/devices/bcm47xxsflash.c b/kernel/drivers/mtd/devices/bcm47xxsflash.c
index 3d008a941..347bb83db 100644
--- a/kernel/drivers/mtd/devices/bcm47xxsflash.c
+++ b/kernel/drivers/mtd/devices/bcm47xxsflash.c
@@ -237,13 +237,14 @@ static int bcm47xxsflash_write(struct mtd_info *mtd, loff_t to, size_t len,
return 0;
}
-static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s)
+static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s,
+ struct device *dev)
{
struct mtd_info *mtd = &b47s->mtd;
mtd->priv = b47s;
+ mtd->dev.parent = dev;
mtd->name = "bcm47xxsflash";
- mtd->owner = THIS_MODULE;
mtd->type = MTD_NORFLASH;
mtd->flags = MTD_CAP_NORFLASH;
@@ -300,7 +301,7 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
b47s->blocksize = sflash->blocksize;
b47s->numblocks = sflash->numblocks;
b47s->size = sflash->size;
- bcm47xxsflash_fill_mtd(b47s);
+ bcm47xxsflash_fill_mtd(b47s, &pdev->dev);
err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0);
if (err) {
diff --git a/kernel/drivers/mtd/devices/block2mtd.c b/kernel/drivers/mtd/devices/block2mtd.c
index b16f3cda9..e2c005773 100644
--- a/kernel/drivers/mtd/devices/block2mtd.c
+++ b/kernel/drivers/mtd/devices/block2mtd.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
+#include <linux/backing-dev.h>
#include <linux/bio.h>
#include <linux/pagemap.h>
#include <linux/list.h>
diff --git a/kernel/drivers/mtd/devices/docg3.c b/kernel/drivers/mtd/devices/docg3.c
index 866d31904..c3a2695a4 100644
--- a/kernel/drivers/mtd/devices/docg3.c
+++ b/kernel/drivers/mtd/devices/docg3.c
@@ -1620,20 +1620,30 @@ static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = {
static int doc_register_sysfs(struct platform_device *pdev,
struct docg3_cascade *cascade)
{
- int ret = 0, floor, i = 0;
struct device *dev = &pdev->dev;
+ int floor;
+ int ret;
+ int i;
- for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS &&
- cascade->floors[floor]; floor++)
- for (i = 0; !ret && i < 4; i++)
+ for (floor = 0;
+ floor < DOC_MAX_NBFLOORS && cascade->floors[floor];
+ floor++) {
+ for (i = 0; i < 4; i++) {
ret = device_create_file(dev, &doc_sys_attrs[floor][i]);
- if (!ret)
- return 0;
+ if (ret)
+ goto remove_files;
+ }
+ }
+
+ return 0;
+
+remove_files:
do {
while (--i >= 0)
device_remove_file(dev, &doc_sys_attrs[floor][i]);
i = 4;
} while (--floor >= 0);
+
return ret;
}
@@ -1815,7 +1825,7 @@ static void doc_dbg_unregister(struct docg3 *docg3)
* @chip_id: The chip ID of the supported chip
* @mtd: The structure to fill
*/
-static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
+static int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
{
struct docg3 *docg3 = mtd->priv;
int cfg;
@@ -1828,6 +1838,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
case DOC_CHIPID_G3:
mtd->name = kasprintf(GFP_KERNEL, "docg3.%d",
docg3->device_id);
+ if (!mtd->name)
+ return -ENOMEM;
docg3->max_block = 2047;
break;
}
@@ -1841,7 +1853,6 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
mtd->erasesize /= 2;
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;
@@ -1850,6 +1861,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
mtd->_block_isbad = doc_block_isbad;
mtd->ecclayout = &docg3_oobinfo;
mtd->ecc_strength = DOC_ECC_BCH_T;
+
+ return 0;
}
/**
@@ -1881,6 +1894,7 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
if (!mtd)
goto nomem2;
mtd->priv = docg3;
+ mtd->dev.parent = dev;
bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1,
8 * DOC_LAYOUT_PAGE_SIZE);
docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL);
@@ -1900,7 +1914,7 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
ret = 0;
if (chip_id != (u16)(~chip_id_inv)) {
- goto nomem3;
+ goto nomem4;
}
switch (chip_id) {
@@ -1910,15 +1924,19 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
break;
default:
doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id);
- goto nomem3;
+ goto nomem4;
}
- doc_set_driver_info(chip_id, mtd);
+ ret = doc_set_driver_info(chip_id, mtd);
+ if (ret)
+ goto nomem4;
doc_hamming_ecc_init(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ);
doc_reload_bbt(docg3);
return mtd;
+nomem4:
+ kfree(docg3->bbt);
nomem3:
kfree(mtd);
nomem2:
@@ -2117,7 +2135,7 @@ static int docg3_release(struct platform_device *pdev)
}
#ifdef CONFIG_OF
-static struct of_device_id docg3_dt_ids[] = {
+static const struct of_device_id docg3_dt_ids[] = {
{ .compatible = "m-systems,diskonchip-g3" },
{}
};
diff --git a/kernel/drivers/mtd/devices/m25p80.c b/kernel/drivers/mtd/devices/m25p80.c
index 3af137f49..fe9ceb7b5 100644
--- a/kernel/drivers/mtd/devices/m25p80.c
+++ b/kernel/drivers/mtd/devices/m25p80.c
@@ -31,7 +31,6 @@
struct m25p {
struct spi_device *spi;
struct spi_nor spi_nor;
- struct mtd_info mtd;
u8 command[MAX_CMD_SIZE];
};
@@ -62,8 +61,7 @@ static int m25p_cmdsz(struct spi_nor *nor)
return 1 + nor->addr_width;
}
-static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
- int wr_en)
+static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
@@ -159,7 +157,7 @@ static int m25p80_erase(struct spi_nor *nor, loff_t offset)
struct m25p *flash = nor->priv;
dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
- flash->mtd.erasesize / 1024, (u32)offset);
+ flash->spi_nor.mtd.erasesize / 1024, (u32)offset);
/* Set up command buffer. */
flash->command[0] = nor->erase_opcode;
@@ -201,11 +199,10 @@ static int m25p_probe(struct spi_device *spi)
nor->read_reg = m25p80_read_reg;
nor->dev = &spi->dev;
- nor->mtd = &flash->mtd;
+ nor->flash_node = spi->dev.of_node;
nor->priv = flash;
spi_set_drvdata(spi, flash);
- flash->mtd.priv = nor;
flash->spi = spi;
if (spi->mode & SPI_RX_QUAD)
@@ -214,7 +211,7 @@ static int m25p_probe(struct spi_device *spi)
mode = SPI_NOR_DUAL;
if (data && data->name)
- flash->mtd.name = data->name;
+ nor->mtd.name = data->name;
/* For some (historical?) reason many platforms provide two different
* names in flash_platform_data: "name" and "type". Quite often name is
@@ -223,8 +220,6 @@ static int m25p_probe(struct spi_device *spi)
*/
if (data && data->type)
flash_name = data->type;
- else if (!strcmp(spi->modalias, "spi-nor"))
- flash_name = NULL; /* auto-detect */
else
flash_name = spi->modalias;
@@ -234,7 +229,7 @@ static int m25p_probe(struct spi_device *spi)
ppdata.of_node = spi->dev.of_node;
- return mtd_device_parse_register(&flash->mtd, NULL, &ppdata,
+ return mtd_device_parse_register(&nor->mtd, NULL, &ppdata,
data ? data->parts : NULL,
data ? data->nr_parts : 0);
}
@@ -245,7 +240,7 @@ static int m25p_remove(struct spi_device *spi)
struct m25p *flash = spi_get_drvdata(spi);
/* Clean up MTD stuff. */
- return mtd_device_unregister(&flash->mtd);
+ return mtd_device_unregister(&flash->spi_nor.mtd);
}
/*
@@ -261,59 +256,52 @@ static int m25p_remove(struct spi_device *spi)
* keep them available as module aliases for existing platforms.
*/
static const struct spi_device_id m25p_ids[] = {
- {"at25fs010"}, {"at25fs040"}, {"at25df041a"}, {"at25df321a"},
- {"at25df641"}, {"at26f004"}, {"at26df081a"}, {"at26df161a"},
- {"at26df321"}, {"at45db081d"},
- {"en25f32"}, {"en25p32"}, {"en25q32b"}, {"en25p64"},
- {"en25q64"}, {"en25qh128"}, {"en25qh256"},
- {"f25l32pa"},
- {"mr25h256"}, {"mr25h10"},
- {"gd25q32"}, {"gd25q64"},
- {"160s33b"}, {"320s33b"}, {"640s33b"},
- {"mx25l2005a"}, {"mx25l4005a"}, {"mx25l8005"}, {"mx25l1606e"},
- {"mx25l3205d"}, {"mx25l3255e"}, {"mx25l6405d"}, {"mx25l12805d"},
- {"mx25l12855e"},{"mx25l25635e"},{"mx25l25655e"},{"mx66l51235l"},
- {"mx66l1g55g"},
- {"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q256a"},
- {"n25q512a"}, {"n25q512ax3"}, {"n25q00"},
- {"pm25lv512"}, {"pm25lv010"}, {"pm25lq032"},
- {"s25sl032p"}, {"s25sl064p"}, {"s25fl256s0"}, {"s25fl256s1"},
- {"s25fl512s"}, {"s70fl01gs"}, {"s25sl12800"}, {"s25sl12801"},
- {"s25fl129p0"}, {"s25fl129p1"}, {"s25sl004a"}, {"s25sl008a"},
- {"s25sl016a"}, {"s25sl032a"}, {"s25sl064a"}, {"s25fl008k"},
- {"s25fl016k"}, {"s25fl064k"}, {"s25fl132k"},
- {"sst25vf040b"},{"sst25vf080b"},{"sst25vf016b"},{"sst25vf032b"},
- {"sst25vf064c"},{"sst25wf512"}, {"sst25wf010"}, {"sst25wf020"},
- {"sst25wf040"},
- {"m25p05"}, {"m25p10"}, {"m25p20"}, {"m25p40"},
- {"m25p80"}, {"m25p16"}, {"m25p32"}, {"m25p64"},
- {"m25p128"}, {"n25q032"},
+ /*
+ * Entries not used in DTs that should be safe to drop after replacing
+ * them with "nor-jedec" in platform data.
+ */
+ {"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"},
+
+ /*
+ * Entries that were used in DTs without "nor-jedec" fallback and should
+ * be kept for backward compatibility.
+ */
+ {"at25df321a"}, {"at25df641"}, {"at26df081a"},
+ {"mr25h256"},
+ {"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
+ {"mx25l25635e"},{"mx66l51235l"},
+ {"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
+ {"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"},
+ {"s25fl064k"},
+ {"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
+ {"m25p40"}, {"m25p80"}, {"m25p16"}, {"m25p32"},
+ {"m25p64"}, {"m25p128"},
+ {"w25x80"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"},
+ {"w25q80bl"}, {"w25q128"}, {"w25q256"},
+
+ /* Flashes that can't be detected using JEDEC */
{"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"},
{"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"},
{"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"},
- {"m45pe10"}, {"m45pe80"}, {"m45pe16"},
- {"m25pe20"}, {"m25pe80"}, {"m25pe16"},
- {"m25px16"}, {"m25px32"}, {"m25px32-s0"}, {"m25px32-s1"},
- {"m25px64"}, {"m25px80"},
- {"w25x10"}, {"w25x20"}, {"w25x40"}, {"w25x80"},
- {"w25x16"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"},
- {"w25x64"}, {"w25q64"}, {"w25q80"}, {"w25q80bl"},
- {"w25q128"}, {"w25q256"}, {"cat25c11"},
- {"cat25c03"}, {"cat25c09"}, {"cat25c17"}, {"cat25128"},
- /*
- * Generic support for SPI NOR that can be identified by the JEDEC READ
- * ID opcode (0x9F). Use this, if possible.
- */
- {"spi-nor"},
{ },
};
MODULE_DEVICE_TABLE(spi, m25p_ids);
+static const struct of_device_id m25p_of_table[] = {
+ /*
+ * Generic compatibility for SPI NOR that can be identified by the
+ * JEDEC READ ID opcode (0x9F). Use this, if possible.
+ */
+ { .compatible = "jedec,spi-nor" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, m25p_of_table);
+
static struct spi_driver m25p80_driver = {
.driver = {
.name = "m25p80",
- .owner = THIS_MODULE,
+ .of_match_table = m25p_of_table,
},
.id_table = m25p_ids,
.probe = m25p_probe,
diff --git a/kernel/drivers/mtd/devices/mtd_dataflash.c b/kernel/drivers/mtd/devices/mtd_dataflash.c
index 0099aba72..e4a88715a 100644
--- a/kernel/drivers/mtd/devices/mtd_dataflash.c
+++ b/kernel/drivers/mtd/devices/mtd_dataflash.c
@@ -102,6 +102,7 @@ static const struct of_device_id dataflash_dt_ids[] = {
{ .compatible = "atmel,dataflash", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, dataflash_dt_ids);
#endif
/* ......................................................................... */
@@ -647,7 +648,6 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages,
device->size = nr_pages * pagesize;
device->erasesize = pagesize;
device->writesize = pagesize;
- device->owner = THIS_MODULE;
device->type = MTD_DATAFLASH;
device->flags = MTD_WRITEABLE;
device->_erase = dataflash_erase;
@@ -910,7 +910,6 @@ static int dataflash_remove(struct spi_device *spi)
static struct spi_driver dataflash_driver = {
.driver = {
.name = "mtd_dataflash",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(dataflash_dt_ids),
},
diff --git a/kernel/drivers/mtd/devices/mtdram.c b/kernel/drivers/mtd/devices/mtdram.c
index 8e2850892..627a9bc37 100644
--- a/kernel/drivers/mtd/devices/mtdram.c
+++ b/kernel/drivers/mtd/devices/mtdram.c
@@ -32,8 +32,29 @@ MODULE_PARM_DESC(erase_size, "Device erase block size in KiB");
// We could store these in the mtd structure, but we only support 1 device..
static struct mtd_info *mtd_info;
+static int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+ int ret = 0;
+
+ /* Start address must align on block boundary */
+ if (mtd_mod_by_eb(ofs, mtd)) {
+ pr_debug("%s: unaligned address\n", __func__);
+ ret = -EINVAL;
+ }
+
+ /* Length must align on block boundary */
+ if (mtd_mod_by_eb(len, mtd)) {
+ pr_debug("%s: length not block aligned\n", __func__);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
+ if (check_offs_len(mtd, instr->addr, instr->len))
+ return -EINVAL;
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
diff --git a/kernel/drivers/mtd/devices/powernv_flash.c b/kernel/drivers/mtd/devices/powernv_flash.c
new file mode 100644
index 000000000..d5b870b3f
--- /dev/null
+++ b/kernel/drivers/mtd/devices/powernv_flash.c
@@ -0,0 +1,285 @@
+/*
+ * OPAL PNOR flash MTD abstraction
+ *
+ * Copyright IBM 2015
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <asm/opal.h>
+
+
+/*
+ * This driver creates the a Linux MTD abstraction for platform PNOR flash
+ * backed by OPAL calls
+ */
+
+struct powernv_flash {
+ struct mtd_info mtd;
+ u32 id;
+};
+
+enum flash_op {
+ FLASH_OP_READ,
+ FLASH_OP_WRITE,
+ FLASH_OP_ERASE,
+};
+
+static int powernv_flash_async_op(struct mtd_info *mtd, enum flash_op op,
+ loff_t offset, size_t len, size_t *retlen, u_char *buf)
+{
+ struct powernv_flash *info = (struct powernv_flash *)mtd->priv;
+ struct device *dev = &mtd->dev;
+ int token;
+ struct opal_msg msg;
+ int rc;
+
+ dev_dbg(dev, "%s(op=%d, offset=0x%llx, len=%zu)\n",
+ __func__, op, offset, len);
+
+ token = opal_async_get_token_interruptible();
+ if (token < 0) {
+ if (token != -ERESTARTSYS)
+ dev_err(dev, "Failed to get an async token\n");
+
+ return token;
+ }
+
+ switch (op) {
+ case FLASH_OP_READ:
+ rc = opal_flash_read(info->id, offset, __pa(buf), len, token);
+ break;
+ case FLASH_OP_WRITE:
+ rc = opal_flash_write(info->id, offset, __pa(buf), len, token);
+ break;
+ case FLASH_OP_ERASE:
+ rc = opal_flash_erase(info->id, offset, len, token);
+ break;
+ default:
+ BUG_ON(1);
+ }
+
+ if (rc != OPAL_ASYNC_COMPLETION) {
+ dev_err(dev, "opal_flash_async_op(op=%d) failed (rc %d)\n",
+ op, rc);
+ opal_async_release_token(token);
+ return -EIO;
+ }
+
+ rc = opal_async_wait_response(token, &msg);
+ opal_async_release_token(token);
+ if (rc) {
+ dev_err(dev, "opal async wait failed (rc %d)\n", rc);
+ return -EIO;
+ }
+
+ rc = be64_to_cpu(msg.params[1]);
+ if (rc == OPAL_SUCCESS) {
+ rc = 0;
+ if (retlen)
+ *retlen = len;
+ } else {
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
+/**
+ * @mtd: the device
+ * @from: the offset to read from
+ * @len: the number of bytes to read
+ * @retlen: the number of bytes actually read
+ * @buf: the filled in buffer
+ *
+ * Returns 0 if read successful, or -ERRNO if an error occurred
+ */
+static int powernv_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ return powernv_flash_async_op(mtd, FLASH_OP_READ, from,
+ len, retlen, buf);
+}
+
+/**
+ * @mtd: the device
+ * @to: the offset to write to
+ * @len: the number of bytes to write
+ * @retlen: the number of bytes actually written
+ * @buf: the buffer to get bytes from
+ *
+ * Returns 0 if write successful, -ERRNO if error occurred
+ */
+static int powernv_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ return powernv_flash_async_op(mtd, FLASH_OP_WRITE, to,
+ len, retlen, (u_char *)buf);
+}
+
+/**
+ * @mtd: the device
+ * @erase: the erase info
+ * Returns 0 if erase successful or -ERRNO if an error occurred
+ */
+static int powernv_flash_erase(struct mtd_info *mtd, struct erase_info *erase)
+{
+ int rc;
+
+ erase->state = MTD_ERASING;
+
+ /* todo: register our own notifier to do a true async implementation */
+ rc = powernv_flash_async_op(mtd, FLASH_OP_ERASE, erase->addr,
+ erase->len, NULL, NULL);
+
+ if (rc) {
+ erase->fail_addr = erase->addr;
+ erase->state = MTD_ERASE_FAILED;
+ } else {
+ erase->state = MTD_ERASE_DONE;
+ }
+ mtd_erase_callback(erase);
+ return rc;
+}
+
+/**
+ * powernv_flash_set_driver_info - Fill the mtd_info structure and docg3
+ * structure @pdev: The platform device
+ * @mtd: The structure to fill
+ */
+static int powernv_flash_set_driver_info(struct device *dev,
+ struct mtd_info *mtd)
+{
+ u64 size;
+ u32 erase_size;
+ int rc;
+
+ rc = of_property_read_u32(dev->of_node, "ibm,flash-block-size",
+ &erase_size);
+ if (rc) {
+ dev_err(dev, "couldn't get resource block size information\n");
+ return rc;
+ }
+
+ rc = of_property_read_u64(dev->of_node, "reg", &size);
+ if (rc) {
+ dev_err(dev, "couldn't get resource size information\n");
+ return rc;
+ }
+
+ /*
+ * Going to have to check what details I need to set and how to
+ * get them
+ */
+ mtd->name = of_get_property(dev->of_node, "name", NULL);
+ mtd->type = MTD_NORFLASH;
+ mtd->flags = MTD_WRITEABLE;
+ mtd->size = size;
+ mtd->erasesize = erase_size;
+ mtd->writebufsize = mtd->writesize = 1;
+ mtd->owner = THIS_MODULE;
+ mtd->_erase = powernv_flash_erase;
+ mtd->_read = powernv_flash_read;
+ mtd->_write = powernv_flash_write;
+ mtd->dev.parent = dev;
+ return 0;
+}
+
+/**
+ * powernv_flash_probe
+ * @pdev: platform device
+ *
+ * Returns 0 on success, -ENOMEM, -ENXIO on error
+ */
+static int powernv_flash_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct powernv_flash *data;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ data->mtd.priv = data;
+
+ ret = of_property_read_u32(dev->of_node, "ibm,opal-id", &(data->id));
+ if (ret) {
+ dev_err(dev, "no device property 'ibm,opal-id'\n");
+ goto out;
+ }
+
+ ret = powernv_flash_set_driver_info(dev, &data->mtd);
+ if (ret)
+ goto out;
+
+ dev_set_drvdata(dev, data);
+
+ /*
+ * The current flash that skiboot exposes is one contiguous flash chip
+ * with an ffs partition at the start, it should prove easier for users
+ * to deal with partitions or not as they see fit
+ */
+ ret = mtd_device_register(&data->mtd, NULL, 0);
+
+out:
+ return ret;
+}
+
+/**
+ * op_release - Release the driver
+ * @pdev: the platform device
+ *
+ * Returns 0
+ */
+static int powernv_flash_release(struct platform_device *pdev)
+{
+ struct powernv_flash *data = dev_get_drvdata(&(pdev->dev));
+
+ /* All resources should be freed automatically */
+ return mtd_device_unregister(&(data->mtd));
+}
+
+static const struct of_device_id powernv_flash_match[] = {
+ { .compatible = "ibm,opal-flash" },
+ {}
+};
+
+static struct platform_driver powernv_flash_driver = {
+ .driver = {
+ .name = "powernv_flash",
+ .of_match_table = powernv_flash_match,
+ },
+ .remove = powernv_flash_release,
+ .probe = powernv_flash_probe,
+};
+
+module_platform_driver(powernv_flash_driver);
+
+MODULE_DEVICE_TABLE(of, powernv_flash_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cyril Bur <cyril.bur@au1.ibm.com>");
+MODULE_DESCRIPTION("MTD abstraction for OPAL flash");
diff --git a/kernel/drivers/mtd/devices/slram.c b/kernel/drivers/mtd/devices/slram.c
index 2fc4957cb..a70eb83e6 100644
--- a/kernel/drivers/mtd/devices/slram.c
+++ b/kernel/drivers/mtd/devices/slram.c
@@ -41,7 +41,7 @@
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/init.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/mtd/mtd.h>
diff --git a/kernel/drivers/mtd/devices/spear_smi.c b/kernel/drivers/mtd/devices/spear_smi.c
index 508bab3bd..64c745834 100644
--- a/kernel/drivers/mtd/devices/spear_smi.c
+++ b/kernel/drivers/mtd/devices/spear_smi.c
@@ -1,8 +1,8 @@
/*
* SMI (Serial Memory Controller) device driver for Serial NOR Flash on
* SPEAr platform
- * The serial nor interface is largely based on drivers/mtd/m25p80.c,
- * however the SPI interface has been replaced by SMI.
+ * The serial nor interface is largely based on m25p80.c, however the SPI
+ * interface has been replaced by SMI.
*
* Copyright © 2010 STMicroelectronics.
* Ashish Priyadarshi
@@ -854,6 +854,7 @@ static int spear_smi_setup_banks(struct platform_device *pdev,
else
flash->mtd.name = flash_devices[flash_index].name;
+ flash->mtd.dev.parent = &pdev->dev;
flash->mtd.type = MTD_NORFLASH;
flash->mtd.writesize = 1;
flash->mtd.flags = MTD_CAP_NORFLASH;
diff --git a/kernel/drivers/mtd/devices/sst25l.c b/kernel/drivers/mtd/devices/sst25l.c
index c63ecbcad..5b84d71ef 100644
--- a/kernel/drivers/mtd/devices/sst25l.c
+++ b/kernel/drivers/mtd/devices/sst25l.c
@@ -374,9 +374,8 @@ static int sst25l_probe(struct spi_device *spi)
data = dev_get_platdata(&spi->dev);
if (data && data->name)
flash->mtd.name = data->name;
- else
- flash->mtd.name = dev_name(&spi->dev);
+ flash->mtd.dev.parent = &spi->dev;
flash->mtd.type = MTD_NORFLASH;
flash->mtd.flags = MTD_CAP_NORFLASH;
flash->mtd.erasesize = flash_info->erase_size;
@@ -417,7 +416,6 @@ static int sst25l_remove(struct spi_device *spi)
static struct spi_driver sst25l_driver = {
.driver = {
.name = "sst25l",
- .owner = THIS_MODULE,
},
.probe = sst25l_probe,
.remove = sst25l_remove,