summaryrefslogtreecommitdiff
path: root/firmware/src
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/src')
-rw-r--r--firmware/src/simtrace/spi_flash.c250
-rw-r--r--firmware/src/simtrace/spi_flash.h19
2 files changed, 251 insertions, 18 deletions
diff --git a/firmware/src/simtrace/spi_flash.c b/firmware/src/simtrace/spi_flash.c
index 98bc369..ffda1b2 100644
--- a/firmware/src/simtrace/spi_flash.c
+++ b/firmware/src/simtrace/spi_flash.c
@@ -1,5 +1,5 @@
-/* Driver for a SST25VF040B spi flash attached to AT91SAM7 SPI
- * (C) 2011 by Harald Welte <hwelte@hmw-consulting.de>
+/* Driver for a SST25VF040B/S25FL032P spi flash attached to AT91SAM7 SPI
+ * (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,11 +30,49 @@
#include <os/dbgu.h>
#include <os/pio_irq.h>
+#include "spi_flash.h"
+
#include "../simtrace.h"
#include "../openpcd.h"
-#define DEBUGPSPI DEBUGP
-//#define DEBUGPSPI(x, y ...) do { } while (0)
+//#define DEBUGPSPI DEBUGP
+#define DEBUGPSPI(x, y ...) do { } while (0)
+
+/* taken from Spansion S25FL032P data sheet */
+
+/* read */
+#define SPIF_CMD_READ 0x03
+#define SPIF_CMD_FAST_READ 0x0B
+#define SPIF_CMD_RDID 0x9F
+#define SPIF_CMD_READ_ID 0x90
+
+/* write control */
+#define SPIF_CMD_WREN 0x06
+#define SPIF_CMD_WRDI 0x04
+
+/* erase */
+#define SPIF_CMD_P4E 0x20 /* 4kB parameter sector erase */
+#define SPIF_CMD_P8E 0x40 /* 4kB parameter sector erase */
+#define SPIF_CMD_SE 0xD8 /* 64kB sector erase */
+#define SPIF_CMD_BE 0x60 /* bulk erase */
+
+/* program */
+#define SPIF_CMD_PP 0x02 /* page program */
+
+/* status & config */
+#define SPIF_CMD_RDSR 0x05 /* read status register */
+#define SPIF_CMD_WRR 0x01 /* write status + cfg register */
+#define SPIF_CMD_RCR 0x35 /* read config register */
+#define SPIF_CMD_CLSR 0x30 /* reset the erase and program fail flag */
+
+/* power saving */
+#define SPIF_CMD_DP 0xB9
+#define SPIF_CMD_RES 0xAB
+
+/* otp */
+#define SPIF_CMD_OTPP 0x42 /* otp program */
+#define SPIF_CMD_OTPR 0x4B /* otp read */
+
static const AT91PS_SPI pSPI = AT91C_BASE_SPI;
@@ -55,6 +93,11 @@ static __ramfunc void spi_irq(void)
AT91F_AIC_ClearIt(AT91C_BASE_AIC, AT91C_ID_SPI);
}
+static const u_int8_t chipid_s25fl032p[3] = { 0x01, 0x02, 0x15 };
+
+static u_int8_t chip_id[3];
+static u_int32_t otp_supported;
+
void spiflash_init(void)
{
DEBUGP("spiflash_init\r\n");
@@ -91,14 +134,20 @@ void spiflash_init(void)
AT91C_SPI_OVRES |
AT91C_SPI_ENDRX |
AT91C_SPI_ENDTX);
+
+ spiflash_get_id(chip_id);
+
+ if (!memcmp(chip_id, chipid_s25fl032p, sizeof(chip_id)))
+ otp_supported = 1;
}
-static int spi_transceive(const u_int8_t *tx_data, u_int16_t tx_len,
- u_int8_t *rx_data, u_int16_t *rx_len)
+static int spi_transceive(const u_int8_t *tx_data, u_int16_t tx_len,
+ u_int8_t *rx_data, u_int16_t *rx_len, u_int16_t rx_skip)
{
u_int16_t tx_cur = 0;
u_int16_t rx_len_max = 0;
u_int16_t rx_cnt = 0;
+ u_int8_t tmp;
DEBUGPSPI("spi_transceive: enter(tx_len=%u) ", tx_len);
@@ -107,38 +156,203 @@ static int spi_transceive(const u_int8_t *tx_data, u_int16_t tx_len,
*rx_len = 0;
}
+ /* make sure we drain any remaining rx data */
+ tmp = pSPI->SPI_RDR;
+
//AT91F_SPI_Enable(pSPI);
- while (1) {
+ while (1) {
u_int32_t sr = pSPI->SPI_SR;
- u_int8_t tmp;
if (sr & AT91C_SPI_RDRF) {
tmp = pSPI->SPI_RDR;
rx_cnt++;
- if (rx_len && *rx_len < rx_len_max)
+ if (rx_cnt > rx_skip &&
+ rx_len && *rx_len < rx_len_max)
rx_data[(*rx_len)++] = tmp;
}
if (sr & AT91C_SPI_TDRE) {
if (tx_len > tx_cur)
pSPI->SPI_TDR = tx_data[tx_cur++];
+ else
+ pSPI->SPI_TDR = 0;
+ }
+ if (rx_len) {
+ if (*rx_len >= rx_len_max)
+ break;
+ } else {
+ if (tx_len <= tx_cur)
+ break;
}
- if (tx_cur >= tx_len && rx_cnt >= tx_len)
- break;
}
+ /* make sure we drain any remaining rx data */
+ tmp = pSPI->SPI_RDR;
+
//AT91F_SPI_Disable(pSPI);
- if (rx_data)
- DEBUGPSPI(" leave(%02x %02x)\r\n", rx_data[0], rx_data[1]);
- else
+ if (rx_data) {
+ int i;
+ DEBUGPSPI("leave(): ");
+ for (i = 0; i < *rx_len; i++)
+ DEBUGPSPI("0x%02x ", rx_data[i]);
+ DEBUGPSPI("\r\n");
+ } else
DEBUGPSPI("leave()\r\n");
return 0;
}
-void spiflash_id(void)
+void spiflash_get_id(u_int8_t *id)
{
- const u_int8_t tx_data[] = { 0x9f, 0, 0, 0 };
+ const u_int8_t tx_data[] = { SPIF_CMD_RDID, 0, 0, 0 };
u_int8_t rx_data[] = { 0,0,0,0 };
u_int16_t rx_len = sizeof(rx_data);
- spi_transceive(tx_data, sizeof(tx_data), rx_data, &rx_len);
- DEBUGP("SPI ID: %02x %02x %02x\n", rx_data[1], rx_data[2], rx_data[3]);
+ spi_transceive(tx_data, sizeof(tx_data), rx_data, &rx_len, 1);
+ DEBUGPSPI("SPI ID: %02x %02x %02x\r\n",
+ rx_data[0], rx_data[1], rx_data[2]);
+ memcpy(id, rx_data, 3);
+}
+
+int spiflash_read_status(void)
+{
+ const u_int8_t tx_data[] = { SPIF_CMD_RDSR, 0 };
+ u_int8_t rx_data[1];
+ u_int16_t rx_len = sizeof(rx_data);
+
+ spi_transceive(tx_data, sizeof(tx_data), rx_data, &rx_len, 1);
+
+ DEBUGPSPI("SPI Flash status: 0x%02x\r\n", rx_data[0]);
+
+ return rx_data[0];
+}
+
+void spiflash_clear_status(void)
+{
+ const u_int8_t tx_data[] = { SPIF_CMD_CLSR };
+
+ spi_transceive(tx_data, sizeof(tx_data), NULL, 0, 0);
+}
+
+int spiflash_write_enable(int enable)
+{
+ u_int8_t tx_data[1];
+
+ if (enable)
+ tx_data[0] = SPIF_CMD_WREN;
+ else
+ tx_data[0] = SPIF_CMD_WRDI;
+
+ spi_transceive(tx_data, sizeof(tx_data), NULL, 0, 0);
+
+ return 0;
+}
+
+int spiflash_otp_read(u_int32_t otp_addr, u_int8_t *out, u_int16_t rx_len)
+{
+ u_int8_t tx_data[] = { SPIF_CMD_OTPR, 0, 0, 0, 0 };
+
+ if (!otp_supported) {
+ DEBUGP("OTP not supported!\r\n");
+ return -1;
+ }
+
+ tx_data[1] = (otp_addr >> 16) & 0xFF;
+ tx_data[2] = (otp_addr >> 8) & 0xFF;
+ tx_data[3] = (otp_addr) & 0xFF;
+
+ spi_transceive(tx_data, sizeof(tx_data), out, &rx_len, 5);
+
+ DEBUGPSPI("OTP READ(0x%x): ", otp_addr);
+ int i;
+ for (i = 0; i < rx_len; i++)
+ DEBUGPSPI("%02x ", out[i]);
+ DEBUGPSPI("\r\n");
+
+ return rx_len;
+}
+
+int spiflash_otp_write(u_int32_t otp_addr, u_int8_t data)
+{
+ u_int8_t tx_data[] = { SPIF_CMD_OTPP, 0, 0, 0, 0 };
+
+ if (!otp_supported) {
+ DEBUGP("OTP not supported!\r\n");
+ return -1;
+ }
+
+ tx_data[1] = (otp_addr >> 16) & 0xFF;
+ tx_data[2] = (otp_addr >> 8) & 0xFF;
+ tx_data[3] = (otp_addr) & 0xFF;
+ tx_data[4] = data;
+
+ spiflash_write_enable(1);
+ spi_transceive(tx_data, sizeof(tx_data), NULL, 0, 0);
+ spiflash_write_enable(0);
+
+ if (spiflash_read_status() & (1 << 6))
+ return -1;
+
+ return 0;
+}
+
+static int otp_region2addr(u_int8_t region)
+{
+ /* see Figure 10.1 of S25FL032P data sheet */
+ if (region > 31 || region < 1)
+ return -1;
+ else if (region > 24)
+ return 0x215;
+ else if (region > 16)
+ return 0x214;
+ else if (region > 8)
+ return 0x113;
+ else
+ return 0x112;
+}
+
+static int otp_region2bit(u_int8_t region)
+{
+ /* see Figure 10.1 of S25FL032P data sheet */
+ if (region > 31 || region < 1)
+ return -1;
+ else if (region > 24)
+ return region - 25;
+ else if (region > 16)
+ return region - 17;
+ else if (region > 8)
+ return region - 9;
+ else
+ return region - 1;
+}
+
+int spiflash_otp_get_lock(u_int8_t region)
+{
+ u_int32_t addr;
+ u_int8_t bit, data;
+
+ if (region > 31 || region < 1)
+ return -1;
+
+ bit = otp_region2bit(region);
+ addr = otp_region2addr(region);
+
+ spiflash_otp_read(addr, &data, 1);
+
+ if (!(data & (1 << bit)))
+ return 1;
+ else
+ return 0;
+}
+
+int spiflash_otp_set_lock(u_int8_t region)
+{
+ u_int32_t addr;
+ u_int8_t bit;
+
+ if (region > 31 || region < 1)
+ return -1;
+
+ bit = otp_region2bit(region);
+ addr = otp_region2addr(region);
+
+ /* clear the respective bit */
+ return spiflash_otp_write(addr, ~(1 << bit));
}
diff --git a/firmware/src/simtrace/spi_flash.h b/firmware/src/simtrace/spi_flash.h
new file mode 100644
index 0000000..d823130
--- /dev/null
+++ b/firmware/src/simtrace/spi_flash.h
@@ -0,0 +1,19 @@
+#ifndef SPI_FLASH_H
+#define SPI_FLASH_H
+
+#include <sys/types.h>
+
+#define OTP_ADDR(x) (0x114 + ( ((x) - 1) * 16 ) )
+
+void spiflash_init(void);
+void spiflash_get_id(u_int8_t *id);
+int spiflash_read_status(void);
+void spiflash_clear_status(void);
+void spiflash_write_protect(int on);
+int spiflash_write_enable(int enable);
+int spiflash_otp_read(u_int32_t otp_addr, u_int8_t *out, u_int16_t rx_len);
+int spiflash_otp_write(u_int32_t otp_addr, u_int8_t data);
+int spiflash_otp_get_lock(u_int8_t region);
+int spiflash_otp_set_lock(u_int8_t region);
+
+#endif
personal git repositories of Harald Welte. Your mileage may vary