|
14 | 14 | #define spi_nor_otp_region_len(nor) ((nor)->params->otp.org->len) |
15 | 15 | #define spi_nor_otp_n_regions(nor) ((nor)->params->otp.org->n_regions) |
16 | 16 |
|
| 17 | +/** |
| 18 | + * spi_nor_otp_read_secr() - read OTP data |
| 19 | + * @nor: pointer to 'struct spi_nor' |
| 20 | + * @from: offset to read from |
| 21 | + * @len: number of bytes to read |
| 22 | + * @buf: pointer to dst buffer |
| 23 | + * |
| 24 | + * Read OTP data from one region by using the SPINOR_OP_RSECR commands. This |
| 25 | + * method is used on GigaDevice and Winbond flashes. |
| 26 | + * |
| 27 | + * Return: number of bytes read successfully, -errno otherwise |
| 28 | + */ |
| 29 | +int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) |
| 30 | +{ |
| 31 | + u8 addr_width, read_opcode, read_dummy; |
| 32 | + struct spi_mem_dirmap_desc *rdesc; |
| 33 | + enum spi_nor_protocol read_proto; |
| 34 | + int ret; |
| 35 | + |
| 36 | + read_opcode = nor->read_opcode; |
| 37 | + addr_width = nor->addr_width; |
| 38 | + read_dummy = nor->read_dummy; |
| 39 | + read_proto = nor->read_proto; |
| 40 | + rdesc = nor->dirmap.rdesc; |
| 41 | + |
| 42 | + nor->read_opcode = SPINOR_OP_RSECR; |
| 43 | + nor->addr_width = 3; |
| 44 | + nor->read_dummy = 8; |
| 45 | + nor->read_proto = SNOR_PROTO_1_1_1; |
| 46 | + nor->dirmap.rdesc = NULL; |
| 47 | + |
| 48 | + ret = spi_nor_read_data(nor, addr, len, buf); |
| 49 | + |
| 50 | + nor->read_opcode = read_opcode; |
| 51 | + nor->addr_width = addr_width; |
| 52 | + nor->read_dummy = read_dummy; |
| 53 | + nor->read_proto = read_proto; |
| 54 | + nor->dirmap.rdesc = rdesc; |
| 55 | + |
| 56 | + return ret; |
| 57 | +} |
| 58 | + |
| 59 | +/** |
| 60 | + * spi_nor_otp_write_secr() - write OTP data |
| 61 | + * @nor: pointer to 'struct spi_nor' |
| 62 | + * @to: offset to write to |
| 63 | + * @len: number of bytes to write |
| 64 | + * @buf: pointer to src buffer |
| 65 | + * |
| 66 | + * Write OTP data to one region by using the SPINOR_OP_PSECR commands. This |
| 67 | + * method is used on GigaDevice and Winbond flashes. |
| 68 | + * |
| 69 | + * Please note, the write must not span multiple OTP regions. |
| 70 | + * |
| 71 | + * Return: number of bytes written successfully, -errno otherwise |
| 72 | + */ |
| 73 | +int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) |
| 74 | +{ |
| 75 | + enum spi_nor_protocol write_proto; |
| 76 | + struct spi_mem_dirmap_desc *wdesc; |
| 77 | + u8 addr_width, program_opcode; |
| 78 | + int ret, written; |
| 79 | + |
| 80 | + program_opcode = nor->program_opcode; |
| 81 | + addr_width = nor->addr_width; |
| 82 | + write_proto = nor->write_proto; |
| 83 | + wdesc = nor->dirmap.wdesc; |
| 84 | + |
| 85 | + nor->program_opcode = SPINOR_OP_PSECR; |
| 86 | + nor->addr_width = 3; |
| 87 | + nor->write_proto = SNOR_PROTO_1_1_1; |
| 88 | + nor->dirmap.wdesc = NULL; |
| 89 | + |
| 90 | + /* |
| 91 | + * We only support a write to one single page. For now all winbond |
| 92 | + * flashes only have one page per OTP region. |
| 93 | + */ |
| 94 | + ret = spi_nor_write_enable(nor); |
| 95 | + if (ret) |
| 96 | + goto out; |
| 97 | + |
| 98 | + written = spi_nor_write_data(nor, addr, len, buf); |
| 99 | + if (written < 0) |
| 100 | + goto out; |
| 101 | + |
| 102 | + ret = spi_nor_wait_till_ready(nor); |
| 103 | + |
| 104 | +out: |
| 105 | + nor->program_opcode = program_opcode; |
| 106 | + nor->addr_width = addr_width; |
| 107 | + nor->write_proto = write_proto; |
| 108 | + nor->dirmap.wdesc = wdesc; |
| 109 | + |
| 110 | + return ret ?: written; |
| 111 | +} |
| 112 | + |
| 113 | +static int spi_nor_otp_lock_bit_cr(unsigned int region) |
| 114 | +{ |
| 115 | + static const int lock_bits[] = { SR2_LB1, SR2_LB2, SR2_LB3 }; |
| 116 | + |
| 117 | + if (region >= ARRAY_SIZE(lock_bits)) |
| 118 | + return -EINVAL; |
| 119 | + |
| 120 | + return lock_bits[region]; |
| 121 | +} |
| 122 | + |
| 123 | +/** |
| 124 | + * spi_nor_otp_lock_sr2() - lock the OTP region |
| 125 | + * @nor: pointer to 'struct spi_nor' |
| 126 | + * @region: OTP region |
| 127 | + * |
| 128 | + * Lock the OTP region by writing the status register-2. This method is used on |
| 129 | + * GigaDevice and Winbond flashes. |
| 130 | + * |
| 131 | + * Return: 0 on success, -errno otherwise. |
| 132 | + */ |
| 133 | +int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region) |
| 134 | +{ |
| 135 | + u8 *cr = nor->bouncebuf; |
| 136 | + int ret, lock_bit; |
| 137 | + |
| 138 | + lock_bit = spi_nor_otp_lock_bit_cr(region); |
| 139 | + if (lock_bit < 0) |
| 140 | + return lock_bit; |
| 141 | + |
| 142 | + ret = spi_nor_read_cr(nor, cr); |
| 143 | + if (ret) |
| 144 | + return ret; |
| 145 | + |
| 146 | + /* no need to write the register if region is already locked */ |
| 147 | + if (cr[0] & lock_bit) |
| 148 | + return 0; |
| 149 | + |
| 150 | + cr[0] |= lock_bit; |
| 151 | + |
| 152 | + return spi_nor_write_16bit_cr_and_check(nor, cr[0]); |
| 153 | +} |
| 154 | + |
| 155 | +/** |
| 156 | + * spi_nor_otp_is_locked_sr2() - get the OTP region lock status |
| 157 | + * @nor: pointer to 'struct spi_nor' |
| 158 | + * @region: OTP region |
| 159 | + * |
| 160 | + * Retrieve the OTP region lock bit by reading the status register-2. This |
| 161 | + * method is used on GigaDevice and Winbond flashes. |
| 162 | + * |
| 163 | + * Return: 0 on success, -errno otherwise. |
| 164 | + */ |
| 165 | +int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region) |
| 166 | +{ |
| 167 | + u8 *cr = nor->bouncebuf; |
| 168 | + int ret, lock_bit; |
| 169 | + |
| 170 | + lock_bit = spi_nor_otp_lock_bit_cr(region); |
| 171 | + if (lock_bit < 0) |
| 172 | + return lock_bit; |
| 173 | + |
| 174 | + ret = spi_nor_read_cr(nor, cr); |
| 175 | + if (ret) |
| 176 | + return ret; |
| 177 | + |
| 178 | + return cr[0] & lock_bit; |
| 179 | +} |
| 180 | + |
17 | 181 | static loff_t spi_nor_otp_region_start(const struct spi_nor *nor, unsigned int region) |
18 | 182 | { |
19 | 183 | const struct spi_nor_otp_organization *org = nor->params->otp.org; |
|
0 commit comments