blob: 07d5f394354c323da718e2d640cef6d2f36163ee [file] [log] [blame]
Pali Rohár70d9bee2022-02-23 14:15:45 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) 2017 Marvell International Ltd.
4 * (C) 2021 Pali Rohár <pali@kernel.org>
5 */
6
7#include <config.h>
8#include <common.h>
9#include <asm/io.h>
10#include <linux/delay.h>
Pali Rohár49690392022-02-23 14:15:49 +010011#include <mach/mbox.h>
Pali Rohár70d9bee2022-02-23 14:15:45 +010012#include <mach/soc.h>
13
14#define OTP_NB_REG_BASE ((void __iomem *)MVEBU_REGISTER(0x12600))
15#define OTP_SB_REG_BASE ((void __iomem *)MVEBU_REGISTER(0x1A200))
16
17#define OTP_CONTROL_OFF 0x00
18#define OTP_MODE_BIT BIT(15)
19#define OTP_RPTR_RST_BIT BIT(14)
20#define OTP_POR_B_BIT BIT(13)
21#define OTP_PRDT_BIT BIT(3)
22#define OTP_READ_PORT_OFF 0x04
23#define OTP_READ_POINTER_OFF 0x08
24#define OTP_PTR_INC_BIT BIT(8)
25
26static void otp_read_parallel(void __iomem *base, u32 *data, u32 count)
27{
28 u32 regval;
29
30 /* 1. Clear OTP_MODE_NB to parallel mode */
31 regval = readl(base + OTP_CONTROL_OFF);
32 regval &= ~OTP_MODE_BIT;
33 writel(regval, base + OTP_CONTROL_OFF);
34
35 /* 2. Set OTP_POR_B_NB enter normal operation */
36 regval = readl(base + OTP_CONTROL_OFF);
37 regval |= OTP_POR_B_BIT;
38 writel(regval, base + OTP_CONTROL_OFF);
39
40 /* 3. Set OTP_PTR_INC_NB to auto-increment pointer after each read */
41 regval = readl(base + OTP_READ_POINTER_OFF);
42 regval |= OTP_PTR_INC_BIT;
43 writel(regval, base + OTP_READ_POINTER_OFF);
44
45 /* 4. Set OTP_RPTR_RST_NB, then clear the same field */
46 regval = readl(base + OTP_CONTROL_OFF);
47 regval |= OTP_RPTR_RST_BIT;
48 writel(regval, base + OTP_CONTROL_OFF);
49
50 regval = readl(base + OTP_CONTROL_OFF);
51 regval &= ~OTP_RPTR_RST_BIT;
52 writel(regval, base + OTP_CONTROL_OFF);
53
54 /* 5. Toggle OTP_PRDT_NB
55 * a. Set OTP_PRDT_NB to 1.
56 * b. Clear OTP_PRDT_NB to 0.
57 * c. Wait for a minimum of 100 ns.
58 * d. Set OTP_PRDT_NB to 1
59 */
60 regval = readl(base + OTP_CONTROL_OFF);
61 regval |= OTP_PRDT_BIT;
62 writel(regval, base + OTP_CONTROL_OFF);
63
64 regval = readl(base + OTP_CONTROL_OFF);
65 regval &= ~OTP_PRDT_BIT;
66 writel(regval, base + OTP_CONTROL_OFF);
67
68 ndelay(100);
69
70 regval = readl(base + OTP_CONTROL_OFF);
71 regval |= OTP_PRDT_BIT;
72 writel(regval, base + OTP_CONTROL_OFF);
73
74 while (count-- > 0) {
75 /* 6. Read the content of OTP 32-bits at a time */
76 ndelay(100000);
77 *(data++) = readl(base + OTP_READ_PORT_OFF);
78 }
79}
80
Pali Rohár49690392022-02-23 14:15:49 +010081static int rwtm_otp_read(u8 row, u32 word, u32 *data)
82{
83 u32 out[3];
84 u32 in[2];
85 int res = -EINVAL;
86
87 if (word < 2) {
88 /*
89 * MBOX_CMD_OTP_READ_32B command is supported by Marvell
90 * fuse.bin firmware and also by new CZ.NIC wtmi firmware.
91 * This command returns raw bits without ECC corrections.
92 * It does not provide access to the lock bit.
93 */
94 in[0] = row;
95 in[1] = word * 32;
96 res = mbox_do_cmd(MBOX_CMD_OTP_READ_32B, in, 2, out, 1);
97 if (!res)
98 *data = out[0];
99 } else if (word == 2) {
100 /*
101 * MBOX_CMD_OTP_READ command is supported only by new CZ.NIC
102 * wtmi firmware and provides access to all bits, including
103 * lock bit without doing ECC corrections. For compatibility
104 * with Marvell fuse.bin firmware, use this command only for
105 * accessing lock bit.
106 */
107 in[0] = row;
108 res = mbox_do_cmd(MBOX_CMD_OTP_READ, in, 1, out, 3);
109 if (!res)
110 *data = out[2];
111 }
112
113 return res;
114}
115
Pali Rohár826a7ca2022-04-07 11:32:10 +0200116static int rwtm_otp_write(u8 row, u32 word, u32 data)
117{
118 u32 in[4];
119 int res = -EINVAL;
120
121 if (word < 2) {
122 /*
123 * MBOX_CMD_OTP_WRITE_32B command is supported by Marvell
124 * fuse.bin firmware and also by new CZ.NIC wtmi firmware.
125 * This command writes only selected bits to OTP and does
126 * not calculate ECC bits. It does not allow to write the
127 * lock bit.
128 */
129 in[0] = row;
130 in[1] = word * 32;
131 in[2] = data;
132 res = mbox_do_cmd(MBOX_CMD_OTP_WRITE_32B, in, 3, NULL, 0);
133 } else if (word == 2 && !(data & ~0x1)) {
134 /*
135 * MBOX_CMD_OTP_WRITE command is supported only by new CZ.NIC
136 * wtmi firmware and allows to write any bit to OTP, including
137 * the lock bit. It does not calculate or write ECC bits too.
138 * For compatibility with Marvell fuse.bin firmware, use this
139 * command only for writing the lock bit.
140 */
141 in[0] = row;
142 in[1] = 0;
143 in[2] = 0;
144 in[3] = data;
145 res = mbox_do_cmd(MBOX_CMD_OTP_WRITE, in, 4, NULL, 0);
146 }
147
148 return res;
149}
150
Pali Rohár70d9bee2022-02-23 14:15:45 +0100151/*
152 * Banks 0-43 are used for accessing Security OTP (44 rows with 67 bits via 44 banks and words 0-2)
153 * Bank 44 is used for accessing North Bridge OTP (69 bits via words 0-2)
154 * Bank 45 is used for accessing South Bridge OTP (97 bits via words 0-3)
155 */
156
157#define RWTM_ROWS 44
158#define RWTM_MAX_BANK (RWTM_ROWS - 1)
159#define RWTM_ROW_WORDS 3
160#define OTP_NB_BANK RWTM_ROWS
161#define OTP_NB_WORDS 3
162#define OTP_SB_BANK (RWTM_ROWS + 1)
163#define OTP_SB_WORDS 4
164
165int fuse_read(u32 bank, u32 word, u32 *val)
166{
167 if (bank <= RWTM_MAX_BANK) {
168 if (word >= RWTM_ROW_WORDS)
169 return -EINVAL;
Pali Rohár49690392022-02-23 14:15:49 +0100170 return rwtm_otp_read(bank, word, val);
Pali Rohár70d9bee2022-02-23 14:15:45 +0100171 } else if (bank == OTP_NB_BANK) {
172 u32 data[OTP_NB_WORDS];
173 if (word >= OTP_NB_WORDS)
174 return -EINVAL;
175 otp_read_parallel(OTP_NB_REG_BASE, data, OTP_NB_WORDS);
176 *val = data[word];
177 return 0;
178 } else if (bank == OTP_SB_BANK) {
179 u32 data[OTP_SB_WORDS];
180 if (word >= OTP_SB_WORDS)
181 return -EINVAL;
182 otp_read_parallel(OTP_SB_REG_BASE, data, OTP_SB_WORDS);
183 *val = data[word];
184 return 0;
185 } else {
186 return -EINVAL;
187 }
188}
189
190int fuse_prog(u32 bank, u32 word, u32 val)
191{
Pali Rohár826a7ca2022-04-07 11:32:10 +0200192 if (bank <= RWTM_MAX_BANK) {
193 if (word >= RWTM_ROW_WORDS)
194 return -EINVAL;
195 return rwtm_otp_write(bank, word, val);
196 } else if (bank == OTP_NB_BANK) {
197 /* TODO: not implemented yet */
198 return -ENOSYS;
199 } else if (bank == OTP_SB_BANK) {
200 /* TODO: not implemented yet */
201 return -ENOSYS;
202 } else {
203 return -EINVAL;
204 }
Pali Rohár70d9bee2022-02-23 14:15:45 +0100205}
206
207int fuse_sense(u32 bank, u32 word, u32 *val)
208{
209 /* not supported */
210 return -ENOSYS;
211}
212
213int fuse_override(u32 bank, u32 word, u32 val)
214{
215 /* not supported */
216 return -ENOSYS;
217}