add class to handle spiflash through jtag. Currently tested with numonyx n25q128
This commit is contained in:
parent
4bc6a304d4
commit
b12dad612b
|
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "ftdijtag.hpp"
|
||||
#include "ftdipp_mpsse.hpp"
|
||||
#include "progressBar.hpp"
|
||||
#include "spiFlash.hpp"
|
||||
|
||||
#define USER1 0x02
|
||||
|
||||
static uint8_t reverseByte(uint8_t src)
|
||||
{
|
||||
uint8_t dst = 0;
|
||||
for (int i=0; i < 8; i++) {
|
||||
dst = (dst << 1) | (src & 0x01);
|
||||
src >>= 1;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
/* read/write status register : 0B addr + 0 dummy */
|
||||
#define FLASH_WRSR 0x01
|
||||
#define FLASH_RDSR 0x05
|
||||
# define FLASH_RDSR_WIP (0x01)
|
||||
# define FLASH_RDSR_WEL (0x02)
|
||||
/* flash program */
|
||||
#define FLASH_PP 0x02
|
||||
/* write [en|dis]able : 0B addr + 0 dummy */
|
||||
#define FLASH_WRDIS 0x04
|
||||
#define FLASH_WREN 0x06
|
||||
/* Read OTP : 3 B addr + 8 clk cycle*/
|
||||
#define FLASH_ROTP 0x4B
|
||||
#define FLASH_POWER_UP 0xAB
|
||||
#define FLASH_POWER_DOWN 0xB9
|
||||
/* read/write non volatile register: 0B addr + 0 dummy */
|
||||
#define FLASH_RDNVCR 0xB5
|
||||
#define FLASH_WRNVCR 0x81
|
||||
/* bulk erase */
|
||||
#define FLASH_BE 0xC7
|
||||
/* sector (64kb) erase */
|
||||
#define FLASH_SE 0xD8
|
||||
/* read/write lock register : 3B addr + 0 dummy */
|
||||
#define FLASH_WRLR 0xE5
|
||||
#define FLASH_RDLR 0xE8
|
||||
/* read/clear flag status register : 0B addr + 0 dummy */
|
||||
#define FLASH_CLFSR 0x50
|
||||
#define FLASH_RFSR 0x70
|
||||
/* */
|
||||
#define FLASH_WRVCR 0x81
|
||||
#define FLASH_RDVCR 0x85
|
||||
/* */
|
||||
#define FLASH_WRVECR 0x61
|
||||
#define FLASH_RDVECR 0x65
|
||||
|
||||
SPIFlash::SPIFlash(FtdiJtag *jtag):_jtag(jtag) {}
|
||||
|
||||
/*
|
||||
* jtag : jtag interface
|
||||
* cmd : opcode for SPI flash
|
||||
* tx : buffer to send
|
||||
* rx : buffer to fill
|
||||
* len : number of byte to send/receive (cmd not comprise)
|
||||
* so to send only a cmd set len to 0 (or omit this param)
|
||||
*/
|
||||
|
||||
void SPIFlash::jtag_write_read(uint8_t cmd,
|
||||
uint8_t *tx, uint8_t *rx, uint16_t len)
|
||||
{
|
||||
int xfer_len = len + 1 + ((rx == NULL) ? 0 : 1);
|
||||
uint8_t jtx[xfer_len] = {reverseByte(cmd)};
|
||||
uint8_t jrx[xfer_len];
|
||||
if (tx != NULL) {
|
||||
for (int i=0; i < len; i++)
|
||||
jtx[i+1] = reverseByte(tx[i]);
|
||||
}
|
||||
/* addr BSCAN user1 */
|
||||
_jtag->shiftIR(USER1, 6);
|
||||
/* send first already stored cmd,
|
||||
* in the same time store each byte
|
||||
* to send next
|
||||
*/
|
||||
_jtag->shiftDR(jtx, (rx == NULL)? NULL: jrx, 8*xfer_len);
|
||||
|
||||
if (rx != NULL) {
|
||||
for (int i=0; i < len; i++)
|
||||
rx[i] = reverseByte(jrx[i+1] >> 1) | (jrx[i+2] & 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
int SPIFlash::wait(uint8_t mask, uint8_t cond, uint32_t timeout, bool verbose)
|
||||
{
|
||||
uint8_t rx[2];
|
||||
uint8_t tmp;
|
||||
uint8_t tx = reverseByte(FLASH_RDSR);
|
||||
uint32_t count = 0;
|
||||
|
||||
_jtag->shiftIR(USER1, 6, FtdiJtag::UPDATE_IR);
|
||||
_jtag->set_state(FtdiJtag::SHIFT_DR);
|
||||
_jtag->read_write(&tx, NULL, 8, 0);
|
||||
|
||||
do {
|
||||
_jtag->read_write(NULL, rx, 8*2, 0);
|
||||
tmp = (reverseByte(rx[0]>>1)) | (0x01 & rx[1]);
|
||||
count ++;
|
||||
if (count == timeout){
|
||||
printf("timeout: %x %x %x\n", tmp, rx[0], rx[1]);
|
||||
break;
|
||||
}
|
||||
if (tmp & ~0x3) {
|
||||
printf("Error: rx %x %x %x\n", tmp, reverseByte(rx[0]), rx[1]);
|
||||
count = timeout;
|
||||
break;
|
||||
}
|
||||
if (verbose) {
|
||||
printf("%x %x %x %d\n", tmp, mask, cond, count);
|
||||
}
|
||||
} while ((tmp & mask) != cond);
|
||||
_jtag->go_test_logic_reset();
|
||||
|
||||
if (count == timeout) {
|
||||
printf("%x\n", tmp);
|
||||
std::cout << "wait: Error" << std::endl;
|
||||
return -1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIFlash::bulk_erase()
|
||||
{
|
||||
if (write_enable() == -1)
|
||||
return -1;
|
||||
jtag_write_read(FLASH_BE, NULL, NULL, 0);
|
||||
return wait(FLASH_RDSR_WIP, 0x00, 100000, true);
|
||||
}
|
||||
|
||||
int SPIFlash::sector_erase(int addr)
|
||||
{
|
||||
uint8_t tx[3] = {(uint8_t)(0xff & (addr >> 16)),
|
||||
(uint8_t)(0xff & (addr >> 8)),
|
||||
(uint8_t)(addr & 0xff)};
|
||||
jtag_write_read(FLASH_SE, tx, NULL, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIFlash::sectors_erase(int base_addr, int size)
|
||||
{
|
||||
int start_addr = base_addr;
|
||||
int end_addr = (size + 0xffff) & ~0xffff;
|
||||
ProgressBar progress("Erasing", end_addr, 50);
|
||||
for (int addr = start_addr; addr < end_addr; addr += 0x10000) {
|
||||
if (write_enable() == -1)
|
||||
return -1;
|
||||
if (sector_erase(addr) == -1)
|
||||
return -1;
|
||||
if (wait(FLASH_RDSR_WIP, 0x00, 100000, false) == -1)
|
||||
return -1;
|
||||
progress.display(addr);
|
||||
}
|
||||
progress.done();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIFlash::write_page(int addr, uint8_t *data, int len)
|
||||
{
|
||||
uint8_t tx[len+3] = {(uint8_t)(0xff & (addr >> 16)),
|
||||
(uint8_t)(0xff & (addr >> 8)),
|
||||
(uint8_t)(addr & 0xff)};
|
||||
for (int i=0; i < len; i++) {
|
||||
tx[i+3] = data[i];
|
||||
}
|
||||
if (write_enable() == -1)
|
||||
return -1;
|
||||
|
||||
jtag_write_read(FLASH_PP, tx, NULL, len+3);
|
||||
return wait(FLASH_RDSR_WIP, 0x00, 1000);
|
||||
}
|
||||
|
||||
int SPIFlash::erase_and_prog(int base_addr, uint8_t *data, int len)
|
||||
{
|
||||
ProgressBar progress("Writing", len, 50);
|
||||
if (sectors_erase(0, len) == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t *ptr = data;
|
||||
int size = 0;
|
||||
for (int addr = base_addr; addr < len; addr += size, ptr+=size) {
|
||||
size = (addr + 256 > len)?(len-addr) : 256;
|
||||
if (write_page(addr, ptr, size) == -1)
|
||||
return -1;
|
||||
progress.display(addr);
|
||||
}
|
||||
progress.done();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SPIFlash::reset()
|
||||
{
|
||||
uint8_t data[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||
jtag_write_read(0xff, data, NULL, 8);
|
||||
}
|
||||
|
||||
void SPIFlash::read_id()
|
||||
{
|
||||
int len = 4;
|
||||
uint8_t rx[512];
|
||||
|
||||
jtag_write_read(0x9F, NULL, rx, 4);
|
||||
int d = 0;
|
||||
for (int i=0; i < 4; i++) {
|
||||
d = d << 8;
|
||||
d |= (0x00ff & (int)rx[i]);
|
||||
printf("%x ", rx[i]);
|
||||
}
|
||||
|
||||
printf("read %x\n", d);
|
||||
/* read extented */
|
||||
len += (d & 0x0ff);
|
||||
|
||||
jtag_write_read(0x9F, NULL, rx, len);
|
||||
|
||||
/* must be 0x20BA1810 ... */
|
||||
|
||||
printf("Detail: \n");
|
||||
printf("Jedec ID : %02x\n", rx[0]);
|
||||
printf("memory type : %02x\n", rx[1]);
|
||||
printf("memory capacity : %02x\n", rx[2]);
|
||||
printf("EDID + CFD length : %02x\n", rx[3]);
|
||||
printf("EDID : %02x%02x\n", rx[5], rx[4]);
|
||||
printf("CFD : ");
|
||||
for (int i = 6; i < len; i++)
|
||||
printf("%02x ", rx[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
uint8_t SPIFlash::read_status_reg(bool display)
|
||||
{
|
||||
uint8_t rx;
|
||||
jtag_write_read(FLASH_RDSR, NULL, &rx, 1);
|
||||
if (display) {
|
||||
printf("RDSR : %02x\n", rx);
|
||||
printf("WIP : %d\n", rx&0x01);
|
||||
printf("WEL : %d\n", (rx>>1)&0x01);
|
||||
printf("BP : %x\n", (((rx>>6)&0x01)<<3) | ((rx >> 2) & 0x07));
|
||||
printf("TB : %d\n", (((rx>>5)&0x01)));
|
||||
printf("SRWD : %d\n", (((rx>>7)&0x01)));
|
||||
}
|
||||
return rx;
|
||||
}
|
||||
|
||||
void SPIFlash::power_up()
|
||||
{
|
||||
jtag_write_read(FLASH_POWER_UP, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
void SPIFlash::power_down()
|
||||
{
|
||||
jtag_write_read(FLASH_POWER_DOWN, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
int SPIFlash::write_enable(bool verbose)
|
||||
{
|
||||
jtag_write_read(FLASH_WREN, NULL, NULL, 0);
|
||||
/* wait WEL */
|
||||
if (wait(FLASH_RDSR_WEL, FLASH_RDSR_WEL, 1000)) {
|
||||
printf("write en: Error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
std::cout << "write en: Success" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIFlash::write_disable()
|
||||
{
|
||||
jtag_write_read(FLASH_WRDIS, NULL, NULL, 0);
|
||||
/* wait ! WEL */
|
||||
int ret = wait(FLASH_RDSR_WEL, 0x00, 1000);
|
||||
if (ret == -1)
|
||||
printf("write disable: Error\n");
|
||||
else
|
||||
printf("write disable: Success\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SPIFlash::disable_protection()
|
||||
{
|
||||
uint8_t data = 0x00;
|
||||
jtag_write_read(FLASH_WRSR, &data, NULL, 1);
|
||||
if (wait(0xff, 0, 1000) < 0)
|
||||
return -1;
|
||||
|
||||
/* read status */
|
||||
if (read_status_reg() != 0) {
|
||||
std::cout << "disable protection failed" << std::endl;
|
||||
return -1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SPIFLASH_HPP
|
||||
#define SPIFLASH_HPP
|
||||
|
||||
#include "ftdijtag.hpp"
|
||||
|
||||
class SPIFlash {
|
||||
public:
|
||||
SPIFlash(FtdiJtag *jtag);
|
||||
/* power */
|
||||
void power_up();
|
||||
void power_down();
|
||||
void reset();
|
||||
/* protection */
|
||||
int write_enable(bool verbose=false);
|
||||
int write_disable();
|
||||
int disable_protection();
|
||||
/* erase */
|
||||
int bulk_erase();
|
||||
int sector_erase(int addr);
|
||||
int sectors_erase(int base_addr, int len);
|
||||
/* write */
|
||||
int write_page(int addr, uint8_t *data, int len);
|
||||
/* combo flash + erase */
|
||||
int erase_and_prog(int base_addr, uint8_t *data, int len);
|
||||
/* display/info */
|
||||
uint8_t read_status_reg(bool display=false);
|
||||
void read_id();
|
||||
private:
|
||||
void jtag_write_read(uint8_t cmd, uint8_t *tx, uint8_t *rx, uint16_t len = 0);
|
||||
int wait(uint8_t mask, uint8_t cond, uint32_t timeout, bool verbose=false);
|
||||
|
||||
FtdiJtag *_jtag;
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue