diff options
Diffstat (limited to 'kernel/drivers/mtd/devices')
-rw-r--r-- | kernel/drivers/mtd/devices/Kconfig | 8 | ||||
-rw-r--r-- | kernel/drivers/mtd/devices/Makefile | 1 | ||||
-rw-r--r-- | kernel/drivers/mtd/devices/bcm47xxsflash.c | 7 | ||||
-rw-r--r-- | kernel/drivers/mtd/devices/block2mtd.c | 1 | ||||
-rw-r--r-- | kernel/drivers/mtd/devices/docg3.c | 42 | ||||
-rw-r--r-- | kernel/drivers/mtd/devices/m25p80.c | 94 | ||||
-rw-r--r-- | kernel/drivers/mtd/devices/mtd_dataflash.c | 3 | ||||
-rw-r--r-- | kernel/drivers/mtd/devices/mtdram.c | 21 | ||||
-rw-r--r-- | kernel/drivers/mtd/devices/powernv_flash.c | 285 | ||||
-rw-r--r-- | kernel/drivers/mtd/devices/slram.c | 2 | ||||
-rw-r--r-- | kernel/drivers/mtd/devices/spear_smi.c | 5 | ||||
-rw-r--r-- | kernel/drivers/mtd/devices/sst25l.c | 4 |
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, |