paul@160 | 1 | /* |
paul@160 | 2 | * Clock and power management. This exposes the combined functionality |
paul@160 | 3 | * provided by the X1600 and related SoCs. The power management |
paul@160 | 4 | * functionality could be exposed using a separate driver. |
paul@160 | 5 | * |
paul@160 | 6 | * Copyright (C) 2017, 2018, 2020, 2021, 2023 Paul Boddie <paul@boddie.org.uk> |
paul@160 | 7 | * |
paul@160 | 8 | * This program is free software; you can redistribute it and/or |
paul@160 | 9 | * modify it under the terms of the GNU General Public License as |
paul@160 | 10 | * published by the Free Software Foundation; either version 2 of |
paul@160 | 11 | * the License, or (at your option) any later version. |
paul@160 | 12 | * |
paul@160 | 13 | * This program is distributed in the hope that it will be useful, |
paul@160 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@160 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@160 | 16 | * GNU General Public License for more details. |
paul@160 | 17 | * |
paul@160 | 18 | * You should have received a copy of the GNU General Public License |
paul@160 | 19 | * along with this program; if not, write to the Free Software |
paul@160 | 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@160 | 21 | * Boston, MA 02110-1301, USA |
paul@160 | 22 | */ |
paul@160 | 23 | |
paul@160 | 24 | #include <l4/devices/hw_mmio_register_block.h> |
paul@160 | 25 | #include "cpm-x1600.h" |
paul@160 | 26 | #include <math.h> |
paul@161 | 27 | #include <stdio.h> |
paul@160 | 28 | |
paul@160 | 29 | |
paul@160 | 30 | |
paul@161 | 31 | // Register locations. |
paul@161 | 32 | |
paul@160 | 33 | enum Regs : unsigned |
paul@160 | 34 | { |
paul@160 | 35 | Clock_control = 0x000, // CPCCR |
paul@160 | 36 | Low_power_control = 0x004, // LCR |
paul@160 | 37 | Clock_gate0 = 0x020, // CLKGR0 |
paul@160 | 38 | Clock_gate1 = 0x028, // CLKGR1 |
paul@160 | 39 | Sleep_control = 0x024, // OPCR (oscillator and power control) |
paul@160 | 40 | Clock_status = 0x0d4, // CPCSR |
paul@167 | 41 | Divider_ddr = 0x02c, // DDRCDR |
paul@167 | 42 | Divider_mac = 0x054, // MACCDR |
paul@167 | 43 | Divider0_i2s0 = 0x060, // I2SCDR |
paul@167 | 44 | Divider1_i2s0 = 0x070, // I2S1CDR |
paul@167 | 45 | Divider_lcd = 0x064, // LPCDR |
paul@167 | 46 | Divider_msc0 = 0x068, // MSC0CDR |
paul@167 | 47 | Divider_msc1 = 0x0a4, // MSC1CDR |
paul@167 | 48 | Divider_sfc = 0x074, // SFCCDR |
paul@167 | 49 | Divider_ssi = 0x05c, // SSICDR |
paul@167 | 50 | Divider_cim = 0x078, // CIMCDR |
paul@167 | 51 | Divider_pwm = 0x06c, // PWMCDR |
paul@167 | 52 | Divider_can0 = 0x0a0, // CAN0CDR |
paul@167 | 53 | Divider_can1 = 0x0a8, // CAN1CDR |
paul@167 | 54 | Divider_cdbus = 0x0ac, // CDBUSCDR |
paul@167 | 55 | Divider_macphy0 = 0x0e4, // MPHY0C |
paul@160 | 56 | Cpm_interrupt = 0x0b0, // CPM_INTR |
paul@160 | 57 | Cpm_interrupt_en = 0x0b4, // CPM_INTRE |
paul@160 | 58 | Cpm_swi = 0x0bc, // CPM_SFTINT |
paul@160 | 59 | Ddr_gate = 0x0d0, // DRCG |
paul@160 | 60 | Cpm_scratch_prot = 0x038, // CPSPPR |
paul@160 | 61 | Cpm_scratch = 0x034, // CPSPR |
paul@160 | 62 | Usb_param_control0 = 0x03c, // USBPCR |
paul@160 | 63 | Usb_reset_detect = 0x040, // USBRDT |
paul@160 | 64 | Usb_vbus_jitter = 0x044, // USBVBFIL |
paul@160 | 65 | Usb_param_control1 = 0x048, // USBPCR1 |
paul@160 | 66 | Pll_control = 0x00c, // CPPCR |
paul@160 | 67 | Pll_control_A = 0x010, // CPAPCR |
paul@160 | 68 | Pll_control_M = 0x014, // CPMPCR |
paul@160 | 69 | Pll_control_E = 0x018, // CPEPCR |
paul@160 | 70 | Pll_fraction_A = 0x084, // CPAPACR |
paul@160 | 71 | Pll_fraction_M = 0x088, // CPMPACR |
paul@160 | 72 | Pll_fraction_E = 0x08c, // CPEPACR |
paul@160 | 73 | }; |
paul@160 | 74 | |
paul@161 | 75 | enum Clock_source_values : unsigned |
paul@160 | 76 | { |
paul@161 | 77 | Source_mME_main = 0, |
paul@161 | 78 | Source_mME_pll_M = 1, |
paul@161 | 79 | Source_mME_pll_E = 2, |
paul@160 | 80 | |
paul@161 | 81 | // Special value |
paul@160 | 82 | |
paul@161 | 83 | Source_mask = 0x3, |
paul@160 | 84 | }; |
paul@160 | 85 | |
paul@160 | 86 | |
paul@160 | 87 | |
paul@167 | 88 | // Register field definitions. |
paul@167 | 89 | |
paul@168 | 90 | Field Clock_source_main (Clock_control, 3, 30); // SEL_SRC (output to SCLK_A) |
paul@168 | 91 | Field Clock_source_cpu (Clock_control, 3, 28); // SEL_CPLL (output to CCLK) |
paul@168 | 92 | Field Clock_source_hclock0 (Clock_control, 3, 26); // SEL_H0PLL (output to AHB0) |
paul@168 | 93 | Field Clock_source_hclock2 (Clock_control, 3, 24); // SEL_H2PLL (output to AHB2) |
paul@168 | 94 | Field Clock_source_can0 (Divider_can0, 3, 30); // CA0CS |
paul@168 | 95 | Field Clock_source_can1 (Divider_can1, 3, 30); // CA1CS |
paul@168 | 96 | Field Clock_source_cdbus (Divider_cdbus, 3, 30); // CDCS |
paul@168 | 97 | Field Clock_source_cim (Divider_cim, 3, 30); // CIMPCS |
paul@168 | 98 | Field Clock_source_ddr (Divider_ddr, 3, 30); // DCS |
paul@168 | 99 | Field Clock_source_i2s (Divider0_i2s0, 1, 31); // I2PCS |
paul@168 | 100 | Field Clock_source_lcd (Divider_lcd, 3, 30); // LPCS |
paul@168 | 101 | Field Clock_source_mac (Divider_mac, 3, 30); // MACPCS |
paul@168 | 102 | Field Clock_source_msc0 (Divider_msc0, 3, 30); // MPCS |
paul@168 | 103 | Field Clock_source_msc1 (Divider_msc1, 3, 30); // MPCS |
paul@168 | 104 | Field Clock_source_pwm (Divider_pwm, 3, 30); // PWMPCS |
paul@168 | 105 | Field Clock_source_sfc (Divider_sfc, 3, 30); // SFCS |
paul@168 | 106 | Field Clock_source_ssi (Divider_ssi, 3, 30); // SPCS |
paul@167 | 107 | |
paul@167 | 108 | Field Clock_busy_cpu (Clock_status, 1, 0); |
paul@167 | 109 | Field Clock_busy_ddr (Divider_ddr, 1, 28); |
paul@167 | 110 | Field Clock_busy_mac (Divider_mac, 1, 28); |
paul@167 | 111 | Field Clock_busy_lcd (Divider_lcd, 1, 28); |
paul@167 | 112 | Field Clock_busy_msc0 (Divider_msc0, 1, 28); |
paul@167 | 113 | Field Clock_busy_msc1 (Divider_msc1, 1, 28); |
paul@167 | 114 | Field Clock_busy_sfc (Divider_sfc, 1, 28); |
paul@167 | 115 | Field Clock_busy_ssi (Divider_ssi, 1, 28); |
paul@167 | 116 | Field Clock_busy_cim (Divider_cim, 1, 28); |
paul@167 | 117 | Field Clock_busy_pwm (Divider_pwm, 1, 28); |
paul@167 | 118 | Field Clock_busy_can0 (Divider_can0, 1, 28); |
paul@167 | 119 | Field Clock_busy_can1 (Divider_can1, 1, 28); |
paul@167 | 120 | Field Clock_busy_cdbus (Divider_cdbus, 1, 28); |
paul@167 | 121 | |
paul@167 | 122 | Field Clock_change_enable_cpu (Clock_control, 1, 22); |
paul@167 | 123 | Field Clock_change_enable_ahb0 (Clock_control, 1, 21); |
paul@167 | 124 | Field Clock_change_enable_ahb2 (Clock_control, 1, 20); |
paul@167 | 125 | Field Clock_change_enable_ddr (Divider_ddr, 1, 29); |
paul@167 | 126 | Field Clock_change_enable_mac (Divider_mac, 1, 29); |
paul@167 | 127 | Field Clock_change_enable_i2s (Divider0_i2s0, 1, 29); |
paul@167 | 128 | Field Clock_change_enable_lcd (Divider_lcd, 1, 29); |
paul@167 | 129 | Field Clock_change_enable_msc0 (Divider_msc0, 1, 29); |
paul@167 | 130 | Field Clock_change_enable_msc1 (Divider_msc1, 1, 29); |
paul@167 | 131 | Field Clock_change_enable_sfc (Divider_sfc, 1, 29); |
paul@167 | 132 | Field Clock_change_enable_ssi (Divider_ssi, 1, 29); |
paul@167 | 133 | Field Clock_change_enable_cim (Divider_cim, 1, 29); |
paul@167 | 134 | Field Clock_change_enable_pwm (Divider_pwm, 1, 29); |
paul@167 | 135 | Field Clock_change_enable_can0 (Divider_can0, 1, 29); |
paul@167 | 136 | Field Clock_change_enable_can1 (Divider_can1, 1, 29); |
paul@167 | 137 | Field Clock_change_enable_cdbus (Divider_cdbus, 1, 29); |
paul@167 | 138 | |
paul@168 | 139 | Field Clock_divider_can0 (Divider_can0, 0xff, 0); // CAN0CDR |
paul@168 | 140 | Field Clock_divider_can1 (Divider_can1, 0xff, 0); // CAN1CDR |
paul@168 | 141 | Field Clock_divider_cdbus (Divider_cdbus, 0xff, 0); // CDBUSCDR |
paul@168 | 142 | Field Clock_divider_cim (Divider_cim, 0xff, 0); // CIMCDR |
paul@168 | 143 | Field Clock_divider_cpu (Clock_control, 0x0f, 0); // CDIV |
paul@168 | 144 | Field Clock_divider_ddr (Divider_ddr, 0x0f, 0); // DDRCDR |
paul@168 | 145 | Field Clock_divider_hclock0 (Clock_control, 0x0f, 8); // H0DIV (fast AHB peripherals) |
paul@168 | 146 | Field Clock_divider_hclock2 (Clock_control, 0x0f, 12); // H2DIV (fast AHB peripherals) |
paul@168 | 147 | Field Clock_divider_l2cache (Clock_control, 0x0f, 4); // L2CDIV |
paul@168 | 148 | Field Clock_divider_lcd (Divider_lcd, 0xff, 0); // LPCDR |
paul@168 | 149 | Field Clock_divider_mac (Divider_mac, 0xff, 0); // MACCDR |
paul@168 | 150 | Field Clock_divider_msc0 (Divider_msc0, 0xff, 0); // MSC0CDR |
paul@168 | 151 | Field Clock_divider_msc1 (Divider_msc1, 0xff, 0); // MSC1CDR |
paul@168 | 152 | Field Clock_divider_pclock (Clock_control, 0x0f, 16); // PDIV (slow APB peripherals) |
paul@168 | 153 | Field Clock_divider_pwm (Divider_pwm, 0x0f, 0); // PWMCDR |
paul@168 | 154 | Field Clock_divider_sfc (Divider_sfc, 0xff, 0); // SFCCDR |
paul@168 | 155 | Field Clock_divider_ssi (Divider_ssi, 0xff, 0); // SSICDR |
paul@167 | 156 | |
paul@168 | 157 | Field Clock_gate_main (Clock_control, 1, 23); // GATE_SCLKA |
paul@168 | 158 | Field Clock_gate_ddr (Clock_gate0, 1, 31); // DDR |
paul@168 | 159 | Field Clock_gate_ahb0 (Clock_gate0, 1, 29); // AHB0 |
paul@168 | 160 | Field Clock_gate_apb0 (Clock_gate0, 1, 28); // APB0 |
paul@168 | 161 | Field Clock_gate_rtc (Clock_gate0, 1, 27); // RTC |
paul@168 | 162 | Field Clock_gate_aes (Clock_gate0, 1, 24); // AES |
paul@168 | 163 | Field Clock_gate_lcd_pixel (Clock_gate0, 1, 23); // LCD |
paul@168 | 164 | Field Clock_gate_cim (Clock_gate0, 1, 22); // CIM |
paul@168 | 165 | Field Clock_gate_dma (Clock_gate0, 1, 21); // PDMA |
paul@168 | 166 | Field Clock_gate_ost (Clock_gate0, 1, 20); // OST |
paul@168 | 167 | Field Clock_gate_ssi0 (Clock_gate0, 1, 19); // SSI0 |
paul@168 | 168 | Field Clock_gate_timer (Clock_gate0, 1, 18); // TCU |
paul@168 | 169 | Field Clock_gate_dtrng (Clock_gate0, 1, 17); // DTRNG |
paul@168 | 170 | Field Clock_gate_uart2 (Clock_gate0, 1, 16); // UART2 |
paul@168 | 171 | Field Clock_gate_uart1 (Clock_gate0, 1, 15); // UART1 |
paul@168 | 172 | Field Clock_gate_uart0 (Clock_gate0, 1, 14); // UART0 |
paul@168 | 173 | Field Clock_gate_sadc (Clock_gate0, 1, 13); // SADC |
paul@168 | 174 | Field Clock_gate_audio (Clock_gate0, 1, 11); // AUDIO |
paul@168 | 175 | Field Clock_gate_ssi_slv (Clock_gate0, 1, 10); // SSI_SLV |
paul@168 | 176 | Field Clock_gate_i2c1 (Clock_gate0, 1, 8); // I2C1 |
paul@168 | 177 | Field Clock_gate_i2c0 (Clock_gate0, 1, 7); // I2C0 |
paul@168 | 178 | Field Clock_gate_msc1 (Clock_gate0, 1, 5); // MSC1 |
paul@168 | 179 | Field Clock_gate_msc0 (Clock_gate0, 1, 4); // MSC0 |
paul@168 | 180 | Field Clock_gate_otg (Clock_gate0, 1, 3); // OTG |
paul@168 | 181 | Field Clock_gate_sfc (Clock_gate0, 1, 2); // SFC |
paul@168 | 182 | Field Clock_gate_efuse (Clock_gate0, 1, 1); // EFUSE |
paul@168 | 183 | Field Clock_gate_nemc (Clock_gate0, 1, 0); // NEMC |
paul@168 | 184 | Field Clock_gate_arb (Clock_gate1, 1, 30); // ARB |
paul@168 | 185 | Field Clock_gate_mipi_csi (Clock_gate1, 1, 28); // MIPI_CSI |
paul@168 | 186 | Field Clock_gate_intc (Clock_gate1, 1, 26); // INTC |
paul@168 | 187 | Field Clock_gate_gmac0 (Clock_gate1, 1, 23); // GMAC0 |
paul@168 | 188 | Field Clock_gate_uart3 (Clock_gate1, 1, 16); // UART3 |
paul@168 | 189 | Field Clock_gate_i2s0_tx (Clock_gate1, 1, 9); // I2S0_dev_tclk |
paul@168 | 190 | Field Clock_gate_i2s0_rx (Clock_gate1, 1, 8); // I2S0_dev_rclk |
paul@168 | 191 | Field Clock_gate_hash (Clock_gate1, 1, 6); // HASH |
paul@168 | 192 | Field Clock_gate_pwm (Clock_gate1, 1, 5); // PWM |
paul@168 | 193 | Field Clock_gate_cdbus (Clock_gate1, 1, 2); // CDBUS |
paul@168 | 194 | Field Clock_gate_can1 (Clock_gate1, 1, 1); // CAN1 |
paul@168 | 195 | Field Clock_gate_can0 (Clock_gate1, 1, 0); // CAN0 |
paul@168 | 196 | |
paul@168 | 197 | Field Pll_enable_A (Pll_control_A, 1, 0); // APLLEN |
paul@168 | 198 | Field Pll_enable_E (Pll_control_E, 1, 0); // EPLLEN |
paul@168 | 199 | Field Pll_enable_M (Pll_control_M, 1, 0); // MPLLEN |
paul@168 | 200 | |
paul@168 | 201 | Field Pll_stable_A (Pll_control_A, 1, 3); // APLL_ON |
paul@168 | 202 | Field Pll_stable_E (Pll_control_E, 1, 3); // EPLL_ON |
paul@168 | 203 | Field Pll_stable_M (Pll_control_M, 1, 3); // MPLL_ON |
paul@168 | 204 | |
paul@168 | 205 | Field Pll_bypass_A (Pll_control_A, 1, 30); // APLL_BP |
paul@168 | 206 | Field Pll_bypass_E (Pll_control_E, 1, 26); // EPLL_BP |
paul@168 | 207 | Field Pll_bypass_M (Pll_control_M, 1, 28); // MPLL_BP |
paul@168 | 208 | |
paul@168 | 209 | Field Pll_multiplier_A (Pll_control_A, 0x1fff, 20); // APLLM |
paul@168 | 210 | Field Pll_multiplier_E (Pll_control_E, 0x1fff, 20); // EPLLM |
paul@168 | 211 | Field Pll_multiplier_M (Pll_control_M, 0x1fff, 20); // MPLLM |
paul@168 | 212 | |
paul@168 | 213 | Field Pll_input_division_A (Pll_control_A, 0x3f, 14); // APLLN |
paul@168 | 214 | Field Pll_input_division_E (Pll_control_E, 0x3f, 14); // EPLLN |
paul@168 | 215 | Field Pll_input_division_M (Pll_control_M, 0x3f, 14); // MPLLN |
paul@168 | 216 | |
paul@168 | 217 | Field Pll_output_division1_A (Pll_control_A, 0x07, 11); // APLLOD1 |
paul@168 | 218 | Field Pll_output_division1_E (Pll_control_E, 0x07, 11); // EPLLOD1 |
paul@168 | 219 | Field Pll_output_division1_M (Pll_control_M, 0x07, 11); // MPLLOD1 |
paul@168 | 220 | |
paul@168 | 221 | Field Pll_output_division0_A (Pll_control_A, 0x07, 8); // APLLOD0 |
paul@168 | 222 | Field Pll_output_division0_E (Pll_control_E, 0x07, 8); // EPLLOD0 |
paul@168 | 223 | Field Pll_output_division0_M (Pll_control_M, 0x07, 8); // MPLLOD0 |
paul@167 | 224 | |
paul@167 | 225 | |
paul@167 | 226 | |
paul@169 | 227 | // Multiplexer instances. |
paul@169 | 228 | |
paul@169 | 229 | #define Clocks(...) ((enum Clock_identifiers []) {__VA_ARGS__}) |
paul@169 | 230 | |
paul@171 | 231 | Mux mux_external(Clock_external); |
paul@171 | 232 | |
paul@171 | 233 | Mux mux_pclock(Clock_pclock); |
paul@171 | 234 | |
paul@171 | 235 | Mux mux_ahb2_apb(Clock_ahb2_apb); |
paul@169 | 236 | |
paul@169 | 237 | Mux mux_core(3, Clocks(Clock_none, Clock_main, Clock_pll_M)); |
paul@169 | 238 | |
paul@169 | 239 | Mux mux_bus(4, Clocks(Clock_main, Clock_pll_M, Clock_pll_E, Clock_external)); |
paul@169 | 240 | |
paul@169 | 241 | Mux mux_dev(3, Clocks(Clock_main, Clock_pll_M, Clock_pll_E)); |
paul@169 | 242 | |
paul@169 | 243 | Mux mux_i2s(2, Clocks(Clock_main, Clock_pll_E)); |
paul@169 | 244 | |
paul@169 | 245 | |
paul@169 | 246 | |
paul@165 | 247 | // Clock instances. |
paul@165 | 248 | |
paul@171 | 249 | Clock clock_ahb2_apb(Source(mux_core, Clock_source_hclock2)); |
paul@165 | 250 | |
paul@165 | 251 | Clock clock_aic_bitclk; |
paul@165 | 252 | |
paul@165 | 253 | Clock clock_aic_pclk; |
paul@165 | 254 | |
paul@171 | 255 | Clock clock_can0(Source(mux_bus, Clock_source_can0), |
paul@167 | 256 | Clock_gate_can0, |
paul@167 | 257 | Clock_change_enable_can0, |
paul@167 | 258 | Clock_busy_can0, |
paul@167 | 259 | Clock_divider_can0); |
paul@165 | 260 | |
paul@171 | 261 | Clock clock_can1(Source(mux_bus, Clock_source_can1), |
paul@167 | 262 | Clock_gate_can1, |
paul@167 | 263 | Clock_change_enable_can1, |
paul@167 | 264 | Clock_busy_can1, |
paul@167 | 265 | Clock_divider_can1); |
paul@165 | 266 | |
paul@171 | 267 | Clock clock_cdbus(Source(mux_dev, Clock_source_cdbus), |
paul@167 | 268 | Clock_gate_cdbus, |
paul@167 | 269 | Clock_change_enable_cdbus, |
paul@167 | 270 | Clock_busy_cdbus, |
paul@167 | 271 | Clock_divider_cdbus); |
paul@165 | 272 | |
paul@171 | 273 | Clock clock_cim(Source(mux_dev, Clock_source_cim), |
paul@167 | 274 | Clock_gate_cim, |
paul@167 | 275 | Clock_change_enable_cim, |
paul@167 | 276 | Clock_busy_cim, |
paul@167 | 277 | Clock_divider_cim); |
paul@165 | 278 | |
paul@171 | 279 | Clock clock_cpu(Source(mux_core, Clock_source_cpu), |
paul@173 | 280 | Field::undefined, |
paul@167 | 281 | Clock_change_enable_cpu, |
paul@167 | 282 | Clock_busy_cpu, |
paul@167 | 283 | Clock_divider_cpu); |
paul@165 | 284 | |
paul@171 | 285 | Clock clock_ddr(Source(mux_core, Clock_source_ddr), |
paul@167 | 286 | Clock_gate_ddr, |
paul@167 | 287 | Clock_change_enable_ddr, |
paul@167 | 288 | Clock_busy_ddr, |
paul@167 | 289 | Clock_divider_ddr); |
paul@165 | 290 | |
paul@171 | 291 | Clock clock_dma(Source(mux_pclock), Clock_gate_dma); |
paul@165 | 292 | |
paul@165 | 293 | Clock clock_emac; |
paul@165 | 294 | |
paul@165 | 295 | Clock clock_external; |
paul@165 | 296 | |
paul@171 | 297 | Clock clock_hclock0(Source(mux_core, Clock_source_hclock0), |
paul@167 | 298 | Clock_gate_ahb0, |
paul@167 | 299 | Clock_change_enable_ahb0, |
paul@173 | 300 | Field::undefined, |
paul@167 | 301 | Clock_divider_hclock0); |
paul@165 | 302 | |
paul@171 | 303 | Clock clock_hclock2(Source(mux_ahb2_apb), |
paul@167 | 304 | Clock_gate_apb0, |
paul@167 | 305 | Clock_change_enable_ahb2, |
paul@173 | 306 | Field::undefined, |
paul@167 | 307 | Clock_divider_hclock2); |
paul@165 | 308 | |
paul@165 | 309 | Clock clock_hdmi; |
paul@165 | 310 | |
paul@171 | 311 | Clock clock_i2c(Source(mux_pclock), Clock_gate_i2c0); |
paul@165 | 312 | |
paul@171 | 313 | Clock clock_i2c0(Source(mux_pclock), Clock_gate_i2c0); |
paul@165 | 314 | |
paul@171 | 315 | Clock clock_i2c1(Source(mux_pclock), Clock_gate_i2c1); |
paul@165 | 316 | |
paul@165 | 317 | Clock clock_i2s; |
paul@165 | 318 | |
paul@171 | 319 | Clock clock_i2s0_rx(Source(mux_i2s, Clock_source_i2s), |
paul@167 | 320 | Clock_gate_i2s0_rx, |
paul@167 | 321 | Clock_change_enable_i2s); |
paul@165 | 322 | |
paul@171 | 323 | Clock clock_i2s0_tx(Source(mux_i2s, Clock_source_i2s), |
paul@167 | 324 | Clock_gate_i2s0_tx, |
paul@167 | 325 | Clock_change_enable_i2s); |
paul@165 | 326 | |
paul@165 | 327 | Clock clock_kbc; |
paul@165 | 328 | |
paul@165 | 329 | Clock clock_lcd; |
paul@165 | 330 | |
paul@171 | 331 | Clock clock_lcd_pixel(Source(mux_dev, Clock_source_lcd), |
paul@167 | 332 | Clock_gate_lcd_pixel, |
paul@167 | 333 | Clock_change_enable_lcd, |
paul@167 | 334 | Clock_busy_lcd, |
paul@167 | 335 | Clock_divider_lcd); |
paul@165 | 336 | |
paul@171 | 337 | Clock clock_mac(Source(mux_dev, Clock_source_mac), |
paul@167 | 338 | Clock_gate_gmac0, |
paul@167 | 339 | Clock_change_enable_mac, |
paul@167 | 340 | Clock_busy_mac, |
paul@167 | 341 | Clock_divider_mac); |
paul@165 | 342 | |
paul@171 | 343 | Clock clock_main(Source(mux_core, Clock_source_main), |
paul@167 | 344 | Clock_gate_main); |
paul@165 | 345 | |
paul@171 | 346 | Clock clock_msc(Source(mux_dev, Clock_source_msc0), |
paul@167 | 347 | Clock_gate_msc0, |
paul@167 | 348 | Clock_change_enable_msc0, |
paul@167 | 349 | Clock_busy_msc0, |
paul@167 | 350 | Clock_divider_msc0); |
paul@165 | 351 | |
paul@171 | 352 | Clock clock_msc0(Source(mux_dev, Clock_source_msc0), |
paul@167 | 353 | Clock_gate_msc0, |
paul@167 | 354 | Clock_change_enable_msc0, |
paul@167 | 355 | Clock_busy_msc0, |
paul@167 | 356 | Clock_divider_msc0); |
paul@165 | 357 | |
paul@171 | 358 | Clock clock_msc1(Source(mux_dev, Clock_source_msc1), |
paul@167 | 359 | Clock_gate_msc1, |
paul@167 | 360 | Clock_change_enable_msc1, |
paul@167 | 361 | Clock_busy_msc1, |
paul@167 | 362 | Clock_divider_msc1); |
paul@165 | 363 | |
paul@165 | 364 | Clock clock_none; |
paul@161 | 365 | |
paul@171 | 366 | Clock clock_pclock(Source(mux_ahb2_apb), |
paul@167 | 367 | Clock_gate_apb0, |
paul@173 | 368 | Field::undefined, |
paul@173 | 369 | Field::undefined, |
paul@167 | 370 | Clock_divider_pclock); |
paul@165 | 371 | |
paul@171 | 372 | Pll clock_pll_A(Source(mux_external), |
paul@168 | 373 | Pll_enable_A, Pll_stable_A, Pll_bypass_A, |
paul@168 | 374 | Pll_multiplier_A, Pll_input_division_A, |
paul@168 | 375 | Pll_output_division0_A, Pll_output_division1_A); |
paul@165 | 376 | |
paul@171 | 377 | Pll clock_pll_E(Source(mux_external), |
paul@168 | 378 | Pll_enable_E, Pll_stable_E, Pll_bypass_E, |
paul@168 | 379 | Pll_multiplier_E, Pll_input_division_E, |
paul@168 | 380 | Pll_output_division0_E, Pll_output_division1_E); |
paul@165 | 381 | |
paul@171 | 382 | Pll clock_pll_M(Source(mux_external), |
paul@168 | 383 | Pll_enable_M, Pll_stable_M, Pll_bypass_M, |
paul@168 | 384 | Pll_multiplier_M, Pll_input_division_M, |
paul@168 | 385 | Pll_output_division0_M, Pll_output_division1_M); |
paul@165 | 386 | |
paul@171 | 387 | Clock clock_pwm(Source(mux_dev, Clock_source_pwm), |
paul@167 | 388 | Clock_gate_pwm, |
paul@167 | 389 | Clock_change_enable_pwm, |
paul@167 | 390 | Clock_busy_pwm, |
paul@167 | 391 | Clock_divider_pwm); |
paul@165 | 392 | |
paul@171 | 393 | Clock clock_pwm0(Source(mux_dev, Clock_source_pwm), |
paul@167 | 394 | Clock_gate_pwm, |
paul@167 | 395 | Clock_change_enable_pwm, |
paul@167 | 396 | Clock_busy_pwm, |
paul@167 | 397 | Clock_divider_pwm); |
paul@165 | 398 | |
paul@165 | 399 | Clock clock_pwm1; |
paul@165 | 400 | |
paul@165 | 401 | Clock clock_scc; |
paul@165 | 402 | |
paul@171 | 403 | Clock clock_sfc(Source(mux_dev, Clock_source_sfc), |
paul@167 | 404 | Clock_gate_sfc, |
paul@167 | 405 | Clock_change_enable_sfc, |
paul@167 | 406 | Clock_busy_sfc, |
paul@167 | 407 | Clock_divider_sfc); |
paul@165 | 408 | |
paul@165 | 409 | Clock clock_smb0; |
paul@165 | 410 | |
paul@165 | 411 | Clock clock_smb1; |
paul@165 | 412 | |
paul@165 | 413 | Clock clock_smb2; |
paul@165 | 414 | |
paul@165 | 415 | Clock clock_smb3; |
paul@165 | 416 | |
paul@165 | 417 | Clock clock_smb4; |
paul@165 | 418 | |
paul@171 | 419 | Clock clock_ssi(Source(mux_dev, Clock_source_ssi), |
paul@167 | 420 | Clock_gate_ssi0, |
paul@167 | 421 | Clock_change_enable_ssi, |
paul@167 | 422 | Clock_busy_ssi, |
paul@167 | 423 | Clock_divider_ssi); |
paul@165 | 424 | |
paul@171 | 425 | Clock clock_timer(Source(mux_pclock), Clock_gate_timer); |
paul@165 | 426 | |
paul@171 | 427 | Clock clock_uart0(Source(mux_external), Clock_gate_uart0); |
paul@165 | 428 | |
paul@171 | 429 | Clock clock_uart1(Source(mux_external), Clock_gate_uart1); |
paul@165 | 430 | |
paul@171 | 431 | Clock clock_uart2(Source(mux_external), Clock_gate_uart2); |
paul@165 | 432 | |
paul@171 | 433 | Clock clock_uart3(Source(mux_external), Clock_gate_uart3); |
paul@165 | 434 | |
paul@165 | 435 | Clock clock_udc; |
paul@165 | 436 | |
paul@165 | 437 | Clock clock_uhc; |
paul@165 | 438 | |
paul@165 | 439 | Clock clock_uprt; |
paul@165 | 440 | |
paul@165 | 441 | |
paul@165 | 442 | |
paul@165 | 443 | // Clock register. |
paul@165 | 444 | |
paul@165 | 445 | static Clock_base *clocks[Clock_identifier_count] = { |
paul@165 | 446 | &clock_ahb2_apb, |
paul@165 | 447 | &clock_aic_bitclk, |
paul@165 | 448 | &clock_aic_pclk, |
paul@165 | 449 | &clock_can0, |
paul@165 | 450 | &clock_can1, |
paul@165 | 451 | &clock_cdbus, |
paul@165 | 452 | &clock_cim, |
paul@165 | 453 | &clock_cpu, |
paul@165 | 454 | &clock_ddr, |
paul@165 | 455 | &clock_dma, |
paul@165 | 456 | &clock_emac, |
paul@165 | 457 | &clock_external, |
paul@165 | 458 | &clock_hclock0, |
paul@165 | 459 | &clock_hclock2, |
paul@165 | 460 | &clock_hdmi, |
paul@165 | 461 | &clock_i2c, |
paul@165 | 462 | &clock_i2c0, |
paul@165 | 463 | &clock_i2c1, |
paul@165 | 464 | &clock_i2s, |
paul@165 | 465 | &clock_i2s0_rx, |
paul@165 | 466 | &clock_i2s0_tx, |
paul@165 | 467 | &clock_kbc, |
paul@165 | 468 | &clock_lcd, |
paul@165 | 469 | &clock_lcd_pixel, |
paul@165 | 470 | &clock_mac, |
paul@165 | 471 | &clock_main, |
paul@165 | 472 | &clock_msc, |
paul@165 | 473 | &clock_msc0, |
paul@165 | 474 | &clock_msc1, |
paul@165 | 475 | &clock_none, |
paul@165 | 476 | &clock_pclock, |
paul@165 | 477 | &clock_pll_A, |
paul@165 | 478 | &clock_pll_E, |
paul@165 | 479 | &clock_pll_M, |
paul@165 | 480 | &clock_pwm, |
paul@165 | 481 | &clock_pwm0, |
paul@165 | 482 | &clock_pwm1, |
paul@165 | 483 | &clock_scc, |
paul@165 | 484 | &clock_sfc, |
paul@165 | 485 | &clock_smb0, |
paul@165 | 486 | &clock_smb1, |
paul@165 | 487 | &clock_smb2, |
paul@165 | 488 | &clock_smb3, |
paul@165 | 489 | &clock_smb4, |
paul@165 | 490 | &clock_ssi, |
paul@165 | 491 | &clock_timer, |
paul@165 | 492 | &clock_uart0, |
paul@165 | 493 | &clock_uart1, |
paul@165 | 494 | &clock_uart2, |
paul@165 | 495 | &clock_uart3, |
paul@165 | 496 | &clock_udc, |
paul@165 | 497 | &clock_uhc, |
paul@165 | 498 | &clock_uprt, |
paul@165 | 499 | }; |
paul@165 | 500 | |
paul@165 | 501 | |
paul@165 | 502 | |
paul@160 | 503 | // If implemented as a Hw::Device, various properties would be |
paul@160 | 504 | // initialised in the constructor and obtained from the device tree |
paul@160 | 505 | // definitions. |
paul@160 | 506 | |
paul@160 | 507 | Cpm_x1600_chip::Cpm_x1600_chip(l4_addr_t addr, uint32_t exclk_freq) |
paul@173 | 508 | : _cpm_regs(addr, clocks, exclk_freq) |
paul@160 | 509 | { |
paul@160 | 510 | // add_cid("cpm"); |
paul@160 | 511 | // add_cid("cpm-x1600"); |
paul@165 | 512 | // register_property("exclk_freq", &exclk_freq); |
paul@161 | 513 | } |
paul@160 | 514 | |
paul@161 | 515 | int |
paul@161 | 516 | Cpm_x1600_chip::have_clock(enum Clock_identifiers clock) |
paul@161 | 517 | { |
paul@165 | 518 | return clocks[clock]->have_clock(_cpm_regs); |
paul@161 | 519 | } |
paul@161 | 520 | |
paul@161 | 521 | void |
paul@161 | 522 | Cpm_x1600_chip::start_clock(enum Clock_identifiers clock) |
paul@160 | 523 | { |
paul@165 | 524 | clocks[clock]->start_clock(_cpm_regs); |
paul@161 | 525 | } |
paul@161 | 526 | |
paul@161 | 527 | void |
paul@161 | 528 | Cpm_x1600_chip::stop_clock(enum Clock_identifiers clock) |
paul@161 | 529 | { |
paul@165 | 530 | clocks[clock]->stop_clock(_cpm_regs); |
paul@160 | 531 | } |
paul@160 | 532 | |
paul@161 | 533 | uint32_t |
paul@161 | 534 | Cpm_x1600_chip::get_divider(enum Clock_identifiers clock) |
paul@160 | 535 | { |
paul@165 | 536 | return clocks[clock]->get_divider(_cpm_regs); |
paul@160 | 537 | } |
paul@160 | 538 | |
paul@161 | 539 | void |
paul@161 | 540 | Cpm_x1600_chip::set_divider(enum Clock_identifiers clock, uint32_t division) |
paul@160 | 541 | { |
paul@165 | 542 | clocks[clock]->set_divider(_cpm_regs, division); |
paul@160 | 543 | } |
paul@160 | 544 | |
paul@160 | 545 | uint8_t |
paul@161 | 546 | Cpm_x1600_chip::get_source(enum Clock_identifiers clock) |
paul@160 | 547 | { |
paul@165 | 548 | return clocks[clock]->get_source(_cpm_regs); |
paul@160 | 549 | } |
paul@160 | 550 | |
paul@160 | 551 | void |
paul@161 | 552 | Cpm_x1600_chip::set_source(enum Clock_identifiers clock, uint8_t source) |
paul@160 | 553 | { |
paul@165 | 554 | clocks[clock]->set_source(_cpm_regs, source); |
paul@160 | 555 | } |
paul@160 | 556 | |
paul@161 | 557 | uint32_t |
paul@161 | 558 | Cpm_x1600_chip::get_source_frequency(enum Clock_identifiers clock) |
paul@160 | 559 | { |
paul@165 | 560 | return clocks[clock]->get_source_frequency(_cpm_regs); |
paul@160 | 561 | } |
paul@160 | 562 | |
paul@160 | 563 | uint32_t |
paul@160 | 564 | Cpm_x1600_chip::get_frequency(enum Clock_identifiers clock) |
paul@160 | 565 | { |
paul@165 | 566 | return clocks[clock]->get_frequency(_cpm_regs); |
paul@160 | 567 | } |
paul@160 | 568 | |
paul@160 | 569 | void |
paul@160 | 570 | Cpm_x1600_chip::set_frequency(enum Clock_identifiers clock, uint32_t frequency) |
paul@160 | 571 | { |
paul@160 | 572 | switch (clock) |
paul@160 | 573 | { |
paul@160 | 574 | // The pixel frequency is based on the selected clock source (SCLK_A, MPLL or |
paul@160 | 575 | // EPLL). |
paul@160 | 576 | |
paul@160 | 577 | case Clock_lcd_pixel: |
paul@165 | 578 | { |
paul@160 | 579 | |
paul@160 | 580 | // Switch to the MPLL and attempt to set the divider. |
paul@160 | 581 | |
paul@165 | 582 | Clock_base *lcd = clocks[Clock_lcd_pixel]; |
paul@165 | 583 | Clock_base *pll = clocks[Clock_pll_M]; |
paul@165 | 584 | |
paul@165 | 585 | lcd->set_source(_cpm_regs, Source_mME_pll_M); |
paul@165 | 586 | pll->start_clock(_cpm_regs); |
paul@165 | 587 | lcd->set_divider(_cpm_regs, lcd->get_source_frequency(_cpm_regs) / frequency); |
paul@160 | 588 | break; |
paul@165 | 589 | } |
paul@160 | 590 | |
paul@160 | 591 | default: |
paul@160 | 592 | break; |
paul@160 | 593 | } |
paul@160 | 594 | } |
paul@160 | 595 | |
paul@165 | 596 | void |
paul@165 | 597 | Cpm_x1600_chip::set_pll_parameters(enum Clock_identifiers clock, uint16_t multiplier, |
paul@165 | 598 | uint8_t in_divider, uint8_t out_divider) |
paul@165 | 599 | { |
paul@165 | 600 | Pll *pll = dynamic_cast<Pll *>(clocks[clock]); |
paul@165 | 601 | |
paul@165 | 602 | pll->set_pll_parameters(_cpm_regs, multiplier, in_divider, out_divider); |
paul@165 | 603 | } |
paul@165 | 604 | |
paul@160 | 605 | |
paul@160 | 606 | |
paul@160 | 607 | // C language interface functions. |
paul@160 | 608 | |
paul@160 | 609 | void |
paul@160 | 610 | *x1600_cpm_init(l4_addr_t cpm_base) |
paul@160 | 611 | { |
paul@160 | 612 | /* Initialise the clock and power management peripheral with the |
paul@160 | 613 | register memory region and a 24MHz EXCLK frequency. */ |
paul@160 | 614 | |
paul@160 | 615 | return (void *) new Cpm_x1600_chip(cpm_base, 24000000); |
paul@160 | 616 | } |
paul@160 | 617 | |
paul@160 | 618 | int |
paul@160 | 619 | x1600_cpm_have_clock(void *cpm, enum Clock_identifiers clock) |
paul@160 | 620 | { |
paul@160 | 621 | return static_cast<Cpm_x1600_chip *>(cpm)->have_clock(clock); |
paul@160 | 622 | } |
paul@160 | 623 | |
paul@160 | 624 | void |
paul@160 | 625 | x1600_cpm_start_clock(void *cpm, enum Clock_identifiers clock) |
paul@160 | 626 | { |
paul@160 | 627 | static_cast<Cpm_x1600_chip *>(cpm)->start_clock(clock); |
paul@160 | 628 | } |
paul@160 | 629 | |
paul@160 | 630 | void |
paul@160 | 631 | x1600_cpm_stop_clock(void *cpm, enum Clock_identifiers clock) |
paul@160 | 632 | { |
paul@160 | 633 | static_cast<Cpm_x1600_chip *>(cpm)->stop_clock(clock); |
paul@160 | 634 | } |
paul@160 | 635 | |
paul@161 | 636 | uint32_t |
paul@161 | 637 | x1600_cpm_get_divider(void *cpm, enum Clock_identifiers clock) |
paul@160 | 638 | { |
paul@161 | 639 | return static_cast<Cpm_x1600_chip *>(cpm)->get_divider(clock); |
paul@160 | 640 | } |
paul@160 | 641 | |
paul@161 | 642 | void |
paul@161 | 643 | x1600_cpm_set_divider(void *cpm, enum Clock_identifiers clock, uint32_t divider) |
paul@160 | 644 | { |
paul@161 | 645 | return static_cast<Cpm_x1600_chip *>(cpm)->set_divider(clock, divider); |
paul@160 | 646 | } |
paul@160 | 647 | |
paul@160 | 648 | uint8_t |
paul@161 | 649 | x1600_cpm_get_source(void *cpm, enum Clock_identifiers clock) |
paul@160 | 650 | { |
paul@161 | 651 | return static_cast<Cpm_x1600_chip *>(cpm)->get_source(clock); |
paul@160 | 652 | } |
paul@160 | 653 | |
paul@160 | 654 | void |
paul@161 | 655 | x1600_cpm_set_source(void *cpm, enum Clock_identifiers clock, uint8_t source) |
paul@160 | 656 | { |
paul@161 | 657 | static_cast<Cpm_x1600_chip *>(cpm)->set_source(clock, source); |
paul@160 | 658 | } |
paul@160 | 659 | |
paul@160 | 660 | uint32_t |
paul@161 | 661 | x1600_cpm_get_source_frequency(void *cpm, enum Clock_identifiers clock) |
paul@160 | 662 | { |
paul@161 | 663 | return static_cast<Cpm_x1600_chip *>(cpm)->get_source_frequency(clock); |
paul@160 | 664 | } |
paul@160 | 665 | |
paul@160 | 666 | uint32_t |
paul@160 | 667 | x1600_cpm_get_frequency(void *cpm, enum Clock_identifiers clock) |
paul@160 | 668 | { |
paul@160 | 669 | return static_cast<Cpm_x1600_chip *>(cpm)->get_frequency(clock); |
paul@160 | 670 | } |
paul@160 | 671 | |
paul@160 | 672 | void |
paul@160 | 673 | x1600_cpm_set_frequency(void *cpm, enum Clock_identifiers clock, uint32_t frequency) |
paul@160 | 674 | { |
paul@160 | 675 | static_cast<Cpm_x1600_chip *>(cpm)->set_frequency(clock, frequency); |
paul@160 | 676 | } |
paul@160 | 677 | |
paul@160 | 678 | void |
paul@160 | 679 | x1600_cpm_set_mpll_parameters(void *cpm, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider) |
paul@160 | 680 | { |
paul@165 | 681 | static_cast<Cpm_x1600_chip *>(cpm)->set_pll_parameters(Clock_pll_M, multiplier, in_divider, out_divider); |
paul@160 | 682 | } |