Ion-Mobility / tock

A secure embedded operating system for microcontrollers
https://www.tockos.org
Other
0 stars 0 forks source link

Peripheral Clock Controller (PCC) implementation #4

Open 0xkelvin opened 1 year ago

0xkelvin commented 1 year ago

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

cc: @nguyenlkdn @calvinchengx

xobs commented 1 year ago

In order to use the UART, you have to do four things:

  1. Enable the peripheral clocks, otherwise even attempting to write the registers will cause a reset.
  2. Mux the pins as UARTs
  3. Configure the clock dividers so your baudrate is correct
  4. Write bytes to the UART, unless you're doing something fancy like DMA

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 */