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> 203d0b16a6SMaxime Ripard #include <linux/module.h> 213d0b16a6SMaxime Ripard #include <linux/nvmem-provider.h> 223d0b16a6SMaxime Ripard #include <linux/of.h> 23*4a72cda5SIcenowy Zheng #include <linux/of_device.h> 243d0b16a6SMaxime Ripard #include <linux/platform_device.h> 253d0b16a6SMaxime Ripard #include <linux/slab.h> 263d0b16a6SMaxime Ripard #include <linux/random.h> 273d0b16a6SMaxime Ripard 283d0b16a6SMaxime Ripard static struct nvmem_config econfig = { 293d0b16a6SMaxime Ripard .name = "sunxi-sid", 303d0b16a6SMaxime Ripard .read_only = true, 319c7b16ebSSrinivas Kandagatla .stride = 4, 329c7b16ebSSrinivas Kandagatla .word_size = 1, 333d0b16a6SMaxime Ripard .owner = THIS_MODULE, 343d0b16a6SMaxime Ripard }; 353d0b16a6SMaxime Ripard 36*4a72cda5SIcenowy Zheng struct sunxi_sid_cfg { 37*4a72cda5SIcenowy Zheng u32 size; 38*4a72cda5SIcenowy Zheng }; 39*4a72cda5SIcenowy Zheng 403d0b16a6SMaxime Ripard struct sunxi_sid { 413d0b16a6SMaxime Ripard void __iomem *base; 423d0b16a6SMaxime Ripard }; 433d0b16a6SMaxime Ripard 443d0b16a6SMaxime Ripard /* We read the entire key, due to a 32 bit read alignment requirement. Since we 453d0b16a6SMaxime Ripard * want to return the requested byte, this results in somewhat slower code and 463d0b16a6SMaxime Ripard * uses 4 times more reads as needed but keeps code simpler. Since the SID is 473d0b16a6SMaxime Ripard * only very rarely probed, this is not really an issue. 483d0b16a6SMaxime Ripard */ 493d0b16a6SMaxime Ripard static u8 sunxi_sid_read_byte(const struct sunxi_sid *sid, 503d0b16a6SMaxime Ripard const unsigned int offset) 513d0b16a6SMaxime Ripard { 523d0b16a6SMaxime Ripard u32 sid_key; 533d0b16a6SMaxime Ripard 543d0b16a6SMaxime Ripard sid_key = ioread32be(sid->base + round_down(offset, 4)); 553d0b16a6SMaxime Ripard sid_key >>= (offset % 4) * 8; 563d0b16a6SMaxime Ripard 573d0b16a6SMaxime Ripard return sid_key; /* Only return the last byte */ 583d0b16a6SMaxime Ripard } 593d0b16a6SMaxime Ripard 609c7b16ebSSrinivas Kandagatla static int sunxi_sid_read(void *context, unsigned int offset, 619c7b16ebSSrinivas Kandagatla void *val, size_t bytes) 623d0b16a6SMaxime Ripard { 633d0b16a6SMaxime Ripard struct sunxi_sid *sid = context; 643d0b16a6SMaxime Ripard u8 *buf = val; 653d0b16a6SMaxime Ripard 669c7b16ebSSrinivas Kandagatla while (bytes--) 679c7b16ebSSrinivas Kandagatla *buf++ = sunxi_sid_read_byte(sid, offset++); 683d0b16a6SMaxime Ripard 693d0b16a6SMaxime Ripard return 0; 703d0b16a6SMaxime Ripard } 713d0b16a6SMaxime Ripard 723d0b16a6SMaxime Ripard static int sunxi_sid_probe(struct platform_device *pdev) 733d0b16a6SMaxime Ripard { 743d0b16a6SMaxime Ripard struct device *dev = &pdev->dev; 753d0b16a6SMaxime Ripard struct resource *res; 763d0b16a6SMaxime Ripard struct nvmem_device *nvmem; 773d0b16a6SMaxime Ripard struct sunxi_sid *sid; 78fb727077SMaxime Ripard int ret, i, size; 793d0b16a6SMaxime Ripard char *randomness; 80*4a72cda5SIcenowy Zheng const struct sunxi_sid_cfg *cfg; 813d0b16a6SMaxime Ripard 823d0b16a6SMaxime Ripard sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL); 833d0b16a6SMaxime Ripard if (!sid) 843d0b16a6SMaxime Ripard return -ENOMEM; 853d0b16a6SMaxime Ripard 86*4a72cda5SIcenowy Zheng cfg = of_device_get_match_data(dev); 87*4a72cda5SIcenowy Zheng if (!cfg) 88*4a72cda5SIcenowy Zheng return -EINVAL; 89*4a72cda5SIcenowy Zheng 903d0b16a6SMaxime Ripard res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 913d0b16a6SMaxime Ripard sid->base = devm_ioremap_resource(dev, res); 923d0b16a6SMaxime Ripard if (IS_ERR(sid->base)) 933d0b16a6SMaxime Ripard return PTR_ERR(sid->base); 943d0b16a6SMaxime Ripard 95*4a72cda5SIcenowy Zheng size = cfg->size; 96*4a72cda5SIcenowy Zheng 97*4a72cda5SIcenowy Zheng econfig.size = size; 983d0b16a6SMaxime Ripard econfig.dev = dev; 999c7b16ebSSrinivas Kandagatla econfig.reg_read = sunxi_sid_read; 1009c7b16ebSSrinivas Kandagatla econfig.priv = sid; 1013d0b16a6SMaxime Ripard nvmem = nvmem_register(&econfig); 1023d0b16a6SMaxime Ripard if (IS_ERR(nvmem)) 1033d0b16a6SMaxime Ripard return PTR_ERR(nvmem); 1043d0b16a6SMaxime Ripard 105d16abd30SCaesar Wang randomness = kzalloc(sizeof(u8) * (size), GFP_KERNEL); 106fb727077SMaxime Ripard if (!randomness) { 107fb727077SMaxime Ripard ret = -EINVAL; 108fb727077SMaxime Ripard goto err_unreg_nvmem; 109fb727077SMaxime Ripard } 110fb727077SMaxime Ripard 1113d0b16a6SMaxime Ripard for (i = 0; i < size; i++) 1123d0b16a6SMaxime Ripard randomness[i] = sunxi_sid_read_byte(sid, i); 1133d0b16a6SMaxime Ripard 1143d0b16a6SMaxime Ripard add_device_randomness(randomness, size); 1153d0b16a6SMaxime Ripard kfree(randomness); 1163d0b16a6SMaxime Ripard 1173d0b16a6SMaxime Ripard platform_set_drvdata(pdev, nvmem); 1183d0b16a6SMaxime Ripard 1193d0b16a6SMaxime Ripard return 0; 120fb727077SMaxime Ripard 121fb727077SMaxime Ripard err_unreg_nvmem: 122fb727077SMaxime Ripard nvmem_unregister(nvmem); 123fb727077SMaxime Ripard return ret; 1243d0b16a6SMaxime Ripard } 1253d0b16a6SMaxime Ripard 1263d0b16a6SMaxime Ripard static int sunxi_sid_remove(struct platform_device *pdev) 1273d0b16a6SMaxime Ripard { 1283d0b16a6SMaxime Ripard struct nvmem_device *nvmem = platform_get_drvdata(pdev); 1293d0b16a6SMaxime Ripard 1303d0b16a6SMaxime Ripard return nvmem_unregister(nvmem); 1313d0b16a6SMaxime Ripard } 1323d0b16a6SMaxime Ripard 133*4a72cda5SIcenowy Zheng static const struct sunxi_sid_cfg sun4i_a10_cfg = { 134*4a72cda5SIcenowy Zheng .size = 0x10, 135*4a72cda5SIcenowy Zheng }; 136*4a72cda5SIcenowy Zheng 137*4a72cda5SIcenowy Zheng static const struct sunxi_sid_cfg sun7i_a20_cfg = { 138*4a72cda5SIcenowy Zheng .size = 0x200, 139*4a72cda5SIcenowy Zheng }; 140*4a72cda5SIcenowy Zheng 1413d0b16a6SMaxime Ripard static const struct of_device_id sunxi_sid_of_match[] = { 142*4a72cda5SIcenowy Zheng { .compatible = "allwinner,sun4i-a10-sid", .data = &sun4i_a10_cfg }, 143*4a72cda5SIcenowy Zheng { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg }, 1443d0b16a6SMaxime Ripard {/* sentinel */}, 1453d0b16a6SMaxime Ripard }; 1463d0b16a6SMaxime Ripard MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); 1473d0b16a6SMaxime Ripard 1483d0b16a6SMaxime Ripard static struct platform_driver sunxi_sid_driver = { 1493d0b16a6SMaxime Ripard .probe = sunxi_sid_probe, 1503d0b16a6SMaxime Ripard .remove = sunxi_sid_remove, 1513d0b16a6SMaxime Ripard .driver = { 1523d0b16a6SMaxime Ripard .name = "eeprom-sunxi-sid", 1533d0b16a6SMaxime Ripard .of_match_table = sunxi_sid_of_match, 1543d0b16a6SMaxime Ripard }, 1553d0b16a6SMaxime Ripard }; 1563d0b16a6SMaxime Ripard module_platform_driver(sunxi_sid_driver); 1573d0b16a6SMaxime Ripard 1583d0b16a6SMaxime Ripard MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>"); 1593d0b16a6SMaxime Ripard MODULE_DESCRIPTION("Allwinner sunxi security id driver"); 1603d0b16a6SMaxime Ripard MODULE_LICENSE("GPL"); 161