/**
* @file flash.c
* Provides functionality for using an external SPI flash
*
* Copyright (C) 2018 Clyne Sullivan
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#define READ 0x03
#define WRITE 0x02
#define WREN 0x06
#define WRDS 0x04
#define SCK GPIO_PORT(D, 2)
#define SI GPIO_PORT(C, 3)
#define SO GPIO_PORT(C, 2)
#define CS GPIO_PORT(B, 7)
void flash_spi_xchg(char *buf, unsigned int count)
{
gpio_dout(CS, 0);
// for each byte
for (unsigned int i = 0; i < count; i++) {
for (int b = 7; b >= 0; b--) {
gpio_dout(SI, buf[i] & (1 << b));
gpio_dout(SCK, 1);
if (gpio_din(SO))
buf[i] |= 1 << b;
else
buf[i] &= ~(1 << b);
gpio_dout(SCK, 0);
}
}
gpio_dout(CS, 1);
}
void flash_init(void)
{
gpio_mode(SCK, OUTPUT);
gpio_mode(SI, OUTPUT);
gpio_mode(CS, OUTPUT);
gpio_mode(SO, OUTPUT);
gpio_speed(SCK, VERYHIGH);
gpio_speed(SI, VERYHIGH);
gpio_speed(SO, VERYHIGH);
gpio_speed(CS, VERYHIGH);
gpio_dout(SO, 0);
gpio_mode(SO, INPUT);
gpio_dout(CS, 1);
gpio_dout(SCK, 0);
gpio_dout(SI, 0);
}
void flash_read(char *buf, uint32_t addr, unsigned int count)
{
if (buf == 0)
return;
char *spibuf = malloc(1028);
unsigned int c = count / 1024;
unsigned int i = 0;
uint32_t paddr = addr;
for (i = 0; i < c; i++, paddr += 1024) {
spibuf[0] = READ;
spibuf[1] = (paddr >> 16) & 0xFF;
spibuf[2] = (paddr >> 8) & 0xFF;
spibuf[3] = paddr & 0xFF;
//memcpy(spibuf + 4, buf, count);
flash_spi_xchg(spibuf, 1028);
memcpy(buf + i * 1024, spibuf + 4, 1024);
}
c = count - i * 1024;
spibuf[0] = READ;
spibuf[1] = (paddr >> 16) & 0xFF;
spibuf[2] = (paddr >> 8) & 0xFF;
spibuf[3] = paddr & 0xFF;
flash_spi_xchg(spibuf, 4 + c);
memcpy(buf + i * 1024, spibuf + 4, c);
free(spibuf);
}
void flash_small_write(char *buf, uint32_t addr, unsigned int count)
{
char wren = WREN;
flash_spi_xchg(&wren, 1);
delay(10);
buf[0] = WRITE;
buf[1] = (addr >> 16) & 0xFF;
buf[2] = (addr >> 8) & 0xFF;
buf[3] = addr & 0xFF;
flash_spi_xchg(buf, count);
delay(10);
}
void flash_write(const char *buf, uint32_t addr, unsigned int count)
{
if (buf == 0)
return;
char *spibuf = malloc(260); // 4 header + 256 page size
uint32_t startaddr = addr;
uint16_t counter = 0;
unsigned int offset = 0;
for (uint32_t i = 0; i < count; i++) {
if (i > 0 && ((addr + i) & 0xFF) == 0) {
memcpy(spibuf + 4, buf + offset, counter);
flash_small_write(spibuf, startaddr, 4 + counter);
offset += counter;
startaddr += counter;
counter = 0;
}
counter++;
}
if (offset < count) {
memcpy(spibuf + 4, buf + offset, count - offset);
flash_small_write(spibuf, startaddr, 4 + count - offset);
}
free(spibuf);
}