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