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