From patchwork Tue Mar 26 21:24:52 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Beno=C3=AEt_Th=C3=A9baudeau?= X-Patchwork-Id: 231538 X-Patchwork-Delegate: sbabic@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 94B392C0097 for ; Wed, 27 Mar 2013 08:26:57 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 4711D4A03A; Tue, 26 Mar 2013 22:26:28 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id aA3eN+CYrOtq; Tue, 26 Mar 2013 22:26:28 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 068304A047; Tue, 26 Mar 2013 22:25:52 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 8AE684A027 for ; Tue, 26 Mar 2013 22:25:46 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id m-HLz2xGSabI for ; Tue, 26 Mar 2013 22:25:43 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from zose-mta11.web4all.fr (zose-mta11.web4all.fr [178.33.204.87]) by theia.denx.de (Postfix) with ESMTPS id B99664A021 for ; Tue, 26 Mar 2013 22:25:39 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by zose-mta11.web4all.fr (Postfix) with ESMTP id 48BC72A009; Tue, 26 Mar 2013 22:25:39 +0100 (CET) X-Virus-Scanned: amavisd-new at zose-mta11.web4all.fr Received: from zose-mta11.web4all.fr ([127.0.0.1]) by localhost (zose-mta11.web4all.fr [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 12U8S73CVQsj; Tue, 26 Mar 2013 22:25:37 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by zose-mta11.web4all.fr (Postfix) with ESMTP id 15FE22A05A; Tue, 26 Mar 2013 22:25:37 +0100 (CET) X-Virus-Scanned: amavisd-new at zose-mta11.web4all.fr Received: from zose-mta11.web4all.fr ([127.0.0.1]) by localhost (zose-mta11.web4all.fr [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id NHqoseIyz3Vb; Tue, 26 Mar 2013 22:25:35 +0100 (CET) Received: from advdt005-ubuntu.?none? (cie44-1-88-188-188-98.fbx.proxad.net [88.188.188.98]) by zose-mta11.web4all.fr (Postfix) with ESMTPA id E39E12A02C; Tue, 26 Mar 2013 22:25:34 +0100 (CET) From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= To: u-boot@lists.denx.de, Stefano Babic , Wolfgang Denk Date: Tue, 26 Mar 2013 22:24:52 +0100 Message-Id: <1364333096-3698-3-git-send-email-benoit.thebaudeau@advansee.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1364333096-3698-1-git-send-email-benoit.thebaudeau@advansee.com> References: <1364333096-3698-1-git-send-email-benoit.thebaudeau@advansee.com> MIME-Version: 1.0 Subject: [U-Boot] [PATCH v3 3/7] Add fuse API and commands X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de This can be useful for fuse-like hardware, OTP SoC options, etc. Signed-off-by: Benoît Thébaudeau --- Changes in v3: - Rebase against latest u-boot-imx/master. - Update copyright years. - Rename the "ovride" command to "override" as it is nicer to read. - For obvious safety reasons, ask the user to confirm each fuse programming operation, unless the '-y' command line option was given, which can be used in scripts. - Rename "row" to "word" since it is less confusing and it is vocabulary common to iim and ocotp in the reference manuals. - Add doc/README.fuse. Changes in v2: - Rebase against latest master. README | 1 + common/Makefile | 1 + common/cmd_fuse.c | 214 ++++++++++++++++++++++++++++++++++++++++++++++ doc/README.fuse | 73 ++++++++++++++++ include/config_cmd_all.h | 1 + include/fuse.h | 49 +++++++++++ 6 files changed, 339 insertions(+) create mode 100644 common/cmd_fuse.c create mode 100644 doc/README.fuse create mode 100644 include/fuse.h diff --git a/README b/README index e45ae4a..14ab9df 100644 --- a/README +++ b/README @@ -844,6 +844,7 @@ The following options need to be configured: CONFIG_CMD_FDOS * Dos diskette Support CONFIG_CMD_FLASH flinfo, erase, protect CONFIG_CMD_FPGA FPGA device initialization support + CONFIG_CMD_FUSE Device fuse support CONFIG_CMD_GETTIME * Get time since boot CONFIG_CMD_GO * the 'go' command (exec code) CONFIG_CMD_GREPENV * search environment diff --git a/common/Makefile b/common/Makefile index 719fc23..7207ad7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -105,6 +105,7 @@ ifdef CONFIG_FPGA COBJS-$(CONFIG_CMD_FPGA) += cmd_fpga.o endif COBJS-$(CONFIG_CMD_FS_GENERIC) += cmd_fs.o +COBJS-$(CONFIG_CMD_FUSE) += cmd_fuse.o COBJS-$(CONFIG_CMD_GETTIME) += cmd_gettime.o COBJS-$(CONFIG_CMD_GPIO) += cmd_gpio.o COBJS-$(CONFIG_CMD_I2C) += cmd_i2c.o diff --git a/common/cmd_fuse.c b/common/cmd_fuse.c new file mode 100644 index 0000000..862efda --- /dev/null +++ b/common/cmd_fuse.c @@ -0,0 +1,214 @@ +/* + * (C) Copyright 2009-2013 ADVANSEE + * Benoît Thébaudeau + * + * Based on the mpc512x iim code: + * Copyright 2008 Silicon Turnkey Express, Inc. + * Martha Marx + * + * 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 + +static int strtou32(const char *str, unsigned int base, u32 *result) +{ + char *ep; + + *result = simple_strtoul(str, &ep, base); + if (ep == str || *ep != '\0') + return -EINVAL; + + return 0; +} + +static int confirm_prog(void) +{ + puts("Warning: Programming fuses is an irreversible operation!\n" + " This may brick your system.\n" + " Use this command only if you are sure of " + "what you are doing!\n" + "\nReally perform this fuse programming? \n"); + + if (getc() == 'y') { + int c; + + putc('y'); + c = getc(); + putc('\n'); + if (c == '\r') + return 1; + } + + puts("Fuse programming aborted\n"); + return 0; +} + +static int do_fuse(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + const char *op = argc >= 2 ? argv[1] : NULL; + int confirmed = argc >= 3 && !strcmp(argv[2], "-y"); + u32 bank, word, bit, cnt, val; + int ret, i; + + argc -= 2 + confirmed; + argv += 2 + confirmed; + + if (argc < 2 || strtou32(argv[0], 0, &bank) || + strtou32(argv[1], 0, &word)) + return CMD_RET_USAGE; + + if (!strcmp(op, "read.bit")) { + if (argc != 3 || strtou32(argv[2], 0, &bit)) + return CMD_RET_USAGE; + + printf("Reading bank %u word 0x%.8x bit %u: ", bank, word, bit); + ret = fuse_read_bit(bank, word, bit, &val); + if (ret) + goto err; + + printf("%u\n", val); + } else if (!strcmp(op, "read.word")) { + if (argc == 2) + cnt = 1; + else if (argc != 3 || strtou32(argv[2], 0, &cnt)) + return CMD_RET_USAGE; + + printf("Reading bank %u:\n", bank); + for (i = 0; i < cnt; i++, word++) { + if (!(i % 4)) + printf("\nWord 0x%.8x:", word); + + ret = fuse_read_word(bank, word, &val); + if (ret) + goto err; + + printf(" %.8x", val); + } + putc('\n'); + } else if (!strcmp(op, "sense.bit")) { + if (argc != 3 || strtou32(argv[2], 0, &bit)) + return CMD_RET_USAGE; + + printf("Sensing bank %u word 0x%.8x bit %u: ", bank, word, bit); + ret = fuse_sense_bit(bank, word, bit, &val); + if (ret) + goto err; + + printf("%u\n", val); + } else if (!strcmp(op, "sense.word")) { + if (argc == 2) + cnt = 1; + else if (argc != 3 || strtou32(argv[2], 0, &cnt)) + return CMD_RET_USAGE; + + printf("Sensing bank %u:\n", bank); + for (i = 0; i < cnt; i++, word++) { + if (!(i % 4)) + printf("\nWord 0x%.8x:", word); + + ret = fuse_sense_word(bank, word, &val); + if (ret) + goto err; + + printf(" %.8x", val); + } + putc('\n'); + } else if (!strcmp(op, "prog.bit")) { + if (argc != 3 || strtou32(argv[2], 0, &bit)) + return CMD_RET_USAGE; + + printf("Programming bank %u word 0x%.8x bit %u...\n", + bank, word, bit); + if (!confirmed && !confirm_prog()) + return CMD_RET_FAILURE; + ret = fuse_prog_bit(bank, word, bit); + if (ret) + goto err; + } else if (!strcmp(op, "prog.word")) { + if (argc < 3) + return CMD_RET_USAGE; + + for (i = 2; i < argc; i++, word++) { + if (strtou32(argv[i], 16, &val)) + return CMD_RET_USAGE; + + printf("Programming bank %u word 0x%.8x to 0x%.8x...\n", + bank, word, val); + if (!confirmed && !confirm_prog()) + return CMD_RET_FAILURE; + ret = fuse_prog_word(bank, word, val); + if (ret) + goto err; + } + } else if (!strcmp(op, "override.bit")) { + if (argc != 4 || strtou32(argv[2], 0, &bit) || + strtou32(argv[3], 0, &val) || val > 1) + return CMD_RET_USAGE; + + printf("Overriding bank %u word 0x%.8x bit %u with %u...\n", + bank, word, bit, val); + ret = fuse_override_bit(bank, word, bit, val); + if (ret) + goto err; + } else if (!strcmp(op, "override.word")) { + if (argc < 3) + return CMD_RET_USAGE; + + for (i = 2; i < argc; i++, word++) { + if (strtou32(argv[i], 16, &val)) + return CMD_RET_USAGE; + + printf("Overriding bank %u word 0x%.8x with " + "0x%.8x...\n", bank, word, val); + ret = fuse_override_word(bank, word, val); + if (ret) + goto err; + } + } else { + return CMD_RET_USAGE; + } + + return 0; + +err: + puts("ERROR\n"); + return ret; +} + +U_BOOT_CMD( + fuse, CONFIG_SYS_MAXARGS, 0, do_fuse, + "Fuse sub-system", + "read.bit - read a fuse bit\n" + "fuse read.word [] - read 1 or 'cnt' fuse words,\n" + " starting at 'word'\n" + "fuse sense.bit - sense a fuse bit\n" + "fuse sense.word [] - sense 1 or 'cnt' fuse words,\n" + " starting at 'word'\n" + "fuse prog.bit [-y] - program a fuse bit\n" + " (PERMANENT)\n" + "fuse prog.word [-y] [...] - program 1\n" + " or several fuse words, starting at 'word' (PERMANENT)\n" + "fuse override.bit - override a fuse bit\n" + "fuse override.word [...] - override 1\n" + " or several fuse words, starting at 'word'" +); diff --git a/doc/README.fuse b/doc/README.fuse new file mode 100644 index 0000000..ee365ff --- /dev/null +++ b/doc/README.fuse @@ -0,0 +1,73 @@ +Fuse API functions and commands + +The fuse API allows to control a fusebox and how it is used by the upper +hardware layers. + +A fuse corresponds to a single non-volatile memory bit that can be programmed +(i.e. blown, set to 1) only once. The programming operation is irreversible. A +fuse that has not been programmed reads 0. + +Fuses can be used by SoCs to store various permanent configuration and data, +e.g. boot configuration, security configuration, MAC addresses, etc. + +A fuse word is the smallest group of fuses that can be read at once from the +fusebox control IP registers. This is limited to 32 bits with the current API. + +A fuse bank is the smallest group of fuse words having a common ID, as defined +by each SoC. + +Upon startup, the fusebox control IP reads the fuse values and stores them to a +volatile shadow cache. + +See the README files of the drivers implementing this API in order to know the +SoC- and implementation-specific details. + +The API provides both bit and word accesses because the access type may change +depending on the IP, and it is safer to manipulate only bits (when no more is +needed) in order to avoid programming a wrong value. + +Functions / commands: + + int fuse_read_bit(u32 bank, u32 word, u32 bit, u32 *val); + fuse read.bit + int fuse_read_word(u32 bank, u32 word, u32 *val); + fuse read.word [] + Read a fuse bit or fuse words from the shadow cache. + + int fuse_sense_bit(u32 bank, u32 word, u32 bit, u32 *val); + fuse sense.bit + int fuse_sense_word(u32 bank, u32 word, u32 *val); + fuse sense.word [] + Sense - i.e. read directly from the fusebox, skipping the shadow cache - a + fuse bit or fuse words. This operation does not update the shadow cache. + + This is useful to know the true value of a fuse if an override has been + performed (see below). + + int fuse_prog_bit(u32 bank, u32 word, u32 bit); + fuse prog.bit [-y] + int fuse_prog_word(u32 bank, u32 word, u32 val); + fuse prog.word [-y] [...] + Program a fuse bit or fuse words. This operation directly affects the + fusebox and is irreversible. The shadow cache is updated accordingly or + not, depending on each IP. + + int fuse_override_bit(u32 bank, u32 word, u32 bit, u32 val); + fuse override.bit + int fuse_override_word(u32 bank, u32 word, u32 val); + fuse override.word [...] + Override a fuse bit or fuse words in the shadow cache. + + The fusebox is unaffected, so following this operation, the shadow cache + may differ from the fusebox values. Read or sense operations can then be + used to get the values from the shadow cache or from the fusebox. + + This is useful to change the behaviors linked to some cached fuse values, + either because this is needed only temporarily, or because some of the + fuses have already been programmed or are locked (if the SoC allows to + override a locked fuse). + +Configuration: + + CONFIG_CMD_FUSE + Define this to enable the fuse commands. diff --git a/include/config_cmd_all.h b/include/config_cmd_all.h index 0930781..041b18e 100644 --- a/include/config_cmd_all.h +++ b/include/config_cmd_all.h @@ -40,6 +40,7 @@ #define CONFIG_CMD_FDOS /* Floppy DOS support */ #define CONFIG_CMD_FLASH /* flinfo, erase, protect */ #define CONFIG_CMD_FPGA /* FPGA configuration Support */ +#define CONFIG_CMD_FUSE /* Device fuse support */ #define CONFIG_CMD_GETTIME /* Get time since boot */ #define CONFIG_CMD_HASH /* calculate hash / digest */ #define CONFIG_CMD_HWFLOW /* RTS/CTS hw flow control */ diff --git a/include/fuse.h b/include/fuse.h new file mode 100644 index 0000000..97dc569 --- /dev/null +++ b/include/fuse.h @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2009-2013 ADVANSEE + * Benoît Thébaudeau + * + * Based on the mpc512x iim code: + * Copyright 2008 Silicon Turnkey Express, Inc. + * Martha Marx + * + * 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 + */ + +#ifndef _FUSE_H_ +#define _FUSE_H_ + +/* + * Read/Sense/Program/Override interface: + * bank: Fuse bank + * word: Fuse word within the bank + * bit: Fuse bit within the word + * val: Value to read/write + * + * Returns: 0 on success, not 0 on failure + */ +int fuse_read_bit(u32 bank, u32 word, u32 bit, u32 *val); +int fuse_read_word(u32 bank, u32 word, u32 *val); +int fuse_sense_bit(u32 bank, u32 word, u32 bit, u32 *val); +int fuse_sense_word(u32 bank, u32 word, u32 *val); +int fuse_prog_bit(u32 bank, u32 word, u32 bit); +int fuse_prog_word(u32 bank, u32 word, u32 val); +int fuse_override_bit(u32 bank, u32 word, u32 bit, u32 val); +int fuse_override_word(u32 bank, u32 word, u32 val); + +#endif /* _FUSE_H_ */