1*49dec7f7SSheetal Tigadoli /* 2*49dec7f7SSheetal Tigadoli * Copyright (c) 2019-2020, Broadcom 3*49dec7f7SSheetal Tigadoli * 4*49dec7f7SSheetal Tigadoli * SPDX-License-Identifier: BSD-3-Clause 5*49dec7f7SSheetal Tigadoli */ 6*49dec7f7SSheetal Tigadoli 7*49dec7f7SSheetal Tigadoli #include <stdbool.h> 8*49dec7f7SSheetal Tigadoli #include <stddef.h> 9*49dec7f7SSheetal Tigadoli #include <stdint.h> 10*49dec7f7SSheetal Tigadoli 11*49dec7f7SSheetal Tigadoli #include <common/debug.h> 12*49dec7f7SSheetal Tigadoli #include <drivers/delay_timer.h> 13*49dec7f7SSheetal Tigadoli #include <errno.h> 14*49dec7f7SSheetal Tigadoli 15*49dec7f7SSheetal Tigadoli #include <sf.h> 16*49dec7f7SSheetal Tigadoli #include <spi.h> 17*49dec7f7SSheetal Tigadoli 18*49dec7f7SSheetal Tigadoli #define SPI_FLASH_CMD_LEN 4 19*49dec7f7SSheetal Tigadoli #define QSPI_WAIT_TIMEOUT_US 200000U /* usec */ 20*49dec7f7SSheetal Tigadoli 21*49dec7f7SSheetal Tigadoli #define FINFO(jedec_id, ext_id, _sector_size, _n_sectors, _page_size, _flags) \ 22*49dec7f7SSheetal Tigadoli .id = { \ 23*49dec7f7SSheetal Tigadoli ((jedec_id) >> 16) & 0xff, \ 24*49dec7f7SSheetal Tigadoli ((jedec_id) >> 8) & 0xff, \ 25*49dec7f7SSheetal Tigadoli (jedec_id) & 0xff, \ 26*49dec7f7SSheetal Tigadoli ((ext_id) >> 8) & 0xff, \ 27*49dec7f7SSheetal Tigadoli (ext_id) & 0xff, \ 28*49dec7f7SSheetal Tigadoli }, \ 29*49dec7f7SSheetal Tigadoli .id_len = (!(jedec_id) ? 0 : (3 + ((ext_id) ? 2 : 0))), \ 30*49dec7f7SSheetal Tigadoli .sector_size = (_sector_size), \ 31*49dec7f7SSheetal Tigadoli .n_sectors = (_n_sectors), \ 32*49dec7f7SSheetal Tigadoli .page_size = _page_size, \ 33*49dec7f7SSheetal Tigadoli .flags = (_flags), 34*49dec7f7SSheetal Tigadoli 35*49dec7f7SSheetal Tigadoli /* SPI/QSPI flash device params structure */ 36*49dec7f7SSheetal Tigadoli const struct spi_flash_info spi_flash_ids[] = { 37*49dec7f7SSheetal Tigadoli {"W25Q64CV", FINFO(0xef4017, 0x0, 64 * 1024, 128, 256, WR_QPP | SECT_4K)}, 38*49dec7f7SSheetal Tigadoli {"W25Q64DW", FINFO(0xef6017, 0x0, 64 * 1024, 128, 256, WR_QPP | SECT_4K)}, 39*49dec7f7SSheetal Tigadoli {"W25Q32", FINFO(0xef4016, 0x0, 64 * 1024, 64, 256, SECT_4K)}, 40*49dec7f7SSheetal Tigadoli {"MX25l3205D", FINFO(0xc22016, 0x0, 64 * 1024, 64, 256, SECT_4K)}, 41*49dec7f7SSheetal Tigadoli }; 42*49dec7f7SSheetal Tigadoli 43*49dec7f7SSheetal Tigadoli static void spi_flash_addr(uint32_t addr, uint8_t *cmd) 44*49dec7f7SSheetal Tigadoli { 45*49dec7f7SSheetal Tigadoli /* 46*49dec7f7SSheetal Tigadoli * cmd[0] holds a SPI Flash command, stored earlier 47*49dec7f7SSheetal Tigadoli * cmd[1/2/3] holds 24bit flash address 48*49dec7f7SSheetal Tigadoli */ 49*49dec7f7SSheetal Tigadoli cmd[1] = addr >> 16; 50*49dec7f7SSheetal Tigadoli cmd[2] = addr >> 8; 51*49dec7f7SSheetal Tigadoli cmd[3] = addr >> 0; 52*49dec7f7SSheetal Tigadoli } 53*49dec7f7SSheetal Tigadoli 54*49dec7f7SSheetal Tigadoli static const struct spi_flash_info *spi_flash_read_id(void) 55*49dec7f7SSheetal Tigadoli { 56*49dec7f7SSheetal Tigadoli const struct spi_flash_info *info; 57*49dec7f7SSheetal Tigadoli uint8_t id[SPI_FLASH_MAX_ID_LEN]; 58*49dec7f7SSheetal Tigadoli int ret; 59*49dec7f7SSheetal Tigadoli 60*49dec7f7SSheetal Tigadoli ret = spi_flash_cmd(CMD_READ_ID, id, SPI_FLASH_MAX_ID_LEN); 61*49dec7f7SSheetal Tigadoli if (ret < 0) { 62*49dec7f7SSheetal Tigadoli ERROR("SF: Error %d reading JEDEC ID\n", ret); 63*49dec7f7SSheetal Tigadoli return NULL; 64*49dec7f7SSheetal Tigadoli } 65*49dec7f7SSheetal Tigadoli 66*49dec7f7SSheetal Tigadoli for (info = spi_flash_ids; info->name != NULL; info++) { 67*49dec7f7SSheetal Tigadoli if (info->id_len) { 68*49dec7f7SSheetal Tigadoli if (!memcmp(info->id, id, info->id_len)) 69*49dec7f7SSheetal Tigadoli return info; 70*49dec7f7SSheetal Tigadoli } 71*49dec7f7SSheetal Tigadoli } 72*49dec7f7SSheetal Tigadoli 73*49dec7f7SSheetal Tigadoli printf("SF: unrecognized JEDEC id bytes: %02x, %02x, %02x\n", 74*49dec7f7SSheetal Tigadoli id[0], id[1], id[2]); 75*49dec7f7SSheetal Tigadoli return NULL; 76*49dec7f7SSheetal Tigadoli } 77*49dec7f7SSheetal Tigadoli 78*49dec7f7SSheetal Tigadoli /* Enable writing on the SPI flash */ 79*49dec7f7SSheetal Tigadoli static inline int spi_flash_cmd_write_enable(struct spi_flash *flash) 80*49dec7f7SSheetal Tigadoli { 81*49dec7f7SSheetal Tigadoli return spi_flash_cmd(CMD_WRITE_ENABLE, NULL, 0); 82*49dec7f7SSheetal Tigadoli } 83*49dec7f7SSheetal Tigadoli 84*49dec7f7SSheetal Tigadoli static int spi_flash_cmd_wait(struct spi_flash *flash) 85*49dec7f7SSheetal Tigadoli { 86*49dec7f7SSheetal Tigadoli uint8_t cmd; 87*49dec7f7SSheetal Tigadoli uint32_t i; 88*49dec7f7SSheetal Tigadoli uint8_t status; 89*49dec7f7SSheetal Tigadoli int ret; 90*49dec7f7SSheetal Tigadoli 91*49dec7f7SSheetal Tigadoli i = 0; 92*49dec7f7SSheetal Tigadoli while (1) { 93*49dec7f7SSheetal Tigadoli cmd = CMD_RDSR; 94*49dec7f7SSheetal Tigadoli ret = spi_flash_cmd_read(&cmd, 1, &status, 1); 95*49dec7f7SSheetal Tigadoli if (ret < 0) { 96*49dec7f7SSheetal Tigadoli ERROR("SF: cmd wait failed\n"); 97*49dec7f7SSheetal Tigadoli break; 98*49dec7f7SSheetal Tigadoli } 99*49dec7f7SSheetal Tigadoli if (!(status & STATUS_WIP)) 100*49dec7f7SSheetal Tigadoli break; 101*49dec7f7SSheetal Tigadoli 102*49dec7f7SSheetal Tigadoli i++; 103*49dec7f7SSheetal Tigadoli if (i >= QSPI_WAIT_TIMEOUT_US) { 104*49dec7f7SSheetal Tigadoli ERROR("SF: cmd wait timeout\n"); 105*49dec7f7SSheetal Tigadoli ret = -1; 106*49dec7f7SSheetal Tigadoli break; 107*49dec7f7SSheetal Tigadoli } 108*49dec7f7SSheetal Tigadoli udelay(1); 109*49dec7f7SSheetal Tigadoli } 110*49dec7f7SSheetal Tigadoli 111*49dec7f7SSheetal Tigadoli return ret; 112*49dec7f7SSheetal Tigadoli } 113*49dec7f7SSheetal Tigadoli 114*49dec7f7SSheetal Tigadoli static int spi_flash_write_common(struct spi_flash *flash, const uint8_t *cmd, 115*49dec7f7SSheetal Tigadoli size_t cmd_len, const void *buf, 116*49dec7f7SSheetal Tigadoli size_t buf_len) 117*49dec7f7SSheetal Tigadoli { 118*49dec7f7SSheetal Tigadoli int ret; 119*49dec7f7SSheetal Tigadoli 120*49dec7f7SSheetal Tigadoli ret = spi_flash_cmd_write_enable(flash); 121*49dec7f7SSheetal Tigadoli if (ret < 0) { 122*49dec7f7SSheetal Tigadoli ERROR("SF: enabling write failed\n"); 123*49dec7f7SSheetal Tigadoli return ret; 124*49dec7f7SSheetal Tigadoli } 125*49dec7f7SSheetal Tigadoli 126*49dec7f7SSheetal Tigadoli ret = spi_flash_cmd_write(cmd, cmd_len, buf, buf_len); 127*49dec7f7SSheetal Tigadoli if (ret < 0) { 128*49dec7f7SSheetal Tigadoli ERROR("SF: write cmd failed\n"); 129*49dec7f7SSheetal Tigadoli return ret; 130*49dec7f7SSheetal Tigadoli } 131*49dec7f7SSheetal Tigadoli 132*49dec7f7SSheetal Tigadoli ret = spi_flash_cmd_wait(flash); 133*49dec7f7SSheetal Tigadoli if (ret < 0) { 134*49dec7f7SSheetal Tigadoli ERROR("SF: write timed out\n"); 135*49dec7f7SSheetal Tigadoli return ret; 136*49dec7f7SSheetal Tigadoli } 137*49dec7f7SSheetal Tigadoli 138*49dec7f7SSheetal Tigadoli return ret; 139*49dec7f7SSheetal Tigadoli } 140*49dec7f7SSheetal Tigadoli 141*49dec7f7SSheetal Tigadoli static int spi_flash_read_common(const uint8_t *cmd, size_t cmd_len, 142*49dec7f7SSheetal Tigadoli void *data, size_t data_len) 143*49dec7f7SSheetal Tigadoli { 144*49dec7f7SSheetal Tigadoli int ret; 145*49dec7f7SSheetal Tigadoli 146*49dec7f7SSheetal Tigadoli ret = spi_flash_cmd_read(cmd, cmd_len, data, data_len); 147*49dec7f7SSheetal Tigadoli if (ret < 0) { 148*49dec7f7SSheetal Tigadoli ERROR("SF: read cmd failed\n"); 149*49dec7f7SSheetal Tigadoli return ret; 150*49dec7f7SSheetal Tigadoli } 151*49dec7f7SSheetal Tigadoli 152*49dec7f7SSheetal Tigadoli return ret; 153*49dec7f7SSheetal Tigadoli } 154*49dec7f7SSheetal Tigadoli 155*49dec7f7SSheetal Tigadoli int spi_flash_read(struct spi_flash *flash, uint32_t offset, 156*49dec7f7SSheetal Tigadoli uint32_t len, void *data) 157*49dec7f7SSheetal Tigadoli { 158*49dec7f7SSheetal Tigadoli uint32_t read_len = 0, read_addr; 159*49dec7f7SSheetal Tigadoli uint8_t cmd[SPI_FLASH_CMD_LEN]; 160*49dec7f7SSheetal Tigadoli int ret; 161*49dec7f7SSheetal Tigadoli 162*49dec7f7SSheetal Tigadoli ret = spi_claim_bus(); 163*49dec7f7SSheetal Tigadoli if (ret) { 164*49dec7f7SSheetal Tigadoli ERROR("SF: unable to claim SPI bus\n"); 165*49dec7f7SSheetal Tigadoli return ret; 166*49dec7f7SSheetal Tigadoli } 167*49dec7f7SSheetal Tigadoli 168*49dec7f7SSheetal Tigadoli cmd[0] = CMD_READ_NORMAL; 169*49dec7f7SSheetal Tigadoli while (len) { 170*49dec7f7SSheetal Tigadoli read_addr = offset; 171*49dec7f7SSheetal Tigadoli read_len = MIN(flash->page_size, (len - read_len)); 172*49dec7f7SSheetal Tigadoli spi_flash_addr(read_addr, cmd); 173*49dec7f7SSheetal Tigadoli 174*49dec7f7SSheetal Tigadoli ret = spi_flash_read_common(cmd, sizeof(cmd), data, read_len); 175*49dec7f7SSheetal Tigadoli if (ret < 0) { 176*49dec7f7SSheetal Tigadoli ERROR("SF: read failed\n"); 177*49dec7f7SSheetal Tigadoli break; 178*49dec7f7SSheetal Tigadoli } 179*49dec7f7SSheetal Tigadoli 180*49dec7f7SSheetal Tigadoli offset += read_len; 181*49dec7f7SSheetal Tigadoli len -= read_len; 182*49dec7f7SSheetal Tigadoli data += read_len; 183*49dec7f7SSheetal Tigadoli } 184*49dec7f7SSheetal Tigadoli SPI_DEBUG("SF read done\n"); 185*49dec7f7SSheetal Tigadoli 186*49dec7f7SSheetal Tigadoli spi_release_bus(); 187*49dec7f7SSheetal Tigadoli return ret; 188*49dec7f7SSheetal Tigadoli } 189*49dec7f7SSheetal Tigadoli 190*49dec7f7SSheetal Tigadoli int spi_flash_write(struct spi_flash *flash, uint32_t offset, 191*49dec7f7SSheetal Tigadoli uint32_t len, void *buf) 192*49dec7f7SSheetal Tigadoli { 193*49dec7f7SSheetal Tigadoli unsigned long byte_addr, page_size; 194*49dec7f7SSheetal Tigadoli uint8_t cmd[SPI_FLASH_CMD_LEN]; 195*49dec7f7SSheetal Tigadoli uint32_t chunk_len, actual; 196*49dec7f7SSheetal Tigadoli uint32_t write_addr; 197*49dec7f7SSheetal Tigadoli int ret; 198*49dec7f7SSheetal Tigadoli 199*49dec7f7SSheetal Tigadoli ret = spi_claim_bus(); 200*49dec7f7SSheetal Tigadoli if (ret) { 201*49dec7f7SSheetal Tigadoli ERROR("SF: unable to claim SPI bus\n"); 202*49dec7f7SSheetal Tigadoli return ret; 203*49dec7f7SSheetal Tigadoli } 204*49dec7f7SSheetal Tigadoli 205*49dec7f7SSheetal Tigadoli page_size = flash->page_size; 206*49dec7f7SSheetal Tigadoli 207*49dec7f7SSheetal Tigadoli cmd[0] = flash->write_cmd; 208*49dec7f7SSheetal Tigadoli for (actual = 0; actual < len; actual += chunk_len) { 209*49dec7f7SSheetal Tigadoli write_addr = offset; 210*49dec7f7SSheetal Tigadoli byte_addr = offset % page_size; 211*49dec7f7SSheetal Tigadoli chunk_len = MIN(len - actual, 212*49dec7f7SSheetal Tigadoli (uint32_t)(page_size - byte_addr)); 213*49dec7f7SSheetal Tigadoli spi_flash_addr(write_addr, cmd); 214*49dec7f7SSheetal Tigadoli 215*49dec7f7SSheetal Tigadoli SPI_DEBUG("SF:0x%p=>cmd:{0x%02x 0x%02x%02x%02x} chunk_len:%d\n", 216*49dec7f7SSheetal Tigadoli buf + actual, cmd[0], cmd[1], 217*49dec7f7SSheetal Tigadoli cmd[2], cmd[3], chunk_len); 218*49dec7f7SSheetal Tigadoli 219*49dec7f7SSheetal Tigadoli ret = spi_flash_write_common(flash, cmd, sizeof(cmd), 220*49dec7f7SSheetal Tigadoli buf + actual, chunk_len); 221*49dec7f7SSheetal Tigadoli if (ret < 0) { 222*49dec7f7SSheetal Tigadoli ERROR("SF: write cmd failed\n"); 223*49dec7f7SSheetal Tigadoli break; 224*49dec7f7SSheetal Tigadoli } 225*49dec7f7SSheetal Tigadoli 226*49dec7f7SSheetal Tigadoli offset += chunk_len; 227*49dec7f7SSheetal Tigadoli } 228*49dec7f7SSheetal Tigadoli SPI_DEBUG("SF write done\n"); 229*49dec7f7SSheetal Tigadoli 230*49dec7f7SSheetal Tigadoli spi_release_bus(); 231*49dec7f7SSheetal Tigadoli return ret; 232*49dec7f7SSheetal Tigadoli } 233*49dec7f7SSheetal Tigadoli 234*49dec7f7SSheetal Tigadoli int spi_flash_erase(struct spi_flash *flash, uint32_t offset, uint32_t len) 235*49dec7f7SSheetal Tigadoli { 236*49dec7f7SSheetal Tigadoli uint8_t cmd[SPI_FLASH_CMD_LEN]; 237*49dec7f7SSheetal Tigadoli uint32_t erase_size, erase_addr; 238*49dec7f7SSheetal Tigadoli int ret; 239*49dec7f7SSheetal Tigadoli 240*49dec7f7SSheetal Tigadoli erase_size = flash->erase_size; 241*49dec7f7SSheetal Tigadoli 242*49dec7f7SSheetal Tigadoli if (offset % erase_size || len % erase_size) { 243*49dec7f7SSheetal Tigadoli ERROR("SF: Erase offset/length not multiple of erase size\n"); 244*49dec7f7SSheetal Tigadoli return -1; 245*49dec7f7SSheetal Tigadoli } 246*49dec7f7SSheetal Tigadoli 247*49dec7f7SSheetal Tigadoli ret = spi_claim_bus(); 248*49dec7f7SSheetal Tigadoli if (ret) { 249*49dec7f7SSheetal Tigadoli ERROR("SF: unable to claim SPI bus\n"); 250*49dec7f7SSheetal Tigadoli return ret; 251*49dec7f7SSheetal Tigadoli } 252*49dec7f7SSheetal Tigadoli 253*49dec7f7SSheetal Tigadoli cmd[0] = flash->erase_cmd; 254*49dec7f7SSheetal Tigadoli while (len) { 255*49dec7f7SSheetal Tigadoli erase_addr = offset; 256*49dec7f7SSheetal Tigadoli spi_flash_addr(erase_addr, cmd); 257*49dec7f7SSheetal Tigadoli 258*49dec7f7SSheetal Tigadoli SPI_DEBUG("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], 259*49dec7f7SSheetal Tigadoli cmd[2], cmd[3], erase_addr); 260*49dec7f7SSheetal Tigadoli 261*49dec7f7SSheetal Tigadoli ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); 262*49dec7f7SSheetal Tigadoli if (ret < 0) { 263*49dec7f7SSheetal Tigadoli ERROR("SF: erase failed\n"); 264*49dec7f7SSheetal Tigadoli break; 265*49dec7f7SSheetal Tigadoli } 266*49dec7f7SSheetal Tigadoli 267*49dec7f7SSheetal Tigadoli offset += erase_size; 268*49dec7f7SSheetal Tigadoli len -= erase_size; 269*49dec7f7SSheetal Tigadoli } 270*49dec7f7SSheetal Tigadoli SPI_DEBUG("sf erase done\n"); 271*49dec7f7SSheetal Tigadoli 272*49dec7f7SSheetal Tigadoli spi_release_bus(); 273*49dec7f7SSheetal Tigadoli return ret; 274*49dec7f7SSheetal Tigadoli } 275*49dec7f7SSheetal Tigadoli 276*49dec7f7SSheetal Tigadoli int spi_flash_probe(struct spi_flash *flash) 277*49dec7f7SSheetal Tigadoli { 278*49dec7f7SSheetal Tigadoli const struct spi_flash_info *info = NULL; 279*49dec7f7SSheetal Tigadoli int ret; 280*49dec7f7SSheetal Tigadoli 281*49dec7f7SSheetal Tigadoli ret = spi_claim_bus(); 282*49dec7f7SSheetal Tigadoli if (ret) { 283*49dec7f7SSheetal Tigadoli ERROR("SF: Unable to claim SPI bus\n"); 284*49dec7f7SSheetal Tigadoli ERROR("SF: probe failed\n"); 285*49dec7f7SSheetal Tigadoli return ret; 286*49dec7f7SSheetal Tigadoli } 287*49dec7f7SSheetal Tigadoli 288*49dec7f7SSheetal Tigadoli info = spi_flash_read_id(); 289*49dec7f7SSheetal Tigadoli if (!info) 290*49dec7f7SSheetal Tigadoli goto probe_fail; 291*49dec7f7SSheetal Tigadoli 292*49dec7f7SSheetal Tigadoli INFO("Flash Name: %s sectors %x, sec size %x\n", 293*49dec7f7SSheetal Tigadoli info->name, info->n_sectors, 294*49dec7f7SSheetal Tigadoli info->sector_size); 295*49dec7f7SSheetal Tigadoli flash->size = info->n_sectors * info->sector_size; 296*49dec7f7SSheetal Tigadoli flash->sector_size = info->sector_size; 297*49dec7f7SSheetal Tigadoli flash->page_size = info->page_size; 298*49dec7f7SSheetal Tigadoli flash->flags = info->flags; 299*49dec7f7SSheetal Tigadoli 300*49dec7f7SSheetal Tigadoli flash->read_cmd = CMD_READ_NORMAL; 301*49dec7f7SSheetal Tigadoli flash->write_cmd = CMD_PAGE_PROGRAM; 302*49dec7f7SSheetal Tigadoli flash->erase_cmd = CMD_ERASE_64K; 303*49dec7f7SSheetal Tigadoli flash->erase_size = ERASE_SIZE_64K; 304*49dec7f7SSheetal Tigadoli 305*49dec7f7SSheetal Tigadoli probe_fail: 306*49dec7f7SSheetal Tigadoli spi_release_bus(); 307*49dec7f7SSheetal Tigadoli return ret; 308*49dec7f7SSheetal Tigadoli } 309