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