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