uboot/board/sifive/fu540/fu540.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
   4 *
   5 * Authors:
   6 *   Anup Patel <anup.patel@wdc.com>
   7 */
   8
   9#include <common.h>
  10#include <dm.h>
  11#include <linux/delay.h>
  12#include <linux/io.h>
  13
  14#ifdef CONFIG_MISC_INIT_R
  15
  16#define FU540_OTP_BASE_ADDR                     0x10070000
  17
  18struct fu540_otp_regs {
  19        u32 pa;     /* Address input */
  20        u32 paio;   /* Program address input */
  21        u32 pas;    /* Program redundancy cell selection input */
  22        u32 pce;    /* OTP Macro enable input */
  23        u32 pclk;   /* Clock input */
  24        u32 pdin;   /* Write data input */
  25        u32 pdout;  /* Read data output */
  26        u32 pdstb;  /* Deep standby mode enable input (active low) */
  27        u32 pprog;  /* Program mode enable input */
  28        u32 ptc;    /* Test column enable input */
  29        u32 ptm;    /* Test mode enable input */
  30        u32 ptm_rep;/* Repair function test mode enable input */
  31        u32 ptr;    /* Test row enable input */
  32        u32 ptrim;  /* Repair function enable input */
  33        u32 pwe;    /* Write enable input (defines program cycle) */
  34} __packed;
  35
  36#define BYTES_PER_FUSE                          4
  37#define NUM_FUSES                               0x1000
  38
  39static int fu540_otp_read(int offset, void *buf, int size)
  40{
  41        struct fu540_otp_regs *regs = (void __iomem *)FU540_OTP_BASE_ADDR;
  42        unsigned int i;
  43        int fuseidx = offset / BYTES_PER_FUSE;
  44        int fusecount = size / BYTES_PER_FUSE;
  45        u32 fusebuf[fusecount];
  46
  47        /* check bounds */
  48        if (offset < 0 || size < 0)
  49                return -EINVAL;
  50        if (fuseidx >= NUM_FUSES)
  51                return -EINVAL;
  52        if ((fuseidx + fusecount) > NUM_FUSES)
  53                return -EINVAL;
  54
  55        /* init OTP */
  56        writel(0x01, &regs->pdstb); /* wake up from stand-by */
  57        writel(0x01, &regs->ptrim); /* enable repair function */
  58        writel(0x01, &regs->pce);   /* enable input */
  59
  60        /* read all requested fuses */
  61        for (i = 0; i < fusecount; i++, fuseidx++) {
  62                writel(fuseidx, &regs->pa);
  63
  64                /* cycle clock to read */
  65                writel(0x01, &regs->pclk);
  66                mdelay(1);
  67                writel(0x00, &regs->pclk);
  68                mdelay(1);
  69
  70                /* read the value */
  71                fusebuf[i] = readl(&regs->pdout);
  72        }
  73
  74        /* shut down */
  75        writel(0, &regs->pce);
  76        writel(0, &regs->ptrim);
  77        writel(0, &regs->pdstb);
  78
  79        /* copy out */
  80        memcpy(buf, fusebuf, size);
  81
  82        return 0;
  83}
  84
  85static u32 fu540_read_serialnum(void)
  86{
  87        int ret;
  88        u32 serial[2] = {0};
  89
  90        for (int i = 0xfe * 4; i > 0; i -= 8) {
  91                ret = fu540_otp_read(i, serial, sizeof(serial));
  92                if (ret) {
  93                        printf("%s: error reading from OTP\n", __func__);
  94                        break;
  95                }
  96                if (serial[0] == ~serial[1])
  97                        return serial[0];
  98        }
  99
 100        return 0;
 101}
 102
 103static void fu540_setup_macaddr(u32 serialnum)
 104{
 105        /* Default MAC address */
 106        unsigned char mac[6] = { 0x70, 0xb3, 0xd5, 0x92, 0xf0, 0x00 };
 107
 108        /*
 109         * We derive our board MAC address by ORing last three bytes
 110         * of board serial number to above default MAC address.
 111         *
 112         * This logic of deriving board MAC address is taken from
 113         * SiFive FSBL and is kept unchanged.
 114         */
 115        mac[5] |= (serialnum >>  0) & 0xff;
 116        mac[4] |= (serialnum >>  8) & 0xff;
 117        mac[3] |= (serialnum >> 16) & 0xff;
 118
 119        /* Update environment variable */
 120        eth_env_set_enetaddr("ethaddr", mac);
 121}
 122
 123int misc_init_r(void)
 124{
 125        u32 serial_num;
 126        char buf[9] = {0};
 127
 128        /* Set ethaddr environment variable from board serial number */
 129        if (!env_get("serial#")) {
 130                serial_num = fu540_read_serialnum();
 131                if (!serial_num) {
 132                        WARN(true, "Board serial number should not be 0 !!\n");
 133                        return 0;
 134                }
 135                snprintf(buf, sizeof(buf), "%08x", serial_num);
 136                env_set("serial#", buf);
 137                fu540_setup_macaddr(serial_num);
 138        }
 139        return 0;
 140}
 141
 142#endif
 143
 144int board_init(void)
 145{
 146        /* For now nothing to do here. */
 147
 148        return 0;
 149}
 150