| // 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, |
| }; |