13d0b16a6SMaxime Ripard /* 23d0b16a6SMaxime Ripard * Allwinner sunXi SoCs Security ID support. 33d0b16a6SMaxime Ripard * 43d0b16a6SMaxime Ripard * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl> 53d0b16a6SMaxime Ripard * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com> 63d0b16a6SMaxime Ripard * 73d0b16a6SMaxime Ripard * This program is free software; you can redistribute it and/or modify 83d0b16a6SMaxime Ripard * it under the terms of the GNU General Public License as published by 93d0b16a6SMaxime Ripard * the Free Software Foundation; either version 2 of the License, or 103d0b16a6SMaxime Ripard * (at your option) any later version. 113d0b16a6SMaxime Ripard * 123d0b16a6SMaxime Ripard * This program is distributed in the hope that it will be useful, 133d0b16a6SMaxime Ripard * but WITHOUT ANY WARRANTY; without even the implied warranty of 143d0b16a6SMaxime Ripard * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 153d0b16a6SMaxime Ripard * GNU General Public License for more details. 163d0b16a6SMaxime Ripard */ 173d0b16a6SMaxime Ripard 183d0b16a6SMaxime Ripard #include <linux/device.h> 193d0b16a6SMaxime Ripard #include <linux/io.h> 201a963642SIcenowy Zheng #include <linux/iopoll.h> 213d0b16a6SMaxime Ripard #include <linux/module.h> 223d0b16a6SMaxime Ripard #include <linux/nvmem-provider.h> 233d0b16a6SMaxime Ripard #include <linux/of.h> 244a72cda5SIcenowy Zheng #include <linux/of_device.h> 253d0b16a6SMaxime Ripard #include <linux/platform_device.h> 263d0b16a6SMaxime Ripard #include <linux/slab.h> 273d0b16a6SMaxime Ripard #include <linux/random.h> 283d0b16a6SMaxime Ripard 291a963642SIcenowy Zheng /* Registers and special values for doing register-based SID readout on H3 */ 301a963642SIcenowy Zheng #define SUN8I_SID_PRCTL 0x40 311a963642SIcenowy Zheng #define SUN8I_SID_RDKEY 0x60 321a963642SIcenowy Zheng 331a963642SIcenowy Zheng #define SUN8I_SID_OFFSET_MASK 0x1FF 341a963642SIcenowy Zheng #define SUN8I_SID_OFFSET_SHIFT 16 351a963642SIcenowy Zheng #define SUN8I_SID_OP_LOCK (0xAC << 8) 361a963642SIcenowy Zheng #define SUN8I_SID_READ BIT(1) 371a963642SIcenowy Zheng 384a72cda5SIcenowy Zheng struct sunxi_sid_cfg { 391a963642SIcenowy Zheng u32 value_offset; 404a72cda5SIcenowy Zheng u32 size; 411a963642SIcenowy Zheng bool need_register_readout; 424a72cda5SIcenowy Zheng }; 434a72cda5SIcenowy Zheng 443d0b16a6SMaxime Ripard struct sunxi_sid { 453d0b16a6SMaxime Ripard void __iomem *base; 461a963642SIcenowy Zheng u32 value_offset; 473d0b16a6SMaxime Ripard }; 483d0b16a6SMaxime Ripard 499c7b16ebSSrinivas Kandagatla static int sunxi_sid_read(void *context, unsigned int offset, 509c7b16ebSSrinivas Kandagatla void *val, size_t bytes) 513d0b16a6SMaxime Ripard { 523d0b16a6SMaxime Ripard struct sunxi_sid *sid = context; 533d0b16a6SMaxime Ripard 54273a474eSChen-Yu Tsai memcpy_fromio(val, sid->base + sid->value_offset + offset, bytes); 553d0b16a6SMaxime Ripard 563d0b16a6SMaxime Ripard return 0; 573d0b16a6SMaxime Ripard } 583d0b16a6SMaxime Ripard 591a963642SIcenowy Zheng static int sun8i_sid_register_readout(const struct sunxi_sid *sid, 600ab09d65SIcenowy Zheng const unsigned int offset, 610ab09d65SIcenowy Zheng u32 *out) 621a963642SIcenowy Zheng { 631a963642SIcenowy Zheng u32 reg_val; 641a963642SIcenowy Zheng int ret; 651a963642SIcenowy Zheng 661a963642SIcenowy Zheng /* Set word, lock access, and set read command */ 670ab09d65SIcenowy Zheng reg_val = (offset & SUN8I_SID_OFFSET_MASK) 681a963642SIcenowy Zheng << SUN8I_SID_OFFSET_SHIFT; 691a963642SIcenowy Zheng reg_val |= SUN8I_SID_OP_LOCK | SUN8I_SID_READ; 701a963642SIcenowy Zheng writel(reg_val, sid->base + SUN8I_SID_PRCTL); 711a963642SIcenowy Zheng 721a963642SIcenowy Zheng ret = readl_poll_timeout(sid->base + SUN8I_SID_PRCTL, reg_val, 731a963642SIcenowy Zheng !(reg_val & SUN8I_SID_READ), 100, 250000); 741a963642SIcenowy Zheng if (ret) 751a963642SIcenowy Zheng return ret; 761a963642SIcenowy Zheng 770ab09d65SIcenowy Zheng if (out) 780ab09d65SIcenowy Zheng *out = readl(sid->base + SUN8I_SID_RDKEY); 790ab09d65SIcenowy Zheng 801a963642SIcenowy Zheng writel(0, sid->base + SUN8I_SID_PRCTL); 810ab09d65SIcenowy Zheng 820ab09d65SIcenowy Zheng return 0; 830ab09d65SIcenowy Zheng } 840ab09d65SIcenowy Zheng 850ab09d65SIcenowy Zheng /* 860ab09d65SIcenowy Zheng * On Allwinner H3, the value on the 0x200 offset of the SID controller seems 870ab09d65SIcenowy Zheng * to be not reliable at all. 880ab09d65SIcenowy Zheng * Read by the registers instead. 890ab09d65SIcenowy Zheng */ 900ab09d65SIcenowy Zheng static int sun8i_sid_read_by_reg(void *context, unsigned int offset, 910ab09d65SIcenowy Zheng void *val, size_t bytes) 920ab09d65SIcenowy Zheng { 930ab09d65SIcenowy Zheng struct sunxi_sid *sid = context; 94de2a3eaeSChen-Yu Tsai u32 word; 950ab09d65SIcenowy Zheng int ret; 960ab09d65SIcenowy Zheng 97de2a3eaeSChen-Yu Tsai /* .stride = 4 so offset is guaranteed to be aligned */ 98de2a3eaeSChen-Yu Tsai while (bytes >= 4) { 99de2a3eaeSChen-Yu Tsai ret = sun8i_sid_register_readout(sid, offset, val); 1000ab09d65SIcenowy Zheng if (ret) 1010ab09d65SIcenowy Zheng return ret; 102de2a3eaeSChen-Yu Tsai 103de2a3eaeSChen-Yu Tsai val += 4; 104de2a3eaeSChen-Yu Tsai offset += 4; 105de2a3eaeSChen-Yu Tsai bytes -= 4; 1060ab09d65SIcenowy Zheng } 1070ab09d65SIcenowy Zheng 108de2a3eaeSChen-Yu Tsai if (!bytes) 109de2a3eaeSChen-Yu Tsai return 0; 110de2a3eaeSChen-Yu Tsai 111de2a3eaeSChen-Yu Tsai /* Handle any trailing bytes */ 112de2a3eaeSChen-Yu Tsai ret = sun8i_sid_register_readout(sid, offset, &word); 113de2a3eaeSChen-Yu Tsai if (ret) 114de2a3eaeSChen-Yu Tsai return ret; 115de2a3eaeSChen-Yu Tsai 116de2a3eaeSChen-Yu Tsai memcpy(val, &word, bytes); 117de2a3eaeSChen-Yu Tsai 1181a963642SIcenowy Zheng return 0; 1191a963642SIcenowy Zheng } 1201a963642SIcenowy Zheng 1213d0b16a6SMaxime Ripard static int sunxi_sid_probe(struct platform_device *pdev) 1223d0b16a6SMaxime Ripard { 1233d0b16a6SMaxime Ripard struct device *dev = &pdev->dev; 1243d0b16a6SMaxime Ripard struct resource *res; 1257fa5ad23SChen-Yu Tsai struct nvmem_config *nvmem_cfg; 1263d0b16a6SMaxime Ripard struct nvmem_device *nvmem; 1273d0b16a6SMaxime Ripard struct sunxi_sid *sid; 1289c4adfb5SChen-Yu Tsai int size; 1293d0b16a6SMaxime Ripard char *randomness; 1304a72cda5SIcenowy Zheng const struct sunxi_sid_cfg *cfg; 1313d0b16a6SMaxime Ripard 1323d0b16a6SMaxime Ripard sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL); 1333d0b16a6SMaxime Ripard if (!sid) 1343d0b16a6SMaxime Ripard return -ENOMEM; 1353d0b16a6SMaxime Ripard 1364a72cda5SIcenowy Zheng cfg = of_device_get_match_data(dev); 1374a72cda5SIcenowy Zheng if (!cfg) 1384a72cda5SIcenowy Zheng return -EINVAL; 1391a963642SIcenowy Zheng sid->value_offset = cfg->value_offset; 1404a72cda5SIcenowy Zheng 1413d0b16a6SMaxime Ripard res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1423d0b16a6SMaxime Ripard sid->base = devm_ioremap_resource(dev, res); 1433d0b16a6SMaxime Ripard if (IS_ERR(sid->base)) 1443d0b16a6SMaxime Ripard return PTR_ERR(sid->base); 1453d0b16a6SMaxime Ripard 1464a72cda5SIcenowy Zheng size = cfg->size; 1474a72cda5SIcenowy Zheng 1487fa5ad23SChen-Yu Tsai nvmem_cfg = devm_kzalloc(dev, sizeof(*nvmem_cfg), GFP_KERNEL); 1497fa5ad23SChen-Yu Tsai if (!nvmem_cfg) 1507fa5ad23SChen-Yu Tsai return -ENOMEM; 1517fa5ad23SChen-Yu Tsai 1527fa5ad23SChen-Yu Tsai nvmem_cfg->dev = dev; 1537fa5ad23SChen-Yu Tsai nvmem_cfg->name = "sunxi-sid"; 1547fa5ad23SChen-Yu Tsai nvmem_cfg->read_only = true; 1557fa5ad23SChen-Yu Tsai nvmem_cfg->size = cfg->size; 1567fa5ad23SChen-Yu Tsai nvmem_cfg->word_size = 1; 1577fa5ad23SChen-Yu Tsai nvmem_cfg->stride = 4; 1587fa5ad23SChen-Yu Tsai nvmem_cfg->priv = sid; 1590ab09d65SIcenowy Zheng if (cfg->need_register_readout) 1607fa5ad23SChen-Yu Tsai nvmem_cfg->reg_read = sun8i_sid_read_by_reg; 1610ab09d65SIcenowy Zheng else 1627fa5ad23SChen-Yu Tsai nvmem_cfg->reg_read = sunxi_sid_read; 1637fa5ad23SChen-Yu Tsai 1647fa5ad23SChen-Yu Tsai nvmem = devm_nvmem_register(dev, nvmem_cfg); 1653d0b16a6SMaxime Ripard if (IS_ERR(nvmem)) 1663d0b16a6SMaxime Ripard return PTR_ERR(nvmem); 1673d0b16a6SMaxime Ripard 1686396bb22SKees Cook randomness = kzalloc(size, GFP_KERNEL); 1696eed8dd9SBartosz Golaszewski if (!randomness) 1706eed8dd9SBartosz Golaszewski return -ENOMEM; 171fb727077SMaxime Ripard 1727fa5ad23SChen-Yu Tsai nvmem_cfg->reg_read(sid, 0, randomness, size); 1733d0b16a6SMaxime Ripard add_device_randomness(randomness, size); 1743d0b16a6SMaxime Ripard kfree(randomness); 1753d0b16a6SMaxime Ripard 1763d0b16a6SMaxime Ripard platform_set_drvdata(pdev, nvmem); 1773d0b16a6SMaxime Ripard 1783d0b16a6SMaxime Ripard return 0; 1793d0b16a6SMaxime Ripard } 1803d0b16a6SMaxime Ripard 1814a72cda5SIcenowy Zheng static const struct sunxi_sid_cfg sun4i_a10_cfg = { 1824a72cda5SIcenowy Zheng .size = 0x10, 1834a72cda5SIcenowy Zheng }; 1844a72cda5SIcenowy Zheng 1854a72cda5SIcenowy Zheng static const struct sunxi_sid_cfg sun7i_a20_cfg = { 1864a72cda5SIcenowy Zheng .size = 0x200, 1874a72cda5SIcenowy Zheng }; 1884a72cda5SIcenowy Zheng 1891a963642SIcenowy Zheng static const struct sunxi_sid_cfg sun8i_h3_cfg = { 1901a963642SIcenowy Zheng .value_offset = 0x200, 1911a963642SIcenowy Zheng .size = 0x100, 1921a963642SIcenowy Zheng .need_register_readout = true, 1931a963642SIcenowy Zheng }; 1941a963642SIcenowy Zheng 195b7fe57b8SIcenowy Zheng static const struct sunxi_sid_cfg sun50i_a64_cfg = { 196b7fe57b8SIcenowy Zheng .value_offset = 0x200, 197b7fe57b8SIcenowy Zheng .size = 0x100, 198b7fe57b8SIcenowy Zheng }; 199b7fe57b8SIcenowy Zheng 2003d0b16a6SMaxime Ripard static const struct of_device_id sunxi_sid_of_match[] = { 2014a72cda5SIcenowy Zheng { .compatible = "allwinner,sun4i-a10-sid", .data = &sun4i_a10_cfg }, 2024a72cda5SIcenowy Zheng { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg }, 203*da75b890SChen-Yu Tsai { .compatible = "allwinner,sun8i-a83t-sid", .data = &sun50i_a64_cfg }, 2041a963642SIcenowy Zheng { .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg }, 205b7fe57b8SIcenowy Zheng { .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg }, 206*da75b890SChen-Yu Tsai { .compatible = "allwinner,sun50i-h5-sid", .data = &sun50i_a64_cfg }, 2073d0b16a6SMaxime Ripard {/* sentinel */}, 2083d0b16a6SMaxime Ripard }; 2093d0b16a6SMaxime Ripard MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); 2103d0b16a6SMaxime Ripard 2113d0b16a6SMaxime Ripard static struct platform_driver sunxi_sid_driver = { 2123d0b16a6SMaxime Ripard .probe = sunxi_sid_probe, 2133d0b16a6SMaxime Ripard .driver = { 2143d0b16a6SMaxime Ripard .name = "eeprom-sunxi-sid", 2153d0b16a6SMaxime Ripard .of_match_table = sunxi_sid_of_match, 2163d0b16a6SMaxime Ripard }, 2173d0b16a6SMaxime Ripard }; 2183d0b16a6SMaxime Ripard module_platform_driver(sunxi_sid_driver); 2193d0b16a6SMaxime Ripard 2203d0b16a6SMaxime Ripard MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>"); 2213d0b16a6SMaxime Ripard MODULE_DESCRIPTION("Allwinner sunxi security id driver"); 2223d0b16a6SMaxime Ripard MODULE_LICENSE("GPL"); 223