diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/nvmem/Kconfig | 8 | ||||
-rw-r--r-- | drivers/nvmem/Makefile | 5 | ||||
-rw-r--r-- | drivers/nvmem/bsec.c | 221 |
3 files changed, 233 insertions, 1 deletions
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index c28a6d4e43..968342b281 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -51,4 +51,12 @@ config EEPROM_93XX46 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 abf9dae429..7101c5aca4 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -16,4 +16,7 @@ obj-$(CONFIG_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o 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 0000000000..8235d468d1 --- /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 <common.h> +#include <driver.h> +#include <malloc.h> +#include <xfuncs.h> +#include <errno.h> +#include <init.h> +#include <net.h> +#include <io.h> +#include <of.h> +#include <regmap.h> +#include <mach/bsec.h> +#include <machine_id.h> +#include <linux/nvmem-provider.h> + +#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); |