diff --git a/arch/arm/dts/stm32mp157c.dtsi b/arch/arm/dts/stm32mp157c.dtsi index 8d9c84a..771139c 100644 --- a/arch/arm/dts/stm32mp157c.dtsi +++ b/arch/arm/dts/stm32mp157c.dtsi @@ -20,3 +20,7 @@ gpio25 = &gpioz; }; }; + +&bsec { + barebox,provide-mac-address = <ðernet0 0x39>; +}; diff --git a/arch/arm/mach-stm32mp/include/mach/bsec.h b/arch/arm/mach-stm32mp/include/mach/bsec.h new file mode 100644 index 0000000..559faaa --- /dev/null +++ b/arch/arm/mach-stm32mp/include/mach/bsec.h @@ -0,0 +1,41 @@ +#ifndef __MACH_STM32_BSEC_H__ +#define __MACH_STM32_BSEC_H__ + +#include + +/* Return status */ +enum bsec_smc { + BSEC_SMC_OK = 0, + BSEC_SMC_ERROR = -1, + BSEC_SMC_DISTURBED = -2, + BSEC_SMC_INVALID_PARAM = -3, + BSEC_SMC_PROG_FAIL = -4, + BSEC_SMC_LOCK_FAIL = -5, + BSEC_SMC_WRITE_FAIL = -6, + BSEC_SMC_SHADOW_FAIL = -7, + BSEC_SMC_TIMEOUT = -8, +}; + +/* Service for BSEC */ +enum bsec_field { + BSEC_SMC_READ_SHADOW = 1, + BSEC_SMC_PROG_OTP = 2, + BSEC_SMC_WRITE_SHADOW = 3, + BSEC_SMC_READ_OTP = 4, + BSEC_SMC_READ_ALL = 5, + BSEC_SMC_WRITE_ALL = 6, +}; + +static inline enum bsec_smc bsec_read_field(enum bsec_field field, unsigned *val) +{ + return stm32mp_smc(STM32_SMC_BSEC, BSEC_SMC_READ_SHADOW, + field, 0, val); +} + +static inline enum bsec_smc bsec_write_field(enum bsec_field field, unsigned val) +{ + return stm32mp_smc(STM32_SMC_BSEC, BSEC_SMC_WRITE_SHADOW, + field, val, NULL); +} + +#endif diff --git a/arch/arm/mach-stm32mp/include/mach/smc.h b/arch/arm/mach-stm32mp/include/mach/smc.h new file mode 100644 index 0000000..6b8e62b --- /dev/null +++ b/arch/arm/mach-stm32mp/include/mach/smc.h @@ -0,0 +1,28 @@ +#ifndef __MACH_STM32_SMC_H__ +#define __MACH_STM32_SMC_H__ + +#include + +/* Secure Service access from Non-secure */ +#define STM32_SMC_RCC 0x82001000 +#define STM32_SMC_PWR 0x82001001 +#define STM32_SMC_RTC 0x82001002 +#define STM32_SMC_BSEC 0x82001003 + +/* Register access service use for RCC/RTC/PWR */ +#define STM32_SMC_REG_WRITE 0x1 +#define STM32_SMC_REG_SET 0x2 +#define STM32_SMC_REG_CLEAR 0x3 + +static inline int stm32mp_smc(u32 svc, u8 op, u32 data1, u32 data2, u32 *val) +{ + struct arm_smccc_res res; + + arm_smccc_smc(svc, op, data1, data2, 0, 0, 0, 0, &res); + if (val) + *val = res.a1; + + return (int)res.a0; +} + +#endif diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index c28a6d4..968342b 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -51,4 +51,12 @@ supports both read and write commands and also the command to erase the whole EEPROM. +config STM32_BSEC + tristate "STM32 Boot and security and OTP control" + depends on ARCH_STM32MP + depends on OFDEVICE + help + This adds support for the STM32 OTP controller. Reads and writes + to will go to the shadow RAM, not the OTP fuses themselvers. + endif diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index abf9dae..7101c5a 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -16,4 +16,7 @@ nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o obj-$(CONFIG_EEPROM_93XX46) += nvmem_eeprom_93xx46.o -nvmem_eeprom_93xx46-y := eeprom_93xx46.o \ No newline at end of file +nvmem_eeprom_93xx46-y := eeprom_93xx46.o + +obj-$(CONFIG_STM32_BSEC) += nvmem_bsec.o +nvmem_bsec-y := bsec.o diff --git a/drivers/nvmem/bsec.c b/drivers/nvmem/bsec.c new file mode 100644 index 0000000..8235d46 --- /dev/null +++ b/drivers/nvmem/bsec.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2019 Ahmad Fatoum, Pengutronix + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BSEC_OTP_SERIAL 13 + +struct bsec_priv { + struct regmap *map; + u32 svc_id; + struct device_d dev; + struct regmap_config map_config; + struct nvmem_config config; +}; + +struct stm32_bsec_data { + unsigned long svc_id; + int num_regs; +}; + +static int bsec_smc(struct bsec_priv *priv, u8 op, enum bsec_field field, + unsigned data2, unsigned *val) +{ + enum bsec_smc ret = stm32mp_smc(priv->svc_id, op, field / 4, data2, val); + switch(ret) + { + case BSEC_SMC_OK: + return 0; + case BSEC_SMC_ERROR: + case BSEC_SMC_DISTURBED: + case BSEC_SMC_PROG_FAIL: + case BSEC_SMC_LOCK_FAIL: + case BSEC_SMC_WRITE_FAIL: + case BSEC_SMC_SHADOW_FAIL: + return -EIO; + case BSEC_SMC_INVALID_PARAM: + return -EINVAL; + case BSEC_SMC_TIMEOUT: + return -ETIMEDOUT; + } + + return -ENXIO; +} + +static int st32_bsec_read_shadow(void *ctx, unsigned reg, unsigned *val) +{ + return bsec_smc(ctx, BSEC_SMC_READ_SHADOW, reg, 0, val); +} + +static int stm32_bsec_reg_write_shadow(void *ctx, unsigned reg, unsigned val) +{ + return bsec_smc(ctx, BSEC_SMC_WRITE_SHADOW, reg, val, NULL); +} + +static struct regmap_bus stm32_bsec_regmap_bus = { + .reg_write = stm32_bsec_reg_write_shadow, + .reg_read = st32_bsec_read_shadow, +}; + +static int stm32_bsec_write(struct device_d *dev, int offset, + const void *val, int bytes) +{ + struct bsec_priv *priv = dev->parent->priv; + + return regmap_bulk_write(priv->map, offset, val, bytes); +} + +static int stm32_bsec_read(struct device_d *dev, int offset, + void *val, int bytes) +{ + struct bsec_priv *priv = dev->parent->priv; + + return regmap_bulk_read(priv->map, offset, val, bytes); +} + +static const struct nvmem_bus stm32_bsec_nvmem_bus = { + .write = stm32_bsec_write, + .read = stm32_bsec_read, +}; + +static void stm32_bsec_set_unique_machine_id(struct regmap *map) +{ + u32 unique_id[3]; + int ret; + + ret = regmap_bulk_read(map, BSEC_OTP_SERIAL * 4, + unique_id, sizeof(unique_id)); + if (ret) + return; + + machine_id_set_hashable(unique_id, sizeof(unique_id)); +} + +static int stm32_bsec_read_mac(struct regmap *map, int offset, u8 *mac) +{ + u8 res[8]; + int ret; + + ret = regmap_bulk_read(map, offset * 4, res, 8); + if (ret) + return ret; + + memcpy(mac, res, ETH_ALEN); + return 0; +} + +static void stm32_bsec_init_dt(struct bsec_priv *priv) +{ + struct device_node *node = priv->dev.parent->device_node; + struct device_node *rnode; + u32 phandle, offset; + char mac[ETH_ALEN]; + const __be32 *prop; + + int len; + int ret; + + if (!node) + return; + + prop = of_get_property(node, "barebox,provide-mac-address", &len); + if (!prop) + return; + + if (len != 2 * sizeof(__be32)) + return; + + phandle = be32_to_cpup(prop++); + + rnode = of_find_node_by_phandle(phandle); + offset = be32_to_cpup(prop++); + + ret = stm32_bsec_read_mac(priv->map, offset, mac); + if (ret) { + dev_warn(&priv->dev, "error setting MAC address: %s\n", + strerror(-ret)); + return; + } + + of_eth_register_ethaddr(rnode, mac); +} + +static int stm32_bsec_probe(struct device_d *dev) +{ + struct bsec_priv *priv; + int ret = 0; + const struct stm32_bsec_data *data; + struct nvmem_device *nvmem; + + ret = dev_get_drvdata(dev, (const void **)&data); + if (ret) + return ret; + + priv = xzalloc(sizeof(*priv)); + + priv->svc_id = data->svc_id; + + dev_set_name(&priv->dev, "bsec"); + priv->dev.parent = dev; + register_device(&priv->dev); + + priv->map_config.reg_bits = 32; + priv->map_config.val_bits = 32; + priv->map_config.reg_stride = 4; + priv->map_config.max_register = data->num_regs; + + priv->map = regmap_init(dev, &stm32_bsec_regmap_bus, priv, &priv->map_config); + if (IS_ERR(priv->map)) + return PTR_ERR(priv->map); + + priv->config.name = "stm32-bsec"; + priv->config.dev = dev; + priv->config.stride = 4; + priv->config.word_size = 4; + priv->config.size = data->num_regs; + priv->config.bus = &stm32_bsec_nvmem_bus; + dev->priv = priv; + + nvmem = nvmem_register(&priv->config); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + if (IS_ENABLED(CONFIG_MACHINE_ID)) + stm32_bsec_set_unique_machine_id(priv->map); + + stm32_bsec_init_dt(priv); + + return 0; +} + +static struct stm32_bsec_data stm32mp15_bsec_data = { + .num_regs = 95 * 4, + .svc_id = STM32_SMC_BSEC, +}; + +static __maybe_unused struct of_device_id stm32_bsec_dt_ids[] = { + { .compatible = "st,stm32mp15-bsec", .data = &stm32mp15_bsec_data }, + { /* sentinel */ } +}; + +static struct driver_d stm32_bsec_driver = { + .name = "stm32_bsec", + .probe = stm32_bsec_probe, + .of_compatible = DRV_OF_COMPAT(stm32_bsec_dt_ids), +}; +postcore_platform_driver(stm32_bsec_driver);