~funderscore blog cgit wiki get in touch
aboutsummaryrefslogtreecommitdiff
blob: 07d5f394354c323da718e2d640cef6d2f36163ee (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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// SPDX-License-Identifier: GPL-2.0+
/*
 * (C) 2017 Marvell International Ltd.
 * (C) 2021 Pali Rohár <pali@kernel.org>
 */

#include <config.h>
#include <common.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <mach/mbox.h>
#include <mach/soc.h>

#define OTP_NB_REG_BASE		((void __iomem *)MVEBU_REGISTER(0x12600))
#define OTP_SB_REG_BASE		((void __iomem *)MVEBU_REGISTER(0x1A200))

#define OTP_CONTROL_OFF		0x00
#define   OTP_MODE_BIT		BIT(15)
#define   OTP_RPTR_RST_BIT	BIT(14)
#define   OTP_POR_B_BIT		BIT(13)
#define   OTP_PRDT_BIT		BIT(3)
#define OTP_READ_PORT_OFF	0x04
#define OTP_READ_POINTER_OFF	0x08
#define   OTP_PTR_INC_BIT	BIT(8)

static void otp_read_parallel(void __iomem *base, u32 *data, u32 count)
{
	u32 regval;

	/* 1. Clear OTP_MODE_NB to parallel mode */
	regval = readl(base + OTP_CONTROL_OFF);
	regval &= ~OTP_MODE_BIT;
	writel(regval, base + OTP_CONTROL_OFF);

	/* 2. Set OTP_POR_B_NB enter normal operation */
	regval = readl(base + OTP_CONTROL_OFF);
	regval |= OTP_POR_B_BIT;
	writel(regval, base + OTP_CONTROL_OFF);

	/* 3. Set OTP_PTR_INC_NB to auto-increment pointer after each read */
	regval = readl(base + OTP_READ_POINTER_OFF);
	regval |= OTP_PTR_INC_BIT;
	writel(regval, base + OTP_READ_POINTER_OFF);

	/* 4. Set OTP_RPTR_RST_NB, then clear the same field */
	regval = readl(base + OTP_CONTROL_OFF);
	regval |= OTP_RPTR_RST_BIT;
	writel(regval, base + OTP_CONTROL_OFF);

	regval = readl(base + OTP_CONTROL_OFF);
	regval &= ~OTP_RPTR_RST_BIT;
	writel(regval, base + OTP_CONTROL_OFF);

	/* 5. Toggle OTP_PRDT_NB
	 * a. Set OTP_PRDT_NB to 1.
	 * b. Clear OTP_PRDT_NB to 0.
	 * c. Wait for a minimum of 100 ns.
	 * d. Set OTP_PRDT_NB to 1
	 */
	regval = readl(base + OTP_CONTROL_OFF);
	regval |= OTP_PRDT_BIT;
	writel(regval, base + OTP_CONTROL_OFF);

	regval = readl(base + OTP_CONTROL_OFF);
	regval &= ~OTP_PRDT_BIT;
	writel(regval, base + OTP_CONTROL_OFF);

	ndelay(100);

	regval = readl(base + OTP_CONTROL_OFF);
	regval |= OTP_PRDT_BIT;
	writel(regval, base + OTP_CONTROL_OFF);

	while (count-- > 0) {
		/* 6. Read the content of OTP 32-bits at a time */
		ndelay(100000);
		*(data++) = readl(base + OTP_READ_PORT_OFF);
	}
}

static int rwtm_otp_read(u8 row, u32 word, u32 *data)
{
	u32 out[3];
	u32 in[2];
	int res = -EINVAL;

	if (word < 2) {
		/*
		 * MBOX_CMD_OTP_READ_32B command is supported by Marvell
		 * fuse.bin firmware and also by new CZ.NIC wtmi firmware.
		 * This command returns raw bits without ECC corrections.
		 * It does not provide access to the lock bit.
		 */
		in[0] = row;
		in[1] = word * 32;
		res = mbox_do_cmd(MBOX_CMD_OTP_READ_32B, in, 2, out, 1);
		if (!res)
			*data = out[0];
	} else if (word == 2) {
		/*
		 * MBOX_CMD_OTP_READ command is supported only by new CZ.NIC
		 * wtmi firmware and provides access to all bits, including
		 * lock bit without doing ECC corrections. For compatibility
		 * with Marvell fuse.bin firmware, use this command only for
		 * accessing lock bit.
		 */
		in[0] = row;
		res = mbox_do_cmd(MBOX_CMD_OTP_READ, in, 1, out, 3);
		if (!res)
			*data = out[2];
	}

	return res;
}

static int rwtm_otp_write(u8 row, u32 word, u32 data)
{
	u32 in[4];
	int res = -EINVAL;

	if (word < 2) {
		/*
		 * MBOX_CMD_OTP_WRITE_32B command is supported by Marvell
		 * fuse.bin firmware and also by new CZ.NIC wtmi firmware.
		 * This command writes only selected bits to OTP and does
		 * not calculate ECC bits. It does not allow to write the
		 * lock bit.
		 */
		in[0] = row;
		in[1] = word * 32;
		in[2] = data;
		res = mbox_do_cmd(MBOX_CMD_OTP_WRITE_32B, in, 3, NULL, 0);
	} else if (word == 2 && !(data & ~0x1)) {
		/*
		 * MBOX_CMD_OTP_WRITE command is supported only by new CZ.NIC
		 * wtmi firmware and allows to write any bit to OTP, including
		 * the lock bit. It does not calculate or write ECC bits too.
		 * For compatibility with Marvell fuse.bin firmware, use this
		 * command only for writing the lock bit.
		 */
		in[0] = row;
		in[1] = 0;
		in[2] = 0;
		in[3] = data;
		res = mbox_do_cmd(MBOX_CMD_OTP_WRITE, in, 4, NULL, 0);
	}

	return res;
}

/*
 * Banks 0-43 are used for accessing Security OTP (44 rows with 67 bits via 44 banks and words 0-2)
 * Bank 44 is used for accessing North Bridge OTP (69 bits via words 0-2)
 * Bank 45 is used for accessing South Bridge OTP (97 bits via words 0-3)
 */

#define RWTM_ROWS	44
#define RWTM_MAX_BANK	(RWTM_ROWS - 1)
#define RWTM_ROW_WORDS	3
#define OTP_NB_BANK	RWTM_ROWS
#define OTP_NB_WORDS	3
#define OTP_SB_BANK	(RWTM_ROWS + 1)
#define OTP_SB_WORDS	4

int fuse_read(u32 bank, u32 word, u32 *val)
{
	if (bank <= RWTM_MAX_BANK) {
		if (word >= RWTM_ROW_WORDS)
			return -EINVAL;
		return rwtm_otp_read(bank, word, val);
	} else if (bank == OTP_NB_BANK) {
		u32 data[OTP_NB_WORDS];
		if (word >= OTP_NB_WORDS)
			return -EINVAL;
		otp_read_parallel(OTP_NB_REG_BASE, data, OTP_NB_WORDS);
		*val = data[word];
		return 0;
	} else if (bank == OTP_SB_BANK) {
		u32 data[OTP_SB_WORDS];
		if (word >= OTP_SB_WORDS)
			return -EINVAL;
		otp_read_parallel(OTP_SB_REG_BASE, data, OTP_SB_WORDS);
		*val = data[word];
		return 0;
	} else {
		return -EINVAL;
	}
}

int fuse_prog(u32 bank, u32 word, u32 val)
{
	if (bank <= RWTM_MAX_BANK) {
		if (word >= RWTM_ROW_WORDS)
			return -EINVAL;
		return rwtm_otp_write(bank, word, val);
	} else if (bank == OTP_NB_BANK) {
		/* TODO: not implemented yet */
		return -ENOSYS;
	} else if (bank == OTP_SB_BANK) {
		/* TODO: not implemented yet */
		return -ENOSYS;
	} else {
		return -EINVAL;
	}
}

int fuse_sense(u32 bank, u32 word, u32 *val)
{
	/* not supported */
	return -ENOSYS;
}

int fuse_override(u32 bank, u32 word, u32 val)
{
	/* not supported */
	return -ENOSYS;
}