summaryrefslogtreecommitdiff
path: root/drivers/misc/socfpga_secreg.c
blob: 48ff80fd262088f7cbecc6e33524c5a11c83a212 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2021-2023 Intel Corporation <www.intel.com>
 */

#include <asm/io.h>
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <linux/sizes.h>

#define NUMBER_OF_ELEMENTS 3

static int socfpga_secreg_probe(struct udevice *dev)
{
	const fdt32_t *list;
	fdt_addr_t offset, base;
	fdt_val_t val, read_val, mask, set_mask;
	int size, i;
	u32 blk_sz, reg;
	ofnode node;
	const char *name = NULL;

	debug("%s(dev=%p)\n", __func__, dev);

	if (!dev_has_ofnode(dev))
		return 0;

	dev_for_each_subnode(node, dev) {
		name = ofnode_get_name(node);
		if (!name)
			return -EINVAL;

		if (ofnode_read_u32_index(node, "reg", 1, &blk_sz))
			return -EINVAL;

		base = ofnode_get_addr(node);
		if (base == FDT_ADDR_T_NONE)
			return -EINVAL;

		debug("%s(node_offset 0x%lx node_name %s ", __func__,
		      node.of_offset, name);
		debug("node addr 0x%llx blk sz 0x%x)\n", base, blk_sz);

		list = ofnode_read_prop(node, "intel,offset-settings", &size);
		if (!list)
			return -EINVAL;

		debug("%s(intel,offset-settings property size=%x)\n", __func__,
		      size);
		size /= sizeof(*list) * NUMBER_OF_ELEMENTS;

		/*
		 * First element: offset
		 * Second element: val
		 * Third element: mask
		 */
		for (i = 0; i < size; i++) {
			offset = fdt32_to_cpu(*list++);
			val = fdt32_to_cpu(*list++);

			/* Reads the masking bit value from the list */
			mask = fdt32_to_cpu(*list++);

			/*
			 * Reads out the offsets, value and masking bits
			 * Ex: <0x00000000 0x00000230 0xffffffff>
			 */
			debug("%s(intel,offset-settings 0x%llx : 0x%llx : 0x%llx)\n",
			      __func__, offset, val, mask);

			if (blk_sz < offset + SZ_4) {
				printf("%s: Overflow as offset 0x%llx or reg",
				       __func__, offset);
				printf(" write is more than block size 0x%x\n",
				       blk_sz);
				return -EINVAL;
			}

			if (mask != 0) {
				if (mask == 0xffffffff) {
					reg = base + offset;
					writel(val, (uintptr_t)reg);
				} else {
					/* Mask the value with the masking bits */
					set_mask = val & mask;

					reg = base + offset;

					/* Clears and sets specific bits in the register */
					clrsetbits_le32(reg, mask, set_mask);
				}
			}

			read_val = readl((uintptr_t)reg);

			/* Reads out the register, masked value and the read value */
			debug("%s(reg 0x%x = wr : 0x%llx  rd : 0x%llx)\n",
			      __func__, reg, set_mask, read_val);

		}
	}

	return 0;
};

static const struct udevice_id socfpga_secreg_ids[] = {
	{.compatible = "intel,socfpga-secreg"},
	{ }
};

U_BOOT_DRIVER(socfpga_secreg) = {
	.name		= "socfpga-secreg",
	.id		= UCLASS_NOP,
	.of_match	= socfpga_secreg_ids,
	.probe		= socfpga_secreg_probe,
};