view spiflash.c @ 89:fc21fb5b171b default tip

Make error message more useful
author Daniel O'Connor <darius@dons.net.au>
date Fri, 13 Mar 2015 11:39:59 +1030
parents 05ba84c7da97
children
line wrap: on
line source

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

#include "stm32f10x.h"
#include "spi.h"
#include "spiflash.h"

#define FL_SELECT()	GPIO_ResetBits(GPIOA, GPIO_Pin_4)
#define FL_DESELECT()	GPIO_SetBits(GPIOA, GPIO_Pin_4)

const char *flstattbl[] = {
    "BUSY",
    "WEL",
    "BP0",
    "BP1",
    "BP2",
    "BP3",
    "AAI",
    "BPL"
};

#define RW_IDLE		0
#define RW_RUNNING	1

static int writestate = RW_IDLE;
static int readstate = RW_IDLE;

void
spiflash4kerase(uint32_t addr) {
    spiflashenablewrite();		/* Enable writing */

    FL_SELECT();			/* Select device */

    SPI_WriteByte(FL_4KERASE);		/* Send command */
    SPI_WriteByte(addr >> 16);	 	/* Send address */
    SPI_WriteByte(addr >> 8);
    SPI_WriteByte(addr);

    FL_DESELECT();

    //fputs("4k erase ", stdout);
    spiflashwait();
}

void
spiflashwait(void) {
    uint8_t	cnt;

    /* Wait for not BUSY */
    for (cnt = 0; (spiflashreadstatus() & FL_BUSY) != 0; cnt++)
	;

    //printf("cnt = %d\n", cnt);
}

uint16_t
spiflashreadid(void) {
    uint8_t	fac, dev;

    FL_SELECT();			/* Select device */

    SPI_WriteByte(FL_RDID);		/* Send command */
    SPI_WriteByte(0x00);		/* Send address cycles (ID data starts at 0) */
    SPI_WriteByte(0x00);
    SPI_WriteByte(0x00);
    fac = SPI_WriteByte(0x00);		/* Read ID */
    dev = SPI_WriteByte(0x00);

    FL_DESELECT();			/* De-select device */

    return fac << 8 | dev;
}

void
spiflashenablewrite(void) {
    FL_SELECT();			/* Select device */

    SPI_WriteByte(FL_WREN);		/* Send command */

    FL_DESELECT();			/* De-select device */
}

uint8_t
spiflashreadstatus(void) {
    uint8_t	status;

    FL_SELECT();			/* Select device */

    SPI_WriteByte(FL_RDSR);		/* Send command */
    SPI_WriteByte(0x00);		/* Send dummy byte for address cycle */
    status = SPI_WriteByte(0x00);	/* Read status */

    FL_DESELECT();			/* De-select device */

    return status;
}

void
spiflashwritestatus(uint8_t status) {
    /* Enable status write */
    FL_SELECT();			/* Select device */
    SPI_WriteByte(FL_EWSR);		/* Send command */
    SPI_WriteByte(0x00);		/* Send data byte */
    FL_DESELECT();

    /* Actually write status */
    FL_SELECT();			/* Re-select device for new command */
    SPI_WriteByte(FL_WRSR);		/* Send command */
    SPI_WriteByte(status);		/* Send data byte */
    FL_DESELECT();			/* De-select device */
}

uint8_t
spiflashread(uint32_t addr) {
    uint8_t	data;

    FL_SELECT();			/* Select device */

    SPI_WriteByte(FL_READ);		/* Send command */
    SPI_WriteByte(addr >> 16);		/* Send address */
    SPI_WriteByte(addr >> 8);
    SPI_WriteByte(addr);
    data = SPI_WriteByte(0x00);		/* Read data */

    FL_DESELECT();			/* De-select device */

    return data;
}

void
spiflashwrite(uint32_t addr, uint8_t data) {
    spiflashwait();
    spiflashenablewrite();			/* Enable writes */

    FL_SELECT();			/* Select device */

    SPI_WriteByte(FL_BYTEPROG);		/* Send command */
    SPI_WriteByte(addr >> 16);		/* Send address */
    SPI_WriteByte(addr >> 8);
    SPI_WriteByte(addr);
    SPI_WriteByte(data);		/* Write data */

    FL_DESELECT();			/* De-select device */

}

/*
 * fStream reading looks like so
 *
 */

void
spiflashstartread(uint32_t addr) {
    assert(readstate == RW_IDLE);

    FL_SELECT();			/* Select device */

    SPI_WriteByte(FL_READ);		/* Send command */
    SPI_WriteByte(addr >> 16); 		/* Send address */
    SPI_WriteByte(addr >> 8);
    SPI_WriteByte(addr);

    readstate = RW_RUNNING;
}

uint8_t
spiflashreadbyte(void) {
    assert(readstate == RW_RUNNING);
    return SPI_WriteByte(0x00);		/* Read data */
}

void
spiflashstopread(void) {
    assert(readstate == RW_RUNNING);

    FL_DESELECT();

    readstate = RW_IDLE;
}

/*
 * Auto increment writing looks like so
 *
 * Enable writing  				CS, WREN, nCS
 * Send start address & first data word		CS, AAI + addr + data, nCS
 * Send subsequent words			wait for nBUSY, CS, AAI + data, nCS
 * ...
 * Disable writing				CS, WRDI, nCS
 *
 * XXX: EBSY command links SO to flash busy state, I don't think the
 * STM32 could sample it without switching out of SPI mode.
 */
