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
//! Common control peripheral of DDR SDRAM.

use base_address::{BaseAddress, Dynamic, Static};
use volatile_register::RW;

use super::COM;

/// Common control peripheral registers.
#[repr(C)]
pub struct RegisterBlock {
    pub work_mode_0: RW<u32>, // 0x00
    pub work_mode_1: RW<u32>, // 0x04
    pub dbgcr: RW<u32>,       // 0x08
    pub tmr: RW<u32>,         // 0x0c
    _reserved0: [u32; 1],
    pub cccr: RW<u32>, // 0x14
    _reserved1: [u32; 2],
    pub maer0: RW<u32>, // 0x20
    pub maer1: RW<u32>, // 0x24
    pub maer2: RW<u32>, // 0x28
    _reserved2: [u32; 309],
    pub remap0: RW<u32>, // 0x500
    pub remap1: RW<u32>, // 0x504
    pub remap2: RW<u32>, // 0x508
    pub remap3: RW<u32>, // 0x50c
}

/// Dram type.
pub enum Type {
    /// DDR2.
    Ddr2,
    /// DDR3.
    Ddr3,
    /// LPDDR2.
    LpDdr2,
    /// LPDDR3.
    LpDdr3,
}

/// Dram configuration.
pub struct Config {
    dram_type: Type,
    unknown_tpr13_bit5: bool,
    // rank: u8,
}

impl<const B: usize> COM<Static<B>> {
    /// Create a peripheral instance from statically known address.
    ///
    /// This function is unsafe for it forces to seize ownership from possible
    /// wrapped peripheral group types. Users should normally retrieve ownership
    /// from wrapped types.
    #[inline]
    pub const unsafe fn steal_static() -> COM<Static<B>> {
        COM { base: Static::<B> }
    }
}

impl COM<Dynamic> {
    /// Create a peripheral instance from dynamically known address.
    ///
    /// This function is unsafe for it forces to seize ownership from possible
    /// wrapped peripheral group types. Users should normally retrieve ownership
    /// from wrapped types.
    #[inline]
    pub unsafe fn steal_dynamic(base: *const ()) -> COM<Dynamic> {
        COM {
            base: Dynamic::new(base as usize),
        }
    }
}

impl<A: BaseAddress> COM<A> {
    /// Configure dram settings.
    #[inline]
    pub fn configure(&self, config: Config) {
        let mut bits = 0;
        let mut mask = 0;
        let dram_type_bits = match config.dram_type {
            Type::Ddr2 => 2,
            Type::Ddr3 => 3,
            Type::LpDdr2 => 6,
            Type::LpDdr3 => 7,
        };
        bits |= dram_type_bits << 16;
        mask |= 0xff << 16;
        let dq_width = 1;
        bits |= dq_width << 12;
        mask |= 0x1 << 16;
        let unknown1 = 1;
        bits |= unknown1 << 22;
        mask |= 0x1 << 22;
        let unknown2 = match config.dram_type {
            Type::LpDdr2 | Type::LpDdr3 => 1,
            _ if config.unknown_tpr13_bit5 => 1,
            _ => 0,
        };
        bits |= unknown2 << 19;
        mask |= 0x1 << 19;
        unsafe { self.work_mode_0.modify(|v| (v & !mask) | bits) };
    }

    /// Get DRAM size in bytes.
    #[inline]
    pub fn dram_size(&self) -> usize {
        let bits_0 = self.work_mode_0.read();
        let bits_1 = self.work_mode_1.read();
        fn rank_size_log2(bits: u32) -> u32 {
            let page_size_bits = (bits >> 8) & 0xf;
            let row_width_bits = (bits >> 4) & 0xf;
            let bank_count_bits = (bits >> 2) & 0x3;
            let page_size = page_size_bits + 3;
            let row_width = row_width_bits + 1;
            let bank_count = bank_count_bits + 2;
            page_size + row_width + bank_count
        }
        let ans_rank_0 = rank_size_log2(bits_0);
        let single_rank = (bits_0 & 0x3) == 0;
        if single_rank {
            return 1 << ans_rank_0;
        }
        let two_identical_ranks = (bits_1 & 0x3) == 0;
        if two_identical_ranks {
            return 1 << (ans_rank_0 + 1);
        }
        let ans_rank_1 = rank_size_log2(bits_1);
        1 << (ans_rank_0 + ans_rank_1)
    }
}

#[cfg(test)]
mod tests {
    use super::RegisterBlock;
    use memoffset::offset_of;
    #[test]
    fn offset_com() {
        assert_eq!(offset_of!(RegisterBlock, tmr), 0x0c);
        assert_eq!(offset_of!(RegisterBlock, cccr), 0x14);
        assert_eq!(offset_of!(RegisterBlock, maer0), 0x20);
        assert_eq!(offset_of!(RegisterBlock, remap0), 0x500);
    }
}