xref: /optee_os/core/drivers/nvmem/atmel_sfc.c (revision b40c76c5c909570bd98bc86b49ac604c991d4e6b)
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