18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * STM32 Factory-programmed memory read access driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017, STMicroelectronics - All Rights Reserved 68c2ecf20Sopenharmony_ci * Author: Fabrice Gasnier <fabrice.gasnier@st.com> for STMicroelectronics. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/nvmem-provider.h> 138c2ecf20Sopenharmony_ci#include <linux/of_device.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* BSEC secure service access from non-secure */ 168c2ecf20Sopenharmony_ci#define STM32_SMC_BSEC 0x82001003 178c2ecf20Sopenharmony_ci#define STM32_SMC_READ_SHADOW 0x01 188c2ecf20Sopenharmony_ci#define STM32_SMC_PROG_OTP 0x02 198c2ecf20Sopenharmony_ci#define STM32_SMC_WRITE_SHADOW 0x03 208c2ecf20Sopenharmony_ci#define STM32_SMC_READ_OTP 0x04 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* shadow registers offest */ 238c2ecf20Sopenharmony_ci#define STM32MP15_BSEC_DATA0 0x200 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 32 (x 32-bits) lower shadow registers */ 268c2ecf20Sopenharmony_ci#define STM32MP15_BSEC_NUM_LOWER 32 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct stm32_romem_cfg { 298c2ecf20Sopenharmony_ci int size; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct stm32_romem_priv { 338c2ecf20Sopenharmony_ci void __iomem *base; 348c2ecf20Sopenharmony_ci struct nvmem_config cfg; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int stm32_romem_read(void *context, unsigned int offset, void *buf, 388c2ecf20Sopenharmony_ci size_t bytes) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct stm32_romem_priv *priv = context; 418c2ecf20Sopenharmony_ci u8 *buf8 = buf; 428c2ecf20Sopenharmony_ci int i; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci for (i = offset; i < offset + bytes; i++) 458c2ecf20Sopenharmony_ci *buf8++ = readb_relaxed(priv->base + i); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int stm32_bsec_smc(u8 op, u32 otp, u32 data, u32 *result) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_HAVE_ARM_SMCCC) 538c2ecf20Sopenharmony_ci struct arm_smccc_res res; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci arm_smccc_smc(STM32_SMC_BSEC, op, otp, data, 0, 0, 0, 0, &res); 568c2ecf20Sopenharmony_ci if (res.a0) 578c2ecf20Sopenharmony_ci return -EIO; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (result) 608c2ecf20Sopenharmony_ci *result = (u32)res.a1; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci#else 648c2ecf20Sopenharmony_ci return -ENXIO; 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int stm32_bsec_read(void *context, unsigned int offset, void *buf, 698c2ecf20Sopenharmony_ci size_t bytes) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct stm32_romem_priv *priv = context; 728c2ecf20Sopenharmony_ci struct device *dev = priv->cfg.dev; 738c2ecf20Sopenharmony_ci u32 roffset, rbytes, val; 748c2ecf20Sopenharmony_ci u8 *buf8 = buf, *val8 = (u8 *)&val; 758c2ecf20Sopenharmony_ci int i, j = 0, ret, skip_bytes, size; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* Round unaligned access to 32-bits */ 788c2ecf20Sopenharmony_ci roffset = rounddown(offset, 4); 798c2ecf20Sopenharmony_ci skip_bytes = offset & 0x3; 808c2ecf20Sopenharmony_ci rbytes = roundup(bytes + skip_bytes, 4); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (roffset + rbytes > priv->cfg.size) 838c2ecf20Sopenharmony_ci return -EINVAL; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci for (i = roffset; (i < roffset + rbytes); i += 4) { 868c2ecf20Sopenharmony_ci u32 otp = i >> 2; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (otp < STM32MP15_BSEC_NUM_LOWER) { 898c2ecf20Sopenharmony_ci /* read lower data from shadow registers */ 908c2ecf20Sopenharmony_ci val = readl_relaxed( 918c2ecf20Sopenharmony_ci priv->base + STM32MP15_BSEC_DATA0 + i); 928c2ecf20Sopenharmony_ci } else { 938c2ecf20Sopenharmony_ci ret = stm32_bsec_smc(STM32_SMC_READ_SHADOW, otp, 0, 948c2ecf20Sopenharmony_ci &val); 958c2ecf20Sopenharmony_ci if (ret) { 968c2ecf20Sopenharmony_ci dev_err(dev, "Can't read data%d (%d)\n", otp, 978c2ecf20Sopenharmony_ci ret); 988c2ecf20Sopenharmony_ci return ret; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci /* skip first bytes in case of unaligned read */ 1028c2ecf20Sopenharmony_ci if (skip_bytes) 1038c2ecf20Sopenharmony_ci size = min(bytes, (size_t)(4 - skip_bytes)); 1048c2ecf20Sopenharmony_ci else 1058c2ecf20Sopenharmony_ci size = min(bytes, (size_t)4); 1068c2ecf20Sopenharmony_ci memcpy(&buf8[j], &val8[skip_bytes], size); 1078c2ecf20Sopenharmony_ci bytes -= size; 1088c2ecf20Sopenharmony_ci j += size; 1098c2ecf20Sopenharmony_ci skip_bytes = 0; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int stm32_bsec_write(void *context, unsigned int offset, void *buf, 1168c2ecf20Sopenharmony_ci size_t bytes) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct stm32_romem_priv *priv = context; 1198c2ecf20Sopenharmony_ci struct device *dev = priv->cfg.dev; 1208c2ecf20Sopenharmony_ci u32 *buf32 = buf; 1218c2ecf20Sopenharmony_ci int ret, i; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Allow only writing complete 32-bits aligned words */ 1248c2ecf20Sopenharmony_ci if ((bytes % 4) || (offset % 4)) 1258c2ecf20Sopenharmony_ci return -EINVAL; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci for (i = offset; i < offset + bytes; i += 4) { 1288c2ecf20Sopenharmony_ci ret = stm32_bsec_smc(STM32_SMC_PROG_OTP, i >> 2, *buf32++, 1298c2ecf20Sopenharmony_ci NULL); 1308c2ecf20Sopenharmony_ci if (ret) { 1318c2ecf20Sopenharmony_ci dev_err(dev, "Can't write data%d (%d)\n", i >> 2, ret); 1328c2ecf20Sopenharmony_ci return ret; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int stm32_romem_probe(struct platform_device *pdev) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci const struct stm32_romem_cfg *cfg; 1428c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1438c2ecf20Sopenharmony_ci struct stm32_romem_priv *priv; 1448c2ecf20Sopenharmony_ci struct resource *res; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 1478c2ecf20Sopenharmony_ci if (!priv) 1488c2ecf20Sopenharmony_ci return -ENOMEM; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1518c2ecf20Sopenharmony_ci priv->base = devm_ioremap_resource(dev, res); 1528c2ecf20Sopenharmony_ci if (IS_ERR(priv->base)) 1538c2ecf20Sopenharmony_ci return PTR_ERR(priv->base); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci priv->cfg.name = "stm32-romem"; 1568c2ecf20Sopenharmony_ci priv->cfg.word_size = 1; 1578c2ecf20Sopenharmony_ci priv->cfg.stride = 1; 1588c2ecf20Sopenharmony_ci priv->cfg.dev = dev; 1598c2ecf20Sopenharmony_ci priv->cfg.priv = priv; 1608c2ecf20Sopenharmony_ci priv->cfg.owner = THIS_MODULE; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci cfg = (const struct stm32_romem_cfg *) 1638c2ecf20Sopenharmony_ci of_match_device(dev->driver->of_match_table, dev)->data; 1648c2ecf20Sopenharmony_ci if (!cfg) { 1658c2ecf20Sopenharmony_ci priv->cfg.read_only = true; 1668c2ecf20Sopenharmony_ci priv->cfg.size = resource_size(res); 1678c2ecf20Sopenharmony_ci priv->cfg.reg_read = stm32_romem_read; 1688c2ecf20Sopenharmony_ci } else { 1698c2ecf20Sopenharmony_ci priv->cfg.size = cfg->size; 1708c2ecf20Sopenharmony_ci priv->cfg.reg_read = stm32_bsec_read; 1718c2ecf20Sopenharmony_ci priv->cfg.reg_write = stm32_bsec_write; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &priv->cfg)); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic const struct stm32_romem_cfg stm32mp15_bsec_cfg = { 1788c2ecf20Sopenharmony_ci .size = 384, /* 96 x 32-bits data words */ 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic const struct of_device_id stm32_romem_of_match[] = { 1828c2ecf20Sopenharmony_ci { .compatible = "st,stm32f4-otp", }, { 1838c2ecf20Sopenharmony_ci .compatible = "st,stm32mp15-bsec", 1848c2ecf20Sopenharmony_ci .data = (void *)&stm32mp15_bsec_cfg, 1858c2ecf20Sopenharmony_ci }, { 1868c2ecf20Sopenharmony_ci }, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, stm32_romem_of_match); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic struct platform_driver stm32_romem_driver = { 1918c2ecf20Sopenharmony_ci .probe = stm32_romem_probe, 1928c2ecf20Sopenharmony_ci .driver = { 1938c2ecf20Sopenharmony_ci .name = "stm32-romem", 1948c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(stm32_romem_of_match), 1958c2ecf20Sopenharmony_ci }, 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_cimodule_platform_driver(stm32_romem_driver); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>"); 2008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics STM32 RO-MEM"); 2018c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:nvmem-stm32-romem"); 2028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 203