Signed-off-by: Rafał Miłecki <zajec5@gmail.com> SVN-Revision: 42260master
parent
b118879cc3
commit
cc3545d165
@ -0,0 +1,263 @@ |
||||
--- a/drivers/mtd/spi-nor/Kconfig
|
||||
+++ b/drivers/mtd/spi-nor/Kconfig
|
||||
@@ -28,4 +28,10 @@ config SPI_FSL_QUADSPI
|
||||
This enables support for the Quad SPI controller in master mode.
|
||||
We only connect the NOR to this controller now.
|
||||
|
||||
+config MTD_SPI_BCM53XXSPIFLASH
|
||||
+ tristate "SPI-NOR flashes connected to the Broadcom ARM SoC"
|
||||
+ depends on MTD_SPI_NOR
|
||||
+ help
|
||||
+ SPI driver for flashes used on Broadcom ARM SoCs.
|
||||
+
|
||||
endif # MTD_SPI_NOR
|
||||
--- a/drivers/mtd/spi-nor/Makefile
|
||||
+++ b/drivers/mtd/spi-nor/Makefile
|
||||
@@ -1,2 +1,3 @@
|
||||
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
|
||||
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
|
||||
+obj-$(CONFIG_MTD_SPI_BCM53XXSPIFLASH) += bcm53xxspiflash.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/mtd/spi-nor/bcm53xxspiflash.c
|
||||
@@ -0,0 +1,241 @@
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/spi/spi.h>
|
||||
+#include <linux/mtd/spi-nor.h>
|
||||
+#include <linux/mtd/mtd.h>
|
||||
+#include <linux/mtd/cfi.h>
|
||||
+
|
||||
+static const char * const probes[] = { "bcm47xxpart", NULL };
|
||||
+
|
||||
+struct bcm53xxsf {
|
||||
+ struct spi_device *spi;
|
||||
+ struct mtd_info mtd;
|
||||
+ struct spi_nor nor;
|
||||
+};
|
||||
+
|
||||
+/**************************************************
|
||||
+ * spi-nor API
|
||||
+ **************************************************/
|
||||
+
|
||||
+static int bcm53xxspiflash_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
|
||||
+ int len)
|
||||
+{
|
||||
+ struct bcm53xxsf *b53sf = nor->priv;
|
||||
+
|
||||
+ return spi_write_then_read(b53sf->spi, &opcode, 1, buf, len);
|
||||
+}
|
||||
+
|
||||
+static int bcm53xxspiflash_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
|
||||
+ int len, int write_enable)
|
||||
+{
|
||||
+ struct bcm53xxsf *b53sf = nor->priv;
|
||||
+ u8 *cmd = kzalloc(len + 1, GFP_KERNEL);
|
||||
+ int err;
|
||||
+
|
||||
+ if (!cmd)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ cmd[0] = opcode;
|
||||
+ memcpy(&cmd[1], buf, len);
|
||||
+ err = spi_write(b53sf->spi, cmd, len + 1);
|
||||
+
|
||||
+ kfree(cmd);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+static int bcm53xxspiflash_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
+ size_t *retlen, u_char *buf)
|
||||
+{
|
||||
+ struct bcm53xxsf *b53sf = nor->priv;
|
||||
+ struct spi_message m;
|
||||
+ struct spi_transfer t[2] = { { 0 }, { 0 } };
|
||||
+ unsigned char cmd[5];
|
||||
+ int cmd_len = 0;
|
||||
+ int err;
|
||||
+
|
||||
+ spi_message_init(&m);
|
||||
+
|
||||
+ cmd[cmd_len++] = SPINOR_OP_READ;
|
||||
+ if (b53sf->mtd.size > 0x1000000)
|
||||
+ cmd[cmd_len++] = (from & 0xFF000000) >> 24;
|
||||
+ cmd[cmd_len++] = (from & 0x00FF0000) >> 16;
|
||||
+ cmd[cmd_len++] = (from & 0x0000FF00) >> 8;
|
||||
+ cmd[cmd_len++] = (from & 0x000000FF) >> 0;
|
||||
+
|
||||
+ t[0].tx_buf = cmd;
|
||||
+ t[0].len = cmd_len;
|
||||
+ spi_message_add_tail(&t[0], &m);
|
||||
+
|
||||
+ t[1].rx_buf = buf;
|
||||
+ t[1].len = len;
|
||||
+ spi_message_add_tail(&t[1], &m);
|
||||
+
|
||||
+ err = spi_sync(b53sf->spi, &m);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ if (retlen && m.actual_length > cmd_len)
|
||||
+ *retlen = m.actual_length - cmd_len;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void bcm53xxspiflash_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
+ size_t *retlen, const u_char *buf)
|
||||
+{
|
||||
+ struct bcm53xxsf *b53sf = nor->priv;
|
||||
+ struct spi_message m;
|
||||
+ struct spi_transfer t = { 0 };
|
||||
+ u8 *cmd = kzalloc(len + 5, GFP_KERNEL);
|
||||
+ int cmd_len = 0;
|
||||
+ int err;
|
||||
+
|
||||
+ if (!cmd)
|
||||
+ return;
|
||||
+
|
||||
+ spi_message_init(&m);
|
||||
+
|
||||
+ cmd[cmd_len++] = nor->program_opcode;
|
||||
+ if (b53sf->mtd.size > 0x1000000)
|
||||
+ cmd[cmd_len++] = (to & 0xFF000000) >> 24;
|
||||
+ cmd[cmd_len++] = (to & 0x00FF0000) >> 16;
|
||||
+ cmd[cmd_len++] = (to & 0x0000FF00) >> 8;
|
||||
+ cmd[cmd_len++] = (to & 0x000000FF) >> 0;
|
||||
+ memcpy(&cmd[cmd_len], buf, len);
|
||||
+
|
||||
+ t.tx_buf = cmd;
|
||||
+ t.len = cmd_len + len;
|
||||
+ spi_message_add_tail(&t, &m);
|
||||
+
|
||||
+ err = spi_sync(b53sf->spi, &m);
|
||||
+ if (err)
|
||||
+ goto out;
|
||||
+
|
||||
+ if (retlen && m.actual_length > cmd_len)
|
||||
+ *retlen += m.actual_length - cmd_len;
|
||||
+
|
||||
+out:
|
||||
+ kfree(cmd);
|
||||
+}
|
||||
+
|
||||
+static int bcm53xxspiflash_erase(struct spi_nor *nor, loff_t offs)
|
||||
+{
|
||||
+ struct bcm53xxsf *b53sf = nor->priv;
|
||||
+ unsigned char cmd[5];
|
||||
+ int i;
|
||||
+
|
||||
+ i = 0;
|
||||
+ cmd[i++] = nor->erase_opcode;
|
||||
+ if (b53sf->mtd.size > 0x1000000)
|
||||
+ cmd[i++] = (offs & 0xFF000000) >> 24;
|
||||
+ cmd[i++] = ((offs & 0x00FF0000) >> 16);
|
||||
+ cmd[i++] = ((offs & 0x0000FF00) >> 8);
|
||||
+ cmd[i++] = ((offs & 0x000000FF) >> 0);
|
||||
+
|
||||
+ return spi_write(b53sf->spi, cmd, i);
|
||||
+}
|
||||
+
|
||||
+static const struct spi_device_id *bcm53xxspiflash_read_id(struct spi_nor *nor)
|
||||
+{
|
||||
+ struct bcm53xxsf *b53sf = nor->priv;
|
||||
+ struct device *dev = &b53sf->spi->dev;
|
||||
+ const struct spi_device_id *id;
|
||||
+ unsigned char cmd[4];
|
||||
+ unsigned char resp[2];
|
||||
+ char *name = NULL;
|
||||
+ int err;
|
||||
+
|
||||
+ /* SST and Winbond/NexFlash specific command */
|
||||
+ cmd[0] = 0x90; /* Read Manufacturer / Device ID */
|
||||
+ cmd[1] = 0;
|
||||
+ cmd[2] = 0;
|
||||
+ cmd[3] = 0;
|
||||
+ err = spi_write_then_read(b53sf->spi, cmd, 4, resp, 2);
|
||||
+ if (err < 0) {
|
||||
+ dev_err(dev, "error reading SPI flash id\n");
|
||||
+ return ERR_PTR(-EBUSY);
|
||||
+ }
|
||||
+ switch (resp[0]) {
|
||||
+ case 0xef: /* Winbond/NexFlash */
|
||||
+ switch (resp[1]) {
|
||||
+ case 0x17:
|
||||
+ name = "w25q128";
|
||||
+ break;
|
||||
+ }
|
||||
+ if (!name) {
|
||||
+ dev_err(dev, "Unknown Winbond/NexFlash flash: %02X %02X\n",
|
||||
+ resp[0], resp[1]);
|
||||
+ return ERR_PTR(-ENOTSUPP);
|
||||
+ }
|
||||
+ goto found_name;
|
||||
+ }
|
||||
+
|
||||
+ /* TODO: Try more ID commands */
|
||||
+
|
||||
+ return ERR_PTR(-ENODEV);
|
||||
+
|
||||
+found_name:
|
||||
+ id = spi_nor_match_id(name);
|
||||
+ if (!id) {
|
||||
+ dev_err(dev, "No matching entry for %s flash\n", name);
|
||||
+ return ERR_PTR(-ENOENT);
|
||||
+ }
|
||||
+
|
||||
+ return id;
|
||||
+}
|
||||
+
|
||||
+/**************************************************
|
||||
+ * SPI driver
|
||||
+ **************************************************/
|
||||
+
|
||||
+static int bcm53xxspiflash_probe(struct spi_device *spi)
|
||||
+{
|
||||
+ struct bcm53xxsf *b53sf;
|
||||
+ int err;
|
||||
+
|
||||
+ b53sf = devm_kzalloc(&spi->dev, sizeof(*b53sf), GFP_KERNEL);
|
||||
+ if (!b53sf)
|
||||
+ return -ENOMEM;
|
||||
+ spi_set_drvdata(spi, b53sf);
|
||||
+
|
||||
+ b53sf->spi = spi;
|
||||
+
|
||||
+ b53sf->mtd.priv = &b53sf->nor;
|
||||
+
|
||||
+ b53sf->nor.mtd = &b53sf->mtd;
|
||||
+ b53sf->nor.dev = &spi->dev;
|
||||
+ b53sf->nor.read_reg = bcm53xxspiflash_read_reg;
|
||||
+ b53sf->nor.write_reg = bcm53xxspiflash_write_reg;
|
||||
+ b53sf->nor.read = bcm53xxspiflash_read;
|
||||
+ b53sf->nor.write = bcm53xxspiflash_write;
|
||||
+ b53sf->nor.erase = bcm53xxspiflash_erase;
|
||||
+ b53sf->nor.read_id = bcm53xxspiflash_read_id;
|
||||
+ b53sf->nor.priv = b53sf;
|
||||
+
|
||||
+ err = spi_nor_scan(&b53sf->nor, NULL, SPI_NOR_NORMAL);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ err = mtd_device_parse_register(&b53sf->mtd, probes, NULL, NULL, 0);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int bcm53xxspiflash_remove(struct spi_device *spi)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct spi_driver bcm53xxspiflash_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "bcm53xxspiflash",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+ .probe = bcm53xxspiflash_probe,
|
||||
+ .remove = bcm53xxspiflash_remove,
|
||||
+};
|
||||
+
|
||||
+module_spi_driver(bcm53xxspiflash_driver);
|
@ -0,0 +1,42 @@ |
||||
--- a/drivers/mtd/spi-nor/bcm53xxspiflash.c
|
||||
+++ b/drivers/mtd/spi-nor/bcm53xxspiflash.c
|
||||
@@ -173,7 +173,8 @@ static const struct spi_device_id *bcm53
|
||||
|
||||
/* TODO: Try more ID commands */
|
||||
|
||||
- return ERR_PTR(-ENODEV);
|
||||
+ /* Some chips used by Broadcom may actually support JEDEC */
|
||||
+ return spi_nor_read_id(nor);
|
||||
|
||||
found_name:
|
||||
id = spi_nor_match_id(name);
|
||||
--- a/drivers/mtd/spi-nor/spi-nor.c
|
||||
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
||||
@@ -629,7 +629,7 @@ const struct spi_device_id spi_nor_ids[]
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(spi_nor_ids);
|
||||
|
||||
-static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
|
||||
+const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
|
||||
{
|
||||
int tmp;
|
||||
u8 id[5];
|
||||
@@ -660,6 +660,7 @@ static const struct spi_device_id *spi_n
|
||||
dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(spi_nor_read_id);
|
||||
|
||||
static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
--- a/include/linux/mtd/spi-nor.h
|
||||
+++ b/include/linux/mtd/spi-nor.h
|
||||
@@ -188,6 +188,8 @@ struct spi_nor {
|
||||
void *priv;
|
||||
};
|
||||
|
||||
+const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor);
|
||||
+
|
||||
/**
|
||||
* spi_nor_scan() - scan the SPI NOR
|
||||
* @nor: the spi_nor structure
|
Loading…
Reference in new issue