From 369f5eef4a04fc3bd1fb5b7d59e7c7e17fdb1d42 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Wed, 13 Feb 2013 18:25:56 -0700 Subject: [PATCH] started a rudimentary driver for writing to SPI flash --- firmware/common/hackrf_core.h | 14 +++++ firmware/common/w25q80bv.c | 100 ++++++++++++++++++++++++++++++++++ firmware/common/w25q80bv.h | 9 +++ 3 files changed, 123 insertions(+) create mode 100644 firmware/common/w25q80bv.c create mode 100644 firmware/common/w25q80bv.h diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index ad84648e..dd6b20a7 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -134,6 +134,14 @@ extern "C" #define RF_LDO_ENABLE (P5_0) /* GPIO2[9] on P5_0 */ #endif +/* SPI flash */ +#define SCU_SSP0_MISO (P3_6) +#define SCU_SSP0_MOSI (P3_7) +#define SCU_SSP0_SCK (P3_3) +#define SCU_SSP0_SSEL (P3_8) /* GPIO5[11] on P3_8 */ +#define SCU_FLASH_HOLD (P3_4) /* GPIO1[14] on P3_4 */ +#define SCU_FLASH_WP (P3_5) /* GPIO1[15] on P3_5 */ + /* TODO add other Pins */ /* @@ -197,6 +205,12 @@ extern "C" #define PORT_RF_LDO_ENABLE (GPIO2) /* PORT for RF_LDO_ENABLE */ #endif +#define PIN_FLASH_HOLD (BIT14) /* GPIO1[14] on P3_4 */ +#define PIN_FLASH_WP (BIT15) /* GPIO1[15] on P3_5 */ +#define PORT_FLASH (GPIO1) +#define PIN_SSP0_SSEL (BIT11) /* GPIO5[11] on P3_8 */ +#define PORT_SSP0_SSEL (GPIO5) + /* GPIO Input */ #define PIN_BOOT0 (BIT8) /* GPIO0[8] on P1_1 */ #define PIN_BOOT1 (BIT9) /* GPIO0[9] on P1_2 */ diff --git a/firmware/common/w25q80bv.c b/firmware/common/w25q80bv.c new file mode 100644 index 00000000..21a3f621 --- /dev/null +++ b/firmware/common/w25q80bv.c @@ -0,0 +1,100 @@ +/* + * This is a rudimentary driver for the W25Q80BV SPI Flash IC using the + * LPC43xx's SSP0 peripheral (not quad SPIFI). The only goal here is to allow + * programming the flash. + */ + +#include +#include +#include "w25q80bv.h" +#include "hackrf_core.h" +#include +#include +#include + +/* + * Set up pins for GPIO and SPI control, configure SSP0 peripheral for SPI. + * SSP0_SSEL is controlled by GPIO in order to handle various transfer lengths. + */ + +void w25q80bv_setup(void) +{ + /* FIXME speed up once everything is working reliably */ + const uint8_t serial_clock_rate = 32; + const uint8_t clock_prescale_rate = 128; + + /* configure SSP pins */ + scu_pinmux(SCU_SSP0_MISO, (SCU_SSP_IO | SCU_CONF_FUNCTION5)); + scu_pinmux(SCU_SSP0_MOSI, (SCU_SSP_IO | SCU_CONF_FUNCTION5)); + scu_pinmux(SCU_SSP0_SCK, (SCU_SSP_IO | SCU_CONF_FUNCTION2)); + + /* configure GPIO pins */ + scu_pinmux(SCU_FLASH_HOLD, SCU_GPIO_FAST); + scu_pinmux(SCU_FLASH_WP, SCU_GPIO_FAST); + scu_pinmux(SCU_SSP0_SSEL, (SCU_GPIO_FAST | SCU_CONF_FUNCTION4)); + + /* drive SSEL, HOLD, and WP pins high */ + gpio_set(PORT_FLASH, (PIN_FLASH_HOLD | PIN_FLASH_WP)); + gpio_set(PORT_SSP0_SSEL, PIN_SSP0_SSEL); + + /* Set GPIO pins as outputs. */ + GPIO1_DIR |= (PIN_FLASH_HOLD | PIN_FLASH_WP); + GPIO5_DIR |= PIN_SSP0_SSEL; + + /* initialize SSP0 */ + ssp_init(SSP0_NUM, + SSP_DATA_8BITS, + SSP_FRAME_SPI, + SSP_CPOL_0_CPHA_0, + serial_clock_rate, + clock_prescale_rate, + SSP_MODE_NORMAL, + SSP_MASTER, + SSP_SLAVE_OUT_ENABLE); +} + +uint8_t w25q80bv_get_status(void) { + uint8_t value; + + gpio_clear(PORT_SSP0_SSEL, PIN_SSP0_SSEL); + ssp_transfer(SSP0_NUM, W25Q80BV_READ_STATUS1); + value = ssp_transfer(SSP0_NUM, 0xFF); + gpio_set(PORT_SSP0_SSEL, PIN_SSP0_SSEL); + + return value; +} + +void w25q80bv_wait_while_busy(void) { + while (w25q80bv_get_status() & W25Q80BV_STATUS_BUSY); +} + +void w25q80bv_chip_erase(void) { + w25q80bv_wait_while_busy(); + gpio_clear(PORT_SSP0_SSEL, PIN_SSP0_SSEL); + ssp_transfer(SSP0_NUM, W25Q80BV_CHIP_ERASE); + gpio_set(PORT_SSP0_SSEL, PIN_SSP0_SSEL); +} + +void w25q80bv_write_enable(void) { + w25q80bv_wait_while_busy(); + gpio_clear(PORT_SSP0_SSEL, PIN_SSP0_SSEL); + ssp_transfer(SSP0_NUM, W25Q80BV_WRITE_ENABLE); + gpio_set(PORT_SSP0_SSEL, PIN_SSP0_SSEL); +} + +/* write a 256 byte page */ +void w25q80bv_page_program(void) { + int i; + + w25q80bv_write_enable(); + w25q80bv_wait_while_busy(); + + gpio_clear(PORT_SSP0_SSEL, PIN_SSP0_SSEL); + ssp_transfer(SSP0_NUM, W25Q80BV_PAGE_PROGRAM); + ssp_transfer(SSP0_NUM, 0x00); //FIXME real address high byte + ssp_transfer(SSP0_NUM, 0x00); //FIXME real address middle byte + ssp_transfer(SSP0_NUM, 0x00); //FIXME real address low byte + for (i = 0; i < 256; i++) + ssp_transfer(SSP0_NUM, i); //FIXME real data + gpio_set(PORT_SSP0_SSEL, PIN_SSP0_SSEL); +} diff --git a/firmware/common/w25q80bv.h b/firmware/common/w25q80bv.h new file mode 100644 index 00000000..7241037f --- /dev/null +++ b/firmware/common/w25q80bv.h @@ -0,0 +1,9 @@ +#define W25Q80BV_WRITE_ENABLE 0x06 +#define W25Q80BV_CHIP_ERASE 0xC7 +#define W25Q80BV_READ_STATUS1 0x05 +#define W25Q80BV_PAGE_PROGRAM 0x02 + +#define W25Q80BV_STATUS_BUSY 0x01 + +void w25q80bv_setup(void); +void w25q80bv_page_program(void);