From 0ae1c77199a6ae0b5bf759f894736898d62d46b0 Mon Sep 17 00:00:00 2001 From: Jim Liu Date: Tue, 7 Jun 2022 16:33:54 +0800 Subject: [PATCH] misc: nuvoton: Add NPCM7xx otp controller driver Add Nuvoton BMC npcm750 otp driver Signed-off-by: Jim Liu --- 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 7b6c371d1c..28d5da49ff 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -321,6 +321,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 0a333640b9..0bf05ca05e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_MPC83XX_SERDES) += mpc83xx_serdes.o obj-$(CONFIG_$(SPL_TPL_)LS2_SFP) += ls2_sfp.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 +#include +#include +#include +#include +#include +#include +#include + +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, +};