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> 233d0b16a6SMaxime Ripard #include <linux/platform_device.h> 243d0b16a6SMaxime Ripard #include <linux/slab.h> 253d0b16a6SMaxime Ripard #include <linux/random.h> 263d0b16a6SMaxime Ripard 273d0b16a6SMaxime Ripard static struct nvmem_config econfig = { 283d0b16a6SMaxime Ripard .name = "sunxi-sid", 293d0b16a6SMaxime Ripard .read_only = true, 30*9c7b16ebSSrinivas Kandagatla .stride = 4, 31*9c7b16ebSSrinivas Kandagatla .word_size = 1, 323d0b16a6SMaxime Ripard .owner = THIS_MODULE, 333d0b16a6SMaxime Ripard }; 343d0b16a6SMaxime Ripard 353d0b16a6SMaxime Ripard struct sunxi_sid { 363d0b16a6SMaxime Ripard void __iomem *base; 373d0b16a6SMaxime Ripard }; 383d0b16a6SMaxime Ripard 393d0b16a6SMaxime Ripard /* We read the entire key, due to a 32 bit read alignment requirement. Since we 403d0b16a6SMaxime Ripard * want to return the requested byte, this results in somewhat slower code and 413d0b16a6SMaxime Ripard * uses 4 times more reads as needed but keeps code simpler. Since the SID is 423d0b16a6SMaxime Ripard * only very rarely probed, this is not really an issue. 433d0b16a6SMaxime Ripard */ 443d0b16a6SMaxime Ripard static u8 sunxi_sid_read_byte(const struct sunxi_sid *sid, 453d0b16a6SMaxime Ripard const unsigned int offset) 463d0b16a6SMaxime Ripard { 473d0b16a6SMaxime Ripard u32 sid_key; 483d0b16a6SMaxime Ripard 493d0b16a6SMaxime Ripard sid_key = ioread32be(sid->base + round_down(offset, 4)); 503d0b16a6SMaxime Ripard sid_key >>= (offset % 4) * 8; 513d0b16a6SMaxime Ripard 523d0b16a6SMaxime Ripard return sid_key; /* Only return the last byte */ 533d0b16a6SMaxime Ripard } 543d0b16a6SMaxime Ripard 55*9c7b16ebSSrinivas Kandagatla static int sunxi_sid_read(void *context, unsigned int offset, 56*9c7b16ebSSrinivas Kandagatla void *val, size_t bytes) 573d0b16a6SMaxime Ripard { 583d0b16a6SMaxime Ripard struct sunxi_sid *sid = context; 593d0b16a6SMaxime Ripard u8 *buf = val; 603d0b16a6SMaxime Ripard 61*9c7b16ebSSrinivas Kandagatla while (bytes--) 62*9c7b16ebSSrinivas Kandagatla *buf++ = sunxi_sid_read_byte(sid, offset++); 633d0b16a6SMaxime Ripard 643d0b16a6SMaxime Ripard return 0; 653d0b16a6SMaxime Ripard } 663d0b16a6SMaxime Ripard 673d0b16a6SMaxime Ripard static int sunxi_sid_probe(struct platform_device *pdev) 683d0b16a6SMaxime Ripard { 693d0b16a6SMaxime Ripard struct device *dev = &pdev->dev; 703d0b16a6SMaxime Ripard struct resource *res; 713d0b16a6SMaxime Ripard struct nvmem_device *nvmem; 723d0b16a6SMaxime Ripard struct sunxi_sid *sid; 73fb727077SMaxime Ripard int ret, i, size; 743d0b16a6SMaxime Ripard char *randomness; 753d0b16a6SMaxime Ripard 763d0b16a6SMaxime Ripard sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL); 773d0b16a6SMaxime Ripard if (!sid) 783d0b16a6SMaxime Ripard return -ENOMEM; 793d0b16a6SMaxime Ripard 803d0b16a6SMaxime Ripard res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 813d0b16a6SMaxime Ripard sid->base = devm_ioremap_resource(dev, res); 823d0b16a6SMaxime Ripard if (IS_ERR(sid->base)) 833d0b16a6SMaxime Ripard return PTR_ERR(sid->base); 843d0b16a6SMaxime Ripard 853d0b16a6SMaxime Ripard size = resource_size(res) - 1; 86*9c7b16ebSSrinivas Kandagatla econfig.size = resource_size(res); 873d0b16a6SMaxime Ripard econfig.dev = dev; 88*9c7b16ebSSrinivas Kandagatla econfig.reg_read = sunxi_sid_read; 89*9c7b16ebSSrinivas Kandagatla econfig.priv = sid; 903d0b16a6SMaxime Ripard nvmem = nvmem_register(&econfig); 913d0b16a6SMaxime Ripard if (IS_ERR(nvmem)) 923d0b16a6SMaxime Ripard return PTR_ERR(nvmem); 933d0b16a6SMaxime Ripard 94d16abd30SCaesar Wang randomness = kzalloc(sizeof(u8) * (size), GFP_KERNEL); 95fb727077SMaxime Ripard if (!randomness) { 96fb727077SMaxime Ripard ret = -EINVAL; 97fb727077SMaxime Ripard goto err_unreg_nvmem; 98fb727077SMaxime Ripard } 99fb727077SMaxime Ripard 1003d0b16a6SMaxime Ripard for (i = 0; i < size; i++) 1013d0b16a6SMaxime Ripard randomness[i] = sunxi_sid_read_byte(sid, i); 1023d0b16a6SMaxime Ripard 1033d0b16a6SMaxime Ripard add_device_randomness(randomness, size); 1043d0b16a6SMaxime Ripard kfree(randomness); 1053d0b16a6SMaxime Ripard 1063d0b16a6SMaxime Ripard platform_set_drvdata(pdev, nvmem); 1073d0b16a6SMaxime Ripard 1083d0b16a6SMaxime Ripard return 0; 109fb727077SMaxime Ripard 110fb727077SMaxime Ripard err_unreg_nvmem: 111fb727077SMaxime Ripard nvmem_unregister(nvmem); 112fb727077SMaxime Ripard return ret; 1133d0b16a6SMaxime Ripard } 1143d0b16a6SMaxime Ripard 1153d0b16a6SMaxime Ripard static int sunxi_sid_remove(struct platform_device *pdev) 1163d0b16a6SMaxime Ripard { 1173d0b16a6SMaxime Ripard struct nvmem_device *nvmem = platform_get_drvdata(pdev); 1183d0b16a6SMaxime Ripard 1193d0b16a6SMaxime Ripard return nvmem_unregister(nvmem); 1203d0b16a6SMaxime Ripard } 1213d0b16a6SMaxime Ripard 1223d0b16a6SMaxime Ripard static const struct of_device_id sunxi_sid_of_match[] = { 1233d0b16a6SMaxime Ripard { .compatible = "allwinner,sun4i-a10-sid" }, 1243d0b16a6SMaxime Ripard { .compatible = "allwinner,sun7i-a20-sid" }, 1253d0b16a6SMaxime Ripard {/* sentinel */}, 1263d0b16a6SMaxime Ripard }; 1273d0b16a6SMaxime Ripard MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); 1283d0b16a6SMaxime Ripard 1293d0b16a6SMaxime Ripard static struct platform_driver sunxi_sid_driver = { 1303d0b16a6SMaxime Ripard .probe = sunxi_sid_probe, 1313d0b16a6SMaxime Ripard .remove = sunxi_sid_remove, 1323d0b16a6SMaxime Ripard .driver = { 1333d0b16a6SMaxime Ripard .name = "eeprom-sunxi-sid", 1343d0b16a6SMaxime Ripard .of_match_table = sunxi_sid_of_match, 1353d0b16a6SMaxime Ripard }, 1363d0b16a6SMaxime Ripard }; 1373d0b16a6SMaxime Ripard module_platform_driver(sunxi_sid_driver); 1383d0b16a6SMaxime Ripard 1393d0b16a6SMaxime Ripard MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>"); 1403d0b16a6SMaxime Ripard MODULE_DESCRIPTION("Allwinner sunxi security id driver"); 1413d0b16a6SMaxime Ripard MODULE_LICENSE("GPL"); 142