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