2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * Allwinner secure ID controller
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/endian.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
40 #include <sys/kernel.h>
42 #include <sys/mutex.h>
43 #include <sys/module.h>
44 #include <sys/sysctl.h>
45 #include <machine/bus.h>
47 #include <dev/ofw/ofw_bus.h>
48 #include <dev/ofw/ofw_bus_subr.h>
50 #include <arm/allwinner/aw_sid.h>
55 * Starting at least from sun8iw6 (A83T) EFUSE starts at 0x200
56 * There is 3 registers in the low area to read/write protected EFUSE.
58 #define SID_PRCTL 0x40
59 #define SID_PRCTL_OFFSET_MASK 0xff
60 #define SID_PRCTL_OFFSET(n) (((n) & SID_PRCTL_OFFSET_MASK) << 16)
61 #define SID_PRCTL_LOCK (0xac << 8)
62 #define SID_PRCTL_READ (0x01 << 1)
63 #define SID_PRCTL_WRITE (0x01 << 0)
64 #define SID_PRKEY 0x50
65 #define SID_RDKEY 0x60
67 #define EFUSE_OFFSET 0x200
68 #define EFUSE_NAME_SIZE 32
69 #define EFUSE_DESC_SIZE 64
72 char name[EFUSE_NAME_SIZE];
73 char desc[EFUSE_DESC_SIZE];
77 enum aw_sid_fuse_id id;
81 static struct aw_sid_efuse a10_efuses[] = {
84 .desc = "Root Key or ChipID",
87 .id = AW_SID_FUSE_ROOTKEY,
92 static struct aw_sid_efuse a64_efuses[] = {
95 .desc = "Root Key or ChipID",
99 .id = AW_SID_FUSE_ROOTKEY,
104 .desc = "Thermal Sensor Calibration Data",
105 .base = EFUSE_OFFSET,
108 .id = AW_SID_FUSE_THSSENSOR,
113 static struct aw_sid_efuse a83t_efuses[] = {
116 .desc = "Root Key or ChipID",
117 .base = EFUSE_OFFSET,
120 .id = AW_SID_FUSE_ROOTKEY,
125 .desc = "Thermal Sensor Calibration Data",
126 .base = EFUSE_OFFSET,
129 .id = AW_SID_FUSE_THSSENSOR,
134 static struct aw_sid_efuse h3_efuses[] = {
137 .desc = "Root Key or ChipID",
138 .base = EFUSE_OFFSET,
141 .id = AW_SID_FUSE_ROOTKEY,
146 .desc = "Thermal Sensor Calibration Data",
147 .base = EFUSE_OFFSET,
150 .id = AW_SID_FUSE_THSSENSOR,
155 static struct aw_sid_efuse h5_efuses[] = {
158 .desc = "Root Key or ChipID",
159 .base = EFUSE_OFFSET,
162 .id = AW_SID_FUSE_ROOTKEY,
167 .desc = "Thermal Sensor Calibration Data",
168 .base = EFUSE_OFFSET,
171 .id = AW_SID_FUSE_THSSENSOR,
177 struct aw_sid_efuse *efuses;
181 static const struct aw_sid_conf a10_conf = {
182 .efuses = a10_efuses,
183 .nfuses = nitems(a10_efuses),
186 static const struct aw_sid_conf a20_conf = {
187 .efuses = a10_efuses,
188 .nfuses = nitems(a10_efuses),
191 static const struct aw_sid_conf a64_conf = {
192 .efuses = a64_efuses,
193 .nfuses = nitems(a64_efuses),
196 static const struct aw_sid_conf a83t_conf = {
197 .efuses = a83t_efuses,
198 .nfuses = nitems(a83t_efuses),
201 static const struct aw_sid_conf h3_conf = {
203 .nfuses = nitems(h3_efuses),
206 static const struct aw_sid_conf h5_conf = {
208 .nfuses = nitems(h5_efuses),
211 static struct ofw_compat_data compat_data[] = {
212 { "allwinner,sun4i-a10-sid", (uintptr_t)&a10_conf},
213 { "allwinner,sun7i-a20-sid", (uintptr_t)&a20_conf},
214 { "allwinner,sun50i-a64-sid", (uintptr_t)&a64_conf},
215 { "allwinner,sun8i-a83t-sid", (uintptr_t)&a83t_conf},
216 { "allwinner,sun8i-h3-sid", (uintptr_t)&h3_conf},
217 { "allwinner,sun50i-h5-sid", (uintptr_t)&h5_conf},
221 struct aw_sid_softc {
223 struct resource *res;
224 struct aw_sid_conf *sid_conf;
225 struct mtx prctl_mtx;
228 static struct aw_sid_softc *aw_sid_sc;
230 static struct resource_spec aw_sid_spec[] = {
231 { SYS_RES_MEMORY, 0, RF_ACTIVE },
235 #define RD1(sc, reg) bus_read_1((sc)->res, (reg))
236 #define RD4(sc, reg) bus_read_4((sc)->res, (reg))
237 #define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
239 static int aw_sid_sysctl(SYSCTL_HANDLER_ARGS);
242 aw_sid_probe(device_t dev)
244 if (!ofw_bus_status_okay(dev))
247 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
250 device_set_desc(dev, "Allwinner Secure ID Controller");
251 return (BUS_PROBE_DEFAULT);
255 aw_sid_attach(device_t dev)
257 struct aw_sid_softc *sc;
261 node = ofw_bus_get_node(dev);
262 sc = device_get_softc(dev);
265 if (bus_alloc_resources(dev, aw_sid_spec, &sc->res) != 0) {
266 device_printf(dev, "cannot allocate resources for device\n");
270 mtx_init(&sc->prctl_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
271 sc->sid_conf = (struct aw_sid_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
274 /* Register ourself so device can resolve who we are */
275 OF_device_register_xref(OF_xref_from_node(node), dev);
277 for (i = 0; i < sc->sid_conf->nfuses ;i++) {\
278 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
279 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
280 OID_AUTO, sc->sid_conf->efuses[i].name,
281 CTLTYPE_STRING | CTLFLAG_RD,
282 dev, sc->sid_conf->efuses[i].id, aw_sid_sysctl,
283 "A", sc->sid_conf->efuses[i].desc);
289 aw_sid_get_fuse(enum aw_sid_fuse_id id, uint8_t *out, uint32_t *size)
291 struct aw_sid_softc *sc;
299 for (i = 0; i < sc->sid_conf->nfuses; i++)
300 if (id == sc->sid_conf->efuses[i].id)
303 if (i == sc->sid_conf->nfuses)
306 if (*size != sc->sid_conf->efuses[i].size) {
307 *size = sc->sid_conf->efuses[i].size;
314 if (sc->sid_conf->efuses[i].public == false)
315 mtx_lock(&sc->prctl_mtx);
316 for (j = 0; j < sc->sid_conf->efuses[i].size; j += 4) {
317 if (sc->sid_conf->efuses[i].public == false) {
318 val = SID_PRCTL_OFFSET(sc->sid_conf->efuses[i].offset + j) |
321 WR4(sc, SID_PRCTL, val);
322 /* Read bit will be cleared once read has concluded */
323 while (RD4(sc, SID_PRCTL) & SID_PRCTL_READ)
325 val = RD4(sc, SID_RDKEY);
327 val = RD4(sc, sc->sid_conf->efuses[i].base +
328 sc->sid_conf->efuses[i].offset + j);
331 out[j + 1] = (val & 0xFF00) >> 8;
333 out[j + 2] = (val & 0xFF0000) >> 16;
335 out[j + 3] = (val & 0xFF000000) >> 24;
337 if (sc->sid_conf->efuses[i].public == false)
338 mtx_unlock(&sc->prctl_mtx);
344 aw_sid_read(device_t dev, uint32_t offset, uint32_t size, uint8_t *buffer)
346 struct aw_sid_softc *sc;
347 enum aw_sid_fuse_id fuse_id = 0;
350 sc = device_get_softc(dev);
352 for (i = 0; i < sc->sid_conf->nfuses; i++)
353 if (offset == (sc->sid_conf->efuses[i].base +
354 sc->sid_conf->efuses[i].offset)) {
355 fuse_id = sc->sid_conf->efuses[i].id;
362 return (aw_sid_get_fuse(fuse_id, buffer, &size));
366 aw_sid_sysctl(SYSCTL_HANDLER_ARGS)
368 struct aw_sid_softc *sc;
370 enum aw_sid_fuse_id fuse = arg2;
376 sc = device_get_softc(dev);
378 /* Get the size of the efuse data */
380 aw_sid_get_fuse(fuse, NULL, &size);
381 /* We now have the real size */
382 ret = aw_sid_get_fuse(fuse, data, &size);
384 device_printf(dev, "Cannot get fuse id %d: %d\n", fuse, ret);
388 for (i = 0; i < size; i++)
389 snprintf(out + (i * 2), sizeof(out) - (i * 2),
392 return sysctl_handle_string(oidp, out, sizeof(out), req);
395 static device_method_t aw_sid_methods[] = {
396 /* Device interface */
397 DEVMETHOD(device_probe, aw_sid_probe),
398 DEVMETHOD(device_attach, aw_sid_attach),
400 /* NVMEM interface */
401 DEVMETHOD(nvmem_read, aw_sid_read),
405 static driver_t aw_sid_driver = {
408 sizeof(struct aw_sid_softc),
411 static devclass_t aw_sid_devclass;
413 EARLY_DRIVER_MODULE(aw_sid, simplebus, aw_sid_driver, aw_sid_devclass, 0, 0,
414 BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_FIRST);
415 MODULE_VERSION(aw_sid, 1);
416 SIMPLEBUS_PNP_INFO(compat_data);