@@ -28,12 +28,11 @@ struct flashdisk_data {
28
28
const size_t size ;
29
29
const size_t sector_size ;
30
30
size_t page_size ;
31
+ off_t cached_addr ;
32
+ bool cache_valid ;
33
+ bool cache_dirty ;
31
34
};
32
35
33
- /* calculate number of blocks required for a given size */
34
- #define GET_NUM_BLOCK (total_size , block_size ) \
35
- ((total_size + block_size - 1) / block_size)
36
-
37
36
#define GET_SIZE_TO_BOUNDARY (start , block_size ) \
38
37
(block_size - (start & (block_size - 1)))
39
38
@@ -83,6 +82,11 @@ static int disk_flash_access_init(struct disk_info *disk)
83
82
return - ENOMEM ;
84
83
}
85
84
85
+ if (ctx -> cache_valid && ctx -> cache_dirty ) {
86
+ LOG_ERR ("Discarding %s dirty cache" , disk -> name );
87
+ ctx -> cache_valid = false;
88
+ }
89
+
86
90
return 0 ;
87
91
}
88
92
@@ -109,8 +113,8 @@ static int disk_flash_access_read(struct disk_info *disk, uint8_t *buff,
109
113
struct flashdisk_data * ctx ;
110
114
off_t fl_addr ;
111
115
uint32_t remaining ;
116
+ uint32_t offset ;
112
117
uint32_t len ;
113
- uint32_t num_read ;
114
118
115
119
ctx = CONTAINER_OF (disk , struct flashdisk_data , info );
116
120
@@ -120,118 +124,125 @@ static int disk_flash_access_read(struct disk_info *disk, uint8_t *buff,
120
124
121
125
fl_addr = ctx -> offset + start_sector * ctx -> sector_size ;
122
126
remaining = (sector_count * ctx -> sector_size );
123
- len = ctx -> page_size ;
124
- num_read = GET_NUM_BLOCK (remaining , ctx -> page_size );
125
127
126
- for (uint32_t i = 0 ; i < num_read ; i ++ ) {
127
- if (remaining < ctx -> page_size ) {
128
+ /* Operate on page addresses to easily check for cached data */
129
+ offset = fl_addr & (ctx -> page_size - 1 );
130
+ fl_addr = ROUND_DOWN (fl_addr , ctx -> page_size );
131
+
132
+ /* Read up to page boundary on first iteration */
133
+ len = ctx -> page_size - offset ;
134
+ while (remaining ) {
135
+ if (remaining < len ) {
128
136
len = remaining ;
129
137
}
130
138
131
- if (flash_read (disk -> dev , fl_addr , buff , len ) < 0 ) {
139
+ if (ctx -> cache_valid && ctx -> cached_addr == fl_addr ) {
140
+ memcpy (buff , & ctx -> cache [offset ], len );
141
+ } else if (flash_read (disk -> dev , fl_addr + offset , buff , len ) < 0 ) {
132
142
return - EIO ;
133
143
}
134
144
135
- fl_addr += len ;
136
- buff += len ;
145
+ fl_addr += ctx -> page_size ;
137
146
remaining -= len ;
147
+ buff += len ;
148
+
149
+ /* Try to read whole page on next iteration */
150
+ len = ctx -> page_size ;
151
+ offset = 0 ;
138
152
}
139
153
140
154
return 0 ;
141
155
}
142
156
143
- /* This performs read-copy into an output buffer */
144
- static int read_copy_flash_block (struct disk_info * disk ,
145
- off_t start_addr ,
146
- uint32_t size ,
147
- const void * src_buff ,
148
- uint8_t * dest_buff )
157
+ static int flashdisk_cache_commit (struct flashdisk_data * ctx )
149
158
{
150
- struct flashdisk_data * ctx ;
151
- off_t fl_addr ;
152
- uint32_t offset ;
153
- uint32_t remaining ;
154
-
155
- ctx = CONTAINER_OF (disk , struct flashdisk_data , info );
156
-
157
- /* adjust offset if starting address is not erase-aligned address */
158
- offset = start_addr & (ctx -> page_size - 1 );
159
-
160
- /* align starting address to page boundary */
161
- fl_addr = ROUND_DOWN (start_addr , ctx -> page_size );
162
-
163
- if (offset > 0 ) {
164
- /* read page contents prior to user data */
165
- if (flash_read (disk -> dev , fl_addr , dest_buff , offset ) < 0 ) {
166
- return - EIO ;
167
- }
159
+ if (!ctx -> cache_valid || !ctx -> cache_dirty ) {
160
+ /* Either no cached data or cache matches flash data */
161
+ return 0 ;
168
162
}
169
163
170
- /* write user data in page buffer */
171
- memcpy (dest_buff + offset , src_buff , size );
164
+ if (flash_erase (ctx -> info .dev , ctx -> cached_addr , ctx -> page_size ) < 0 ) {
165
+ return - EIO ;
166
+ }
172
167
173
- remaining = ctx -> page_size - (offset + size );
174
- if (remaining ) {
175
- /* read page contents after user data */
176
- if (flash_read (disk -> dev , start_addr + size ,
177
- & dest_buff [offset + size ], remaining ) < 0 ) {
178
- return - EIO ;
179
- }
168
+ /* write data to flash */
169
+ if (flash_write (ctx -> info .dev , ctx -> cached_addr , ctx -> cache , ctx -> page_size ) < 0 ) {
170
+ return - EIO ;
180
171
}
181
172
173
+ ctx -> cache_dirty = false;
182
174
return 0 ;
183
175
}
184
176
185
- static int overwrite_flash_block (struct disk_info * disk , off_t fl_addr ,
186
- const void * buff )
177
+ static int flashdisk_cache_load (struct flashdisk_data * ctx , off_t fl_addr )
187
178
{
188
- struct flashdisk_data * ctx ;
179
+ int rc ;
189
180
190
- ctx = CONTAINER_OF ( disk , struct flashdisk_data , info );
181
+ __ASSERT_NO_MSG (( fl_addr & ( ctx -> page_size - 1 )) == 0 );
191
182
192
- if (flash_erase (disk -> dev , fl_addr , ctx -> page_size ) < 0 ) {
193
- return - EIO ;
183
+ if (ctx -> cache_valid ) {
184
+ if (ctx -> cached_addr == fl_addr ) {
185
+ /* Page is already cached */
186
+ return 0 ;
187
+ }
188
+ /* Different page is in cache, commit it first */
189
+ rc = flashdisk_cache_commit (ctx );
190
+ if (rc < 0 ) {
191
+ /* Failed to commit dirty page, abort */
192
+ return rc ;
193
+ }
194
194
}
195
195
196
- /* write data to flash */
197
- if (flash_write (disk -> dev , fl_addr , buff , ctx -> page_size ) < 0 ) {
198
- return - EIO ;
196
+ /* Load page into cache */
197
+ ctx -> cache_valid = false;
198
+ ctx -> cache_dirty = false;
199
+ ctx -> cached_addr = fl_addr ;
200
+ rc = flash_read (ctx -> info .dev , fl_addr , ctx -> cache , ctx -> page_size );
201
+ if (rc == 0 ) {
202
+ /* Successfully loaded into cache, mark as valid */
203
+ ctx -> cache_valid = true;
204
+ return 0 ;
199
205
}
200
206
201
- return 0 ;
207
+ return - EIO ;
202
208
}
203
209
204
210
/* input size is either less or equal to a block size (ctx->page_size)
205
211
* and write data never spans across adjacent blocks.
206
212
*/
207
- static int update_flash_block (struct disk_info * disk , off_t start_addr ,
208
- uint32_t size , const void * buff )
213
+ static int flashdisk_cache_write (struct flashdisk_data * ctx , off_t start_addr ,
214
+ uint32_t size , const void * buff )
209
215
{
210
- struct flashdisk_data * ctx ;
211
- off_t fl_addr ;
212
216
int rc ;
217
+ off_t fl_addr ;
218
+ uint32_t offset ;
213
219
214
- ctx = CONTAINER_OF (disk , struct flashdisk_data , info );
220
+ /* adjust offset if starting address is not erase-aligned address */
221
+ offset = start_addr & (ctx -> page_size - 1 );
215
222
216
- /* always align starting address for flash write operation */
223
+ /* always align starting address for flash cache operations */
217
224
fl_addr = ROUND_DOWN (start_addr , ctx -> page_size );
218
225
219
226
/* when writing full page the address must be page aligned
220
227
* when writing partial page user data must be within a single page
221
228
*/
222
229
__ASSERT_NO_MSG (fl_addr + ctx -> page_size >= start_addr + size );
223
230
224
- if (size == ctx -> page_size ) {
225
- rc = overwrite_flash_block (disk , fl_addr , buff );
226
- } else {
227
- /* partial block, perform read-copy with user data */
228
- rc = read_copy_flash_block (disk , start_addr , size , buff , ctx -> cache );
229
- if (rc == 0 ) {
230
- rc = overwrite_flash_block (disk , fl_addr , ctx -> cache );
231
- }
231
+ rc = flashdisk_cache_load (ctx , fl_addr );
232
+ if (rc < 0 ) {
233
+ return rc ;
232
234
}
233
235
234
- return rc == 0 ? 0 : - EIO ;
236
+ /* Do not mark cache as dirty if data to be written matches cache.
237
+ * If cache is already dirty, copy data to cache without compare.
238
+ */
239
+ if (ctx -> cache_dirty || memcmp (& ctx -> cache [offset ], buff , size )) {
240
+ /* Update cache and mark it as dirty */
241
+ memcpy (& ctx -> cache [offset ], buff , size );
242
+ ctx -> cache_dirty = true;
243
+ }
244
+
245
+ return 0 ;
235
246
}
236
247
237
248
static int disk_flash_access_write (struct disk_info * disk , const uint8_t * buff ,
@@ -261,7 +272,7 @@ static int disk_flash_access_write(struct disk_info *disk, const uint8_t *buff,
261
272
block_bnd = block_bnd & ~(ctx -> page_size - 1 );
262
273
if ((fl_addr + remaining ) <= block_bnd ) {
263
274
/* not over block boundary (a partial block also) */
264
- if (update_flash_block ( disk , fl_addr , remaining , buff ) < 0 ) {
275
+ if (flashdisk_cache_write ( ctx , fl_addr , remaining , buff ) < 0 ) {
265
276
return - EIO ;
266
277
}
267
278
return 0 ;
@@ -271,7 +282,7 @@ static int disk_flash_access_write(struct disk_info *disk, const uint8_t *buff,
271
282
size = GET_SIZE_TO_BOUNDARY (fl_addr , ctx -> page_size );
272
283
273
284
/* write first partial block */
274
- if (update_flash_block ( disk , fl_addr , size , buff ) < 0 ) {
285
+ if (flashdisk_cache_write ( ctx , fl_addr , size , buff ) < 0 ) {
275
286
return - EIO ;
276
287
}
277
288
@@ -286,7 +297,7 @@ static int disk_flash_access_write(struct disk_info *disk, const uint8_t *buff,
286
297
break ;
287
298
}
288
299
289
- if (update_flash_block ( disk , fl_addr , ctx -> page_size , buff ) < 0 ) {
300
+ if (flashdisk_cache_write ( ctx , fl_addr , ctx -> page_size , buff ) < 0 ) {
290
301
return - EIO ;
291
302
}
292
303
@@ -297,7 +308,7 @@ static int disk_flash_access_write(struct disk_info *disk, const uint8_t *buff,
297
308
298
309
/* remaining partial block */
299
310
if (remaining ) {
300
- if (update_flash_block ( disk , fl_addr , remaining , buff ) < 0 ) {
311
+ if (flashdisk_cache_write ( ctx , fl_addr , remaining , buff ) < 0 ) {
301
312
return - EIO ;
302
313
}
303
314
}
@@ -313,7 +324,7 @@ static int disk_flash_access_ioctl(struct disk_info *disk, uint8_t cmd, void *bu
313
324
314
325
switch (cmd ) {
315
326
case DISK_IOCTL_CTRL_SYNC :
316
- return 0 ;
327
+ return flashdisk_cache_commit ( ctx ) ;
317
328
case DISK_IOCTL_GET_SECTOR_COUNT :
318
329
* (uint32_t * )buff = ctx -> size / ctx -> sector_size ;
319
330
return 0 ;
0 commit comments