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