Patchwork [U-Boot,v3,3/7] Add fuse API and commands

login
register
mail settings
Submitter Benoît Thébaudeau
Date March 26, 2013, 9:24 p.m.
Message ID <1364333096-3698-3-git-send-email-benoit.thebaudeau@advansee.com>
Download mbox | patch
Permalink /patch/231538/
State Changes Requested
Delegated to: Stefano Babic
Headers show

Comments

Benoît Thébaudeau - March 26, 2013, 9:24 p.m.
This can be useful for fuse-like hardware, OTP SoC options, etc.

Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
---
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
Benoît Thébaudeau - April 4, 2013, 7:48 p.m.
On Tuesday, March 26, 2013 10:28:39 PM, Benoît Thébaudeau wrote:
> On Tuesday, March 26, 2013 10:24:52 PM, Benoît Thébaudeau wrote:
> > This can be useful for fuse-like hardware, OTP SoC options, etc.
> > 
> > Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
> > ---
> > 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
> 
> FYI, I have started to work on the OCOTP stuff too with this API. I attach
> the
> corresponding README that I made.
> 
> I will resume my work on this subject in about two weeks. This will be either
> a
> v4 or a new series, depending on what the v3 became in the meantime.

Forget this v3. I will drop the bit interface in v4. It is redundant and not
very useful, except to reduce the human error factor when playing with a single
fuse, and people are supposed to know what they're doing, so...

Best regards,
Benoît

Patch

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 <benoit.thebaudeau@advansee.com>
+ *
+ * Based on the mpc512x iim code:
+ * Copyright 2008 Silicon Turnkey Express, Inc.
+ * Martha Marx <mmarx@silicontkx.com>
+ *
+ * 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 <command.h>
+#include <fuse.h>
+#include <asm/errno.h>
+
+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? <y/N>\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 <bank> <word> <bit> - read a fuse bit\n"
+	"fuse read.word <bank> <word> [<cnt>] - read 1 or 'cnt' fuse words,\n"
+	"    starting at 'word'\n"
+	"fuse sense.bit <bank> <word> <bit> - sense a fuse bit\n"
+	"fuse sense.word <bank> <word> [<cnt>] - sense 1 or 'cnt' fuse words,\n"
+	"    starting at 'word'\n"
+	"fuse prog.bit [-y] <bank> <word> <bit> - program a fuse bit\n"
+	"    (PERMANENT)\n"
+	"fuse prog.word [-y] <bank> <word> <hexval> [<hexval>...] - program 1\n"
+	"    or several fuse words, starting at 'word' (PERMANENT)\n"
+	"fuse override.bit <bank> <word> <bit> <val> - override a fuse bit\n"
+	"fuse override.word <bank> <word> <hexval> [<hexval>...] - 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 <bank> <word> <bit>
+   int fuse_read_word(u32 bank, u32 word, u32 *val);
+   fuse read.word <bank> <word> [<cnt>]
+      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 <bank> <word> <bit>
+   int fuse_sense_word(u32 bank, u32 word, u32 *val);
+   fuse sense.word <bank> <word> [<cnt>]
+      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] <bank> <word> <bit>
+   int fuse_prog_word(u32 bank, u32 word, u32 val);
+   fuse prog.word [-y] <bank> <word> <hexval> [<hexval>...]
+      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 <bank> <word> <bit> <val>
+   int fuse_override_word(u32 bank, u32 word, u32 val);
+   fuse override.word <bank> <word> <hexval> [<hexval>...]
+      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 <benoit.thebaudeau@advansee.com>
+ *
+ * Based on the mpc512x iim code:
+ * Copyright 2008 Silicon Turnkey Express, Inc.
+ * Martha Marx <mmarx@silicontkx.com>
+ *
+ * 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_ */