[U-Boot] [PATCH 0/2] NXP WaARP7 set serial# from OTP fuses for USB iSerial

Greetings.
These two patches add support for automatic setting of the serial# environment variable based on OTP fuse settings. Once the serial# field is set then subsequent USB gadget mode instances of WaARP7 will export the serial number based on the OTP fuse settings.
This feature gives a handy output like this:
usb 1-1.1.1: new high-speed USB device number 17 using xhci_hcd usb 1-1.1.1: New USB device found, idVendor=0525, idProduct=a4a5 usb 1-1.1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1.1.1: Product: USB download gadget usb 1-1.1.1: Manufacturer: FSL usb 1-1.1.1: SerialNumber: WaRP7-0xf42400d3000001d4
The 64 bit value post-fixed to the WaARP7 string is based on OTP fuses which provide a unique serial number for each NXP i.MX7 SoC.
We make use of this feature when discerning a unique identifier for WaARP7 boards in our automated LAVA testing environment, hopefully its useful and acceptable to others.
Bryan O'Donoghue (2): warp7: usb: Introduce a get method for serial number warp7: usb: Set u-boot serial# based on OTP value
board/warp7/warp7.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)

We want to be able to set the USB device descriptor number iSerial number or indeed a disk-label unique identifier based on a chip-specific piece of data for the purposes of differentiating between WaRP7 boards via lsusb when connected to a host machine.
In order to do this we want to have a serial number encoded in hardware, which will persist across bootloader, filesystem and config file changes.
This patch utilises OCOTP_TESTER0 AND OCOTP_TESTER1 respectively for this purpose. OCOTP_TESTER is a unique identifier for each chip representing
31:0 OCOTP_TESTER0 (most significant) - FSL-wide unique, encoded LOT ID STD II/SJC CHALLENGE/ Unique ID
OCOTP_TESTER1 (least significant) 31:24 - The X-coordinate of the die location on the wafer/SJC CHALLENGE/ Unique ID 23:16 - The Y-coordinate of the die location on the wafer/SJC CHALLENGE/ Unique ID 15:11 - The wafer number of the wafer on which the device was fabricated/SJC CHALLENGE/ Unique ID 10:0 - FSL-wide unique, encoded LOT ID STD II/SJC CHALLENGE/ Unique ID
The 64 bits of data generate a unique serial number per-chip.
Signed-off-by: Bryan O'Donoghue bryan.odonoghue@linaro.org Cc: Fabio Estevam fabio.estevam@nxp.com Reviewed-by: Rui Miguel Silva rui.silva@linaro.org Reviewed-by: Ryan Harkin ryan.harkin@linaro.org --- board/warp7/warp7.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+)
diff --git a/board/warp7/warp7.c b/board/warp7/warp7.c index d422d63..2cec448 100644 --- a/board/warp7/warp7.c +++ b/board/warp7/warp7.c @@ -23,6 +23,7 @@ #include <power/pmic.h> #include <power/pfuze3000_pmic.h> #include "../freescale/common/pfuze.h" +#include <fuse.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -90,6 +91,58 @@ static struct fsl_esdhc_cfg usdhc_cfg[1] = { {USDHC3_BASE_ADDR}, };
+/* + * OCOTP_TESTER + * i.MX 7Solo Applications Processor Reference Manual, Rev. 0.1, 08/2016 + * OCOTP_TESTER describes a unique ID based on silicon wafer + * and die X/Y position + * + * OCOTOP_TESTER offset 0x410 + * 31:0 fuse 0 + * FSL-wide unique, encoded LOT ID STD II/SJC CHALLENGE/ Unique ID + * + * OCOTP_TESTER1 offset 0x420 + * 31:24 fuse 1 + * The X-coordinate of the die location on the wafer/SJC CHALLENGE/ Unique ID + * 23:16 fuse 1 + * The Y-coordinate of the die location on the wafer/SJC CHALLENGE/ Unique ID + * 15:11 fuse 1 + * The wafer number of the wafer on which the device was fabricated/SJC + * CHALLENGE/ Unique ID + * 10:0 fuse 1 + * FSL-wide unique, encoded LOT ID STD II/SJC CHALLENGE/ Unique ID + */ +#define WARP7_USB_SERIALID_BANK 0 +#define WARP7_USB_SERIALID_MSWORD 1 +#define WARP7_USB_SERIALID_LSWORD 2 + +static int warp7_get_serialid(u64 *id) +{ + u32 val; + int ret; + + if (!id) + return -EINVAL; + + /* Read first word */ + ret = fuse_read(WARP7_USB_SERIALID_BANK, WARP7_USB_SERIALID_MSWORD, &val); + if (ret) + goto done; + + *id = val; + *id <<= 32; + + /* Read second word */ + ret = fuse_read(WARP7_USB_SERIALID_BANK, WARP7_USB_SERIALID_LSWORD, &val); + if (ret) + goto done; + + *id |= val; + +done: + return ret; +} + int board_mmc_getcd(struct mmc *mmc) { /* Assume uSDHC3 emmc is always present */

