1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (c) 2021, Microchip
4 */
5
6 #include <drivers/nvmem.h>
7 #include <io.h>
8 #include <kernel/dt_driver.h>
9 #include <malloc.h>
10 #include <matrix.h>
11 #include <platform_config.h>
12 #include <string.h>
13 #include <tee_api_defines.h>
14 #include <tee_api_types.h>
15 #include <types_ext.h>
16
17 #define ATMEL_SFC_KR 0x0
18 #define ATMEL_SFC_SR 0x1C
19 #define ATMEL_SFC_SR_PGMC BIT(0)
20 #define ATMEL_SFC_SR_PGMF BIT(1)
21 #define ATMEL_SFC_DR 0x20
22
23 #define ATMEL_SFC_CELLS_32 17
24 #define ATMEL_SFC_CELLS_8 (ATMEL_SFC_CELLS_32 * sizeof(uint32_t))
25
26 struct atmel_sfc {
27 vaddr_t base;
28 uint8_t fuses[ATMEL_SFC_CELLS_8];
29 };
30
atmel_sfc_read_cell(struct nvmem_cell * cell,uint8_t * data)31 static TEE_Result atmel_sfc_read_cell(struct nvmem_cell *cell, uint8_t *data)
32 {
33 struct atmel_sfc *atmel_sfc = cell->drv_data;
34
35 if (cell->offset + cell->len > ATMEL_SFC_CELLS_8)
36 return TEE_ERROR_GENERIC;
37
38 memcpy(data, &atmel_sfc->fuses[cell->offset], cell->len);
39
40 return TEE_SUCCESS;
41 }
42
atmel_sfc_put_cell(struct nvmem_cell * cell)43 static void atmel_sfc_put_cell(struct nvmem_cell *cell)
44 {
45 free(cell);
46 }
47
48 static const struct nvmem_ops atmel_sfc_nvmem_ops = {
49 .read_cell = atmel_sfc_read_cell,
50 .put_cell = atmel_sfc_put_cell,
51 };
52
atmel_sfc_dt_get(struct dt_pargs * args,void * data,struct nvmem_cell ** out_cell)53 static TEE_Result atmel_sfc_dt_get(struct dt_pargs *args,
54 void *data, struct nvmem_cell **out_cell)
55 {
56 TEE_Result res = TEE_ERROR_GENERIC;
57 struct nvmem_cell *cell = NULL;
58
59 /* Freed from atmel_sfc_put_cell() */
60 cell = calloc(1, sizeof(*cell));
61 if (!cell)
62 return TEE_ERROR_OUT_OF_MEMORY;
63
64 res = nvmem_cell_parse_dt(args->fdt, args->phandle_node, cell);
65 if (res)
66 goto out_free;
67
68 if (cell->offset + cell->len > ATMEL_SFC_CELLS_8) {
69 res = TEE_ERROR_GENERIC;
70 goto out_free;
71 }
72
73 cell->ops = &atmel_sfc_nvmem_ops;
74 cell->drv_data = data;
75 *out_cell = cell;
76
77 return TEE_SUCCESS;
78
79 out_free:
80 free(cell);
81
82 return res;
83 }
84
atmel_sfc_read_fuse(struct atmel_sfc * atmel_sfc)85 static void atmel_sfc_read_fuse(struct atmel_sfc *atmel_sfc)
86 {
87 size_t i = 0;
88 uint32_t val = 0;
89
90 for (i = 0; i < ATMEL_SFC_CELLS_32; i++) {
91 val = io_read32(atmel_sfc->base + ATMEL_SFC_DR + i * 4);
92 memcpy(&atmel_sfc->fuses[i * 4], &val, sizeof(val));
93 }
94 }
95
atmel_sfc_probe(const void * fdt,int node,const void * compat_data __unused)96 static TEE_Result atmel_sfc_probe(const void *fdt, int node,
97 const void *compat_data __unused)
98 {
99 vaddr_t base = 0;
100 size_t size = 0;
101 struct atmel_sfc *atmel_sfc = NULL;
102
103 if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
104 return TEE_ERROR_NODE_DISABLED;
105
106 matrix_configure_periph_secure(AT91C_ID_SFC);
107
108 if (dt_map_dev(fdt, node, &base, &size, DT_MAP_AUTO) < 0)
109 return TEE_ERROR_GENERIC;
110
111 atmel_sfc = calloc(1, sizeof(*atmel_sfc));
112 if (!atmel_sfc)
113 return TEE_ERROR_OUT_OF_MEMORY;
114
115 atmel_sfc->base = base;
116
117 atmel_sfc_read_fuse(atmel_sfc);
118
119 return nvmem_register_provider(fdt, node, atmel_sfc_dt_get, atmel_sfc);
120 }
121
122 static const struct dt_device_match atmel_sfc_match_table[] = {
123 { .compatible = "atmel,sama5d2-sfc" },
124 { }
125 };
126
127 DEFINE_DT_DRIVER(atmel_sfc_dt_driver) = {
128 .name = "atmel_sfc",
129 .type = DT_DRIVER_NVMEM,
130 .match_table = atmel_sfc_match_table,
131 .probe = atmel_sfc_probe,
132 };
133