Signed-off-by: Wolfgang Wegner <w.weg...@astro-kom.de> --- This patch adds the external "update" command to ASTRO MCF5373L boards. It needs this previous patch that is not included in mainline: non-blocking flash write/erase/status check functions (Message-Id 1260374411-11299-1-git-send-email-w.weg...@astro-kom.de)
board/astro/mcf5373l/Makefile | 2 +- board/astro/mcf5373l/update.c | 615 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 616 insertions(+), 1 deletions(-) create mode 100644 board/astro/mcf5373l/update.c diff --git a/board/astro/mcf5373l/Makefile b/board/astro/mcf5373l/Makefile index c7a1d05..e0b1bc6 100644 --- a/board/astro/mcf5373l/Makefile +++ b/board/astro/mcf5373l/Makefile @@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)lib$(BOARD).a -COBJS = $(BOARD).o fpga.o +COBJS = $(BOARD).o fpga.o update.o SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS)) diff --git a/board/astro/mcf5373l/update.c b/board/astro/mcf5373l/update.c new file mode 100644 index 0000000..63170b3 --- /dev/null +++ b/board/astro/mcf5373l/update.c @@ -0,0 +1,615 @@ +/* + * (C) Copyright 2006-2009 + * Adnan El-Bardawil <a.el-barda...@astro-kom.de> + * Wolfgang Wegner <w.weg...@astro-kom.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <asm/m5329.h> +#include <asm/immap_5329.h> +#include <asm/io.h> +#include <flash.h> +#include "astro.h" + +#include <asm/uart.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define DEBUG_INFO 1 +#define DEBUG_ERROR 1 + +/* dummy software version to signal card is in boot loader */ +#define SOFTWARE_VERSION 0x00 +#define SOFTWARE_SUBVERSION ' ' + +typedef enum { + CMD_INIT = 0, + CMD_NEW, + CMD_TX, + CMD_RX, + CMD_RX_RDY, + CMD_RX_ERROR +} cmd_stat_e; + +#define INT_CMD_IDLE 0 +#define INT_CMD_FLASH_MODE 1 +#define INT_CMD_WRITE_FLASH 2 +#define INT_CMD_RESTART 4 + +#define FL_STAT_IDLE 0 +#define FL_STAT_ERASE 1 +#define FL_STAT_PROG 2 +#define FL_STAT_ERROR 4 + +/* + * UART update subcommand codes + */ + +#define F_SAVE_PARAMETERS 0x00 /* save parameters on card */ +#define F_READ_PARAMETERS 0x01 /* read parameters */ +#define F_READ_CARDINFO 0x10 /* read card information */ +#define F_READ_ERROR_STATUS 0x11 /* read card error */ +#define F_TRANSPARENT_DATA 0x20 /* length byte + data */ + +#define SC_PREPARE_FOR_FLASH_DATA 0xc0 +#define SC_FLASH_DATA 0xc1 +#define SC_CLEAR_CRC 0xc2 +#define SC_CHECK_CRC 0xc3 +#define SC_RESTART 0xc4 + +typedef struct { + unsigned char command; + unsigned char length; + unsigned char subcommand; +} rcv_cmd_t; + +card_id_t card_information; + +struct { + parameters_t a; + parameters_t b; +} parameters; + +typedef struct { + int cnt; + cmd_stat_e stat; +} upd_command_t; + +typedef struct { + int cnt; + int length; + unsigned char *buffer; +} comm_status_t; + +const unsigned long fl_base[3] = { 0x80000, 0x540000, 0x6c0000 }; +const char fl_type_str[3][6] = { "Kernel", "Xilinx", "Altera" }; + +/* info for FLASH chips */ +extern flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; + +unsigned long crc, crc_count; + +/* CRC divisor value */ +#define CRC_POLYNOM 0x800500 + +void do_crc(unsigned char data) +{ + unsigned char count; + + crc |= data; + for (count = 0; count < 8; count++) { + crc <<= 1; + if (crc & 0x1000000) + crc ^= CRC_POLYNOM; + } + crc_count++; +} + +static unsigned char flash_buffer[0x10000]; +static unsigned long flash_data_length, flash_data_count; +static unsigned long flash_prog_count, flash_sect_count; +static unsigned char flash_data_type, crc_en; +static int flash_prog_stat; +static unsigned char uart_buffer[64]; + +void fl_prog(int force_write) +{ + int s, i; + unsigned long fl_adr, fl_sector; + + fl_adr = fl_base[flash_data_type] + flash_prog_count; + fl_sector = fl_adr >> 17; + + switch (flash_prog_stat) { + case FL_STAT_IDLE: + if (((flash_prog_count & 0x1ffff) == 0) && + ((flash_prog_count >> 17) == flash_sect_count)) { + flash_protect(FLAG_PROTECT_CLEAR, fl_adr, + fl_adr + 0x1ffff, flash_info); + s = flash_erase_nb(flash_info, fl_sector); + flash_prog_stat = FL_STAT_ERASE; + } else if ((flash_data_count >= (flash_prog_count + 64)) + || (force_write + && (flash_data_count > flash_prog_count))) { + s = write_buff_nb(flash_info, + flash_buffer + + (flash_prog_count & 0xffff), + fl_adr, 64); + flash_prog_stat = FL_STAT_PROG; + } + break; + case FL_STAT_ERASE: + s = flash_full_status_check_nb(flash_info, fl_sector, + 0, "erase"); + if (s != ERR_BUSY) { + if (s == ERR_OK) { + flash_sect_count++; + flash_prog_stat = FL_STAT_IDLE; + } else { + flash_prog_stat = FL_STAT_ERROR; + } + } + break; + case FL_STAT_PROG: + s = flash_full_status_check_nb(flash_info, fl_sector, + 0, "write"); + if (s == ERR_BUSY) + return; + + if (s != ERR_OK) { + flash_prog_stat = FL_STAT_ERROR; + return; + } + + flash_prog_stat = FL_STAT_IDLE; + for (i = 0; i < 64; i++) { + if (flash_buffer[(flash_prog_count + i) & 0xffff] != + *((unsigned char *)fl_adr + i)) + flash_prog_stat = FL_STAT_ERROR; + } + if ((flash_prog_count & 0xfff) == 0) + printf("."); + flash_prog_count += 64; + if (((flash_prog_count & 0x1ffff) == 0) + || (force_write + && (flash_data_count <= flash_prog_count))) { + flash_protect(FLAG_PROTECT_SET, + fl_adr & 0xfffe0000, + fl_adr | 0x1ffff, + flash_info); + } + break; + } +} + +void handle_rx_char(unsigned char rx_data, upd_command_t *cmd, + int *rcv_cnt, rcv_cmd_t *rcv_cmd, + comm_status_t *rx_stat) +{ + uart_t *uart; + + uart = (uart_t *)(MMAP_UART0); + if (readb(&uart->usr) & UART_USR_OE) { +#if DEBUG_ERROR + printf("UART FIFO Overrun Error!\n"); +#endif + writeb(UART_UCR_RESET_ERROR, &uart->ucr); + cmd->stat = CMD_RX_ERROR; + } + if (cmd->stat == CMD_INIT) { + if (*rcv_cnt == 0) { + /* First received Byte is the Command */ + rcv_cmd->command = rx_data; + /* non-Transparent Data: start Command Processing */ + if (rcv_cmd->command != F_TRANSPARENT_DATA) { + cmd->stat = CMD_NEW; + cmd->cnt++; + } else { + (*rcv_cnt)++; + } + } else { + /* Length and Subcommand for Transparent Data */ + if (*rcv_cnt == 1) { + rcv_cmd->length = rx_data; + (*rcv_cnt)++; + } else { + rcv_cmd->subcommand = rx_data; + cmd->stat = CMD_NEW; + cmd->cnt++; + } + } + } else if (cmd->stat == CMD_RX) { + /* Store received Data to Rx Buffer */ + rx_stat->buffer[rx_stat->cnt] = rx_data; + if (crc_en) + do_crc(rx_data); + (rx_stat->cnt)++; + if (rx_stat->cnt == rx_stat->length) + cmd->stat = CMD_RX_RDY; + } +} + +/* + * handle a transparent data command; these are used for software update + * return value is 1 if cmd_stat is set to CMD_INIT to let caller decide + * to reset the receive count, else 0. + */ +int handle_transparent_command(upd_command_t *cmd, rcv_cmd_t *rcv_cmd, + comm_status_t *rx_stat, comm_status_t *tx_stat, + unsigned char *int_command) +{ + int i; + unsigned char crc_status; + + if (cmd->stat == CMD_NEW) { + rx_stat->cnt = 0; + rx_stat->length = rcv_cmd->length - 1; + } + switch (rcv_cmd->subcommand) { + case SC_PREPARE_FOR_FLASH_DATA: + if (cmd->stat == CMD_NEW) { + rx_stat->buffer = uart_buffer; + cmd->stat = CMD_RX; + } else if (cmd->stat == CMD_RX_RDY) { + for (i = 0; i < 4; i++) + flash_data_length = (flash_data_length << 8) + + uart_buffer[i + 1]; + flash_data_type = uart_buffer[0]; +#if DEBUG_INFO + printf(" -- Flash Programming Initialized -- \n"); + printf(" -- Receiving %lu Bytes of type ", + flash_data_length); + for (i = 0; i < 6; i++) + printf("%c", fl_type_str[flash_data_type][i]); + printf(" [0x%lx]\n", fl_base[flash_data_type]); +#endif + uart_buffer[0] = rcv_cmd->command; + uart_buffer[1] = 1; + uart_buffer[2] = rcv_cmd->subcommand; + tx_stat->buffer = uart_buffer; + tx_stat->length = 3; + crc_count = 0; + crc = 0; + flash_data_count = 0; + flash_prog_count = 0; + flash_sect_count = 0; + flash_prog_stat = FL_STAT_IDLE; + crc_en = 0; + *int_command = INT_CMD_FLASH_MODE; + cmd->stat = CMD_TX; + return 1; + } + break; + case SC_FLASH_DATA: + if (cmd->stat == CMD_NEW) { + rx_stat->buffer = flash_buffer + + (flash_data_count & 0xffff); + rx_stat->cnt = 0; + cmd->stat = CMD_RX; + crc_en = 1; + } else if (cmd->stat == CMD_RX_RDY) { + crc_en = 0; + flash_data_count += rcv_cmd->length - 1; + cmd->stat = CMD_INIT; + return 1; + } + break; + case SC_CLEAR_CRC: + if (cmd->stat == CMD_NEW) { + uart_buffer[0] = rcv_cmd->command; + uart_buffer[1] = 1; + uart_buffer[2] = rcv_cmd->subcommand; + tx_stat->buffer = uart_buffer; + tx_stat->length = 3; + cmd->stat = CMD_TX; + crc_count = 0; + crc = 0; + } + break; + case SC_CHECK_CRC: + if (cmd->stat == CMD_NEW) { + rx_stat->buffer = uart_buffer; + cmd->stat = CMD_RX; + } else if (cmd->stat == CMD_RX_RDY) { + if ((flash_data_count <= flash_prog_count) + || (flash_prog_stat == FL_STAT_ERROR)) { + crc_status = 0; + if (uart_buffer[0] != ((crc >> 16) & 0xff)) + crc_status = 1; + if (uart_buffer[1] != ((crc >> 8) & 0xff)) + crc_status = 1; + if (flash_prog_stat == FL_STAT_ERROR) + crc_status |= 2; + uart_buffer[0] = rcv_cmd->command; + uart_buffer[1] = 4; + uart_buffer[2] = rcv_cmd->subcommand; + uart_buffer[3] = (crc >> 16); + uart_buffer[4] = (crc >> 8); + uart_buffer[5] = crc_status; +#if DEBUG_INFO + printf(" CRC %u / %lu of %lu kB \n", + crc_status, + (flash_data_count >> 10), + (flash_data_length >> 10)); +#endif + tx_stat->buffer = uart_buffer; + tx_stat->length = 6; + cmd->stat = CMD_TX; + if (crc_status) { + flash_prog_count = + flash_data_count = + (flash_data_count & 0xffff0000) + - 0x10000; + flash_prog_stat = FL_STAT_IDLE; + *int_command = INT_CMD_FLASH_MODE; + } + } + if (flash_data_count & 0xffff) + *int_command |= INT_CMD_WRITE_FLASH; + } + break; + case SC_RESTART: + if (cmd->stat == CMD_NEW) { + uart_buffer[0] = rcv_cmd->command; + uart_buffer[1] = 1; + uart_buffer[2] = rcv_cmd->subcommand; + tx_stat->buffer = uart_buffer; + tx_stat->length = 3; + cmd->stat = CMD_TX; + crc_count = 0; + crc = 0; + /* + * set restart command; sending the reply + * is done before finishing the loop. + */ + *int_command = INT_CMD_RESTART; + return 1; + } + break; + default: + /* + * for un-handled zero length transparent command, + * we have to handle this condition first! + */ + if ((cmd->stat == CMD_RX_RDY) + || (rx_stat->cnt == rx_stat->length)) + cmd->stat = CMD_INIT; + else if (cmd->stat == CMD_NEW) + cmd->stat = CMD_RX; + } + return 0; +} + +/* + * handle a general command (i.e. status and version information query) + * return value is always 0 except for transparent data where the + * return code is passed through to allow caller reset receive count. + */ +int handle_command(upd_command_t *cmd, rcv_cmd_t *rcv_cmd, + comm_status_t *rx_stat, comm_status_t *tx_stat, + unsigned char *int_command) +{ + int rv = 0; + + switch (rcv_cmd->command) { + case F_READ_ERROR_STATUS: +#if DEBUG_INFO + printf("read error status\n"); +#endif + uart_buffer[0] = 0; + uart_buffer[1] = 0; + uart_buffer[2] = 0; + uart_buffer[3] = 0; + tx_stat->buffer = uart_buffer; + tx_stat->length = 4; + cmd->stat = CMD_TX; + break; + case F_READ_PARAMETERS: + if (cmd->stat == CMD_NEW) { +#if DEBUG_INFO + printf("read parameters\n"); +#endif + memcpy(uart_buffer, (unsigned char *)¶meters.a, + 2 * sizeof(parameters_t)); + cmd->stat = CMD_TX; + tx_stat->length = 2 * sizeof(parameters_t); + } + break; + case F_READ_CARDINFO: +#if DEBUG_INFO + printf("read card info\n"); +#endif + card_information.software_version = + ((*int_command) & INT_CMD_FLASH_MODE) ? + 0xEE : SOFTWARE_VERSION; + tx_stat->buffer = (unsigned char *)&card_information; + tx_stat->length = 5; + cmd->stat = CMD_TX; + break; + + case F_TRANSPARENT_DATA: + rv = handle_transparent_command(cmd, rcv_cmd, rx_stat, + tx_stat, int_command); + break; + } + return rv; +} + +/* transmit data prepared in comm_status_t structure */ +void transmit_buffer(comm_status_t *tx_stat) +{ + int cnt; + for (cnt = 0; cnt < tx_stat->length; cnt++) + astro_put_char(tx_stat->buffer[cnt]); +} + +int do_update(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + unsigned char rx_data; + comm_status_t rx_stat, tx_stat; + upd_command_t cmd; + int rcv_cnt; + rcv_cmd_t rcv_cmd; + unsigned char int_command, tmp_char; + unsigned short tmp_short; + + int select, old_select, got_char; + eport_t *eport = (eport_t *)MMAP_EPORT; + gpio_t *gpio = (gpio_t *)MMAP_GPIO; + uart_t *uart = (uart_t *)MMAP_UART0; + char *env_string; + + cmd.cnt = 0; + + /* we set some dummy parameters to make the protocol work */ + card_information.card_type = ASTRO_ID; + env_string = getenv("loaderversion"); + if (env_string != NULL) { + card_information.hardware_version = + (int)simple_strtol(env_string, NULL, 16); + } else { + card_information.hardware_version = 0x10; + } + card_information.software_version = SOFTWARE_VERSION; + card_information.software_subversion = SOFTWARE_SUBVERSION; + card_information.fpga_version_altera = 0x12; + card_information.fpga_version_xilinx = 0x13; + + parameters.a.symbolrate = 0x5027; + parameters.a.viterbirate = 0x34; + parameters.a.satfreq = 0x6616; + parameters.a.card_error = 0; + + parameters.b.symbolrate = 0x5027; + parameters.b.viterbirate = 0x34; + parameters.b.satfreq = 0x6616; + parameters.b.card_error = 0; + + puts("Initializing serial port to ASTRO bus...\n"); + /* init serial port */ + rs_serial_init(0, 2400); + /* set UART pin for UART */ + tmp_short = __raw_readw(&gpio->par_uart); + tmp_short |= 0x0003; + __raw_writew(tmp_short, &gpio->par_uart); + /* set select pin as GPIO */ + tmp_short = __raw_readw(&gpio->par_irq); + tmp_short &= 0x0FF0; + __raw_writew(tmp_short, &gpio->par_irq); + /* set select pin as input */ + tmp_char = readb(&eport->pdr); + tmp_char &= 0xBF; + writeb(tmp_char, &eport->pdr); + /* + * set interrupt to both falling and rising edge + * (no IRQ used, though) + */ + tmp_short = __raw_readw(&eport->par); + tmp_short |= 0x3000; + __raw_writew(tmp_short, &eport->par); + + int_command = INT_CMD_IDLE; + + rcv_cnt = 0; + cmd.stat = CMD_INIT; + old_select = 0; /* always start from "deselected" state */ + rx_data = 0xFF; + + while (int_command != INT_CMD_RESTART) { + +#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) + WATCHDOG_RESET(); +#endif + + /* Flash program routine only called in flash mode */ + if (int_command & INT_CMD_FLASH_MODE) + fl_prog(int_command & INT_CMD_WRITE_FLASH); + + /* + * UART Card Select Routine + * Write ID on Card Select and switch baud rate + */ + select = ((readb(&eport->pdr) & 0x40) == 0x0); + + if ((select) && (!old_select)) { + /* set low baud rate for ID only */ + rs_serial_init(0, 2400); + astro_put_char(ASTRO_ID); + + /* Wait for TX finished before switching baudrate */ + while ((readb(&uart->usr) & UART_USR_TXEMP) == 0) { + } + + /* set high baud rate */ + rs_serial_init(0, 115200); + /* Reset the Receive Byte Counter */ + rcv_cnt = 0; + cmd.stat = CMD_INIT; + } else if ((!select) && old_select && (cmd.stat != CMD_INIT)) { + cmd.stat = CMD_INIT; +#if DEBUG_ERROR + printf("Command %u: 0x%02x (0x%02x) failed," + " len %d, cnt %d\n", + cmd.cnt, rcv_cmd.command, + rcv_cmd.subcommand, rcv_cmd.length, + rx_stat.cnt); +#endif + } + + old_select = select; + + got_char = astro_is_char(); + if (got_char) + rx_data = astro_get_char(); + + if (select) { + /* UART RX Routine */ + if (got_char) + handle_rx_char(rx_data, &cmd, &rcv_cnt, + &rcv_cmd, &rx_stat); + + /* Command Control Routine */ + if ((cmd.stat == CMD_NEW) + || (cmd.stat == CMD_RX_RDY)) { + if (handle_command(&cmd, &rcv_cmd, + &rx_stat, &tx_stat, + &int_command)) + rcv_cnt = 0; + } + + /* UART TX Routine */ + if (cmd.stat == CMD_TX) { + transmit_buffer(&tx_stat); + /* Reset the Receive Byte Counter */ + rcv_cnt = 0; + cmd.stat = CMD_INIT; + } + } + } + /* restart is done outside, so we simply leave this function */ + return 0; +} + +U_BOOT_CMD(update, 1, 1, do_update, + "automatic update via ASTRO bus", + "\n - start the update, waiting for transfer via bus"); -- 1.5.6.5 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot