1 /*
2 * Copyright (c) 2018 Foundries.io Ltd
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT openisa_rv32m1_lptmr
8
9 #include <zephyr/init.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/sys/util.h>
12 #include <zephyr/drivers/timer/system_timer.h>
13 #include <soc.h>
14 #include <zephyr/irq.h>
15
16 /*
17 * This is just a getting started point.
18 *
19 * Assumptions and limitations:
20 *
21 * - system clock based on an LPTMR instance, clocked by SIRC output
22 * SIRCDIV3, prescaler divide-by-1, SIRC at 8MHz
23 * - no tickless
24 */
25
26 #define CYCLES_PER_SEC sys_clock_hw_cycles_per_sec()
27 #define CYCLES_PER_TICK (CYCLES_PER_SEC / CONFIG_SYS_CLOCK_TICKS_PER_SEC)
28 #if defined(CONFIG_TEST)
29 const int32_t z_sys_timer_irq_for_test = DT_IRQN(DT_ALIAS(system_lptmr));
30 #endif
31
32 /*
33 * As a simplifying assumption, we only support a clock ticking at the
34 * SIRC reset rate of 8MHz.
35 */
36 #if defined(CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME) || \
37 (MHZ(8) != CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC)
38 #error "system timer misconfiguration; unsupported clock rate"
39 #endif
40
41 #define SYSTEM_TIMER_INSTANCE \
42 ((LPTMR_Type *)(DT_INST_REG_ADDR(0)))
43
44 #define SIRC_RANGE_8MHZ SCG_SIRCCFG_RANGE(1)
45 #define SIRCDIV3_DIVIDE_BY_1 1
46 #define PCS_SOURCE_SIRCDIV3 0
47
48 struct device; /* forward declaration; type is not used. */
49
50 static volatile uint32_t cycle_count;
51
lptmr_irq_handler(const struct device * unused)52 static void lptmr_irq_handler(const struct device *unused)
53 {
54 ARG_UNUSED(unused);
55
56 SYSTEM_TIMER_INSTANCE->CSR |= LPTMR_CSR_TCF(1); /* Rearm timer. */
57 cycle_count += CYCLES_PER_TICK; /* Track cycles. */
58 sys_clock_announce(1); /* Poke the scheduler. */
59 }
60
sys_clock_cycle_get_32(void)61 uint32_t sys_clock_cycle_get_32(void)
62 {
63 return cycle_count + SYSTEM_TIMER_INSTANCE->CNR;
64 }
65
66 /*
67 * Since we're not tickless, this is identically zero.
68 */
sys_clock_elapsed(void)69 uint32_t sys_clock_elapsed(void)
70 {
71 return 0;
72 }
73
sys_clock_driver_init(void)74 static int sys_clock_driver_init(void)
75 {
76 uint32_t csr, psr, sircdiv; /* LPTMR registers */
77
78 IRQ_CONNECT(DT_INST_IRQN(0),
79 0, lptmr_irq_handler, NULL, 0);
80
81 if ((SCG->SIRCCSR & SCG_SIRCCSR_SIRCEN_MASK) == SCG_SIRCCSR_SIRCEN(0)) {
82 /*
83 * SIRC is on by default, so something else turned it off.
84 *
85 * This is incompatible with this driver, which is SIRC-based.
86 */
87 return -ENODEV;
88 }
89
90 /* Disable the timer and clear any pending IRQ. */
91 csr = SYSTEM_TIMER_INSTANCE->CSR;
92 csr &= ~LPTMR_CSR_TEN(0);
93 csr |= LPTMR_CSR_TFC(1);
94 SYSTEM_TIMER_INSTANCE->CSR = csr;
95
96 /*
97 * Set up the timer clock source and configure the timer.
98 */
99
100 /*
101 * SIRCDIV3 is the SIRC divider for LPTMR (SoC dependent).
102 * Pass it directly through without any divider.
103 */
104 sircdiv = SCG->SIRCDIV;
105 sircdiv &= ~SCG_SIRCDIV_SIRCDIV3_MASK;
106 sircdiv |= SCG_SIRCDIV_SIRCDIV3(SIRCDIV3_DIVIDE_BY_1);
107 SCG->SIRCDIV = sircdiv;
108 /*
109 * TMS = 0: time counter mode, not pulse counter
110 * TCF = 0: reset counter register on reaching compare value
111 * TDRE = 0: disable DMA request
112 */
113 csr &= ~(LPTMR_CSR_TMS(1) | LPTMR_CSR_TFC(1) | LPTMR_CSR_TDRE(1));
114 /*
115 * TIE = 1: enable interrupt
116 */
117 csr |= LPTMR_CSR_TIE(1);
118 SYSTEM_TIMER_INSTANCE->CSR = csr;
119 /*
120 * PCS = 0: clock source is SIRCDIV3 (SoC dependent)
121 * PBYP = 1: bypass the prescaler
122 */
123 psr = SYSTEM_TIMER_INSTANCE->PSR;
124 psr &= ~LPTMR_PSR_PCS_MASK;
125 psr |= (LPTMR_PSR_PBYP(1) | LPTMR_PSR_PCS(PCS_SOURCE_SIRCDIV3));
126 SYSTEM_TIMER_INSTANCE->PSR = psr;
127
128 /*
129 * Set compare register to the proper tick count. The check
130 * here makes sure SIRC is left at its default reset value to
131 * make the defconfig setting work properly.
132 *
133 * TODO: be smarter to meet arbitrary Kconfig settings.
134 */
135 if ((SCG->SIRCCFG & SCG_SIRCCFG_RANGE_MASK) != SIRC_RANGE_8MHZ) {
136 return -EINVAL;
137 }
138 SYSTEM_TIMER_INSTANCE->CMR = CYCLES_PER_TICK;
139
140 /*
141 * Enable interrupts and the timer. There's no need to clear the
142 * TFC bit in the csr variable, as it's already clear.
143 */
144 irq_enable(DT_INST_IRQN(0));
145 csr = SYSTEM_TIMER_INSTANCE->CSR;
146 csr |= LPTMR_CSR_TEN(1);
147 SYSTEM_TIMER_INSTANCE->CSR = csr;
148 return 0;
149 }
150
151 SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2,
152 CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
153