blob: ac74f82473aece4ba3542309e6e6ba2bc92e5e2f (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
/* SPDX-License-Identifier: GPL-2.0-only */
#include <stddef.h>
#include <delay.h>
#include <device/mmio.h>
#include <console/console.h>
#include <console/uart.h>
#include <soc/addressmap.h>
#include <soc/otp.h>
/*
* This is a driver for the eMemory EG004K32TQ028XW01 NeoFuse
* One-Time-Programmable (OTP) memory used within the SiFive FU540.
* It is documented in the FU540 manual here:
* https://www.sifive.com/documentation/chips/freedom-u540-c000-manual/
*/
struct sifive_otp_registers {
u32 pa; /* Address input */
u32 paio; /* Program address input */
u32 pas; /* Program redundancy cell selection input */
u32 pce; /* OTP Macro enable input */
u32 pclk; /* Clock input */
u32 pdin; /* Write data input */
u32 pdout; /* Read data output */
u32 pdstb; /* Deep standby mode enable input (active low) */
u32 pprog; /* Program mode enable input */
u32 ptc; /* Test column enable input */
u32 ptm; /* Test mode enable input */
u32 ptm_rep;/* Repair function test mode enable input */
u32 ptr; /* Test row enable input */
u32 ptrim; /* Repair function enable input */
u32 pwe; /* Write enable input (defines program cycle) */
} __packed;
/*
* Read a 32 bit value addressed by its index from the OTP.
* The FU540 stores 4096x32 bit (16KiB) values.
* Index 0x00-0xff are reserved for SiFive internal use. (first 1KiB)
*/
u32 otp_read_word(u16 idx)
{
u32 w;
if (idx >= 0x1000)
die("otp: idx out of bounds");
struct sifive_otp_registers *regs = (void *)(FU540_OTP);
// wake up from stand-by
write32(®s->pdstb, 0x01);
// enable repair function
write32(®s->ptrim, 0x01);
// enable input
write32(®s->pce, 0x01);
// address to read
write32(®s->pa, idx);
// cycle clock to read
write32(®s->pclk, 0x01);
mdelay(1);
write32(®s->pclk, 0x00);
mdelay(1);
w = read32(®s->pdout);
// shut down
write32(®s->pce, 0x00);
write32(®s->ptrim, 0x00);
write32(®s->pdstb, 0x00);
return w;
}
u32 otp_read_serial(void)
{
u32 serial = 0;
u32 serial_n = 0;
for (int i = 0xfe; i > 0; i -= 2) {
serial = otp_read_word(i);
serial_n = otp_read_word(i+1);
if (serial == ~serial_n)
break;
}
return serial;
}
|