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