stm32f4: Add OTP support.

Add support for OTP memory: if needed store serial number in first bank.

BUG=chromium:746471
BRANCH=none
TEST=On sweetberry, check we can write serial number with serialno
command. Check serial number survive a firmware update.

First, check without write protect, check we can write 0s (but not 1s)
serialno
Serial number: NNNNNNNNNNNNNNNNNNNNNN
>
> serial set MMMMMMMMMMMMMMMMMMMMMMMMMMMMM
Saving serial number
Serial number: LLLLLLLLLLLLLLLLLLLLLL

After lock enabled, check we can not overwrite.
> serial set AMMMMMMMMMMMMMMMMMMMMMMMMMMMM
Saving serial number
Serial number: LLLLLLLLLLLLLLLLLLLLLL
Access Denied

Check that serialno returns "Uninitialized" if it was never set.

Change-Id: I9ab08486a7c3e1958e964649640d69b5b70947e3
Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/580290
Reviewed-by: Nick Sanders <nsanders@chromium.org>
This commit is contained in:
Gwendal Grignou
2017-07-20 10:15:40 -07:00
committed by chrome-bot
parent 1b25735b73
commit a35218e204
4 changed files with 136 additions and 0 deletions

View File

@@ -56,6 +56,7 @@ endif
chip-$(CONFIG_ADC)+=adc-$(CHIP_FAMILY).o
chip-$(CONFIG_STM32_CHARGER_DETECT)+=charger_detect.o
chip-$(CONFIG_DEBUG_PRINTF)+=debug_printf.o
chip-$(CONFIG_OTP)+=otp-$(CHIP_FAMILY).o
chip-$(CONFIG_PWM)+=pwm.o
chip-$(CONFIG_RNG)+=trng.o

View File

@@ -54,5 +54,8 @@
#define CONFIG_FLASH_PSTATE
#undef CONFIG_FLASH_PSTATE_BANK
/* Use OTP regions */
#define CONFIG_OTP
/* Number of IRQ vectors on the NVIC */
#define CONFIG_IRQ_COUNT 97

119
chip/stm32/otp-stm32f4.c Normal file
View File

@@ -0,0 +1,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 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 = 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

View File

@@ -1479,6 +1479,19 @@ typedef volatile struct stm32_spi_regs stm32_spi_regs_t;
#define STM32_OPTB_COMPL_SHIFT 8
#define STM32_OTP_BASE 0x1FFF7800
#define STM32_OTP_BLOCK_NB 16
#define STM32_OTP_BLOCK_SIZE 32
#define STM32_OTP_BLOCK_DATA(_block, _offset) \
(STM32_OTP_BASE + STM32_OTP_BLOCK_SIZE * (_block) + (_offset) * 4)
#define STM32_OTP_UNLOCK_BYTE 0x00
#define STM32_OTP_LOCK_BYTE 0xFF
#define STM32_OTP_LOCK_BASE \
(STM32_OTP_BASE + STM32_OTP_BLOCK_NB * STM32_OTP_BLOCK_SIZE)
#define STM32_OTP_LOCK(_block) \
(STM32_OTP_LOCK_BASE + ((_block) / 4) * 4)
#define STM32_OPT_LOCK_MASK(_block) ((0xFF << ((_block) % 4) * 8))
#else
#error Unsupported chip variant
#endif