sifive/
prci.rs

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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Power Reset Clock Interrupt controller driver.

use core::cell::Cell;
use kernel::utilities::registers::interfaces::ReadWriteable;
use kernel::utilities::registers::interfaces::Readable;
use kernel::utilities::registers::{register_bitfields, ReadWrite};
use kernel::utilities::StaticRef;
use rv32i::csr;

#[repr(C)]
pub struct PrciRegisters {
    /// Clock Configuration Register
    hfrosccfg: ReadWrite<u32, hfrosccfg::Register>,
    /// Clock Configuration Register
    hfxosccfg: ReadWrite<u32, hfxosccfg::Register>,
    /// PLL Configuration Register
    pllcfg: ReadWrite<u32, pllcfg::Register>,
    /// PLL Divider Register
    plloutdiv: ReadWrite<u32, plloutdiv::Register>,
    /// Clock Configuration Register
    coreclkcfg: ReadWrite<u32>,
}

register_bitfields![u32,
    hfrosccfg [
        ready OFFSET(31) NUMBITS(1) [],
        enable OFFSET(30) NUMBITS(1) [],
        trim OFFSET(16) NUMBITS(5) [],
        div OFFSET(0) NUMBITS(6) []
    ],
    hfxosccfg [
        ready OFFSET(31) NUMBITS(1) [],
        enable OFFSET(30) NUMBITS(1) []
    ],
    pllcfg [
        lock OFFSET(31) NUMBITS(1) [],
        bypass OFFSET(18) NUMBITS(1) [],
        refsel OFFSET(17) NUMBITS(1) [],
        sel OFFSET(16) NUMBITS(1) [],
        pllq OFFSET(10) NUMBITS(2) [],
        pllf OFFSET(4) NUMBITS(6) [],
        pllr OFFSET(0) NUMBITS(3) [
            R1 = 0
        ]
    ],
    plloutdiv [
        divby1 OFFSET(8) NUMBITS(1) [],
        div OFFSET(0) NUMBITS(6) []
    ]
];

pub enum ClockFrequency {
    Freq16Mhz,
    Freq344Mhz,
}

pub struct Prci {
    registers: StaticRef<PrciRegisters>,
    current_frequency: Cell<ClockFrequency>,
}

impl Prci {
    pub const fn new(base: StaticRef<PrciRegisters>) -> Prci {
        Prci {
            registers: base,
            current_frequency: Cell::new(ClockFrequency::Freq16Mhz),
        }
    }

    pub fn switch_to_internal_clock(&self) {
        let regs = self.registers;
        // Enable internal high-frequency clock if it's not enabled
        if regs.hfrosccfg.read(hfrosccfg::enable) == 0 {
            regs.hfrosccfg.modify(hfrosccfg::enable::SET);
        }
        // ... Wait until the clock is ready
        while regs.hfrosccfg.read(hfrosccfg::ready) == 0 {}
        // ... and now actually switch
        regs.pllcfg
            .modify(pllcfg::sel::CLEAR + pllcfg::bypass::CLEAR);
    }

    pub fn set_internal_clock_default(&self) {
        let regs = self.registers;
        // Set to defaults, which according to data sheet should set to 14.4MHz +- 50%,
        regs.hfrosccfg
            .modify(hfrosccfg::div.val(4) + hfrosccfg::trim.val(0x10));
    }

    pub fn enable_external_clock(&self) {
        let regs = self.registers;
        // Make sure external crystal oscillator is enabled
        if regs.hfxosccfg.read(hfxosccfg::enable) == 0 {
            regs.hfxosccfg.modify(hfxosccfg::enable::SET);
        }
        // ... Wait until the clock is ready
        while regs.hfxosccfg.read(hfxosccfg::ready) == 0 {}
    }

    pub fn set_clock_frequency(&self, frequency: ClockFrequency) {
        let regs = self.registers;
        // According to someone affiliated with SiFive in forum post:
        // https://forums.sifive.com/t/is-it-possible-to-brick-the-hifive-board/751/6,
        // it is safe to adjust the internal high frequency clock while it
        // is in use, but not the PLL output.
        //
        // So first switch to internal clock before doing any other clock manipulation

        // Reset internal clock to defaults so we can estimate how long we're spinning for the PLL
        // lock delay. At default, the frequency should be 14.4MHz +- 50%, so a maximum frequency
        // of just under 22MHz
        self.set_internal_clock_default();
        self.switch_to_internal_clock();

        // Make sure external clock subsystem is enabled before adjusting the PLL
        self.enable_external_clock();

        match frequency {
            ClockFrequency::Freq16Mhz => {
                // Bypass enabled, feeds clock directly from external clock. For HiFive1 revB, this
                // is a 16 MHz clock
                regs.pllcfg
                    .modify(pllcfg::bypass::SET + pllcfg::refsel::SET);
                // ... configure final PLL divider to divide by 1
                regs.plloutdiv
                    .modify(plloutdiv::divby1.val(1) + plloutdiv::div.val(0));
                // ... and finally enable the output
                regs.pllcfg.modify(pllcfg::sel::SET);
            }
            ClockFrequency::Freq344Mhz => {
                // Disable bypass, and set external clock as source for PLL
                // divide 16 MHz input by 2 (pllr(1)), times 86 (pllf(42)), divide by 2 (pllq(1))
                // for a frequency of 344MHz
                regs.pllcfg.modify(
                    pllcfg::bypass::CLEAR
                        + pllcfg::refsel::SET
                        + pllcfg::pllr.val(1)
                        + pllcfg::pllf.val(42)
                        + pllcfg::pllq.val(1),
                );
                // Divide PLL output by 1
                regs.plloutdiv
                    .modify(plloutdiv::divby1.val(1) + plloutdiv::div.val(0));

                // We need to wait for PLL to settle before checking if it's stable, which takes
                // about 100 microseconds. Assuming internal clock is worst case of 22MHz (14.7 MHz
                // +- 50%), that's about 2200 cycles.
                let start = csr::CSR.mcycle.get();
                while csr::CSR.mcycle.get() - start < 2200 {}
                // ... and now wait for the PLL lock
                while regs.pllcfg.read(pllcfg::lock) == 0 {}
                // ... and finally switch to the PLL output
                regs.pllcfg.modify(pllcfg::sel::SET);
            }
        };
        self.current_frequency.set(frequency);

        // Finally, disable internal clock as we've now switched to something else.
        regs.hfrosccfg.modify(hfrosccfg::enable::CLEAR);
    }
}