-
Notifications
You must be signed in to change notification settings - Fork 6.8k
/
clock_control_npcx.c
200 lines (170 loc) · 5.56 KB
/
clock_control_npcx.c
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/*
* Copyright (c) 2020 Nuvoton Technology Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nuvoton_npcx_pcc
#include <soc.h>
#include <drivers/clock_control.h>
#include <dt-bindings/clock/npcx_clock.h>
#include <logging/log.h>
LOG_MODULE_REGISTER(clock_control_npcx, LOG_LEVEL_ERR);
/* Driver config */
struct npcx_pcc_config {
/* cdcg device base address */
uintptr_t base_cdcg;
/* pmc device base address */
uintptr_t base_pmc;
};
/* Driver convenience defines */
#define DRV_CONFIG(dev) \
((const struct npcx_pcc_config *)(dev)->config)
#define HAL_CDCG_INST(dev) \
(struct cdcg_reg *)(DRV_CONFIG(dev)->base_cdcg)
#define HAL_PMC_INST(dev) \
(struct pmc_reg *)(DRV_CONFIG(dev)->base_pmc)
/* Clock controller local functions */
static inline int npcx_clock_control_on(const struct device *dev,
clock_control_subsys_t sub_system)
{
ARG_UNUSED(dev);
struct npcx_clk_cfg *clk_cfg = (struct npcx_clk_cfg *)(sub_system);
const uint32_t pmc_base = DRV_CONFIG(dev)->base_pmc;
if (clk_cfg->ctrl >= NPCX_PWDWN_CTL_COUNT || clk_cfg->bit >= 8)
return -EINVAL;
/* Clear related PD (Power-Down) bit of module to turn on clock */
NPCX_PWDWN_CTL(pmc_base, clk_cfg->ctrl) &= ~(BIT(clk_cfg->bit));
return 0;
}
static inline int npcx_clock_control_off(const struct device *dev,
clock_control_subsys_t sub_system)
{
ARG_UNUSED(dev);
struct npcx_clk_cfg *clk_cfg = (struct npcx_clk_cfg *)(sub_system);
const uint32_t pmc_base = DRV_CONFIG(dev)->base_pmc;
if (clk_cfg->ctrl >= NPCX_PWDWN_CTL_COUNT || clk_cfg->bit >= 8)
return -EINVAL;
/* Set related PD (Power-Down) bit of module to turn off clock */
NPCX_PWDWN_CTL(pmc_base, clk_cfg->ctrl) |= BIT(clk_cfg->bit);
return 0;
}
static int npcx_clock_control_get_subsys_rate(const struct device *dev,
clock_control_subsys_t sub_system,
uint32_t *rate)
{
ARG_UNUSED(dev);
struct npcx_clk_cfg *clk_cfg = (struct npcx_clk_cfg *)(sub_system);
switch (clk_cfg->bus) {
case NPCX_CLOCK_BUS_APB1:
*rate = NPCX_APB_CLOCK(1);
break;
case NPCX_CLOCK_BUS_APB2:
*rate = NPCX_APB_CLOCK(2);
break;
case NPCX_CLOCK_BUS_APB3:
*rate = NPCX_APB_CLOCK(3);
break;
case NPCX_CLOCK_BUS_AHB6:
*rate = CORE_CLK/(AHB6DIV_VAL + 1);
break;
case NPCX_CLOCK_BUS_FIU:
*rate = CORE_CLK/(FIUDIV_VAL + 1);
break;
case NPCX_CLOCK_BUS_CORE:
*rate = CORE_CLK;
break;
case NPCX_CLOCK_BUS_LFCLK:
*rate = LFCLK;
break;
default:
*rate = 0U;
/* Invalid parameters */
return -EINVAL;
};
return 0;
}
/* Platform specific clock controller functions */
#if defined(CONFIG_PM)
void npcx_clock_control_turn_on_system_sleep(bool is_deep, bool is_instant)
{
const struct device *const clk_dev =
device_get_binding(NPCX_CLK_CTRL_NAME);
struct pmc_reg *const inst_pmc = HAL_PMC_INST(clk_dev);
/* Configure that ec enters system sleep mode if receiving 'wfi' */
uint8_t pm_flags = BIT(NPCX_PMCSR_IDLE);
/* Add 'Disable High-Frequency' flag (ie. 'deep sleep' mode) */
if (is_deep) {
pm_flags |= BIT(NPCX_PMCSR_DHF);
/* Add 'Instant Wake-up' flag if sleep time is within 200 ms */
if (is_instant)
pm_flags |= BIT(NPCX_PMCSR_DI_INSTW);
}
inst_pmc->PMCSR = pm_flags;
}
void npcx_clock_control_turn_off_system_sleep(void)
{
const struct device *const clk_dev =
device_get_binding(NPCX_CLK_CTRL_NAME);
struct pmc_reg *const inst_pmc = HAL_PMC_INST(clk_dev);
inst_pmc->PMCSR = 0;
}
#endif /* CONFIG_PM */
/* Clock controller driver registration */
static struct clock_control_driver_api npcx_clock_control_api = {
.on = npcx_clock_control_on,
.off = npcx_clock_control_off,
.get_rate = npcx_clock_control_get_subsys_rate,
};
static int npcx_clock_control_init(const struct device *dev)
{
struct cdcg_reg *const inst_cdcg = HAL_CDCG_INST(dev);
const uint32_t pmc_base = DRV_CONFIG(dev)->base_pmc;
/*
* Resetting the OSC_CLK (even to the same value) will make the clock
* unstable for a little which can affect peripheral communication like
* eSPI. Skip this if not needed.
*/
if (inst_cdcg->HFCGN != HFCGN_VAL || inst_cdcg->HFCGML != HFCGML_VAL
|| inst_cdcg->HFCGMH != HFCGMH_VAL) {
/*
* Configure frequency multiplier M/N values according to
* the requested OSC_CLK (Unit:Hz).
*/
inst_cdcg->HFCGN = HFCGN_VAL;
inst_cdcg->HFCGML = HFCGML_VAL;
inst_cdcg->HFCGMH = HFCGMH_VAL;
/* Load M and N values into the frequency multiplier */
inst_cdcg->HFCGCTRL |= BIT(NPCX_HFCGCTRL_LOAD);
/* Wait for stable */
while (IS_BIT_SET(inst_cdcg->HFCGCTRL, NPCX_HFCGCTRL_CLK_CHNG))
;
}
/* Set all clock prescalers of core and peripherals. */
inst_cdcg->HFCGP = ((FPRED_VAL << 4) | AHB6DIV_VAL);
inst_cdcg->HFCBCD = (FIUDIV_VAL << 4);
inst_cdcg->HFCBCD1 = (APB1DIV_VAL | (APB2DIV_VAL << 4));
inst_cdcg->HFCBCD2 = APB3DIV_VAL;
/*
* Power-down (turn off clock) the modules initially for better
* power consumption.
*/
NPCX_PWDWN_CTL(pmc_base, NPCX_PWDWN_CTL1) = 0xF9; /* No SDP_PD/FIU_PD */
NPCX_PWDWN_CTL(pmc_base, NPCX_PWDWN_CTL2) = 0xFF;
NPCX_PWDWN_CTL(pmc_base, NPCX_PWDWN_CTL3) = 0x1F; /* No GDMA_PD */
NPCX_PWDWN_CTL(pmc_base, NPCX_PWDWN_CTL4) = 0xFF;
NPCX_PWDWN_CTL(pmc_base, NPCX_PWDWN_CTL5) = 0xFA;
NPCX_PWDWN_CTL(pmc_base, NPCX_PWDWN_CTL6) = 0xFF;
NPCX_PWDWN_CTL(pmc_base, NPCX_PWDWN_CTL7) = 0xE7;
return 0;
}
const struct npcx_pcc_config pcc_config = {
.base_cdcg = DT_INST_REG_ADDR_BY_NAME(0, cdcg),
.base_pmc = DT_INST_REG_ADDR_BY_NAME(0, pmc),
};
DEVICE_DT_INST_DEFINE(0,
&npcx_clock_control_init,
device_pm_control_nop,
NULL, &pcc_config,
PRE_KERNEL_1,
CONFIG_KERNEL_INIT_PRIORITY_OBJECTS,
&npcx_clock_control_api);