📄 amd29f040.c
字号:
/*
* File: amd29f040.c
* Purpose: Device driver for the AMD 29F040 Flash.
*
* Notes: This driver supports a single type (AMD29F040) and
* width of FLASH. The width support is done during
* compilation. It can support multiple banks of
* same-width ADM29F040 FLASH.
*
* If different types and width of FLASH are required,
* see driver amd29f0x0.c.
*
* Modifications:
*
*/
#include "src/include/dbug.h"
#include "src/dev/flash/amd29f040.h"
/*
* The AMD29F040 FLASH devices. These devices are 128K x 8 devices.
*
* Note: This driver supports one of the following configurations
* for these devices. Possible configurations are:
*
* 1 AMD29F040 as a single 8-bit memory device
* 2 AMD29F040s as a single 16-bit memory device
* 4 AMD29F040s as a single 32-bit memory device
*
* One of the following C pre-processor define statements must
* exist prior to compilation:
*
* #define FLASH_8BIT_WIDE
* #define FLASH_16BIT_WIDE
* #define FLASH_32BIT_WIDE
*
* IMPORTANT: It is not possible to execute any of these routines
* if the code for these routines is in the FLASH itself. This is
* because these routines change the mode of the device so that reads
* from it are not always possible. The code must be executed out of
* DRAM.
*/
/*
* WARNING: To eliminate playing alot of games with the linker
* to properly treat this PIC driver, the two routines/symbols
* amd29f010_start and amd29f010_end are used to determine the
* start and end of the FLASH PIC driver in ROM/FLASH so that it
* can be copied out to DRAM and then executed. In other words,
* any modifications/additions must be between these two functions!
*/
/*****************************************************************/
void
amd29f040_start (void)
{
}
/*****************************************************************/
void
amd29f040_read (AMD29F040 *amd29f040)
{
/*
* This routine places the FLASH in the read/reset mode.
*/
amd29f040->device.cmdreg1 = AMD29F040_READ_CMD1;
amd29f040->device.cmdreg2 = AMD29F040_READ_CMD2;
amd29f040->device.cmdreg1 = AMD29F040_READ_CMD3;
}
/*****************************************************************/
int
amd29f040_sector_erase (AMD29F040 *amd29f040, AMD29F040_SECTOR sector)
{
/*
* This routine erases one sector in the FLASH.
*/
AMD29F040_CELL *s;
s = (AMD29F040_CELL *)&(amd29f040->sector[sector]);
/*
* Perform the Sector Erase command sequence.
*/
amd29f040->device.cmdreg1 = AMD29F040_SECTORERASE_CMD1;
amd29f040->device.cmdreg2 = AMD29F040_SECTORERASE_CMD2;
amd29f040->device.cmdreg1 = AMD29F040_SECTORERASE_CMD3;
amd29f040->device.cmdreg1 = AMD29F040_SECTORERASE_CMD4;
amd29f040->device.cmdreg2 = AMD29F040_SECTORERASE_CMD5;
*s = AMD29F040_SECTORERASE_CMD6;
/*
* Wait for the device to erase the sector
*/
while ((*s & AMD29F040_SECTORERASE_DONE) != AMD29F040_SECTORERASE_DONE)
;
/*
* Place the device back in the Read/Reset mode.
*/
amd29f040_read (amd29f040);
return 1;
}
/*****************************************************************/
int
amd29f040_id (AMD29F040 *amd29f040)
{
/*
* This routine extracts from the FLASH the manufacturer and
* and device code. If the codes match those for the AMD29F040,
* then 1 is returned, else 0.
*/
unsigned int manuf, device;
/*
* Perform the Autoselect command sequence.
*/
amd29f040->device.cmdreg1 = AMD29F040_AUTOSELECT_CMD1;
amd29f040->device.cmdreg2 = AMD29F040_AUTOSELECT_CMD2;
amd29f040->device.cmdreg1 = AMD29F040_AUTOSELECT_CMD3;
/*
* Read the manufacturer and device id codes.
*/
manuf = amd29f040->device.manuf;
device = amd29f040->device.id;
/*
* Place the device back in the Read/Reset mode.
*/
amd29f040_read (amd29f040);
/*
* Determine if an AMD29F040 or not.
*/
if (device == AMD29F040_DEVICE_CODE)
{
return 1;
}
else
{
return 0;
}
}
/*****************************************************************/
int
amd29f040_write (AMD29F040 *amd29f040,
void *data, unsigned length, AMD29F040_SECTOR sector, void (*func)(void))
{
/*
* This routine programs the device beginning at 'sector' with
* 'data' for 'length' bytes. This routine is capable of
* programming multiple consecutive sectors.
*/
int index, written, done;
AMD29F040_CELL *source;
AMD29F040_CELL *target;
AMD29F040_CELL rdata;
/*
* Step 1) ID the flash as a AMD29F040.
*/
if (!amd29f040_id(amd29f040))
return 0;
/*
* Step 2) Point to the beginning sector.
*/
target = (AMD29F040_CELL *)&(amd29f040->sector[sector]);
source = (AMD29F040_CELL *)data;
done = 0;
written = 0;
while (!done)
{
/*
* Step 3) Erase the sector.
*/
if (!amd29f040_sector_erase (amd29f040, sector))
return -1;
/*
* Step 5) Program the FLASH with the sector(s) contained in RAM.
*/
for (index = 0; (index < AMD29F040_SECTOR_SIZE) && (written < length);
++index)
{
/*
* Perform the Byte Program command sequence.
*/
amd29f040->device.cmdreg1 = AMD29F040_BYTEPROG_CMD1;
amd29f040->device.cmdreg2 = AMD29F040_BYTEPROG_CMD2;
amd29f040->device.cmdreg1 = AMD29F040_BYTEPROG_CMD3;
*target = *source;
/*
* Poll to make sure it is done before continuing...
*/
while (1)
{
int timeouts;
timeouts = 0;
rdata = *(volatile AMD29F040_CELL *)target;
if ((rdata & AMD29F040_DATA_POLL_MASK) ==
(*source & AMD29F040_DATA_POLL_MASK))
{
break;
}
else
{
rdata = *(volatile AMD29F040_CELL *)target;
if (rdata & AMD29F040_TIME_EXCEEDED)
{
rdata = *(volatile AMD29F040_CELL *)target;
if ((rdata & AMD29F040_DATA_POLL_MASK) ==
(*source & AMD29F040_DATA_POLL_MASK))
{
break;
}
if (++timeouts > 1024)
{
amd29f040_read (amd29f040);
break;
}
}
}
}
/*
* Verify write and increment bytes written counter.
*/
amd29f040_read (amd29f040);
if (*target == *source)
{
written += sizeof(AMD29F040_CELL);
++target;
++source;
}
else
{
done = 1;
break;
}
}
if (++sector >= AMD29F040_SECTORS)
done = 1;
if (written >= length)
done = 1;
}
/*
* Step 6) Place the device back in the read mode.
*/
amd29f040_read (amd29f040);
if (func != NULL)
{
func();
}
return written;
}
void
amd29f040_end(void)
{
/*
* This function is empty and at the END of all code so that it
* can be used to compute the size of the code contained in this
* driver.
*/
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -