1 /*
2 * Copyright (c) 2020 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <string.h>
10 #include <zephyr/drivers/flash.h>
11 #include <errno.h>
12 #include <zephyr/init.h>
13 #include <soc.h>
14 #include "flash_priv.h"
15
16 #include "fsl_common.h"
17 #include "fsl_flashiap.h"
18
19
20 #if DT_NODE_HAS_STATUS(DT_INST(0, nxp_iap_fmc11), okay)
21 #define DT_DRV_COMPAT nxp_iap_fmc11
22 #elif DT_NODE_HAS_STATUS(DT_INST(0, nxp_iap_fmc54), okay)
23 #define DT_DRV_COMPAT nxp_iap_fmc54
24 #else
25 #error No matching compatible for soc_flash_lpc.c
26 #endif
27
28 #define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash)
29
30 struct flash_priv {
31 /* HACK: flash write protection is managed in software. */
32 struct k_sem write_lock;
33 uint32_t pflash_block_base;
34 uint32_t sector_size;
35 };
36
37 static const struct flash_parameters flash_lpc_parameters = {
38 #if DT_NODE_HAS_PROP(SOC_NV_FLASH_NODE, write_block_size)
39 .write_block_size = DT_PROP(SOC_NV_FLASH_NODE, write_block_size),
40 #else
41 .write_block_size = FSL_FEATURE_FLASH_PFLASH_BLOCK_WRITE_UNIT_SIZE,
42 #endif
43 .erase_value = 0xff,
44 };
45
prepare_erase_write(off_t offset,size_t len,uint32_t sector_size)46 static inline void prepare_erase_write(off_t offset, size_t len,
47 uint32_t sector_size)
48 {
49 uint32_t start;
50 uint32_t stop;
51
52 start = offset / sector_size;
53 stop = (offset+len-1) / sector_size;
54 FLASHIAP_PrepareSectorForWrite(start, stop);
55 }
56
flash_lpc_erase(const struct device * dev,off_t offset,size_t len)57 static int flash_lpc_erase(const struct device *dev, off_t offset, size_t len)
58 {
59 struct flash_priv *priv = dev->data;
60 status_t rc;
61 unsigned int key;
62 uint32_t start;
63 uint32_t stop;
64 uint32_t page_size;
65
66 if (k_sem_take(&priv->write_lock, K_FOREVER)) {
67 return -EACCES;
68 }
69
70 key = irq_lock();
71 prepare_erase_write(offset, len, priv->sector_size);
72 page_size = flash_lpc_parameters.write_block_size;
73 start = offset / page_size;
74 stop = (offset+len-1) / page_size;
75 rc = FLASHIAP_ErasePage(start, stop,
76 CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC);
77 irq_unlock(key);
78
79 k_sem_give(&priv->write_lock);
80
81 return (rc == kStatus_FLASHIAP_Success) ? 0 : -EINVAL;
82 }
83
flash_lpc_read(const struct device * dev,off_t offset,void * data,size_t len)84 static int flash_lpc_read(const struct device *dev, off_t offset,
85 void *data, size_t len)
86 {
87 struct flash_priv *priv = dev->data;
88 uint32_t addr;
89
90 addr = offset + priv->pflash_block_base;
91
92 memcpy(data, (void *) addr, len);
93
94 return 0;
95 }
96
flash_lpc_write(const struct device * dev,off_t offset,const void * data,size_t len)97 static int flash_lpc_write(const struct device *dev, off_t offset,
98 const void *data, size_t len)
99 {
100 struct flash_priv *priv = dev->data;
101 uint32_t addr;
102 status_t rc;
103 unsigned int key;
104
105 if (k_sem_take(&priv->write_lock, K_FOREVER)) {
106 return -EACCES;
107 }
108
109 addr = offset + priv->pflash_block_base;
110
111 key = irq_lock();
112 prepare_erase_write(offset, len, priv->sector_size);
113 rc = FLASHIAP_CopyRamToFlash(addr, (uint32_t *) data, len,
114 CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC);
115 irq_unlock(key);
116
117 k_sem_give(&priv->write_lock);
118
119 return (rc == kStatus_FLASHIAP_Success) ? 0 : -EINVAL;
120 }
121
122 #if defined(CONFIG_FLASH_PAGE_LAYOUT)
123 static const struct flash_pages_layout dev_layout = {
124 .pages_count = DT_REG_SIZE(SOC_NV_FLASH_NODE) /
125 DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
126 .pages_size = DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
127 };
128
flash_lpc_pages_layout(const struct device * dev,const struct flash_pages_layout ** layout,size_t * layout_size)129 static void flash_lpc_pages_layout(const struct device *dev,
130 const struct flash_pages_layout **layout,
131 size_t *layout_size)
132 {
133 *layout = &dev_layout;
134 *layout_size = 1;
135 }
136 #endif /* CONFIG_FLASH_PAGE_LAYOUT */
137
138 static const struct flash_parameters *
flash_lpc_get_parameters(const struct device * dev)139 flash_lpc_get_parameters(const struct device *dev)
140 {
141 ARG_UNUSED(dev);
142
143 return &flash_lpc_parameters;
144 }
145
146 static struct flash_priv flash_data;
147
148 static const struct flash_driver_api flash_lpc_api = {
149 .erase = flash_lpc_erase,
150 .write = flash_lpc_write,
151 .read = flash_lpc_read,
152 .get_parameters = flash_lpc_get_parameters,
153 #if defined(CONFIG_FLASH_PAGE_LAYOUT)
154 .page_layout = flash_lpc_pages_layout,
155 #endif
156 };
157
flash_lpc_init(const struct device * dev)158 static int flash_lpc_init(const struct device *dev)
159 {
160 struct flash_priv *priv = dev->data;
161
162 k_sem_init(&priv->write_lock, 1, 1);
163
164 priv->pflash_block_base = DT_REG_ADDR(SOC_NV_FLASH_NODE);
165
166 #if defined(FSL_FEATURE_SYSCON_FLASH_SECTOR_SIZE_BYTES)
167 priv->sector_size = FSL_FEATURE_SYSCON_FLASH_SECTOR_SIZE_BYTES;
168 #else
169 #error "Sector size not set"
170 #endif
171
172 return 0;
173 }
174
175 DEVICE_DT_INST_DEFINE(0, flash_lpc_init, NULL,
176 &flash_data, NULL, POST_KERNEL,
177 CONFIG_FLASH_INIT_PRIORITY, &flash_lpc_api);
178