1 /* 2 * Allwinner sunXi SoCs Security ID support. 3 * 4 * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl> 5 * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 */ 18 19 20 #include <linux/device.h> 21 #include <linux/io.h> 22 #include <linux/module.h> 23 #include <linux/nvmem-provider.h> 24 #include <linux/of.h> 25 #include <linux/platform_device.h> 26 #include <linux/regmap.h> 27 #include <linux/slab.h> 28 #include <linux/random.h> 29 30 31 static struct nvmem_config econfig = { 32 .name = "sunxi-sid", 33 .read_only = true, 34 .owner = THIS_MODULE, 35 }; 36 37 struct sunxi_sid { 38 void __iomem *base; 39 }; 40 41 /* We read the entire key, due to a 32 bit read alignment requirement. Since we 42 * want to return the requested byte, this results in somewhat slower code and 43 * uses 4 times more reads as needed but keeps code simpler. Since the SID is 44 * only very rarely probed, this is not really an issue. 45 */ 46 static u8 sunxi_sid_read_byte(const struct sunxi_sid *sid, 47 const unsigned int offset) 48 { 49 u32 sid_key; 50 51 sid_key = ioread32be(sid->base + round_down(offset, 4)); 52 sid_key >>= (offset % 4) * 8; 53 54 return sid_key; /* Only return the last byte */ 55 } 56 57 static int sunxi_sid_read(void *context, 58 const void *reg, size_t reg_size, 59 void *val, size_t val_size) 60 { 61 struct sunxi_sid *sid = context; 62 unsigned int offset = *(u32 *)reg; 63 u8 *buf = val; 64 65 while (val_size) { 66 *buf++ = sunxi_sid_read_byte(sid, offset); 67 val_size--; 68 offset++; 69 } 70 71 return 0; 72 } 73 74 static int sunxi_sid_write(void *context, const void *data, size_t count) 75 { 76 /* Unimplemented, dummy to keep regmap core happy */ 77 return 0; 78 } 79 80 static struct regmap_bus sunxi_sid_bus = { 81 .read = sunxi_sid_read, 82 .write = sunxi_sid_write, 83 .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, 84 .val_format_endian_default = REGMAP_ENDIAN_NATIVE, 85 }; 86 87 static bool sunxi_sid_writeable_reg(struct device *dev, unsigned int reg) 88 { 89 return false; 90 } 91 92 static struct regmap_config sunxi_sid_regmap_config = { 93 .reg_bits = 32, 94 .val_bits = 8, 95 .reg_stride = 1, 96 .writeable_reg = sunxi_sid_writeable_reg, 97 }; 98 99 static int sunxi_sid_probe(struct platform_device *pdev) 100 { 101 struct device *dev = &pdev->dev; 102 struct resource *res; 103 struct nvmem_device *nvmem; 104 struct regmap *regmap; 105 struct sunxi_sid *sid; 106 int i, size; 107 char *randomness; 108 109 sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL); 110 if (!sid) 111 return -ENOMEM; 112 113 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 114 sid->base = devm_ioremap_resource(dev, res); 115 if (IS_ERR(sid->base)) 116 return PTR_ERR(sid->base); 117 118 size = resource_size(res) - 1; 119 sunxi_sid_regmap_config.max_register = size; 120 121 regmap = devm_regmap_init(dev, &sunxi_sid_bus, sid, 122 &sunxi_sid_regmap_config); 123 if (IS_ERR(regmap)) { 124 dev_err(dev, "regmap init failed\n"); 125 return PTR_ERR(regmap); 126 } 127 128 econfig.dev = dev; 129 nvmem = nvmem_register(&econfig); 130 if (IS_ERR(nvmem)) 131 return PTR_ERR(nvmem); 132 133 randomness = kzalloc(sizeof(u8) * size, GFP_KERNEL); 134 for (i = 0; i < size; i++) 135 randomness[i] = sunxi_sid_read_byte(sid, i); 136 137 add_device_randomness(randomness, size); 138 kfree(randomness); 139 140 platform_set_drvdata(pdev, nvmem); 141 142 return 0; 143 } 144 145 static int sunxi_sid_remove(struct platform_device *pdev) 146 { 147 struct nvmem_device *nvmem = platform_get_drvdata(pdev); 148 149 return nvmem_unregister(nvmem); 150 } 151 152 static const struct of_device_id sunxi_sid_of_match[] = { 153 { .compatible = "allwinner,sun4i-a10-sid" }, 154 { .compatible = "allwinner,sun7i-a20-sid" }, 155 {/* sentinel */}, 156 }; 157 MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); 158 159 static struct platform_driver sunxi_sid_driver = { 160 .probe = sunxi_sid_probe, 161 .remove = sunxi_sid_remove, 162 .driver = { 163 .name = "eeprom-sunxi-sid", 164 .of_match_table = sunxi_sid_of_match, 165 }, 166 }; 167 module_platform_driver(sunxi_sid_driver); 168 169 MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>"); 170 MODULE_DESCRIPTION("Allwinner sunxi security id driver"); 171 MODULE_LICENSE("GPL"); 172