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