|
|
|
/**
|
|
|
|
MKS Robin Nano
|
|
|
|
U5 W25Q64BV, 16K SERIAL EEPROM:
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
#include "../../inc/MarlinConfig.h"
|
|
|
|
|
|
|
|
#if ENABLED(SPI_EEPROM_W25Q)
|
|
|
|
|
|
|
|
#include "../HAL.h"
|
|
|
|
#include "../../module/mks_wifi/small_cmsis.h"
|
|
|
|
#include "../../module/mks_wifi/dwt.h"
|
|
|
|
|
|
|
|
#define SPI_DIR_READ 0
|
|
|
|
#define SPI_DIR_WRITE 1
|
|
|
|
|
|
|
|
#define W25X_WriteEnable 0x06
|
|
|
|
#define W25X_WriteDisable 0x04
|
|
|
|
#define W25X_ReadStatusReg 0x05
|
|
|
|
#define W25X_WriteStatusReg 0x01
|
|
|
|
#define W25X_ReadData 0x03
|
|
|
|
#define W25X_FastReadData 0x0B
|
|
|
|
#define W25X_PageProgram 0x02
|
|
|
|
#define W25X_BlockErase 0xD8
|
|
|
|
#define W25X_SectorErase 0x20
|
|
|
|
#define W25X_ChipErase 0xC7
|
|
|
|
#define W25X_ReleasePowerDown 0xAB
|
|
|
|
#define W25X_DeviceID 0xAB
|
|
|
|
#define W25X_ManufactDeviceID 0x90
|
|
|
|
#define W25X_JedecDeviceID 0x9F
|
|
|
|
|
|
|
|
#define SPI_EEPROM_SIZE 2048
|
|
|
|
#define SPIFLASH_PAGESIZE 256
|
|
|
|
#define SPI_TIMEOUT 2000 //таймаут на ожидание опереций
|
|
|
|
#define CHECK_TIMEOUT do{if(dwt_get_timeout() == 0){ERROR("Timeout");return 0;}}while(0)
|
|
|
|
|
|
|
|
#define SPI2_START PORTB->BSRR=GPIO_BSRR_BR12
|
|
|
|
#define SPI2_STOP PORTB->BSRR=GPIO_BSRR_BS12
|
|
|
|
|
|
|
|
volatile uint8_t spi_eeprom[SPI_EEPROM_SIZE];
|
|
|
|
|
|
|
|
uint8_t spi_send(uint8_t data);
|
|
|
|
void spi_read(uint32_t addr, uint8_t *buf, uint32_t len);
|
|
|
|
void spi_write(uint32_t addr, uint8_t *buf, uint32_t len);
|
|
|
|
uint8_t spi_read_status(void);
|
|
|
|
|
|
|
|
|
|
|
|
void eeprom_hw_init(void){
|
|
|
|
uint32_t tmp;
|
|
|
|
uint8_t device_id, manuf_id;
|
|
|
|
uint16_t chip_id;
|
|
|
|
|
|
|
|
/*
|
|
|
|
SPI2
|
|
|
|
MISO - PB14 Input floating / Input pull-up
|
|
|
|
MOSI - PB15 Alternate function push-pull
|
|
|
|
SCK - PB13 Alternate function push-pull
|
|
|
|
CS - PB12 Out push-pull
|
|
|
|
*/
|
|
|
|
|
|
|
|
DEBUG("Start SPI");
|
|
|
|
|
|
|
|
dwt_init();
|
|
|
|
|
|
|
|
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN|RCC_APB2ENR_IOPAEN|RCC_APB2ENR_AFIOEN;
|
|
|
|
|
|
|
|
tmp = PORTB->CRH;
|
|
|
|
tmp &= ~(GPIO_CRH_MODE14|GPIO_CRH_CNF14|GPIO_CRH_CNF12|GPIO_CRH_CNF13|GPIO_CRH_CNF14|GPIO_CRH_CNF15);
|
|
|
|
tmp |= (GPIO_CRH_MODE12|GPIO_CRH_MODE13|GPIO_CRH_MODE15|GPIO_CRH_CNF13_1|GPIO_CRH_CNF15_1|GPIO_CRH_CNF14_0);
|
|
|
|
PORTB->CRH = tmp;
|
|
|
|
|
|
|
|
//CS PIN
|
|
|
|
tmp= PORTA->CRL;
|
|
|
|
tmp &= ~GPIO_CRL_CNF7;
|
|
|
|
tmp |= GPIO_CRL_MODE7;
|
|
|
|
PORTA->CRL = tmp;
|
|
|
|
|
|
|
|
PORTA->BSRR = GPIO_BSRR_BS7;
|
|
|
|
|
|
|
|
SPI2_STOP;
|
|
|
|
|
|
|
|
RCC->APB1ENR|= RCC_APB1ENR_SPI2EN;
|
|
|
|
|
|
|
|
SPI2->CR1 = SPI_CR1_SSM|\
|
|
|
|
SPI_CR1_SSI|\
|
|
|
|
(4 << SPI_CR1_BR_Pos)|\
|
|
|
|
SPI_CR1_MSTR;
|
|
|
|
|
|
|
|
SPI2->CR1 |= SPI_CR1_SPE;
|
|
|
|
|
|
|
|
//Wake up
|
|
|
|
SPI2_START;
|
|
|
|
spi_send(W25X_ReleasePowerDown);
|
|
|
|
SPI2_STOP;
|
|
|
|
|
|
|
|
for(uint32_t i=0; i<0x1000; i++){NOP;} //3us для выхода из power down
|
|
|
|
|
|
|
|
//Device ID
|
|
|
|
SPI2_START;
|
|
|
|
spi_send(W25X_DeviceID);
|
|
|
|
for(uint32_t i=0; i<3; i++){spi_send(0);}
|
|
|
|
device_id = spi_send(0);
|
|
|
|
SPI2_STOP;
|
|
|
|
|
|
|
|
//Jedec ID
|
|
|
|
SPI2_START;
|
|
|
|
spi_send(W25X_JedecDeviceID);
|
|
|
|
manuf_id = spi_send(0);
|
|
|
|
chip_id = spi_send(0) << 8;
|
|
|
|
chip_id |= spi_send(0);
|
|
|
|
DEBUG("W25Q Device ID %0X Manuf ID: %0X Chip ID %0X",device_id,manuf_id,chip_id);
|
|
|
|
SPI2_STOP;
|
|
|
|
|
|
|
|
spi_read(SPI_EEPROM_OFFSET,(uint8_t *)spi_eeprom,SPI_EEPROM_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void eeprom_hw_deinit(void){
|
|
|
|
|
|
|
|
DEBUG("Finish SPI");
|
|
|
|
//Write Enable
|
|
|
|
SPI2_START;
|
|
|
|
spi_send(W25X_WriteEnable);
|
|
|
|
SPI2_STOP;
|
|
|
|
|
|
|
|
while (spi_read_status() & 1){ //Busy
|
|
|
|
NOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Erase 4K
|
|
|
|
SPI2_START;
|
|
|
|
spi_send(W25X_SectorErase);
|
|
|
|
spi_send((SPI_EEPROM_OFFSET >> 16) & 0xFF);
|
|
|
|
spi_send((SPI_EEPROM_OFFSET >> 8) & 0xFF);
|
|
|
|
spi_send(SPI_EEPROM_OFFSET & 0xFF);
|
|
|
|
SPI2_STOP;
|
|
|
|
|
|
|
|
while (spi_read_status() & 1){ //Busy
|
|
|
|
NOP;
|
|
|
|
}
|
|
|
|
//write
|
|
|
|
spi_write(SPI_EEPROM_OFFSET,(uint8_t *)spi_eeprom,SPI_EEPROM_SIZE);
|
|
|
|
//deinit spi
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void eeprom_write_byte(uint8_t *pos, unsigned char value){
|
|
|
|
uint16_t addr=(unsigned)pos;
|
|
|
|
|
|
|
|
if(addr < SPI_EEPROM_SIZE){
|
|
|
|
spi_eeprom[addr]=value;
|
|
|
|
}else{
|
|
|
|
ERROR("Write out of SPI size: %d %d",addr,SPI_EEPROM_SIZE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t eeprom_read_byte(uint8_t *pos) {
|
|
|
|
uint16_t addr=(unsigned)pos;
|
|
|
|
|
|
|
|
if(addr < SPI_EEPROM_SIZE){
|
|
|
|
return spi_eeprom[addr];
|
|
|
|
}else{
|
|
|
|
ERROR("Read out of SPI size: %d %d",addr,SPI_EEPROM_SIZE);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void eeprom_read_block(void *__dst, const void *__src, size_t __n){
|
|
|
|
ERROR("Call to missing function");
|
|
|
|
};
|
|
|
|
|
|
|
|
void eeprom_update_block(const void *__src, void *__dst, size_t __n){
|
|
|
|
ERROR("Call to missing function");
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t spi_send(uint8_t data){
|
|
|
|
while((SPI2->SR & SPI_SR_TXE) == 0){NOP;};
|
|
|
|
SPI2->DR = data;
|
|
|
|
|
|
|
|
while((SPI2->SR & SPI_SR_RXNE) == 0){NOP;};
|
|
|
|
return SPI2->DR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void spi_read(uint32_t addr, uint8_t *buf, uint32_t len){
|
|
|
|
|
|
|
|
if( (len == 0) || (len > SPI_EEPROM_SIZE) ){
|
|
|
|
ERROR("Len size error: %d",len);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr &= 0xFFFFFF; //24bit address
|
|
|
|
|
|
|
|
SPI2_START;
|
|
|
|
if(len == 1){
|
|
|
|
spi_send(W25X_ReadData);
|
|
|
|
spi_send((addr >> 16) & 0xFF);
|
|
|
|
spi_send((addr >> 8) & 0xFF);
|
|
|
|
spi_send(addr & 0xFF);
|
|
|
|
}else{
|
|
|
|
spi_send(W25X_FastReadData);
|
|
|
|
spi_send((addr >> 16) & 0xFF);
|
|
|
|
spi_send((addr >> 8) & 0xFF);
|
|
|
|
spi_send(addr & 0xFF);
|
|
|
|
spi_send(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (len--){
|
|
|
|
*buf++ = spi_send(0);
|
|
|
|
}
|
|
|
|
SPI2_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
void spi_write(uint32_t addr, uint8_t *buf, uint32_t len){
|
|
|
|
|
|
|
|
uint16_t bytes_in_page = SPIFLASH_PAGESIZE - (addr % SPIFLASH_PAGESIZE);
|
|
|
|
uint16_t offset = 0;
|
|
|
|
|
|
|
|
addr &= 0xFFFFFF; //24bit address
|
|
|
|
|
|
|
|
while (spi_read_status() & 1){ //Busy
|
|
|
|
NOP;
|
|
|
|
}
|
|
|
|
//Write Enable
|
|
|
|
|
|
|
|
while (len > 0){
|
|
|
|
uint16_t batch_size = (len <= bytes_in_page) ? len : bytes_in_page;
|
|
|
|
|
|
|
|
SPI2_START;
|
|
|
|
spi_send(W25X_WriteEnable);
|
|
|
|
SPI2_STOP;
|
|
|
|
|
|
|
|
while (spi_read_status() & 1){ //Busy
|
|
|
|
NOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
SPI2_START;
|
|
|
|
spi_send(W25X_PageProgram);
|
|
|
|
spi_send((addr >> 16) & 0xFF);
|
|
|
|
spi_send((addr >> 8) & 0xFF);
|
|
|
|
spi_send(addr & 0xFF);
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < batch_size; i++){
|
|
|
|
spi_send(((uint8_t*)buf)[offset + i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
SPI2_STOP;
|
|
|
|
|
|
|
|
//wait till it's programmed
|
|
|
|
while (spi_read_status() & 2){ //Busy
|
|
|
|
NOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr += batch_size;
|
|
|
|
offset += batch_size;
|
|
|
|
len -= batch_size;
|
|
|
|
bytes_in_page = SPIFLASH_PAGESIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t spi_read_status(void){
|
|
|
|
uint8_t data;
|
|
|
|
|
|
|
|
SPI2_START;
|
|
|
|
spi_send(W25X_ReadStatusReg);
|
|
|
|
data = spi_send(0);
|
|
|
|
SPI2_STOP;
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif // SPI_EEPROM_W25Q
|