1 /*
2  * Copyright (c) 2022 Schlumberger
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT infineon_xmc4xxx_flash_controller
8 
9 #define FLASH_WRITE_BLK_SZ DT_PROP(DT_INST(0, infineon_xmc4xxx_nv_flash), write_block_size)
10 
11 #include <stdint.h>
12 #include <zephyr/device.h>
13 #include <zephyr/drivers/flash.h>
14 #include <zephyr/kernel.h>
15 
16 #include <xmc_flash.h>
17 
18 struct flash_xmc4xxx_data {
19 	struct k_sem sem;
20 };
21 
22 struct flash_xmc4xxx_config {
23 	uint32_t base;
24 	uint32_t size;
25 	struct flash_parameters parameters;
26 };
27 
is_aligned_32(uint32_t data)28 static inline bool is_aligned_32(uint32_t data)
29 {
30 	return (data & 0x3) ? false : true;
31 }
32 
flash_xmc4xxx_init(const struct device * dev)33 static int flash_xmc4xxx_init(const struct device *dev)
34 {
35 	struct flash_xmc4xxx_data *dev_data = dev->data;
36 
37 	k_sem_init(&dev_data->sem, 1, 1);
38 	return 0;
39 }
40 
41 #define SET_PAGES(node_id)    \
42 	{.pages_count = DT_PROP(node_id, pages_count), .pages_size = DT_PROP(node_id, pages_size)},
43 
44 #if CONFIG_FLASH_PAGE_LAYOUT
45 static const struct flash_pages_layout flash_xmc4xxx_pages_layout[] = {
46 	DT_FOREACH_CHILD(DT_NODELABEL(pages_layout), SET_PAGES)};
47 
flash_xmc4xxx_page_layout(const struct device * dev,const struct flash_pages_layout ** layout,size_t * layout_size)48 static void flash_xmc4xxx_page_layout(const struct device *dev,
49 				      const struct flash_pages_layout **layout, size_t *layout_size)
50 {
51 	*layout = &flash_xmc4xxx_pages_layout[0];
52 	*layout_size = ARRAY_SIZE(flash_xmc4xxx_pages_layout);
53 }
54 #endif
55 
flash_xmc4xxx_read(const struct device * dev,off_t offset,void * data,size_t len)56 static int flash_xmc4xxx_read(const struct device *dev, off_t offset, void *data, size_t len)
57 {
58 	const struct flash_xmc4xxx_config *dev_config = dev->config;
59 
60 	if (offset < 0 || offset + len > dev_config->size) {
61 		return -1;
62 	}
63 	memcpy(data, (void *)(dev_config->base + offset), len);
64 	return 0;
65 }
66 
67 static __aligned(4) uint8_t
68 	aligned_page[DT_PROP(DT_INST(0, infineon_xmc4xxx_nv_flash), write_block_size)];
69 
flash_xmc4xxx_write(const struct device * dev,off_t offset,const void * data,size_t len)70 static int flash_xmc4xxx_write(const struct device *dev, off_t offset, const void *data, size_t len)
71 {
72 	struct flash_xmc4xxx_data *dev_data = dev->data;
73 	const struct flash_xmc4xxx_config *dev_config = dev->config;
74 	uint32_t irq_key;
75 	uint32_t flash_addr = dev_config->base;
76 	const uint8_t *src = data;
77 	int num_pages;
78 
79 	if (offset < 0 || offset + len > dev_config->size) {
80 		return -1;
81 	}
82 
83 	if (len % dev_config->parameters.write_block_size ||
84 	    offset % dev_config->parameters.write_block_size > 0) {
85 		return -1;
86 	}
87 
88 	k_sem_take(&dev_data->sem, K_FOREVER);
89 
90 	/* erase and write operations must be on the uncached base address */
91 	flash_addr |= 0xc000000;
92 	flash_addr += offset;
93 
94 	num_pages = len / dev_config->parameters.write_block_size;
95 	for (int i = 0; i < num_pages; i++) {
96 		uint32_t *src_ptr = (uint32_t *)src;
97 
98 		/* XMC_FLASH_ProgramPage() needs a 32 bit aligned input. */
99 		/* Copy the data to an aligned array if needed.  */
100 		if (!is_aligned_32((uint32_t)src)) {
101 			memcpy(aligned_page, src, dev_config->parameters.write_block_size);
102 			src_ptr = (uint32_t *)aligned_page;
103 		}
104 
105 		irq_key = irq_lock();
106 		XMC_FLASH_ProgramPage((uint32_t *)flash_addr, src_ptr);
107 		irq_unlock(irq_key);
108 		flash_addr += dev_config->parameters.write_block_size;
109 		src += dev_config->parameters.write_block_size;
110 	}
111 
112 	k_sem_give(&dev_data->sem);
113 	return 0;
114 }
115 
116 #if CONFIG_FLASH_PAGE_LAYOUT
flash_xmc4xxx_erase(const struct device * dev,off_t offset,size_t size)117 static int flash_xmc4xxx_erase(const struct device *dev, off_t offset, size_t size)
118 {
119 	struct flash_xmc4xxx_data *dev_data = dev->data;
120 	const struct flash_xmc4xxx_config *dev_config = dev->config;
121 	uint32_t irq_key;
122 	uint32_t offset_page = 0;
123 	int ret = 0;
124 
125 	if (offset < 0 || offset > dev_config->size) {
126 		return -1;
127 	}
128 
129 	k_sem_take(&dev_data->sem, K_FOREVER);
130 
131 	for (int i = 0; i < ARRAY_SIZE(flash_xmc4xxx_pages_layout); i++) {
132 		for (int k = 0; k < flash_xmc4xxx_pages_layout[i].pages_count; k++) {
133 			uint32_t pages_size = flash_xmc4xxx_pages_layout[i].pages_size;
134 			/* erase and write operations must be on the uncached base address */
135 			uint32_t flash_addr = dev_config->base | 0xc000000;
136 
137 			if (offset == offset_page && size >= pages_size) {
138 				flash_addr += offset;
139 				irq_key = irq_lock();
140 				XMC_FLASH_EraseSector((uint32_t *)flash_addr);
141 				irq_unlock(irq_key);
142 
143 				size -= pages_size;
144 				offset += pages_size;
145 			}
146 			offset_page += pages_size;
147 
148 			if (size == 0) {
149 				ret = 0;
150 				goto finish;
151 			}
152 
153 			/* page not aligned with offset address */
154 			if (offset_page > offset) {
155 				ret = -1;
156 				goto finish;
157 			}
158 		}
159 	}
160 
161 finish:
162 	k_sem_give(&dev_data->sem);
163 	return ret;
164 }
165 #else
flash_xmc4xxx_erase(const struct device * dev,off_t offset,size_t size)166 static int flash_xmc4xxx_erase(const struct device *dev, off_t offset, size_t size)
167 {
168 	ARG_UNUSED(dev);
169 	ARG_UNUSED(offset);
170 	ARG_UNUSED(size);
171 
172 	return -ENOTSUP;
173 }
174 #endif
175 
flash_xmc4xxx_get_parameters(const struct device * dev)176 static const struct flash_parameters *flash_xmc4xxx_get_parameters(const struct device *dev)
177 {
178 	const struct flash_xmc4xxx_config *dev_config = dev->config;
179 
180 	return &dev_config->parameters;
181 }
182 
183 static const struct flash_driver_api flash_xmc4xxx_api = {.erase = flash_xmc4xxx_erase,
184 							  .write = flash_xmc4xxx_write,
185 							  .read = flash_xmc4xxx_read,
186 #ifdef CONFIG_FLASH_PAGE_LAYOUT
187 							  .page_layout = flash_xmc4xxx_page_layout,
188 #endif
189 							  .get_parameters =
190 								  flash_xmc4xxx_get_parameters};
191 
192 static struct flash_xmc4xxx_data flash_xmc4xxx_data_0;
193 static struct flash_xmc4xxx_config flash_xmc4xxx_cfg_0 = {
194 	.base = DT_REG_ADDR(DT_INST(0, infineon_xmc4xxx_nv_flash)),
195 	.size = DT_REG_SIZE(DT_INST(0, infineon_xmc4xxx_nv_flash)),
196 	.parameters = {.write_block_size = FLASH_WRITE_BLK_SZ, .erase_value = 0}};
197 
198 DEVICE_DT_INST_DEFINE(0, flash_xmc4xxx_init, NULL, &flash_xmc4xxx_data_0, &flash_xmc4xxx_cfg_0,
199 		      POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, &flash_xmc4xxx_api);
200