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 9*7c1cd8fdSFabrice 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 15*7c1cd8fdSFabrice Gasnier /* BSEC secure service access from non-secure */ 16*7c1cd8fdSFabrice Gasnier #define STM32_SMC_BSEC 0x82001003 17*7c1cd8fdSFabrice Gasnier #define STM32_SMC_READ_SHADOW 0x01 18*7c1cd8fdSFabrice Gasnier #define STM32_SMC_PROG_OTP 0x02 19*7c1cd8fdSFabrice Gasnier #define STM32_SMC_WRITE_SHADOW 0x03 20*7c1cd8fdSFabrice Gasnier #define STM32_SMC_READ_OTP 0x04 21*7c1cd8fdSFabrice Gasnier 22*7c1cd8fdSFabrice Gasnier /* shadow registers offest */ 23*7c1cd8fdSFabrice Gasnier #define STM32MP15_BSEC_DATA0 0x200 24*7c1cd8fdSFabrice Gasnier 25*7c1cd8fdSFabrice Gasnier /* 32 (x 32-bits) lower shadow registers */ 26*7c1cd8fdSFabrice Gasnier #define STM32MP15_BSEC_NUM_LOWER 32 27*7c1cd8fdSFabrice Gasnier 28*7c1cd8fdSFabrice Gasnier struct stm32_romem_cfg { 29*7c1cd8fdSFabrice Gasnier int size; 30*7c1cd8fdSFabrice Gasnier }; 31*7c1cd8fdSFabrice Gasnier 32ded1b7fcSFabrice Gasnier struct stm32_romem_priv { 33ded1b7fcSFabrice Gasnier void __iomem *base; 34ded1b7fcSFabrice Gasnier struct nvmem_config cfg; 35ded1b7fcSFabrice Gasnier }; 36ded1b7fcSFabrice Gasnier 37ded1b7fcSFabrice Gasnier static int stm32_romem_read(void *context, unsigned int offset, void *buf, 38ded1b7fcSFabrice Gasnier size_t bytes) 39ded1b7fcSFabrice Gasnier { 40ded1b7fcSFabrice Gasnier struct stm32_romem_priv *priv = context; 41ded1b7fcSFabrice Gasnier u8 *buf8 = buf; 42ded1b7fcSFabrice Gasnier int i; 43ded1b7fcSFabrice Gasnier 44ded1b7fcSFabrice Gasnier for (i = offset; i < offset + bytes; i++) 45ded1b7fcSFabrice Gasnier *buf8++ = readb_relaxed(priv->base + i); 46ded1b7fcSFabrice Gasnier 47ded1b7fcSFabrice Gasnier return 0; 48ded1b7fcSFabrice Gasnier } 49ded1b7fcSFabrice Gasnier 50*7c1cd8fdSFabrice Gasnier static int stm32_bsec_smc(u8 op, u32 otp, u32 data, u32 *result) 51*7c1cd8fdSFabrice Gasnier { 52*7c1cd8fdSFabrice Gasnier #if IS_ENABLED(CONFIG_HAVE_ARM_SMCCC) 53*7c1cd8fdSFabrice Gasnier struct arm_smccc_res res; 54*7c1cd8fdSFabrice Gasnier 55*7c1cd8fdSFabrice Gasnier arm_smccc_smc(STM32_SMC_BSEC, op, otp, data, 0, 0, 0, 0, &res); 56*7c1cd8fdSFabrice Gasnier if (res.a0) 57*7c1cd8fdSFabrice Gasnier return -EIO; 58*7c1cd8fdSFabrice Gasnier 59*7c1cd8fdSFabrice Gasnier if (result) 60*7c1cd8fdSFabrice Gasnier *result = (u32)res.a1; 61*7c1cd8fdSFabrice Gasnier 62*7c1cd8fdSFabrice Gasnier return 0; 63*7c1cd8fdSFabrice Gasnier #else 64*7c1cd8fdSFabrice Gasnier return -ENXIO; 65*7c1cd8fdSFabrice Gasnier #endif 66*7c1cd8fdSFabrice Gasnier } 67*7c1cd8fdSFabrice Gasnier 68*7c1cd8fdSFabrice Gasnier static int stm32_bsec_read(void *context, unsigned int offset, void *buf, 69*7c1cd8fdSFabrice Gasnier size_t bytes) 70*7c1cd8fdSFabrice Gasnier { 71*7c1cd8fdSFabrice Gasnier struct stm32_romem_priv *priv = context; 72*7c1cd8fdSFabrice Gasnier struct device *dev = priv->cfg.dev; 73*7c1cd8fdSFabrice Gasnier u32 roffset, rbytes, val; 74*7c1cd8fdSFabrice Gasnier u8 *buf8 = buf, *val8 = (u8 *)&val; 75*7c1cd8fdSFabrice Gasnier int i, j = 0, ret, skip_bytes, size; 76*7c1cd8fdSFabrice Gasnier 77*7c1cd8fdSFabrice Gasnier /* Round unaligned access to 32-bits */ 78*7c1cd8fdSFabrice Gasnier roffset = rounddown(offset, 4); 79*7c1cd8fdSFabrice Gasnier skip_bytes = offset & 0x3; 80*7c1cd8fdSFabrice Gasnier rbytes = roundup(bytes + skip_bytes, 4); 81*7c1cd8fdSFabrice Gasnier 82*7c1cd8fdSFabrice Gasnier if (roffset + rbytes > priv->cfg.size) 83*7c1cd8fdSFabrice Gasnier return -EINVAL; 84*7c1cd8fdSFabrice Gasnier 85*7c1cd8fdSFabrice Gasnier for (i = roffset; (i < roffset + rbytes); i += 4) { 86*7c1cd8fdSFabrice Gasnier u32 otp = i >> 2; 87*7c1cd8fdSFabrice Gasnier 88*7c1cd8fdSFabrice Gasnier if (otp < STM32MP15_BSEC_NUM_LOWER) { 89*7c1cd8fdSFabrice Gasnier /* read lower data from shadow registers */ 90*7c1cd8fdSFabrice Gasnier val = readl_relaxed( 91*7c1cd8fdSFabrice Gasnier priv->base + STM32MP15_BSEC_DATA0 + i); 92*7c1cd8fdSFabrice Gasnier } else { 93*7c1cd8fdSFabrice Gasnier ret = stm32_bsec_smc(STM32_SMC_READ_SHADOW, otp, 0, 94*7c1cd8fdSFabrice Gasnier &val); 95*7c1cd8fdSFabrice Gasnier if (ret) { 96*7c1cd8fdSFabrice Gasnier dev_err(dev, "Can't read data%d (%d)\n", otp, 97*7c1cd8fdSFabrice Gasnier ret); 98*7c1cd8fdSFabrice Gasnier return ret; 99*7c1cd8fdSFabrice Gasnier } 100*7c1cd8fdSFabrice Gasnier } 101*7c1cd8fdSFabrice Gasnier /* skip first bytes in case of unaligned read */ 102*7c1cd8fdSFabrice Gasnier if (skip_bytes) 103*7c1cd8fdSFabrice Gasnier size = min(bytes, (size_t)(4 - skip_bytes)); 104*7c1cd8fdSFabrice Gasnier else 105*7c1cd8fdSFabrice Gasnier size = min(bytes, (size_t)4); 106*7c1cd8fdSFabrice Gasnier memcpy(&buf8[j], &val8[skip_bytes], size); 107*7c1cd8fdSFabrice Gasnier bytes -= size; 108*7c1cd8fdSFabrice Gasnier j += size; 109*7c1cd8fdSFabrice Gasnier skip_bytes = 0; 110*7c1cd8fdSFabrice Gasnier } 111*7c1cd8fdSFabrice Gasnier 112*7c1cd8fdSFabrice Gasnier return 0; 113*7c1cd8fdSFabrice Gasnier } 114*7c1cd8fdSFabrice Gasnier 115*7c1cd8fdSFabrice Gasnier static int stm32_bsec_write(void *context, unsigned int offset, void *buf, 116*7c1cd8fdSFabrice Gasnier size_t bytes) 117*7c1cd8fdSFabrice Gasnier { 118*7c1cd8fdSFabrice Gasnier struct stm32_romem_priv *priv = context; 119*7c1cd8fdSFabrice Gasnier struct device *dev = priv->cfg.dev; 120*7c1cd8fdSFabrice Gasnier u32 *buf32 = buf; 121*7c1cd8fdSFabrice Gasnier int ret, i; 122*7c1cd8fdSFabrice Gasnier 123*7c1cd8fdSFabrice Gasnier /* Allow only writing complete 32-bits aligned words */ 124*7c1cd8fdSFabrice Gasnier if ((bytes % 4) || (offset % 4)) 125*7c1cd8fdSFabrice Gasnier return -EINVAL; 126*7c1cd8fdSFabrice Gasnier 127*7c1cd8fdSFabrice Gasnier for (i = offset; i < offset + bytes; i += 4) { 128*7c1cd8fdSFabrice Gasnier ret = stm32_bsec_smc(STM32_SMC_PROG_OTP, i >> 2, *buf32++, 129*7c1cd8fdSFabrice Gasnier NULL); 130*7c1cd8fdSFabrice Gasnier if (ret) { 131*7c1cd8fdSFabrice Gasnier dev_err(dev, "Can't write data%d (%d)\n", i >> 2, ret); 132*7c1cd8fdSFabrice Gasnier return ret; 133*7c1cd8fdSFabrice Gasnier } 134*7c1cd8fdSFabrice Gasnier } 135*7c1cd8fdSFabrice Gasnier 136*7c1cd8fdSFabrice Gasnier return 0; 137*7c1cd8fdSFabrice Gasnier } 138*7c1cd8fdSFabrice Gasnier 139ded1b7fcSFabrice Gasnier static int stm32_romem_probe(struct platform_device *pdev) 140ded1b7fcSFabrice Gasnier { 141*7c1cd8fdSFabrice Gasnier const struct stm32_romem_cfg *cfg; 142ded1b7fcSFabrice Gasnier struct device *dev = &pdev->dev; 143ded1b7fcSFabrice Gasnier struct stm32_romem_priv *priv; 144ded1b7fcSFabrice Gasnier struct resource *res; 145ded1b7fcSFabrice Gasnier 146ded1b7fcSFabrice Gasnier priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 147ded1b7fcSFabrice Gasnier if (!priv) 148ded1b7fcSFabrice Gasnier return -ENOMEM; 149ded1b7fcSFabrice Gasnier 150ded1b7fcSFabrice Gasnier res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 151ded1b7fcSFabrice Gasnier priv->base = devm_ioremap_resource(dev, res); 152ded1b7fcSFabrice Gasnier if (IS_ERR(priv->base)) 153ded1b7fcSFabrice Gasnier return PTR_ERR(priv->base); 154ded1b7fcSFabrice Gasnier 155ded1b7fcSFabrice Gasnier priv->cfg.name = "stm32-romem"; 156ded1b7fcSFabrice Gasnier priv->cfg.word_size = 1; 157ded1b7fcSFabrice Gasnier priv->cfg.stride = 1; 158ded1b7fcSFabrice Gasnier priv->cfg.dev = dev; 159ded1b7fcSFabrice Gasnier priv->cfg.priv = priv; 160ded1b7fcSFabrice Gasnier priv->cfg.owner = THIS_MODULE; 161ded1b7fcSFabrice Gasnier 162*7c1cd8fdSFabrice Gasnier cfg = (const struct stm32_romem_cfg *) 163*7c1cd8fdSFabrice Gasnier of_match_device(dev->driver->of_match_table, dev)->data; 164*7c1cd8fdSFabrice Gasnier if (!cfg) { 165*7c1cd8fdSFabrice Gasnier priv->cfg.read_only = true; 166*7c1cd8fdSFabrice Gasnier priv->cfg.size = resource_size(res); 167*7c1cd8fdSFabrice Gasnier priv->cfg.reg_read = stm32_romem_read; 168*7c1cd8fdSFabrice Gasnier } else { 169*7c1cd8fdSFabrice Gasnier priv->cfg.size = cfg->size; 170*7c1cd8fdSFabrice Gasnier priv->cfg.reg_read = stm32_bsec_read; 171*7c1cd8fdSFabrice Gasnier priv->cfg.reg_write = stm32_bsec_write; 172*7c1cd8fdSFabrice Gasnier } 173*7c1cd8fdSFabrice Gasnier 174ded1b7fcSFabrice Gasnier return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &priv->cfg)); 175ded1b7fcSFabrice Gasnier } 176ded1b7fcSFabrice Gasnier 177*7c1cd8fdSFabrice Gasnier static const struct stm32_romem_cfg stm32mp15_bsec_cfg = { 178*7c1cd8fdSFabrice Gasnier .size = 384, /* 96 x 32-bits data words */ 179*7c1cd8fdSFabrice Gasnier }; 180*7c1cd8fdSFabrice Gasnier 181ded1b7fcSFabrice Gasnier static const struct of_device_id stm32_romem_of_match[] = { 182*7c1cd8fdSFabrice Gasnier { .compatible = "st,stm32f4-otp", }, { 183*7c1cd8fdSFabrice Gasnier .compatible = "st,stm32mp15-bsec", 184*7c1cd8fdSFabrice Gasnier .data = (void *)&stm32mp15_bsec_cfg, 185*7c1cd8fdSFabrice Gasnier }, { 186*7c1cd8fdSFabrice Gasnier }, 187ded1b7fcSFabrice Gasnier }; 188ded1b7fcSFabrice Gasnier MODULE_DEVICE_TABLE(of, stm32_romem_of_match); 189ded1b7fcSFabrice Gasnier 190ded1b7fcSFabrice Gasnier static struct platform_driver stm32_romem_driver = { 191ded1b7fcSFabrice Gasnier .probe = stm32_romem_probe, 192ded1b7fcSFabrice Gasnier .driver = { 193ded1b7fcSFabrice Gasnier .name = "stm32-romem", 194ded1b7fcSFabrice Gasnier .of_match_table = of_match_ptr(stm32_romem_of_match), 195ded1b7fcSFabrice Gasnier }, 196ded1b7fcSFabrice Gasnier }; 197ded1b7fcSFabrice Gasnier module_platform_driver(stm32_romem_driver); 198ded1b7fcSFabrice Gasnier 199ded1b7fcSFabrice Gasnier MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>"); 200ded1b7fcSFabrice Gasnier MODULE_DESCRIPTION("STMicroelectronics STM32 RO-MEM"); 201ded1b7fcSFabrice Gasnier MODULE_ALIAS("platform:nvmem-stm32-romem"); 202ded1b7fcSFabrice Gasnier MODULE_LICENSE("GPL v2"); 203