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