123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- #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>
- #include <linux/mtd/partitions.h>
- static const char * const probes[] = { "ofpart", "bcm47xxpart", NULL };
- struct bcm53xxsf {
- struct spi_device *spi;
- 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)
- {
- 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 (nor->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 (nor->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 (nor->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 char *bcm53xxspiflash_chip_name(struct spi_nor *nor)
- {
- struct bcm53xxsf *b53sf = nor->priv;
- struct device *dev = &b53sf->spi->dev;
- unsigned char cmd[4];
- unsigned char resp[2];
- 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:
- return "w25q128";
- }
- dev_err(dev, "Unknown Winbond/NexFlash flash: %02X %02X\n",
- resp[0], resp[1]);
- return NULL;
- }
- /* TODO: Try more ID commands */
- return NULL;
- }
- /**************************************************
- * SPI driver
- **************************************************/
- static int bcm53xxspiflash_probe(struct spi_device *spi)
- {
- struct mtd_part_parser_data parser_data = {};
- struct bcm53xxsf *b53sf;
- struct spi_nor *nor;
- int err;
- b53sf = devm_kzalloc(&spi->dev, sizeof(*b53sf), GFP_KERNEL);
- if (!b53sf)
- return -ENOMEM;
- spi_set_drvdata(spi, b53sf);
- nor = &b53sf->nor;
- b53sf->spi = spi;
- nor->dev = &spi->dev;
- nor->read_reg = bcm53xxspiflash_read_reg;
- nor->write_reg = bcm53xxspiflash_write_reg;
- nor->read = bcm53xxspiflash_read;
- nor->write = bcm53xxspiflash_write;
- nor->erase = bcm53xxspiflash_erase;
- nor->priv = b53sf;
- err = spi_nor_scan(&b53sf->nor, bcm53xxspiflash_chip_name(nor),
- SPI_NOR_NORMAL);
- if (err)
- return err;
- parser_data.of_node = spi->master->dev.parent->of_node;
- err = mtd_device_parse_register(&nor->mtd, probes, &parser_data,
- 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);
|