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