Open 0xkelvin opened 1 year ago
In order to use the UART, you have to do four things:
Note you'll also need to disable the watchdog. To disable the watchdog, you need to write 0x2900 followed by 0xffff. This code: https://github.com/Ion-Mobility-Systems/gridania-bringup-dvt1/blob/e3d628f451388e47a64e4dad2b4bf2a05a7b791d/148/src/main.c#L92-L99 becomes:
const S32K1XX_WDOG_BASE: *mut usize = 0x40052000 as *mut usize;
fn disable_watchdog() {
// Set timeout val to max
unsafe { S32K1XX_WDOG_BASE.add(0).write_volatile(0x2900); };
// Disable watchdog, leaving it unlocked and with the same clock. Note that `8` from the C code
// becomes `2` here, because Rust pointers must always be aligned and this is a pointer to a
// usize.
unsafe { S32K1XX_WDOG_BASE.add(2).write_volatile(0xffff); };
}
I've ported the bringup code directly to Rust, which you can use to verify things work. You could stick this at the top of your program and just kind of assume the clocks and UART are working. You might need to adapt it for LPUART1 if you'd rather use that.
const S32K1XX_WDOG_BASE: *mut usize = 0x40052000 as *mut usize; /* Software watchdog */
const S32K1XX_PCC_BASE: *mut usize = 0x40065000 as *mut usize; /* Peripheral Clock Control */
const S32K1XX_SCG_BASE: *mut usize = 0x40064000 as *mut usize; /* System Clock Generator */
const S32K1XX_LPUART0_BASE: *mut usize = 0x4006a000 as *mut usize; /* Low Power UART 0 */
const S32K1XX_LPUART1_BASE: *mut usize = 0x4006b000 as *mut usize; /* Low Power UART 1 */
const S32K1XX_LPUART2_BASE: *mut usize = 0x4006c000 as *mut usize; /* Low Power UART 2 */
const S32K1XX_PORTA_BASE: *mut usize = 0x40049000 as *mut usize; /* Port A multiplexing control */
const S32K1XX_PORTB_BASE: *mut usize = 0x4004a000 as *mut usize; /* Port B multiplexing control */
const S32K1XX_PORTC_BASE: *mut usize = 0x4004b000 as *mut usize; /* Port C multiplexing control */
const S32K1XX_PORTD_BASE: *mut usize = 0x4004c000 as *mut usize; /* Port D multiplexing control */
const S32K1XX_PORTE_BASE: *mut usize = 0x4004d000 as *mut usize; /* Port E multiplexing control */
fn disable_watchdog() {
// Disable watchdog, leaving it unlocked and with the same clock
unsafe { S32K1XX_WDOG_BASE.add(8/4).write_volatile(0xffff); };
// Set timeout val to max
unsafe { S32K1XX_WDOG_BASE.add(0/4).write_volatile(0x2900); };
}
fn enable_clocks() {
// Enable PORTA-PORTE GPIO clocks
unsafe { S32K1XX_PCC_BASE.add(0x124/4).write_volatile(0xc0000000) };
unsafe { S32K1XX_PCC_BASE.add(0x128/4).write_volatile(0xc0000000) };
unsafe { S32K1XX_PCC_BASE.add(0x12c/4).write_volatile(0xc0000000) };
unsafe { S32K1XX_PCC_BASE.add(0x130/4).write_volatile(0xc0000000) };
unsafe { S32K1XX_PCC_BASE.add(0x134/4).write_volatile(0xc0000000) };
// Enable SIRCDIV_2 and SIRCDIV_1 clocks
unsafe { S32K1XX_SCG_BASE.add(0x200/4).write_volatile(4) };
unsafe { S32K1XX_SCG_BASE.add(0x204/4).write_volatile((1 << 8) | (1 << 0)) };
unsafe { S32K1XX_SCG_BASE.add(0x200/4).write_volatile(5) };
// Enable I2C clocks -- LPI2C0
unsafe { S32K1XX_PCC_BASE.add(0x198/4).write_volatile(0) };
unsafe { S32K1XX_PCC_BASE.add(0x198/4).write_volatile(2 << 24) };
unsafe { S32K1XX_PCC_BASE.add(0x198/4).write_volatile((1 << 30) | (2 << 24)) };
// Enable SPI clocks -- LPSPI0
unsafe { S32K1XX_PCC_BASE.add(0xb0/4).write_volatile(0) };
unsafe { S32K1XX_PCC_BASE.add(0xb0/4).write_volatile(2 << 24) };
unsafe { S32K1XX_PCC_BASE.add(0xb0/4).write_volatile((1 << 30) | (2 << 24)) };
// Enable SPI clocks -- LPSPI1
unsafe { S32K1XX_PCC_BASE.add(0xb4/4).write_volatile(0) };
unsafe { S32K1XX_PCC_BASE.add(0xb4/4).write_volatile(2 << 24) };
unsafe { S32K1XX_PCC_BASE.add(0xb4/4).write_volatile((1 << 30) | (2 << 24)) };
// Enable SPI clocks -- LPSPI2
unsafe { S32K1XX_PCC_BASE.add(0xb8/4).write_volatile(0) };
unsafe { S32K1XX_PCC_BASE.add(0xb8/4).write_volatile(2 << 24) };
unsafe { S32K1XX_PCC_BASE.add(0xb8/4).write_volatile((1 << 30) | (2 << 24)) };
// Enable SPI clocks -- QSPI
unsafe { S32K1XX_PCC_BASE.add(0x1d8/4).write_volatile(0) };
unsafe { S32K1XX_PCC_BASE.add(0x1d8/4).write_volatile(2 << 24) };
unsafe { S32K1XX_PCC_BASE.add(0x1d8/4).write_volatile((1 << 30) | (2 << 24)) };
}
fn enable_osc() -> bool {
// Disable OSC for configuration
unsafe { S32K1XX_SCG_BASE.add(0x100/4).write_volatile(0) };
// Enable the internal oscillator with external XTAL, Medium Range (1-8 MHz)
unsafe { S32K1XX_SCG_BASE.add(0x108/4).write_volatile((2 << 4) | (1 << 2)) };
// Enable OSC
unsafe { S32K1XX_SCG_BASE.add(0x100/4).write_volatile(1) };
// Verify "LK" bit is 1 and return the result
unsafe { S32K1XX_SCG_BASE.add(0x100/4).read_volatile() & (1 << 23) != 0}
}
fn configure_uart_148() {
// Enable SIRCDIV_2 and SIRCDIV_1 clocks.
// (This should already have been done anyway)
unsafe { S32K1XX_SCG_BASE.add(0x200/4).write_volatile(4) };
unsafe { S32K1XX_SCG_BASE.add(0x204/4).write_volatile((1 << 8) | (1 << 0)) };
unsafe { S32K1XX_SCG_BASE.add(0x200/4).write_volatile(5) };
// Enable LPUART clock, and take it from SIRC_DIV2
unsafe { S32K1XX_PCC_BASE.add(0x1A8/4).write_volatile(0) };
unsafe { S32K1XX_PCC_BASE.add(0x1A8/4).write_volatile(2<<24) };
unsafe { S32K1XX_PCC_BASE.add(0x1A8/4).write_volatile((1<<30) | (2<<24)) };
// Set PTB0,1 to ALT2 (LPUART)
unsafe { S32K1XX_PORTB_BASE.add(0).write_volatile(2<<8) };
unsafe { S32K1XX_PORTB_BASE.add(1).write_volatile(2<<8) };
// SIRCDIV_2 is running at 8 MHz. Baud rate is `baud clock (8 MHz) /
// ((OSR+1) x SBR)`. We want 115200, and our OSR defaults to 16. Solve for
// 115200 = 4000000 / ((16+1) x SBR) 115200 = 8000000 / 17 * SBR SBR =
// 4000000/17/115200 SBR = 2
unsafe { S32K1XX_LPUART0_BASE.add(0x18/4).write_volatile(0) }; // Disable transmitter and receiver
unsafe { S32K1XX_LPUART0_BASE.add(0x10/4).write_volatile((17 << 24) | (1 << 16) | (4 << 0)) };
unsafe { S32K1XX_LPUART0_BASE.add(0x18/4).write_volatile((1 << 19) | (1 << 18)) }; // Enable the transmitter and receiver
}
For (4), you can literally steal https://github.com/Ion-Mobility/tock/blob/main/chips/imxrt10xx/src/lpuart.rs wholesale, just change https://github.com/Ion-Mobility/tock/blob/main/chips/imxrt10xx/src/lpuart.rs#L300-L303 to the following addresses:
#define S32K1XX_LPUART0_BASE 0x4006a000 /* Low Power UART 0 */
#define S32K1XX_LPUART1_BASE 0x4006b000 /* Low Power UART 1 */
#define S32K1XX_LPUART2_BASE 0x4006c000 /* Low Power UART 2 */
Hi @xobs
I think i still don't get you, so would like to pursue your patience :grin:
From the bring-up code, i understand which register and which value should i set to make it work. (https://github.com/Ion-Mobility-Systems/gridania-bringup-dvt1/blob/0eea23b284e75715d6aca15eb72aacb0c2bfbdfa/148/src/main.c)
But i feeling stuck when look at the implementation in ccm.rs
In order to reuse the api in ccm.rs, should i change this definition according to S32K14x clock registers, keep the API as the same ?
Or i just need to implement same as bring-up code but in Rust, doesn't care about ccm.rs anymore ?
And should we porting this driver into S32k144_evb in TOCK rather than inherit from imxrt1050-evkb ? i recall that the uart on imxrt is exactly the same as s32k144 in term of register address.
cc: @nguyenlkdn @calvinchengx