[PATCH v1] misc: nuvoton: Add NPCM7xx otp controller driver
Jim Liu
jim.t90615 at gmail.com
Tue Jun 7 10:33:54 CEST 2022
Add Nuvoton BMC npcm750 otp driver
Signed-off-by: Jim Liu <JJLIU0 at nuvoton.com>
---
arch/arm/include/asm/arch-npcm7xx/otp.h | 90 +++++
drivers/misc/Kconfig | 9 +
drivers/misc/Makefile | 1 +
drivers/misc/npcm_otp.c | 512 ++++++++++++++++++++++++
4 files changed, 612 insertions(+)
create mode 100644 arch/arm/include/asm/arch-npcm7xx/otp.h
create mode 100644 drivers/misc/npcm_otp.c
diff --git a/arch/arm/include/asm/arch-npcm7xx/otp.h b/arch/arm/include/asm/arch-npcm7xx/otp.h
new file mode 100644
index 0000000000..11d1e8550c
--- /dev/null
+++ b/arch/arm/include/asm/arch-npcm7xx/otp.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _NPCM_OTP_H_
+#define _NPCM_OTP_H_
+
+#ifdef CONFIG_ARCH_NPCM8XX
+enum {
+ NPCM_KEY_SA = 0,
+ NPCM_FUSE_SA = 0,
+ NPCM_NUM_OF_SA = 1
+};
+#else
+enum {
+ NPCM_KEY_SA = 0,
+ NPCM_FUSE_SA = 1,
+ NPCM_NUM_OF_SA = 2
+};
+#endif
+
+struct npcm_otp_regs {
+ unsigned int fst;
+ unsigned int faddr;
+ unsigned int fdata;
+ unsigned int fcfg;
+ unsigned int fustrap_fkeyind;
+ unsigned int fctl;
+};
+
+#define FST_RDY BIT(0)
+#define FST_RDST BIT(1)
+#define FST_RIEN BIT(2)
+
+#ifdef CONFIG_ARCH_NPCM8XX
+#define FADDR_BYTEADDR(addr) ((addr) << 3)
+#define FADDR_BITPOS(pos) ((pos) << 0)
+#define FADDR_VAL(addr, pos) (FADDR_BITPOS(pos) | FADDR_BYTEADDR(addr))
+#define FADDR_IN_PROG BIT(16)
+#else
+#define FADDR_BYTEADDR(addr) ((addr) << 0)
+#define FADDR_BITPOS(pos) ((pos) << 10)
+#define FADDR_VAL(addr, pos) (FADDR_BYTEADDR(addr) | FADDR_BITPOS(pos))
+#define FADDR_IN_PROG BIT(16)
+#endif
+
+#define FDATA_MASK (0xff)
+
+#define FUSTRAP_O_SECBOOT BIT(23)
+
+#define FCFG_FDIS BIT(31)
+
+#define FKEYIND_KVAL BIT(0)
+#define FKEYIND_KSIZE_MASK (0x00000070)
+#define FKEYIND_KSIZE_128 (0x40)
+#define FKEYIND_KSIZE_192 (0x50)
+#define FKEYIND_KSIZE_256 (0x60)
+#define FKEYIND_KIND_MASK (0x000c0000)
+#define FKEYIND_KIND_KEY(indx) ((indx) << 18)
+
+// Program cycle initiation values (sequence of two adjacent writes)
+#define PROGRAM_ARM 0x1
+#define PROGRAM_INIT 0xBF79E5D0
+
+#define OTP2_BASE 0xF018A000
+#define FUSTRAP (OTP2_BASE + 0x10)
+
+// Read cycle initiation value
+#define READ_INIT 0x02
+
+// Value to clean FDATA contents
+#define FDATA_CLEAN_VALUE 0x01
+
+#ifdef CONFIG_ARCH_NPCM8XX
+#define NPCM_OTP_ARR_BYTE_SIZE 8192
+#else
+#define NPCM_OTP_ARR_BYTE_SIZE 1024
+#endif
+
+#define MIN_PROGRAM_PULSES 4
+#define MAX_PROGRAM_PULSES 20
+#define NPCM_OTP_ARR_BYTE_SIZE 1024
+
+int fuse_prog_image(u32 bank, uintptr_t address);
+int fuse_program_data(u32 bank, u32 word, u8 *data, u32 size);
+int npcm_otp_select_key(u8 key_index);
+bool npcm_otp_is_fuse_array_disabled(u32 arr);
+void npcm_otp_nibble_parity_ecc_encode(u8 *datain, u8 *dataout, u32 size);
+void npcm_otp_majority_rule_ecc_encode(u8 *datain, u8 *dataout, u32 size);
+void npcm_arch_preboot_os(void);
+
+#endif
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 85ae7f62e9..4c61b55ba4 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -291,6 +291,15 @@ config SPL_MXC_OCOTP
Programmable memory pages, that are stored on some
Freescale i.MX processors, in SPL.
+config NPCM_OTP
+ bool "Nnvoton NPCM BMC On-Chip OTP Memory Support"
+ depends on (ARM && ARCH_NPCM)
+ default n
+ help
+ Support NPCM BMC OTP memory (fuse).
+ To compile this driver as a module, choose M here: the module
+ will be called npcm_otp.
+
config NUVOTON_NCT6102D
bool "Enable Nuvoton NCT6102D Super I/O driver"
help
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7a6047f64f..29fbf5640d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_LED_STATUS_GPIO) += gpio_led.o
obj-$(CONFIG_MPC83XX_SERDES) += mpc83xx_serdes.o
obj-$(CONFIG_$(SPL_)MXC_OCOTP) += mxc_ocotp.o
obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o
+obj-$(CONFIG_NPCM_OTP) += npcm_otp.o
obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o
obj-$(CONFIG_P2SB) += p2sb-uclass.o
obj-$(CONFIG_PCA9551_LED) += pca9551_led.o
diff --git a/drivers/misc/npcm_otp.c b/drivers/misc/npcm_otp.c
new file mode 100644
index 0000000000..304910888b
--- /dev/null
+++ b/drivers/misc/npcm_otp.c
@@ -0,0 +1,512 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2021 Nuvoton Technology Corp.
+ */
+
+#include <clk.h>
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fuse.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <asm/arch/otp.h>
+
+struct npcm_otp_priv {
+ struct npcm_otp_regs *regs[2];
+};
+
+static struct npcm_otp_priv *otp_priv;
+
+/*----------------------------------------------------------------------------*/
+/* Function: npcm_otp_check_inputs */
+/* */
+/* Parameters: arr - fuse array number to check */
+/* word - fuse word (offset) to check */
+/* Returns: int */
+/* Side effects: */
+/* Description: Checks is arr and word are illegal and do not exceed */
+/* their range. Return 0 if they are legal, -1 if not */
+/*----------------------------------------------------------------------------*/
+static int npcm_otp_check_inputs(u32 arr, u32 word)
+{
+ if (arr >= NPCM_NUM_OF_SA) {
+ if (IS_ENABLED(CONFIG_ARCH_NPCM8XX))
+ printf("\nError: npcm8XX otp includs only one bank: 0\n");
+ if (IS_ENABLED(CONFIG_ARCH_NPCM7XX))
+ printf("\nError: npcm7XX otp includs only two banks: 0 and 1\n");
+ return -1;
+ }
+
+ if (word >= NPCM_OTP_ARR_BYTE_SIZE) {
+ printf("\nError: npcm otp array comprises only %d bytes, numbered from 0 to %d\n",
+ NPCM_OTP_ARR_BYTE_SIZE, NPCM_OTP_ARR_BYTE_SIZE - 1);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Function: npcm_otp_wait_for_otp_ready */
+/* */
+/* Parameters: array - fuse array to wait for */
+/* Returns: int */
+/* Side effects: */
+/* Description: Initialize the Fuse HW module. */
+/*----------------------------------------------------------------------------*/
+static int npcm_otp_wait_for_otp_ready(u32 arr, u32 timeout)
+{
+ struct npcm_otp_regs *regs = otp_priv->regs[arr];
+ u32 time = timeout;
+
+ /*------------------------------------------------------------------------*/
+ /* check parameters validity */
+ /*------------------------------------------------------------------------*/
+ if (arr > NPCM_FUSE_SA)
+ return -EINVAL;
+
+ while (--time > 1) {
+ if (readl(®s->fst) & FST_RDY) {
+ /* fuse is ready, clear the status. */
+ writel(readl(®s->fst) | FST_RDST, ®s->fst);
+ return 0;
+ }
+ }
+
+ /* try to clear the status in case it was set */
+ writel(readl(®s->fst) | FST_RDST, ®s->fst);
+
+ return -EINVAL;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Function: npcm_otp_read_byte */
+/* */
+/* Parameters: arr - Storage Array type [input]. */
+/* addr - Byte-address to read from [input]. */
+/* data - Pointer to result [output]. */
+/* Returns: none */
+/* Side effects: */
+/* Description: Read 8-bit data from an OTP storage array. */
+/*----------------------------------------------------------------------------*/
+static void npcm_otp_read_byte(u32 arr, u32 addr, u8 *data)
+{
+ struct npcm_otp_regs *regs = otp_priv->regs[arr];
+
+ /* Wait for the Fuse Box Idle */
+ npcm_otp_wait_for_otp_ready(arr, 0xDEADBEEF);
+
+ /* Configure the byte address in the fuse array for read operation */
+ writel(FADDR_VAL(addr, 0), ®s->faddr);
+
+ /* Initiate a read cycle */
+ writel(READ_INIT, ®s->fctl);
+
+ /* Wait for read operation completion */
+ npcm_otp_wait_for_otp_ready(arr, 0xDEADBEEF);
+
+ /* Read the result */
+ *data = readl(®s->fdata) & FDATA_MASK;
+
+ /* Clean FDATA contents to prevent unauthorized software from reading
+ * sensitive information
+ */
+ writel(FDATA_CLEAN_VALUE, ®s->fdata);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Function: npcm_otp_bit_is_programmed */
+/* */
+/* Parameters: arr - Storage Array type [input]. */
+/* byte_offset - Byte offset in array [input]. */
+/* bit_offset - Bit offset in byte [input]. */
+/* Returns: Nonzero if bit is programmed, zero otherwise. */
+/* Side effects: */
+/* Description: Check if a bit is programmed in an OTP storage array. */
+/*----------------------------------------------------------------------------*/
+static bool npcm_otp_bit_is_programmed(u32 arr,
+ u32 byte_offset, u8 bit_offset)
+{
+ u32 data = 0;
+
+ /* Read the entire byte you wish to program */
+ npcm_otp_read_byte(arr, byte_offset, (u8 *)&data);
+
+ /* Check whether the bit is already programmed */
+ if (data & (1 << bit_offset))
+ return true;
+
+ return false;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Function: npcm_otp_program_bit */
+/* */
+/* Parameters: arr - Storage Array type [input]. */
+/* byte)offset - Byte offset in array [input]. */
+/* bit_offset - Bit offset in byte [input]. */
+/* Returns: int */
+/* Side effects: */
+/* Description: Program (set to 1) a bit in an OTP storage array. */
+/*----------------------------------------------------------------------------*/
+static int npcm_otp_program_bit(u32 arr, u32 byte_offset,
+ u8 bit_offset)
+{
+ struct npcm_otp_regs *regs = otp_priv->regs[arr];
+ int count;
+ u8 read_data;
+
+ /* Wait for the Fuse Box Idle */
+ npcm_otp_wait_for_otp_ready(arr, 0xDEADBEEF);
+
+ /* Make sure the bit is not already programmed */
+ if (npcm_otp_bit_is_programmed(arr, byte_offset, bit_offset))
+ return 0;
+
+ /* Configure the bit address in the fuse array for program operation */
+ writel(FADDR_VAL(byte_offset, bit_offset), ®s->faddr);
+ writel(readl(®s->faddr) | FADDR_IN_PROG, ®s->faddr);
+
+ // program up to MAX_PROGRAM_PULSES
+ for (count = 1; count <= MAX_PROGRAM_PULSES; count++) {
+ /* Initiate a program cycle */
+ writel(PROGRAM_ARM, ®s->fctl);
+ writel(PROGRAM_INIT, ®s->fctl);
+
+ /* Wait for program operation completion */
+ npcm_otp_wait_for_otp_ready(arr, 0xDEADBEEF);
+
+ // after MIN_PROGRAM_PULSES start verifying the result
+ if (count >= MIN_PROGRAM_PULSES) {
+ /* Initiate a read cycle */
+ writel(READ_INIT, ®s->fctl);
+
+ /* Wait for read operation completion */
+ npcm_otp_wait_for_otp_ready(arr, 0xDEADBEEF);
+
+ /* Read the result */
+ read_data = readl(®s->fdata) & FDATA_MASK;
+
+ /* If the bit is set the sequence ended correctly */
+ if (read_data & (1 << bit_offset))
+ break;
+ }
+ }
+
+ // check if programmking failed
+ if (count > MAX_PROGRAM_PULSES) {
+ printf("program fail\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Clean FDATA contents to prevent unauthorized software from reading
+ * sensitive information
+ */
+ writel(FDATA_CLEAN_VALUE, ®s->fdata);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Function: npcm_otp_program_byte */
+/* */
+/* Parameters: arr - Storage Array type [input]. */
+/* byte_offset - Byte offset in array [input]. */
+/* value - Byte to program [input]. */
+/* Returns: int */
+/* Side effects: */
+/* Description: Program (set to 1) a given byte's relevant bits in an */
+/* OTP storage array. */
+/*----------------------------------------------------------------------------*/
+static int npcm_otp_program_byte(u32 arr, u32 byte_offset,
+ u8 value)
+{
+ int status = 0;
+ unsigned int i;
+ u8 data = 0;
+ int rc;
+
+ rc = npcm_otp_check_inputs(arr, byte_offset);
+ if (rc != 0)
+ return rc;
+
+ /* Wait for the Fuse Box Idle */
+ npcm_otp_wait_for_otp_ready(arr, 0xDEADBEEF);
+
+ /* Read the entire byte you wish to program */
+ npcm_otp_read_byte(arr, byte_offset, &data);
+
+ /* In case all relevant bits are already programmed - nothing to do */
+ if ((~data & value) == 0)
+ return status;
+
+ /* Program unprogrammed bits. */
+ for (i = 0; i < 8; i++) {
+ if (value & (1 << i)) {
+ /* Program (set to 1) the relevant bit */
+ int last_status = npcm_otp_program_bit(arr, byte_offset, (u8)i);
+
+ if (last_status != 0)
+ status = last_status;
+ }
+ }
+ return status;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Function: npcm_otp_is_fuse_array_disabled */
+/* */
+/* Parameters: arr - Storage Array type [input]. */
+/* Returns: bool */
+/* Side effects: */
+/* Description: Return true if access to the first 2048 bits of the */
+/* specified fuse array is disabled, false if not */
+/*----------------------------------------------------------------------------*/
+bool npcm_otp_is_fuse_array_disabled(u32 arr)
+{
+ struct npcm_otp_regs *regs = otp_priv->regs[arr];
+
+ return (readl(®s->fcfg) & FCFG_FDIS) != 0;
+}
+
+int npcm_otp_select_key(u8 key_index)
+{
+ struct npcm_otp_regs *regs = otp_priv->regs[NPCM_KEY_SA];
+ u32 idx = 0;
+ u32 time = 0xDAEDBEEF;
+
+ if (key_index >= 4)
+ return -1;
+
+ /* Do not destroy ECCDIS bit */
+ idx = readl(®s->fustrap_fkeyind);
+
+ /* Configure the key size */
+ idx &= ~FKEYIND_KSIZE_MASK;
+ idx |= FKEYIND_KSIZE_256;
+
+ /* Configure the key index (0 to 3) */
+ idx &= ~FKEYIND_KIND_MASK;
+ idx |= FKEYIND_KIND_KEY(key_index);
+
+ writel(idx, ®s->fustrap_fkeyind);
+
+ /* Wait for selection completetion */
+ while (--time > 1) {
+ if (readl(®s->fustrap_fkeyind) & FKEYIND_KVAL)
+ return 0;
+ udelay(1);
+ }
+
+ return -1;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Function: npcm_otp_nibble_parity_ecc_encode */
+/* */
+/* Parameters: datain - pointer to decoded data buffer */
+/* dataout - pointer to encoded data buffer (buffer size */
+/* should be 2 x dataout) */
+/* size - size of encoded data (decoded data x 2) */
+/* Returns: none */
+/* Side effects: */
+/* Description: Decodes the data according to nibble parity ECC scheme. */
+/* Size specifies the encoded data size. */
+/* Decodes whole bytes only */
+/*----------------------------------------------------------------------------*/
+void npcm_otp_nibble_parity_ecc_encode(u8 *datain, u8 *dataout, u32 size)
+{
+ u32 i, idx;
+ u8 E0, E1, E2, E3;
+
+ for (i = 0; i < (size / 2); i++) {
+ E0 = (datain[i] >> 0) & 0x01;
+ E1 = (datain[i] >> 1) & 0x01;
+ E2 = (datain[i] >> 2) & 0x01;
+ E3 = (datain[i] >> 3) & 0x01;
+
+ idx = i * 2;
+ dataout[idx] = datain[i] & 0x0f;
+ dataout[idx] |= (E0 ^ E1) << 4;
+ dataout[idx] |= (E2 ^ E3) << 5;
+ dataout[idx] |= (E0 ^ E2) << 6;
+ dataout[idx] |= (E1 ^ E3) << 7;
+
+ E0 = (datain[i] >> 4) & 0x01;
+ E1 = (datain[i] >> 5) & 0x01;
+ E2 = (datain[i] >> 6) & 0x01;
+ E3 = (datain[i] >> 7) & 0x01;
+
+ idx = i * 2 + 1;
+ dataout[idx] = (datain[i] & 0xf0) >> 4;
+ dataout[idx] |= (E0 ^ E1) << 4;
+ dataout[idx] |= (E2 ^ E3) << 5;
+ dataout[idx] |= (E0 ^ E2) << 6;
+ dataout[idx] |= (E1 ^ E3) << 7;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/* Function: npcm_otp_majority_rule_ecc_encode */
+/* */
+/* Parameters: datain - pointer to decoded data buffer */
+/* dataout - pointer to encoded data buffer (buffer size */
+/* should be 3 x dataout) */
+/* size - size of encoded data (decoded data x 3) */
+/* Returns: none */
+/* Side effects: */
+/* Description: Decodes the data according to Major Rule ECC scheme. */
+/* Size specifies the encoded data size. */
+/* Decodes whole bytes only */
+/*----------------------------------------------------------------------------*/
+void npcm_otp_majority_rule_ecc_encode(u8 *datain, u8 *dataout, u32 size)
+{
+ u32 byte;
+ u32 bit;
+ u8 bit_val;
+ u32 decoded_size = size / 3;
+
+ for (byte = 0; byte < decoded_size; byte++) {
+ for (bit = 0; bit < 8; bit++) {
+ bit_val = (datain[byte] >> bit) & 0x01;
+
+ if (bit_val) {
+ dataout[byte] |= (1 << bit);
+ dataout[decoded_size + byte] |= (1 << bit);
+ dataout[decoded_size * 2 + byte] |= (1 << bit);
+ } else {
+ dataout[byte] &= ~(1 << bit);
+ dataout[decoded_size + byte] &= ~(1 << bit);
+ dataout[decoded_size * 2 + byte] &= ~(1 << bit);
+ }
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/* Function: fuse_program_data */
+/* */
+/* Parameters: bank - Storage Array type [input]. */
+/* word - Byte offset in array [input]. */
+/* data - Pointer to data buffer to program. */
+/* size - Number of bytes to program. */
+/* Returns: none */
+/* Side effects: */
+/* Description: Programs the given byte array (size bytes) to the given */
+/* OTP storage array, starting from offset word. */
+/*----------------------------------------------------------------------------*/
+int fuse_program_data(u32 bank, u32 word, u8 *data, u32 size)
+{
+ u32 arr = (u32)bank;
+ u32 byte;
+ int rc;
+
+ rc = npcm_otp_check_inputs(bank, word + size - 1);
+ if (rc != 0)
+ return rc;
+
+ for (byte = 0; byte < size; byte++) {
+ u8 val;
+
+ val = data[byte];
+ if (val == 0) // optimization
+ continue;
+
+ rc = npcm_otp_program_byte(arr, word + byte, data[byte]);
+ if (rc != 0)
+ return rc;
+
+ // verify programming of every '1' bit
+ val = 0;
+ npcm_otp_read_byte((u32)bank, byte, &val);
+ if ((data[byte] & ~val) != 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+int fuse_prog_image(u32 bank, uintptr_t address)
+{
+ return fuse_program_data(bank, 0, (u8 *)address, NPCM_OTP_ARR_BYTE_SIZE);
+}
+
+int fuse_read(u32 bank, u32 word, u32 *val)
+{
+ int rc = npcm_otp_check_inputs(bank, word);
+
+ if (rc != 0)
+ return rc;
+
+ *val = 0;
+ npcm_otp_read_byte((u32)bank, word, (u8 *)val);
+
+ return 0;
+}
+
+int fuse_sense(u32 bank, u32 word, u32 *val)
+{
+ /* We do not support overriding */
+ return -EINVAL;
+}
+
+int fuse_prog(u32 bank, u32 word, u32 val)
+{
+ int rc;
+
+ rc = npcm_otp_check_inputs(bank, word);
+ if (rc != 0)
+ return rc;
+
+ return npcm_otp_program_byte(bank, word, (u8)val);
+}
+
+int fuse_override(u32 bank, u32 word, u32 val)
+{
+ /* We do not support overriding */
+ return -EINVAL;
+}
+
+static int npcm_otp_bind(struct udevice *dev)
+{
+ struct npcm_otp_regs *regs;
+
+ otp_priv = calloc(1, sizeof(struct npcm_otp_priv));
+ if (!otp_priv)
+ return -ENOMEM;
+
+ regs = dev_remap_addr_index(dev, 0);
+ if (!regs) {
+ printf("Cannot find reg address (arr #0), binding failed\n");
+ return -EINVAL;
+ }
+ otp_priv->regs[0] = regs;
+
+ if (IS_ENABLED(CONFIG_ARCH_NPCM7xx)) {
+ regs = dev_remap_addr_index(dev, 1);
+ if (!regs) {
+ printf("Cannot find reg address (arr #1), binding failed\n");
+ return -EINVAL;
+ }
+ otp_priv->regs[1] = regs;
+ }
+ printf("OTP: NPCM OTP module bind OK\n");
+
+ return 0;
+}
+
+static const struct udevice_id npcm_otp_ids[] = {
+ { .compatible = "nuvoton,npcm845-otp" },
+ { .compatible = "nuvoton,npcm750-otp" },
+ { }
+};
+
+U_BOOT_DRIVER(npcm_otp) = {
+ .name = "npcm_otp",
+ .id = UCLASS_MISC,
+ .of_match = npcm_otp_ids,
+ .priv_auto = sizeof(struct npcm_otp_priv),
+ .bind = npcm_otp_bind,
+};
--
2.17.1
More information about the U-Boot
mailing list