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 193d0b16a6SMaxime Ripard 203d0b16a6SMaxime Ripard #include <linux/device.h> 213d0b16a6SMaxime Ripard #include <linux/io.h> 223d0b16a6SMaxime Ripard #include <linux/module.h> 233d0b16a6SMaxime Ripard #include <linux/nvmem-provider.h> 243d0b16a6SMaxime Ripard #include <linux/of.h> 253d0b16a6SMaxime Ripard #include <linux/platform_device.h> 263d0b16a6SMaxime Ripard #include <linux/regmap.h> 273d0b16a6SMaxime Ripard #include <linux/slab.h> 283d0b16a6SMaxime Ripard #include <linux/random.h> 293d0b16a6SMaxime Ripard 303d0b16a6SMaxime Ripard 313d0b16a6SMaxime Ripard static struct nvmem_config econfig = { 323d0b16a6SMaxime Ripard .name = "sunxi-sid", 333d0b16a6SMaxime Ripard .read_only = true, 343d0b16a6SMaxime Ripard .owner = THIS_MODULE, 353d0b16a6SMaxime Ripard }; 363d0b16a6SMaxime Ripard 373d0b16a6SMaxime Ripard struct sunxi_sid { 383d0b16a6SMaxime Ripard void __iomem *base; 393d0b16a6SMaxime Ripard }; 403d0b16a6SMaxime Ripard 413d0b16a6SMaxime Ripard /* We read the entire key, due to a 32 bit read alignment requirement. Since we 423d0b16a6SMaxime Ripard * want to return the requested byte, this results in somewhat slower code and 433d0b16a6SMaxime Ripard * uses 4 times more reads as needed but keeps code simpler. Since the SID is 443d0b16a6SMaxime Ripard * only very rarely probed, this is not really an issue. 453d0b16a6SMaxime Ripard */ 463d0b16a6SMaxime Ripard static u8 sunxi_sid_read_byte(const struct sunxi_sid *sid, 473d0b16a6SMaxime Ripard const unsigned int offset) 483d0b16a6SMaxime Ripard { 493d0b16a6SMaxime Ripard u32 sid_key; 503d0b16a6SMaxime Ripard 513d0b16a6SMaxime Ripard sid_key = ioread32be(sid->base + round_down(offset, 4)); 523d0b16a6SMaxime Ripard sid_key >>= (offset % 4) * 8; 533d0b16a6SMaxime Ripard 543d0b16a6SMaxime Ripard return sid_key; /* Only return the last byte */ 553d0b16a6SMaxime Ripard } 563d0b16a6SMaxime Ripard 573d0b16a6SMaxime Ripard static int sunxi_sid_read(void *context, 583d0b16a6SMaxime Ripard const void *reg, size_t reg_size, 593d0b16a6SMaxime Ripard void *val, size_t val_size) 603d0b16a6SMaxime Ripard { 613d0b16a6SMaxime Ripard struct sunxi_sid *sid = context; 623d0b16a6SMaxime Ripard unsigned int offset = *(u32 *)reg; 633d0b16a6SMaxime Ripard u8 *buf = val; 643d0b16a6SMaxime Ripard 653d0b16a6SMaxime Ripard while (val_size) { 663d0b16a6SMaxime Ripard *buf++ = sunxi_sid_read_byte(sid, offset); 673d0b16a6SMaxime Ripard val_size--; 683d0b16a6SMaxime Ripard offset++; 693d0b16a6SMaxime Ripard } 703d0b16a6SMaxime Ripard 713d0b16a6SMaxime Ripard return 0; 723d0b16a6SMaxime Ripard } 733d0b16a6SMaxime Ripard 743d0b16a6SMaxime Ripard static int sunxi_sid_write(void *context, const void *data, size_t count) 753d0b16a6SMaxime Ripard { 763d0b16a6SMaxime Ripard /* Unimplemented, dummy to keep regmap core happy */ 773d0b16a6SMaxime Ripard return 0; 783d0b16a6SMaxime Ripard } 793d0b16a6SMaxime Ripard 803d0b16a6SMaxime Ripard static struct regmap_bus sunxi_sid_bus = { 813d0b16a6SMaxime Ripard .read = sunxi_sid_read, 823d0b16a6SMaxime Ripard .write = sunxi_sid_write, 833d0b16a6SMaxime Ripard .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, 843d0b16a6SMaxime Ripard .val_format_endian_default = REGMAP_ENDIAN_NATIVE, 853d0b16a6SMaxime Ripard }; 863d0b16a6SMaxime Ripard 873d0b16a6SMaxime Ripard static bool sunxi_sid_writeable_reg(struct device *dev, unsigned int reg) 883d0b16a6SMaxime Ripard { 893d0b16a6SMaxime Ripard return false; 903d0b16a6SMaxime Ripard } 913d0b16a6SMaxime Ripard 923d0b16a6SMaxime Ripard static struct regmap_config sunxi_sid_regmap_config = { 933d0b16a6SMaxime Ripard .reg_bits = 32, 943d0b16a6SMaxime Ripard .val_bits = 8, 953d0b16a6SMaxime Ripard .reg_stride = 1, 963d0b16a6SMaxime Ripard .writeable_reg = sunxi_sid_writeable_reg, 973d0b16a6SMaxime Ripard }; 983d0b16a6SMaxime Ripard 993d0b16a6SMaxime Ripard static int sunxi_sid_probe(struct platform_device *pdev) 1003d0b16a6SMaxime Ripard { 1013d0b16a6SMaxime Ripard struct device *dev = &pdev->dev; 1023d0b16a6SMaxime Ripard struct resource *res; 1033d0b16a6SMaxime Ripard struct nvmem_device *nvmem; 1043d0b16a6SMaxime Ripard struct regmap *regmap; 1053d0b16a6SMaxime Ripard struct sunxi_sid *sid; 106*fb727077SMaxime Ripard int ret, i, size; 1073d0b16a6SMaxime Ripard char *randomness; 1083d0b16a6SMaxime Ripard 1093d0b16a6SMaxime Ripard sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL); 1103d0b16a6SMaxime Ripard if (!sid) 1113d0b16a6SMaxime Ripard return -ENOMEM; 1123d0b16a6SMaxime Ripard 1133d0b16a6SMaxime Ripard res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1143d0b16a6SMaxime Ripard sid->base = devm_ioremap_resource(dev, res); 1153d0b16a6SMaxime Ripard if (IS_ERR(sid->base)) 1163d0b16a6SMaxime Ripard return PTR_ERR(sid->base); 1173d0b16a6SMaxime Ripard 1183d0b16a6SMaxime Ripard size = resource_size(res) - 1; 1193d0b16a6SMaxime Ripard sunxi_sid_regmap_config.max_register = size; 1203d0b16a6SMaxime Ripard 1213d0b16a6SMaxime Ripard regmap = devm_regmap_init(dev, &sunxi_sid_bus, sid, 1223d0b16a6SMaxime Ripard &sunxi_sid_regmap_config); 1233d0b16a6SMaxime Ripard if (IS_ERR(regmap)) { 1243d0b16a6SMaxime Ripard dev_err(dev, "regmap init failed\n"); 1253d0b16a6SMaxime Ripard return PTR_ERR(regmap); 1263d0b16a6SMaxime Ripard } 1273d0b16a6SMaxime Ripard 1283d0b16a6SMaxime Ripard econfig.dev = dev; 1293d0b16a6SMaxime Ripard nvmem = nvmem_register(&econfig); 1303d0b16a6SMaxime Ripard if (IS_ERR(nvmem)) 1313d0b16a6SMaxime Ripard return PTR_ERR(nvmem); 1323d0b16a6SMaxime Ripard 1333d0b16a6SMaxime Ripard randomness = kzalloc(sizeof(u8) * size, GFP_KERNEL); 134*fb727077SMaxime Ripard if (!randomness) { 135*fb727077SMaxime Ripard ret = -EINVAL; 136*fb727077SMaxime Ripard goto err_unreg_nvmem; 137*fb727077SMaxime Ripard } 138*fb727077SMaxime Ripard 1393d0b16a6SMaxime Ripard for (i = 0; i < size; i++) 1403d0b16a6SMaxime Ripard randomness[i] = sunxi_sid_read_byte(sid, i); 1413d0b16a6SMaxime Ripard 1423d0b16a6SMaxime Ripard add_device_randomness(randomness, size); 1433d0b16a6SMaxime Ripard kfree(randomness); 1443d0b16a6SMaxime Ripard 1453d0b16a6SMaxime Ripard platform_set_drvdata(pdev, nvmem); 1463d0b16a6SMaxime Ripard 1473d0b16a6SMaxime Ripard return 0; 148*fb727077SMaxime Ripard 149*fb727077SMaxime Ripard err_unreg_nvmem: 150*fb727077SMaxime Ripard nvmem_unregister(nvmem); 151*fb727077SMaxime Ripard return ret; 1523d0b16a6SMaxime Ripard } 1533d0b16a6SMaxime Ripard 1543d0b16a6SMaxime Ripard static int sunxi_sid_remove(struct platform_device *pdev) 1553d0b16a6SMaxime Ripard { 1563d0b16a6SMaxime Ripard struct nvmem_device *nvmem = platform_get_drvdata(pdev); 1573d0b16a6SMaxime Ripard 1583d0b16a6SMaxime Ripard return nvmem_unregister(nvmem); 1593d0b16a6SMaxime Ripard } 1603d0b16a6SMaxime Ripard 1613d0b16a6SMaxime Ripard static const struct of_device_id sunxi_sid_of_match[] = { 1623d0b16a6SMaxime Ripard { .compatible = "allwinner,sun4i-a10-sid" }, 1633d0b16a6SMaxime Ripard { .compatible = "allwinner,sun7i-a20-sid" }, 1643d0b16a6SMaxime Ripard {/* sentinel */}, 1653d0b16a6SMaxime Ripard }; 1663d0b16a6SMaxime Ripard MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); 1673d0b16a6SMaxime Ripard 1683d0b16a6SMaxime Ripard static struct platform_driver sunxi_sid_driver = { 1693d0b16a6SMaxime Ripard .probe = sunxi_sid_probe, 1703d0b16a6SMaxime Ripard .remove = sunxi_sid_remove, 1713d0b16a6SMaxime Ripard .driver = { 1723d0b16a6SMaxime Ripard .name = "eeprom-sunxi-sid", 1733d0b16a6SMaxime Ripard .of_match_table = sunxi_sid_of_match, 1743d0b16a6SMaxime Ripard }, 1753d0b16a6SMaxime Ripard }; 1763d0b16a6SMaxime Ripard module_platform_driver(sunxi_sid_driver); 1773d0b16a6SMaxime Ripard 1783d0b16a6SMaxime Ripard MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>"); 1793d0b16a6SMaxime Ripard MODULE_DESCRIPTION("Allwinner sunxi security id driver"); 1803d0b16a6SMaxime Ripard MODULE_LICENSE("GPL"); 181