1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2022-2023, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8 #include <mod_clock.h>
9 #include <mod_sp805.h>
10
11 #include <fwk_interrupt.h>
12 #include <fwk_log.h>
13 #include <fwk_module.h>
14 #include <fwk_notification.h>
15
16 #define MOD_NAME "[SP805] "
17
18 /* SP805 Watchdog Timer device context */
19 struct mod_sp805_dev_ctx {
20 const struct mod_sp805_config *config;
21 struct sp805_reg *reg_base;
22 };
23
24 static struct mod_sp805_dev_ctx ctx;
25
sp805_isr(uintptr_t unused)26 static void sp805_isr(uintptr_t unused)
27 {
28 FWK_LOG_DEBUG(MOD_NAME "SP805 watchdog timer interrupt generated.");
29
30 /* Clear the watchdog Interrupt */
31 ctx.reg_base->LOCK = ENABLE_WR_ACCESS;
32 ctx.reg_base->INTCLR = 0;
33 ctx.reg_base->LOCK = DISABLE_WR_ACCESS;
34
35 fwk_interrupt_clear_pending(ctx.config->sp805_irq);
36 }
37
enable_sp805_interrupt(void)38 static void enable_sp805_interrupt(void)
39 {
40 /*
41 * Ensure that a valid interrupt number has been specified in the
42 * configuration data. If not specified, the SP805 WDT is not enabled
43 * because there is no other mechanism to write to the WdogIntClr register
44 * when the counter value reaches zero.
45 */
46 if (ctx.config->sp805_irq == FWK_INTERRUPT_NONE) {
47 FWK_LOG_ERR(MOD_NAME
48 "Interrupt number not specified in config data, failed to "
49 "enable watchdog!");
50 return;
51 }
52
53 ctx.reg_base->LOCK = ENABLE_WR_ACCESS;
54 ctx.reg_base->LOAD = ctx.config->wdt_load_value;
55
56 /* Enable interrupt event and reset output */
57 ctx.reg_base->CONTROL = RESET_EN | INT_EN;
58 ctx.reg_base->LOCK = DISABLE_WR_ACCESS;
59 fwk_interrupt_set_isr_param(
60 ctx.config->sp805_irq, sp805_isr, (uintptr_t)NULL);
61
62 fwk_interrupt_clear_pending(ctx.config->sp805_irq);
63 fwk_interrupt_enable(ctx.config->sp805_irq);
64
65 FWK_LOG_INFO(
66 MOD_NAME "WatchDog Interrupt enabled, WdogLoad value: %x",
67 (unsigned int)ctx.reg_base->LOAD);
68 }
69
70 /*
71 * Framework handlers
72 */
mod_sp805_init(fwk_id_t module_id,unsigned int unused,const void * data)73 static int mod_sp805_init(
74 fwk_id_t module_id,
75 unsigned int unused,
76 const void *data)
77 {
78 ctx.config = (struct mod_sp805_config *)data;
79 fwk_assert(ctx.config != NULL);
80
81 ctx.reg_base = (struct sp805_reg *)ctx.config->reg_base;
82 if (ctx.reg_base == NULL) {
83 FWK_LOG_ERR(MOD_NAME "Register base address missing in config data");
84 return FWK_E_DATA;
85 }
86
87 return FWK_SUCCESS;
88 }
89
mod_sp805_start(fwk_id_t id)90 static int mod_sp805_start(fwk_id_t id)
91 {
92 if (!fwk_id_is_type(ctx.config->driver_id, FWK_ID_TYPE_NONE)) {
93 /* Register for clock state notifications */
94 return fwk_notification_subscribe(
95 mod_clock_notification_id_state_changed, ctx.config->driver_id, id);
96 } else {
97 enable_sp805_interrupt();
98 }
99
100 return FWK_SUCCESS;
101 }
102
mod_sp805_process_notification(const struct fwk_event * event,struct fwk_event * resp_event)103 static int mod_sp805_process_notification(
104 const struct fwk_event *event,
105 struct fwk_event *resp_event)
106 {
107 struct clock_notification_params *params;
108
109 fwk_assert(
110 fwk_id_is_equal(event->id, mod_clock_notification_id_state_changed));
111
112 params = (struct clock_notification_params *)event->params;
113 if (params->new_state == MOD_CLOCK_STATE_RUNNING) {
114 enable_sp805_interrupt();
115 }
116
117 return FWK_SUCCESS;
118 }
119
120 const struct fwk_module module_sp805 = {
121 .type = FWK_MODULE_TYPE_DRIVER,
122 .init = mod_sp805_init,
123 .start = mod_sp805_start,
124 .process_notification = mod_sp805_process_notification,
125 };
126