diff options
author | Hauke Mehrtens <hauke@hauke-m.de> | 2014-08-21 21:11:04 +0000 |
---|---|---|
committer | Hauke Mehrtens <hauke@hauke-m.de> | 2014-08-21 21:11:04 +0000 |
commit | cc3545d165d835411bda533f702dd833741e92a2 (patch) | |
tree | 4a841bd6beb889c1bcf96cd3e79abbee1399c0c9 /target/linux | |
parent | b118879cc39270c623239a9775ad3acdfdbbcc35 (diff) | |
download | upstream-cc3545d165d835411bda533f702dd833741e92a2.tar.gz upstream-cc3545d165d835411bda533f702dd833741e92a2.tar.bz2 upstream-cc3545d165d835411bda533f702dd833741e92a2.zip |
bcm53xx: add bcm53xxspiflash driver for SPI flashes
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
SVN-Revision: 42260
Diffstat (limited to 'target/linux')
3 files changed, 306 insertions, 0 deletions
diff --git a/target/linux/bcm53xx/config-3.14 b/target/linux/bcm53xx/config-3.14 index e0fd0fd2d2..2192b83d18 100644 --- a/target/linux/bcm53xx/config-3.14 +++ b/target/linux/bcm53xx/config-3.14 @@ -167,6 +167,7 @@ CONFIG_MIGHT_HAVE_PCI=y CONFIG_MODULES_USE_ELF_REL=y CONFIG_MTD_BCM47XX_PARTS=y # CONFIG_MTD_PHYSMAP_OF is not set +CONFIG_MTD_SPI_BCM53XXSPIFLASH=y CONFIG_MTD_SPI_NOR=y # CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set CONFIG_MULTI_IRQ_HANDLER=y diff --git a/target/linux/bcm53xx/patches-3.14/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch b/target/linux/bcm53xx/patches-3.14/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch new file mode 100644 index 0000000000..9a9d903f82 --- /dev/null +++ b/target/linux/bcm53xx/patches-3.14/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch @@ -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); diff --git a/target/linux/bcm53xx/patches-3.14/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch b/target/linux/bcm53xx/patches-3.14/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch new file mode 100644 index 0000000000..f2bb0542b9 --- /dev/null +++ b/target/linux/bcm53xx/patches-3.14/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch @@ -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 |