1 /*
2  * Copyright (c) 2016 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <string.h>
8 #include <zephyr/types.h>
9 #include <sys/__assert.h>
10 #include <sys/util.h>
11 #include <drivers/disk.h>
12 #include <errno.h>
13 #include <init.h>
14 #include <device.h>
15 #include <drivers/flash.h>
16 
17 #define SECTOR_SIZE CONFIG_DISK_FLASH_SECTOR_SIZE
18 
19 static const struct device *flash_dev;
20 
21 /* flash read-copy-erase-write operation */
22 static uint8_t __aligned(4) read_copy_buf[CONFIG_DISK_ERASE_BLOCK_SIZE];
23 static uint8_t *fs_buff = read_copy_buf;
24 
25 /* calculate number of blocks required for a given size */
26 #define GET_NUM_BLOCK(total_size, block_size) \
27 	((total_size + block_size - 1) / block_size)
28 
29 #define GET_SIZE_TO_BOUNDARY(start, block_size) \
30 	(block_size - (start & (block_size - 1)))
31 
lba_to_address(uint32_t sector_num)32 static off_t lba_to_address(uint32_t sector_num)
33 {
34 	off_t flash_addr;
35 
36 	flash_addr = CONFIG_DISK_FLASH_START + sector_num * SECTOR_SIZE;
37 
38 	__ASSERT(flash_addr < (CONFIG_DISK_FLASH_START +
39 		CONFIG_DISK_VOLUME_SIZE), "FS bound error");
40 
41 	return flash_addr;
42 }
43 
disk_flash_access_status(struct disk_info * disk)44 static int disk_flash_access_status(struct disk_info *disk)
45 {
46 	if (!flash_dev) {
47 		return DISK_STATUS_NOMEDIA;
48 	}
49 
50 	return DISK_STATUS_OK;
51 }
52 
disk_flash_access_init(struct disk_info * disk)53 static int disk_flash_access_init(struct disk_info *disk)
54 {
55 	if (flash_dev) {
56 		return 0;
57 	}
58 
59 	flash_dev = device_get_binding(CONFIG_DISK_FLASH_DEV_NAME);
60 	if (!flash_dev) {
61 		return -ENODEV;
62 	}
63 
64 	return 0;
65 }
66 
disk_flash_access_read(struct disk_info * disk,uint8_t * buff,uint32_t start_sector,uint32_t sector_count)67 static int disk_flash_access_read(struct disk_info *disk, uint8_t *buff,
68 				uint32_t start_sector, uint32_t sector_count)
69 {
70 	off_t fl_addr;
71 	uint32_t remaining;
72 	uint32_t len;
73 	uint32_t num_read;
74 
75 	fl_addr = lba_to_address(start_sector);
76 	remaining = (sector_count * SECTOR_SIZE);
77 	len = CONFIG_DISK_FLASH_MAX_RW_SIZE;
78 
79 	num_read = GET_NUM_BLOCK(remaining, CONFIG_DISK_FLASH_MAX_RW_SIZE);
80 
81 	for (uint32_t i = 0; i < num_read; i++) {
82 		if (remaining < CONFIG_DISK_FLASH_MAX_RW_SIZE) {
83 			len = remaining;
84 		}
85 
86 		if (flash_read(flash_dev, fl_addr, buff, len) != 0) {
87 			return -EIO;
88 		}
89 
90 		fl_addr += len;
91 		buff += len;
92 		remaining -= len;
93 	}
94 
95 	return 0;
96 }
97 
98 /* This performs read-copy into an output buffer */
read_copy_flash_block(off_t start_addr,uint32_t size,const void * src_buff,uint8_t * dest_buff)99 static int read_copy_flash_block(off_t start_addr, uint32_t size,
100 				 const void *src_buff,
101 				 uint8_t *dest_buff)
102 {
103 	off_t fl_addr;
104 	uint32_t num_read;
105 	uint32_t offset = 0U;
106 
107 	/* adjust offset if starting address is not erase-aligned address */
108 	if (start_addr & (CONFIG_DISK_FLASH_ERASE_ALIGNMENT - 1)) {
109 		offset = start_addr & (CONFIG_DISK_FLASH_ERASE_ALIGNMENT - 1);
110 	}
111 
112 	/* align starting address to an aligned address for flash erase-write */
113 	fl_addr = ROUND_DOWN(start_addr, CONFIG_DISK_FLASH_ERASE_ALIGNMENT);
114 
115 	num_read = GET_NUM_BLOCK(CONFIG_DISK_ERASE_BLOCK_SIZE,
116 				 CONFIG_DISK_FLASH_MAX_RW_SIZE);
117 
118 	/* read one block from flash */
119 	for (uint32_t i = 0; i < num_read; i++) {
120 		int rc;
121 
122 		rc = flash_read(flash_dev,
123 				fl_addr + (CONFIG_DISK_FLASH_MAX_RW_SIZE * i),
124 				dest_buff + (CONFIG_DISK_FLASH_MAX_RW_SIZE * i),
125 				CONFIG_DISK_FLASH_MAX_RW_SIZE);
126 		if (rc != 0) {
127 			return -EIO;
128 		}
129 	}
130 
131 	/* overwrite with user data */
132 	memcpy(dest_buff + offset, src_buff, size);
133 
134 	return 0;
135 }
136 
137 /* input size is either less or equal to a block size,
138  * CONFIG_DISK_ERASE_BLOCK_SIZE.
139  */
update_flash_block(off_t start_addr,uint32_t size,const void * buff)140 static int update_flash_block(off_t start_addr, uint32_t size, const void *buff)
141 {
142 	off_t fl_addr;
143 	uint8_t *src = (uint8_t *)buff;
144 	uint32_t num_write;
145 
146 	/* if size is a partial block, perform read-copy with user data */
147 	if (size < CONFIG_DISK_ERASE_BLOCK_SIZE) {
148 		int rc;
149 
150 		rc = read_copy_flash_block(start_addr, size, buff, fs_buff);
151 		if (rc != 0) {
152 			return -EIO;
153 		}
154 
155 		/* now use the local buffer as the source */
156 		src = (uint8_t *)fs_buff;
157 	}
158 
159 	/* always align starting address for flash write operation */
160 	fl_addr = ROUND_DOWN(start_addr, CONFIG_DISK_FLASH_ERASE_ALIGNMENT);
161 
162 	if (flash_erase(flash_dev, fl_addr, CONFIG_DISK_ERASE_BLOCK_SIZE)
163 			!= 0) {
164 		return -EIO;
165 	}
166 
167 	/* write data to flash */
168 	num_write = GET_NUM_BLOCK(CONFIG_DISK_ERASE_BLOCK_SIZE,
169 				  CONFIG_DISK_FLASH_MAX_RW_SIZE);
170 
171 	for (uint32_t i = 0; i < num_write; i++) {
172 		if (flash_write(flash_dev, fl_addr, src,
173 				CONFIG_DISK_FLASH_MAX_RW_SIZE) != 0) {
174 			return -EIO;
175 		}
176 
177 		fl_addr += CONFIG_DISK_FLASH_MAX_RW_SIZE;
178 		src += CONFIG_DISK_FLASH_MAX_RW_SIZE;
179 	}
180 
181 	return 0;
182 }
183 
disk_flash_access_write(struct disk_info * disk,const uint8_t * buff,uint32_t start_sector,uint32_t sector_count)184 static int disk_flash_access_write(struct disk_info *disk, const uint8_t *buff,
185 				 uint32_t start_sector, uint32_t sector_count)
186 {
187 	off_t fl_addr;
188 	uint32_t remaining;
189 	uint32_t size;
190 
191 	fl_addr = lba_to_address(start_sector);
192 	remaining = (sector_count * SECTOR_SIZE);
193 
194 	/* check if start address is erased-aligned address  */
195 	if (fl_addr & (CONFIG_DISK_FLASH_ERASE_ALIGNMENT - 1)) {
196 		off_t block_bnd;
197 
198 		/* not aligned */
199 		/* check if the size goes over flash block boundary */
200 		block_bnd = fl_addr + CONFIG_DISK_ERASE_BLOCK_SIZE;
201 		block_bnd = block_bnd & ~(CONFIG_DISK_ERASE_BLOCK_SIZE - 1);
202 		if ((fl_addr + remaining) < block_bnd) {
203 			/* not over block boundary (a partial block also) */
204 			if (update_flash_block(fl_addr, remaining, buff) != 0) {
205 				return -EIO;
206 			}
207 			return 0;
208 		}
209 
210 		/* write goes over block boundary */
211 		size = GET_SIZE_TO_BOUNDARY(fl_addr,
212 						CONFIG_DISK_ERASE_BLOCK_SIZE);
213 
214 		/* write first partial block */
215 		if (update_flash_block(fl_addr, size, buff) != 0) {
216 			return -EIO;
217 		}
218 
219 		fl_addr += size;
220 		remaining -= size;
221 		buff += size;
222 	}
223 
224 	/* start is an erase-aligned address */
225 	while (remaining) {
226 		int rc;
227 
228 		if (remaining < CONFIG_DISK_ERASE_BLOCK_SIZE) {
229 			break;
230 		}
231 
232 		rc = update_flash_block(fl_addr, CONFIG_DISK_ERASE_BLOCK_SIZE,
233 					 buff);
234 		if (rc != 0) {
235 			return -EIO;
236 		}
237 
238 		fl_addr += CONFIG_DISK_ERASE_BLOCK_SIZE;
239 		remaining -= CONFIG_DISK_ERASE_BLOCK_SIZE;
240 		buff += CONFIG_DISK_ERASE_BLOCK_SIZE;
241 	}
242 
243 	/* remaining partial block */
244 	if (remaining) {
245 		if (update_flash_block(fl_addr, remaining, buff) != 0) {
246 			return -EIO;
247 		}
248 	}
249 
250 	return 0;
251 }
252 
disk_flash_access_ioctl(struct disk_info * disk,uint8_t cmd,void * buff)253 static int disk_flash_access_ioctl(struct disk_info *disk, uint8_t cmd, void *buff)
254 {
255 	switch (cmd) {
256 	case DISK_IOCTL_CTRL_SYNC:
257 		return 0;
258 	case DISK_IOCTL_GET_SECTOR_COUNT:
259 		*(uint32_t *)buff = CONFIG_DISK_VOLUME_SIZE / SECTOR_SIZE;
260 		return 0;
261 	case DISK_IOCTL_GET_SECTOR_SIZE:
262 		*(uint32_t *) buff = SECTOR_SIZE;
263 		return 0;
264 	case DISK_IOCTL_GET_ERASE_BLOCK_SZ: /* in sectors */
265 		*(uint32_t *)buff = CONFIG_DISK_ERASE_BLOCK_SIZE / SECTOR_SIZE;
266 		return 0;
267 	default:
268 		break;
269 	}
270 
271 	return -EINVAL;
272 }
273 
274 static const struct disk_operations flash_disk_ops = {
275 	.init = disk_flash_access_init,
276 	.status = disk_flash_access_status,
277 	.read = disk_flash_access_read,
278 	.write = disk_flash_access_write,
279 	.ioctl = disk_flash_access_ioctl,
280 };
281 
282 static struct disk_info flash_disk = {
283 	.name = CONFIG_DISK_FLASH_VOLUME_NAME,
284 	.ops = &flash_disk_ops,
285 };
286 
disk_flash_init(const struct device * dev)287 static int disk_flash_init(const struct device *dev)
288 {
289 	ARG_UNUSED(dev);
290 
291 	return disk_access_register(&flash_disk);
292 }
293 
294 SYS_INIT(disk_flash_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
295