1
0
mirror of git://projects.qi-hardware.com/xburst-tools.git synced 2024-11-01 10:22:48 +02:00
xburst-tools/ingenic.c
Sergey Gridassov 86ba12af12 USB abstraction: implemented sent length checking
Ingenic: implemented SDRAM load and go
USBBoot cmdset: implemented 'load' and 'go'
2010-12-04 13:17:33 +03:00

476 lines
11 KiB
C

/*
* JzBoot: an USB bootloader for JZ series of Ingenic(R) microprocessors.
* Copyright (C) 2010 Sergey Gridassov <grindars@gmail.com>,
* Peter Zotov <whitequark@whitequark.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include "ingenic.h"
#include "usbdev.h"
#include "debug.h"
#include "config.h"
#define HANDLE ingenic_handle_t *handle = hndl
#define BUILDTYPE(cmdset, id) (((cmdset) << 16) | (0x##id & 0xFFFF))
#define CPUID(id) ((id) & 0xFFFF)
#define CMDSET(id) (((id) & 0xFFFF0000) >> 16)
typedef struct {
void *usb;
uint32_t type;
uint32_t total_sdram_size;
const ingenic_callbacks_t *callbacks;
void *callbacks_data;
firmware_config_t cfg;
nand_config_t nand;
} ingenic_handle_t;
static const struct {
const char * const magic;
uint32_t id;
} magic_list[] = {
{ "JZ4740V1", BUILDTYPE(CMDSET_SPL, 4740) },
{ "JZ4750V1", BUILDTYPE(CMDSET_SPL, 4750) },
{ "JZ4760V1", BUILDTYPE(CMDSET_SPL, 4760) },
{ "Boot4740", BUILDTYPE(CMDSET_USBBOOT, 4740) },
{ "Boot4750", BUILDTYPE(CMDSET_USBBOOT, 4750) },
{ "Boot4760", BUILDTYPE(CMDSET_USBBOOT, 4760) },
{ NULL, 0 }
};
static void hexdump(const void *data, size_t size) {
const unsigned char *bytes = data;
for(int i = 0; i < size; i+= 16) {
debug(LEVEL_DEBUG, "%04X ", i);
int chunk_size = size - i;
if(chunk_size > 16)
chunk_size = 16;
for(int j = 0; j < chunk_size; j++) {
debug(LEVEL_DEBUG, "%02X ", bytes[i + j]);
if(j == 7)
debug(LEVEL_DEBUG, " ");
}
for(int j = 0; j < 16 - chunk_size; j++) {
debug(LEVEL_DEBUG, " ");
if(j == 8)
debug(LEVEL_DEBUG, " ");
}
debug(LEVEL_DEBUG, "|");
for(int j = 0; j < chunk_size; j++) {
debug(LEVEL_DEBUG, "%c", isprint(bytes[i + j]) ? bytes[i + j] : '.');
}
debug(LEVEL_DEBUG, "|\n");
}
}
static uint32_t ingenic_probe(void *usb_hndl) {
char magic[9];
if(usbdev_vendor(usb_hndl, USBDEV_FROMDEV, VR_GET_CPU_INFO, 0, 0, magic, 8) == -1)
return 0;
magic[8] = 0;
for(int i = 0; magic_list[i].magic != NULL; i++)
if(strcmp(magic_list[i].magic, magic) == 0) {
debug(LEVEL_DEBUG, "Magic: '%s', type: %08X\n", magic, magic_list[i].id);
return magic_list[i].id;
}
debug(LEVEL_ERROR, "Unknown CPU: '%s'\n", magic);
errno = EINVAL;
return 0;
}
void *ingenic_open(void *usb_hndl) {
uint32_t type = ingenic_probe(usb_hndl);
if(type == 0)
return NULL;
ingenic_handle_t *ret = malloc(sizeof(ingenic_handle_t));
ret->usb = usb_hndl;
ret->type = type;
ret->callbacks = NULL;
return ret;
}
int ingenic_redetect(void *hndl) {
HANDLE;
uint32_t type = ingenic_probe(handle->usb);
if(type == 0)
return -1;
uint32_t prev = handle->type;
handle->type = type;
if(CMDSET(prev) != CMDSET(type) && handle->callbacks && handle->callbacks->cmdset_change)
handle->callbacks->cmdset_change(handle->callbacks_data);
return 0;
}
void ingenic_set_callbacks(void *hndl, const ingenic_callbacks_t *callbacks, void *arg) {
HANDLE;
handle->callbacks = callbacks;
handle->callbacks_data = arg;
}
int ingenic_cmdset(void *hndl) {
HANDLE;
return CMDSET(handle->type);
}
int ingenic_type(void *hndl) {
HANDLE;
return CPUID(handle->type);
}
void ingenic_close(void *hndl) {
HANDLE;
free(handle);
}
#define CFGOPT(name, var, exp) { char *str = cfg_getenv(name); if(str == NULL) { debug(LEVEL_ERROR, "%s is not set\n", name); errno = EINVAL; return -1; }; int v = atoi(str); if(!(exp)) { debug(LEVEL_ERROR, "%s must be in %s\n", name, #exp); return -1; }; handle->cfg.var = v; }
#define NOPT(name, var, exp) { char *str = cfg_getenv("NAND_" name); if(str == NULL) { debug(LEVEL_ERROR, "%s is not set\n", "NAND_" name); errno = EINVAL; return -1; }; int v = atoi(str); if(!(exp)) { debug(LEVEL_ERROR, "%s must be in %s\n", "NAND_" name, #exp); return -1; }; handle->nand.nand_##var = v; }
int ingenic_rebuild(void *hndl) {
HANDLE;
handle->cfg.cpu_id = CPUID(handle->type);
CFGOPT("EXTCLK", ext_clk, v <= 27 && v >= 12);
CFGOPT("CPUSPEED", cpu_speed, (v % 12) == 0);
handle->cfg.cpu_speed /= handle->cfg.ext_clk;
CFGOPT("PHMDIV", phm_div, v <= 32 && v >= 2);
CFGOPT("USEUART", use_uart, 1);
CFGOPT("BAUDRATE", baudrate, 1);
CFGOPT("SDRAM_BUSWIDTH", bus_width, (v == 16) || (v == 32));
handle->cfg.bus_width = handle->cfg.bus_width == 16 ? 1 : 0;
CFGOPT("SDRAM_BANKS", bank_num, (v >= 4) && ((v % 4) == 0));
handle->cfg.bank_num /= 4;
CFGOPT("SDRAM_ROWADDR", row_addr, 1);
CFGOPT("SDRAM_COLADDR", col_addr, 1);
CFGOPT("SDRAM_ISMOBILE", is_mobile, v == 0 || v == 1);
CFGOPT("SDRAM_ISBUSSHARE", is_busshare, v == 0 || v == 1);
memset(&handle->cfg.debug, 0, sizeof(ingenic_stage1_debug_t));
handle->total_sdram_size = (uint32_t)
(2 << (handle->cfg.row_addr + handle->cfg.col_addr - 1)) * 2
* (handle->cfg.bank_num + 1) * 2
* (2 - handle->cfg.bus_width);
debug(LEVEL_DEBUG, "Firmware configuration dump:\n");
hexdump(&handle->cfg, sizeof(firmware_config_t));
handle->nand.cpuid = CPUID(handle->type);
NOPT("BUSWIDTH", bw, 1);
NOPT("ROWCYCLES", rc, 1);
NOPT("PAGESIZE", ps, 1);
NOPT("PAGEPERBLOCK", ppb, 1);
NOPT("FORCEERASE", force_erase, 1);
// FIXME: pn is not set by xburst-tools usbboot. Is this intended?
NOPT("OOBSIZE", os, 1);
NOPT("ECCPOS", eccpos, 1);
NOPT("BADBLOCKPOS", bbpos, 1);
NOPT("BADBLOCKPAGE", bbpage, 1);
NOPT("PLANENUM", plane, 1);
NOPT("BCHBIT", bchbit, 1);
NOPT("WPPIN", wppin, 1);
NOPT("BLOCKPERCHIP", bpc, 1);
debug(LEVEL_DEBUG, "NAND configuration dump:\n");
hexdump(&handle->nand, sizeof(nand_config_t));
return 0;
}
uint32_t ingenic_sdram_size(void *hndl) {
HANDLE;
return handle->total_sdram_size;
}
static int ingenic_wordop(void *usb, int op, uint32_t base) {
return usbdev_vendor(usb, USBDEV_TODEV, op, (base >> 16), base & 0xFFFF, 0, 0);
}
int ingenic_stage1_debugop(void *hndl, const char *filename, uint32_t op, uint32_t pin, uint32_t base, uint32_t size) {
HANDLE;
handle->cfg.debug.debug_ops = op;
handle->cfg.debug.pin_num = pin;
handle->cfg.debug.start = base;
handle->cfg.debug.size = size;
debug(LEVEL_DEBUG, "Debug configuration dump:\n");
hexdump(&handle->cfg, sizeof(firmware_config_t));
int ret = ingenic_loadstage(handle, INGENIC_STAGE1, filename);
memset(&handle->cfg.debug, 0, sizeof(ingenic_stage1_debug_t));
return ret;
}
int ingenic_loadstage(void *hndl, int id, const char *file) {
HANDLE;
if(file == NULL) {
errno = EINVAL;
return -1;
}
uint32_t base;
int cmd;
switch(id) {
case INGENIC_STAGE1:
base = SDRAM_BASE + STAGE1_BASE;
cmd = VR_PROGRAM_START1;
break;
case INGENIC_STAGE2:
base = SDRAM_BASE + handle->total_sdram_size - STAGE2_CODESIZE;
cmd = VR_PROGRAM_START2;
break;
default:
errno = EINVAL;
return -1;
}
FILE *fd = fopen(file, "rb");
if(fd == NULL)
return -1;
fseek(fd, 0, SEEK_END);
int size = ftell(fd);
fseek(fd, 0, SEEK_SET);
void *data = malloc(size);
fread(data, size, 1, fd);
fclose(fd);
memcpy(data + 8, &handle->cfg, sizeof(firmware_config_t));
if(ingenic_wordop(handle->usb, VR_SET_DATA_ADDRESS, base) == -1) {
free(data);
return -1;
}
int ret = usbdev_sendbulk(handle->usb, data, size);
free(data);
if(ret == -1)
return -1;
if(id == INGENIC_STAGE2) {
ret = usbdev_vendor(handle->usb, USBDEV_TODEV, VR_FLUSH_CACHES, 0, 0, 0, 0);
if(ret == -1)
return -1;
}
ret = usbdev_vendor(handle->usb, USBDEV_TODEV, cmd, (base >> 16), base & 0xFFFF, 0, 0);
if(ret == -1)
return -1;
if(id == INGENIC_STAGE2)
return ingenic_redetect(hndl);
else
return 0;
}
int ingenic_memtest(void *hndl, const char *filename, uint32_t base, uint32_t size, uint32_t *fail) {
HANDLE;
int ret = ingenic_stage1_debugop(handle, filename, STAGE1_DEBUG_MEMTEST, 0, base, size);
if(ret == -1)
return -1;
uint32_t data[2];
ret = usbdev_recvbulk(handle->usb, &data, sizeof(data));
if(ret == -1)
return -1;
hexdump(data, ret);
if(ret < 4) {
errno = EIO;
return -1;
}
if(data[0] != 0) {
errno = EFAULT;
*fail = data[0];
return -1;
}
return 0;
}
int ingenic_configure_stage2(void *hndl) {
HANDLE;
// DS_flash_info (nand_config_t only) is not implemented in stage2, so using DS_hand (nand_config_t + firmware_config_t)
uint8_t *hand = malloc(sizeof(nand_config_t) + sizeof(firmware_config_t));
memcpy(hand, &handle->nand, sizeof(nand_config_t));
memcpy(hand + sizeof(nand_config_t), &handle->cfg, sizeof(firmware_config_t));
int ret = usbdev_sendbulk(handle->usb, hand, sizeof(nand_config_t) + sizeof(firmware_config_t));
free(hand);
if(ret == -1)
return -1;
if(usbdev_vendor(handle->usb, USBDEV_TODEV, VR_CONFIGRATION, DS_hand, 0, 0, 0) == -1)
return -1;
uint32_t result[8];
ret = usbdev_recvbulk(handle->usb, result, sizeof(result));
if(ret == -1)
return -1;
debug(LEVEL_DEBUG, "Stage2 configured\n");
return 0;
}
int ingenic_load_sdram(void *hndl, void *data, uint32_t base, uint32_t size) {
HANDLE;
while(size) {
int block = size > 65535 ? 65535 : size;
debug(LEVEL_DEBUG, "Loading SDRAM from %p to 0x%08X, size %u\n", data, base, block);
if(ingenic_wordop(handle->usb, VR_SET_DATA_ADDRESS, base) == -1)
return -1;
if(ingenic_wordop(handle->usb, VR_SET_DATA_LENGTH, block) == -1)
return -1;
if(usbdev_sendbulk(handle->usb, data, block) == -1)
return -1;
if(usbdev_vendor(handle->usb, USBDEV_TODEV, VR_SDRAM_OPS, SDRAM_LOAD, 0, 0, 0) == -1)
return -1;
uint32_t result[8];
if(usbdev_recvbulk(handle->usb, result, sizeof(result)) == -1)
return -1;
data += 65535;
base += 65535;
if(size <= 65535)
break;
size -= 65535;
}
debug(LEVEL_DEBUG, "Load done\n");
return 0;
}
int ingenic_load_sdram_file(void *hndl, uint32_t base, const char *file) {
HANDLE;
FILE *fd = fopen(file, "rb");
if(fd == NULL)
return -1;
fseek(fd, 0, SEEK_END);
int size = ftell(fd);
fseek(fd, 0, SEEK_SET);
void *data = malloc(size);
fread(data, size, 1, fd);
fclose(fd);
int ret = ingenic_load_sdram(handle, data, base, size);
free(data);
return ret;
}
int ingenic_go(void *hndl, uint32_t address) {
HANDLE;
return ingenic_wordop(handle->usb, VR_PROGRAM_START2, address);
}