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@160 | 41 | Ddr_divider = 0x02c, // DDRCDR |
paul@160 | 42 | Mac_divider = 0x054, // MACCDR |
paul@160 | 43 | I2s_divider0 = 0x060, // I2SCDR |
paul@160 | 44 | I2s_divider1 = 0x070, // I2S1CDR |
paul@160 | 45 | Lcd_divider = 0x064, // LPCDR |
paul@160 | 46 | Msc_divider0 = 0x068, // MSC0CDR |
paul@160 | 47 | Msc_divider1 = 0x0a4, // MSC1CDR |
paul@160 | 48 | Sfc_divider = 0x074, // SFCCDR |
paul@160 | 49 | Ssi_divider = 0x05c, // SSICDR |
paul@160 | 50 | Cim_divider = 0x078, // CIMCDR |
paul@160 | 51 | Pwm_divider = 0x06c, // PWMCDR |
paul@160 | 52 | Can_divider0 = 0x0a0, // CAN0CDR |
paul@160 | 53 | Can_divider1 = 0x0a8, // CAN1CDR |
paul@160 | 54 | Cdbus_divider = 0x0ac, // CDBUSCDR |
paul@160 | 55 | Macphy0_divider = 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@160 | 75 | enum Clock_source_bits : unsigned |
paul@160 | 76 | { |
paul@160 | 77 | // Clock_control |
paul@160 | 78 | |
paul@160 | 79 | Clock_source_main = 30, // SEL_SRC (output to SCLK_A) |
paul@160 | 80 | Clock_source_cpu = 28, // SEL_CPLL (output to CCLK) |
paul@160 | 81 | Clock_source_hclock0 = 26, // SEL_H0PLL (output to AHB0) |
paul@160 | 82 | Clock_source_hclock2 = 24, // SEL_H2PLL (output to AHB2) |
paul@160 | 83 | |
paul@161 | 84 | // Divider registers |
paul@160 | 85 | |
paul@160 | 86 | Clock_source_can0 = 30, // CA0CS |
paul@160 | 87 | Clock_source_can1 = 30, // CA1CS |
paul@161 | 88 | Clock_source_cdbus = 30, // CDCS |
paul@161 | 89 | Clock_source_cim = 30, // CIMPCS |
paul@161 | 90 | Clock_source_ddr = 30, // DCS |
paul@161 | 91 | Clock_source_i2s = 31, // I2PCS |
paul@161 | 92 | Clock_source_lcd = 30, // LPCS |
paul@161 | 93 | Clock_source_mac = 30, // MACPCS |
paul@161 | 94 | Clock_source_msc0 = 30, // MPCS |
paul@161 | 95 | Clock_source_msc1 = 30, // MPCS |
paul@161 | 96 | Clock_source_pwm = 30, // PWMPCS |
paul@161 | 97 | Clock_source_sfc = 30, // SFCS |
paul@161 | 98 | Clock_source_ssi = 30, // SPCS |
paul@160 | 99 | }; |
paul@160 | 100 | |
paul@161 | 101 | enum Clock_source_values : unsigned |
paul@160 | 102 | { |
paul@161 | 103 | Source_mME_main = 0, |
paul@161 | 104 | Source_mME_pll_M = 1, |
paul@161 | 105 | Source_mME_pll_E = 2, |
paul@160 | 106 | |
paul@161 | 107 | // Special value |
paul@160 | 108 | |
paul@161 | 109 | Source_mask = 0x3, |
paul@160 | 110 | }; |
paul@160 | 111 | |
paul@160 | 112 | enum Clock_gate_bits : unsigned |
paul@160 | 113 | { |
paul@161 | 114 | // Clock_control |
paul@161 | 115 | |
paul@161 | 116 | Clock_gate_main = 23, // GATE_SCLKA |
paul@161 | 117 | |
paul@160 | 118 | // Clock_gate0 |
paul@160 | 119 | |
paul@160 | 120 | Clock_gate_ddr = 31, // DDR |
paul@160 | 121 | Clock_gate_ahb0 = 29, // AHB0 |
paul@160 | 122 | Clock_gate_apb0 = 28, // APB0 |
paul@160 | 123 | Clock_gate_rtc = 27, // RTC |
paul@160 | 124 | Clock_gate_aes = 24, // AES |
paul@160 | 125 | Clock_gate_lcd_pixel = 23, // LCD |
paul@160 | 126 | Clock_gate_cim = 22, // CIM |
paul@160 | 127 | Clock_gate_dma = 21, // PDMA |
paul@160 | 128 | Clock_gate_ost = 20, // OST |
paul@160 | 129 | Clock_gate_ssi0 = 19, // SSI0 |
paul@160 | 130 | Clock_gate_timer = 18, // TCU |
paul@160 | 131 | Clock_gate_dtrng = 17, // DTRNG |
paul@160 | 132 | Clock_gate_uart2 = 16, // UART2 |
paul@160 | 133 | Clock_gate_uart1 = 15, // UART1 |
paul@160 | 134 | Clock_gate_uart0 = 14, // UART0 |
paul@160 | 135 | Clock_gate_sadc = 13, // SADC |
paul@160 | 136 | Clock_gate_audio = 11, // AUDIO |
paul@160 | 137 | Clock_gate_ssi_slv = 10, // SSI_SLV |
paul@160 | 138 | Clock_gate_i2c1 = 8, // I2C1 |
paul@160 | 139 | Clock_gate_i2c0 = 7, // I2C0 |
paul@160 | 140 | Clock_gate_msc1 = 5, // MSC1 |
paul@160 | 141 | Clock_gate_msc0 = 4, // MSC0 |
paul@160 | 142 | Clock_gate_otg = 3, // OTG |
paul@160 | 143 | Clock_gate_sfc = 2, // SFC |
paul@160 | 144 | Clock_gate_efuse = 1, // EFUSE |
paul@160 | 145 | Clock_gate_nemc = 0, // NEMC |
paul@160 | 146 | |
paul@160 | 147 | // Clock_gate1 |
paul@160 | 148 | |
paul@160 | 149 | Clock_gate_arb = 30, // ARB |
paul@160 | 150 | Clock_gate_mipi_csi = 28, // MIPI_CSI |
paul@160 | 151 | Clock_gate_intc = 26, // INTC |
paul@160 | 152 | Clock_gate_gmac0 = 23, // GMAC0 |
paul@160 | 153 | Clock_gate_uart3 = 16, // UART3 |
paul@160 | 154 | Clock_gate_i2s0_tx = 9, // I2S0_dev_tclk |
paul@160 | 155 | Clock_gate_i2s0_rx = 8, // I2S0_dev_rclk |
paul@160 | 156 | Clock_gate_hash = 6, // HASH |
paul@160 | 157 | Clock_gate_pwm = 5, // PWM |
paul@160 | 158 | Clock_gate_cdbus = 2, // CDBUS |
paul@160 | 159 | Clock_gate_can1 = 1, // CAN1 |
paul@160 | 160 | Clock_gate_can0 = 0, // CAN0 |
paul@160 | 161 | }; |
paul@160 | 162 | |
paul@161 | 163 | enum Clock_change_enable_bits : unsigned |
paul@161 | 164 | { |
paul@161 | 165 | Clock_change_enable_cpu = 22, |
paul@161 | 166 | Clock_change_enable_ahb0 = 21, |
paul@161 | 167 | Clock_change_enable_ahb2 = 20, |
paul@161 | 168 | Clock_change_enable_ddr = 29, |
paul@161 | 169 | Clock_change_enable_mac = 29, |
paul@161 | 170 | Clock_change_enable_i2s = 29, |
paul@161 | 171 | Clock_change_enable_lcd = 29, |
paul@161 | 172 | Clock_change_enable_msc0 = 29, |
paul@161 | 173 | Clock_change_enable_msc1 = 29, |
paul@161 | 174 | Clock_change_enable_sfc = 29, |
paul@161 | 175 | Clock_change_enable_ssi = 29, |
paul@161 | 176 | Clock_change_enable_cim = 29, |
paul@161 | 177 | Clock_change_enable_pwm = 29, |
paul@161 | 178 | Clock_change_enable_can0 = 29, |
paul@161 | 179 | Clock_change_enable_can1 = 29, |
paul@161 | 180 | Clock_change_enable_cdbus = 29, |
paul@160 | 181 | }; |
paul@160 | 182 | |
paul@161 | 183 | enum Clock_busy_bits : unsigned |
paul@161 | 184 | { |
paul@161 | 185 | Clock_busy_cpu = 0, |
paul@161 | 186 | Clock_busy_ddr = 28, |
paul@161 | 187 | Clock_busy_mac = 28, |
paul@161 | 188 | Clock_busy_lcd = 28, |
paul@161 | 189 | Clock_busy_msc0 = 28, |
paul@161 | 190 | Clock_busy_msc1 = 28, |
paul@161 | 191 | Clock_busy_sfc = 28, |
paul@161 | 192 | Clock_busy_ssi = 28, |
paul@161 | 193 | Clock_busy_cim = 28, |
paul@161 | 194 | Clock_busy_pwm = 28, |
paul@161 | 195 | Clock_busy_can0 = 28, |
paul@161 | 196 | Clock_busy_can1 = 28, |
paul@161 | 197 | Clock_busy_cdbus = 28, |
paul@160 | 198 | }; |
paul@160 | 199 | |
paul@161 | 200 | enum Clock_divider_bits : unsigned |
paul@160 | 201 | { |
paul@161 | 202 | Clock_divider_can0 = 0, // CAN0CDR |
paul@161 | 203 | Clock_divider_can1 = 0, // CAN1CDR |
paul@161 | 204 | Clock_divider_cdbus = 0, // CDBUSCDR |
paul@161 | 205 | Clock_divider_cim = 0, // CIMCDR |
paul@161 | 206 | Clock_divider_cpu = 0, // CDIV |
paul@161 | 207 | Clock_divider_ddr = 0, // DDRCDR |
paul@161 | 208 | Clock_divider_hclock0 = 8, // H0DIV (fast AHB peripherals) |
paul@161 | 209 | Clock_divider_hclock2 = 12, // H2DIV (fast AHB peripherals) |
paul@161 | 210 | Clock_divider_l2cache = 4, // L2CDIV |
paul@161 | 211 | Clock_divider_lcd = 0, // LPCDR |
paul@161 | 212 | Clock_divider_mac = 0, // MACCDR |
paul@161 | 213 | Clock_divider_msc0 = 0, // MSC0CDR |
paul@161 | 214 | Clock_divider_msc1 = 0, // MSC1CDR |
paul@161 | 215 | Clock_divider_pclock = 16, // PDIV (slow APB peripherals) |
paul@161 | 216 | Clock_divider_pwm = 0, // PWMCDR |
paul@161 | 217 | Clock_divider_sfc = 0, // SFCCDR |
paul@161 | 218 | Clock_divider_ssi = 0, // SSICDR |
paul@160 | 219 | }; |
paul@160 | 220 | |
paul@160 | 221 | enum Pll_bits : unsigned |
paul@160 | 222 | { |
paul@160 | 223 | // Pll_control_A, Pll_control_M, Pll_control_E |
paul@160 | 224 | |
paul@160 | 225 | Pll_multiplier = 20, // xPLLM |
paul@160 | 226 | Pll_input_division = 14, // xPLLN |
paul@160 | 227 | Pll_output_division1 = 11, // xPLLOD1 |
paul@160 | 228 | Pll_output_division0 = 8, // xPLLOD0 |
paul@160 | 229 | Pll_stable = 3, // xPLL_ON |
paul@160 | 230 | Pll_enabled = 0, // xPLLEN |
paul@160 | 231 | }; |
paul@160 | 232 | |
paul@160 | 233 | enum Pll_bypass_bits : unsigned |
paul@160 | 234 | { |
paul@160 | 235 | Pll_bypass_A = 30, // APLL_BP |
paul@160 | 236 | Pll_bypass_M = 28, // MPLL_BP |
paul@160 | 237 | Pll_bypass_E = 26, // EPLL_BP |
paul@160 | 238 | }; |
paul@160 | 239 | |
paul@160 | 240 | |
paul@160 | 241 | |
paul@166 | 242 | // Register field abstraction. |
paul@166 | 243 | |
paul@166 | 244 | class Field |
paul@166 | 245 | { |
paul@166 | 246 | uint32_t reg; |
paul@166 | 247 | uint32_t mask; |
paul@166 | 248 | uint8_t bit; |
paul@166 | 249 | bool defined; |
paul@166 | 250 | |
paul@166 | 251 | public: |
paul@166 | 252 | explicit Field() |
paul@166 | 253 | : defined(false) |
paul@166 | 254 | { |
paul@166 | 255 | } |
paul@166 | 256 | |
paul@166 | 257 | explicit Field(uint32_t reg, uint32_t mask, uint32_t bit) |
paul@166 | 258 | : reg(reg), mask(mask), bit(bit), defined(true) |
paul@166 | 259 | { |
paul@166 | 260 | } |
paul@166 | 261 | |
paul@166 | 262 | uint32_t get_field(Cpm_regs ®s); |
paul@166 | 263 | void set_field(Cpm_regs ®s, uint32_t value); |
paul@166 | 264 | bool is_defined() { return defined; } |
paul@166 | 265 | }; |
paul@166 | 266 | |
paul@166 | 267 | // Undefined fields. |
paul@166 | 268 | |
paul@166 | 269 | Field Source_undefined, Gate_undefined, Change_enable_undefined, Busy_undefined, Divider_undefined; |
paul@166 | 270 | |
paul@166 | 271 | |
paul@166 | 272 | |
paul@165 | 273 | // Common clock abstraction. |
paul@165 | 274 | |
paul@165 | 275 | class Clock_base |
paul@165 | 276 | { |
paul@165 | 277 | protected: |
paul@165 | 278 | |
paul@165 | 279 | // Clock sources and source selection. |
paul@165 | 280 | |
paul@165 | 281 | int num_inputs; |
paul@165 | 282 | enum Clock_identifiers *inputs; |
paul@166 | 283 | Field _source; |
paul@165 | 284 | |
paul@165 | 285 | public: |
paul@165 | 286 | explicit Clock_base(int num_inputs = 0, |
paul@165 | 287 | enum Clock_identifiers inputs[] = NULL, |
paul@166 | 288 | Field source = Source_undefined) |
paul@166 | 289 | : num_inputs(num_inputs), inputs(inputs), _source(source) |
paul@165 | 290 | { |
paul@165 | 291 | } |
paul@165 | 292 | |
paul@165 | 293 | // Clock control. |
paul@165 | 294 | |
paul@165 | 295 | virtual int have_clock(Cpm_regs ®s); |
paul@165 | 296 | virtual void start_clock(Cpm_regs ®s); |
paul@165 | 297 | virtual void stop_clock(Cpm_regs ®s); |
paul@165 | 298 | |
paul@165 | 299 | // Clock divider. |
paul@165 | 300 | |
paul@165 | 301 | virtual uint32_t get_divider(Cpm_regs ®s); |
paul@165 | 302 | virtual void set_divider(Cpm_regs ®s, uint32_t division); |
paul@165 | 303 | |
paul@165 | 304 | // Clock source. |
paul@165 | 305 | |
paul@165 | 306 | virtual uint8_t get_source(Cpm_regs ®s); |
paul@165 | 307 | virtual void set_source(Cpm_regs ®s, uint8_t source); |
paul@165 | 308 | |
paul@165 | 309 | // Clock source frequency. |
paul@165 | 310 | |
paul@165 | 311 | virtual uint32_t get_source_frequency(Cpm_regs ®s); |
paul@165 | 312 | |
paul@165 | 313 | // Output frequency. |
paul@165 | 314 | |
paul@165 | 315 | virtual uint32_t get_frequency(Cpm_regs ®s); |
paul@165 | 316 | }; |
paul@165 | 317 | |
paul@165 | 318 | |
paul@165 | 319 | |
paul@165 | 320 | // PLL descriptions. |
paul@165 | 321 | |
paul@165 | 322 | class Pll : public Clock_base |
paul@165 | 323 | { |
paul@165 | 324 | uint32_t control_reg; |
paul@165 | 325 | enum Pll_bypass_bits bypass_bit; |
paul@165 | 326 | |
paul@165 | 327 | public: |
paul@165 | 328 | explicit Pll(int num_inputs, enum Clock_identifiers inputs[], |
paul@165 | 329 | uint32_t control_reg, enum Pll_bypass_bits bypass_bit) |
paul@165 | 330 | : Clock_base(num_inputs, inputs), control_reg(control_reg), bypass_bit(bypass_bit) |
paul@165 | 331 | { |
paul@165 | 332 | } |
paul@165 | 333 | |
paul@165 | 334 | // PLL_specific control. |
paul@165 | 335 | |
paul@165 | 336 | int have_pll(Cpm_regs ®s); |
paul@165 | 337 | int pll_enabled(Cpm_regs ®s); |
paul@165 | 338 | int pll_bypassed(Cpm_regs ®s); |
paul@165 | 339 | |
paul@165 | 340 | // Clock control. |
paul@165 | 341 | |
paul@165 | 342 | int have_clock(Cpm_regs ®s); |
paul@165 | 343 | void start_clock(Cpm_regs ®s); |
paul@165 | 344 | void stop_clock(Cpm_regs ®s); |
paul@165 | 345 | |
paul@165 | 346 | // General frequency modifiers. |
paul@165 | 347 | |
paul@165 | 348 | uint16_t get_multiplier(Cpm_regs ®s); |
paul@165 | 349 | void set_multiplier(Cpm_regs ®s, uint16_t multiplier); |
paul@165 | 350 | uint8_t get_input_division(Cpm_regs ®s); |
paul@165 | 351 | void set_input_division(Cpm_regs ®s, uint8_t divider); |
paul@165 | 352 | uint8_t get_output_division(Cpm_regs ®s); |
paul@165 | 353 | void set_output_division(Cpm_regs ®s, uint8_t divider); |
paul@165 | 354 | |
paul@165 | 355 | // PLL output frequency. |
paul@165 | 356 | |
paul@165 | 357 | uint32_t get_frequency(Cpm_regs ®s); |
paul@165 | 358 | |
paul@165 | 359 | // Other operations. |
paul@165 | 360 | |
paul@165 | 361 | void set_pll_parameters(Cpm_regs ®s, uint16_t multiplier, |
paul@165 | 362 | uint8_t in_divider, uint8_t out_divider); |
paul@165 | 363 | }; |
paul@165 | 364 | |
paul@165 | 365 | |
paul@165 | 366 | |
paul@161 | 367 | // Clock descriptions. |
paul@161 | 368 | |
paul@165 | 369 | class Clock : public Clock_base |
paul@161 | 370 | { |
paul@166 | 371 | Field _gate, _change_enable, _busy, _divider; |
paul@161 | 372 | |
paul@165 | 373 | // Clock control. |
paul@161 | 374 | |
paul@165 | 375 | void change_disable(Cpm_regs ®s); |
paul@165 | 376 | void change_enable(Cpm_regs ®s); |
paul@165 | 377 | void wait_busy(Cpm_regs ®s); |
paul@161 | 378 | |
paul@165 | 379 | public: |
paul@166 | 380 | explicit Clock(int num_inputs = 0, enum Clock_identifiers inputs[] = NULL, |
paul@166 | 381 | Field source = Source_undefined, |
paul@166 | 382 | Field gate = Gate_undefined, |
paul@166 | 383 | Field change_enable = Change_enable_undefined, |
paul@166 | 384 | Field busy = Busy_undefined, |
paul@166 | 385 | Field divider = Divider_undefined) |
paul@166 | 386 | : Clock_base(num_inputs, inputs, source), |
paul@166 | 387 | _gate(gate), _change_enable(change_enable), _busy(busy), _divider(divider) |
paul@165 | 388 | { |
paul@165 | 389 | } |
paul@161 | 390 | |
paul@165 | 391 | // Clock control. |
paul@161 | 392 | |
paul@165 | 393 | int have_clock(Cpm_regs ®s); |
paul@165 | 394 | void start_clock(Cpm_regs ®s); |
paul@165 | 395 | void stop_clock(Cpm_regs ®s); |
paul@161 | 396 | |
paul@165 | 397 | // Clock divider. |
paul@161 | 398 | |
paul@165 | 399 | uint32_t get_divider(Cpm_regs ®s); |
paul@165 | 400 | void set_divider(Cpm_regs ®s, uint32_t division); |
paul@161 | 401 | |
paul@165 | 402 | // Clock source. |
paul@161 | 403 | |
paul@165 | 404 | void set_source(Cpm_regs ®s, uint8_t source); |
paul@161 | 405 | }; |
paul@161 | 406 | |
paul@161 | 407 | |
paul@161 | 408 | |
paul@165 | 409 | // Clock instances. |
paul@165 | 410 | |
paul@165 | 411 | #define Clock_inputs(...) ((enum Clock_identifiers []) {__VA_ARGS__}) |
paul@165 | 412 | |
paul@165 | 413 | Clock clock_ahb2_apb(3, Clock_inputs(Clock_none, Clock_main, Clock_pll_M), |
paul@166 | 414 | Field(Clock_control, 3, Clock_source_hclock2)); |
paul@165 | 415 | |
paul@165 | 416 | Clock clock_aic_bitclk; |
paul@165 | 417 | |
paul@165 | 418 | Clock clock_aic_pclk; |
paul@165 | 419 | |
paul@165 | 420 | Clock clock_can0(4, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E, Clock_external), |
paul@166 | 421 | Field(Can_divider0, 3, Clock_source_can0), |
paul@166 | 422 | Field(Clock_gate1, 1, Clock_gate_can0), |
paul@166 | 423 | Field(Can_divider0, 1, Clock_change_enable_can0), |
paul@166 | 424 | Field(Can_divider0, 1, Clock_busy_can0), |
paul@166 | 425 | Field(Can_divider0, 0xff, Clock_divider_can0)); |
paul@165 | 426 | |
paul@165 | 427 | Clock clock_can1(4, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E, Clock_external), |
paul@166 | 428 | Field(Can_divider1, 3, Clock_source_can1), |
paul@166 | 429 | Field(Clock_gate1, 1, Clock_gate_can1), |
paul@166 | 430 | Field(Can_divider1, 1, Clock_change_enable_can1), |
paul@166 | 431 | Field(Can_divider1, 1, Clock_busy_can1), |
paul@166 | 432 | Field(Can_divider1, 0xff, Clock_divider_can1)); |
paul@165 | 433 | |
paul@165 | 434 | Clock clock_cdbus(3, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E), |
paul@166 | 435 | Field(Cdbus_divider, 3, Clock_source_cdbus), |
paul@166 | 436 | Field(Clock_gate1, 1, Clock_gate_cdbus), |
paul@166 | 437 | Field(Cdbus_divider, 1, Clock_change_enable_cdbus), |
paul@166 | 438 | Field(Cdbus_divider, 1, Clock_busy_cdbus), |
paul@166 | 439 | Field(Cdbus_divider, 0xff, Clock_divider_cdbus)); |
paul@165 | 440 | |
paul@165 | 441 | Clock clock_cim(3, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E), |
paul@166 | 442 | Field(Cim_divider, 3, Clock_source_cim), |
paul@166 | 443 | Field(Clock_gate0, 1, Clock_gate_cim), |
paul@166 | 444 | Field(Cim_divider, 1, Clock_change_enable_cim), |
paul@166 | 445 | Field(Cim_divider, 1, Clock_busy_cim), |
paul@166 | 446 | Field(Cim_divider, 0xff, Clock_divider_cim)); |
paul@165 | 447 | |
paul@165 | 448 | Clock clock_cpu(3, Clock_inputs(Clock_none, Clock_main, Clock_pll_M), |
paul@166 | 449 | Field(Clock_control, 3, Clock_source_cpu), |
paul@166 | 450 | Gate_undefined, |
paul@166 | 451 | Field(Clock_control, 1, Clock_change_enable_cpu), |
paul@166 | 452 | Field(Clock_status, 1, Clock_busy_cpu), |
paul@166 | 453 | Field(Clock_control, 0x0f, Clock_divider_cpu)); |
paul@165 | 454 | |
paul@165 | 455 | Clock clock_ddr(3, Clock_inputs(Clock_none, Clock_main, Clock_pll_M), |
paul@166 | 456 | Field(Ddr_divider, 3, Clock_source_ddr), |
paul@166 | 457 | Field(Clock_gate0, 1, Clock_gate_ddr), |
paul@166 | 458 | Field(Ddr_divider, 1, Clock_change_enable_ddr), |
paul@166 | 459 | Field(Ddr_divider, 1, Clock_busy_ddr), |
paul@166 | 460 | Field(Ddr_divider, 0x0f, Clock_divider_ddr)); |
paul@165 | 461 | |
paul@165 | 462 | Clock clock_dma(1, Clock_inputs(Clock_pclock), |
paul@166 | 463 | Source_undefined, |
paul@166 | 464 | Field(Clock_gate0, 1, Clock_gate_dma)); |
paul@165 | 465 | |
paul@165 | 466 | Clock clock_emac; |
paul@165 | 467 | |
paul@165 | 468 | Clock clock_external; |
paul@165 | 469 | |
paul@165 | 470 | Clock clock_hclock0(3, Clock_inputs(Clock_none, Clock_main, Clock_pll_M), |
paul@166 | 471 | Field(Clock_control, 3, Clock_source_hclock0), |
paul@166 | 472 | Field(Clock_gate0, 1, Clock_gate_ahb0), |
paul@166 | 473 | Field(Clock_control, 1, Clock_change_enable_ahb0), |
paul@166 | 474 | Busy_undefined, |
paul@166 | 475 | Field(Clock_control, 0x0f, Clock_divider_hclock0)); |
paul@165 | 476 | |
paul@165 | 477 | Clock clock_hclock2(1, Clock_inputs(Clock_ahb2_apb), |
paul@166 | 478 | Source_undefined, |
paul@166 | 479 | Field(Clock_gate0, 1, Clock_gate_apb0), |
paul@166 | 480 | Field(Clock_control, 1, Clock_change_enable_ahb2), |
paul@166 | 481 | Busy_undefined, |
paul@166 | 482 | Field(Clock_control, 0x0f, Clock_divider_hclock2)); |
paul@165 | 483 | |
paul@165 | 484 | Clock clock_hdmi; |
paul@165 | 485 | |
paul@165 | 486 | Clock clock_i2c(1, Clock_inputs(Clock_pclock), |
paul@166 | 487 | Source_undefined, |
paul@166 | 488 | Field(Clock_gate0, 1, Clock_gate_i2c0)); |
paul@165 | 489 | |
paul@165 | 490 | Clock clock_i2c0(1, Clock_inputs(Clock_pclock), |
paul@166 | 491 | Source_undefined, |
paul@166 | 492 | Field(Clock_gate0, 1, Clock_gate_i2c0)); |
paul@165 | 493 | |
paul@165 | 494 | Clock clock_i2c1(1, Clock_inputs(Clock_pclock), |
paul@166 | 495 | Source_undefined, |
paul@166 | 496 | Field(Clock_gate0, 1, Clock_gate_i2c1)); |
paul@165 | 497 | |
paul@165 | 498 | Clock clock_i2s; |
paul@165 | 499 | |
paul@165 | 500 | Clock clock_i2s0_rx(2, Clock_inputs(Clock_main, Clock_pll_E), |
paul@166 | 501 | Field(I2s_divider0, 1, Clock_source_i2s), |
paul@166 | 502 | Field(Clock_gate1, 1, Clock_gate_i2s0_rx), |
paul@166 | 503 | Field(I2s_divider0, 1, Clock_change_enable_i2s)); |
paul@165 | 504 | |
paul@165 | 505 | Clock clock_i2s0_tx(2, Clock_inputs(Clock_main, Clock_pll_E), |
paul@166 | 506 | Field(I2s_divider0, 1, Clock_source_i2s), |
paul@166 | 507 | Field(Clock_gate1, 1, Clock_gate_i2s0_tx), |
paul@166 | 508 | Field(I2s_divider0, 1, Clock_change_enable_i2s)); |
paul@165 | 509 | |
paul@165 | 510 | Clock clock_kbc; |
paul@165 | 511 | |
paul@165 | 512 | Clock clock_lcd; |
paul@165 | 513 | |
paul@165 | 514 | Clock clock_lcd_pixel(3, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E), |
paul@166 | 515 | Field(Lcd_divider, 3, Clock_source_lcd), |
paul@166 | 516 | Field(Clock_gate0, 1, Clock_gate_lcd_pixel), |
paul@166 | 517 | Field(Lcd_divider, 1, Clock_change_enable_lcd), |
paul@166 | 518 | Field(Lcd_divider, 1, Clock_busy_lcd), |
paul@166 | 519 | Field(Lcd_divider, 0xff, Clock_divider_lcd)); |
paul@165 | 520 | |
paul@165 | 521 | Clock clock_mac(3, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E), |
paul@166 | 522 | Field(Mac_divider, 3, Clock_source_mac), |
paul@166 | 523 | Field(Clock_gate1, 1, Clock_gate_gmac0), |
paul@166 | 524 | Field(Mac_divider, 1, Clock_change_enable_mac), |
paul@166 | 525 | Field(Mac_divider, 1, Clock_busy_mac), |
paul@166 | 526 | Field(Mac_divider, 0xff, Clock_divider_mac)); |
paul@165 | 527 | |
paul@165 | 528 | Clock clock_main(3, Clock_inputs(Clock_none, Clock_external, Clock_pll_A), |
paul@166 | 529 | Field(Clock_control, 3, Clock_source_main), |
paul@166 | 530 | Field(Clock_control, 1, Clock_gate_main)); |
paul@165 | 531 | |
paul@165 | 532 | Clock clock_msc(3, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E), |
paul@166 | 533 | Field(Msc_divider0, 3, Clock_source_msc0), |
paul@166 | 534 | Field(Clock_gate0, 1, Clock_gate_msc0), |
paul@166 | 535 | Field(Msc_divider0, 1, Clock_change_enable_msc0), |
paul@166 | 536 | Field(Msc_divider0, 1, Clock_busy_msc0), |
paul@166 | 537 | Field(Msc_divider0, 0xff, Clock_divider_msc0)); |
paul@165 | 538 | |
paul@165 | 539 | Clock clock_msc0(3, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E), |
paul@166 | 540 | Field(Msc_divider0, 3, Clock_source_msc0), |
paul@166 | 541 | Field(Clock_gate0, 1, Clock_gate_msc0), |
paul@166 | 542 | Field(Msc_divider0, 1, Clock_change_enable_msc0), |
paul@166 | 543 | Field(Msc_divider0, 1, Clock_busy_msc0), |
paul@166 | 544 | Field(Msc_divider0, 0xff, Clock_divider_msc0)); |
paul@165 | 545 | |
paul@165 | 546 | Clock clock_msc1(3, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E), |
paul@166 | 547 | Field(Msc_divider1, 3, Clock_source_msc1), |
paul@166 | 548 | Field(Clock_gate0, 1, Clock_gate_msc1), |
paul@166 | 549 | Field(Msc_divider1, 1, Clock_change_enable_msc1), |
paul@166 | 550 | Field(Msc_divider1, 1, Clock_busy_msc1), |
paul@166 | 551 | Field(Msc_divider1, 0xff, Clock_divider_msc1)); |
paul@165 | 552 | |
paul@165 | 553 | Clock clock_none; |
paul@161 | 554 | |
paul@165 | 555 | Clock clock_pclock(1, Clock_inputs(Clock_ahb2_apb), |
paul@166 | 556 | Source_undefined, |
paul@166 | 557 | Field(Clock_gate0, 1, Clock_gate_apb0), |
paul@166 | 558 | Change_enable_undefined, |
paul@166 | 559 | Busy_undefined, |
paul@166 | 560 | Field(Clock_control, 0x0f, Clock_divider_pclock)); |
paul@165 | 561 | |
paul@165 | 562 | Pll clock_pll_A(1, Clock_inputs(Clock_external), |
paul@165 | 563 | Pll_control_A, Pll_bypass_A); |
paul@165 | 564 | |
paul@165 | 565 | Pll clock_pll_E(1, Clock_inputs(Clock_external), |
paul@165 | 566 | Pll_control_E, Pll_bypass_E); |
paul@165 | 567 | |
paul@165 | 568 | Pll clock_pll_M(1, Clock_inputs(Clock_external), |
paul@165 | 569 | Pll_control_M, Pll_bypass_M); |
paul@165 | 570 | |
paul@165 | 571 | Clock clock_pwm(3, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E), |
paul@166 | 572 | Field(Pwm_divider, 3, Clock_source_pwm), |
paul@166 | 573 | Field(Clock_gate1, 1, Clock_gate_pwm), |
paul@166 | 574 | Field(Pwm_divider, 1, Clock_change_enable_pwm), |
paul@166 | 575 | Field(Pwm_divider, 1, Clock_busy_pwm), |
paul@166 | 576 | Field(Pwm_divider, 0x0f, Clock_divider_pwm)); |
paul@165 | 577 | |
paul@165 | 578 | Clock clock_pwm0(3, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E), |
paul@166 | 579 | Field(Pwm_divider, 3, Clock_source_pwm), |
paul@166 | 580 | Field(Clock_gate1, 1, Clock_gate_pwm), |
paul@166 | 581 | Field(Pwm_divider, 1, Clock_change_enable_pwm), |
paul@166 | 582 | Field(Pwm_divider, 1, Clock_busy_pwm), |
paul@166 | 583 | Field(Pwm_divider, 0x0f, Clock_divider_pwm)); |
paul@165 | 584 | |
paul@165 | 585 | Clock clock_pwm1; |
paul@165 | 586 | |
paul@165 | 587 | Clock clock_scc; |
paul@165 | 588 | |
paul@165 | 589 | Clock clock_sfc(3, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E), |
paul@166 | 590 | Field(Sfc_divider, 3, Clock_source_sfc), |
paul@166 | 591 | Field(Clock_gate0, 1, Clock_gate_sfc), |
paul@166 | 592 | Field(Sfc_divider, 1, Clock_change_enable_sfc), |
paul@166 | 593 | Field(Sfc_divider, 1, Clock_busy_sfc), |
paul@166 | 594 | Field(Sfc_divider, 0xff, Clock_divider_sfc)); |
paul@165 | 595 | |
paul@165 | 596 | Clock clock_smb0; |
paul@165 | 597 | |
paul@165 | 598 | Clock clock_smb1; |
paul@165 | 599 | |
paul@165 | 600 | Clock clock_smb2; |
paul@165 | 601 | |
paul@165 | 602 | Clock clock_smb3; |
paul@165 | 603 | |
paul@165 | 604 | Clock clock_smb4; |
paul@165 | 605 | |
paul@165 | 606 | Clock clock_ssi(3, Clock_inputs(Clock_main, Clock_pll_M, Clock_pll_E), |
paul@166 | 607 | Field(Ssi_divider, 3, Clock_source_ssi), |
paul@166 | 608 | Field(Clock_gate0, 1, Clock_gate_ssi0), |
paul@166 | 609 | Field(Ssi_divider, 1, Clock_change_enable_ssi), |
paul@166 | 610 | Field(Ssi_divider, 1, Clock_busy_ssi), |
paul@166 | 611 | Field(Ssi_divider, 0xff, Clock_divider_ssi)); |
paul@165 | 612 | |
paul@165 | 613 | Clock clock_timer(1, Clock_inputs(Clock_pclock), |
paul@166 | 614 | Source_undefined, |
paul@166 | 615 | Field(Clock_gate0, 1, Clock_gate_timer)); |
paul@165 | 616 | |
paul@165 | 617 | Clock clock_uart0(1, Clock_inputs(Clock_external), |
paul@166 | 618 | Source_undefined, |
paul@166 | 619 | Field(Clock_gate0, 1, Clock_gate_uart0)); |
paul@165 | 620 | |
paul@165 | 621 | Clock clock_uart1(1, Clock_inputs(Clock_external), |
paul@166 | 622 | Source_undefined, |
paul@166 | 623 | Field(Clock_gate0, 1, Clock_gate_uart1)); |
paul@165 | 624 | |
paul@165 | 625 | Clock clock_uart2(1, Clock_inputs(Clock_external), |
paul@166 | 626 | Source_undefined, |
paul@166 | 627 | Field(Clock_gate0, 1, Clock_gate_uart2)); |
paul@165 | 628 | |
paul@165 | 629 | Clock clock_uart3(1, Clock_inputs(Clock_external), |
paul@166 | 630 | Source_undefined, |
paul@166 | 631 | Field(Clock_gate1, 1, Clock_gate_uart3)); |
paul@165 | 632 | |
paul@165 | 633 | Clock clock_udc; |
paul@165 | 634 | |
paul@165 | 635 | Clock clock_uhc; |
paul@165 | 636 | |
paul@165 | 637 | Clock clock_uprt; |
paul@165 | 638 | |
paul@165 | 639 | |
paul@165 | 640 | |
paul@165 | 641 | // Clock register. |
paul@165 | 642 | |
paul@165 | 643 | static Clock_base *clocks[Clock_identifier_count] = { |
paul@165 | 644 | &clock_ahb2_apb, |
paul@165 | 645 | &clock_aic_bitclk, |
paul@165 | 646 | &clock_aic_pclk, |
paul@165 | 647 | &clock_can0, |
paul@165 | 648 | &clock_can1, |
paul@165 | 649 | &clock_cdbus, |
paul@165 | 650 | &clock_cim, |
paul@165 | 651 | &clock_cpu, |
paul@165 | 652 | &clock_ddr, |
paul@165 | 653 | &clock_dma, |
paul@165 | 654 | &clock_emac, |
paul@165 | 655 | &clock_external, |
paul@165 | 656 | &clock_hclock0, |
paul@165 | 657 | &clock_hclock2, |
paul@165 | 658 | &clock_hdmi, |
paul@165 | 659 | &clock_i2c, |
paul@165 | 660 | &clock_i2c0, |
paul@165 | 661 | &clock_i2c1, |
paul@165 | 662 | &clock_i2s, |
paul@165 | 663 | &clock_i2s0_rx, |
paul@165 | 664 | &clock_i2s0_tx, |
paul@165 | 665 | &clock_kbc, |
paul@165 | 666 | &clock_lcd, |
paul@165 | 667 | &clock_lcd_pixel, |
paul@165 | 668 | &clock_mac, |
paul@165 | 669 | &clock_main, |
paul@165 | 670 | &clock_msc, |
paul@165 | 671 | &clock_msc0, |
paul@165 | 672 | &clock_msc1, |
paul@165 | 673 | &clock_none, |
paul@165 | 674 | &clock_pclock, |
paul@165 | 675 | &clock_pll_A, |
paul@165 | 676 | &clock_pll_E, |
paul@165 | 677 | &clock_pll_M, |
paul@165 | 678 | &clock_pwm, |
paul@165 | 679 | &clock_pwm0, |
paul@165 | 680 | &clock_pwm1, |
paul@165 | 681 | &clock_scc, |
paul@165 | 682 | &clock_sfc, |
paul@165 | 683 | &clock_smb0, |
paul@165 | 684 | &clock_smb1, |
paul@165 | 685 | &clock_smb2, |
paul@165 | 686 | &clock_smb3, |
paul@165 | 687 | &clock_smb4, |
paul@165 | 688 | &clock_ssi, |
paul@165 | 689 | &clock_timer, |
paul@165 | 690 | &clock_uart0, |
paul@165 | 691 | &clock_uart1, |
paul@165 | 692 | &clock_uart2, |
paul@165 | 693 | &clock_uart3, |
paul@165 | 694 | &clock_udc, |
paul@165 | 695 | &clock_uhc, |
paul@165 | 696 | &clock_uprt, |
paul@165 | 697 | }; |
paul@165 | 698 | |
paul@165 | 699 | |
paul@165 | 700 | |
paul@165 | 701 | // Register access. |
paul@165 | 702 | |
paul@165 | 703 | Cpm_regs::Cpm_regs(l4_addr_t addr, uint32_t exclk_freq) |
paul@165 | 704 | : exclk_freq(exclk_freq) |
paul@161 | 705 | { |
paul@165 | 706 | _regs = new Hw::Mmio_register_block<32>(addr); |
paul@161 | 707 | } |
paul@161 | 708 | |
paul@165 | 709 | // Utility methods. |
paul@165 | 710 | |
paul@165 | 711 | uint32_t |
paul@165 | 712 | Cpm_regs::get_field(uint32_t reg, uint32_t mask, uint8_t shift) |
paul@165 | 713 | { |
paul@165 | 714 | return (_regs[reg] & (mask << shift)) >> shift; |
paul@165 | 715 | } |
paul@165 | 716 | |
paul@165 | 717 | void |
paul@165 | 718 | Cpm_regs::set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value) |
paul@165 | 719 | { |
paul@165 | 720 | _regs[reg] = (_regs[reg] & (~(mask << shift))) | ((mask & value) << shift); |
paul@165 | 721 | } |
paul@165 | 722 | |
paul@165 | 723 | |
paul@165 | 724 | |
paul@166 | 725 | // Field methods. |
paul@166 | 726 | |
paul@166 | 727 | uint32_t |
paul@166 | 728 | Field::get_field(Cpm_regs ®s) |
paul@166 | 729 | { |
paul@166 | 730 | if (defined) |
paul@166 | 731 | return regs.get_field(reg, mask, bit); |
paul@166 | 732 | else |
paul@166 | 733 | return 0; |
paul@166 | 734 | } |
paul@166 | 735 | |
paul@166 | 736 | void |
paul@166 | 737 | Field::set_field(Cpm_regs ®s, uint32_t value) |
paul@166 | 738 | { |
paul@166 | 739 | if (defined) |
paul@166 | 740 | regs.set_field(reg, mask, bit, value); |
paul@166 | 741 | } |
paul@166 | 742 | |
paul@166 | 743 | |
paul@166 | 744 | |
paul@165 | 745 | // Clock control. |
paul@165 | 746 | |
paul@165 | 747 | int |
paul@165 | 748 | Clock_base::have_clock(Cpm_regs ®s) |
paul@165 | 749 | { |
paul@165 | 750 | (void) regs; |
paul@165 | 751 | return true; |
paul@165 | 752 | } |
paul@165 | 753 | |
paul@165 | 754 | void |
paul@165 | 755 | Clock_base::start_clock(Cpm_regs ®s) |
paul@165 | 756 | { |
paul@165 | 757 | (void) regs; |
paul@165 | 758 | } |
paul@165 | 759 | |
paul@165 | 760 | void |
paul@165 | 761 | Clock_base::stop_clock(Cpm_regs ®s) |
paul@165 | 762 | { |
paul@165 | 763 | (void) regs; |
paul@165 | 764 | } |
paul@165 | 765 | |
paul@165 | 766 | // Default divider. |
paul@165 | 767 | |
paul@165 | 768 | uint32_t |
paul@165 | 769 | Clock_base::get_divider(Cpm_regs ®s) |
paul@165 | 770 | { |
paul@165 | 771 | (void) regs; |
paul@165 | 772 | return 1; |
paul@165 | 773 | } |
paul@165 | 774 | |
paul@165 | 775 | void |
paul@165 | 776 | Clock_base::set_divider(Cpm_regs ®s, uint32_t division) |
paul@165 | 777 | { |
paul@165 | 778 | (void) regs; |
paul@165 | 779 | (void) division; |
paul@165 | 780 | } |
paul@165 | 781 | |
paul@165 | 782 | // Clock sources. |
paul@165 | 783 | |
paul@165 | 784 | uint8_t |
paul@165 | 785 | Clock_base::get_source(Cpm_regs ®s) |
paul@165 | 786 | { |
paul@166 | 787 | if (_source.is_defined()) |
paul@166 | 788 | return _source.get_field(regs); |
paul@165 | 789 | else |
paul@165 | 790 | return 0; |
paul@165 | 791 | } |
paul@165 | 792 | |
paul@165 | 793 | void |
paul@165 | 794 | Clock_base::set_source(Cpm_regs ®s, uint8_t source) |
paul@165 | 795 | { |
paul@166 | 796 | if (_source.is_defined()) |
paul@165 | 797 | return; |
paul@165 | 798 | |
paul@166 | 799 | _source.set_field(regs, source); |
paul@165 | 800 | } |
paul@165 | 801 | |
paul@165 | 802 | // Clock source frequencies. |
paul@165 | 803 | |
paul@165 | 804 | uint32_t |
paul@165 | 805 | Clock_base::get_source_frequency(Cpm_regs ®s) |
paul@165 | 806 | { |
paul@165 | 807 | // Return the external clock frequency without any input clock. |
paul@165 | 808 | |
paul@165 | 809 | if (num_inputs == 0) |
paul@165 | 810 | return regs.exclk_freq; |
paul@165 | 811 | |
paul@165 | 812 | // Clocks with one source yield that input frequency. |
paul@165 | 813 | |
paul@165 | 814 | else if (num_inputs == 1) |
paul@165 | 815 | return clocks[inputs[0]]->get_frequency(regs); |
paul@165 | 816 | |
paul@165 | 817 | // With multiple sources, obtain the selected source for the clock. |
paul@165 | 818 | |
paul@165 | 819 | uint8_t source = get_source(regs); |
paul@165 | 820 | |
paul@165 | 821 | // Return the frequency of the source. |
paul@165 | 822 | |
paul@165 | 823 | if (source < num_inputs) |
paul@165 | 824 | return clocks[inputs[source]]->get_frequency(regs); |
paul@165 | 825 | else |
paul@165 | 826 | return 0; |
paul@165 | 827 | } |
paul@165 | 828 | |
paul@165 | 829 | // Output clock frequencies. |
paul@165 | 830 | |
paul@165 | 831 | uint32_t |
paul@165 | 832 | Clock_base::get_frequency(Cpm_regs ®s) |
paul@165 | 833 | { |
paul@165 | 834 | return get_source_frequency(regs) / get_divider(regs); |
paul@165 | 835 | } |
paul@165 | 836 | |
paul@165 | 837 | |
paul@165 | 838 | |
paul@165 | 839 | // PLL-specific control. |
paul@165 | 840 | |
paul@165 | 841 | int |
paul@165 | 842 | Pll::have_pll(Cpm_regs ®s) |
paul@165 | 843 | { |
paul@165 | 844 | return regs.get_field(control_reg, 1, Pll_stable); |
paul@165 | 845 | } |
paul@165 | 846 | |
paul@165 | 847 | int |
paul@165 | 848 | Pll::pll_enabled(Cpm_regs ®s) |
paul@165 | 849 | { |
paul@165 | 850 | return regs.get_field(control_reg, 1, Pll_enabled); |
paul@165 | 851 | } |
paul@165 | 852 | |
paul@165 | 853 | int |
paul@165 | 854 | Pll::pll_bypassed(Cpm_regs ®s) |
paul@165 | 855 | { |
paul@165 | 856 | return regs.get_field(control_reg, 1, bypass_bit); |
paul@165 | 857 | } |
paul@165 | 858 | |
paul@165 | 859 | // Clock control. |
paul@165 | 860 | |
paul@165 | 861 | int |
paul@165 | 862 | Pll::have_clock(Cpm_regs ®s) |
paul@165 | 863 | { |
paul@165 | 864 | return have_pll(regs) && pll_enabled(regs); |
paul@165 | 865 | } |
paul@165 | 866 | |
paul@165 | 867 | void |
paul@165 | 868 | Pll::start_clock(Cpm_regs ®s) |
paul@165 | 869 | { |
paul@165 | 870 | regs.set_field(control_reg, 1, Pll_enabled, 1); |
paul@165 | 871 | while (!have_pll(regs)); |
paul@165 | 872 | } |
paul@165 | 873 | |
paul@165 | 874 | void |
paul@165 | 875 | Pll::stop_clock(Cpm_regs ®s) |
paul@161 | 876 | { |
paul@165 | 877 | regs.set_field(control_reg, 1, Pll_enabled, 0); |
paul@165 | 878 | while (have_pll(regs)); |
paul@165 | 879 | } |
paul@165 | 880 | |
paul@165 | 881 | // Feedback (13-bit) multiplier. |
paul@165 | 882 | |
paul@165 | 883 | uint16_t |
paul@165 | 884 | Pll::get_multiplier(Cpm_regs ®s) |
paul@165 | 885 | { |
paul@165 | 886 | return regs.get_field(control_reg, 0x1fff, Pll_multiplier) + 1; |
paul@165 | 887 | } |
paul@165 | 888 | |
paul@165 | 889 | void |
paul@165 | 890 | Pll::set_multiplier(Cpm_regs ®s, uint16_t multiplier) |
paul@165 | 891 | { |
paul@165 | 892 | regs.set_field(control_reg, 0x1fff, Pll_multiplier, multiplier - 1); |
paul@165 | 893 | } |
paul@165 | 894 | |
paul@165 | 895 | // Input (6-bit) divider. |
paul@165 | 896 | |
paul@165 | 897 | uint8_t |
paul@165 | 898 | Pll::get_input_division(Cpm_regs ®s) |
paul@165 | 899 | { |
paul@165 | 900 | return regs.get_field(control_reg, 0x3f, Pll_input_division) + 1; |
paul@165 | 901 | } |
paul@165 | 902 | |
paul@165 | 903 | void |
paul@165 | 904 | Pll::set_input_division(Cpm_regs ®s, uint8_t divider) |
paul@165 | 905 | { |
paul@165 | 906 | regs.set_field(control_reg, 0x3f, Pll_input_division, divider - 1); |
paul@165 | 907 | } |
paul@165 | 908 | |
paul@165 | 909 | // Output (dual 3-bit) dividers. |
paul@165 | 910 | |
paul@165 | 911 | uint8_t |
paul@165 | 912 | Pll::get_output_division(Cpm_regs ®s) |
paul@165 | 913 | { |
paul@165 | 914 | uint8_t d0 = regs.get_field(control_reg, 0x07, Pll_output_division0); |
paul@165 | 915 | uint8_t d1 = regs.get_field(control_reg, 0x07, Pll_output_division1); |
paul@165 | 916 | |
paul@165 | 917 | return d0 * d1; |
paul@165 | 918 | } |
paul@165 | 919 | |
paul@165 | 920 | void |
paul@165 | 921 | Pll::set_output_division(Cpm_regs ®s, uint8_t divider) |
paul@165 | 922 | { |
paul@165 | 923 | // Assert 1 as a minimum. |
paul@165 | 924 | // Divider 0 must be less than or equal to divider 1. |
paul@165 | 925 | |
paul@165 | 926 | uint8_t d0 = (uint8_t) floor(sqrt(divider ? divider : 1)); |
paul@165 | 927 | uint8_t d1 = divider / d0; |
paul@165 | 928 | |
paul@165 | 929 | regs.set_field(control_reg, 0x07, Pll_output_division0, d0); |
paul@165 | 930 | regs.set_field(control_reg, 0x07, Pll_output_division1, d1); |
paul@165 | 931 | } |
paul@165 | 932 | |
paul@165 | 933 | uint32_t |
paul@165 | 934 | Pll::get_frequency(Cpm_regs ®s) |
paul@165 | 935 | { |
paul@165 | 936 | // Test for PLL enable and not PLL bypass. |
paul@165 | 937 | |
paul@165 | 938 | if (pll_enabled(regs)) |
paul@165 | 939 | { |
paul@165 | 940 | if (!pll_bypassed(regs)) |
paul@165 | 941 | return (get_source_frequency(regs) * get_multiplier(regs)) / |
paul@165 | 942 | (get_input_division(regs) * get_output_division(regs)); |
paul@165 | 943 | else |
paul@165 | 944 | return get_source_frequency(regs); |
paul@165 | 945 | } |
paul@165 | 946 | else |
paul@165 | 947 | return 0; |
paul@165 | 948 | } |
paul@165 | 949 | |
paul@165 | 950 | void |
paul@165 | 951 | Pll::set_pll_parameters(Cpm_regs ®s, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider) |
paul@165 | 952 | { |
paul@165 | 953 | set_multiplier(regs, multiplier); |
paul@165 | 954 | set_input_division(regs, in_divider); |
paul@165 | 955 | set_output_division(regs, out_divider); |
paul@161 | 956 | |
paul@165 | 957 | if (pll_enabled(regs) && !pll_bypassed(regs)) |
paul@165 | 958 | while (!have_pll(regs)); |
paul@165 | 959 | } |
paul@165 | 960 | |
paul@165 | 961 | |
paul@165 | 962 | |
paul@165 | 963 | // Clock control. |
paul@165 | 964 | |
paul@165 | 965 | void |
paul@165 | 966 | Clock::change_disable(Cpm_regs ®s) |
paul@165 | 967 | { |
paul@166 | 968 | if (_change_enable.is_defined()) |
paul@166 | 969 | _change_enable.set_field(regs, 0); |
paul@165 | 970 | } |
paul@165 | 971 | |
paul@165 | 972 | void |
paul@165 | 973 | Clock::change_enable(Cpm_regs ®s) |
paul@165 | 974 | { |
paul@166 | 975 | if (_change_enable.is_defined()) |
paul@166 | 976 | _change_enable.set_field(regs, 1); |
paul@165 | 977 | } |
paul@165 | 978 | |
paul@165 | 979 | int |
paul@165 | 980 | Clock::have_clock(Cpm_regs ®s) |
paul@165 | 981 | { |
paul@166 | 982 | if (_gate.is_defined()) |
paul@166 | 983 | return !_gate.get_field(regs); |
paul@165 | 984 | else |
paul@165 | 985 | return true; |
paul@165 | 986 | } |
paul@165 | 987 | |
paul@165 | 988 | void |
paul@165 | 989 | Clock::start_clock(Cpm_regs ®s) |
paul@165 | 990 | { |
paul@166 | 991 | if (_gate.is_defined()) |
paul@166 | 992 | _gate.set_field(regs, 0); |
paul@165 | 993 | } |
paul@165 | 994 | |
paul@165 | 995 | void |
paul@165 | 996 | Clock::stop_clock(Cpm_regs ®s) |
paul@165 | 997 | { |
paul@166 | 998 | if (_gate.is_defined()) |
paul@166 | 999 | _gate.set_field(regs, 1); |
paul@165 | 1000 | } |
paul@165 | 1001 | |
paul@165 | 1002 | void |
paul@165 | 1003 | Clock::wait_busy(Cpm_regs ®s) |
paul@165 | 1004 | { |
paul@166 | 1005 | if (_busy.is_defined()) |
paul@166 | 1006 | while (_busy.get_field(regs)); |
paul@165 | 1007 | } |
paul@165 | 1008 | |
paul@165 | 1009 | |
paul@165 | 1010 | |
paul@165 | 1011 | // Clock dividers. |
paul@165 | 1012 | |
paul@165 | 1013 | uint32_t |
paul@165 | 1014 | Clock::get_divider(Cpm_regs ®s) |
paul@165 | 1015 | { |
paul@166 | 1016 | if (_divider.is_defined()) |
paul@166 | 1017 | return _divider.get_field(regs) + 1; |
paul@165 | 1018 | else |
paul@165 | 1019 | return 1; |
paul@165 | 1020 | } |
paul@165 | 1021 | |
paul@165 | 1022 | void |
paul@165 | 1023 | Clock::set_divider(Cpm_regs ®s, uint32_t division) |
paul@165 | 1024 | { |
paul@166 | 1025 | if (_divider.is_defined()) |
paul@165 | 1026 | return; |
paul@165 | 1027 | |
paul@165 | 1028 | change_enable(regs); |
paul@166 | 1029 | _divider.set_field(regs, division - 1); |
paul@165 | 1030 | wait_busy(regs); |
paul@165 | 1031 | change_disable(regs); |
paul@165 | 1032 | } |
paul@165 | 1033 | |
paul@165 | 1034 | void |
paul@165 | 1035 | Clock::set_source(Cpm_regs ®s, uint8_t source) |
paul@165 | 1036 | { |
paul@165 | 1037 | change_enable(regs); |
paul@165 | 1038 | Clock_base::set_source(regs, source); |
paul@165 | 1039 | wait_busy(regs); |
paul@165 | 1040 | change_disable(regs); |
paul@161 | 1041 | } |
paul@161 | 1042 | |
paul@161 | 1043 | |
paul@161 | 1044 | |
paul@160 | 1045 | // If implemented as a Hw::Device, various properties would be |
paul@160 | 1046 | // initialised in the constructor and obtained from the device tree |
paul@160 | 1047 | // definitions. |
paul@160 | 1048 | |
paul@160 | 1049 | Cpm_x1600_chip::Cpm_x1600_chip(l4_addr_t addr, uint32_t exclk_freq) |
paul@165 | 1050 | : _cpm_regs(addr, exclk_freq) |
paul@160 | 1051 | { |
paul@160 | 1052 | // add_cid("cpm"); |
paul@160 | 1053 | // add_cid("cpm-x1600"); |
paul@165 | 1054 | // register_property("exclk_freq", &exclk_freq); |
paul@161 | 1055 | } |
paul@160 | 1056 | |
paul@161 | 1057 | int |
paul@161 | 1058 | Cpm_x1600_chip::have_clock(enum Clock_identifiers clock) |
paul@161 | 1059 | { |
paul@165 | 1060 | return clocks[clock]->have_clock(_cpm_regs); |
paul@161 | 1061 | } |
paul@161 | 1062 | |
paul@161 | 1063 | void |
paul@161 | 1064 | Cpm_x1600_chip::start_clock(enum Clock_identifiers clock) |
paul@160 | 1065 | { |
paul@165 | 1066 | clocks[clock]->start_clock(_cpm_regs); |
paul@161 | 1067 | } |
paul@161 | 1068 | |
paul@161 | 1069 | void |
paul@161 | 1070 | Cpm_x1600_chip::stop_clock(enum Clock_identifiers clock) |
paul@161 | 1071 | { |
paul@165 | 1072 | clocks[clock]->stop_clock(_cpm_regs); |
paul@160 | 1073 | } |
paul@160 | 1074 | |
paul@161 | 1075 | uint32_t |
paul@161 | 1076 | Cpm_x1600_chip::get_divider(enum Clock_identifiers clock) |
paul@160 | 1077 | { |
paul@165 | 1078 | return clocks[clock]->get_divider(_cpm_regs); |
paul@160 | 1079 | } |
paul@160 | 1080 | |
paul@161 | 1081 | void |
paul@161 | 1082 | Cpm_x1600_chip::set_divider(enum Clock_identifiers clock, uint32_t division) |
paul@160 | 1083 | { |
paul@165 | 1084 | clocks[clock]->set_divider(_cpm_regs, division); |
paul@160 | 1085 | } |
paul@160 | 1086 | |
paul@160 | 1087 | uint8_t |
paul@161 | 1088 | Cpm_x1600_chip::get_source(enum Clock_identifiers clock) |
paul@160 | 1089 | { |
paul@165 | 1090 | return clocks[clock]->get_source(_cpm_regs); |
paul@160 | 1091 | } |
paul@160 | 1092 | |
paul@160 | 1093 | void |
paul@161 | 1094 | Cpm_x1600_chip::set_source(enum Clock_identifiers clock, uint8_t source) |
paul@160 | 1095 | { |
paul@165 | 1096 | clocks[clock]->set_source(_cpm_regs, source); |
paul@160 | 1097 | } |
paul@160 | 1098 | |
paul@161 | 1099 | uint32_t |
paul@161 | 1100 | Cpm_x1600_chip::get_source_frequency(enum Clock_identifiers clock) |
paul@160 | 1101 | { |
paul@165 | 1102 | return clocks[clock]->get_source_frequency(_cpm_regs); |
paul@160 | 1103 | } |
paul@160 | 1104 | |
paul@160 | 1105 | uint32_t |
paul@160 | 1106 | Cpm_x1600_chip::get_frequency(enum Clock_identifiers clock) |
paul@160 | 1107 | { |
paul@165 | 1108 | return clocks[clock]->get_frequency(_cpm_regs); |
paul@160 | 1109 | } |
paul@160 | 1110 | |
paul@160 | 1111 | void |
paul@160 | 1112 | Cpm_x1600_chip::set_frequency(enum Clock_identifiers clock, uint32_t frequency) |
paul@160 | 1113 | { |
paul@160 | 1114 | switch (clock) |
paul@160 | 1115 | { |
paul@160 | 1116 | // The pixel frequency is based on the selected clock source (SCLK_A, MPLL or |
paul@160 | 1117 | // EPLL). |
paul@160 | 1118 | |
paul@160 | 1119 | case Clock_lcd_pixel: |
paul@165 | 1120 | { |
paul@160 | 1121 | |
paul@160 | 1122 | // Switch to the MPLL and attempt to set the divider. |
paul@160 | 1123 | |
paul@165 | 1124 | Clock_base *lcd = clocks[Clock_lcd_pixel]; |
paul@165 | 1125 | Clock_base *pll = clocks[Clock_pll_M]; |
paul@165 | 1126 | |
paul@165 | 1127 | lcd->set_source(_cpm_regs, Source_mME_pll_M); |
paul@165 | 1128 | pll->start_clock(_cpm_regs); |
paul@165 | 1129 | lcd->set_divider(_cpm_regs, lcd->get_source_frequency(_cpm_regs) / frequency); |
paul@160 | 1130 | break; |
paul@165 | 1131 | } |
paul@160 | 1132 | |
paul@160 | 1133 | default: |
paul@160 | 1134 | break; |
paul@160 | 1135 | } |
paul@160 | 1136 | } |
paul@160 | 1137 | |
paul@165 | 1138 | void |
paul@165 | 1139 | Cpm_x1600_chip::set_pll_parameters(enum Clock_identifiers clock, uint16_t multiplier, |
paul@165 | 1140 | uint8_t in_divider, uint8_t out_divider) |
paul@165 | 1141 | { |
paul@165 | 1142 | Pll *pll = dynamic_cast<Pll *>(clocks[clock]); |
paul@165 | 1143 | |
paul@165 | 1144 | pll->set_pll_parameters(_cpm_regs, multiplier, in_divider, out_divider); |
paul@165 | 1145 | } |
paul@165 | 1146 | |
paul@160 | 1147 | |
paul@160 | 1148 | |
paul@160 | 1149 | // C language interface functions. |
paul@160 | 1150 | |
paul@160 | 1151 | void |
paul@160 | 1152 | *x1600_cpm_init(l4_addr_t cpm_base) |
paul@160 | 1153 | { |
paul@160 | 1154 | /* Initialise the clock and power management peripheral with the |
paul@160 | 1155 | register memory region and a 24MHz EXCLK frequency. */ |
paul@160 | 1156 | |
paul@160 | 1157 | return (void *) new Cpm_x1600_chip(cpm_base, 24000000); |
paul@160 | 1158 | } |
paul@160 | 1159 | |
paul@160 | 1160 | int |
paul@160 | 1161 | x1600_cpm_have_clock(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1162 | { |
paul@160 | 1163 | return static_cast<Cpm_x1600_chip *>(cpm)->have_clock(clock); |
paul@160 | 1164 | } |
paul@160 | 1165 | |
paul@160 | 1166 | void |
paul@160 | 1167 | x1600_cpm_start_clock(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1168 | { |
paul@160 | 1169 | static_cast<Cpm_x1600_chip *>(cpm)->start_clock(clock); |
paul@160 | 1170 | } |
paul@160 | 1171 | |
paul@160 | 1172 | void |
paul@160 | 1173 | x1600_cpm_stop_clock(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1174 | { |
paul@160 | 1175 | static_cast<Cpm_x1600_chip *>(cpm)->stop_clock(clock); |
paul@160 | 1176 | } |
paul@160 | 1177 | |
paul@161 | 1178 | uint32_t |
paul@161 | 1179 | x1600_cpm_get_divider(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1180 | { |
paul@161 | 1181 | return static_cast<Cpm_x1600_chip *>(cpm)->get_divider(clock); |
paul@160 | 1182 | } |
paul@160 | 1183 | |
paul@161 | 1184 | void |
paul@161 | 1185 | x1600_cpm_set_divider(void *cpm, enum Clock_identifiers clock, uint32_t divider) |
paul@160 | 1186 | { |
paul@161 | 1187 | return static_cast<Cpm_x1600_chip *>(cpm)->set_divider(clock, divider); |
paul@160 | 1188 | } |
paul@160 | 1189 | |
paul@160 | 1190 | uint8_t |
paul@161 | 1191 | x1600_cpm_get_source(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1192 | { |
paul@161 | 1193 | return static_cast<Cpm_x1600_chip *>(cpm)->get_source(clock); |
paul@160 | 1194 | } |
paul@160 | 1195 | |
paul@160 | 1196 | void |
paul@161 | 1197 | x1600_cpm_set_source(void *cpm, enum Clock_identifiers clock, uint8_t source) |
paul@160 | 1198 | { |
paul@161 | 1199 | static_cast<Cpm_x1600_chip *>(cpm)->set_source(clock, source); |
paul@160 | 1200 | } |
paul@160 | 1201 | |
paul@160 | 1202 | uint32_t |
paul@161 | 1203 | x1600_cpm_get_source_frequency(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1204 | { |
paul@161 | 1205 | return static_cast<Cpm_x1600_chip *>(cpm)->get_source_frequency(clock); |
paul@160 | 1206 | } |
paul@160 | 1207 | |
paul@160 | 1208 | uint32_t |
paul@160 | 1209 | x1600_cpm_get_frequency(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1210 | { |
paul@160 | 1211 | return static_cast<Cpm_x1600_chip *>(cpm)->get_frequency(clock); |
paul@160 | 1212 | } |
paul@160 | 1213 | |
paul@160 | 1214 | void |
paul@160 | 1215 | x1600_cpm_set_frequency(void *cpm, enum Clock_identifiers clock, uint32_t frequency) |
paul@160 | 1216 | { |
paul@160 | 1217 | static_cast<Cpm_x1600_chip *>(cpm)->set_frequency(clock, frequency); |
paul@160 | 1218 | } |
paul@160 | 1219 | |
paul@160 | 1220 | void |
paul@160 | 1221 | x1600_cpm_set_mpll_parameters(void *cpm, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider) |
paul@160 | 1222 | { |
paul@165 | 1223 | static_cast<Cpm_x1600_chip *>(cpm)->set_pll_parameters(Clock_pll_M, multiplier, in_divider, out_divider); |
paul@160 | 1224 | } |