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