common/recipes-lib/cpld/files/altera.c (611 lines of code) (raw):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <syslog.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include "cpld.h"
#include "altera.h"
#include <openbmc/pal.h>
//#define _DEBUGMSG_
#ifdef _DEBUGMSG_
#define DEBUGMSG(format, args...) printf("[%s:%d] "format, __FILE__, __LINE__, ##args)
#else
#define DEBUGMSG(args...)
#endif
// status register
#define BUSY_IDLE 0x00
#define BUSY_ERASE 0x01
#define BUSY_WRITE 0x02
#define BUSY_READ 0x03
#define READ_SUCCESS 0x04
#define WRITE_SUCCESS 0x08
#define ERASE_SUCCESS 0x10
#define STATUS_BIT_MASK 0x1F
#define SIGNED_FULL_SIZE (32)
#define SIGNED_HEAD_SIZE (5)
#define SIGNED_DATA_SIZE SIGNED_FULL_SIZE - SIGNED_HEAD_SIZE
enum {
PROTECT_SEC_ID_5 = 0x1<<27,
PROTECT_SEC_ID_4 = 0x1<<26,
PROTECT_SEC_ID_3 = 0x1<<25,
PROTECT_SEC_ID_2 = 0x1<<24,
PROTECT_SEC_ID_1 = 0x1<<23,
};
enum {
CFM_IMAGE_NONE = 0,
CFM_IMAGE_1,
CFM_IMAGE_2,
CFM_IMAGE_1_M04,
};
// According to QSYS setting in FPGA project
static int g_i2c_file = 0;
static uint8_t g_i2c_bridge_addr = 0;
static char g_i2c_file_dp[64];
static unsigned long g_i2c_funcs = 0;
// on-chip Flash IP
static uint32_t g_flash_csr_base = 0;
static uint32_t g_flash_csr_status_reg = 0;
static uint32_t g_flash_csr_ctrl_reg = 0;
static uint32_t g_flash_data_reg = 0;
// Dual-boot IP
static uint32_t g_dual_boot_base = 0;
// CFM Info
static uint32_t g_cfm_start_addr = 0;
static uint32_t g_cfm_end_addr = 0;
static uint32_t g_cfm_image_type = 0;
int set_i2c_register(int file, uint8_t addr, int reg, int value)
{
uint8_t outbuf[8];
if (ioctl(file, I2C_SLAVE, addr) < 0) {
/* ERROR HANDLING; you can check errno to see what went wrong */
return -1;
}
// write register 4 bytes
outbuf[0] = (reg >> 24 ) & 0xFF;
outbuf[1] = (reg >> 16 ) & 0xFF;
outbuf[2] = (reg >> 8 ) & 0xFF;
outbuf[3] = (reg >> 0 ) & 0xFF;
outbuf[4] = (value >> 0 ) & 0xFF;
outbuf[5] = (value >> 8 ) & 0xFF;
outbuf[6] = (value >> 16 ) & 0xFF;
outbuf[7] = (value >> 24 ) & 0xFF;
return write(file, outbuf, 8);
}
int get_i2c_register(int file, uint8_t addr, uint32_t reg, uint32_t *val)
{
int ret = 0;
uint8_t outbuf[4], inbuf[4];
uint32_t readval;
struct i2c_msg messages[] = {
{ addr, 0, sizeof(outbuf), outbuf },
{ addr, I2C_M_RD, sizeof(inbuf), inbuf },
};
struct i2c_rdwr_ioctl_data ioctl_data = { messages, 2 };
if (ioctl(file, I2C_SLAVE, addr) < 0) {
/* ERROR HANDLING; you can check errno to see what went wrong */
return -1;
}
// write register 4 bytes
outbuf[0] = (reg >> 24 ) & 0xFF;
outbuf[1] = (reg >> 16 ) & 0xFF;
outbuf[2] = (reg >> 8 ) & 0xFF;
outbuf[3] = (reg >> 0 ) & 0xFF;
if (g_i2c_funcs & I2C_FUNC_I2C) {
ret = ioctl(file, I2C_RDWR, &ioctl_data);
if (ret == 2) {
ret = sizeof(inbuf); // update read length
} else {
ret = 0;
}
} else {
ret = write(file, outbuf, 4);
DEBUGMSG(" write() ret = %d\r\n", ret);
// dummy read 4 byte
ret = read(file, inbuf, 4);
}
readval = (inbuf[0] << 0);
readval |= (inbuf[1] << 8);
readval |= (inbuf[2] << 16);
readval |= (inbuf[3] << 24);
*val = readval;
DEBUGMSG(" val = %x len=%d\r\n", *val, ret);
return ret;
}
void max10_update_init(int i2c_file)
{
g_i2c_file = i2c_file;
// check i2c functionality
if (ioctl(g_i2c_file, I2C_FUNCS, &g_i2c_funcs) < 0) {
/* ERROR HANDLING; you can check errno to see what went wrong */
syslog(LOG_WARNING, "ioctl(file, I2C_FUNCS) fail.");
g_i2c_funcs = 0;
}
// Set timeout /* set timeout in units of 10 ms */
if (ioctl(g_i2c_file, I2C_TIMEOUT, 1000) < 0) {
/* ERROR HANDLING; you can check errno to see what went wrong */
syslog(LOG_WARNING, "ioctl(file, I2C_TIMEOUT) fail.");
}
// set retry time
if (ioctl(g_i2c_file, I2C_RETRIES, 100) < 0) {
/* ERROR HANDLING; you can check errno to see what went wrong */
syslog(LOG_WARNING, "ioctl(file, I2C_RETRIES) fail.");
}
}
void max10_done()
{
close(g_i2c_file);
g_i2c_file = 0;
g_i2c_funcs = 0;
}
int max10_reg_write(int address, int data)
{
int ret ;
if (g_i2c_file == 0) {
printf("\n i2c device not specified. \n");
return -1;
} else {
ret = set_i2c_register(g_i2c_file, g_i2c_bridge_addr, address, data);
if (ret != 8) {
syslog(LOG_WARNING, "set_i2c_register() ERROR. ret = %d.", ret);
}
}
usleep(100);
return ret;
}
int max10_reg_read(uint32_t address)
{
uint32_t data;
int ret;
if (g_i2c_file == 0) {
printf("\n i2c device not specified. \n");
return -1;
} else {
ret = get_i2c_register(g_i2c_file, g_i2c_bridge_addr, address, &data);
if (ret != 4) {
syslog(LOG_WARNING, "get_i2c_register() ERROR. ret = %d.", ret);
}
}
//usleep(1000);
return data;
}
int max10_protect_sectors(void)
{
int ret = 0;
int value;
DEBUGMSG("Enter %s\r\n", __func__);
value = max10_reg_read(g_flash_csr_ctrl_reg);
value |= (0x1F<<5);
ret = max10_reg_write(g_flash_csr_ctrl_reg, value);
DEBUGMSG("Exit %s\r\n", __func__);
return ret;
}
int max10_unprotect_sector(int sector_id)
{
int ret = 0;
int value;
int val_before, val_after;
printf("Max10 Unprotect Sector %d. \r\n", sector_id);
value = max10_reg_read(g_flash_csr_ctrl_reg);
val_before = value;
DEBUGMSG("\t read ctrl reg = 0x%08X. \r\n", val_before);
switch(sector_id)
{
case 5:
value = value & (~PROTECT_SEC_ID_5); // set to zero
break;
case 4:
value = value & (~PROTECT_SEC_ID_4);
break;
case 3:
value = value & (~PROTECT_SEC_ID_3);
break;
case 2:
value = value & (~PROTECT_SEC_ID_2);
break;
case 1:
value = value & (~PROTECT_SEC_ID_1);
break;
case 0:
default:
value = 0xFFFFFFFF;
break;
}
DEBUGMSG("\t write ctrl reg = 0x%08X. \r\n", value);
ret = max10_reg_write(g_flash_csr_ctrl_reg, value);
val_after = max10_reg_read(g_flash_csr_ctrl_reg);
DEBUGMSG("\t read ctrl reg = 0x%08X. \r\n", val_after);
// compare
value = val_after ^ val_before;
DEBUGMSG("\t diff = 0x%08X. \r\n", value);
DEBUGMSG("Exit %s\r\n", __func__);
return ret;
}
int max10_unprotect_sector_all(void)
{
int ret = 0;
int value;
int val_before, val_after;
DEBUGMSG("Enter %s\r\n", __func__);
value = max10_reg_read(g_flash_csr_ctrl_reg);
val_before = value;
DEBUGMSG("\t read ctrl reg = 0x%08X. \r\n", val_before);
value = value & ~(0x1F<<23); // set to zero
DEBUGMSG("\t write ctrl reg = 0x%08X. \r\n", value);
ret = max10_reg_write(g_flash_csr_ctrl_reg, value);
val_after = max10_reg_read(g_flash_csr_ctrl_reg);
DEBUGMSG("\t read ctrl reg = 0x%08X. \r\n", val_after);
// compare
value = val_after ^ val_before;
DEBUGMSG("\t diff = 0x%08X. \r\n", value);
DEBUGMSG("Exit %s\r\n", __func__);
return ret;
}
int max10_erase_sector(int sector_id)
{
int ret = 0;
int value;
int status;
int done = 0;
int cnt = 0;
//Control register bit 20~22
enum{
SECTOR_ID_5 = 0b101 << 20,
SECTOR_ID_4 = 0b100 << 20,
SECTOR_ID_3 = 0b011 << 20,
SECTOR_ID_2 = 0b010 << 20,
SECTOR_ID_1 = 0b001 << 20,
Sector_erase_NotSet = 0b111 << 20,
};
printf("Max10 Erase Sector %d \r\n", sector_id);
value = max10_reg_read(g_flash_csr_ctrl_reg);
DEBUGMSG("\t read ctrl reg = 0x%08X. \r\n", value);
value &= ~(0x7<<20); // clear bit 20~22.
switch(sector_id)
{
case 5:
value |= SECTOR_ID_5;
break;
case 4:
value |= SECTOR_ID_4;
break;
case 3:
value |= SECTOR_ID_3;
break;
case 2:
value |= SECTOR_ID_2;
break;
case 1:
value |= SECTOR_ID_1;
break;
case 0:
value |= Sector_erase_NotSet;
break;
default:
printf("\t WRONG sector id = 0x%08X. \r\n", sector_id);
}
max10_reg_write(g_flash_csr_ctrl_reg, value);
DEBUGMSG("\t write ctrl reg = 0x%08X. \r\n", value);
cnt = 0;
//Wait erase finished.
while(!done) {
status = max10_reg_read(g_flash_csr_status_reg);
DEBUGMSG("\t read status reg = 0x%08X. \r\n", status);
if (cnt < MAX10_RETRY_TIMEOUT) {
cnt++;
usleep(10);
} else {
printf("Programming Fail Timeout\n");
return -1;
}
if (status & BUSY_ERASE) {
usleep(500*1000);
continue;
}
if (status & ERASE_SUCCESS) {
done = 1;
printf("Erase sector SUCCESS. \r\n");
} else {
printf("Erase sector FAIL. \r\n");
ret = -1;
}
}
return ret;
}
// write 4 byte into on-chip flash.
int max10_flash_write(int address, int data)
{
int ret = 0;
ret = max10_reg_write(g_flash_data_reg + address, data);
return ret;
}
// Read 4 byte from on-chip flash.
int max10_flash_read(int address)
{
int ret = 0;
ret = max10_reg_read(g_flash_data_reg + address);
return ret;
}
int max10_status_read(void)
{
int ret = 0;
ret = max10_reg_read(g_flash_csr_status_reg);
return ret;
}
int max10_is_busy(void)
{
int value;
value = max10_reg_read(g_flash_csr_status_reg);
if (value & (BUSY_ERASE|BUSY_READ|BUSY_WRITE))
return 1;
else
return 0; // timeout
}
int max10_update_rpd(uint8_t* rpd_file, uint8_t image_type, int cfm_start_addr, int cfm_end_addr)
{
int ret = 0;
int address;
int data;
int receivedHex[4];
int byte;
int bContinue = 1;
int status;
int offset;
int cnt;
printf("OnChip Flash Status = 0x%X. \r\n", max10_status_read());
printf("image type = 0x%X. \r\n", image_type);
switch (image_type)
{
case CFM_IMAGE_1:
ret = max10_unprotect_sector(5);
ret = max10_erase_sector(5);
break;
case CFM_IMAGE_2:
ret = max10_unprotect_sector(3);
ret = max10_unprotect_sector(4);
ret = max10_erase_sector(3);
ret = max10_erase_sector(4);
break;
case CFM_IMAGE_1_M04:
ret = max10_unprotect_sector(4);
ret = max10_erase_sector(4);
break;
case CFM_IMAGE_NONE:
printf(" WRONG image type = 0x%X. \r\n", image_type);
return -1;
}
if (ret != 0) {
printf("Erase sector fail. \r\n");
return -1;
}
// Set erase none.
ret = max10_erase_sector(0);
// start program
offset = 0;
for(address = cfm_start_addr; address < cfm_end_addr; address += 4) {
/*Get 4 bytes from UART Terminal*/
receivedHex[0] = rpd_file[offset + 0];
receivedHex[1] = rpd_file[offset + 1];
receivedHex[2] = rpd_file[offset + 2];
receivedHex[3] = rpd_file[offset + 3];
/*Swap LSB with MSB before write into CFM*/
for(byte=0; byte<4; byte++)
{
receivedHex[byte] = (((receivedHex[byte] & 0xaa)>>1)|((receivedHex[byte] & 0x55)<<1));
receivedHex[byte] = (((receivedHex[byte] & 0xcc)>>2)|((receivedHex[byte] & 0x33)<<2));
receivedHex[byte] = (((receivedHex[byte] & 0xf0)>>4)|((receivedHex[byte] & 0x0f)<<4));
}
/*Combine 4 bytes to become 1 word before write operation*/
data = (receivedHex[0]<<24)|(receivedHex[1]<<16)|(receivedHex[2]<<8)|(receivedHex[3]);
DEBUGMSG("%s write data=%x\n", __func__, data);
/*Command to write into On-Chip Flash IP*/
max10_flash_write(address, data);
// Wait for write operation
bContinue = 1;
cnt = 0;
while( bContinue ) {
status = max10_status_read();
status &= STATUS_BIT_MASK;
if (cnt < MAX10_RETRY_TIMEOUT) {
cnt++;
usleep(10);
} else {
printf("Programming Fail Timeout\n");
return -1;
}
if( (status & BUSY_WRITE) == BUSY_WRITE ){
DEBUGMSG("Writing CFM0(0x%X)\n",address);
continue;
}
if( (status & WRITE_SUCCESS) == WRITE_SUCCESS){
DEBUGMSG("Write to addr: 0x%X SUCCESSFUL\n", address);
bContinue = 0;
continue;
}
if(status != BUSY_IDLE){
printf("Write to addr: 0x%X failed. status = 0x%X. \n", address, status);
bContinue = 1;
continue;
}
}
// show percentage
{
static int last_percent = 0;
int total = cfm_end_addr - cfm_start_addr;
int percent = ((address+4 - cfm_start_addr)*100)/total;
if(last_percent != percent) {
last_percent = percent;
printf("\rProgress: Addr: 0x%X [%d %%]", address, percent);
fflush(stdout);
}
}
offset+= 4;
}
printf("\n Done!\n");
max10_protect_sectors();
return ret;
}
/*Dual configuration IP on MAX 10*/
int max10_dual_boot_busy(void)
{
int value;
// offset 3 register.
value = max10_reg_read(g_dual_boot_base + 3*4);
if(value == 0)
return 0;
else
return 1;
}
uint8_t max10_dual_boot_cfg_sel_get(void)
{
int value;
// offset 7 register.
value = max10_reg_read(g_dual_boot_base + 7*4);
if(value & (0x1<<1))
return CONFIG_1;
else
return CONFIG_0;
}
int max10_dual_boot_cfg_sel_set(uint8_t img_no)
{
int value;
int address = g_dual_boot_base + 4; // offset 1
int ret;
// offset 1 (write only), bit 0, config_sel_overwrite
value = 0x1; //config_sel_overwrite = 1
switch(img_no) {
case CONFIG_0:
break;
case CONFIG_1:
value |= 0x1<<1;
break;
}
if(max10_dual_boot_busy() == 1) {
return -1;
}
ret = max10_reg_write(address, value);
return ret;
}
int max10_trig_reconfig(void)
{
int value = 0x1;
int address = g_dual_boot_base + 0; // offset 0
int ret;
if (max10_dual_boot_busy() == 0)
ret = max10_reg_write(address, value);
else
return -1;
return ret;
}
int max10_cpld_cfm_update(FILE *fd, char *key, char is_signed)
{
struct stat finfo;
char buf[SIGNED_FULL_SIZE] = {0};
char str[SIGNED_FULL_SIZE] = {0};
uint8_t *rpd_file_buff;
uint8_t image_type;
int rpd_filesize;
int readbytes;
int cfm_start_addr, cfm_end_addr;
int ret = 0;
int flash_size;
if (is_signed == true) {
memcpy(str, key, 32);
fseek(fd, -(SIGNED_DATA_SIZE), SEEK_END);
if (fread(buf, 1, SIGNED_FULL_SIZE, fd)) {
if (strstr(buf, str) == NULL) {
printf("Error, Signed Key not Match\n");
return -1;
}
} else {
printf("File read fail\n");
return -1;
}
fseek(fd, 0, SEEK_SET);
printf("Singed Key Match\n");
}
// Get file size
fstat(fileno(fd), &finfo);
if (is_signed == true) {
rpd_filesize = finfo.st_size - SIGNED_FULL_SIZE;
} else {
rpd_filesize = finfo.st_size;
}
printf("rpd file size = %d bytes. \r\n", rpd_filesize);
// allocate memory
rpd_file_buff = malloc(rpd_filesize);
if (rpd_file_buff == 0) {
printf("allocate memory fail. \r\n");
return -1;
}
//read rpt file into memory
readbytes = read(fileno(fd), rpd_file_buff, rpd_filesize);
if (readbytes != rpd_filesize) {
printf("read(), ret = %d. \r\n", readbytes);
}
cfm_start_addr = g_cfm_start_addr;
cfm_end_addr = g_cfm_end_addr;
image_type = g_cfm_image_type;
flash_size = cfm_end_addr - cfm_start_addr +1;
if(rpd_filesize != flash_size) {
printf("Error, File Size=%x Flash Size=%x\n", rpd_filesize, flash_size);
return -1;
}
ret = max10_update_rpd(rpd_file_buff, image_type, cfm_start_addr, cfm_end_addr);
free(rpd_file_buff);
return ret;
}
int max10_cpld_get_id(uint32_t *dev_id)
{
*dev_id = 0x01234567;
return 0;
}
static void max10_iic_init(uint8_t bus, uint8_t addr)
{
snprintf(g_i2c_file_dp, sizeof(g_i2c_file_dp), "/dev/i2c-%u", bus);
g_i2c_bridge_addr = addr;
return;
}
static void max10_dev_init(altera_max10_attr_t *attr)
{
g_flash_csr_base = attr->csr_base;
g_flash_csr_status_reg = g_flash_csr_base + 0x00;
g_flash_csr_ctrl_reg = g_flash_csr_base + 0x04;
g_flash_data_reg = attr->data_base;
g_dual_boot_base = attr->boot_base;
g_cfm_start_addr = attr->start_addr;
g_cfm_end_addr = attr->end_addr;
g_cfm_image_type = attr->img_type;
DEBUGMSG("%s file dp=%s\n", __func__, g_i2c_file_dp);
DEBUGMSG("base reg=%x, status_reg=%x, ctrl_reg=%x\n", g_flash_csr_base, g_flash_csr_status_reg, g_flash_csr_ctrl_reg);
DEBUGMSG("cfm start addr=%x, endaddr=%x\n", g_cfm_start_addr, g_cfm_end_addr);
return;
}
static int cpld_dev_open(void *_attr)
{
int i2c_file;
altera_max10_attr_t *attr = (altera_max10_attr_t *)_attr;
if (!attr) {
return -1;
}
max10_dev_init(attr);
max10_iic_init(attr->bus_id, attr->slv_addr);
// Open a connection to the I2C userspace control file.
if ((i2c_file = open(g_i2c_file_dp, O_RDWR)) < 0) {
printf("Unable to open %s\n", g_i2c_file_dp);
return -1;
}
max10_update_init(i2c_file);
return 0;
}
static int cpld_dev_close()
{
max10_done();
return 0;
}
static int max10_get_fw_ver(uint32_t *ver)
{
int ret;
ret = get_i2c_register(g_i2c_file, g_i2c_bridge_addr, 0x00100028, ver);
if (ret != 4) {
syslog(LOG_WARNING, "get_i2c_register() ERROR. ret = %d.", ret);
return -1;
}
return 0;
}
struct cpld_dev_info altera_dev_list[] = {
[0] = {
.name = "MAX10-10M16",
.dev_id = 0x01234567,
.intf = INTF_I2C,
.cpld_open = cpld_dev_open,
.cpld_close = cpld_dev_close,
.cpld_ver = max10_get_fw_ver,
.cpld_program = max10_cpld_cfm_update,
.cpld_dev_id = max10_cpld_get_id,
},
[1] = {
.name = "MAX10-10M25",
.dev_id = 0x01234567,
.intf = INTF_I2C,
.cpld_open = cpld_dev_open,
.cpld_close = cpld_dev_close,
.cpld_ver = max10_get_fw_ver,
.cpld_program = max10_cpld_cfm_update,
.cpld_dev_id = max10_cpld_get_id,
},
[2] = {
.name = "MAX10-10M04",
.dev_id = 0x01234567,
.intf = INTF_I2C,
.cpld_open = cpld_dev_open,
.cpld_close = cpld_dev_close,
.cpld_ver = max10_get_fw_ver,
.cpld_program = max10_cpld_cfm_update,
.cpld_dev_id = max10_cpld_get_id,
},
};