276 lines
6.2 KiB
C
276 lines
6.2 KiB
C
#include "gd25d10c.h"
|
|
|
|
#include <string.h>
|
|
|
|
#if (DEF_GD25D10C_ENABLED)
|
|
|
|
#define ERASE_BLOCK_SIZE (32 * 1024)
|
|
#define ERASE_SECT_SIZE 4096
|
|
#define PROG_PAGE_SIZE 256
|
|
#define PAGE_PER_SECT 16
|
|
#define PAGE_PER_BLOCK 128
|
|
#define SECT_PER_BLOCK 8
|
|
#define BLOCK_PER_DEV 4
|
|
|
|
#define CMD_READ 0x03
|
|
#define CMD_WREN 0x06
|
|
#define CMD_WRDI 0x04
|
|
#define CMD_RDSR 0x05
|
|
#define CMD_WRSR 0x01
|
|
#define CMD_PP 0x02
|
|
#define CMD_RDID 0x9F
|
|
#define CMD_SE 0x20
|
|
#define CMD_BE 0x52
|
|
|
|
#define WIP_BIT 0x01
|
|
#define WEL_BIT 0x02
|
|
|
|
#define MANUFACTURER_ID 0xC8
|
|
#define MEMORY_TYPE 0x40
|
|
#define CAPACITY 0x11
|
|
|
|
#define EXPECT_ID ((MANUFACTURER_ID << 16) | (MEMORY_TYPE << 8) | (CAPACITY << 0))
|
|
|
|
uint8_t xfer_buf[4096 + 32];
|
|
|
|
static int read_data(uint8_t *p_dest, uint32_t addr, uint32_t size)
|
|
{
|
|
struct send
|
|
{
|
|
union
|
|
{
|
|
uint32_t val;
|
|
struct
|
|
{
|
|
uint32_t addr : 24;
|
|
uint32_t cmd : 8;
|
|
};
|
|
};
|
|
} *p_tx = (void *)xfer_buf;
|
|
|
|
struct recv
|
|
{
|
|
uint32_t dummy;
|
|
uint8_t recv[0];
|
|
} *p_rx = (void *)xfer_buf;
|
|
|
|
p_tx->cmd = CMD_READ;
|
|
p_tx->addr = addr;
|
|
p_tx->val = __REV(p_tx->val);
|
|
spim_xfer(CS_MEM_PIN, NRF_SPIM_MODE_0, (void *)p_tx, sizeof(*p_tx), (void *)p_rx, offsetof(struct recv, recv) + size);
|
|
memcpy(p_dest, &xfer_buf[sizeof(*p_tx)], size);
|
|
return BLOCK_DEV_SUCCESS;
|
|
}
|
|
|
|
static void write_enable(void)
|
|
{
|
|
uint32_t tx = 0;
|
|
struct send
|
|
{
|
|
uint8_t cmd;
|
|
} *p_tx = (void *)&tx;
|
|
p_tx->cmd = CMD_WREN;
|
|
spim_xfer(CS_MEM_PIN, NRF_SPIM_MODE_0, (void *)p_tx, sizeof(*p_tx), NULL, 0);
|
|
}
|
|
|
|
static void write_disable(void)
|
|
{
|
|
uint32_t tx = 0;
|
|
struct send
|
|
{
|
|
uint8_t cmd;
|
|
} *p_tx = (void *)&tx;
|
|
p_tx->cmd = CMD_WRDI;
|
|
spim_xfer(CS_MEM_PIN, NRF_SPIM_MODE_0, (void *)p_tx, sizeof(*p_tx), NULL, 0);
|
|
}
|
|
|
|
static uint32_t read_status(void)
|
|
{
|
|
uint32_t tx = 0, rx = 0;
|
|
|
|
struct send
|
|
{
|
|
uint8_t cmd;
|
|
uint8_t dummy;
|
|
} *p_tx = (void *)&tx;
|
|
|
|
struct recv
|
|
{
|
|
uint8_t dummy;
|
|
uint8_t status;
|
|
} *p_rx = (void *)℞
|
|
|
|
p_tx->cmd = CMD_RDSR;
|
|
p_tx->dummy = 0xFF;
|
|
spim_xfer(CS_MEM_PIN, NRF_SPIM_MODE_0, (void *)p_tx, sizeof(*p_tx), (void *)p_rx, offsetof(struct recv, status) + sizeof(p_rx->status));
|
|
return p_rx->status;
|
|
}
|
|
|
|
static void write_status(uint8_t stauts)
|
|
{
|
|
uint32_t tx = 0;
|
|
struct send
|
|
{
|
|
uint8_t cmd;
|
|
uint8_t stauts;
|
|
} *p_tx = (void *)&tx;
|
|
p_tx->cmd = CMD_RDSR;
|
|
p_tx->stauts = stauts;
|
|
spim_xfer(CS_MEM_PIN, NRF_SPIM_MODE_0, (void *)p_tx, sizeof(*p_tx), NULL, 0);
|
|
}
|
|
|
|
static int page_prog(uint8_t *p_src, uint32_t addr, uint32_t page_cnt)
|
|
{
|
|
for (uint32_t i = 0; i < page_cnt; i++)
|
|
{
|
|
write_enable();
|
|
|
|
struct send
|
|
{
|
|
union
|
|
{
|
|
uint32_t val;
|
|
struct
|
|
{
|
|
uint32_t addr : 24;
|
|
uint32_t cmd : 8;
|
|
};
|
|
};
|
|
|
|
uint8_t data[256];
|
|
} *p_tx = (void *)xfer_buf;
|
|
|
|
p_tx->cmd = CMD_PP;
|
|
p_tx->addr = addr + PROG_PAGE_SIZE * i;
|
|
p_tx->val = __REV(p_tx->val);
|
|
memcpy(p_tx->data, (void *)(&p_src[PROG_PAGE_SIZE * i]), PROG_PAGE_SIZE);
|
|
|
|
spim_xfer(CS_MEM_PIN, NRF_SPIM_MODE_0, (void *)p_tx, sizeof(*p_tx), NULL, 0);
|
|
|
|
do {
|
|
} while ((read_status() & WIP_BIT));
|
|
}
|
|
return BLOCK_DEV_SUCCESS;
|
|
}
|
|
|
|
int sect_erase(uint32_t addr, uint32_t sect_cnt)
|
|
{
|
|
for (uint32_t i = 0; i < sect_cnt; i++)
|
|
{
|
|
write_enable();
|
|
|
|
struct send
|
|
{
|
|
union
|
|
{
|
|
uint32_t val;
|
|
struct
|
|
{
|
|
uint32_t addr : 24;
|
|
uint32_t cmd : 8;
|
|
};
|
|
};
|
|
} *p_tx = (void *)xfer_buf;
|
|
|
|
p_tx->cmd = CMD_SE;
|
|
p_tx->addr = addr + i * ERASE_SECT_SIZE;
|
|
p_tx->val = __REV(p_tx->val);
|
|
spim_xfer(CS_MEM_PIN, NRF_SPIM_MODE_0, (void *)p_tx, sizeof(*p_tx), NULL, 0);
|
|
|
|
do {
|
|
} while ((read_status() & WIP_BIT));
|
|
}
|
|
return BLOCK_DEV_SUCCESS;
|
|
}
|
|
|
|
int block_erase(uint32_t addr, uint32_t block_cnt)
|
|
{
|
|
|
|
for (uint32_t i = 0; i < block_cnt; i++)
|
|
{
|
|
write_enable();
|
|
|
|
struct send
|
|
{
|
|
union
|
|
{
|
|
uint32_t val;
|
|
struct
|
|
{
|
|
uint32_t addr : 24;
|
|
uint32_t cmd : 8;
|
|
};
|
|
};
|
|
} *p_tx = (void *)xfer_buf;
|
|
|
|
p_tx->cmd = CMD_BE;
|
|
p_tx->addr = addr + i * ERASE_BLOCK_SIZE;
|
|
p_tx->val = __REV(p_tx->val);
|
|
|
|
spim_xfer(CS_MEM_PIN, NRF_SPIM_MODE_0, (void *)p_tx, sizeof(*p_tx), NULL, 0);
|
|
|
|
do {
|
|
} while ((read_status() & WIP_BIT));
|
|
}
|
|
return BLOCK_DEV_SUCCESS;
|
|
}
|
|
|
|
uint32_t dev_id(void)
|
|
{
|
|
uint32_t tx = 0, rx = 0;
|
|
|
|
struct send
|
|
{
|
|
uint32_t cmd : 8;
|
|
uint32_t dummy : 24;
|
|
} *p_tx = (void *)&tx;
|
|
p_tx->cmd = CMD_RDID;
|
|
p_tx->dummy = 0x000000;
|
|
|
|
struct recv
|
|
{
|
|
uint32_t dummy : 8;
|
|
uint32_t manufacturer : 8;
|
|
uint32_t memory_type : 8;
|
|
uint32_t capacity : 8;
|
|
} *p_rx = (void *)℞
|
|
|
|
spim_xfer(CS_MEM_PIN, NRF_SPIM_MODE_0, (void *)p_tx, sizeof(*p_tx), (void *)p_rx, sizeof(*p_rx));
|
|
|
|
return __REV(rx) & 0x00FFFFFF;
|
|
}
|
|
|
|
static int init(void)
|
|
{
|
|
uint32_t id = dev_id();
|
|
|
|
if (EXPECT_ID == id)
|
|
{
|
|
return BLOCK_DEV_SUCCESS;
|
|
}
|
|
|
|
return BLOCK_DEV_ERROR;
|
|
}
|
|
|
|
static int reset(void)
|
|
{
|
|
// Do nothing
|
|
return 0;
|
|
}
|
|
|
|
const block_dev_drv_if_t gd25d110c = {
|
|
.init = init,
|
|
.reset = reset,
|
|
.read_data = read_data,
|
|
.page_prog = page_prog,
|
|
.sect_erase = sect_erase,
|
|
.block_erase = block_erase,
|
|
.block_size = ERASE_BLOCK_SIZE,
|
|
.sect_size = ERASE_SECT_SIZE,
|
|
.sect_count = BLOCK_PER_DEV * SECT_PER_BLOCK,
|
|
.page_size = PROG_PAGE_SIZE,
|
|
.page_per_sect = (ERASE_SECT_SIZE / PROG_PAGE_SIZE)
|
|
};
|
|
|
|
#endif /* ! DEF_GD25D10C_ENABLED */
|