summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAhmad Fatoum <a.fatoum@pengutronix.de>2019-10-28 00:18:30 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2019-11-06 11:22:35 +0100
commit3bf910ddb0488689860e8ee6893142a93fdb3cf1 (patch)
treeeb2940a0cb214944cabeb7cc72bb2ceffd9de4e6
parent6fb978dd7162b4aa708eb415f2bb1278403e041c (diff)
downloadbarebox-3bf910ddb0488689860e8ee6893142a93fdb3cf1.tar.gz
barebox-3bf910ddb0488689860e8ee6893142a93fdb3cf1.tar.xz
nvmem: add read support for STM32MP1 bsec OTP
The bsec on the STM32MP157C provides a 380 byte OTP. Add initial support for reading and writing the shadow copy of the fuses. Direct fuse access is not yet supported. Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
-rw-r--r--arch/arm/dts/stm32mp157c.dtsi4
-rw-r--r--arch/arm/mach-stm32mp/include/mach/bsec.h41
-rw-r--r--arch/arm/mach-stm32mp/include/mach/smc.h28
-rw-r--r--drivers/nvmem/Kconfig8
-rw-r--r--drivers/nvmem/Makefile5
-rw-r--r--drivers/nvmem/bsec.c221
6 files changed, 306 insertions, 1 deletions
diff --git a/arch/arm/dts/stm32mp157c.dtsi b/arch/arm/dts/stm32mp157c.dtsi
index 8d9c84a047..771139c28a 100644
--- a/arch/arm/dts/stm32mp157c.dtsi
+++ b/arch/arm/dts/stm32mp157c.dtsi
@@ -20,3 +20,7 @@
gpio25 = &gpioz;
};
};
+
+&bsec {
+ barebox,provide-mac-address = <&ethernet0 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 0000000000..559faaa2ba
--- /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 <mach/smc.h>
+
+/* 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 0000000000..6b8e62bd53
--- /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 <linux/arm-smccc.h>
+
+/* 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 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);