Hi Bryan,
On Tue, Mar 13, 2018 at 9:00 AM, Bryan O'Donoghue bryan.odonoghue@linaro.org wrote:
+static int warp7_get_serialid(u64 *id)
Maybe you could turn place this function in a common location as it may be useful for others.
+{
u32 val;
int ret;
if (!id)
return -EINVAL;
/* Read first word */
ret = fuse_read(WARP7_USB_SERIALID_BANK, WARP7_USB_SERIALID_MSWORD, &val);
if (ret)
goto done;
Better just do 'return ret' instead of jumping to the 'done' label.

On 13/03/18 13:25, Fabio Estevam wrote:
+static int warp7_get_serialid(u64 *id)
Maybe you could turn place this function in a common location as it may be useful for others.
Ah, looking for a place to stick this as shared code I've found something which already does what this patch does
arch/arm/mach-imx/mx7/soc.c::void get_board_serial(struct tag_serialnr *serialnr)
commit c5752f73a53a ("imx: imx7d: Add SoC system support")

u-boot has a standard "serial#" environment variable that is suitable for storing the iSerial number we will supply via the USB device descriptor. serial# is automatically picked up by the disk subsystem in u-boot - thus providing a handy unique identifier in /dev/disk/by-id as detailed below.
Storing the hardware serial identifier in serial# means we can change the serial# if we want before USB enumeration - thus making iSerial automatic via OTP but overridable if necessary.
This patch reads the defined OTP fuse and sets environment variable "serial#" to the value read. If there is any error in reading the value the boot will continue and "serial#" will be set to zero.
With this patch in place the USB mass storage device will appear in /dev/disk/by-id with a unique name based on the OTP value. For example
/dev/disk/by-id/usb-Linux_UMS_disk_0_WaRP7-0xf42400d3000000d4-0:0
Signed-off-by: Bryan O'Donoghue bryan.odonoghue@linaro.org Cc: Fabio Estevam fabio.estevam@nxp.com Reviewed-by: Rui Miguel Silva rui.silva@linaro.org Reviewed-by: Ryan Harkin ryan.harkin@linaro.org --- board/warp7/warp7.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/board/warp7/warp7.c b/board/warp7/warp7.c index 2cec448..a27993f 100644 --- a/board/warp7/warp7.c +++ b/board/warp7/warp7.c @@ -239,6 +239,9 @@ int board_usb_phy_mode(int port) int board_late_init(void) { struct wdog_regs *wdog = (struct wdog_regs *)WDOG1_BASE_ADDR; + u64 serial_id = 0; + char serial_string[0x20]; + int ret;
imx_iomux_v3_setup_multiple_pads(wdog_pads, ARRAY_SIZE(wdog_pads));
@@ -250,5 +253,14 @@ int board_late_init(void) */ clrsetbits_le16(&wdog->wcr, 0, 0x10);
+ /* Set serial# standard environment variable based on OTP settings */ + ret = warp7_get_serialid(&serial_id); + if (ret) + printf("error %d reading from serial# OTP fuse\n", ret); + + snprintf(serial_string, sizeof(serial_string), "WaRP7-0x%016llx", + serial_id); + env_set("serial#", serial_string); + return 0; }

Hi Bryan,
On Tue, Mar 13, 2018 at 9:00 AM, Bryan O'Donoghue bryan.odonoghue@linaro.org wrote:
/* Set serial# standard environment variable based on OTP settings */
ret = warp7_get_serialid(&serial_id);
if (ret)
printf("error %d reading from serial# OTP fuse\n", ret);
Isn't a 'return ret' missing here?
participants (2)
-
Bryan O'Donoghue
-
Fabio Estevam