dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_flash.c
Go to the documentation of this file.
1 
11 /*
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, see <http://www.gnu.org/licenses/>
24  */
25 
26 #include "pios.h"
27 
28 #if defined(PIOS_INCLUDE_FLASH)
29 
30 #include "pios_flash_priv.h" /* External API definition */
31 
32 #include <stdbool.h> /* bool */
33 #include <stdlib.h> /* NULL */
34 
35 static bool pios_flash_partition_get_chip_extents(const struct pios_flash_partition *partition, uint32_t *chip_start_offset, uint32_t *chip_end_offset);
36 
37 static struct pios_flash_partition const *partitions;
38 static uint8_t num_partitions;
39 
40 #define MIN(x,y) ((x) < (y) ? (x) : (y))
41 
51  struct pios_flash_partition *partition_table,
52  uint8_t partition_table_len,
53  const struct pios_flash_chip *descriptor,
54  struct pios_flash_sector_range *sectors,
55  uint32_t num_bytes)
56 {
57  PIOS_Assert(descriptor->sector_blocks == sectors);
58 
59  uint32_t basic_capacity = 0;
60 
61  for (int i = 0; i < descriptor->num_blocks; i++) {
62  basic_capacity +=
63  (sectors[i].last_sector - sectors[i].base_sector + 1) *
64  sectors[i].sector_size;
65  }
66 
67  PIOS_Assert(num_bytes >= basic_capacity);
68 
69  if (num_bytes == basic_capacity) {
70  return; // nothing to see here.
71  }
72 
73  /* Select the last group of sectors */
74  sectors += descriptor->num_blocks - 1;
75 
76  uint32_t old_last_sector = sectors->last_sector;
77 
78  uint32_t additional_bytes = num_bytes - basic_capacity;
79  uint32_t additional_sectors = additional_bytes / sectors->sector_size;
80 
81  PIOS_Assert(additional_bytes ==
82  (additional_sectors * sectors->sector_size));
83 
84  sectors->last_sector += additional_sectors;
85 
86  // iterate partitions.. where descriptor and old last sector
87  // matches, increase the size.
88  for (int i = 0; i < partition_table_len; i++) {
89  if (partition_table[i].last_sector == old_last_sector) {
90  if (partition_table[i].chip_desc == descriptor) {
91  partition_table[i].last_sector =
92  sectors->last_sector;
93  partition_table[i].size += additional_bytes;
94  }
95  }
96  }
97 }
98 
104 void PIOS_FLASH_register_partition_table(const struct pios_flash_partition partition_table[], uint8_t partition_table_len)
105 {
106  PIOS_Assert(partition_table);
107 
108  for (uint8_t i = 0; i < partition_table_len; i++) {
109  const struct pios_flash_partition *partition = &partition_table[i];
111  PIOS_Assert(partition->chip_desc);
112  PIOS_Assert(partition->chip_desc->driver);
113  PIOS_Assert(partition->chip_desc->chip_id);
114  PIOS_Assert(partition->chip_desc->sector_blocks);
115  PIOS_Assert(partition->chip_desc->num_blocks > 0);
116  PIOS_Assert(partition->last_sector >= partition->first_sector);
117 
118  /* Make sure the provided chip extents match the computed values */
119  uint32_t chip_start_offset;
120  uint32_t chip_end_offset;
121  if (!pios_flash_partition_get_chip_extents(partition,
122  &chip_start_offset,
123  &chip_end_offset))
124  PIOS_Abort();
125 
126  PIOS_Assert(partition->chip_offset == chip_start_offset);
127  PIOS_Assert(partition->size == (chip_end_offset - chip_start_offset + 1));
128  }
129 
130  partitions = partition_table;
131  num_partitions = partition_table_len;
132 }
133 
141 int32_t PIOS_FLASH_find_partition_id(enum pios_flash_partition_labels label, uintptr_t *partition_id)
142 {
143  PIOS_Assert(partition_id);
144 
145  for (uint8_t i = 0; i < num_partitions; i++) {
146  if (partitions[i].label == label) {
147  *partition_id = (uintptr_t) &partitions[i];
148  return 0;
149  }
150  }
151 
152  return -1;
153 
154 }
155 
160 uint16_t PIOS_FLASH_get_num_partitions(void)
161 {
162  return num_partitions;
163 }
164 
165 static bool PIOS_FLASH_validate_partition(const struct pios_flash_partition *partition)
166 {
167  return ((partition >= &partitions[0]) && (partition < &partitions[num_partitions]));
168 }
169 
170 struct pios_flash_sector_desc {
171  /* Context */
172  uint16_t block_id;
173  uint32_t sector;
174 
175  /* User information */
176  uint32_t chip_offset;
177  uint32_t partition_offset;
178  uint32_t sector_size;
179 };
180 
181 static bool pios_flash_get_partition_first_sector(const struct pios_flash_partition *partition, struct pios_flash_sector_desc *curr)
182 {
183  /* Find the beginning of the partition */
184  uint32_t chip_offset = 0;
185  for (uint16_t block_id = 0; block_id < partition->chip_desc->num_blocks; block_id++) {
186  const struct pios_flash_sector_range *block = &partition->chip_desc->sector_blocks[block_id];
187 
188  if ((partition->first_sector >= block->base_sector) && (partition->first_sector <= block->last_sector)) {
189  /* Sector is in this block. Compute offset within this block */
190  chip_offset += (partition->first_sector - block->base_sector) *block->sector_size;
191 
192  curr->block_id = block_id;
193  curr->sector = partition->first_sector;
194 
195  curr->chip_offset = chip_offset;
196  curr->sector_size = block->sector_size;
197  curr->partition_offset = 0;
198 
199  return true;
200  } else {
201  /* Not this block. Skip forward to the next block. */
202  uint32_t num_sectors_in_range = (block->last_sector - block->base_sector + 1);
203  chip_offset += num_sectors_in_range *block->sector_size;
204  }
205  }
206 
207  return false;
208 }
209 
210 static bool pios_flash_get_partition_next_sector(const struct pios_flash_partition *partition, struct pios_flash_sector_desc *curr)
211 {
212  /* Are we past the end of the device? */
213  if (curr->block_id >= partition->chip_desc->num_blocks)
214  return false;
215 
216  const struct pios_flash_sector_range *block = &partition->chip_desc->sector_blocks[curr->block_id];
217 
218  /* Is the current sector within the current block? */
219  if ((curr->sector < block->base_sector) || (curr->sector > block->last_sector))
220  return false;
221 
222  /* Is the current sector within the current partition? */
223  if ((curr->sector < partition->first_sector) || (curr->sector > partition->last_sector))
224  return false;
225 
226  /*
227  * We've been given a valid context, find the next sector in the partition
228  */
229 
230  /* Accumulate the size of the current sector into the offsets */
231  curr->chip_offset += block->sector_size;
232  curr->partition_offset += block->sector_size;
233 
234  curr->sector++;
235 
236  /* Are we still in the partition? */
237  if (curr->sector > partition->last_sector)
238  return false;
239 
240  /* Are we now beyond the end of the current block? */
241  if (curr->sector > block->last_sector) {
242  /* Move to the next block */
243  curr->block_id++;
244 
245  /* Are we still within the chip boundaries? */
246  if (curr->block_id >= partition->chip_desc->num_blocks)
247  return false;
248 
249  block = &partition->chip_desc->sector_blocks[curr->block_id];
250  }
251 
252  curr->sector_size = block->sector_size;
253 
254  return true;
255 }
256 
257 static bool pios_flash_partition_get_chip_extents(const struct pios_flash_partition *partition, uint32_t *chip_start_offset, uint32_t *chip_end_offset)
258 {
259  struct pios_flash_sector_desc sector_desc;
260  if (!pios_flash_get_partition_first_sector(partition, &sector_desc))
261  return false;
262 
263  if (chip_start_offset)
264  *chip_start_offset = sector_desc.chip_offset;
265 
266  /* Traverse the current partition to find the end of it */
267  do {
268  ;
269  } while (pios_flash_get_partition_next_sector(partition, &sector_desc));
270 
271  if (chip_end_offset)
272  *chip_end_offset = sector_desc.chip_offset - 1;
273 
274  return true;
275 }
276 
285 int32_t PIOS_FLASH_get_partition_size(uintptr_t partition_id, uint32_t *partition_size)
286 {
287  PIOS_Assert(partition_size);
288 
289  struct pios_flash_partition *partition = (struct pios_flash_partition *)partition_id;
290 
291  PIOS_Assert(PIOS_FLASH_validate_partition(partition));
292 
293  *partition_size = partition->size;
294 
295  return 0;
296 }
297 
304 void *PIOS_FLASH_get_address(uintptr_t partition_id, uint32_t *partition_size) {
305  struct pios_flash_partition *partition = (struct pios_flash_partition *)partition_id;
306 
307  PIOS_Assert(PIOS_FLASH_validate_partition(partition));
308 
309  if (partition_size) {
310  *partition_size = partition->size;
311  }
312 
313  if (!partition->chip_desc->driver->get_pointer) {
314  return NULL;
315  }
316 
317  return partition->chip_desc->driver->get_pointer(*partition->chip_desc->chip_id, partition->chip_offset);
318 }
319 
327 int32_t PIOS_FLASH_start_transaction(uintptr_t partition_id)
328 {
329  struct pios_flash_partition *partition = (struct pios_flash_partition *)partition_id;
330 
331  PIOS_Assert(PIOS_FLASH_validate_partition(partition));
332 
333  if (partition->chip_desc->driver->start_transaction)
334  return partition->chip_desc->driver->start_transaction(*partition->chip_desc->chip_id);
335 
336  /* If the underlying driver doesn't support this, just return OK */
337  return 0;
338 }
339 
347 int32_t PIOS_FLASH_end_transaction(uintptr_t partition_id)
348 {
349  struct pios_flash_partition *partition = (struct pios_flash_partition *)partition_id;
350 
351  if (!PIOS_FLASH_validate_partition(partition))
352  return -20;
353 
354  if (partition->chip_desc->driver->end_transaction)
355  return partition->chip_desc->driver->end_transaction(*partition->chip_desc->chip_id);
356 
357  /* If the underlying driver doesn't support this, just return OK */
358  return 0;
359 }
360 
376 int32_t PIOS_FLASH_erase_range(uintptr_t partition_id, uint32_t start_offset, uint32_t size)
377 {
378  struct pios_flash_partition *partition = (struct pios_flash_partition *)partition_id;
379 
380  PIOS_Assert(PIOS_FLASH_validate_partition(partition));
381 
382  if (!partition->chip_desc->driver->erase_sector)
383  return -21;
384 
385  struct pios_flash_sector_desc sector_desc;
386  if (!pios_flash_get_partition_first_sector(partition, &sector_desc))
387  return -22;
388 
389  /* Traverse the current partition and erase sectors within the requested range */
390  uint32_t erase_offset = start_offset;
391  do {
392  /* Have we finished erasing? */
393  if (size == 0)
394  break;
395 
396  /* Is our start offset in the current sector? */
397  if ((erase_offset >= sector_desc.partition_offset) &&
398  (erase_offset < sector_desc.partition_offset + sector_desc.sector_size)) {
399  /* Make sure we're erasing on a sector boundary */
400  if (erase_offset != sector_desc.partition_offset)
401  return -23;
402 
403  /* Make sure we're intending to erase the entire sector */
404  if (size < sector_desc.sector_size)
405  return -24;
406 
407  /* Erase the sector */
408  if (partition->chip_desc->driver->erase_sector(*partition->chip_desc->chip_id,
409  sector_desc.sector,
410  sector_desc.chip_offset) != 0) {
411  return -25;
412  }
413 
414  /* Update our accounting */
415  erase_offset += sector_desc.sector_size;
416  size -= sector_desc.sector_size;
417  }
418  } while (pios_flash_get_partition_next_sector(partition, &sector_desc));
419 
420  return 0;
421 }
422 
434 int32_t PIOS_FLASH_erase_partition(uintptr_t partition_id)
435 {
436  struct pios_flash_partition *partition = (struct pios_flash_partition *)partition_id;
437 
438  PIOS_Assert(PIOS_FLASH_validate_partition(partition));
439 
440  if (!partition->chip_desc->driver->erase_sector)
441  return -21;
442 
443  struct pios_flash_sector_desc sector_desc;
444 
445  if (!pios_flash_get_partition_first_sector(partition, &sector_desc))
446  return -22;
447 
448  /* Traverse the current partition and erase sectors within the requested range */
449  do {
450  /* Erase the sector */
451  if (partition->chip_desc->driver->erase_sector(*partition->chip_desc->chip_id,
452  sector_desc.sector,
453  sector_desc.chip_offset) != 0) {
454  return -23;
455  }
456  } while (pios_flash_get_partition_next_sector(partition, &sector_desc));
457 
458  return 0;
459 }
460 
474 int32_t PIOS_FLASH_write_data(uintptr_t partition_id, uint32_t partition_offset, const uint8_t *data, uint16_t len)
475 {
476  struct pios_flash_partition *partition = (struct pios_flash_partition *)partition_id;
477 
478  PIOS_Assert(PIOS_FLASH_validate_partition(partition));
479 
480  if (!partition->chip_desc->driver->write_data)
481  return -21;
482 
483  /* Are we writing past the end of the partition? */
484  if ((partition_offset + len) > partition->size)
485  return -22;
486 
487  /* Fragment the writes to chip's page size */
488  uint32_t page_size = partition->chip_desc->page_size;
489  while (len > 0) {
490  /* Individual writes must fit entirely within a single page buffer. */
491  uint32_t page_remaining = page_size - (partition_offset % page_size);
492  uint16_t write_size = MIN(len, page_remaining);
493 
494  int32_t rc = partition->chip_desc->driver->write_data(*partition->chip_desc->chip_id,
495  partition->chip_offset + partition_offset,
496  data,
497  write_size);
498  if (rc != 0) {
499  /* Failed to write the data to the underlying flash */
500  return rc;
501  }
502 
503  /* Update our accounting */
504  data += write_size;
505  partition_offset += write_size;
506  len -= write_size;
507  }
508 
509  return 0;
510 }
511 
524 int32_t PIOS_FLASH_read_data(uintptr_t partition_id, uint32_t partition_offset, uint8_t *data, uint16_t len)
525 {
526  struct pios_flash_partition *partition = (struct pios_flash_partition *)partition_id;
527 
528  PIOS_Assert(PIOS_FLASH_validate_partition(partition));
529 
530  if (!partition->chip_desc->driver->read_data)
531  return -21;
532 
533  /* Are we reading past the end of the partition? */
534  if ((partition_offset + len) > partition->size)
535  return -22;
536 
537  return partition->chip_desc->driver->read_data(*partition->chip_desc->chip_id,
538  partition->chip_offset + partition_offset,
539  data,
540  len);
541 }
542 
543 #endif /* PIOS_INCLUDE_FLASH */
544 
int32_t PIOS_FLASH_find_partition_id(enum pios_flash_partition_labels label, uintptr_t *partition_id)
int32_t PIOS_FLASH_erase_partition(uintptr_t partition_id)
int32_t(* read_data)(uintptr_t chip_id, uint32_t chip_offset, uint8_t *data, uint16_t len)
enum dfu_partition_label label
Definition: bl_messages.h:121
Main PiOS header to include all the compiled in PiOS options.
uint16_t PIOS_FLASH_get_num_partitions(void)
void *(* get_pointer)(uintptr_t chip_id, uint32_t chip_offset)
const struct pios_flash_sector_range * sector_blocks
int32_t PIOS_FLASH_get_partition_size(uintptr_t partition_id, uint32_t *partition_size)
void * PIOS_FLASH_get_address(uintptr_t partition_id, uint32_t *partition_size)
int32_t(* erase_sector)(uintptr_t chip_id, uint32_t chip_sector, uint32_t chip_offset)
uintptr_t * chip_id
const struct pios_flash_chip * chip_desc
uint8_t data[XFER_BYTES_PER_PACKET]
Definition: bl_messages.h:129
int32_t PIOS_FLASH_write_data(uintptr_t partition_id, uint32_t offset, const uint8_t *data, uint16_t len)
int32_t PIOS_FLASH_end_transaction(uintptr_t partition_id)
int32_t(* end_transaction)(uintptr_t chip_id)
uint8_t i
Definition: msp_messages.h:97
#define PIOS_Abort()
Definition: pios_debug.h:55
int32_t PIOS_FLASH_erase_range(uintptr_t partition_id, uint32_t start_offset, uint32_t size)
void PIOS_FLASH_fixup_partitions_for_capacity(struct pios_flash_partition *partition_table, uint8_t partition_table_len, const struct pios_flash_chip *descriptor, struct pios_flash_sector_range *sectors, uint32_t num_bytes)
int32_t(* write_data)(uintptr_t chip_id, uint32_t chip_offset, const uint8_t *data, uint16_t len)
#define MIN(a, b)
Definition: misc_math.h:41
void PIOS_FLASH_register_partition_table(const struct pios_flash_partition partition_table[], uint8_t num_partitions)
pios_flash_partition_labels
Definition: pios_flash.h:31
const struct pios_flash_driver * driver
int32_t PIOS_FLASH_read_data(uintptr_t partition_id, uint32_t offset, uint8_t *data, uint16_t len)
int32_t PIOS_FLASH_start_transaction(uintptr_t partition_id)
enum pios_flash_partition_labels label
#define PIOS_Assert(test)
Definition: pios_debug.h:52
int32_t(* start_transaction)(uintptr_t chip_id)