8250: merge bcm aux uart into 8250 driver
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockController, ClockHandle},
|
||||
clock::{ClockController, ClockHandle, Hertz, IntoHertz},
|
||||
device::{Device, DeviceInitContext},
|
||||
};
|
||||
use device_tree::{
|
||||
@@ -54,10 +54,10 @@ impl ClockController for Bcm2835Aux {
|
||||
let regs = regs.lock();
|
||||
match clock {
|
||||
Some(0) => {
|
||||
// TODO CPRMAN driver
|
||||
regs.AUX_ENABLES.modify(AUX_ENABLES::MU_ENABLE::SET);
|
||||
Ok(())
|
||||
}
|
||||
None => todo!(),
|
||||
_ => Err(Error::InvalidArgument),
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,16 @@ impl ClockController for Bcm2835Aux {
|
||||
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
|
||||
match clock {
|
||||
Some(0) => {
|
||||
// TODO CPRMAN driver
|
||||
Ok(54u64.mhz())
|
||||
}
|
||||
_ => Err(Error::InvalidArgument),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreeClockController for Bcm2835Aux {
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::ClockHandle,
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
|
||||
use libk::{
|
||||
debug::DebugSink,
|
||||
device::manager::DEVICE_REGISTRY,
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{TerminalOptions, TerminalOutputOptions},
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
AUX_MU_IER_REG [
|
||||
RX_IRQ OFFSET(0) NUMBITS(1) [],
|
||||
TX_IRQ OFFSET(1) NUMBITS(1) [],
|
||||
],
|
||||
AUX_MU_IIR_REG [
|
||||
IID OFFSET(1) NUMBITS(2) [
|
||||
None = 0,
|
||||
TxEmpty = 1,
|
||||
RxNotEmpty = 2,
|
||||
],
|
||||
PENDING OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
AUX_MU_LSR_REG [
|
||||
TX_EMPTY OFFSET(5) NUMBITS(1) [],
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => AUX_MU_IO_REG: ReadWrite<u32>),
|
||||
(0x04 => AUX_MU_IER_REG: ReadWrite<u32, AUX_MU_IER_REG::Register>),
|
||||
(0x08 => AUX_MU_IIR_REG: ReadWrite<u32, AUX_MU_IIR_REG::Register>),
|
||||
(0x0C => _0),
|
||||
(0x14 => AUX_MU_LSR_REG: ReadOnly<u32, AUX_MU_LSR_REG::Register>),
|
||||
(0x18 => _1),
|
||||
(0x30 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
}
|
||||
|
||||
/// Broadcom 283x mini-UART driver
|
||||
pub struct Bcm2835AuxUart {
|
||||
base: PhysicalAddress,
|
||||
irq: IrqHandle,
|
||||
clock: ClockHandle,
|
||||
inner: OneTimeInit<Arc<Terminal<Inner>>>,
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
fn write_byte(&self, byte: u8) -> Result<(), Error> {
|
||||
while !self
|
||||
.AUX_MU_LSR_REG
|
||||
.matches_all(AUX_MU_LSR_REG::TX_EMPTY::SET)
|
||||
{
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.AUX_MU_IO_REG.set(byte as u32);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalOutput for Inner {
|
||||
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
|
||||
let regs = self.regs.lock();
|
||||
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
regs.write_byte(b'\r')?;
|
||||
}
|
||||
regs.write_byte(byte)
|
||||
}
|
||||
|
||||
fn write_multiple(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
options: &TerminalOutputOptions,
|
||||
) -> Result<usize, Error> {
|
||||
let regs = self.regs.lock();
|
||||
for &byte in bytes {
|
||||
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
regs.write_byte(b'\r')?;
|
||||
}
|
||||
regs.write_byte(byte)?;
|
||||
}
|
||||
Ok(bytes.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugSink for Bcm2835AuxUart {
|
||||
fn putc(&self, c: u8) -> Result<(), Error> {
|
||||
self.inner.get().putc_to_output(c)
|
||||
}
|
||||
|
||||
fn puts(&self, s: &str) -> Result<(), Error> {
|
||||
self.inner.get().write_to_output(s.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Bcm2835AuxUart {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
let inner = self.inner.get();
|
||||
|
||||
let (status, byte) = {
|
||||
let regs = inner.output().regs.lock();
|
||||
|
||||
// Reset IRQ
|
||||
regs.AUX_MU_IIR_REG.modify(AUX_MU_IIR_REG::IID::SET);
|
||||
|
||||
let byte = regs.AUX_MU_IO_REG.get() as u8;
|
||||
let status = regs
|
||||
.AUX_MU_IIR_REG
|
||||
.matches_all(AUX_MU_IIR_REG::PENDING::SET);
|
||||
|
||||
(status, byte)
|
||||
};
|
||||
|
||||
if status {
|
||||
inner.write_to_input(byte);
|
||||
}
|
||||
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Bcm2835AuxUart {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
// TODO initialize pinctrl
|
||||
|
||||
self.clock.enable()?;
|
||||
|
||||
let regs = unsafe { DeviceMemoryIo::map(self.base, Default::default()) }?;
|
||||
let config = TerminalOptions {
|
||||
output: TerminalOutputOptions::NL_TO_CRNL,
|
||||
..Default::default()
|
||||
};
|
||||
let output = Inner {
|
||||
regs: IrqSafeSpinlock::new(regs),
|
||||
};
|
||||
let input = TerminalInput::with_capacity(64)?;
|
||||
|
||||
let inner = self
|
||||
.inner
|
||||
.init(Arc::new(Terminal::from_parts(config, input, output)));
|
||||
|
||||
DEVICE_REGISTRY
|
||||
.serial_terminal
|
||||
.register(inner.clone(), Some(self.clone()))
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
self.irq.register(self.clone())?;
|
||||
self.irq.enable()?;
|
||||
|
||||
let inner = self.inner.get().output();
|
||||
let regs = inner.regs.lock();
|
||||
|
||||
regs.AUX_MU_IER_REG
|
||||
.modify(AUX_MU_IER_REG::RX_IRQ::SET + AUX_MU_IER_REG::TX_IRQ::CLEAR);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"bcm2835 mini-UART"
|
||||
}
|
||||
}
|
||||
|
||||
// TODO handle pinctrl
|
||||
device_tree_driver! {
|
||||
compatible: ["brcm,bcm2835-aux-uart"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
let irq = node.interrupt(0)?;
|
||||
let clock = node.clock(0)?;
|
||||
|
||||
Some(Arc::new(Bcm2835AuxUart {
|
||||
base,
|
||||
irq,
|
||||
clock,
|
||||
inner: OneTimeInit::new(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,6 @@
|
||||
extern crate alloc;
|
||||
|
||||
mod aux;
|
||||
mod aux_uart;
|
||||
// mod aux_uart;
|
||||
mod gpio;
|
||||
mod mbox;
|
||||
|
||||
@@ -121,6 +121,18 @@ impl Config for Fdt8250Config {
|
||||
}
|
||||
}
|
||||
|
||||
// uart1: serial@7e215040 {
|
||||
// compatible = "brcm,bcm2835-aux-uart";
|
||||
// reg = <0x7e215040 0x40>;
|
||||
// interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
|
||||
// clocks = <&aux 0x00>;
|
||||
// status = "okay";
|
||||
// pinctrl-names = "default";
|
||||
// pinctrl-0 = <&uart1_gpio14>;
|
||||
// skip-init;
|
||||
// bootph-all;
|
||||
// };
|
||||
|
||||
impl Fdt8250Config {
|
||||
fn from_device_tree(node: &Arc<Node>, context: &mut ProbeContext) -> Option<(Self, Vendor)> {
|
||||
let vendor = if node.is_compatible("snps,dw-apb-uart") {
|
||||
@@ -133,16 +145,19 @@ impl Fdt8250Config {
|
||||
|
||||
let base = node.map_base(context, 0)?;
|
||||
let irq = node.interrupt(0)?;
|
||||
let resets = node.resets().map(|r| r.collect()).unwrap_or_default();
|
||||
let resets: Vec<_> = node.resets().map(|r| r.collect()).unwrap_or_default();
|
||||
let clock_frequency = node
|
||||
.prop_usize("clock-frequency")
|
||||
.map(|clk| Hertz(clk as _));
|
||||
let clk_baud = node.named_clock("baudclk");
|
||||
let mut clk_baud = node.named_clock("baudclk");
|
||||
let clk_apb = node.named_clock("apb_pclk");
|
||||
if clk_baud.is_none() && clk_apb.is_none() {
|
||||
clk_baud = node.clock(0);
|
||||
}
|
||||
|
||||
let reg_io_width = node.prop_usize("reg-io-width").unwrap_or(1);
|
||||
let reg_io_width = node.prop_usize("reg-io-width").unwrap_or(4);
|
||||
let reg_offset = node.prop_usize("reg-offset").unwrap_or(0);
|
||||
let reg_shift = node.prop_usize("reg-shift").unwrap_or(0);
|
||||
let reg_shift = node.prop_usize("reg-shift").unwrap_or(2);
|
||||
|
||||
Some((
|
||||
Self {
|
||||
@@ -168,6 +183,7 @@ impl Fdt8250Config {
|
||||
device_tree_driver! {
|
||||
compatible: [
|
||||
"snps,dw-apb-uart",
|
||||
"brcm,bcm2835-aux-uart",
|
||||
"ns16550a",
|
||||
],
|
||||
driver: {
|
||||
|
||||
@@ -46,6 +46,7 @@ pub trait Access: Sized + Send + 'static {
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Vendor {
|
||||
DwApbUart,
|
||||
Bcm2835Aux,
|
||||
Generic8250,
|
||||
Generic16550,
|
||||
}
|
||||
@@ -102,6 +103,7 @@ impl<A: Access> Device for Uart8250<A> {
|
||||
fn display_name(&self) -> &str {
|
||||
match self.vendor {
|
||||
Vendor::DwApbUart => "Synopsys DesignWare 16550 UART",
|
||||
Vendor::Bcm2835Aux => "bcm2835 AUX UART",
|
||||
Vendor::Generic16550 => "16550 UART",
|
||||
Vendor::Generic8250 => "8250 UART",
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ const REG_LSR: u8 = 5;
|
||||
const DW_REG_USR: u8 = 31;
|
||||
const DW_REG_SRR: u8 = 34;
|
||||
const DW_REG_HTX: u8 = 41;
|
||||
const BCM2835_REG_AUX_MU_CNTL: u8 = 24;
|
||||
|
||||
/// Received Data available interrupt
|
||||
const IER_RXDE: u8 = 1 << 0;
|
||||
@@ -43,6 +44,9 @@ const MCR_DTR: u8 = 1 << 0;
|
||||
const MCR_RTS: u8 = 1 << 1;
|
||||
const LSR_TEMT: u8 = 1 << 6;
|
||||
|
||||
const BCM2835_AUX_MU_CNTL_RE: u8 = 1 << 0;
|
||||
const BCM2835_AUX_MU_CNTL_TE: u8 = 1 << 1;
|
||||
|
||||
pub struct Port8250<A: Access> {
|
||||
pub(crate) vendor: Vendor,
|
||||
pub(crate) inner: OneTimeInit<Arc<Terminal<Inner<A>>>>,
|
||||
@@ -167,16 +171,26 @@ impl<A: Access> TerminalOutput for Inner<A> {
|
||||
|
||||
impl<A: Access> PortRegs<A> {
|
||||
fn init(&mut self, vendor: Vendor, baud_rate: u32) -> Result<(), Error> {
|
||||
if vendor == Vendor::DwApbUart {
|
||||
self.write(DW_REG_SRR, 1);
|
||||
delay();
|
||||
self.write(DW_REG_HTX, 0);
|
||||
delay();
|
||||
|
||||
// wait while DW USR.BUSY == 1
|
||||
while self.read(DW_REG_USR) & (1 << 0) != 0 {
|
||||
core::hint::spin_loop();
|
||||
match vendor {
|
||||
Vendor::Bcm2835Aux => {
|
||||
self.write(
|
||||
BCM2835_REG_AUX_MU_CNTL,
|
||||
BCM2835_AUX_MU_CNTL_RE | BCM2835_AUX_MU_CNTL_TE,
|
||||
);
|
||||
delay();
|
||||
}
|
||||
Vendor::DwApbUart => {
|
||||
self.write(DW_REG_SRR, 1);
|
||||
delay();
|
||||
self.write(DW_REG_HTX, 0);
|
||||
delay();
|
||||
|
||||
// wait while DW USR.BUSY == 1
|
||||
while self.read(DW_REG_USR) & (1 << 0) != 0 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.set_baud_rate(baud_rate)
|
||||
|
||||
Reference in New Issue
Block a user