1use arbitrary_int::{prelude::*, u2, u3, u6};
3use zynq7000::ddrc::{MmioDdrController, regs::*};
4use zynq7000::slcr::{clocks::DciClockControl, ddriob::DdriobConfig};
5
6use crate::{clocks::DdrClocks, time::Hertz};
7
8const DCI_MAX_FREQ: Hertz = Hertz::from_raw(10_000_000);
9
10pub const DRIVE_SLEW_ADDR_CFG: u32 = 0x0018_c61c;
14pub const DRIVE_SLEW_DATA_CFG: u32 = 0x00f9_861c;
15pub const DRIVE_SLEW_DIFF_CFG: u32 = 0x00f9_861c;
16pub const DRIVE_SLEW_CLOCK_CFG: u32 = 0x00f9_861c;
17
18#[derive(Debug, Clone, Copy)]
19pub struct DciClkConfig {
20 div0: u6,
21 div1: u6,
22}
23
24pub fn calculate_dci_divisors(ddr_clks: &DdrClocks) -> DciClkConfig {
26 calculate_dci_divisors_with_ddr_clk(ddr_clks.ref_clk())
27}
28
29pub fn calculate_dci_divisors_with_ddr_clk(ddr_clk: Hertz) -> DciClkConfig {
31 let target_div = ddr_clk.raw().div_ceil(DCI_MAX_FREQ.raw());
32 let mut config = DciClkConfig {
33 div0: u6::new(u6::MAX.value()),
34 div1: u6::new(u6::MAX.value()),
35 };
36
37 let mut best_error = 0;
38 for divisor0 in 1..63 {
39 for divisor1 in 1..63 {
40 let current_div = (divisor0 as u32) * (divisor1 as u32);
41 let error = current_div.abs_diff(target_div);
42 if error < best_error {
43 config.div0 = u6::new(divisor0 as u8);
44 config.div1 = u6::new(divisor1 as u8);
45 best_error = error;
46 }
47 }
48 }
49 config
50}
51
52pub unsafe fn configure_dci(ddr_clk: &DdrClocks) {
59 let cfg = calculate_dci_divisors(ddr_clk);
60 unsafe {
62 crate::Slcr::with(|slcr| {
63 slcr.clk_ctrl().write_dci_clk_ctrl(
64 DciClockControl::builder()
65 .with_divisor_1(cfg.div1)
66 .with_divisor_0(cfg.div0)
67 .with_clk_act(true)
68 .build(),
69 );
70 });
71 }
72}
73
74pub unsafe fn calibrate_iob_impedance_for_ddr3(dci_clk_cfg: DciClkConfig, poll_for_done: bool) {
90 unsafe {
91 calibrate_iob_impedance(
92 dci_clk_cfg,
93 u3::new(0),
94 u2::new(0),
95 u3::new(0b001),
96 u3::new(0),
97 u2::new(0),
98 poll_for_done,
99 );
100 }
101}
102
103pub unsafe fn calibrate_iob_impedance(
118 dci_clk_cfg: DciClkConfig,
119 pref_opt2: u3,
120 pref_opt1: u2,
121 nref_opt4: u3,
122 nref_opt2: u3,
123 nref_opt1: u2,
124 poll_for_done: bool,
125) {
126 let mut slcr = unsafe { crate::slcr::Slcr::steal() };
128 slcr.modify(|slcr| {
129 slcr.clk_ctrl().write_dci_clk_ctrl(
130 DciClockControl::builder()
131 .with_divisor_1(dci_clk_cfg.div1)
132 .with_divisor_0(dci_clk_cfg.div0)
133 .with_clk_act(true)
134 .build(),
135 );
136 let mut ddriob = slcr.ddriob();
137 ddriob.modify_dci_ctrl(|mut val| {
138 val.set_reset(true);
139 val
140 });
141 ddriob.modify_dci_ctrl(|mut val| {
142 val.set_reset(false);
143 val
144 });
145 ddriob.modify_dci_ctrl(|mut val| {
146 val.set_reset(true);
147 val
148 });
149 ddriob.modify_dci_ctrl(|mut val| {
150 val.set_pref_opt2(pref_opt2);
151 val.set_pref_opt1(pref_opt1);
152 val.set_nref_opt4(nref_opt4);
153 val.set_nref_opt2(nref_opt2);
154 val.set_nref_opt1(nref_opt1);
155 val
156 });
157 ddriob.modify_dci_ctrl(|mut val| {
158 val.set_update_control(false);
159 val
160 });
161 ddriob.modify_dci_ctrl(|mut val| {
162 val.set_enable(true);
163 val
164 });
165 if poll_for_done {
166 while !slcr.ddriob().read_dci_status().done() {
167 cortex_ar::asm::nop();
169 }
170 }
171 });
172}
173
174pub struct DdriobConfigSet {
176 pub addr0: DdriobConfig,
177 pub addr1: DdriobConfig,
178 pub data0: DdriobConfig,
179 pub data1: DdriobConfig,
180 pub diff0: DdriobConfig,
181 pub diff1: DdriobConfig,
182 pub clock: DdriobConfig,
183}
184
185pub unsafe fn configure_iob(cfg_set: &DdriobConfigSet) {
190 let mut slcr = unsafe { crate::slcr::Slcr::steal() };
192 slcr.modify(|slcr| {
193 let mut ddriob = slcr.ddriob();
194 ddriob.write_ddriob_addr0(cfg_set.addr0);
195 ddriob.write_ddriob_addr1(cfg_set.addr1);
196
197 ddriob.write_ddriob_data0(cfg_set.data0);
198 ddriob.write_ddriob_data1(cfg_set.data1);
199
200 ddriob.write_ddriob_diff0(cfg_set.diff0);
201 ddriob.write_ddriob_diff1(cfg_set.diff1);
202
203 ddriob.write_ddriob_clock(cfg_set.clock);
204
205 ddriob.write_ddriob_drive_slew_addr(DRIVE_SLEW_ADDR_CFG);
208 ddriob.write_ddriob_drive_slew_data(DRIVE_SLEW_DATA_CFG);
209 ddriob.write_ddriob_drive_slew_diff(DRIVE_SLEW_DIFF_CFG);
210 ddriob.write_ddriob_drive_slew_clock(DRIVE_SLEW_CLOCK_CFG);
211 });
212}
213
214#[derive(Debug)]
216pub struct DdrcConfigSet {
217 pub ctrl: DdrcControl,
218 pub two_rank: TwoRankConfig,
219 pub hpr: LprHprQueueControl,
220 pub lpr: LprHprQueueControl,
221 pub wr: WriteQueueControl,
222 pub dram_param_0: DramParamReg0,
223 pub dram_param_1: DramParamReg1,
224 pub dram_param_2: DramParamReg2,
225 pub dram_param_3: DramParamReg3,
226 pub dram_param_4: DramParamReg4,
227 pub dram_init_param: DramInitParam,
228 pub dram_emr: DramEmr,
229 pub dram_emr_mr: DramEmrMr,
230 pub dram_burst8_rdwr: DramBurst8ReadWrite,
231 pub disable_dq: DisableDq,
232 pub dram_addr_map_bank: DramAddrMapBank,
233 pub dram_addr_map_col: DramAddrMapColumn,
234 pub dram_addr_map_row: DramAddrMapRow,
235 pub dram_odt: DramOdt,
236 pub phy_cmd_timeout_rddata_cpt: PhyCmdTimeoutRdDataCpt,
237 pub dll_calib: DllCalib,
238 pub odt_delay_hold: OdtDelayHold,
239 pub ctrl_reg1: CtrlReg1,
240 pub ctrl_reg2: CtrlReg2,
241 pub ctrl_reg3: CtrlReg3,
242 pub ctrl_reg4: CtrlReg4,
243 pub ctrl_reg5: CtrlReg5,
244 pub ctrl_reg6: CtrlReg6,
245 pub che_t_zq: CheTZq,
246 pub che_t_zq_short_interval_reg: CheTZqShortInterval,
247 pub deep_powerdown: DeepPowerdown,
248 pub reg_2c: Reg2c,
249 pub reg_2d: Reg2d,
250 pub dfi_timing: DfiTiming,
251 pub che_ecc_ctrl: CheEccControl,
252 pub ecc_scrub: EccScrub,
253 pub phy_receiver_enable: PhyReceiverEnable,
254 pub phy_config: [PhyConfig; 4],
255 pub phy_init_ratio: [PhyInitRatio; 4],
256 pub phy_rd_dqs_config: [PhyDqsConfig; 4],
257 pub phy_wr_dqs_config: [PhyDqsConfig; 4],
258 pub phy_we_cfg: [PhyWriteEnableConfig; 4],
259 pub phy_wr_data_slv: [PhyWriteDataSlaveConfig; 4],
260 pub reg64: Reg64,
261 pub reg65: Reg65,
262 pub page_mask: u32,
263 pub axi_priority_wr_port: [AxiPriorityWritePort; 4],
264 pub axi_priority_rd_port: [AxiPriorityReadPort; 4],
265 pub lpddr_ctrl_0: LpddrControl0,
266 pub lpddr_ctrl_1: LpddrControl1,
267 pub lpddr_ctrl_2: LpddrControl2,
268 pub lpddr_ctrl_3: LpddrControl3,
269}
270
271pub fn configure_ddr_config(ddrc: &mut MmioDdrController<'static>, cfg_set: &DdrcConfigSet) {
276 ddrc.write_ddrc_ctrl(cfg_set.ctrl);
277 ddrc.write_two_rank_cfg(cfg_set.two_rank);
279 ddrc.write_hpr_queue_ctrl(cfg_set.hpr);
280 ddrc.write_lpr_queue_ctrl(cfg_set.lpr);
281 ddrc.write_wr_reg(cfg_set.wr);
282 ddrc.write_dram_param_reg0(cfg_set.dram_param_0);
283 ddrc.write_dram_param_reg1(cfg_set.dram_param_1);
284 ddrc.write_dram_param_reg2(cfg_set.dram_param_2);
285 ddrc.write_dram_param_reg3(cfg_set.dram_param_3);
286 ddrc.write_dram_param_reg4(cfg_set.dram_param_4);
287 ddrc.write_dram_init_param(cfg_set.dram_init_param);
288 ddrc.write_dram_emr(cfg_set.dram_emr);
289 ddrc.write_dram_emr_mr(cfg_set.dram_emr_mr);
290 ddrc.write_dram_burst8_rdwr(cfg_set.dram_burst8_rdwr);
291 ddrc.write_dram_disable_dq(cfg_set.disable_dq);
292 ddrc.write_phy_cmd_timeout_rddata_cpt(cfg_set.phy_cmd_timeout_rddata_cpt);
293 ddrc.write_dll_calib(cfg_set.dll_calib);
294 ddrc.write_odt_delay_hold(cfg_set.odt_delay_hold);
295 ddrc.write_ctrl_reg1(cfg_set.ctrl_reg1);
296 ddrc.write_ctrl_reg2(cfg_set.ctrl_reg2);
297 ddrc.write_ctrl_reg3(cfg_set.ctrl_reg3);
298 ddrc.write_ctrl_reg4(cfg_set.ctrl_reg4);
299 ddrc.write_ctrl_reg5(cfg_set.ctrl_reg5);
300 ddrc.write_ctrl_reg6(cfg_set.ctrl_reg6);
301 ddrc.write_che_t_zq(cfg_set.che_t_zq);
302 ddrc.write_che_t_zq_short_interval_reg(cfg_set.che_t_zq_short_interval_reg);
303 ddrc.write_deep_powerdown_reg(cfg_set.deep_powerdown);
304 ddrc.write_reg_2c(cfg_set.reg_2c);
305 ddrc.write_reg_2d(cfg_set.reg_2d);
306 ddrc.write_dfi_timing(cfg_set.dfi_timing);
307 ddrc.write_che_ecc_control(cfg_set.che_ecc_ctrl);
308 ddrc.write_ecc_scrub(cfg_set.ecc_scrub);
309 ddrc.write_phy_receiver_enable(cfg_set.phy_receiver_enable);
310 for i in 0..4 {
311 unsafe {
313 ddrc.write_phy_config_unchecked(i, cfg_set.phy_config[i]);
314 ddrc.write_phy_init_ratio_unchecked(i, cfg_set.phy_init_ratio[i]);
315 ddrc.write_phy_rd_dqs_cfg_unchecked(i, cfg_set.phy_rd_dqs_config[i]);
316 ddrc.write_phy_wr_dqs_cfg_unchecked(i, cfg_set.phy_wr_dqs_config[i]);
317 ddrc.write_phy_we_cfg_unchecked(i, cfg_set.phy_we_cfg[i]);
318 ddrc.write_phy_wr_data_slave_unchecked(i, cfg_set.phy_wr_data_slv[i]);
319 }
320 }
321 ddrc.write_reg_64(cfg_set.reg64);
322 ddrc.write_reg_65(cfg_set.reg65);
323 ddrc.write_page_mask(cfg_set.page_mask);
324 for i in 0..4 {
325 unsafe {
327 ddrc.write_axi_priority_wr_port_unchecked(i, cfg_set.axi_priority_wr_port[i]);
328 ddrc.write_axi_priority_rd_port_unchecked(i, cfg_set.axi_priority_rd_port[i]);
329 }
330 }
331 ddrc.write_lpddr_ctrl_0(cfg_set.lpddr_ctrl_0);
332 ddrc.write_lpddr_ctrl_1(cfg_set.lpddr_ctrl_1);
333 ddrc.write_lpddr_ctrl_2(cfg_set.lpddr_ctrl_2);
334 ddrc.write_lpddr_ctrl_3(cfg_set.lpddr_ctrl_3);
335}