]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/nvmem/sunxi_sid.c
powerpc/dma: dma_set_coherent_mask() should not be GPL only
[karo-tx-linux.git] / drivers / nvmem / sunxi_sid.c
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");