[MMIO] | description ------------|------------------------ 0xb0000200 | clock and reset controller [MMIO]: ./Memory-map ## Registers offset | type | name | description --------|-------|------------|------------------------------------------------------ 0x00 | u32 | CLKEN | clock enable 0x04 | u32 | CLKSEL | clock select 0x08 | u32 | CLKDIV | clock divide 0x0c | u32 | PLLCON0 | PLL0 configuration 0x10 | u32 | PLLCON1 | PLL1 configuration 0x14 | u32 | PMCON | power management control 0x18 | u32 | IRQWAKECON | IRQ wake-up control 0x1c | u32 | IRQWAKEFLAG| IRQ wake-up flags 0x20 | u32 | IPSRST | reset lines 0x24 | u32 | ??? | undocumented PLL ## Clocks The clock tree of the WPCM450 consists of: - An external 48MHz reference clock oscillator - Two [PLLs](https://github.com/neuschaefer/u-boot/blob/vendor/wpcm450-ami/include/wpcm450/hwdef.h#L165), which can be individually enabled and configured - [Clock enable gates](https://github.com/neuschaefer/u-boot/blob/vendor/wpcm450-ami/include/wpcm450/hwdef.h#L104) for every peripheral, allowing selection between [the 48MHz refclock, PLL0, PLL1, and GPIO 83](https://github.com/neuschaefer/u-boot/blob/vendor/wpcm450-ami/include/wpcm450/clocks.h#L61) - [Clock selection muxes](https://github.com/neuschaefer/u-boot/blob/vendor/wpcm450-ami/include/wpcm450/hwdef.h#L135) for different parts of the chip - [Clock dividers](https://github.com/neuschaefer/u-boot/blob/vendor/wpcm450-ami/include/wpcm450/hwdef.h#L151) for different parts of the chip ### PLLs (PLLCONx) ``` reference +---------+ +---------------+ +---------+ output ---------->| input |------------>| |---.-->| output |-------> (48 MHz) | divider | | comparator | | | divider | +---------+ | and | | +---------+ feedback | oscillator | | .----------->| | | | +---------------+ | | | | +----------+ | '--------------| feedback |<-----' | divider | +----------+ ``` The two PLLs are each controlled by a 32-bit register containing multiple fields: bits | name | description -------|-------|--------------------------------------------- 0:5 | INDV | input divider 8:10 | OTDV | output divider 12 | PWDEN | powerdown enable 13 | PRST | PLL reset mode 16:24 | FBDV | feedback divider The output frequency is calculated as: Fout = Fref / (INDV+1) * (FBDV+1) / (OTDV+1) ### Intermediate clocks (CLKSEL and CLKDIV) Between the 48 MHz reference oscillator and the PLLs on one side, and peripherals on the other side, there are intermediate clocks, that allow selecting their parent clock (CLKSEL) or setting a divider (CLKDIV). Most of these clocks correspond to memory buses in the SoC. clock | parent(s) | CLKSEL | divider | CLKDIV | description --------|-----------------|--------|---------|--------|------------------------- CPU | PLL0, PLL1, REF | 0:1 | 2 | -- | CPU clock CLKOUT | PLL0, PLL1, REF | 2:3 | 1 | -- | clock output USBPHY | PLL0, PLL1, REF | 6:7 | 1 | -- | USB 1.1 PHY clock, should be 60 MHz UART | PLL0, PLL1, REF | 8:9 | DIV+1 | 16:19 | UART reference clock HUART | REF, REF/2 | 10 | 1 | -- | Host UART reference clock AHB | CPU | -- | 1,2 | 24:25 | AHB3 | AHB | -- | 1,2,4,8 | 8:9 | AHB1,2 | AHB | -- | 1 | -- | APB | AHB | -- | 1,2,4,8 | 26:27 | ADC | REF | -- | 1,2,4,8 | 28:29 | Unless otherwise noted, two-bit fields can take the following values: value | CLKDIV | CLKSEL --------|-------------------|--------------------------------------------------- 0 | divide by 1 | PLL0 1 | divide by 2 | PLL1 2 | divide by 4 | REF (48 MHz) 3 | divide by 8 | REF or reserved #### Caveats ##### Clock domain crossing When reconfiguring the CPU clock, you must ensure that the bus clocks don't run slower then the connected peripherals. For example, in a fairly standard configuration, we have these values: - PLL0 = 400 MHz - CPU = 200 MHz (PLL0/2) - AHB = 100 MHz (CPU/2) - APB = 50 MHz (AHB/2) - UART = 24 MHz (REF/2) Switching the CPU clock to 100 MHz works, because the APB clock (25 MHz) runs slightly faster than the UART clock (24 MHz). Switching the CPU clock to 66.6 MHz induces malfunction in the UART peripheral, probably at the interface between the APB bus and the UART peripheral. ``` Unknnoowwn coommmmand wwbb0000000 > ``` Thus, when switching the CPU to a clock slower than 96 MHz, the AHB and APB clock dividers should be adjusted before using the UART. ##### Memory controller When you set the CPU clock rate to 24 MHz (REF/2), a few different malfunctions can happen: ###### Zeroes The external DRAM is no longer able to hold data properly: Every byte reads back as 0x00. ###### Weird reordering ``` > ww 3000 1 2 3 4 > rw 3000 4 00003000: 00000002 00000001 00000004 00000003 ``` There are additional random errors, though: ``` >>> l.write32(0x2000, [x for x in range(0x100)]) >>> l.read32(0x2000, 0x100) == [x^1 for x in range(0x100)] False >>> l.read32(0x2000, 0x100) == [x^1 for x in range(0x100)] True >>> l.read32(0x2000, 0x100) == [x^1 for x in range(0x100)] True >>> l.read32(0x2000, 0x100) == [x^1 for x in range(0x100)] False >>> a = l.read32(0x2000, 0x100); b = [x^1 for x in range(0x100)]; print(a==b); [(i, a[i] ^ b[i]) for i in range(0x100) if a[i]!=b[i]] False [(124, 21), (204, 4)] >>> a = l.read32(0x2000, 0x100); b = [x^1 for x in range(0x100)]; print(a==b); [(i, a[i] ^ b[i]) for i in range(0x100) if a[i]!=b[i]] False [(7, 4)] ``` ### Clock gates (CLKEN) bit | name | parent clk | description -----|--------|------------|------------------------------- 0 | FIU | AHB3 | [SPI flash controller](./SPI) 1 | XBUS | AHB3 | [XBUS](./XBUS) 2 | KCS | APB? | 3 | GDMA | | [DMA engine (?)](./GDMA) (reserved) 4 | SHM | AHB3? | 5 | USB1 | AHB1? | [USB 1.1 controller](./USB) 6 | EMC0 | AHB1 | [Ethernet MAC 0](./Ethernet) 7 | EMC1 | AHB1 | [Ethernet MAC 1](./Ethernet) 8 | USB0 | AHB1 | [USB 2.0 controller](./USB) 9 | PECI | APB? | 10 | AES | APB? | 11 | UART0 | UART? | [UART 0](./UARTs) 12 | UART1 | UART? | [UART 1](./UARTs) 13 | SMB2 | APB? | [I2C/SMBus 2](./I2C) 14 | SMB3 | APB? | [I2C/SMBus 3](./I2C) 15 | SMB4 | APB? | [I2C/SMBus 4](./I2C) 16 | SMB5 | APB? | [I2C/SMBus 5](./I2C) 17 | HUART | HUART? | 18 | PWM | APB? | [PWM](./PWM-and-Tachometer) 19 | TIMER0 | REF/2 | 20 | TIMER1 | REF/2 | 21 | TIMER2 | REF/2 | 22 | TIMER3 | REF/2 | 23 | TIMER4 | REF/2 | 24 | MFT0 | APB? | [Multi-function timer 0](./PWM-and-Tachometer) 25 | MFT1 | APB? | [Multi-function timer 1](./PWM-and-Tachometer) 26 | WDT | REF/2 | 27 | ADC | ADC? | 28 | SDIO | AHB1? | [SD card interface](./SD) — note that the SDIO clock speed is based on 48 MHz 29 | SSPI | APB? | 30 | SMB0 | APB? | [I2C/SMBus 0](./I2C) 31 | SMB1 | APB? | [I2C/SMBus 1](./I2C) ## Resets (IPSRST) In addition, the clock controller controls the [reset lines](https://github.com/neuschaefer/u-boot/blob/vendor/wpcm450-ami/include/wpcm450/hwdef.h#L189) of different parts of the chip. bit | name | description -----|--------|------------------------------- 0 | FIU | [SPI flash controller](./SPI) 1 | -- | 2 | -- | 3 | GDMA | [DMA engine (?)](./GDMA) (reserved) 4 | -- | 5 | RNG | [Random number generator](./RNG) (reserved) 6 | EMC0 | [Ethernet MAC 0](./Ethernet) 7 | EMC1 | [Ethernet MAC 1](./Ethernet) 8 | USB0 | [USB 2.0 controller](./USB) 9 | USB1 | [USB 1.1 controller](./USB) 10 |AES/PECI| [AES engine](./AES) and [PECI controller](./PECI) 11 | UART | [both UARTs](./UARTs) 12 | MC | [Memory controller](./Memory-controller) 13 | SMB2 ?| [I2C/SMBus 2](./I2C) 14 | SMB3 ?| [I2C/SMBus 3](./I2C) 15 | SMB4 ?| [I2C/SMBus 4](./I2C) 16 | SMB5 ?| [I2C/SMBus 5](./I2C) 17 | -- | 18 | PWM | [PWM](./PWM-and-Tachometer) 19 | TIMER ?| [Timers](./Timers) 20 | -- | 21 | -- | 22 | -- | 23 | -- | 24 | -- | 25 | -- | 26 | -- | 27 | ADC ?| [ADC](./ADC) 28 | SDIO ?| [SDIO controller](./SD) 29 | SSPI ?| [Secondary SPI controller](./Secondary-SPI) 30 | SMB0 ?| [I2C/SMBus 0](./I2C) 31 | SMB1 ?| [I2C/SMBus 1](./I2C) ### Observations - When FIU is clock-gated, its register and flash windows read 0. After ungating, the old state is restored. - When FIU is reset, its register and flash windows read ff. When it is released from reset, the registers have their initial values. - Ethernet - reset: read 0, release: read 0 (initial values) - gate: read 0, ungate: read values - Some register spaces, such as the one of the AES engine, do not read all-0 when held in reset ## References - [Registers (ATEN)](https://github.com/neuschaefer/linux/blob/vendor/wpcm450-aten/include/asm-arm/arch-wpcm450/regs-clock.h#L31) - Clock enable bits ([AMI](https://github.com/neuschaefer/u-boot/blob/vendor/wpcm450-ami/include/wpcm450/hwdef.h#L104), [Dell](https://github.com/neuschaefer/u-boot/blob/vendor/dell-idrac6-1.70/board/wpcm450/wpcm450_clk_regs.h#L21))