1*91f16700Schasinglulu /* 2*91f16700Schasinglulu * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. 3*91f16700Schasinglulu * 4*91f16700Schasinglulu * SPDX-License-Identifier: BSD-3-Clause 5*91f16700Schasinglulu */ 6*91f16700Schasinglulu 7*91f16700Schasinglulu #include <errno.h> 8*91f16700Schasinglulu 9*91f16700Schasinglulu #include <drivers/cfi/v2m_flash.h> 10*91f16700Schasinglulu #include <lib/mmio.h> 11*91f16700Schasinglulu 12*91f16700Schasinglulu /* 13*91f16700Schasinglulu * This file supplies a low level interface to the vexpress NOR flash 14*91f16700Schasinglulu * memory of juno and fvp. This memory is organized as an interleaved 15*91f16700Schasinglulu * memory of two chips with a 16 bit word. It means that every 32 bit 16*91f16700Schasinglulu * access is going to access to two different chips. This is very 17*91f16700Schasinglulu * important when we send commands or read status of the chips. 18*91f16700Schasinglulu */ 19*91f16700Schasinglulu 20*91f16700Schasinglulu /* 21*91f16700Schasinglulu * DWS ready poll retries. The number of retries in this driver have been 22*91f16700Schasinglulu * obtained empirically from Juno. FVP implements a zero wait state NOR flash 23*91f16700Schasinglulu * model 24*91f16700Schasinglulu */ 25*91f16700Schasinglulu #define DWS_WORD_PROGRAM_RETRIES 1000 26*91f16700Schasinglulu #define DWS_WORD_ERASE_RETRIES 3000000 27*91f16700Schasinglulu #define DWS_WORD_LOCK_RETRIES 1000 28*91f16700Schasinglulu 29*91f16700Schasinglulu /* Helper macro to detect end of command */ 30*91f16700Schasinglulu #define NOR_CMD_END (NOR_DWS | (NOR_DWS << 16l)) 31*91f16700Schasinglulu 32*91f16700Schasinglulu /* Helper macros to access two flash banks in parallel */ 33*91f16700Schasinglulu #define NOR_2X16(d) ((d << 16) | (d & 0xffff)) 34*91f16700Schasinglulu 35*91f16700Schasinglulu static unsigned int nor_status(uintptr_t base_addr) 36*91f16700Schasinglulu { 37*91f16700Schasinglulu unsigned long status; 38*91f16700Schasinglulu 39*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG); 40*91f16700Schasinglulu status = mmio_read_32(base_addr); 41*91f16700Schasinglulu status |= status >> 16; /* merge status from both flash banks */ 42*91f16700Schasinglulu 43*91f16700Schasinglulu return status & 0xFFFF; 44*91f16700Schasinglulu } 45*91f16700Schasinglulu 46*91f16700Schasinglulu /* 47*91f16700Schasinglulu * Poll Write State Machine. 48*91f16700Schasinglulu * Return values: 49*91f16700Schasinglulu * 0 = WSM ready 50*91f16700Schasinglulu * -EBUSY = WSM busy after the number of retries 51*91f16700Schasinglulu */ 52*91f16700Schasinglulu static int nor_poll_dws(uintptr_t base_addr, unsigned long int retries) 53*91f16700Schasinglulu { 54*91f16700Schasinglulu unsigned long status; 55*91f16700Schasinglulu 56*91f16700Schasinglulu do { 57*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG); 58*91f16700Schasinglulu status = mmio_read_32(base_addr); 59*91f16700Schasinglulu if ((status & NOR_CMD_END) == NOR_CMD_END) 60*91f16700Schasinglulu return 0; 61*91f16700Schasinglulu } while (retries-- > 0); 62*91f16700Schasinglulu 63*91f16700Schasinglulu return -EBUSY; 64*91f16700Schasinglulu } 65*91f16700Schasinglulu 66*91f16700Schasinglulu /* 67*91f16700Schasinglulu * Return values: 68*91f16700Schasinglulu * 0 = success 69*91f16700Schasinglulu * -EPERM = Device protected or Block locked 70*91f16700Schasinglulu * -EIO = General I/O error 71*91f16700Schasinglulu */ 72*91f16700Schasinglulu static int nor_full_status_check(uintptr_t base_addr) 73*91f16700Schasinglulu { 74*91f16700Schasinglulu unsigned long status; 75*91f16700Schasinglulu 76*91f16700Schasinglulu /* Full status check */ 77*91f16700Schasinglulu status = nor_status(base_addr); 78*91f16700Schasinglulu 79*91f16700Schasinglulu if (status & (NOR_PS | NOR_BLS | NOR_ESS | NOR_PSS)) 80*91f16700Schasinglulu return -EPERM; 81*91f16700Schasinglulu if (status & (NOR_VPPS | NOR_ES)) 82*91f16700Schasinglulu return -EIO; 83*91f16700Schasinglulu return 0; 84*91f16700Schasinglulu } 85*91f16700Schasinglulu 86*91f16700Schasinglulu void nor_send_cmd(uintptr_t base_addr, unsigned long cmd) 87*91f16700Schasinglulu { 88*91f16700Schasinglulu mmio_write_32(base_addr, NOR_2X16(cmd)); 89*91f16700Schasinglulu } 90*91f16700Schasinglulu 91*91f16700Schasinglulu /* 92*91f16700Schasinglulu * This function programs a word in the flash. Be aware that it only 93*91f16700Schasinglulu * can reset bits that were previously set. It cannot set bits that 94*91f16700Schasinglulu * were previously reset. The resulting bits = old_bits & new bits. 95*91f16700Schasinglulu * Return values: 96*91f16700Schasinglulu * 0 = success 97*91f16700Schasinglulu * otherwise it returns a negative value 98*91f16700Schasinglulu */ 99*91f16700Schasinglulu int nor_word_program(uintptr_t base_addr, unsigned long data) 100*91f16700Schasinglulu { 101*91f16700Schasinglulu uint32_t status; 102*91f16700Schasinglulu int ret; 103*91f16700Schasinglulu 104*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); 105*91f16700Schasinglulu 106*91f16700Schasinglulu /* Set the device in write word mode */ 107*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_WORD_PROGRAM); 108*91f16700Schasinglulu mmio_write_32(base_addr, data); 109*91f16700Schasinglulu 110*91f16700Schasinglulu ret = nor_poll_dws(base_addr, DWS_WORD_PROGRAM_RETRIES); 111*91f16700Schasinglulu if (ret == 0) { 112*91f16700Schasinglulu /* Full status check */ 113*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG); 114*91f16700Schasinglulu status = mmio_read_32(base_addr); 115*91f16700Schasinglulu 116*91f16700Schasinglulu if (status & (NOR_PS | NOR_BLS)) { 117*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); 118*91f16700Schasinglulu ret = -EPERM; 119*91f16700Schasinglulu } 120*91f16700Schasinglulu } 121*91f16700Schasinglulu 122*91f16700Schasinglulu if (ret == 0) 123*91f16700Schasinglulu ret = nor_full_status_check(base_addr); 124*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); 125*91f16700Schasinglulu 126*91f16700Schasinglulu return ret; 127*91f16700Schasinglulu } 128*91f16700Schasinglulu 129*91f16700Schasinglulu /* 130*91f16700Schasinglulu * Erase a full 256K block 131*91f16700Schasinglulu * Return values: 132*91f16700Schasinglulu * 0 = success 133*91f16700Schasinglulu * otherwise it returns a negative value 134*91f16700Schasinglulu */ 135*91f16700Schasinglulu int nor_erase(uintptr_t base_addr) 136*91f16700Schasinglulu { 137*91f16700Schasinglulu int ret; 138*91f16700Schasinglulu 139*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); 140*91f16700Schasinglulu 141*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE); 142*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE_ACK); 143*91f16700Schasinglulu 144*91f16700Schasinglulu ret = nor_poll_dws(base_addr, DWS_WORD_ERASE_RETRIES); 145*91f16700Schasinglulu if (ret == 0) 146*91f16700Schasinglulu ret = nor_full_status_check(base_addr); 147*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); 148*91f16700Schasinglulu 149*91f16700Schasinglulu return ret; 150*91f16700Schasinglulu } 151*91f16700Schasinglulu 152*91f16700Schasinglulu /* 153*91f16700Schasinglulu * Lock a full 256 block 154*91f16700Schasinglulu * Return values: 155*91f16700Schasinglulu * 0 = success 156*91f16700Schasinglulu * otherwise it returns a negative value 157*91f16700Schasinglulu */ 158*91f16700Schasinglulu int nor_lock(uintptr_t base_addr) 159*91f16700Schasinglulu { 160*91f16700Schasinglulu int ret; 161*91f16700Schasinglulu 162*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); 163*91f16700Schasinglulu 164*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK); 165*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_LOCK_BLOCK); 166*91f16700Schasinglulu 167*91f16700Schasinglulu ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES); 168*91f16700Schasinglulu if (ret == 0) 169*91f16700Schasinglulu ret = nor_full_status_check(base_addr); 170*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); 171*91f16700Schasinglulu 172*91f16700Schasinglulu return ret; 173*91f16700Schasinglulu } 174*91f16700Schasinglulu 175*91f16700Schasinglulu /* 176*91f16700Schasinglulu * unlock a full 256 block 177*91f16700Schasinglulu * Return values: 178*91f16700Schasinglulu * 0 = success 179*91f16700Schasinglulu * otherwise it returns a negative value 180*91f16700Schasinglulu */ 181*91f16700Schasinglulu int nor_unlock(uintptr_t base_addr) 182*91f16700Schasinglulu { 183*91f16700Schasinglulu int ret; 184*91f16700Schasinglulu 185*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); 186*91f16700Schasinglulu 187*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK); 188*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_UNLOCK_BLOCK); 189*91f16700Schasinglulu 190*91f16700Schasinglulu ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES); 191*91f16700Schasinglulu if (ret == 0) 192*91f16700Schasinglulu ret = nor_full_status_check(base_addr); 193*91f16700Schasinglulu nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); 194*91f16700Schasinglulu 195*91f16700Schasinglulu return ret; 196*91f16700Schasinglulu } 197