summaryrefslogtreecommitdiff
path: root/chip/stm32/otp-stm32f4.c
blob: 45ce38d159fd86d441b637c06ddd72219aa2b877 (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
/* Copyright 2017 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* OTP implementation for STM32F411 */

#include "common.h"
#include "console.h"
#include "flash.h"
#include "otp.h"
#include "registers.h"
#include "util.h"

/*
 * OTP is only used for saving the USB serial number.
 */
#ifdef CONFIG_SERIALNO_LEN
/* Which block to use */
#define OTP_SERIAL_BLOCK 0
#define OTP_SERIAL_ADDR \
	REG32_ADDR(STM32_OTP_BLOCK_DATA(OTP_SERIAL_BLOCK, 0))

/* Number of word used in the block */
#define OTP_SERIAL_BLOCK_SIZE (CONFIG_SERIALNO_LEN / sizeof(uint32_t))
BUILD_ASSERT(CONFIG_SERIALNO_LEN % sizeof(uint32_t) == 0);
BUILD_ASSERT(OTP_SERIAL_BLOCK_SIZE < STM32_OTP_BLOCK_SIZE);

/*
 * Write an OTP block
 *
 * @param block         block to write.
 * @param size          Number of words to write.
 * @param data          Destination buffer for data.
 */
static int otp_write(uint8_t block, int size, const char *data)
{
	if (block >= STM32_OTP_BLOCK_NB)
		return EC_ERROR_PARAM1;
	if (size >= STM32_OTP_BLOCK_SIZE)
		return EC_ERROR_PARAM2;
	return crec_flash_physical_write(STM32_OTP_BLOCK_DATA(block, 0) -
					 CONFIG_PROGRAM_MEMORY_BASE,
					 size * sizeof(uint32_t), data);
}

/*
 * Check if an OTP block is protected.
 *
 * @param block        protected block.
 * @return non-zero if that block is read only.
 */
static int otp_get_protect(uint8_t block)
{
	uint32_t lock;

	lock = REG32(STM32_OTP_LOCK(block));
	return ((lock & STM32_OPT_LOCK_MASK(block)) == 0);
}

/*
 * Set a particular OTP block as read only.
 *
 * @param block        block to protect.
 */
static int otp_set_protect(uint8_t block)
{
	int rv;
	uint32_t lock;

	if (otp_get_protect(block))
		return EC_SUCCESS;

	lock = REG32(STM32_OTP_LOCK(block));
	lock &= ~STM32_OPT_LOCK_MASK(block);
	rv = crec_flash_physical_write(STM32_OTP_LOCK(block) -
				       CONFIG_PROGRAM_MEMORY_BASE,
				       sizeof(uint32_t), (char *)&lock);
	if (rv)
		return rv;
	else
		return EC_SUCCESS;
}

const char *otp_read_serial(void)
{
	int i;

	for (i = 0; i < OTP_SERIAL_BLOCK_SIZE; i++) {
		if (OTP_SERIAL_ADDR[i] != -1)
			return (char *)OTP_SERIAL_ADDR;
	}
	return NULL;
}

int otp_write_serial(const char *serialno)
{
	int i, ret;
	char otp_serial[CONFIG_SERIALNO_LEN];

	if (otp_get_protect(OTP_SERIAL_BLOCK))
		return EC_ERROR_ACCESS_DENIED;

	/* Copy in serialno. */
	for (i = 0; i < CONFIG_SERIALNO_LEN - 1; i++) {
		otp_serial[i] = serialno[i];
		if (serialno[i] == 0)
			break;
	}
	for (; i < CONFIG_SERIALNO_LEN; i++)
		otp_serial[i] = 0;

	ret = otp_write(OTP_SERIAL_BLOCK, OTP_SERIAL_BLOCK_SIZE, otp_serial);
	if (ret == EC_SUCCESS)
		return otp_set_protect(OTP_SERIAL_BLOCK);
	else
		return ret;
}
#endif