1ded1b7fcSFabrice Gasnier // SPDX-License-Identifier: GPL-2.0 2ded1b7fcSFabrice Gasnier /* 3ded1b7fcSFabrice Gasnier * STM32 Factory-programmed memory read access driver 4ded1b7fcSFabrice Gasnier * 5ded1b7fcSFabrice Gasnier * Copyright (C) 2017, STMicroelectronics - All Rights Reserved 6ded1b7fcSFabrice Gasnier * Author: Fabrice Gasnier <fabrice.gasnier@st.com> for STMicroelectronics. 7ded1b7fcSFabrice Gasnier */ 8ded1b7fcSFabrice Gasnier 97c1cd8fdSFabrice Gasnier #include <linux/arm-smccc.h> 10ded1b7fcSFabrice Gasnier #include <linux/io.h> 11ded1b7fcSFabrice Gasnier #include <linux/module.h> 12ded1b7fcSFabrice Gasnier #include <linux/nvmem-provider.h> 13ded1b7fcSFabrice Gasnier #include <linux/of_device.h> 14ded1b7fcSFabrice Gasnier 157c1cd8fdSFabrice Gasnier /* BSEC secure service access from non-secure */ 167c1cd8fdSFabrice Gasnier #define STM32_SMC_BSEC 0x82001003 177c1cd8fdSFabrice Gasnier #define STM32_SMC_READ_SHADOW 0x01 187c1cd8fdSFabrice Gasnier #define STM32_SMC_PROG_OTP 0x02 197c1cd8fdSFabrice Gasnier #define STM32_SMC_WRITE_SHADOW 0x03 207c1cd8fdSFabrice Gasnier #define STM32_SMC_READ_OTP 0x04 217c1cd8fdSFabrice Gasnier 227c1cd8fdSFabrice Gasnier /* shadow registers offest */ 237c1cd8fdSFabrice Gasnier #define STM32MP15_BSEC_DATA0 0x200 247c1cd8fdSFabrice Gasnier 257c1cd8fdSFabrice Gasnier struct stm32_romem_cfg { 267c1cd8fdSFabrice Gasnier int size; 27fbfc4ca4SPatrick Delaunay u8 lower; 287c1cd8fdSFabrice Gasnier }; 297c1cd8fdSFabrice Gasnier 30ded1b7fcSFabrice Gasnier struct stm32_romem_priv { 31ded1b7fcSFabrice Gasnier void __iomem *base; 32ded1b7fcSFabrice Gasnier struct nvmem_config cfg; 33fbfc4ca4SPatrick Delaunay u8 lower; 34ded1b7fcSFabrice Gasnier }; 35ded1b7fcSFabrice Gasnier 36ded1b7fcSFabrice Gasnier static int stm32_romem_read(void *context, unsigned int offset, void *buf, 37ded1b7fcSFabrice Gasnier size_t bytes) 38ded1b7fcSFabrice Gasnier { 39ded1b7fcSFabrice Gasnier struct stm32_romem_priv *priv = context; 40ded1b7fcSFabrice Gasnier u8 *buf8 = buf; 41ded1b7fcSFabrice Gasnier int i; 42ded1b7fcSFabrice Gasnier 43ded1b7fcSFabrice Gasnier for (i = offset; i < offset + bytes; i++) 44ded1b7fcSFabrice Gasnier *buf8++ = readb_relaxed(priv->base + i); 45ded1b7fcSFabrice Gasnier 46ded1b7fcSFabrice Gasnier return 0; 47ded1b7fcSFabrice Gasnier } 48ded1b7fcSFabrice Gasnier 497c1cd8fdSFabrice Gasnier static int stm32_bsec_smc(u8 op, u32 otp, u32 data, u32 *result) 507c1cd8fdSFabrice Gasnier { 517c1cd8fdSFabrice Gasnier #if IS_ENABLED(CONFIG_HAVE_ARM_SMCCC) 527c1cd8fdSFabrice Gasnier struct arm_smccc_res res; 537c1cd8fdSFabrice Gasnier 547c1cd8fdSFabrice Gasnier arm_smccc_smc(STM32_SMC_BSEC, op, otp, data, 0, 0, 0, 0, &res); 557c1cd8fdSFabrice Gasnier if (res.a0) 567c1cd8fdSFabrice Gasnier return -EIO; 577c1cd8fdSFabrice Gasnier 587c1cd8fdSFabrice Gasnier if (result) 597c1cd8fdSFabrice Gasnier *result = (u32)res.a1; 607c1cd8fdSFabrice Gasnier 617c1cd8fdSFabrice Gasnier return 0; 627c1cd8fdSFabrice Gasnier #else 637c1cd8fdSFabrice Gasnier return -ENXIO; 647c1cd8fdSFabrice Gasnier #endif 657c1cd8fdSFabrice Gasnier } 667c1cd8fdSFabrice Gasnier 677c1cd8fdSFabrice Gasnier static int stm32_bsec_read(void *context, unsigned int offset, void *buf, 687c1cd8fdSFabrice Gasnier size_t bytes) 697c1cd8fdSFabrice Gasnier { 707c1cd8fdSFabrice Gasnier struct stm32_romem_priv *priv = context; 717c1cd8fdSFabrice Gasnier struct device *dev = priv->cfg.dev; 727c1cd8fdSFabrice Gasnier u32 roffset, rbytes, val; 737c1cd8fdSFabrice Gasnier u8 *buf8 = buf, *val8 = (u8 *)&val; 747c1cd8fdSFabrice Gasnier int i, j = 0, ret, skip_bytes, size; 757c1cd8fdSFabrice Gasnier 767c1cd8fdSFabrice Gasnier /* Round unaligned access to 32-bits */ 777c1cd8fdSFabrice Gasnier roffset = rounddown(offset, 4); 787c1cd8fdSFabrice Gasnier skip_bytes = offset & 0x3; 797c1cd8fdSFabrice Gasnier rbytes = roundup(bytes + skip_bytes, 4); 807c1cd8fdSFabrice Gasnier 817c1cd8fdSFabrice Gasnier if (roffset + rbytes > priv->cfg.size) 827c1cd8fdSFabrice Gasnier return -EINVAL; 837c1cd8fdSFabrice Gasnier 847c1cd8fdSFabrice Gasnier for (i = roffset; (i < roffset + rbytes); i += 4) { 857c1cd8fdSFabrice Gasnier u32 otp = i >> 2; 867c1cd8fdSFabrice Gasnier 87fbfc4ca4SPatrick Delaunay if (otp < priv->lower) { 887c1cd8fdSFabrice Gasnier /* read lower data from shadow registers */ 897c1cd8fdSFabrice Gasnier val = readl_relaxed( 907c1cd8fdSFabrice Gasnier priv->base + STM32MP15_BSEC_DATA0 + i); 917c1cd8fdSFabrice Gasnier } else { 927c1cd8fdSFabrice Gasnier ret = stm32_bsec_smc(STM32_SMC_READ_SHADOW, otp, 0, 937c1cd8fdSFabrice Gasnier &val); 947c1cd8fdSFabrice Gasnier if (ret) { 957c1cd8fdSFabrice Gasnier dev_err(dev, "Can't read data%d (%d)\n", otp, 967c1cd8fdSFabrice Gasnier ret); 977c1cd8fdSFabrice Gasnier return ret; 987c1cd8fdSFabrice Gasnier } 997c1cd8fdSFabrice Gasnier } 1007c1cd8fdSFabrice Gasnier /* skip first bytes in case of unaligned read */ 1017c1cd8fdSFabrice Gasnier if (skip_bytes) 1027c1cd8fdSFabrice Gasnier size = min(bytes, (size_t)(4 - skip_bytes)); 1037c1cd8fdSFabrice Gasnier else 1047c1cd8fdSFabrice Gasnier size = min(bytes, (size_t)4); 1057c1cd8fdSFabrice Gasnier memcpy(&buf8[j], &val8[skip_bytes], size); 1067c1cd8fdSFabrice Gasnier bytes -= size; 1077c1cd8fdSFabrice Gasnier j += size; 1087c1cd8fdSFabrice Gasnier skip_bytes = 0; 1097c1cd8fdSFabrice Gasnier } 1107c1cd8fdSFabrice Gasnier 1117c1cd8fdSFabrice Gasnier return 0; 1127c1cd8fdSFabrice Gasnier } 1137c1cd8fdSFabrice Gasnier 1147c1cd8fdSFabrice Gasnier static int stm32_bsec_write(void *context, unsigned int offset, void *buf, 1157c1cd8fdSFabrice Gasnier size_t bytes) 1167c1cd8fdSFabrice Gasnier { 1177c1cd8fdSFabrice Gasnier struct stm32_romem_priv *priv = context; 1187c1cd8fdSFabrice Gasnier struct device *dev = priv->cfg.dev; 1197c1cd8fdSFabrice Gasnier u32 *buf32 = buf; 1207c1cd8fdSFabrice Gasnier int ret, i; 1217c1cd8fdSFabrice Gasnier 1227c1cd8fdSFabrice Gasnier /* Allow only writing complete 32-bits aligned words */ 1237c1cd8fdSFabrice Gasnier if ((bytes % 4) || (offset % 4)) 1247c1cd8fdSFabrice Gasnier return -EINVAL; 1257c1cd8fdSFabrice Gasnier 1267c1cd8fdSFabrice Gasnier for (i = offset; i < offset + bytes; i += 4) { 1277c1cd8fdSFabrice Gasnier ret = stm32_bsec_smc(STM32_SMC_PROG_OTP, i >> 2, *buf32++, 1287c1cd8fdSFabrice Gasnier NULL); 1297c1cd8fdSFabrice Gasnier if (ret) { 1307c1cd8fdSFabrice Gasnier dev_err(dev, "Can't write data%d (%d)\n", i >> 2, ret); 1317c1cd8fdSFabrice Gasnier return ret; 1327c1cd8fdSFabrice Gasnier } 1337c1cd8fdSFabrice Gasnier } 1347c1cd8fdSFabrice Gasnier 135d61784e6SPatrick Delaunay if (offset + bytes >= priv->lower * 4) 136d61784e6SPatrick Delaunay dev_warn(dev, "Update of upper OTPs with ECC protection (word programming, only once)\n"); 137d61784e6SPatrick Delaunay 1387c1cd8fdSFabrice Gasnier return 0; 1397c1cd8fdSFabrice Gasnier } 1407c1cd8fdSFabrice Gasnier 141ded1b7fcSFabrice Gasnier static int stm32_romem_probe(struct platform_device *pdev) 142ded1b7fcSFabrice Gasnier { 1437c1cd8fdSFabrice Gasnier const struct stm32_romem_cfg *cfg; 144ded1b7fcSFabrice Gasnier struct device *dev = &pdev->dev; 145ded1b7fcSFabrice Gasnier struct stm32_romem_priv *priv; 146ded1b7fcSFabrice Gasnier struct resource *res; 147ded1b7fcSFabrice Gasnier 148ded1b7fcSFabrice Gasnier priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 149ded1b7fcSFabrice Gasnier if (!priv) 150ded1b7fcSFabrice Gasnier return -ENOMEM; 151ded1b7fcSFabrice Gasnier 152ded1b7fcSFabrice Gasnier res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 153ded1b7fcSFabrice Gasnier priv->base = devm_ioremap_resource(dev, res); 154ded1b7fcSFabrice Gasnier if (IS_ERR(priv->base)) 155ded1b7fcSFabrice Gasnier return PTR_ERR(priv->base); 156ded1b7fcSFabrice Gasnier 157ded1b7fcSFabrice Gasnier priv->cfg.name = "stm32-romem"; 158ded1b7fcSFabrice Gasnier priv->cfg.word_size = 1; 159ded1b7fcSFabrice Gasnier priv->cfg.stride = 1; 160ded1b7fcSFabrice Gasnier priv->cfg.dev = dev; 161ded1b7fcSFabrice Gasnier priv->cfg.priv = priv; 162ded1b7fcSFabrice Gasnier priv->cfg.owner = THIS_MODULE; 163*a3816a7dSPatrick Delaunay priv->cfg.type = NVMEM_TYPE_OTP; 164ded1b7fcSFabrice Gasnier 165fbfc4ca4SPatrick Delaunay priv->lower = 0; 166fbfc4ca4SPatrick Delaunay 1677c1cd8fdSFabrice Gasnier cfg = (const struct stm32_romem_cfg *) 1687c1cd8fdSFabrice Gasnier of_match_device(dev->driver->of_match_table, dev)->data; 1697c1cd8fdSFabrice Gasnier if (!cfg) { 1707c1cd8fdSFabrice Gasnier priv->cfg.read_only = true; 1717c1cd8fdSFabrice Gasnier priv->cfg.size = resource_size(res); 1727c1cd8fdSFabrice Gasnier priv->cfg.reg_read = stm32_romem_read; 1737c1cd8fdSFabrice Gasnier } else { 1747c1cd8fdSFabrice Gasnier priv->cfg.size = cfg->size; 175fbfc4ca4SPatrick Delaunay priv->lower = cfg->lower; 1767c1cd8fdSFabrice Gasnier priv->cfg.reg_read = stm32_bsec_read; 1777c1cd8fdSFabrice Gasnier priv->cfg.reg_write = stm32_bsec_write; 1787c1cd8fdSFabrice Gasnier } 1797c1cd8fdSFabrice Gasnier 180ded1b7fcSFabrice Gasnier return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &priv->cfg)); 181ded1b7fcSFabrice Gasnier } 182ded1b7fcSFabrice Gasnier 183fbfc4ca4SPatrick Delaunay /* 184fbfc4ca4SPatrick Delaunay * STM32MP15 BSEC OTP regions: 4096 OTP bits (with 3072 effective bits) 185fbfc4ca4SPatrick Delaunay * => 96 x 32-bits data words 186fbfc4ca4SPatrick Delaunay * - Lower: 1K bits, 2:1 redundancy, incremental bit programming 187fbfc4ca4SPatrick Delaunay * => 32 (x 32-bits) lower shadow registers = words 0 to 31 188fbfc4ca4SPatrick Delaunay * - Upper: 2K bits, ECC protection, word programming only 189fbfc4ca4SPatrick Delaunay * => 64 (x 32-bits) = words 32 to 95 190fbfc4ca4SPatrick Delaunay */ 1917c1cd8fdSFabrice Gasnier static const struct stm32_romem_cfg stm32mp15_bsec_cfg = { 192fbfc4ca4SPatrick Delaunay .size = 384, 193fbfc4ca4SPatrick Delaunay .lower = 32, 1947c1cd8fdSFabrice Gasnier }; 1957c1cd8fdSFabrice Gasnier 196ded1b7fcSFabrice Gasnier static const struct of_device_id stm32_romem_of_match[] = { 1977c1cd8fdSFabrice Gasnier { .compatible = "st,stm32f4-otp", }, { 1987c1cd8fdSFabrice Gasnier .compatible = "st,stm32mp15-bsec", 1997c1cd8fdSFabrice Gasnier .data = (void *)&stm32mp15_bsec_cfg, 2007c1cd8fdSFabrice Gasnier }, { 2017c1cd8fdSFabrice Gasnier }, 202ded1b7fcSFabrice Gasnier }; 203ded1b7fcSFabrice Gasnier MODULE_DEVICE_TABLE(of, stm32_romem_of_match); 204ded1b7fcSFabrice Gasnier 205ded1b7fcSFabrice Gasnier static struct platform_driver stm32_romem_driver = { 206ded1b7fcSFabrice Gasnier .probe = stm32_romem_probe, 207ded1b7fcSFabrice Gasnier .driver = { 208ded1b7fcSFabrice Gasnier .name = "stm32-romem", 209ded1b7fcSFabrice Gasnier .of_match_table = of_match_ptr(stm32_romem_of_match), 210ded1b7fcSFabrice Gasnier }, 211ded1b7fcSFabrice Gasnier }; 212ded1b7fcSFabrice Gasnier module_platform_driver(stm32_romem_driver); 213ded1b7fcSFabrice Gasnier 214ded1b7fcSFabrice Gasnier MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>"); 215ded1b7fcSFabrice Gasnier MODULE_DESCRIPTION("STMicroelectronics STM32 RO-MEM"); 216ded1b7fcSFabrice Gasnier MODULE_ALIAS("platform:nvmem-stm32-romem"); 217ded1b7fcSFabrice Gasnier MODULE_LICENSE("GPL v2"); 218