psoc62xa/
tcpwm.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright OxidOS Automotive 2025 SRL.
4
5use kernel::hil::{
6    self,
7    time::{self, Alarm, Ticks, Time},
8};
9use kernel::utilities::cells::OptionalCell;
10use kernel::utilities::registers::{
11    interfaces::{ReadWriteable, Readable},
12    register_bitfields, register_structs, ReadOnly, ReadWrite,
13};
14use kernel::utilities::StaticRef;
15
16register_structs! {
17    Tcpwm0Registers {
18        (0x000 => ctrl: ReadWrite<u32, CTRL::Register>),
19        (0x004 => ctrl_clr: ReadWrite<u32, CTRL_CLR::Register>),
20        (0x008 => ctrl_set: ReadWrite<u32, CTRL_SET::Register>),
21        (0x00C => cmd_capture: ReadWrite<u32, CMD_CAPTURE::Register>),
22        (0x010 => cmd_reload: ReadWrite<u32, CMD_RELOAD::Register>),
23        (0x014 => cmd_stop: ReadWrite<u32, CMD_STOP::Register>),
24        (0x018 => cmd_start: ReadWrite<u32, CMD_START::Register>),
25        (0x01C => intr_cause: ReadOnly<u32, INTR_CAUSE::Register>),
26        (0x020 => _reserved0),
27        (0x100 => cnt0_ctrl: ReadWrite<u32, CNT_CTRL::Register>),
28        (0x104 => cnt0_status: ReadWrite<u32, CNT_STATUS::Register>),
29        (0x108 => cnt0_counter: ReadWrite<u32, CNT_COUNTER::Register>),
30        (0x10c => cnt0_cc: ReadWrite<u32, CNT_CC::Register>),
31        (0x110 => cnt0_cc_buff: ReadWrite<u32, CNT_CC_BUFF::Register>),
32        (0x114 => cnt0_period: ReadWrite<u32, CNT_PERIOD::Register>),
33        (0x118 => cnt0_period_buff: ReadWrite<u32, CNT_PERIOD_BUFF::Register>),
34        (0x11c => _reserved1),
35        (0x120 => cnt0_tr_ctrl0: ReadWrite<u32, CNT_TR_CTRL0::Register>),
36        (0x124 => cnt0_tr_ctrl1: ReadWrite<u32, CNT_TR_CTRL1::Register>),
37        (0x128 => cnt0_tr_ctrl2: ReadWrite<u32, CNT_TR_CTRL2::Register>),
38        (0x12c => _reserved2),
39        (0x130 => cnt0_intr: ReadWrite<u32, CNT_INTR::Register>),
40        (0x134 => cnt0_intr_set: ReadWrite<u32, CNT_INTR_SET::Register>),
41        (0x138 => cnt0_intr_mask: ReadWrite<u32, CNT_INTR_MASK::Register>),
42        (0x13c => cnt0_intr_masked: ReadWrite<u32, CNT_INTR_MASKED::Register>),
43        (0x140 => @END),
44    }
45}
46register_bitfields![u32,
47CTRL [
48    COUNTER_ENABLED OFFSET(0) NUMBITS(32) []
49],
50CTRL_CLR [
51    COUNTER_ENABLED OFFSET(0) NUMBITS(32) []
52],
53CTRL_SET [
54    COUNTER_ENABLED OFFSET(0) NUMBITS(32) []
55],
56CMD_CAPTURE [
57    COUNTER_CAPTURE OFFSET(0) NUMBITS(32) []
58],
59CMD_RELOAD [
60    COUNTER_RELOAD OFFSET(0) NUMBITS(32) []
61],
62CMD_STOP [
63    COUNTER_STOP OFFSET(0) NUMBITS(32) []
64],
65CMD_START [
66    COUNTER_START OFFSET(0) NUMBITS(32) []
67],
68INTR_CAUSE [
69    COUNTER_INT OFFSET(0) NUMBITS(32) []
70],
71CNT_CTRL [
72    AUTO_RELOAD_CC OFFSET(0) NUMBITS(1) [],
73    AUTO_RELOAD_PERIOD OFFSET(1) NUMBITS(1) [],
74    PWM_SYNC_KILL OFFSET(2) NUMBITS(1) [],
75    PWM_STOP_ON_KILL OFFSET(3) NUMBITS(1) [],
76    GENERIC OFFSET(8) NUMBITS(8) [
77        DivBy1 = 0,
78        DivBy2 = 1,
79        DivBy4 = 2,
80        DivBy8 = 3,
81        DivBy16 = 4,
82        DivBy32 = 5,
83        DivBy64 = 6,
84        DivBy128 = 7,
85    ],
86    UP_DOWN_MODE OFFSET(16) NUMBITS(2) [
87        Count_UP = 0,
88        Count_DOWN = 1,
89        Count_UPDN1 = 2,
90        Count_UPDN2 = 3,
91    ],
92    ONE_SHOT OFFSET(18) NUMBITS(1) [],
93    QUADRATURE_MODE OFFSET(20) NUMBITS(2) [],
94    MODE OFFSET(24) NUMBITS(3) [
95        Timer = 0,
96        Capture = 2,
97        Quad = 3,
98        Pwm = 4,
99        Pwm_DT = 5,
100        Pwm_PR = 6
101    ]
102],
103CNT_STATUS [
104    DOWN OFFSET(0) NUMBITS(1) [],
105    GENERIC OFFSET(8) NUMBITS(8) [],
106    RUNNING OFFSET(31) NUMBITS(1) [],
107],
108CNT_COUNTER [
109    COUNTER OFFSET(0) NUMBITS(32) []
110],
111CNT_CC [
112    CC OFFSET(0) NUMBITS(32) []
113],
114CNT_CC_BUFF [
115    CC OFFSET(0) NUMBITS(32) []
116],
117CNT_PERIOD [
118    PERIOD OFFSET(0) NUMBITS(32) []
119],
120CNT_PERIOD_BUFF [
121    PERIOD OFFSET(0) NUMBITS(32) []
122],
123CNT_TR_CTRL0 [
124    CAPTURE_SEL OFFSET(0) NUMBITS(4) [],
125    COUNT_SEL OFFSET(4) NUMBITS(4) [],
126    RELOAD_SEL OFFSET(8) NUMBITS(4) [],
127    STOP_SEL OFFSET(12) NUMBITS(4) [],
128    START_SEL OFFSET(16) NUMBITS(4) []
129],
130CNT_TR_CTRL1 [
131    CAPTURE_EDGE OFFSET(0) NUMBITS(2) [
132        Rising = 0,
133        Falling = 1,
134        Both = 2,
135        NoEdge = 3,
136    ],
137    COUNT_EDGE OFFSET(2) NUMBITS(2) [
138        Rising = 0,
139        Falling = 1,
140        Both = 2,
141        NoEdge = 3,
142    ],
143    RELOAD_EDGE OFFSET(4) NUMBITS(2) [
144        Rising = 0,
145        Falling = 1,
146        Both = 2,
147        NoEdge = 3,
148    ],
149    STOP_EDGE OFFSET(6) NUMBITS(2) [
150        Rising = 0,
151        Falling = 1,
152        Both = 2,
153        NoEdge = 3,
154    ],
155    START_EDGE OFFSET(8) NUMBITS(2) [
156        Rising = 0,
157        Falling = 1,
158        Both = 2,
159        NoEdge = 3,
160    ]
161],
162CNT_TR_CTRL2 [
163    CC_MATCH_MODE OFFSET(0) NUMBITS(2) [
164        Set = 0,
165        Clear = 1,
166        Invert = 2,
167        NoChange = 3
168    ],
169    OVERFLOW_MODE OFFSET(2) NUMBITS(2) [
170        Set = 0,
171        Clear = 1,
172        Invert = 2,
173        NoChange = 3
174    ],
175    UNDERFLOW_MODE OFFSET(4) NUMBITS(2) [
176        Set = 0,
177        Clear = 1,
178        Invert = 2,
179        NoChange = 3
180    ]
181],
182CNT_INTR [
183    TC OFFSET(0) NUMBITS(1) [],
184    CC_MATCH OFFSET(1) NUMBITS(1) []
185],
186CNT_INTR_SET [
187    TC OFFSET(0) NUMBITS(1) [],
188    CC_MATCH OFFSET(1) NUMBITS(1) []
189],
190CNT_INTR_MASK [
191    TC OFFSET(0) NUMBITS(1) [],
192    CC_MATCH OFFSET(1) NUMBITS(1) []
193],
194CNT_INTR_MASKED [
195    TC OFFSET(0) NUMBITS(1) [],
196    CC_MATCH OFFSET(1) NUMBITS(1) []
197]
198];
199const TCPWM0_BASE: StaticRef<Tcpwm0Registers> =
200    unsafe { StaticRef::new(0x40380000 as *const Tcpwm0Registers) };
201
202pub struct Tcpwm0<'a> {
203    registers: StaticRef<Tcpwm0Registers>,
204    client: OptionalCell<&'a dyn hil::time::AlarmClient>,
205}
206
207impl Tcpwm0<'_> {
208    pub const fn new() -> Self {
209        Self {
210            registers: TCPWM0_BASE,
211            client: OptionalCell::empty(),
212        }
213    }
214
215    pub fn enable_interrupt(&self) {
216        self.registers
217            .cnt0_intr_mask
218            .modify(CNT_INTR_MASK::CC_MATCH::SET);
219    }
220
221    pub fn init_timer(&self) {
222        self.registers
223            .ctrl_clr
224            .modify(CTRL_CLR::COUNTER_ENABLED.val(1));
225        self.registers.cnt0_ctrl.modify(CNT_CTRL::MODE::Timer);
226        self.registers
227            .cnt0_period
228            .modify(CNT_PERIOD::PERIOD.val(!0));
229        self.registers.cnt0_cc.modify(CNT_CC::CC.val(!0));
230        self.registers.cnt0_cc_buff.modify(CNT_CC_BUFF::CC.val(0));
231        self.registers.cnt0_ctrl.modify(
232            CNT_CTRL::AUTO_RELOAD_CC::CLEAR
233                + CNT_CTRL::GENERIC::DivBy1
234                + CNT_CTRL::UP_DOWN_MODE::Count_UP
235                + CNT_CTRL::ONE_SHOT::CLEAR,
236        );
237        self.registers.cnt0_tr_ctrl0.modify(
238            CNT_TR_CTRL0::STOP_SEL::CLEAR
239                + CNT_TR_CTRL0::COUNT_SEL.val(1)
240                + CNT_TR_CTRL0::CAPTURE_SEL::CLEAR
241                + CNT_TR_CTRL0::RELOAD_SEL::CLEAR
242                + CNT_TR_CTRL0::START_SEL::CLEAR,
243        );
244        self.registers
245            .ctrl_set
246            .modify(CTRL_SET::COUNTER_ENABLED.val(1));
247        self.registers
248            .cmd_reload
249            .modify(CMD_RELOAD::COUNTER_RELOAD.val(1));
250        while self.registers.cmd_reload.read(CMD_RELOAD::COUNTER_RELOAD) == 1 {}
251    }
252
253    pub fn set_timer_ticks(&self, ticks: u32) {
254        self.registers.cnt0_cc.modify(CNT_CC::CC.val(ticks - 1));
255    }
256
257    pub fn disable_interrupt(&self) {
258        self.registers
259            .cnt0_intr_mask
260            .modify(CNT_INTR_MASK::CC_MATCH::CLEAR);
261    }
262
263    pub fn handle_interrupt(&self) {
264        if self.registers.cnt0_intr.is_set(CNT_INTR::CC_MATCH) {
265            self.client.map(|client| client.alarm());
266            self.registers.cnt0_intr.modify(CNT_INTR::CC_MATCH::SET);
267        }
268    }
269}
270
271impl Time for Tcpwm0<'_> {
272    type Frequency = time::Freq1MHz;
273    type Ticks = time::Ticks32;
274
275    fn now(&self) -> Self::Ticks {
276        time::Ticks32::from(self.registers.cnt0_counter.read(CNT_COUNTER::COUNTER))
277    }
278}
279
280impl<'a> Alarm<'a> for Tcpwm0<'a> {
281    fn set_alarm_client(&self, client: &'a dyn time::AlarmClient) {
282        self.client.set(client)
283    }
284
285    fn set_alarm(&self, reference: Self::Ticks, dt: Self::Ticks) {
286        let mut expire = reference.wrapping_add(dt);
287        let now = self.now();
288        if !now.within_range(reference, expire) {
289            expire = now;
290        }
291
292        if expire.wrapping_sub(now) < self.minimum_dt() {
293            expire = now.wrapping_add(self.minimum_dt());
294        }
295
296        self.set_timer_ticks(expire.into_u32());
297        self.enable_interrupt();
298    }
299
300    fn disarm(&self) -> Result<(), kernel::ErrorCode> {
301        self.disable_interrupt();
302        Ok(())
303    }
304
305    fn is_armed(&self) -> bool {
306        self.registers
307            .cnt0_intr_mask
308            .is_set(CNT_INTR_MASK::CC_MATCH)
309    }
310
311    fn minimum_dt(&self) -> Self::Ticks {
312        // This is a small arbitrary value.
313        Self::Ticks::from(50)
314    }
315
316    fn get_alarm(&self) -> Self::Ticks {
317        Self::Ticks::from(self.registers.cnt0_cc.read(CNT_CC::CC))
318    }
319}