diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/spi/Makefile | 2 | ||||
-rw-r--r-- | drivers/mtd/spi/sf.c | 53 | ||||
-rw-r--r-- | drivers/mtd/spi/sf_dataflash.c | 22 | ||||
-rw-r--r-- | drivers/mtd/spi/sf_internal.h | 19 | ||||
-rw-r--r-- | drivers/mtd/spi/spi-nor-core.c | 181 | ||||
-rw-r--r-- | drivers/mtd/spi/spi-nor-ids.c | 8 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 1 | ||||
-rw-r--r-- | drivers/spi/mvebu_a3700_spi.c | 5 | ||||
-rw-r--r-- | drivers/spi/soft_spi.c | 4 | ||||
-rw-r--r-- | drivers/spi/spi-uclass.c | 24 |
10 files changed, 228 insertions, 91 deletions
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index f99f6cb16e..20db1015d9 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -18,6 +18,6 @@ spi-nor-y += spi-nor-core.o endif obj-$(CONFIG_SPI_FLASH) += spi-nor.o -obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o sf.o +obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o obj-$(CONFIG_SPI_FLASH_MTD) += sf_mtd.o obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o diff --git a/drivers/mtd/spi/sf.c b/drivers/mtd/spi/sf.c deleted file mode 100644 index ee3cf8b759..0000000000 --- a/drivers/mtd/spi/sf.c +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * SPI flash interface - * - * Copyright (C) 2008 Atmel Corporation - * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik - */ - -#include <common.h> -#include <spi.h> - -static int spi_flash_read_write(struct spi_slave *spi, - const u8 *cmd, size_t cmd_len, - const u8 *data_out, u8 *data_in, - size_t data_len) -{ - unsigned long flags = SPI_XFER_BEGIN; - int ret; - - if (data_len == 0) - flags |= SPI_XFER_END; - - ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); - if (ret) { - debug("SF: Failed to send command (%zu bytes): %d\n", - cmd_len, ret); - } else if (data_len != 0) { - ret = spi_xfer(spi, data_len * 8, data_out, data_in, - SPI_XFER_END); - if (ret) - debug("SF: Failed to transfer %zu bytes of data: %d\n", - data_len, ret); - } - - return ret; -} - -int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, - size_t cmd_len, void *data, size_t data_len) -{ - return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len); -} - -int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len) -{ - return spi_flash_cmd_read(spi, &cmd, 1, response, len); -} - -int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, - const void *data, size_t data_len) -{ - return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len); -} diff --git a/drivers/mtd/spi/sf_dataflash.c b/drivers/mtd/spi/sf_dataflash.c index b6a2631747..55fb4bd31a 100644 --- a/drivers/mtd/spi/sf_dataflash.c +++ b/drivers/mtd/spi/sf_dataflash.c @@ -76,12 +76,14 @@ struct dataflash { static inline int dataflash_status(struct spi_slave *spi) { int ret; + u8 opcode = OP_READ_STATUS; u8 status; + /* * NOTE: at45db321c over 25 MHz wants to write * a dummy byte after the opcode... */ - ret = spi_flash_cmd(spi, OP_READ_STATUS, &status, 1); + ret = spi_write_then_read(spi, &opcode, 1, NULL, &status, 1); return ret ? -EIO : status; } @@ -173,7 +175,7 @@ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len) command[0], command[1], command[2], command[3], pageaddr); - status = spi_flash_cmd_write(spi, command, 4, NULL, 0); + status = spi_write_then_read(spi, command, 4, NULL, NULL, 0); if (status < 0) { debug("%s: erase send command error!\n", dev->name); return -EIO; @@ -248,7 +250,7 @@ static int spi_dataflash_read(struct udevice *dev, u32 offset, size_t len, command[3] = (uint8_t)(addr >> 0); /* plus 4 "don't care" bytes, command len: 4 + 4 "don't care" bytes */ - status = spi_flash_cmd_read(spi, command, 8, buf, len); + status = spi_write_then_read(spi, command, 8, NULL, buf, len); spi_release_bus(spi); @@ -327,7 +329,8 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len, debug("TRANSFER: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]); - status = spi_flash_cmd_write(spi, command, 4, NULL, 0); + status = spi_write_then_read(spi, command, 4, + NULL, NULL, 0); if (status < 0) { debug("%s: write(<pagesize) command error!\n", dev->name); @@ -352,8 +355,8 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len, debug("PROGRAM: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]); - status = spi_flash_cmd_write(spi, command, - 4, writebuf, writelen); + status = spi_write_then_read(spi, command, 4, + writebuf, NULL, writelen); if (status < 0) { debug("%s: write send command error!\n", dev->name); return -EIO; @@ -376,8 +379,8 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len, debug("COMPARE: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]); - status = spi_flash_cmd_write(spi, command, - 4, writebuf, writelen); + status = spi_write_then_read(spi, command, 4, + writebuf, NULL, writelen); if (status < 0) { debug("%s: write(compare) send command error!\n", dev->name); @@ -508,6 +511,7 @@ static struct data_flash_info *jedec_probe(struct spi_slave *spi) uint8_t id[5]; uint32_t jedec; struct data_flash_info *info; + u8 opcode = CMD_READ_ID; int status; /* @@ -519,7 +523,7 @@ static struct data_flash_info *jedec_probe(struct spi_slave *spi) * That's not an error; only rev C and newer chips handle it, and * only Atmel sells these chips. */ - tmp = spi_flash_cmd(spi, CMD_READ_ID, id, sizeof(id)); + tmp = spi_write_then_read(spi, &opcode, 1, NULL, id, sizeof(id)); if (tmp < 0) { printf("dataflash: error %d reading JEDEC ID\n", tmp); return ERR_PTR(tmp); diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index a6bf734830..bb8c19a31c 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -65,6 +65,7 @@ struct flash_info { #define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */ #define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */ #define USE_CLSR BIT(14) /* use CLSR command */ +#define SPI_NOR_HAS_SST26LOCK BIT(15) /* Flash supports lock/unlock via BPR */ }; extern const struct flash_info spi_nor_ids[]; @@ -72,24 +73,6 @@ extern const struct flash_info spi_nor_ids[]; #define JEDEC_MFR(info) ((info)->id[0]) #define JEDEC_ID(info) (((info)->id[1]) << 8 | ((info)->id[2])) -/* Send a single-byte command to the device and read the response */ -int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); - -/* - * Send a multi-byte command to the device and read the response. Used - * for flash array reads, etc. - */ -int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, - size_t cmd_len, void *data, size_t data_len); - -/* - * Send a multi-byte command to the device followed by (optional) - * data. Used for programming the flash array, etc. - */ -int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, - const void *data, size_t data_len); - - /* Get software write-protect value (BP bits) */ int spi_flash_cmd_get_sw_write_prot(struct spi_flash *flash); diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index 1acff745d1..990e39d7c2 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -945,6 +945,177 @@ read_err: } #ifdef CONFIG_SPI_FLASH_SST +/* + * sst26 flash series has its own block protection implementation: + * 4x - 8 KByte blocks - read & write protection bits - upper addresses + * 1x - 32 KByte blocks - write protection bits + * rest - 64 KByte blocks - write protection bits + * 1x - 32 KByte blocks - write protection bits + * 4x - 8 KByte blocks - read & write protection bits - lower addresses + * + * We'll support only per 64k lock/unlock so lower and upper 64 KByte region + * will be treated as single block. + */ +#define SST26_BPR_8K_NUM 4 +#define SST26_MAX_BPR_REG_LEN (18 + 1) +#define SST26_BOUND_REG_SIZE ((32 + SST26_BPR_8K_NUM * 8) * SZ_1K) + +enum lock_ctl { + SST26_CTL_LOCK, + SST26_CTL_UNLOCK, + SST26_CTL_CHECK +}; + +static bool sst26_process_bpr(u32 bpr_size, u8 *cmd, u32 bit, enum lock_ctl ctl) +{ + switch (ctl) { + case SST26_CTL_LOCK: + cmd[bpr_size - (bit / 8) - 1] |= BIT(bit % 8); + break; + case SST26_CTL_UNLOCK: + cmd[bpr_size - (bit / 8) - 1] &= ~BIT(bit % 8); + break; + case SST26_CTL_CHECK: + return !!(cmd[bpr_size - (bit / 8) - 1] & BIT(bit % 8)); + } + + return false; +} + +/* + * Lock, unlock or check lock status of the flash region of the flash (depending + * on the lock_ctl value) + */ +static int sst26_lock_ctl(struct spi_nor *nor, loff_t ofs, uint64_t len, enum lock_ctl ctl) +{ + struct mtd_info *mtd = &nor->mtd; + u32 i, bpr_ptr, rptr_64k, lptr_64k, bpr_size; + bool lower_64k = false, upper_64k = false; + u8 bpr_buff[SST26_MAX_BPR_REG_LEN] = {}; + int ret; + + /* Check length and offset for 64k alignment */ + if ((ofs & (SZ_64K - 1)) || (len & (SZ_64K - 1))) { + dev_err(nor->dev, "length or offset is not 64KiB allighned\n"); + return -EINVAL; + } + + if (ofs + len > mtd->size) { + dev_err(nor->dev, "range is more than device size: %#llx + %#llx > %#llx\n", + ofs, len, mtd->size); + return -EINVAL; + } + + /* SST26 family has only 16 Mbit, 32 Mbit and 64 Mbit IC */ + if (mtd->size != SZ_2M && + mtd->size != SZ_4M && + mtd->size != SZ_8M) + return -EINVAL; + + bpr_size = 2 + (mtd->size / SZ_64K / 8); + + ret = nor->read_reg(nor, SPINOR_OP_READ_BPR, bpr_buff, bpr_size); + if (ret < 0) { + dev_err(nor->dev, "fail to read block-protection register\n"); + return ret; + } + + rptr_64k = min_t(u32, ofs + len, mtd->size - SST26_BOUND_REG_SIZE); + lptr_64k = max_t(u32, ofs, SST26_BOUND_REG_SIZE); + + upper_64k = ((ofs + len) > (mtd->size - SST26_BOUND_REG_SIZE)); + lower_64k = (ofs < SST26_BOUND_REG_SIZE); + + /* Lower bits in block-protection register are about 64k region */ + bpr_ptr = lptr_64k / SZ_64K - 1; + + /* Process 64K blocks region */ + while (lptr_64k < rptr_64k) { + if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) + return EACCES; + + bpr_ptr++; + lptr_64k += SZ_64K; + } + + /* 32K and 8K region bits in BPR are after 64k region bits */ + bpr_ptr = (mtd->size - 2 * SST26_BOUND_REG_SIZE) / SZ_64K; + + /* Process lower 32K block region */ + if (lower_64k) + if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) + return EACCES; + + bpr_ptr++; + + /* Process upper 32K block region */ + if (upper_64k) + if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) + return EACCES; + + bpr_ptr++; + + /* Process lower 8K block regions */ + for (i = 0; i < SST26_BPR_8K_NUM; i++) { + if (lower_64k) + if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) + return EACCES; + + /* In 8K area BPR has both read and write protection bits */ + bpr_ptr += 2; + } + + /* Process upper 8K block regions */ + for (i = 0; i < SST26_BPR_8K_NUM; i++) { + if (upper_64k) + if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl)) + return EACCES; + + /* In 8K area BPR has both read and write protection bits */ + bpr_ptr += 2; + } + + /* If we check region status we don't need to write BPR back */ + if (ctl == SST26_CTL_CHECK) + return 0; + + ret = nor->write_reg(nor, SPINOR_OP_WRITE_BPR, bpr_buff, bpr_size); + if (ret < 0) { + dev_err(nor->dev, "fail to write block-protection register\n"); + return ret; + } + + return 0; +} + +static int sst26_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + return sst26_lock_ctl(nor, ofs, len, SST26_CTL_UNLOCK); +} + +static int sst26_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + return sst26_lock_ctl(nor, ofs, len, SST26_CTL_LOCK); +} + +/* + * Returns EACCES (positive value) if region is locked, 0 if region is unlocked, + * and negative on errors. + */ +static int sst26_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + /* + * is_locked function is used for check before reading or erasing flash + * region, so offset and length might be not 64k allighned, so adjust + * them to be 64k allighned as sst26_lock_ctl works only with 64k + * allighned regions. + */ + ofs -= ofs & (SZ_64K - 1); + len = len & (SZ_64K - 1) ? (len & ~(SZ_64K - 1)) + SZ_64K : len; + + return sst26_lock_ctl(nor, ofs, len, SST26_CTL_CHECK); +} + static int sst_write_byteprogram(struct spi_nor *nor, loff_t to, size_t len, size_t *retlen, const u_char *buf) { @@ -2302,6 +2473,16 @@ int spi_nor_scan(struct spi_nor *nor) #endif #ifdef CONFIG_SPI_FLASH_SST + /* + * sst26 series block protection implementation differs from other + * series. + */ + if (info->flags & SPI_NOR_HAS_SST26LOCK) { + nor->flash_lock = sst26_lock; + nor->flash_unlock = sst26_unlock; + nor->flash_is_locked = sst26_is_locked; + } + /* sst nor chips use AAI word program */ if (info->flags & SST_WRITE) mtd->_write = sst_write; diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c index a3920ba520..6996c0a286 100644 --- a/drivers/mtd/spi/spi-nor-ids.c +++ b/drivers/mtd/spi/spi-nor-ids.c @@ -214,10 +214,10 @@ const struct flash_info spi_nor_ids[] = { { INFO("sst25wf040b", 0x621613, 0, 64 * 1024, 8, SECT_4K) }, { INFO("sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, { INFO("sst25wf080", 0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, - { INFO("sst26vf064b", 0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { INFO("sst26wf016", 0xbf2651, 0, 64 * 1024, 32, SECT_4K) }, - { INFO("sst26wf032", 0xbf2622, 0, 64 * 1024, 64, SECT_4K) }, - { INFO("sst26wf064", 0xbf2643, 0, 64 * 1024, 128, SECT_4K) }, + { INFO("sst26vf064b", 0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_SST26LOCK | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("sst26wf016", 0xbf2651, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_HAS_SST26LOCK) }, + { INFO("sst26wf032", 0xbf2622, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_SST26LOCK) }, + { INFO("sst26wf064", 0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_SST26LOCK) }, #endif #ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */ /* ST Microelectronics -- newer production may have feature updates */ diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f459c0a411..8dd3213d48 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -419,7 +419,6 @@ config MXC_SPI config MXS_SPI bool "MXS SPI Driver" - depends on DEPRECATED help Enable the MXS SPI controller driver. This driver can be used on the i.MX23 and i.MX28 SoCs. diff --git a/drivers/spi/mvebu_a3700_spi.c b/drivers/spi/mvebu_a3700_spi.c index feeafdceaa..99ad505f24 100644 --- a/drivers/spi/mvebu_a3700_spi.c +++ b/drivers/spi/mvebu_a3700_spi.c @@ -181,10 +181,9 @@ static int mvebu_spi_set_speed(struct udevice *bus, uint hz) data = readl(®->cfg); prescale = DIV_ROUND_UP(clk_get_rate(&plat->clk), hz); - if (prescale > 0x1f) - prescale = 0x1f; - else if (prescale > 0xf) + if (prescale > 0xf) prescale = 0x10 + (prescale + 1) / 2; + prescale = min(prescale, 0x1fu); data &= ~MVEBU_SPI_A3700_CLK_PRESCALE_MASK; data |= prescale & MVEBU_SPI_A3700_CLK_PRESCALE_MASK; diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c index b06883f9d0..b80f810bd1 100644 --- a/drivers/spi/soft_spi.c +++ b/drivers/spi/soft_spi.c @@ -215,8 +215,8 @@ static int soft_spi_probe(struct udevice *dev) int cs_flags, clk_flags; int ret; - cs_flags = (slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW; - clk_flags = (slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0; + cs_flags = (slave && slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW; + clk_flags = (slave && slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0; if (gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs, GPIOD_IS_OUT | cs_flags) || diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 88cb2a1262..76c4b53c16 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -108,6 +108,30 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, return dm_spi_xfer(slave->dev, bitlen, dout, din, flags); } +int spi_write_then_read(struct spi_slave *slave, const u8 *opcode, + size_t n_opcode, const u8 *txbuf, u8 *rxbuf, + size_t n_buf) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (n_buf == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(slave, n_opcode * 8, opcode, NULL, flags); + if (ret) { + debug("spi: failed to send command (%zu bytes): %d\n", + n_opcode, ret); + } else if (n_buf != 0) { + ret = spi_xfer(slave, n_buf * 8, txbuf, rxbuf, SPI_XFER_END); + if (ret) + debug("spi: failed to transfer %zu bytes of data: %d\n", + n_buf, ret); + } + + return ret; +} + #if !CONFIG_IS_ENABLED(OF_PLATDATA) static int spi_child_post_bind(struct udevice *dev) { |