bcm53xx: add bcm53xxspiflash driver for SPI flashes
authorHauke Mehrtens <hauke@hauke-m.de>
Thu, 21 Aug 2014 21:11:04 +0000 (21:11 +0000)
committerHauke Mehrtens <hauke@hauke-m.de>
Thu, 21 Aug 2014 21:11:04 +0000 (21:11 +0000)
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
SVN-Revision: 42260

target/linux/bcm53xx/config-3.14
target/linux/bcm53xx/patches-3.14/404-mtd-bcm53xxspiflash-new-driver-for-SPI-flahes-on-Bro.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-3.14/405-mtd-bcm53xxspiflash-try-using-JEDEC-as-one-of-method.patch [new file with mode: 0644]

index e0fd0fd2d221a6f2f6167296a723af8b6d02f75a..2192b83d1852e05ff564272ad5d4d8227122af62 100644 (file)
@@ -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 (file)
index 0000000..9a9d903
--- /dev/null
@@ -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 (file)
index 0000000..f2bb054
--- /dev/null
@@ -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