summaryrefslogtreecommitdiff
path: root/drivers/st/iwdg/stm32_iwdg.c
blob: ea6fbb2b912dc0bd74a3c6b22d26d053b65b8a2d (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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*
 * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <assert.h>
#include <errno.h>
#include <string.h>

#include <libfdt.h>

#include <platform_def.h>

#include <arch_helpers.h>
#include <common/debug.h>
#include <drivers/arm/gicv2.h>
#include <drivers/delay_timer.h>
#include <drivers/st/stm32_iwdg.h>
#include <drivers/st/stm32mp_clkfunc.h>
#include <lib/mmio.h>
#include <lib/utils.h>
#include <plat/common/platform.h>

/* IWDG registers offsets */
#define IWDG_KR_OFFSET		0x00U

/* Registers values */
#define IWDG_KR_RELOAD_KEY	0xAAAA

struct stm32_iwdg_instance {
	uintptr_t base;
	unsigned long clock;
	uint8_t flags;
	int num_irq;
};

static struct stm32_iwdg_instance stm32_iwdg[IWDG_MAX_INSTANCE];

static int stm32_iwdg_get_dt_node(struct dt_node_info *info, int offset)
{
	int node;

	node = dt_get_node(info, offset, DT_IWDG_COMPAT);
	if (node < 0) {
		if (offset == -1) {
			VERBOSE("%s: No IDWG found\n", __func__);
		}
		return -FDT_ERR_NOTFOUND;
	}

	return node;
}

void stm32_iwdg_refresh(void)
{
	uint8_t i;

	for (i = 0U; i < IWDG_MAX_INSTANCE; i++) {
		struct stm32_iwdg_instance *iwdg = &stm32_iwdg[i];

		/* 0x00000000 is not a valid address for IWDG peripherals */
		if (iwdg->base != 0U) {
			stm32mp_clk_enable(iwdg->clock);

			mmio_write_32(iwdg->base + IWDG_KR_OFFSET,
				      IWDG_KR_RELOAD_KEY);

			stm32mp_clk_disable(iwdg->clock);
		}
	}
}

int stm32_iwdg_init(void)
{
	int node = -1;
	struct dt_node_info dt_info;
	void *fdt;
	uint32_t __unused count = 0;

	if (fdt_get_address(&fdt) == 0) {
		panic();
	}

	for (node = stm32_iwdg_get_dt_node(&dt_info, node);
	     node != -FDT_ERR_NOTFOUND;
	     node = stm32_iwdg_get_dt_node(&dt_info, node)) {
		struct stm32_iwdg_instance *iwdg;
		uint32_t hw_init;
		uint32_t idx;

		count++;

		idx = stm32_iwdg_get_instance(dt_info.base);
		iwdg = &stm32_iwdg[idx];
		iwdg->base = dt_info.base;
		iwdg->clock = (unsigned long)dt_info.clock;

		/* DT can specify low power cases */
		if (fdt_getprop(fdt, node, "stm32,enable-on-stop", NULL) ==
		    NULL) {
			iwdg->flags |= IWDG_DISABLE_ON_STOP;
		}

		if (fdt_getprop(fdt, node, "stm32,enable-on-standby", NULL) ==
		    NULL) {
			iwdg->flags |= IWDG_DISABLE_ON_STANDBY;
		}

		/* Explicit list of supported bit flags */
		hw_init = stm32_iwdg_get_otp_config(idx);

		if ((hw_init & IWDG_HW_ENABLED) != 0) {
			if (dt_info.status == DT_DISABLED) {
				ERROR("OTP enabled but iwdg%u DT-disabled\n",
				      idx + 1U);
				panic();
			}
			iwdg->flags |= IWDG_HW_ENABLED;
		}

		if (dt_info.status == DT_DISABLED) {
			zeromem((void *)iwdg,
				sizeof(struct stm32_iwdg_instance));
			continue;
		}

		if ((hw_init & IWDG_DISABLE_ON_STOP) != 0) {
			iwdg->flags |= IWDG_DISABLE_ON_STOP;
		}

		if ((hw_init & IWDG_DISABLE_ON_STANDBY) != 0) {
			iwdg->flags |= IWDG_DISABLE_ON_STANDBY;
		}

		VERBOSE("IWDG%u found, %ssecure\n", idx + 1U,
			((dt_info.status & DT_NON_SECURE) != 0) ?
			"non-" : "");

#if defined(IMAGE_BL2)
		if (stm32_iwdg_shadow_update(idx, iwdg->flags) != BSEC_OK) {
			return -1;
		}
#endif
	}

	VERBOSE("%u IWDG instance%s found\n", count, (count > 1U) ? "s" : "");

	return 0;
}