summaryrefslogtreecommitdiffstats
path: root/drivers/brcm/ocotp.c
blob: 6ff855453d14de998fd9cc48a51cdd13f25e0573 (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
/*
 * Copyright (c) 2017 - 2020, Broadcom
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdint.h>

#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>

#include <ocotp.h>
#include <platform_def.h>

#define OTP_MAP 2
#define OTP_NUM_WORDS 2048
/*
 * # of tries for OTP Status. The time to execute a command varies. The slowest
 * commands are writes which also vary based on the # of bits turned on. Writing
 * 0xffffffff takes ~3800 us.
 */
#define OTPC_RETRIES_US                 5000

/* Sequence to enable OTP program */
#define OTPC_PROG_EN_SEQ             { 0xf, 0x4, 0x8, 0xd }

/* OTPC Commands */
#define OTPC_CMD_READ                0x0
#define OTPC_CMD_OTP_PROG_ENABLE     0x2
#define OTPC_CMD_OTP_PROG_DISABLE    0x3
#define OTPC_CMD_PROGRAM             0x8
#define OTPC_CMD_ECC                 0x10
#define OTPC_ECC_ADDR                0x1A
#define OTPC_ECC_VAL                 0x00EC0000

/* OTPC Status Bits */
#define OTPC_STAT_CMD_DONE           BIT(1)
#define OTPC_STAT_PROG_OK            BIT(2)

/* OTPC register definition */
#define OTPC_MODE_REG_OFFSET         0x0
#define OTPC_MODE_REG_OTPC_MODE      0
#define OTPC_COMMAND_OFFSET          0x4
#define OTPC_COMMAND_COMMAND_WIDTH   6
#define OTPC_CMD_START_OFFSET        0x8
#define OTPC_CMD_START_START         0
#define OTPC_CPU_STATUS_OFFSET       0xc
#define OTPC_CPUADDR_REG_OFFSET      0x28
#define OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH 16
#define OTPC_CPU_WRITE_REG_OFFSET    0x2c

#define OTPC_CMD_MASK  (BIT(OTPC_COMMAND_COMMAND_WIDTH) - 1)
#define OTPC_ADDR_MASK (BIT(OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH) - 1)

#define OTPC_MODE_REG			OCOTP_REGS_BASE

struct chip_otp_cfg {
	uint32_t base;
	uint32_t num_words;
};

struct chip_otp_cfg ocotp_cfg = {
	.base = OTPC_MODE_REG,
	.num_words = 2048,
};

struct otpc_priv {
	uint32_t base;
	struct otpc_map *map;
	int size;
	int state;
};

struct otpc_priv otpc_info;

static inline void set_command(uint32_t base, uint32_t command)
{
	mmio_write_32(base + OTPC_COMMAND_OFFSET, command & OTPC_CMD_MASK);
}

static inline void set_cpu_address(uint32_t base, uint32_t addr)
{
	mmio_write_32(base + OTPC_CPUADDR_REG_OFFSET, addr & OTPC_ADDR_MASK);
}

static inline void set_start_bit(uint32_t base)
{
	mmio_write_32(base + OTPC_CMD_START_OFFSET, 1 << OTPC_CMD_START_START);
}

static inline void reset_start_bit(uint32_t base)
{
	mmio_write_32(base + OTPC_CMD_START_OFFSET, 0);
}

static inline void write_cpu_data(uint32_t base, uint32_t value)
{
	mmio_write_32(base + OTPC_CPU_WRITE_REG_OFFSET, value);
}

static int poll_cpu_status(uint32_t base, uint32_t value)
{
	uint32_t status;
	uint32_t retries;

	for (retries = 0; retries < OTPC_RETRIES_US; retries++) {
		status = mmio_read_32(base + OTPC_CPU_STATUS_OFFSET);
		if (status & value)
			break;
		udelay(1);
	}
	if (retries == OTPC_RETRIES_US)
		return -1;

	return 0;
}

static int bcm_otpc_ecc(uint32_t enable)
{
	struct otpc_priv *priv = &otpc_info;
	int ret;

	set_command(priv->base, OTPC_CMD_ECC);
	set_cpu_address(priv->base, OTPC_ECC_ADDR);

	if (!enable)
		write_cpu_data(priv->base, OTPC_ECC_VAL);
	else
		write_cpu_data(priv->base, ~OTPC_ECC_VAL);

	set_start_bit(priv->base);
	ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
	if (ret) {
		ERROR("otp ecc op error: 0x%x", ret);
		return -1;
	}
	reset_start_bit(priv->base);

	return 0;
}

/*
 * bcm_otpc_read read otp data in the size of 8 byte rows.
 * bytes has to be the multiple of 8.
 * return -1 in error case, return read bytes in success.
 */
int bcm_otpc_read(unsigned int offset, void *val, uint32_t bytes,
		  uint32_t ecc_flag)
{
	struct otpc_priv *priv = &otpc_info;
	uint32_t *buf = val;
	uint32_t bytes_read;
	uint32_t address = offset / priv->map->word_size;
	int i, ret;

	if (!priv->state) {
		ERROR("OCOTP read failed\n");
		return -1;
	}

	bcm_otpc_ecc(ecc_flag);

	for (bytes_read = 0; (bytes_read + priv->map->word_size) <= bytes;) {
		set_command(priv->base, OTPC_CMD_READ);
		set_cpu_address(priv->base, address++);
		set_start_bit(priv->base);
		ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
		if (ret) {
			ERROR("otp read error: 0x%x", ret);
			return -1;
		}

		for (i = 0; i < priv->map->otpc_row_size; i++) {
			*buf++ = mmio_read_32(priv->base +
					priv->map->data_r_offset[i]);
			bytes_read += sizeof(*buf);
		}

		reset_start_bit(priv->base);
	}

	return bytes_read;
}

int bcm_otpc_init(struct otpc_map *map)
{
	struct otpc_priv *priv;

	priv = &otpc_info;
	priv->base = ocotp_cfg.base;
	priv->map = map;

	priv->size = 4 * ocotp_cfg.num_words;

	/* Enable CPU access to OTPC. */
	mmio_setbits_32(priv->base + OTPC_MODE_REG_OFFSET,
			BIT(OTPC_MODE_REG_OTPC_MODE));
	reset_start_bit(priv->base);
	priv->state = 1;
	VERBOSE("OTPC Initialization done\n");

	return 0;
}