mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-26 06:08:20 +00:00
bsps: Move libchip to bsps
This patch is a part of the BSP source reorganization. Update #3285.
This commit is contained in:
473
bsps/shared/dev/flash/am29lv160.c
Normal file
473
bsps/shared/dev/flash/am29lv160.c
Normal file
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
* RTEMS Project (http://www.rtems.org/)
|
||||
*
|
||||
* Copyright 2007 Chris Johns (chrisj@rtems.org)
|
||||
*/
|
||||
/**
|
||||
* Provide flash support for the AM26LV160 device.
|
||||
*
|
||||
* The M29W160D is the same device.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <rtems.h>
|
||||
|
||||
#include <libchip/am29lv160.h>
|
||||
|
||||
#ifndef AM26LV160_ERROR_TRACE
|
||||
#define AM26LV160_ERROR_TRACE (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Boot blocks at the top
|
||||
*/
|
||||
const rtems_fdisk_segment_desc rtems_am29lv160t_segments[4] =
|
||||
{
|
||||
{
|
||||
.count = 31,
|
||||
.segment = 0,
|
||||
.offset = 0x00000000,
|
||||
.size = RTEMS_FDISK_KBYTES (64)
|
||||
},
|
||||
{
|
||||
.count = 1,
|
||||
.segment = 31,
|
||||
.offset = 0x001f0000,
|
||||
.size = RTEMS_FDISK_KBYTES (32)
|
||||
},
|
||||
{
|
||||
.count = 2,
|
||||
.segment = 32,
|
||||
.offset = 0x001f8000,
|
||||
.size = RTEMS_FDISK_KBYTES (8)
|
||||
},
|
||||
{
|
||||
.count = 1,
|
||||
.segment = 34,
|
||||
.offset = 0x001fc000,
|
||||
.size = RTEMS_FDISK_KBYTES (16)
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Boot blocks at the bottom.
|
||||
*/
|
||||
const rtems_fdisk_segment_desc rtems_am29lv160b_segments[] =
|
||||
{
|
||||
{
|
||||
.count = 1,
|
||||
.segment = 0,
|
||||
.offset = 0x00000000,
|
||||
.size = RTEMS_FDISK_KBYTES (16)
|
||||
},
|
||||
{
|
||||
. count = 2,
|
||||
.segment = 1,
|
||||
.offset = 0x00004000,
|
||||
.size = RTEMS_FDISK_KBYTES (8)
|
||||
},
|
||||
{
|
||||
.count = 1,
|
||||
.segment = 3,
|
||||
.offset = 0x00008000,
|
||||
.size = RTEMS_FDISK_KBYTES (32)
|
||||
},
|
||||
{
|
||||
.count = 31,
|
||||
.segment = 4,
|
||||
.offset = 0x00010000,
|
||||
.size = RTEMS_FDISK_KBYTES (64)
|
||||
}
|
||||
};
|
||||
|
||||
static int
|
||||
rtems_am29lv160_blank (const rtems_fdisk_segment_desc* sd,
|
||||
uint32_t device,
|
||||
uint32_t segment,
|
||||
uint32_t offset,
|
||||
uint32_t size)
|
||||
{
|
||||
const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device];
|
||||
volatile uint8_t* seg_8 = ac->base;
|
||||
volatile uint32_t* seg_32;
|
||||
uint32_t count;
|
||||
|
||||
offset += sd->offset + (segment - sd->segment) * sd->size;
|
||||
|
||||
seg_8 += offset;
|
||||
|
||||
count = offset & (sizeof (uint32_t) - 1);
|
||||
size -= count;
|
||||
|
||||
while (count--)
|
||||
if (*seg_8++ != 0xff)
|
||||
{
|
||||
#if AM26LV160_ERROR_TRACE
|
||||
printf ("AM26LV160: blank check error: %p = 0x%02x\n",
|
||||
seg_8 - 1, *(seg_8 - 1));
|
||||
#endif
|
||||
return EIO;
|
||||
}
|
||||
|
||||
seg_32 = (volatile uint32_t*) seg_8;
|
||||
|
||||
count = size / sizeof (uint32_t);
|
||||
size -= count * sizeof (uint32_t);
|
||||
|
||||
while (count--)
|
||||
if (*seg_32++ != 0xffffffff)
|
||||
{
|
||||
#if AM26LV160_ERROR_TRACE
|
||||
printf ("AM26LV160: blank check error: %p = 0x%08lx\n",
|
||||
seg_32 - 1, *(seg_32 - 1));
|
||||
#endif
|
||||
return EIO;
|
||||
}
|
||||
|
||||
seg_8 = (volatile uint8_t*) seg_32;
|
||||
|
||||
while (size--)
|
||||
if (*seg_8++ != 0xff)
|
||||
{
|
||||
#if AM26LV160_ERROR_TRACE
|
||||
printf ("AM26LV160: blank check error: %p = 0x%02x\n",
|
||||
seg_8 - 1, *(seg_8 - 1));
|
||||
#endif
|
||||
return EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rtems_am29lv160_verify (const rtems_fdisk_segment_desc* sd,
|
||||
uint32_t device,
|
||||
uint32_t segment,
|
||||
uint32_t offset,
|
||||
const void* buffer,
|
||||
uint32_t size)
|
||||
{
|
||||
const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device];
|
||||
const uint8_t* addr = ac->base;
|
||||
|
||||
addr += (sd->offset + (segment - sd->segment) * sd->size) + offset;
|
||||
|
||||
if (memcmp (addr, buffer, size) != 0)
|
||||
return EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rtems_am29lv160_toggle_wait_8 (volatile uint8_t* status)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
volatile uint8_t status1 = *status;
|
||||
volatile uint8_t status2 = *status;
|
||||
|
||||
if (((status1 ^ status2) & (1 << 6)) == 0)
|
||||
return 0;
|
||||
|
||||
if ((status1 & (1 << 5)) != 0)
|
||||
{
|
||||
status1 = *status;
|
||||
status2 = *status;
|
||||
|
||||
if (((status1 ^ status2) & (1 << 6)) == 0)
|
||||
return 0;
|
||||
|
||||
#if AM26LV160_ERROR_TRACE
|
||||
printf ("AM26LV160: error bit detected: %p = 0x%04x\n",
|
||||
status, status1);
|
||||
#endif
|
||||
|
||||
*status = 0xf0;
|
||||
return EIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
rtems_am29lv160_toggle_wait_16 (volatile uint16_t* status)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
volatile uint16_t status1 = *status;
|
||||
volatile uint16_t status2 = *status;
|
||||
|
||||
if (((status1 ^ status2) & (1 << 6)) == 0)
|
||||
return 0;
|
||||
|
||||
if ((status1 & (1 << 5)) != 0)
|
||||
{
|
||||
status1 = *status;
|
||||
status2 = *status;
|
||||
|
||||
if (((status1 ^ status2) & (1 << 6)) == 0)
|
||||
return 0;
|
||||
|
||||
#if AM26LV160_ERROR_TRACE
|
||||
printf ("AM26LV160: error bit detected: %p = 0x%04x/0x%04x\n",
|
||||
status, status1, status2);
|
||||
#endif
|
||||
|
||||
*status = 0xf0;
|
||||
return EIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
rtems_am29lv160_write_data_8 (volatile uint8_t* base,
|
||||
uint32_t offset,
|
||||
const uint8_t* data,
|
||||
uint32_t size)
|
||||
{
|
||||
volatile uint8_t* seg = base + offset;
|
||||
rtems_interrupt_level level;
|
||||
|
||||
/*
|
||||
* Issue a reset.
|
||||
*/
|
||||
*base = 0xf0;
|
||||
|
||||
while (size)
|
||||
{
|
||||
rtems_interrupt_disable (level);
|
||||
*(base + 0xaaa) = 0xaa;
|
||||
*(base + 0x555) = 0x55;
|
||||
*(base + 0xaaa) = 0xa0;
|
||||
*seg = *data++;
|
||||
rtems_interrupt_enable (level);
|
||||
if (rtems_am29lv160_toggle_wait_8 (seg++) != 0)
|
||||
return EIO;
|
||||
size--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Issue a reset.
|
||||
*/
|
||||
*base = 0xf0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rtems_am29lv160_write_data_16 (volatile uint16_t* base,
|
||||
uint32_t offset,
|
||||
const uint16_t* data,
|
||||
uint32_t size)
|
||||
{
|
||||
volatile uint16_t* seg = base + (offset / 2);
|
||||
rtems_interrupt_level level;
|
||||
|
||||
size /= 2;
|
||||
|
||||
/*
|
||||
* Issue a reset.
|
||||
*/
|
||||
*base = 0xf0;
|
||||
|
||||
while (size)
|
||||
{
|
||||
rtems_interrupt_disable (level);
|
||||
*(base + 0x555) = 0xaa;
|
||||
*(base + 0x2aa) = 0x55;
|
||||
*(base + 0x555) = 0xa0;
|
||||
*seg = *data++;
|
||||
rtems_interrupt_enable (level);
|
||||
if (rtems_am29lv160_toggle_wait_16 (seg++) != 0)
|
||||
return EIO;
|
||||
size--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Issue a reset.
|
||||
*/
|
||||
*base = 0xf0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rtems_am29lv160_read (const rtems_fdisk_segment_desc* sd,
|
||||
uint32_t device,
|
||||
uint32_t segment,
|
||||
uint32_t offset,
|
||||
void* buffer,
|
||||
uint32_t size)
|
||||
{
|
||||
unsigned char* addr =
|
||||
rtems_am29lv160_configuration[device].base +
|
||||
sd->offset + ((segment - sd->segment) * sd->size) + offset;
|
||||
memcpy (buffer, addr, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* @todo Fix the odd alignment and odd sizes.
|
||||
*/
|
||||
static int
|
||||
rtems_am29lv160_write (const rtems_fdisk_segment_desc* sd,
|
||||
uint32_t device,
|
||||
uint32_t segment,
|
||||
uint32_t offset,
|
||||
const void* buffer,
|
||||
uint32_t size)
|
||||
{
|
||||
int ret = rtems_am29lv160_verify (sd, device, segment, offset, buffer, size);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device];
|
||||
uint32_t soffset;
|
||||
|
||||
soffset = offset + sd->offset + ((segment - sd->segment) * sd->size);
|
||||
|
||||
if (offset & 1)
|
||||
printf ("rtems_am29lv160_write: offset is odd\n");
|
||||
|
||||
if (size & 1)
|
||||
printf ("rtems_am29lv160_write: size is odd\n");
|
||||
|
||||
if (ac->bus_8bit)
|
||||
ret = rtems_am29lv160_write_data_8 (ac->base, soffset, buffer, size);
|
||||
else
|
||||
ret = rtems_am29lv160_write_data_16 (ac->base, soffset, buffer, size);
|
||||
|
||||
/*
|
||||
* Verify the write worked.
|
||||
*/
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = rtems_am29lv160_verify (sd, device, segment, offset, buffer, size);
|
||||
#if AM26LV160_ERROR_TRACE
|
||||
if (ret)
|
||||
printf ("AM26LV160: verify failed: %ld-%ld-%08lx: s=%ld\n",
|
||||
device, segment, offset, size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
rtems_am29lv160_erase (const rtems_fdisk_segment_desc* sd,
|
||||
uint32_t device,
|
||||
uint32_t segment)
|
||||
{
|
||||
int ret = rtems_am29lv160_blank (sd, device, segment, 0, sd->size);
|
||||
if (ret != 0)
|
||||
{
|
||||
const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device];
|
||||
uint32_t offset;
|
||||
rtems_interrupt_level level;
|
||||
|
||||
offset = sd->offset + ((segment - sd->segment) * sd->size);
|
||||
|
||||
if (ac->bus_8bit)
|
||||
{
|
||||
volatile uint8_t* base = ac->base;
|
||||
volatile uint8_t* seg = base + offset;
|
||||
|
||||
/*
|
||||
* Issue a reset.
|
||||
*/
|
||||
rtems_interrupt_disable (level);
|
||||
*base = 0xf0;
|
||||
*(base + 0xaaa) = 0xaa;
|
||||
*(base + 0x555) = 0x55;
|
||||
*(base + 0xaaa) = 0x80;
|
||||
*(base + 0xaaa) = 0xaa;
|
||||
*(base + 0x555) = 0x55;
|
||||
*seg = 0x30;
|
||||
rtems_interrupt_enable (level);
|
||||
|
||||
ret = rtems_am29lv160_toggle_wait_8 (seg);
|
||||
|
||||
/*
|
||||
* Issue a reset.
|
||||
*/
|
||||
*base = 0xf0;
|
||||
}
|
||||
else
|
||||
{
|
||||
volatile uint16_t* base = ac->base;
|
||||
volatile uint16_t* seg = base + (offset / 2);
|
||||
|
||||
/*
|
||||
* Issue a reset.
|
||||
*/
|
||||
rtems_interrupt_disable (level);
|
||||
*base = 0xf0;
|
||||
*(base + 0x555) = 0xaa;
|
||||
*(base + 0x2aa) = 0x55;
|
||||
*(base + 0x555) = 0x80;
|
||||
*(base + 0x555) = 0xaa;
|
||||
*(base + 0x2aa) = 0x55;
|
||||
*seg = 0x30;
|
||||
rtems_interrupt_enable (level);
|
||||
|
||||
ret = rtems_am29lv160_toggle_wait_16 (seg);
|
||||
|
||||
/*
|
||||
* Issue a reset.
|
||||
*/
|
||||
*base = 0xf0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the erase worked.
|
||||
*/
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = rtems_am29lv160_blank (sd, device, segment, 0, sd->size);
|
||||
#if AM26LV160_ERROR_TRACE
|
||||
if (ret)
|
||||
printf ("AM26LV160: erase failed: %ld-%ld\n", device, segment);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
rtems_am29lv160_erase_device (const rtems_fdisk_device_desc* dd,
|
||||
uint32_t device)
|
||||
{
|
||||
uint32_t segment;
|
||||
|
||||
for (segment = 0; segment < dd->segment_count; segment++)
|
||||
{
|
||||
uint32_t seg_segment;
|
||||
|
||||
for (seg_segment = 0;
|
||||
seg_segment < dd->segments[segment].count;
|
||||
seg_segment++)
|
||||
{
|
||||
int ret = rtems_am29lv160_erase (&dd->segments[segment],
|
||||
device,
|
||||
segment + seg_segment);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const rtems_fdisk_driver_handlers rtems_am29lv160_handlers =
|
||||
{
|
||||
.read = rtems_am29lv160_read,
|
||||
.write = rtems_am29lv160_write,
|
||||
.blank = rtems_am29lv160_blank,
|
||||
.verify = rtems_am29lv160_verify,
|
||||
.erase = rtems_am29lv160_erase,
|
||||
.erase_device = rtems_am29lv160_erase_device
|
||||
};
|
||||
Reference in New Issue
Block a user