void
spiflashstartwrite(uint32_t addr, uint16_t data) {
    assert(writestate == RW_IDLE);

    spiflashenablewrite();		/* Enable writes */

    FL_SELECT();			/* Select device */

    SPI_WriteByte(FL_AAIWP);		/* Send command */
    SPI_WriteByte(addr >> 16);
    SPI_WriteByte(addr >> 8);
    SPI_WriteByte(addr & 0xff); 	/* Send address */

    SPI_WriteByte(data & 0xff);		/* Write LSB */
    SPI_WriteByte(data >> 8);		/* Write MSB */

    FL_DESELECT();

    writestate = RW_RUNNING;
}

void
spiflashwriteword(uint16_t data) {
    assert(writestate == RW_RUNNING);

    //fputs("write word ", stdout);
    spiflashwait();			/* Wait until not busy */

    FL_SELECT();			/* Select device */

    SPI_WriteByte(FL_AAIWP);		/* Send command */
    SPI_WriteByte(data & 0xff);		/* Write LSB */
    SPI_WriteByte(data >> 8);		/* Write MSB */

    FL_DESELECT();			/* De-select device */
}

void
spiflashstopwrite(void) {
    assert(writestate == RW_RUNNING);

    //fputs("flash stop write start ", stdout);
    spiflashwait();			/* Wait until not busy */

    FL_SELECT();			/* Select device */

    SPI_WriteByte(FL_WRDI);		/* Send command */

    FL_DESELECT();			/* Deselect device */

    //fputs("flash stop write end ", stdout);
    spiflashwait();			/* Wait until not busy */

    writestate = RW_IDLE;
}

int
spiflashreadblock(uint32_t addr, uint32_t len, void *_data) {
    uint32_t	*data = _data;
    uint32_t	flashcrc, ramcrc, tmp;

    /* Must be a multiple of 4 due to CRC check */
    assert(len % 4 == 0);

    spiflashstartread(addr);
    CRC_ResetDR();
    for (int i = len; i > 0; i -= 4) {
	tmp = spiflashreadbyte() |
	  spiflashreadbyte() << 8 |
	  spiflashreadbyte() << 16 |
	  spiflashreadbyte() << 24;

	*data = tmp;
	CRC_CalcCRC(tmp);
	data++;
    }

    flashcrc = spiflashreadbyte();
    flashcrc |= spiflashreadbyte() << 8;
    flashcrc |= spiflashreadbyte() << 16;
    flashcrc |= spiflashreadbyte() << 24;

    spiflashstopread();

    ramcrc = CRC_GetCRC();

    //printf("RAM CRC 0x%08x Flash CRC 0x%08x\n", (uint)ramcrc, (uint)flashcrc);
    if (ramcrc == flashcrc)
	return 1;
    else
	return 0;
}

uint32_t
spiflashcrcblock(uint32_t addr, uint32_t len) {
    uint32_t tmp;

    assert(len % 4 == 0);

    CRC_ResetDR();

    spiflashstartread(addr);
    for (; len > 0; len -= 4) {
	tmp = spiflashreadbyte() |
	  spiflashreadbyte() << 8 |
	  spiflashreadbyte() << 16 |
	  spiflashreadbyte() << 24;
	CRC_CalcCRC(tmp);
    }
    spiflashstopread();

    return CRC_GetCRC();
}

int
spiflashwriteblock(uint32_t addr, uint32_t len, void *_data) {
    uint16_t	*data = _data;
    uint32_t	crc, vcrc, tmp;

    //printf("Writing %u bytes to 0x%06x\n", (uint)len, (uint)addr);

    /* Ensure data is
     * - 16 bit aligned
     * - a multiple of 32 bits in length (for CRCs, the flash only need 16 bits)
     * - not longer than a sector
     */
    assert(addr % 2 == 0);
    assert(len % 4 == 0);
    assert(len <= 4096);

    /* Disable write protect */
    spiflashwritestatus(0x00);

    /* Erase sector */
    spiflash4kerase(addr);

    /* Write data */
    CRC_ResetDR();
    for (uint i = 0; i < len / 2; i++) {
	//printf("0x%04x: %04x\n", i, *data);
	if (i == 0)
	    spiflashstartwrite(addr, *data);
	else
	    spiflashwriteword(*data);

	if (i % 2 == 0)
	    tmp = *data;
	else {
	    tmp |= *data << 16;
	    CRC_CalcCRC(tmp);
	}
	data++;
    }

    /* Calculate CRC */
    crc = CRC_GetCRC();
    //printf("CRC is 0x%08x\n", (uint)crc);

    /* Write CRC */
    spiflashwriteword(crc);
    spiflashwriteword(crc >> 16);

    spiflashstopwrite();

    /* Read back and check CRC */
    vcrc = spiflashcrcblock(addr, len);
    //printf("CRC read back as 0x%08x\n", (uint)vcrc);
    if (vcrc != crc)
	return 1;
    else
	return 0;
}

void
spiflashprintstatus(uint8_t status, FILE *out) {
    for (unsigned int i = 0; i < sizeof(flstattbl) / sizeof(flstattbl[0]); i++)
	if (status & 1 << i) {
	    fputs(flstattbl[i], out);
	    fputs(" ", out);
	}
}