From 3de15e03d89eeda18e665d270c6e3abf65c94d1d Mon Sep 17 00:00:00 2001 From: Terry Lv Date: Tue, 26 Jan 2010 20:18:22 +0800 Subject: ENGR00120476: Add fuse support for mx51 Add fuse support for mx51. Signed-off-by: Terry Lv --- cpu/arm_cortexa8/mx51/Makefile | 3 + cpu/arm_cortexa8/mx51/cmd_fuse.c | 92 +++++++++++ cpu/arm_cortexa8/mx51/imx_fuse.c | 333 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 cpu/arm_cortexa8/mx51/cmd_fuse.c create mode 100644 cpu/arm_cortexa8/mx51/imx_fuse.c (limited to 'cpu') diff --git a/cpu/arm_cortexa8/mx51/Makefile b/cpu/arm_cortexa8/mx51/Makefile index 435f26f..79f697d 100644 --- a/cpu/arm_cortexa8/mx51/Makefile +++ b/cpu/arm_cortexa8/mx51/Makefile @@ -28,6 +28,9 @@ include $(TOPDIR)/config.mk LIB = $(obj)lib$(SOC).a COBJS = interrupts.o serial.o generic.o iomux.o timer.o cache.o +COBJS-$(CONFIG_CMD_FUSE) += cmd_fuse.o +COBJS-$(CONFIG_IMX_FUSE) += imx_fuse.o +COBJS += $(COBJS-y) SOBJS = mxc_nand_load.o SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) diff --git a/cpu/arm_cortexa8/mx51/cmd_fuse.c b/cpu/arm_cortexa8/mx51/cmd_fuse.c new file mode 100644 index 0000000..1d32e3a --- /dev/null +++ b/cpu/arm_cortexa8/mx51/cmd_fuse.c @@ -0,0 +1,92 @@ +/* + * (C) Copyright 2008-2010 Freescale Semiconductor, Inc. + * Terry Lv + * + * Copyright 2007, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the pxa mmc code: + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * + * 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 +#include +#include +#include +#include +#include + +int do_fuseops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int rc = 0; + int bank = 0, + row = 0, + val = 0; + + switch (argc) { + case 4: + if (strcmp(argv[1], "read") == 0) { + bank = simple_strtoul(argv[2], NULL, 16); + row = simple_strtoul(argv[3], NULL, 16); + + fuse_read(bank, row); + } else if (strcmp(argv[1], "blow") == 0) { + fuse_blow_func(argv[2], argv[3]); + } else { + printf("It is too dangeous for you to use fuse command.\n"); + printf("Usage:\n%s\n", cmdtp->usage); + rc = 1; + } + break; + case 5: + if (strcmp(argv[1], "blow") == 0) { + bank = simple_strtoul(argv[2], NULL, 16); + row = simple_strtoul(argv[3], NULL, 16); + val = simple_strtoul(argv[4], NULL, 16); + + fuse_blow(bank, row, val); + } else { + printf("It is too dangeous for you to use fuse command.\n"); + printf("Usage:\n%s\n", cmdtp->usage); + rc = 1; + } + break; + default: + rc = 1; + printf("It is too dangeous for you to use fuse command.\n"); + printf("Usage:\n%s\n", cmdtp->usage); + break; + } + + return rc; +} + +U_BOOT_CMD( + fuse, 5, 1, do_fuseops, + "Fuse sub system", + "read - Read some fuses\n" + "blow - Blow some fuses\n" + "blow scc - Blow scc value\n" + "blow srk - Blow srk value\n" + "blow fecmac <0x##:0x##:0x##:0x##:0x##:0x##>" + "- Blow FEC Mac address\n"); + diff --git a/cpu/arm_cortexa8/mx51/imx_fuse.c b/cpu/arm_cortexa8/mx51/imx_fuse.c new file mode 100644 index 0000000..3901a25 --- /dev/null +++ b/cpu/arm_cortexa8/mx51/imx_fuse.c @@ -0,0 +1,333 @@ +/* + * (C) Copyright 2008-2010 Freescale Semiconductor, Inc. + * Terry Lv + * + * Copyright 2007, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the pxa mmc code: + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * + * 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 +#include +#include +#include +#include +#include + +static void quick_itoa(u32 num, char *a) +{ + int i, j, k; + for (i = 0; i <= 7; i++) { + j = (num >> (4 * i)) & 0xF; + k = (j < 10) ? '0' : ('a' - 0xa); + a[i] = j + k; + } +} + +/* slen - streng length, e.g.: 23 -> slen=2; abcd -> slen=4 */ +/* only convert hex value as string input. so "12" is 0x12. */ +static u32 quick_atoi(char *a, u32 slen) +{ + u32 i, num = 0, digit; + + for (i = 0; i < slen; i++) { + if (a[i] >= '0' && a[i] <= '9') { + digit = a[i] - '0'; + } else if (a[i] >= 'a' && a[i] <= 'f') { + digit = a[i] - 'a' + 10; + } else if (a[i] >= 'A' && a[i] <= 'F') { + digit = a[i] - 'A' + 10; + } else { + printf("ERROR: %c\n", a[i]); + return -1; + } + num = (num * 16) + digit; + } + + return num; +} + +static void fuse_op_start(void) +{ + /* Do not generate interrupt */ + writel(0, IIM_BASE_ADDR + IIM_STATM_OFF); + /* clear the status bits and error bits */ + writel(0x3, IIM_BASE_ADDR + IIM_STAT_OFF); + writel(0xFE, IIM_BASE_ADDR + IIM_ERR_OFF); +} + +/* + * The action should be either: + * POLL_FUSE_PRGD + * or: + * POLL_FUSE_SNSD + */ +static s32 poll_fuse_op_done(s32 action) +{ + u32 status, error; + + if (action != POLL_FUSE_PRGD && action != POLL_FUSE_SNSD) { + printf("%s(%d) invalid operation\n", __func__, action); + return -1; + } + + /* Poll busy bit till it is NOT set */ + while ((readl(IIM_BASE_ADDR + IIM_STAT_OFF) & IIM_STAT_BUSY) != 0) + ; + + /* Test for successful write */ + status = readl(IIM_BASE_ADDR + IIM_STAT_OFF); + error = readl(IIM_BASE_ADDR + IIM_ERR_OFF); + + if ((status & action) != 0 && \ + (error & (action >> IIM_ERR_SHIFT)) == 0) { + if (error) { + printf("Even though the operation seems successful...\n"); + printf("There are some error(s) at addr=0x%x: 0x%x\n", + (IIM_BASE_ADDR + IIM_ERR_OFF), error); + } + return 0; + } + printf("%s(%d) failed\n", __func__, action); + printf("status address=0x%x, value=0x%x\n", + (IIM_BASE_ADDR + IIM_STAT_OFF), status); + printf("There are some error(s) at addr=0x%x: 0x%x\n", + (IIM_BASE_ADDR + IIM_ERR_OFF), error); + return -1; +} + +static u32 sense_fuse(s32 bank, s32 row, s32 bit) +{ + s32 addr, addr_l, addr_h, reg_addr; + + fuse_op_start(); + + addr = ((bank << 11) | (row << 3) | (bit & 0x7)); + /* Set IIM Program Upper Address */ + addr_h = (addr >> 8) & 0x000000FF; + /* Set IIM Program Lower Address */ + addr_l = (addr & 0x000000FF); + +#ifdef IIM_FUSE_DEBUG + printf("%s: addr_h=0x%x, addr_l=0x%x\n", + __func__, addr_h, addr_l); +#endif + writel(addr_h, IIM_BASE_ADDR + IIM_UA_OFF); + writel(addr_l, IIM_BASE_ADDR + IIM_LA_OFF); + + /* Start sensing */ + writel(0x8, IIM_BASE_ADDR + IIM_FCTL_OFF); + if (poll_fuse_op_done(POLL_FUSE_SNSD) != 0) { + printf("%s(bank: %d, row: %d, bit: %d failed\n", + __func__, bank, row, bit); + } + reg_addr = IIM_BASE_ADDR + IIM_SDAT_OFF; + + return readl(reg_addr); +} + +int fuse_read(int bank, char row) +{ + u32 fuse_val; + s32 err = 0; + + printf("Read fuse at bank:%d row:%d\n", bank, row); + fuse_val = sense_fuse(bank, row, 0); + printf("fuses at (bank:%d, row:%d) = 0x%x\n", bank, row, fuse_val); + + return err; +} + +/* Blow fuses based on the bank, row and bit positions (all 0-based) +*/ +static s32 fuse_blow_bit(s32 bank, s32 row, s32 bit) +{ + int addr, addr_l, addr_h, ret = -1; + + fuse_op_start(); + + /* Disable IIM Program Protect */ + writel(0xAA, IIM_BASE_ADDR + IIM_PREG_P_OFF); + + addr = ((bank << 11) | (row << 3) | (bit & 0x7)); + /* Set IIM Program Upper Address */ + addr_h = (addr >> 8) & 0x000000FF; + /* Set IIM Program Lower Address */ + addr_l = (addr & 0x000000FF); + +#ifdef IIM_FUSE_DEBUG + printf("blowing addr_h=0x%x, addr_l=0x%x\n", addr_h, addr_l); +#endif + + writel(addr_h, IIM_BASE_ADDR + IIM_UA_OFF); + writel(addr_l, IIM_BASE_ADDR + IIM_LA_OFF); + + /* Start Programming */ + writel(0x31, IIM_BASE_ADDR + IIM_FCTL_OFF); + if (poll_fuse_op_done(POLL_FUSE_PRGD) == 0) + ret = 0; + + /* Enable IIM Program Protect */ + writel(0x0, IIM_BASE_ADDR + IIM_PREG_P_OFF); + + return ret; +} + +static void fuse_blow_row(s32 bank, s32 row, s32 value) +{ + u32 reg, i; + + /* enable fuse blown */ + reg = readl(CCM_BASE_ADDR + 0x64); + reg |= 0x10; + writel(reg, CCM_BASE_ADDR + 0x64); + + for (i = 0; i < 8; i++) { + if (((value >> i) & 0x1) == 0) + continue; + if (fuse_blow_bit(bank, row, i) != 0) { + printf("fuse_blow_bit(bank: %d, row: %d, bit: %d failed\n", + bank, row, i); + } + } + reg &= ~0x10; + writel(reg, CCM_BASE_ADDR + 0x64); +} + +int fuse_blow(int bank, int row, int val) +{ + u32 fuse_val, err = 0; + + printf("Blowing fuse at bank:%d row:%d value:%d\n", + bank, row, val); + fuse_blow_row(bank, row, val); + fuse_val = sense_fuse(bank, row, 0); + printf("fuses at (bank:%d, row:%d) = 0x%x\n", bank, row, fuse_val); + + return err; +} + +static int fuse_read_mac_addr(u8 *data) +{ + data[0] = sense_fuse(1, 9, 0) ; + data[1] = sense_fuse(1, 10, 0) ; + data[2] = sense_fuse(1, 11, 0) ; + data[3] = sense_fuse(1, 12, 0) ; + data[4] = sense_fuse(1, 13, 0) ; + data[5] = sense_fuse(1, 14, 0) ; + + if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) && + (data[3] == 0) && (data[4] == 0) && (data[5] == 0)) { + return 0; + } + + return 1; +} + +int fuse_blow_func(char *func_name, char *func_val) +{ + u32 value, i; + char *s; + char val[3]; + s32 err = 0; + + if (0 == strcmp(func_name, "scc")) { + /* fuse_blow scc C3D153EDFD2EA9982226EF5047D3B9A0B9C7138EA87C028401D28C2C2C0B9AA2 */ + printf("Ready to burn SCC fuses\n"); + s = func_val; + for (i = 0; ; ++i) { + memcpy(val, s, 2); + val[2] = '\0'; + value = quick_atoi(val, 2); + /* printf("fuse_blow_row(2, %d, value=0x%x)\n", i, value); */ + fuse_blow_row(2, i, value); + + if ((++s)[0] == '\0') { + printf("ERROR: Odd string input\n"); + err = -1; + break; + } + if ((++s)[0] == '\0') { + printf("Successful\n"); + break; + } + } + } else if (0 == strcmp(func_name, "srk")) { + /* fuse_blow srk 418bccd09b53bee1ab59e2662b3c7877bc0094caee201052add49be8780dff95 */ + printf("Ready to burn SRK key fuses\n"); + s = func_val; + for (i = 0; ; ++i) { + memcpy(val, s, 2); + val[2] = '\0'; + value = quick_atoi(val, 2); + if (i == 0) { + /* 0x41 goes to SRK_HASH[255:248], bank 1, row 1 */ + fuse_blow_row(1, 1, value); + } else { + /* 0x8b in SRK_HASH[247:240] bank 3, row 1 */ + /* 0xcc in SRK_HASH[239:232] bank 3, row 2 */ + /* ... */ + fuse_blow_row(3, i, value); + + if ((++s)[0] == '\0') { + printf("ERROR: Odd string input\n"); + err = -1; + break; + } + if ((++s)[0] == '\0') { + printf("Successful\n"); + break; + } + } + } + } else if (0 == strcmp(func_name, "fecmac")) { + u8 ea[6] = { 0 }; + + if (NULL == func_val) { + /* Read the Mac address and print it */ + fuse_read_mac_addr(ea); + + printf("FEC MAC address: "); + printf("0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n\n", + ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]); + + return 0; + } + + eth_parse_enetaddr(func_val, ea); + if (!is_valid_ether_addr(ea)) { + printf("Error: invalid mac address parameter!\n"); + err = -1; + } else { + for (i = 0; i < 6; ++i) + fuse_blow_row(1, i + 9, ea[i]); + } + } else { + printf("This command is not supported\n"); + } + + return err; +} + + -- cgit v1.1