summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Krummenacher <max.krummenacher@toradex.com>2018-09-06 18:05:09 +0200
committerMax Krummenacher <max.krummenacher@toradex.com>2018-11-29 11:19:09 +0100
commit906059a7abf413bc165101812fb916743dd8eee7 (patch)
tree00c855ed9bca53766091486572df6b0e90307aa0
parent7e0886f0a114bf3e81294a5b392855548a3081e8 (diff)
pf8100: add pmic fusing command
Note that this requires the SCFW in a version which provides access to the PMIC I2C. Something which the regular SCFW should not do. Signed-off-by: Max Krummenacher <max.krummenacher@toradex.com> (cherry picked from commit 582a98a218dab6ac5a13ebd1cbd7a16e4b8305f3)
-rw-r--r--board/toradex/colibri-imx8qxp/Kconfig8
-rw-r--r--board/toradex/colibri-imx8qxp/Makefile1
-rw-r--r--board/toradex/colibri-imx8qxp/pf8100.c328
-rw-r--r--board/toradex/colibri-imx8qxp/pf8100_otp.inc93
4 files changed, 430 insertions, 0 deletions
diff --git a/board/toradex/colibri-imx8qxp/Kconfig b/board/toradex/colibri-imx8qxp/Kconfig
index 340fe72816..387c00b62b 100644
--- a/board/toradex/colibri-imx8qxp/Kconfig
+++ b/board/toradex/colibri-imx8qxp/Kconfig
@@ -25,6 +25,14 @@ config TDX_CFG_BLOCK_PART
config TDX_CFG_BLOCK_OFFSET
default "-512"
+config TDX_CMD_IMX_MFGR
+ bool "Enable factory testing commands for Toradex Colibri iMX8QXP modules"
+ help
+ This adds the commands
+ pf8100_otp_prog - Program the OTP fuses on the PMIC PF8100
+ If executed on already fused modules it doesn't change any fuse setting.
+ default y
+
source "board/toradex/common/Kconfig"
endif
diff --git a/board/toradex/colibri-imx8qxp/Makefile b/board/toradex/colibri-imx8qxp/Makefile
index 9e85b99179..e0ffa88e4f 100644
--- a/board/toradex/colibri-imx8qxp/Makefile
+++ b/board/toradex/colibri-imx8qxp/Makefile
@@ -5,3 +5,4 @@
#
obj-y += colibri-imx8qxp.o
+obj-$(CONFIG_TDX_CMD_IMX_MFGR) += pf8100.o
diff --git a/board/toradex/colibri-imx8qxp/pf8100.c b/board/toradex/colibri-imx8qxp/pf8100.c
new file mode 100644
index 0000000000..bc67538eb8
--- /dev/null
+++ b/board/toradex/colibri-imx8qxp/pf8100.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2014-2016, Toradex AG
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * Helpers for Freescale PMIC PF8100
+*/
+
+#include <common.h>
+#include <i2c.h>
+#include <asm/imx-common/sci/sci.h>
+#include <asm/arch/imx8-pins.h>
+#include <asm/gpio.h>
+#include <asm/arch/iomux.h>
+
+#include "pf8100_otp.inc"
+
+/* Register Addresses */
+#define PF8100_DEVICEID 0x00
+#define PF8100_REVID 0x01
+#define PF8100_EMREV 0x02
+#define PF8100_PROGID 0x03
+#define PF8100_PAGE_SELECT 0x9f
+
+/* output some informational messages, return if device is programmed */
+/* i.e. 0: unprogrammed (progid=0xfff, 1: programmed */
+unsigned pmic_init(void);
+
+/* programmes OTP fuses to values required on a Toradex Apalis iMX6 */
+int pf8100_prog(void);
+
+/* define for PMIC register dump */
+#define DEBUG
+
+/* Fuse the PMIC in production
+ *
+ * TBBEN: ADC_IN2 LSIO.GPIO1.IO12 on module
+ * ProgVolt: SC_P_USB_SS3_TC1_GPIO4_IO04 SODIMM 133
+*/
+#define GPIO_PAD_CTRL ((SC_PAD_CONFIG_NORMAL << PADRING_CONFIG_SHIFT) | (SC_PAD_ISO_OFF << PADRING_LPCONFIG_SHIFT) \
+ | (SC_PAD_28FDSOI_DSE_DV_HIGH << PADRING_DSE_SHIFT) | (SC_PAD_28FDSOI_PS_PU << PADRING_PULL_SHIFT))
+static iomux_cfg_t const pmic_prog_pads[] = {
+ SC_P_ADC_IN2 | MUX_MODE_ALT(4) | MUX_PAD_CTRL(GPIO_PAD_CTRL),
+# define PMIC_TBBEN IMX_GPIO_NR(1, 12)
+ SC_P_USB_SS3_TC1 | MUX_MODE_ALT(4) | MUX_PAD_CTRL(GPIO_PAD_CTRL),
+# define PMIC_PROG_VOLTAGE IMX_GPIO_NR(4, 4)
+};
+
+
+int pmic_i2c_read(unsigned reg, unsigned *val)
+{
+ sc_err_t err;
+ err = sc_misc_set_control(SC_IPC_CH, SC_R_BOARD_R0, SC_C_PMIC_I2C_READ_REG, reg);
+ err |= sc_misc_get_control(SC_IPC_CH, SC_R_BOARD_R0, SC_C_PMIC_I2C, val);
+ return err != SC_ERR_NONE;
+}
+
+int pmic_i2c_write(unsigned reg, unsigned val)
+{
+ sc_err_t err;
+ val = (val & 0xff) | ((reg & 0xff) << 8);
+ err = sc_misc_set_control(SC_IPC_CH, SC_R_BOARD_R0, SC_C_PMIC_I2C, val);
+ return err != SC_ERR_NONE;
+}
+
+unsigned pmic_init(void)
+{
+ unsigned devid, revid, emrev, progid;
+
+ puts("PMIC: ");
+
+ /* get device ident */
+ if (pmic_i2c_read(PF8100_DEVICEID, &devid)) {
+ puts("i2c pmic devid read failed\n");
+ return 0;
+ }
+ if (pmic_i2c_read(PF8100_REVID, &revid) < 0) {
+ puts("i2c pmic revid read failed\n");
+ return 0;
+ }
+ if (pmic_i2c_read(PF8100_EMREV, &emrev)) {
+ puts("i2c pmic emrev read failed\n");
+ return 0;
+ }
+ if (pmic_i2c_read(PF8100_PROGID, &progid) < 0) {
+ puts("i2c pmic prog id read failed\n");
+ return 0;
+ }
+
+ progid |= (emrev & 0xf0) << 4;
+ printf("device id: 0x%.2x, revision id: 0x%.2x, emrev %x, prog id %.3x\n",
+ devid, revid, emrev & 7, progid);
+
+
+#ifdef DEBUG
+ {
+ unsigned i, j, val;
+
+ /* Set TBBEN high */
+ gpio_request(PMIC_TBBEN, "PMIC_TBBEN");
+ gpio_direction_output(PMIC_TBBEN, 1);
+ mdelay(100);
+
+ /* Page select 1 */
+ pmic_i2c_write(PF8100_PAGE_SELECT, 0);
+ mdelay(100);
+ pmic_i2c_read(PF8100_PAGE_SELECT, &val);
+ printf(" readback is %x\n", val);
+
+ for (i = 0; i < 16; i++)
+ printf("\t%x", i);
+ for (j = 0; j < 0xa0; ) {
+ printf("\n%2x", j);
+ for (i = 0; i < 16; i++) {
+ pmic_i2c_read(j+i, &val);
+ printf("\t%2x", val);
+ }
+ j += 0x10;
+ }
+
+ printf("\nEXT Page 1");
+ /* Page select 1 */
+ pmic_i2c_write(PF8100_PAGE_SELECT, 1);
+ mdelay(100);
+ pmic_i2c_read(PF8100_PAGE_SELECT, &val);
+ printf(" readback is %x", val);
+
+ for (j = 0xa0; j < 0xf0; ) {
+ printf("\n%2x", j);
+ for (i = 0; i < 16; i++) {
+ pmic_i2c_read(j+i, &val);
+ printf("\t%2x", val);
+ }
+ j += 0x10;
+ }
+
+ printf("\nEXT Page 2");
+ /* Page select 2 */
+ pmic_i2c_write(PF8100_PAGE_SELECT, 2);
+ mdelay(100);
+ pmic_i2c_read(PF8100_PAGE_SELECT, &val);
+ printf(" readback is %x", val);
+
+ for (j = 0xa0; j < 0xc0; ) {
+ printf("\n%2x", j);
+ for (i = 0; i < 16; i++) {
+ pmic_i2c_read(j+i, &val);
+ printf("\t%2x", val);
+ }
+ j += 0x10;
+ }
+ printf("\n");
+
+ /* Set TBBEN low */
+ gpio_direction_output(PMIC_TBBEN, 0);
+ mdelay(100);
+ }
+#endif
+
+ return (progid != 0xfff);
+}
+
+int pf8100_prog(void)
+{
+ unsigned int err, i, read;
+ int ret = CMD_RET_SUCCESS;
+
+ if (pmic_init() == 1) {
+ printf("PMIC already programmed, exiting\n");
+ return CMD_RET_FAILURE;
+ }
+ /* set up gpio to manipulate vprog, initially off */
+ imx8_iomux_setup_multiple_pads(pmic_prog_pads, ARRAY_SIZE(pmic_prog_pads));
+ gpio_request(PMIC_TBBEN, "PMIC_TBBEN");
+ gpio_request(PMIC_PROG_VOLTAGE, "PMIC_PROG_VOLT");
+
+ /* Set TBBEN high */
+ gpio_direction_output(PMIC_TBBEN, 1);
+ mdelay(100);
+
+ /* Page select 2 */
+ if (pmic_i2c_write(PF8100_PAGE_SELECT, 2)) {
+ printf("PMIC i2c page select failed\n");
+ ret = CMD_RET_FAILURE;
+ goto out1;
+ }
+
+ err = pmic_i2c_read(0xA7, &read);
+ if (err | read) {
+ printf("PMIC i2c page 2, A7 != 0\n");
+ ret = CMD_RET_FAILURE;
+ goto out1;
+ }
+
+ /* PROG_VOLTAGE at 8 V */
+ gpio_direction_output(PMIC_PROG_VOLTAGE, 1);
+ mdelay(200);
+
+ /* Page select 1 */
+ if (pmic_i2c_write(PF8100_PAGE_SELECT, 1)) {
+ printf("PMIC i2c page select failed\n");
+ ret = CMD_RET_FAILURE;
+ goto out2;
+ }
+
+ /* write all OTP mirror values */
+ for(i = 0; i < ARRAY_SIZE(pf8100fuses); i++)
+ if (pmic_i2c_write(pf8100fuses[i].address, pf8100fuses[i].data)) {
+ printf("PMIC i2c OTP write failed at 0x%.2x\n", pf8100fuses[i].address);
+ ret = CMD_RET_FAILURE;
+ goto out2;
+ }
+ err = pmic_i2c_write(0x9F,0x02); //4.Write 0x9F -> 0x02 // Set register Page to OTP controller page
+ err |= pmic_i2c_write(0xA0,0x80); //5.Write 0xA0 -> 0x80 // Set OTP Controller in Idle state
+ err |= pmic_i2c_write(0xA1,0x00); //6.Write 0xA1 -> 0x00 // Set Start Address to 0x00 in Fuse Array
+ err |= pmic_i2c_write(0XA2,0x42); //7.Write 0XA2 -> 0x42 // Set Stop Address to 0x48 in Fuse Array
+ err |= pmic_i2c_write(0xA8,0x0F); //8.Write 0xA8 -> 0x0F // Set the Maximum number of programming attempts
+ err |= pmic_i2c_write(0xA9,0x02); //9.Write 0xA9 -> 0x02 // Set MRR voltage regulator
+ err |= pmic_i2c_write(0xAA,0x07); //10.Write 0xAA -> 0x07 //Write SC Reference 1
+ err |= pmic_i2c_write(0xAB,0x09); //11.Write 0xAB -> 0x09 // Write I Reference 1
+ err |= pmic_i2c_write(0xAC,0x07); //12.Write 0xAC -> 0x07 // Write SC Reference 2
+ err |= pmic_i2c_write(0xAD,0x35); //13.Write 0xAD -> 0x35 // Write I Reference 2
+ err |= pmic_i2c_write(0xAE,0x04); //14.Write 0xAE -> 0x04 // Set the High byte of the MR_TEST register
+ err |= pmic_i2c_write(0xAF,0x42); //15.Write 0xAF -> 0x42 // Set the Low byte of the MR_TEST register
+ err |= pmic_i2c_write(0xB0,0x00); //16.Write 0xB0 -> 0x00 // Set the High byte of the MREF_TEST register
+ err |= pmic_i2c_write(0xB1,0x00); //17. Write 0xB1 -> 0x00 // Set the Low byte of the MREF_TEST register
+
+ //Calculate CRC to be written to OTP
+ err |= pmic_i2c_write(0xA0,0xA5); //18.Write 0xA0 -> 0xA5 // Calculate CRC values
+ err |= pmic_i2c_write(0xA0,0xA4); //19.Write 0xA0 -> 0xA4 // Test CRC Values
+
+ //Verify CRC is generated -optional (CRC value is dependent of the OTP configuration)
+ err |= pmic_i2c_write(0x9F,0x01); //20.Write 0x9F -> 0x01 // Page select 01
+ err |= pmic_i2c_read(0xE7,&read); //21.Read -> 0xE7 // Must be different than 0x00
+ if (read == 0x00)
+ printf("ERROR register 0xE7 = 0x0\r\n");
+ err |= pmic_i2c_read(0xE8,&read); //22.Read -> 0xE8 // Must be different than 0x00
+ if (read == 0x00)
+ printf("ERROR register 0xE8 = 0x0\r\n");
+
+ err |= pmic_i2c_write(0x9F,0x02); //24.Write 0x9F -> 0x02 // Page select 02
+ err |= pmic_i2c_read(0xA7,&read);
+ printf("Register 0xA7 = 0x%X\r\n",read);
+ err |= pmic_i2c_read(0xA6,&read);
+ printf("Register 0xA6 = 0x%X\r\n",read);
+ if (err) {
+ printf("Programming preparation failed! Don't starting the programming step\n");
+ ret = CMD_RET_FAILURE;
+ goto out2;
+ }
+
+#if 0
+ //Lets burn the fuses
+ err |= pmic_i2c_write(0x9F,0x02); //24.Write 0x9F -> 0x02 // Page select 02
+
+ err |= pmic_i2c_write(0xA0,0x96); //25.Write 0xA0 -> 0x96
+ read = 0xFF;
+ mdelay(200);
+ do {
+ err |= pmic_i2c_read(0xA6,&read); //26.Read -> 0xA0 // if Bit 0 = 1 Controller is still busy.
+ } while((read & 0x1) == 0x1); //27.Loop step 22 until bit 0 = 0 (Controller is done)
+
+ err |= pmic_i2c_write(0xA2,0xFF); //28.Write A2 -> 0xFF // Set mask to write to full byte.
+ err |= pmic_i2c_write(0xA1,0xFC); //29.Write A1 -> 0xFC // Set single Write Address to Write Protect(WP) Low Byte
+ err |= pmic_i2c_write(0xA3,0xAA); //30.Write A3 -> 0xAA // Set Data for single write on Write Protect(WP) Low Byte
+ err |= pmic_i2c_write(0xA0,0x87); //31.Write A0 -> 0x87 // Burn Write Protect low Byte
+ err |= pmic_i2c_write(0xA1,0xFD); //32.Write A1 -> 0xFD // Set single Write Address to Write Protect high Byte
+ err |= pmic_i2c_write(0xA3,0x55); //33.Write A3 -> 0x55 // Set Data for single write on Write Protect high Byte
+ err |= pmic_i2c_write(0xA0,0x87); //34.Write A0 -> 0x87 // Burn Write Protect low Byte
+ err |= pmic_i2c_write(0xA1,0xFE); //35.Write A1 -> 0xFE // Set single Write Address to Boot Enable (BE)Low Byte
+ err |= pmic_i2c_write(0xA3,0xAA); //36.Write A3 -> 0xAA // Set Data for single write to Boot enable(BE) Low Byte
+ err |= pmic_i2c_write(0xA0,0x87); //37.Write A0 -> 0x87 // Burn Boot enable Low Byte
+ err |= pmic_i2c_write(0xA1,0xFF); //38.Write A1 -> 0xFF // Set single Write Address to Boot Enable High Byte
+ err |= pmic_i2c_write(0xA3,0x55); //39.Write A3 -> 0x55 // Set Data for single write Boot enable High Byte
+ err |= pmic_i2c_write(0xA0,0x87); //40.Write A0 -> 0x87 // Burn Boot enable High Byte
+ mdelay(200);
+
+ //41.Apply VDDOTP = 1.5V (but should be 0V)
+ gpio_direction_output(PMIC_PROG_VOLTAGE, 0);
+ mdelay(100);
+
+ err |= pmic_i2c_read(0x0,&read); //
+ printf("Device ID = 0x%X\r\n",read);
+ err |= pmic_i2c_write(0x9F,0x2);
+ err |= pmic_i2c_read(0xA7,&read); //
+ printf("SECT_STATUS = 0x%X\r\n",read);
+ err |= pmic_i2c_read(0xA6,&read); //
+ printf("FSTATUS = 0x%X\r\n",read);
+
+ if (err) {
+ printf("Programming failed!\n");
+ ret = CMD_RET_FAILURE;
+ goto out2;
+ }
+
+#endif
+out2:
+ gpio_direction_output(PMIC_PROG_VOLTAGE, 0);
+ mdelay(100);
+
+out1:
+ /* Set TBBEN low */
+ gpio_direction_output(PMIC_TBBEN, 0);
+ mdelay(100);
+
+ return ret;
+}
+
+int do_pf8100_prog(cmd_tbl_t *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ int ret;
+ puts("Programming PMIC OTP...");
+ ret = pf8100_prog();
+ if (ret == CMD_RET_SUCCESS)
+ puts("done.\n");
+ else
+ puts("failed.\n");
+ return ret;
+}
+
+U_BOOT_CMD(
+ pf8100_otp_prog, 1, 0, do_pf8100_prog,
+ "Program the OTP fuses on the PMIC PF8100",
+ ""
+);
diff --git a/board/toradex/colibri-imx8qxp/pf8100_otp.inc b/board/toradex/colibri-imx8qxp/pf8100_otp.inc
new file mode 100644
index 0000000000..a71291185a
--- /dev/null
+++ b/board/toradex/colibri-imx8qxp/pf8100_otp.inc
@@ -0,0 +1,93 @@
+// Device Configuration: PF8100
+// Customer: Toradex AG
+// Program: Colibri iMX8X
+// Sample marking: PC33PF8100A0ES
+// Date: 23.08.2018
+// Time: 16:56:36
+// Generated from Spreadsheet Revision: Rev 2.5
+
+// converted by:
+// - dos2unix A0_PF8100_S_HEX.txt
+// - sed 's/^\([0-9A-Fa-f][0-9A-Fa-f]\),.\(..\)/\t{0x\1, 0x\2},/' A0_PF8100_S_HEX.txt > otp.h
+// - added struct:
+
+typedef struct
+{
+ unsigned address;
+ unsigned data;
+} t_pf8100data;
+
+t_pf8100data pf8100fuses[] =
+{
+ {0xA0, 0x00},
+ {0xA1, 0x00},
+ {0xA2, 0x02},
+ {0xA3, 0x45},
+ {0xA4, 0x01},
+ {0xA5, 0x80},
+ {0xA6, 0x0B},
+ {0xA7, 0x00},
+ {0xA8, 0x30},
+ {0xA9, 0x07},
+ {0xAA, 0xAF},
+ {0xAB, 0x00},
+ {0xAC, 0x0F},
+ {0xAD, 0x00},
+ {0xAE, 0x81},
+ {0xAF, 0x02},
+ {0xB0, 0x15},
+ {0xB1, 0x00},
+ {0xB2, 0x60},
+ {0xB3, 0x01},
+ {0xB4, 0x53},
+ {0xB5, 0x3A},
+ {0xB6, 0x60},
+ {0xB7, 0x01},
+ {0xB8, 0x53},
+ {0xB9, 0x1A},
+ {0xBA, 0x70},
+ {0xBB, 0x00},
+ {0xBC, 0x53},
+ {0xBD, 0x02},
+ {0xBE, 0x70},
+ {0xBF, 0x00},
+ {0xC0, 0x53},
+ {0xC1, 0x0A},
+ {0xC2, 0x70},
+ {0xC3, 0x03},
+ {0xC4, 0x53},
+ {0xC5, 0x12},
+ {0xC6, 0xB1},
+ {0xC7, 0x03},
+ {0xC8, 0x53},
+ {0xC9, 0x22},
+ {0xCA, 0x15},
+ {0xCB, 0x00},
+ {0xCC, 0x53},
+ {0xCD, 0x22},
+ {0xCE, 0x52},
+ {0xCF, 0x01},
+ {0xD0, 0x04},
+ {0xD1, 0x5B},
+ {0xD2, 0x03},
+ {0xD3, 0x24},
+ {0xD4, 0x5B},
+ {0xD5, 0x00},
+ {0xD6, 0x05},
+ {0xD7, 0x5B},
+ {0xD8, 0x03},
+ {0xD9, 0x05},
+ {0xDA, 0x02},
+ {0xDB, 0x00},
+ {0xDC, 0x00},
+ {0xDD, 0x00},
+ {0xDE, 0x00},
+ {0xDF, 0x00},
+ {0xE0, 0x00},
+ {0xE1, 0x00},
+ {0xE2, 0x00},
+ {0xE3, 0x01},
+ {0xE4, 0x00},
+ {0xE5, 0x00},
+ {0xE6, 0x00},
+};