2019-12-18 06:35:58 +08:00
|
|
|
use libregister::*;
|
2019-05-22 07:42:00 +08:00
|
|
|
use super::regs::{RegisterBlock, BaudRateGen, BaudRateDiv};
|
|
|
|
|
2019-05-30 06:22:45 +08:00
|
|
|
const BDIV_MIN: u32 = 4;
|
|
|
|
const BDIV_MAX: u32 = 255;
|
2019-05-22 07:42:00 +08:00
|
|
|
const CD_MAX: u16 = 65535;
|
|
|
|
|
2019-05-23 21:37:07 +08:00
|
|
|
fn div_round_closest(q: u32, d: u32) -> u32 {
|
|
|
|
(q + (d / 2)) / d
|
|
|
|
}
|
|
|
|
|
2019-05-22 07:42:00 +08:00
|
|
|
/// Algorithm as in the Linux 5.1 driver
|
2019-05-24 00:01:18 +08:00
|
|
|
pub fn configure(regs: &mut RegisterBlock, mut clk: u32, baud: u32) {
|
2019-05-22 07:42:00 +08:00
|
|
|
if regs.mode.read().clks() {
|
|
|
|
clk /= 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut best = None;
|
|
|
|
for bdiv in BDIV_MIN..=BDIV_MAX {
|
2019-05-23 21:37:07 +08:00
|
|
|
let cd = div_round_closest(clk, baud * (bdiv + 1));
|
2019-05-22 07:42:00 +08:00
|
|
|
if cd < 1 || cd > CD_MAX.into() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let actual_baud = clk / (cd * (bdiv + 1));
|
|
|
|
let error = if baud > actual_baud {
|
|
|
|
baud - actual_baud
|
|
|
|
} else {
|
|
|
|
actual_baud - baud
|
|
|
|
};
|
|
|
|
let better = best
|
|
|
|
.map(|(_cd, _bdiv, best_error)| error < best_error)
|
|
|
|
.unwrap_or(true);
|
|
|
|
if better {
|
|
|
|
best = Some((cd as u16, bdiv as u8, error));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match best {
|
2019-08-11 06:55:27 +08:00
|
|
|
Some((cd, bdiv, _error)) => {
|
2019-05-22 07:42:00 +08:00
|
|
|
regs.baud_rate_gen.write(BaudRateGen::zeroed().cd(cd));
|
|
|
|
regs.baud_rate_divider.write(BaudRateDiv::zeroed().bdiv(bdiv));
|
|
|
|
}
|
|
|
|
None => panic!("Cannot configure baud rate")
|
|
|
|
}
|
|
|
|
}
|