From cc3545d165d835411bda533f702dd833741e92a2 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Thu, 21 Aug 2014 21:11:04 +0000 Subject: [PATCH] bcm53xx: add bcm53xxspiflash driver for SPI flashes MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki SVN-Revision: 42260 --- target/linux/bcm53xx/config-3.14 | 1 + ...ash-new-driver-for-SPI-flahes-on-Bro.patch | 263 ++++++++++++++++++ ...ash-try-using-JEDEC-as-one-of-method.patch | 42 +++ 3 files changed, 306 insertions(+) create mode 100644 target/linux/bcm53xx/patches-3.14/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch create mode 100644 target/linux/bcm53xx/patches-3.14/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch diff --git a/target/linux/bcm53xx/config-3.14 b/target/linux/bcm53xx/config-3.14 index e0fd0fd2d221..2192b83d1852 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 000000000000..9a9d903f825a --- /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 ++#include ++#include ++#include ++#include ++#include ++ ++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 000000000000..f2bb0542b91c --- /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 -- 2.30.2