blob: 07344af3182ceef14cd45793e27dad5da8fbf843 [file] [log] [blame]
Ricardo Salveti9d033292016-07-25 23:34:52 -03001/*
2 * Copyright (c) 2016 Linaro Limited
3 * 2016 Intel Corporation
4 *
David B. Kinderac74d8b2017-01-18 17:01:01 -08005 * SPDX-License-Identifier: Apache-2.0
Ricardo Salveti9d033292016-07-25 23:34:52 -03006 */
7
8#include <errno.h>
9
Flavio Santesb04cdcd2016-12-04 14:59:37 -060010#include <kernel.h>
Ricardo Salveti9d033292016-07-25 23:34:52 -030011#include <device.h>
12#include <init.h>
13#include <soc.h>
14#include <flash.h>
15#include <string.h>
16
Kumar Galaccad5bf2017-04-21 10:03:20 -050017static inline bool is_aligned_32(u32_t data)
Ricardo Salveti9d033292016-07-25 23:34:52 -030018{
19 return (data & 0x3) ? false : true;
20}
21
22static inline bool is_addr_valid(off_t addr, size_t len)
23{
24 if (addr + len > NRF_FICR->CODEPAGESIZE * NRF_FICR->CODESIZE ||
25 addr < 0) {
26 return false;
27 }
28
29 return true;
30}
31
32static void nvmc_wait_ready(void)
33{
34 while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
35 ;
36 }
37}
38
39static int flash_nrf5_read(struct device *dev, off_t addr,
40 void *data, size_t len)
41{
42 if (!is_addr_valid(addr, len)) {
43 return -EINVAL;
44 }
45
Jorge Ramirez-Ortiz28e60ef2017-02-02 19:46:50 +010046 if (!len) {
47 return 0;
48 }
49
Ricardo Salveti9d033292016-07-25 23:34:52 -030050 memcpy(data, (void *)addr, len);
51
52 return 0;
53}
54
55static int flash_nrf5_write(struct device *dev, off_t addr,
56 const void *data, size_t len)
57{
Kumar Galaccad5bf2017-04-21 10:03:20 -050058 u32_t addr_word;
59 u32_t tmp_word;
Ricardo Salveti9d033292016-07-25 23:34:52 -030060 void *data_word;
Kumar Galaccad5bf2017-04-21 10:03:20 -050061 u32_t remaining = len;
62 u32_t count = 0;
Ricardo Salveti9d033292016-07-25 23:34:52 -030063
64 if (!is_addr_valid(addr, len)) {
65 return -EINVAL;
66 }
67
Jorge Ramirez-Ortiz28e60ef2017-02-02 19:46:50 +010068 if (!len) {
69 return 0;
70 }
71
Ricardo Salvetic7e60762016-09-13 13:32:55 -030072 /* Start with a word-aligned address and handle the offset */
73 addr_word = addr & ~0x3;
74
75 /* If not aligned, read first word, update and write it back */
76 if (!is_aligned_32(addr)) {
Kumar Galaccad5bf2017-04-21 10:03:20 -050077 tmp_word = *(u32_t *)(addr_word);
78 count = sizeof(u32_t) - (addr & 0x3);
Ricardo Salvetic7e60762016-09-13 13:32:55 -030079 if (count > len) {
80 count = len;
81 }
Kumar Galaccad5bf2017-04-21 10:03:20 -050082 memcpy((u8_t *)&tmp_word + (addr & 0x3), data, count);
Ricardo Salvetic7e60762016-09-13 13:32:55 -030083 nvmc_wait_ready();
Kumar Galaccad5bf2017-04-21 10:03:20 -050084 *(u32_t *)addr_word = tmp_word;
Ricardo Salvetic7e60762016-09-13 13:32:55 -030085 addr_word = addr + count;
86 remaining -= count;
87 }
88
Ricardo Salveti9d033292016-07-25 23:34:52 -030089 /* Write all the 4-byte aligned data */
Ricardo Salvetic7e60762016-09-13 13:32:55 -030090 data_word = (void *) data + count;
Kumar Galaccad5bf2017-04-21 10:03:20 -050091 while (remaining >= sizeof(u32_t)) {
Ricardo Salveti9d033292016-07-25 23:34:52 -030092 nvmc_wait_ready();
Kumar Galaccad5bf2017-04-21 10:03:20 -050093 *(u32_t *)addr_word = *(u32_t *)data_word;
94 addr_word += sizeof(u32_t);
95 data_word += sizeof(u32_t);
96 remaining -= sizeof(u32_t);
Ricardo Salveti9d033292016-07-25 23:34:52 -030097 }
Ricardo Salvetic7e60762016-09-13 13:32:55 -030098
99 /* Write remaining data */
100 if (remaining) {
Kumar Galaccad5bf2017-04-21 10:03:20 -0500101 tmp_word = *(u32_t *)(addr_word);
102 memcpy((u8_t *)&tmp_word, data_word, remaining);
Ricardo Salvetic7e60762016-09-13 13:32:55 -0300103 nvmc_wait_ready();
Kumar Galaccad5bf2017-04-21 10:03:20 -0500104 *(u32_t *)addr_word = tmp_word;
Ricardo Salvetic7e60762016-09-13 13:32:55 -0300105 }
106
Ricardo Salveti9d033292016-07-25 23:34:52 -0300107 nvmc_wait_ready();
108
109 return 0;
110}
111
112static int flash_nrf5_erase(struct device *dev, off_t addr, size_t size)
113{
Kumar Galaccad5bf2017-04-21 10:03:20 -0500114 u32_t pg_size = NRF_FICR->CODEPAGESIZE;
115 u32_t n_pages = size / pg_size;
Ricardo Salveti9d033292016-07-25 23:34:52 -0300116
117 /* Erase can only be done per page */
Jorge Ramirez-Ortiz28e60ef2017-02-02 19:46:50 +0100118 if (((addr % pg_size) != 0) || ((size % pg_size) != 0)) {
Ricardo Salveti9d033292016-07-25 23:34:52 -0300119 return -EINVAL;
120 }
121
122 if (!is_addr_valid(addr, size)) {
123 return -EINVAL;
124 }
125
Jorge Ramirez-Ortiz28e60ef2017-02-02 19:46:50 +0100126 if (!n_pages) {
127 return 0;
128 }
129
Ricardo Salveti9d033292016-07-25 23:34:52 -0300130 /* Erase uses a specific configuration register */
131 NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos;
132 nvmc_wait_ready();
133
Kumar Galaccad5bf2017-04-21 10:03:20 -0500134 for (u32_t i = 0; i < n_pages; i++) {
135 NRF_NVMC->ERASEPAGE = (u32_t)addr + (i * pg_size);
Ricardo Salveti9d033292016-07-25 23:34:52 -0300136 nvmc_wait_ready();
137 }
138
139 NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
140 nvmc_wait_ready();
141
142 return 0;
143}
144
145static int flash_nrf5_write_protection(struct device *dev, bool enable)
146{
147 if (enable) {
148 NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
149 } else {
150 NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
151 }
152 nvmc_wait_ready();
153
154 return 0;
155}
156
Marcus Shawcroft6cec26c2016-10-24 08:20:19 +0100157static const struct flash_driver_api flash_nrf5_api = {
Ricardo Salveti9d033292016-07-25 23:34:52 -0300158 .read = flash_nrf5_read,
159 .write = flash_nrf5_write,
160 .erase = flash_nrf5_erase,
161 .write_protection = flash_nrf5_write_protection,
162};
163
164static int nrf5_flash_init(struct device *dev)
165{
166 dev->driver_api = &flash_nrf5_api;
167
168 return 0;
169}
170
171DEVICE_INIT(nrf5_flash, CONFIG_SOC_FLASH_NRF5_DEV_NAME, nrf5_flash_init,
Andrew Boie0b474ee2016-11-08 11:06:55 -0800172 NULL, NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);