273 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2005, Intec Automation Inc.4 * Copyright (C) 2014, Freescale Semiconductor, Inc.5 */6 7#include <linux/mtd/spi-nor.h>8 9#include "core.h"10 11/* SST flash_info mfr_flag. Used to specify SST byte programming. */12#define SST_WRITE BIT(0)13 14#define SST26VF_CR_BPNV BIT(3)15 16static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len)17{18 return -EOPNOTSUPP;19}20 21static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 len)22{23 int ret;24 25 /* We only support unlocking the entire flash array. */26 if (ofs != 0 || len != nor->params->size)27 return -EINVAL;28 29 ret = spi_nor_read_cr(nor, nor->bouncebuf);30 if (ret)31 return ret;32 33 if (!(nor->bouncebuf[0] & SST26VF_CR_BPNV)) {34 dev_dbg(nor->dev, "Any block has been permanently locked\n");35 return -EINVAL;36 }37 38 return spi_nor_global_block_unlock(nor);39}40 41static int sst26vf_nor_is_locked(struct spi_nor *nor, loff_t ofs, u64 len)42{43 return -EOPNOTSUPP;44}45 46static const struct spi_nor_locking_ops sst26vf_nor_locking_ops = {47 .lock = sst26vf_nor_lock,48 .unlock = sst26vf_nor_unlock,49 .is_locked = sst26vf_nor_is_locked,50};51 52static int sst26vf_nor_late_init(struct spi_nor *nor)53{54 nor->params->locking_ops = &sst26vf_nor_locking_ops;55 56 return 0;57}58 59static const struct spi_nor_fixups sst26vf_nor_fixups = {60 .late_init = sst26vf_nor_late_init,61};62 63static const struct flash_info sst_nor_parts[] = {64 {65 .id = SNOR_ID(0x62, 0x16, 0x12),66 .name = "sst25wf020a",67 .size = SZ_256K,68 .flags = SPI_NOR_HAS_LOCK,69 .no_sfdp_flags = SECT_4K,70 }, {71 .id = SNOR_ID(0x62, 0x16, 0x13),72 .name = "sst25wf040b",73 .size = SZ_512K,74 .flags = SPI_NOR_HAS_LOCK,75 .no_sfdp_flags = SECT_4K,76 }, {77 .id = SNOR_ID(0xbf, 0x25, 0x01),78 .name = "sst25wf512",79 .size = SZ_64K,80 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,81 .no_sfdp_flags = SECT_4K,82 .mfr_flags = SST_WRITE,83 }, {84 .id = SNOR_ID(0xbf, 0x25, 0x02),85 .name = "sst25wf010",86 .size = SZ_128K,87 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,88 .no_sfdp_flags = SECT_4K,89 .mfr_flags = SST_WRITE,90 }, {91 .id = SNOR_ID(0xbf, 0x25, 0x03),92 .name = "sst25wf020",93 .size = SZ_256K,94 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,95 .no_sfdp_flags = SECT_4K,96 .mfr_flags = SST_WRITE,97 }, {98 .id = SNOR_ID(0xbf, 0x25, 0x04),99 .name = "sst25wf040",100 .size = SZ_512K,101 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,102 .no_sfdp_flags = SECT_4K,103 .mfr_flags = SST_WRITE,104 }, {105 .id = SNOR_ID(0xbf, 0x25, 0x05),106 .name = "sst25wf080",107 .size = SZ_1M,108 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,109 .no_sfdp_flags = SECT_4K,110 .mfr_flags = SST_WRITE,111 }, {112 .id = SNOR_ID(0xbf, 0x25, 0x41),113 .name = "sst25vf016b",114 .size = SZ_2M,115 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,116 .no_sfdp_flags = SECT_4K,117 .mfr_flags = SST_WRITE,118 }, {119 .id = SNOR_ID(0xbf, 0x25, 0x4a),120 .name = "sst25vf032b",121 .size = SZ_4M,122 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,123 .no_sfdp_flags = SECT_4K,124 .mfr_flags = SST_WRITE,125 }, {126 .id = SNOR_ID(0xbf, 0x25, 0x4b),127 .name = "sst25vf064c",128 .size = SZ_8M,129 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP | SPI_NOR_SWP_IS_VOLATILE,130 .no_sfdp_flags = SECT_4K,131 }, {132 .id = SNOR_ID(0xbf, 0x25, 0x8d),133 .name = "sst25vf040b",134 .size = SZ_512K,135 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,136 .no_sfdp_flags = SECT_4K,137 .mfr_flags = SST_WRITE,138 }, {139 .id = SNOR_ID(0xbf, 0x25, 0x8e),140 .name = "sst25vf080b",141 .size = SZ_1M,142 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,143 .no_sfdp_flags = SECT_4K,144 .mfr_flags = SST_WRITE,145 }, {146 .id = SNOR_ID(0xbf, 0x26, 0x41),147 .name = "sst26vf016b",148 .size = SZ_2M,149 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ,150 }, {151 .id = SNOR_ID(0xbf, 0x26, 0x42),152 .name = "sst26vf032b",153 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,154 .fixups = &sst26vf_nor_fixups,155 }, {156 .id = SNOR_ID(0xbf, 0x26, 0x43),157 .name = "sst26vf064b",158 .size = SZ_8M,159 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,160 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,161 .fixups = &sst26vf_nor_fixups,162 }, {163 .id = SNOR_ID(0xbf, 0x26, 0x51),164 .name = "sst26wf016b",165 .size = SZ_2M,166 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,167 }168};169 170static int sst_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,171 const u_char *buf)172{173 u8 op = (len == 1) ? SPINOR_OP_BP : SPINOR_OP_AAI_WP;174 int ret;175 176 nor->program_opcode = op;177 ret = spi_nor_write_data(nor, to, 1, buf);178 if (ret < 0)179 return ret;180 WARN(ret != len, "While writing %zu byte written %i bytes\n", len, ret);181 182 return spi_nor_wait_till_ready(nor);183}184 185static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len,186 size_t *retlen, const u_char *buf)187{188 struct spi_nor *nor = mtd_to_spi_nor(mtd);189 size_t actual = 0;190 int ret;191 192 dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);193 194 ret = spi_nor_prep_and_lock(nor);195 if (ret)196 return ret;197 198 ret = spi_nor_write_enable(nor);199 if (ret)200 goto out;201 202 nor->sst_write_second = false;203 204 /* Start write from odd address. */205 if (to % 2) {206 /* write one byte. */207 ret = sst_nor_write_data(nor, to, 1, buf);208 if (ret < 0)209 goto out;210 211 to++;212 actual++;213 }214 215 /* Write out most of the data here. */216 for (; actual < len - 1; actual += 2) {217 /* write two bytes. */218 ret = sst_nor_write_data(nor, to, 2, buf + actual);219 if (ret < 0)220 goto out;221 222 to += 2;223 nor->sst_write_second = true;224 }225 nor->sst_write_second = false;226 227 ret = spi_nor_write_disable(nor);228 if (ret)229 goto out;230 231 ret = spi_nor_wait_till_ready(nor);232 if (ret)233 goto out;234 235 /* Write out trailing byte if it exists. */236 if (actual != len) {237 ret = spi_nor_write_enable(nor);238 if (ret)239 goto out;240 241 ret = sst_nor_write_data(nor, to, 1, buf + actual);242 if (ret < 0)243 goto out;244 245 actual += 1;246 247 ret = spi_nor_write_disable(nor);248 }249out:250 *retlen += actual;251 spi_nor_unlock_and_unprep(nor);252 return ret;253}254 255static int sst_nor_late_init(struct spi_nor *nor)256{257 if (nor->info->mfr_flags & SST_WRITE)258 nor->mtd._write = sst_nor_write;259 260 return 0;261}262 263static const struct spi_nor_fixups sst_nor_fixups = {264 .late_init = sst_nor_late_init,265};266 267const struct spi_nor_manufacturer spi_nor_sst = {268 .name = "sst",269 .parts = sst_nor_parts,270 .nparts = ARRAY_SIZE(sst_nor_parts),271 .fixups = &sst_nor_fixups,272};273