xref: /optee_os/core/drivers/nvmem/atmel_sfc.c (revision b40c76c5c909570bd98bc86b49ac604c991d4e6b)
195e26dbdSClément Léger // SPDX-License-Identifier: BSD-2-Clause
295e26dbdSClément Léger /*
395e26dbdSClément Léger  * Copyright (c) 2021, Microchip
495e26dbdSClément Léger  */
595e26dbdSClément Léger 
695e26dbdSClément Léger #include <drivers/nvmem.h>
795e26dbdSClément Léger #include <io.h>
895e26dbdSClément Léger #include <kernel/dt_driver.h>
995e26dbdSClément Léger #include <malloc.h>
1095e26dbdSClément Léger #include <matrix.h>
116370f75dSTony Han #include <platform_config.h>
1295e26dbdSClément Léger #include <string.h>
1395e26dbdSClément Léger #include <tee_api_defines.h>
1495e26dbdSClément Léger #include <tee_api_types.h>
1595e26dbdSClément Léger #include <types_ext.h>
1695e26dbdSClément Léger 
1795e26dbdSClément Léger #define ATMEL_SFC_KR		0x0
1895e26dbdSClément Léger #define ATMEL_SFC_SR		0x1C
1995e26dbdSClément Léger #define ATMEL_SFC_SR_PGMC	BIT(0)
2095e26dbdSClément Léger #define ATMEL_SFC_SR_PGMF	BIT(1)
2195e26dbdSClément Léger #define ATMEL_SFC_DR		0x20
2295e26dbdSClément Léger 
2395e26dbdSClément Léger #define ATMEL_SFC_CELLS_32	17
2495e26dbdSClément Léger #define ATMEL_SFC_CELLS_8	(ATMEL_SFC_CELLS_32 * sizeof(uint32_t))
2595e26dbdSClément Léger 
2695e26dbdSClément Léger struct atmel_sfc {
2795e26dbdSClément Léger 	vaddr_t base;
2895e26dbdSClément Léger 	uint8_t fuses[ATMEL_SFC_CELLS_8];
2995e26dbdSClément Léger };
3095e26dbdSClément Léger 
atmel_sfc_read_cell(struct nvmem_cell * cell,uint8_t * data)3195e26dbdSClément Léger static TEE_Result atmel_sfc_read_cell(struct nvmem_cell *cell, uint8_t *data)
3295e26dbdSClément Léger {
3395e26dbdSClément Léger 	struct atmel_sfc *atmel_sfc = cell->drv_data;
3495e26dbdSClément Léger 
35*b40c76c5SThomas Perrot 	if (cell->offset + cell->len > ATMEL_SFC_CELLS_8)
36*b40c76c5SThomas Perrot 		return TEE_ERROR_GENERIC;
37*b40c76c5SThomas Perrot 
3895e26dbdSClément Léger 	memcpy(data, &atmel_sfc->fuses[cell->offset], cell->len);
3995e26dbdSClément Léger 
4095e26dbdSClément Léger 	return TEE_SUCCESS;
4195e26dbdSClément Léger }
4295e26dbdSClément Léger 
atmel_sfc_put_cell(struct nvmem_cell * cell)4395e26dbdSClément Léger static void atmel_sfc_put_cell(struct nvmem_cell *cell)
4495e26dbdSClément Léger {
4595e26dbdSClément Léger 	free(cell);
4695e26dbdSClément Léger }
4795e26dbdSClément Léger 
4895e26dbdSClément Léger static const struct nvmem_ops atmel_sfc_nvmem_ops = {
4995e26dbdSClément Léger 	.read_cell = atmel_sfc_read_cell,
5095e26dbdSClément Léger 	.put_cell = atmel_sfc_put_cell,
5195e26dbdSClément Léger };
5295e26dbdSClément Léger 
atmel_sfc_dt_get(struct dt_pargs * args,void * data,struct nvmem_cell ** out_cell)5395e26dbdSClément Léger static TEE_Result atmel_sfc_dt_get(struct dt_pargs *args,
5495e26dbdSClément Léger 				   void *data, struct nvmem_cell **out_cell)
5595e26dbdSClément Léger {
5695e26dbdSClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
5795e26dbdSClément Léger 	struct nvmem_cell *cell = NULL;
5895e26dbdSClément Léger 
5995e26dbdSClément Léger 	/* Freed from atmel_sfc_put_cell() */
6095e26dbdSClément Léger 	cell = calloc(1, sizeof(*cell));
6195e26dbdSClément Léger 	if (!cell)
6295e26dbdSClément Léger 		return TEE_ERROR_OUT_OF_MEMORY;
6395e26dbdSClément Léger 
6495e26dbdSClément Léger 	res = nvmem_cell_parse_dt(args->fdt, args->phandle_node, cell);
6595e26dbdSClément Léger 	if (res)
6695e26dbdSClément Léger 		goto out_free;
6795e26dbdSClément Léger 
6895e26dbdSClément Léger 	if (cell->offset + cell->len > ATMEL_SFC_CELLS_8) {
6995e26dbdSClément Léger 		res = TEE_ERROR_GENERIC;
7095e26dbdSClément Léger 		goto out_free;
7195e26dbdSClément Léger 	}
7295e26dbdSClément Léger 
7395e26dbdSClément Léger 	cell->ops = &atmel_sfc_nvmem_ops;
7495e26dbdSClément Léger 	cell->drv_data = data;
7595e26dbdSClément Léger 	*out_cell = cell;
7695e26dbdSClément Léger 
7795e26dbdSClément Léger 	return TEE_SUCCESS;
7895e26dbdSClément Léger 
7995e26dbdSClément Léger out_free:
8095e26dbdSClément Léger 	free(cell);
8195e26dbdSClément Léger 
8295e26dbdSClément Léger 	return res;
8395e26dbdSClément Léger }
8495e26dbdSClément Léger 
atmel_sfc_read_fuse(struct atmel_sfc * atmel_sfc)8595e26dbdSClément Léger static void atmel_sfc_read_fuse(struct atmel_sfc *atmel_sfc)
8695e26dbdSClément Léger {
8795e26dbdSClément Léger 	size_t i = 0;
8895e26dbdSClément Léger 	uint32_t val = 0;
8995e26dbdSClément Léger 
9095e26dbdSClément Léger 	for (i = 0; i < ATMEL_SFC_CELLS_32; i++) {
9195e26dbdSClément Léger 		val = io_read32(atmel_sfc->base + ATMEL_SFC_DR + i * 4);
9295e26dbdSClément Léger 		memcpy(&atmel_sfc->fuses[i * 4], &val, sizeof(val));
9395e26dbdSClément Léger 	}
9495e26dbdSClément Léger }
9595e26dbdSClément Léger 
atmel_sfc_probe(const void * fdt,int node,const void * compat_data __unused)9695e26dbdSClément Léger static TEE_Result atmel_sfc_probe(const void *fdt, int node,
9795e26dbdSClément Léger 				  const void *compat_data __unused)
9895e26dbdSClément Léger {
9995e26dbdSClément Léger 	vaddr_t base = 0;
10095e26dbdSClément Léger 	size_t size = 0;
10195e26dbdSClément Léger 	struct atmel_sfc *atmel_sfc = NULL;
10295e26dbdSClément Léger 
10395e26dbdSClément Léger 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
10495e26dbdSClément Léger 		return TEE_ERROR_NODE_DISABLED;
10595e26dbdSClément Léger 
10695e26dbdSClément Léger 	matrix_configure_periph_secure(AT91C_ID_SFC);
10795e26dbdSClément Léger 
10895e26dbdSClément Léger 	if (dt_map_dev(fdt, node, &base, &size, DT_MAP_AUTO) < 0)
10995e26dbdSClément Léger 		return TEE_ERROR_GENERIC;
11095e26dbdSClément Léger 
11195e26dbdSClément Léger 	atmel_sfc = calloc(1, sizeof(*atmel_sfc));
11295e26dbdSClément Léger 	if (!atmel_sfc)
11395e26dbdSClément Léger 		return TEE_ERROR_OUT_OF_MEMORY;
11495e26dbdSClément Léger 
11595e26dbdSClément Léger 	atmel_sfc->base = base;
11695e26dbdSClément Léger 
11795e26dbdSClément Léger 	atmel_sfc_read_fuse(atmel_sfc);
11895e26dbdSClément Léger 
11995e26dbdSClément Léger 	return nvmem_register_provider(fdt, node, atmel_sfc_dt_get, atmel_sfc);
12095e26dbdSClément Léger }
12195e26dbdSClément Léger 
12295e26dbdSClément Léger static const struct dt_device_match atmel_sfc_match_table[] = {
12395e26dbdSClément Léger 	{ .compatible = "atmel,sama5d2-sfc" },
12495e26dbdSClément Léger 	{ }
12595e26dbdSClément Léger };
12695e26dbdSClément Léger 
12795e26dbdSClément Léger DEFINE_DT_DRIVER(atmel_sfc_dt_driver) = {
12895e26dbdSClément Léger 	.name = "atmel_sfc",
12995e26dbdSClément Léger 	.type = DT_DRIVER_NVMEM,
13095e26dbdSClément Léger 	.match_table = atmel_sfc_match_table,
13195e26dbdSClément Léger 	.probe = atmel_sfc_probe,
13295e26dbdSClément Léger };
133