1055f5df5SYangtao Li // SPDX-License-Identifier: GPL-2.0+ 23d0b16a6SMaxime Ripard /* 33d0b16a6SMaxime Ripard * Allwinner sunXi SoCs Security ID support. 43d0b16a6SMaxime Ripard * 53d0b16a6SMaxime Ripard * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl> 63d0b16a6SMaxime Ripard * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com> 73d0b16a6SMaxime Ripard */ 83d0b16a6SMaxime Ripard 93d0b16a6SMaxime Ripard #include <linux/device.h> 103d0b16a6SMaxime Ripard #include <linux/io.h> 111a963642SIcenowy Zheng #include <linux/iopoll.h> 123d0b16a6SMaxime Ripard #include <linux/module.h> 133d0b16a6SMaxime Ripard #include <linux/nvmem-provider.h> 143d0b16a6SMaxime Ripard #include <linux/of.h> 154a72cda5SIcenowy Zheng #include <linux/of_device.h> 163d0b16a6SMaxime Ripard #include <linux/platform_device.h> 173d0b16a6SMaxime Ripard #include <linux/slab.h> 183d0b16a6SMaxime Ripard #include <linux/random.h> 193d0b16a6SMaxime Ripard 201a963642SIcenowy Zheng /* Registers and special values for doing register-based SID readout on H3 */ 211a963642SIcenowy Zheng #define SUN8I_SID_PRCTL 0x40 221a963642SIcenowy Zheng #define SUN8I_SID_RDKEY 0x60 231a963642SIcenowy Zheng 241a963642SIcenowy Zheng #define SUN8I_SID_OFFSET_MASK 0x1FF 251a963642SIcenowy Zheng #define SUN8I_SID_OFFSET_SHIFT 16 261a963642SIcenowy Zheng #define SUN8I_SID_OP_LOCK (0xAC << 8) 271a963642SIcenowy Zheng #define SUN8I_SID_READ BIT(1) 281a963642SIcenowy Zheng 294a72cda5SIcenowy Zheng struct sunxi_sid_cfg { 301a963642SIcenowy Zheng u32 value_offset; 314a72cda5SIcenowy Zheng u32 size; 321a963642SIcenowy Zheng bool need_register_readout; 334a72cda5SIcenowy Zheng }; 344a72cda5SIcenowy Zheng 353d0b16a6SMaxime Ripard struct sunxi_sid { 363d0b16a6SMaxime Ripard void __iomem *base; 371a963642SIcenowy Zheng u32 value_offset; 383d0b16a6SMaxime Ripard }; 393d0b16a6SMaxime Ripard 409c7b16ebSSrinivas Kandagatla static int sunxi_sid_read(void *context, unsigned int offset, 419c7b16ebSSrinivas Kandagatla void *val, size_t bytes) 423d0b16a6SMaxime Ripard { 433d0b16a6SMaxime Ripard struct sunxi_sid *sid = context; 443d0b16a6SMaxime Ripard 45273a474eSChen-Yu Tsai memcpy_fromio(val, sid->base + sid->value_offset + offset, bytes); 463d0b16a6SMaxime Ripard 473d0b16a6SMaxime Ripard return 0; 483d0b16a6SMaxime Ripard } 493d0b16a6SMaxime Ripard 501a963642SIcenowy Zheng static int sun8i_sid_register_readout(const struct sunxi_sid *sid, 510ab09d65SIcenowy Zheng const unsigned int offset, 520ab09d65SIcenowy Zheng u32 *out) 531a963642SIcenowy Zheng { 541a963642SIcenowy Zheng u32 reg_val; 551a963642SIcenowy Zheng int ret; 561a963642SIcenowy Zheng 571a963642SIcenowy Zheng /* Set word, lock access, and set read command */ 580ab09d65SIcenowy Zheng reg_val = (offset & SUN8I_SID_OFFSET_MASK) 591a963642SIcenowy Zheng << SUN8I_SID_OFFSET_SHIFT; 601a963642SIcenowy Zheng reg_val |= SUN8I_SID_OP_LOCK | SUN8I_SID_READ; 611a963642SIcenowy Zheng writel(reg_val, sid->base + SUN8I_SID_PRCTL); 621a963642SIcenowy Zheng 631a963642SIcenowy Zheng ret = readl_poll_timeout(sid->base + SUN8I_SID_PRCTL, reg_val, 641a963642SIcenowy Zheng !(reg_val & SUN8I_SID_READ), 100, 250000); 651a963642SIcenowy Zheng if (ret) 661a963642SIcenowy Zheng return ret; 671a963642SIcenowy Zheng 680ab09d65SIcenowy Zheng if (out) 690ab09d65SIcenowy Zheng *out = readl(sid->base + SUN8I_SID_RDKEY); 700ab09d65SIcenowy Zheng 711a963642SIcenowy Zheng writel(0, sid->base + SUN8I_SID_PRCTL); 720ab09d65SIcenowy Zheng 730ab09d65SIcenowy Zheng return 0; 740ab09d65SIcenowy Zheng } 750ab09d65SIcenowy Zheng 760ab09d65SIcenowy Zheng /* 770ab09d65SIcenowy Zheng * On Allwinner H3, the value on the 0x200 offset of the SID controller seems 780ab09d65SIcenowy Zheng * to be not reliable at all. 790ab09d65SIcenowy Zheng * Read by the registers instead. 800ab09d65SIcenowy Zheng */ 810ab09d65SIcenowy Zheng static int sun8i_sid_read_by_reg(void *context, unsigned int offset, 820ab09d65SIcenowy Zheng void *val, size_t bytes) 830ab09d65SIcenowy Zheng { 840ab09d65SIcenowy Zheng struct sunxi_sid *sid = context; 85de2a3eaeSChen-Yu Tsai u32 word; 860ab09d65SIcenowy Zheng int ret; 870ab09d65SIcenowy Zheng 88de2a3eaeSChen-Yu Tsai /* .stride = 4 so offset is guaranteed to be aligned */ 89de2a3eaeSChen-Yu Tsai while (bytes >= 4) { 90de2a3eaeSChen-Yu Tsai ret = sun8i_sid_register_readout(sid, offset, val); 910ab09d65SIcenowy Zheng if (ret) 920ab09d65SIcenowy Zheng return ret; 93de2a3eaeSChen-Yu Tsai 94de2a3eaeSChen-Yu Tsai val += 4; 95de2a3eaeSChen-Yu Tsai offset += 4; 96de2a3eaeSChen-Yu Tsai bytes -= 4; 970ab09d65SIcenowy Zheng } 980ab09d65SIcenowy Zheng 99de2a3eaeSChen-Yu Tsai if (!bytes) 100de2a3eaeSChen-Yu Tsai return 0; 101de2a3eaeSChen-Yu Tsai 102de2a3eaeSChen-Yu Tsai /* Handle any trailing bytes */ 103de2a3eaeSChen-Yu Tsai ret = sun8i_sid_register_readout(sid, offset, &word); 104de2a3eaeSChen-Yu Tsai if (ret) 105de2a3eaeSChen-Yu Tsai return ret; 106de2a3eaeSChen-Yu Tsai 107de2a3eaeSChen-Yu Tsai memcpy(val, &word, bytes); 108de2a3eaeSChen-Yu Tsai 1091a963642SIcenowy Zheng return 0; 1101a963642SIcenowy Zheng } 1111a963642SIcenowy Zheng 1123d0b16a6SMaxime Ripard static int sunxi_sid_probe(struct platform_device *pdev) 1133d0b16a6SMaxime Ripard { 1143d0b16a6SMaxime Ripard struct device *dev = &pdev->dev; 1153d0b16a6SMaxime Ripard struct resource *res; 1167fa5ad23SChen-Yu Tsai struct nvmem_config *nvmem_cfg; 1173d0b16a6SMaxime Ripard struct nvmem_device *nvmem; 1183d0b16a6SMaxime Ripard struct sunxi_sid *sid; 1199c4adfb5SChen-Yu Tsai int size; 1203d0b16a6SMaxime Ripard char *randomness; 1214a72cda5SIcenowy Zheng const struct sunxi_sid_cfg *cfg; 1223d0b16a6SMaxime Ripard 1233d0b16a6SMaxime Ripard sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL); 1243d0b16a6SMaxime Ripard if (!sid) 1253d0b16a6SMaxime Ripard return -ENOMEM; 1263d0b16a6SMaxime Ripard 1274a72cda5SIcenowy Zheng cfg = of_device_get_match_data(dev); 1284a72cda5SIcenowy Zheng if (!cfg) 1294a72cda5SIcenowy Zheng return -EINVAL; 1301a963642SIcenowy Zheng sid->value_offset = cfg->value_offset; 1314a72cda5SIcenowy Zheng 1323d0b16a6SMaxime Ripard res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1333d0b16a6SMaxime Ripard sid->base = devm_ioremap_resource(dev, res); 1343d0b16a6SMaxime Ripard if (IS_ERR(sid->base)) 1353d0b16a6SMaxime Ripard return PTR_ERR(sid->base); 1363d0b16a6SMaxime Ripard 1374a72cda5SIcenowy Zheng size = cfg->size; 1384a72cda5SIcenowy Zheng 1397fa5ad23SChen-Yu Tsai nvmem_cfg = devm_kzalloc(dev, sizeof(*nvmem_cfg), GFP_KERNEL); 1407fa5ad23SChen-Yu Tsai if (!nvmem_cfg) 1417fa5ad23SChen-Yu Tsai return -ENOMEM; 1427fa5ad23SChen-Yu Tsai 1437fa5ad23SChen-Yu Tsai nvmem_cfg->dev = dev; 1447fa5ad23SChen-Yu Tsai nvmem_cfg->name = "sunxi-sid"; 145*78a005a2SSamuel Holland nvmem_cfg->type = NVMEM_TYPE_OTP; 1467fa5ad23SChen-Yu Tsai nvmem_cfg->read_only = true; 1477fa5ad23SChen-Yu Tsai nvmem_cfg->size = cfg->size; 1487fa5ad23SChen-Yu Tsai nvmem_cfg->word_size = 1; 1497fa5ad23SChen-Yu Tsai nvmem_cfg->stride = 4; 1507fa5ad23SChen-Yu Tsai nvmem_cfg->priv = sid; 1510ab09d65SIcenowy Zheng if (cfg->need_register_readout) 1527fa5ad23SChen-Yu Tsai nvmem_cfg->reg_read = sun8i_sid_read_by_reg; 1530ab09d65SIcenowy Zheng else 1547fa5ad23SChen-Yu Tsai nvmem_cfg->reg_read = sunxi_sid_read; 1557fa5ad23SChen-Yu Tsai 1567fa5ad23SChen-Yu Tsai nvmem = devm_nvmem_register(dev, nvmem_cfg); 1573d0b16a6SMaxime Ripard if (IS_ERR(nvmem)) 1583d0b16a6SMaxime Ripard return PTR_ERR(nvmem); 1593d0b16a6SMaxime Ripard 1606396bb22SKees Cook randomness = kzalloc(size, GFP_KERNEL); 1616eed8dd9SBartosz Golaszewski if (!randomness) 1626eed8dd9SBartosz Golaszewski return -ENOMEM; 163fb727077SMaxime Ripard 1647fa5ad23SChen-Yu Tsai nvmem_cfg->reg_read(sid, 0, randomness, size); 1653d0b16a6SMaxime Ripard add_device_randomness(randomness, size); 1663d0b16a6SMaxime Ripard kfree(randomness); 1673d0b16a6SMaxime Ripard 1683d0b16a6SMaxime Ripard platform_set_drvdata(pdev, nvmem); 1693d0b16a6SMaxime Ripard 1703d0b16a6SMaxime Ripard return 0; 1713d0b16a6SMaxime Ripard } 1723d0b16a6SMaxime Ripard 1734a72cda5SIcenowy Zheng static const struct sunxi_sid_cfg sun4i_a10_cfg = { 1744a72cda5SIcenowy Zheng .size = 0x10, 1754a72cda5SIcenowy Zheng }; 1764a72cda5SIcenowy Zheng 1774a72cda5SIcenowy Zheng static const struct sunxi_sid_cfg sun7i_a20_cfg = { 1784a72cda5SIcenowy Zheng .size = 0x200, 1794a72cda5SIcenowy Zheng }; 1804a72cda5SIcenowy Zheng 1811a963642SIcenowy Zheng static const struct sunxi_sid_cfg sun8i_h3_cfg = { 1821a963642SIcenowy Zheng .value_offset = 0x200, 1831a963642SIcenowy Zheng .size = 0x100, 1841a963642SIcenowy Zheng .need_register_readout = true, 1851a963642SIcenowy Zheng }; 1861a963642SIcenowy Zheng 187b7fe57b8SIcenowy Zheng static const struct sunxi_sid_cfg sun50i_a64_cfg = { 188b7fe57b8SIcenowy Zheng .value_offset = 0x200, 189b7fe57b8SIcenowy Zheng .size = 0x100, 1902ac00e34SStefan Mavrodiev .need_register_readout = true, 191b7fe57b8SIcenowy Zheng }; 192b7fe57b8SIcenowy Zheng 193fc1eb6ebSYangtao Li static const struct sunxi_sid_cfg sun50i_h6_cfg = { 194fc1eb6ebSYangtao Li .value_offset = 0x200, 195fc1eb6ebSYangtao Li .size = 0x200, 196fc1eb6ebSYangtao Li }; 197fc1eb6ebSYangtao Li 1983d0b16a6SMaxime Ripard static const struct of_device_id sunxi_sid_of_match[] = { 1994a72cda5SIcenowy Zheng { .compatible = "allwinner,sun4i-a10-sid", .data = &sun4i_a10_cfg }, 2004a72cda5SIcenowy Zheng { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg }, 201da75b890SChen-Yu Tsai { .compatible = "allwinner,sun8i-a83t-sid", .data = &sun50i_a64_cfg }, 2021a963642SIcenowy Zheng { .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg }, 203b7fe57b8SIcenowy Zheng { .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg }, 204da75b890SChen-Yu Tsai { .compatible = "allwinner,sun50i-h5-sid", .data = &sun50i_a64_cfg }, 205fc1eb6ebSYangtao Li { .compatible = "allwinner,sun50i-h6-sid", .data = &sun50i_h6_cfg }, 2063d0b16a6SMaxime Ripard {/* sentinel */}, 2073d0b16a6SMaxime Ripard }; 2083d0b16a6SMaxime Ripard MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); 2093d0b16a6SMaxime Ripard 2103d0b16a6SMaxime Ripard static struct platform_driver sunxi_sid_driver = { 2113d0b16a6SMaxime Ripard .probe = sunxi_sid_probe, 2123d0b16a6SMaxime Ripard .driver = { 2133d0b16a6SMaxime Ripard .name = "eeprom-sunxi-sid", 2143d0b16a6SMaxime Ripard .of_match_table = sunxi_sid_of_match, 2153d0b16a6SMaxime Ripard }, 2163d0b16a6SMaxime Ripard }; 2173d0b16a6SMaxime Ripard module_platform_driver(sunxi_sid_driver); 2183d0b16a6SMaxime Ripard 2193d0b16a6SMaxime Ripard MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>"); 2203d0b16a6SMaxime Ripard MODULE_DESCRIPTION("Allwinner sunxi security id driver"); 2213d0b16a6SMaxime Ripard MODULE_LICENSE("GPL"); 222