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