atmel.c (6266B)
1// SPDX-License-Identifier: GPL-2.0 2/* 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#define ATMEL_SR_GLOBAL_PROTECT_MASK GENMASK(5, 2) 12 13/* 14 * The Atmel AT25FS010/AT25FS040 parts have some weird configuration for the 15 * block protection bits. We don't support them. But legacy behavior in linux 16 * is to unlock the whole flash array on startup. Therefore, we have to support 17 * exactly this operation. 18 */ 19static int at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) 20{ 21 return -EOPNOTSUPP; 22} 23 24static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) 25{ 26 int ret; 27 28 /* We only support unlocking the whole flash array */ 29 if (ofs || len != nor->params->size) 30 return -EINVAL; 31 32 /* Write 0x00 to the status register to disable write protection */ 33 ret = spi_nor_write_sr_and_check(nor, 0); 34 if (ret) 35 dev_dbg(nor->dev, "unable to clear BP bits, WP# asserted?\n"); 36 37 return ret; 38} 39 40static int at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) 41{ 42 return -EOPNOTSUPP; 43} 44 45static const struct spi_nor_locking_ops at25fs_nor_locking_ops = { 46 .lock = at25fs_nor_lock, 47 .unlock = at25fs_nor_unlock, 48 .is_locked = at25fs_nor_is_locked, 49}; 50 51static void at25fs_nor_late_init(struct spi_nor *nor) 52{ 53 nor->params->locking_ops = &at25fs_nor_locking_ops; 54} 55 56static const struct spi_nor_fixups at25fs_nor_fixups = { 57 .late_init = at25fs_nor_late_init, 58}; 59 60/** 61 * atmel_nor_set_global_protection - Do a Global Protect or Unprotect command 62 * @nor: pointer to 'struct spi_nor' 63 * @ofs: offset in bytes 64 * @len: len in bytes 65 * @is_protect: if true do a Global Protect otherwise it is a Global Unprotect 66 * 67 * Return: 0 on success, -error otherwise. 68 */ 69static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs, 70 uint64_t len, bool is_protect) 71{ 72 int ret; 73 u8 sr; 74 75 /* We only support locking the whole flash array */ 76 if (ofs || len != nor->params->size) 77 return -EINVAL; 78 79 ret = spi_nor_read_sr(nor, nor->bouncebuf); 80 if (ret) 81 return ret; 82 83 sr = nor->bouncebuf[0]; 84 85 /* SRWD bit needs to be cleared, otherwise the protection doesn't change */ 86 if (sr & SR_SRWD) { 87 sr &= ~SR_SRWD; 88 ret = spi_nor_write_sr_and_check(nor, sr); 89 if (ret) { 90 dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n"); 91 return ret; 92 } 93 } 94 95 if (is_protect) { 96 sr |= ATMEL_SR_GLOBAL_PROTECT_MASK; 97 /* 98 * Set the SRWD bit again as soon as we are protecting 99 * anything. This will ensure that the WP# pin is working 100 * correctly. By doing this we also behave the same as 101 * spi_nor_sr_lock(), which sets SRWD if any block protection 102 * is active. 103 */ 104 sr |= SR_SRWD; 105 } else { 106 sr &= ~ATMEL_SR_GLOBAL_PROTECT_MASK; 107 } 108 109 nor->bouncebuf[0] = sr; 110 111 /* 112 * We cannot use the spi_nor_write_sr_and_check() because this command 113 * isn't really setting any bits, instead it is an pseudo command for 114 * "Global Unprotect" or "Global Protect" 115 */ 116 return spi_nor_write_sr(nor, nor->bouncebuf, 1); 117} 118 119static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs, 120 uint64_t len) 121{ 122 return atmel_nor_set_global_protection(nor, ofs, len, true); 123} 124 125static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs, 126 uint64_t len) 127{ 128 return atmel_nor_set_global_protection(nor, ofs, len, false); 129} 130 131static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs, 132 uint64_t len) 133{ 134 int ret; 135 136 if (ofs >= nor->params->size || (ofs + len) > nor->params->size) 137 return -EINVAL; 138 139 ret = spi_nor_read_sr(nor, nor->bouncebuf); 140 if (ret) 141 return ret; 142 143 return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK); 144} 145 146static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = { 147 .lock = atmel_nor_global_protect, 148 .unlock = atmel_nor_global_unprotect, 149 .is_locked = atmel_nor_is_global_protected, 150}; 151 152static void atmel_nor_global_protection_late_init(struct spi_nor *nor) 153{ 154 nor->params->locking_ops = &atmel_nor_global_protection_ops; 155} 156 157static const struct spi_nor_fixups atmel_nor_global_protection_fixups = { 158 .late_init = atmel_nor_global_protection_late_init, 159}; 160 161static const struct flash_info atmel_nor_parts[] = { 162 /* Atmel -- some are (confusingly) marketed as "DataFlash" */ 163 { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4) 164 FLAGS(SPI_NOR_HAS_LOCK) 165 NO_SFDP_FLAGS(SECT_4K) 166 .fixups = &at25fs_nor_fixups }, 167 { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8) 168 FLAGS(SPI_NOR_HAS_LOCK) 169 NO_SFDP_FLAGS(SECT_4K) 170 .fixups = &at25fs_nor_fixups }, 171 { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8) 172 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 173 NO_SFDP_FLAGS(SECT_4K) 174 .fixups = &atmel_nor_global_protection_fixups }, 175 { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64) 176 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 177 NO_SFDP_FLAGS(SECT_4K) 178 .fixups = &atmel_nor_global_protection_fixups }, 179 { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64) 180 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 181 NO_SFDP_FLAGS(SECT_4K) 182 .fixups = &atmel_nor_global_protection_fixups }, 183 { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128) 184 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 185 NO_SFDP_FLAGS(SECT_4K) 186 .fixups = &atmel_nor_global_protection_fixups }, 187 { "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64) 188 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, 189 { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8) 190 NO_SFDP_FLAGS(SECT_4K) }, 191 { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16) 192 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 193 NO_SFDP_FLAGS(SECT_4K) 194 .fixups = &atmel_nor_global_protection_fixups }, 195 { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32) 196 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 197 NO_SFDP_FLAGS(SECT_4K) 198 .fixups = &atmel_nor_global_protection_fixups }, 199 { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64) 200 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) 201 NO_SFDP_FLAGS(SECT_4K) 202 .fixups = &atmel_nor_global_protection_fixups }, 203 { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16) 204 NO_SFDP_FLAGS(SECT_4K) }, 205}; 206 207const struct spi_nor_manufacturer spi_nor_atmel = { 208 .name = "atmel", 209 .parts = atmel_nor_parts, 210 .nparts = ARRAY_SIZE(atmel_nor_parts), 211};