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@161 | 74 | // Special value |
paul@161 | 75 | |
paul@161 | 76 | Reg_undefined = 0xfff, |
paul@160 | 77 | }; |
paul@160 | 78 | |
paul@160 | 79 | enum Clock_source_bits : unsigned |
paul@160 | 80 | { |
paul@160 | 81 | // Clock_control |
paul@160 | 82 | |
paul@160 | 83 | Clock_source_main = 30, // SEL_SRC (output to SCLK_A) |
paul@160 | 84 | Clock_source_cpu = 28, // SEL_CPLL (output to CCLK) |
paul@160 | 85 | Clock_source_hclock0 = 26, // SEL_H0PLL (output to AHB0) |
paul@160 | 86 | Clock_source_hclock2 = 24, // SEL_H2PLL (output to AHB2) |
paul@160 | 87 | |
paul@161 | 88 | // Divider registers |
paul@160 | 89 | |
paul@160 | 90 | Clock_source_can0 = 30, // CA0CS |
paul@160 | 91 | Clock_source_can1 = 30, // CA1CS |
paul@161 | 92 | Clock_source_cdbus = 30, // CDCS |
paul@161 | 93 | Clock_source_cim = 30, // CIMPCS |
paul@161 | 94 | Clock_source_ddr = 30, // DCS |
paul@161 | 95 | Clock_source_i2s = 31, // I2PCS |
paul@161 | 96 | Clock_source_lcd = 30, // LPCS |
paul@161 | 97 | Clock_source_mac = 30, // MACPCS |
paul@161 | 98 | Clock_source_msc0 = 30, // MPCS |
paul@161 | 99 | Clock_source_msc1 = 30, // MPCS |
paul@161 | 100 | Clock_source_pwm = 30, // PWMPCS |
paul@161 | 101 | Clock_source_sfc = 30, // SFCS |
paul@161 | 102 | Clock_source_ssi = 30, // SPCS |
paul@160 | 103 | |
paul@161 | 104 | // Special value |
paul@160 | 105 | |
paul@161 | 106 | Clock_source_undefined = 32, |
paul@160 | 107 | }; |
paul@160 | 108 | |
paul@161 | 109 | enum Clock_source_values : unsigned |
paul@160 | 110 | { |
paul@161 | 111 | Source_mME_main = 0, |
paul@161 | 112 | Source_mME_pll_M = 1, |
paul@161 | 113 | Source_mME_pll_E = 2, |
paul@160 | 114 | |
paul@161 | 115 | // Special value |
paul@160 | 116 | |
paul@161 | 117 | Source_mask = 0x3, |
paul@160 | 118 | }; |
paul@160 | 119 | |
paul@160 | 120 | enum Clock_gate_bits : unsigned |
paul@160 | 121 | { |
paul@161 | 122 | // Clock_control |
paul@161 | 123 | |
paul@161 | 124 | Clock_gate_main = 23, // GATE_SCLKA |
paul@161 | 125 | |
paul@160 | 126 | // Clock_gate0 |
paul@160 | 127 | |
paul@160 | 128 | Clock_gate_ddr = 31, // DDR |
paul@160 | 129 | Clock_gate_ahb0 = 29, // AHB0 |
paul@160 | 130 | Clock_gate_apb0 = 28, // APB0 |
paul@160 | 131 | Clock_gate_rtc = 27, // RTC |
paul@160 | 132 | Clock_gate_aes = 24, // AES |
paul@160 | 133 | Clock_gate_lcd_pixel = 23, // LCD |
paul@160 | 134 | Clock_gate_cim = 22, // CIM |
paul@160 | 135 | Clock_gate_dma = 21, // PDMA |
paul@160 | 136 | Clock_gate_ost = 20, // OST |
paul@160 | 137 | Clock_gate_ssi0 = 19, // SSI0 |
paul@160 | 138 | Clock_gate_timer = 18, // TCU |
paul@160 | 139 | Clock_gate_dtrng = 17, // DTRNG |
paul@160 | 140 | Clock_gate_uart2 = 16, // UART2 |
paul@160 | 141 | Clock_gate_uart1 = 15, // UART1 |
paul@160 | 142 | Clock_gate_uart0 = 14, // UART0 |
paul@160 | 143 | Clock_gate_sadc = 13, // SADC |
paul@160 | 144 | Clock_gate_audio = 11, // AUDIO |
paul@160 | 145 | Clock_gate_ssi_slv = 10, // SSI_SLV |
paul@160 | 146 | Clock_gate_i2c1 = 8, // I2C1 |
paul@160 | 147 | Clock_gate_i2c0 = 7, // I2C0 |
paul@160 | 148 | Clock_gate_msc1 = 5, // MSC1 |
paul@160 | 149 | Clock_gate_msc0 = 4, // MSC0 |
paul@160 | 150 | Clock_gate_otg = 3, // OTG |
paul@160 | 151 | Clock_gate_sfc = 2, // SFC |
paul@160 | 152 | Clock_gate_efuse = 1, // EFUSE |
paul@160 | 153 | Clock_gate_nemc = 0, // NEMC |
paul@160 | 154 | |
paul@160 | 155 | // Clock_gate1 |
paul@160 | 156 | |
paul@160 | 157 | Clock_gate_arb = 30, // ARB |
paul@160 | 158 | Clock_gate_mipi_csi = 28, // MIPI_CSI |
paul@160 | 159 | Clock_gate_intc = 26, // INTC |
paul@160 | 160 | Clock_gate_gmac0 = 23, // GMAC0 |
paul@160 | 161 | Clock_gate_uart3 = 16, // UART3 |
paul@160 | 162 | Clock_gate_i2s0_tx = 9, // I2S0_dev_tclk |
paul@160 | 163 | Clock_gate_i2s0_rx = 8, // I2S0_dev_rclk |
paul@160 | 164 | Clock_gate_hash = 6, // HASH |
paul@160 | 165 | Clock_gate_pwm = 5, // PWM |
paul@160 | 166 | Clock_gate_cdbus = 2, // CDBUS |
paul@160 | 167 | Clock_gate_can1 = 1, // CAN1 |
paul@160 | 168 | Clock_gate_can0 = 0, // CAN0 |
paul@160 | 169 | |
paul@160 | 170 | // Special value |
paul@160 | 171 | |
paul@160 | 172 | Clock_gate_undefined = 32, |
paul@160 | 173 | }; |
paul@160 | 174 | |
paul@161 | 175 | enum Clock_change_enable_bits : unsigned |
paul@161 | 176 | { |
paul@161 | 177 | Clock_change_enable_cpu = 22, |
paul@161 | 178 | Clock_change_enable_ahb0 = 21, |
paul@161 | 179 | Clock_change_enable_ahb2 = 20, |
paul@161 | 180 | Clock_change_enable_ddr = 29, |
paul@161 | 181 | Clock_change_enable_mac = 29, |
paul@161 | 182 | Clock_change_enable_i2s = 29, |
paul@161 | 183 | Clock_change_enable_lcd = 29, |
paul@161 | 184 | Clock_change_enable_msc0 = 29, |
paul@161 | 185 | Clock_change_enable_msc1 = 29, |
paul@161 | 186 | Clock_change_enable_sfc = 29, |
paul@161 | 187 | Clock_change_enable_ssi = 29, |
paul@161 | 188 | Clock_change_enable_cim = 29, |
paul@161 | 189 | Clock_change_enable_pwm = 29, |
paul@161 | 190 | Clock_change_enable_can0 = 29, |
paul@161 | 191 | Clock_change_enable_can1 = 29, |
paul@161 | 192 | Clock_change_enable_cdbus = 29, |
paul@160 | 193 | |
paul@161 | 194 | // Special value |
paul@161 | 195 | |
paul@161 | 196 | Clock_change_enable_undefined = 32, |
paul@160 | 197 | }; |
paul@160 | 198 | |
paul@161 | 199 | enum Clock_busy_bits : unsigned |
paul@161 | 200 | { |
paul@161 | 201 | Clock_busy_cpu = 0, |
paul@161 | 202 | Clock_busy_ddr = 28, |
paul@161 | 203 | Clock_busy_mac = 28, |
paul@161 | 204 | Clock_busy_lcd = 28, |
paul@161 | 205 | Clock_busy_msc0 = 28, |
paul@161 | 206 | Clock_busy_msc1 = 28, |
paul@161 | 207 | Clock_busy_sfc = 28, |
paul@161 | 208 | Clock_busy_ssi = 28, |
paul@161 | 209 | Clock_busy_cim = 28, |
paul@161 | 210 | Clock_busy_pwm = 28, |
paul@161 | 211 | Clock_busy_can0 = 28, |
paul@161 | 212 | Clock_busy_can1 = 28, |
paul@161 | 213 | Clock_busy_cdbus = 28, |
paul@160 | 214 | |
paul@161 | 215 | // Special value |
paul@161 | 216 | |
paul@161 | 217 | Clock_busy_undefined = 32, |
paul@160 | 218 | }; |
paul@160 | 219 | |
paul@161 | 220 | enum Clock_divider_bits : unsigned |
paul@160 | 221 | { |
paul@161 | 222 | Clock_divider_can0 = 0, // CAN0CDR |
paul@161 | 223 | Clock_divider_can1 = 0, // CAN1CDR |
paul@161 | 224 | Clock_divider_cdbus = 0, // CDBUSCDR |
paul@161 | 225 | Clock_divider_cim = 0, // CIMCDR |
paul@161 | 226 | Clock_divider_cpu = 0, // CDIV |
paul@161 | 227 | Clock_divider_ddr = 0, // DDRCDR |
paul@161 | 228 | Clock_divider_hclock0 = 8, // H0DIV (fast AHB peripherals) |
paul@161 | 229 | Clock_divider_hclock2 = 12, // H2DIV (fast AHB peripherals) |
paul@161 | 230 | Clock_divider_l2cache = 4, // L2CDIV |
paul@161 | 231 | Clock_divider_lcd = 0, // LPCDR |
paul@161 | 232 | Clock_divider_mac = 0, // MACCDR |
paul@161 | 233 | Clock_divider_msc0 = 0, // MSC0CDR |
paul@161 | 234 | Clock_divider_msc1 = 0, // MSC1CDR |
paul@161 | 235 | Clock_divider_pclock = 16, // PDIV (slow APB peripherals) |
paul@161 | 236 | Clock_divider_pwm = 0, // PWMCDR |
paul@161 | 237 | Clock_divider_sfc = 0, // SFCCDR |
paul@161 | 238 | Clock_divider_ssi = 0, // SSICDR |
paul@160 | 239 | |
paul@161 | 240 | // Special value |
paul@161 | 241 | |
paul@161 | 242 | Clock_divider_undefined = 32, |
paul@160 | 243 | }; |
paul@160 | 244 | |
paul@160 | 245 | enum Pll_bits : unsigned |
paul@160 | 246 | { |
paul@160 | 247 | // Pll_control_A, Pll_control_M, Pll_control_E |
paul@160 | 248 | |
paul@160 | 249 | Pll_multiplier = 20, // xPLLM |
paul@160 | 250 | Pll_input_division = 14, // xPLLN |
paul@160 | 251 | Pll_output_division1 = 11, // xPLLOD1 |
paul@160 | 252 | Pll_output_division0 = 8, // xPLLOD0 |
paul@160 | 253 | Pll_stable = 3, // xPLL_ON |
paul@160 | 254 | Pll_enabled = 0, // xPLLEN |
paul@160 | 255 | }; |
paul@160 | 256 | |
paul@160 | 257 | enum Pll_bypass_bits : unsigned |
paul@160 | 258 | { |
paul@160 | 259 | Pll_bypass_A = 30, // APLL_BP |
paul@160 | 260 | Pll_bypass_M = 28, // MPLL_BP |
paul@160 | 261 | Pll_bypass_E = 26, // EPLL_BP |
paul@160 | 262 | }; |
paul@160 | 263 | |
paul@160 | 264 | |
paul@160 | 265 | |
paul@161 | 266 | // Clock input descriptions. |
paul@161 | 267 | |
paul@161 | 268 | struct Clock_input_desc |
paul@161 | 269 | { |
paul@161 | 270 | uint32_t source_reg; |
paul@161 | 271 | enum Clock_source_bits source_bit; |
paul@161 | 272 | int num_inputs; |
paul@161 | 273 | enum Clock_input_identifiers inputs[3]; |
paul@161 | 274 | }; |
paul@161 | 275 | |
paul@161 | 276 | struct Clock_input_desc clock_input_desc[Clock_input_identifier_count] = { |
paul@161 | 277 | |
paul@161 | 278 | /* Clock_input_ahb2_apb */ {Clock_control, Clock_source_hclock2, |
paul@161 | 279 | 3, {Clock_input_none, Clock_input_main, Clock_input_pll_M}}, |
paul@161 | 280 | |
paul@161 | 281 | /* Clock_input_external */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 282 | 0, {}}, |
paul@161 | 283 | |
paul@161 | 284 | /* Clock_input_main */ {Clock_control, Clock_source_main, |
paul@161 | 285 | 3, {Clock_input_none, Clock_input_external, Clock_input_pll_A}}, |
paul@161 | 286 | |
paul@161 | 287 | /* Clock_input_none */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 288 | 0, {}}, |
paul@161 | 289 | |
paul@161 | 290 | /* Clock_input_pll_A */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 291 | 1, {Clock_input_external}}, |
paul@161 | 292 | |
paul@161 | 293 | /* Clock_input_pll_E */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 294 | 1, {Clock_input_external}}, |
paul@161 | 295 | |
paul@161 | 296 | /* Clock_input_pll_M */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 297 | 1, {Clock_input_external}}, |
paul@161 | 298 | }; |
paul@161 | 299 | |
paul@161 | 300 | |
paul@161 | 301 | |
paul@161 | 302 | // Clock descriptions. |
paul@161 | 303 | |
paul@161 | 304 | struct Clock_desc |
paul@161 | 305 | { |
paul@161 | 306 | uint32_t source_reg; |
paul@161 | 307 | enum Clock_source_bits source_bit; |
paul@161 | 308 | uint32_t gate_reg; |
paul@161 | 309 | enum Clock_gate_bits gate_bit; |
paul@161 | 310 | uint32_t change_enable_reg; |
paul@161 | 311 | enum Clock_change_enable_bits change_enable_bit; |
paul@161 | 312 | uint32_t busy_reg; |
paul@161 | 313 | enum Clock_busy_bits busy_bit; |
paul@161 | 314 | uint32_t divider_reg; |
paul@161 | 315 | enum Clock_divider_bits divider_bit; |
paul@161 | 316 | uint32_t divider_mask; |
paul@161 | 317 | int num_inputs; |
paul@161 | 318 | enum Clock_input_identifiers inputs[4]; |
paul@162 | 319 | enum Clock_identifiers clock_input; |
paul@161 | 320 | }; |
paul@161 | 321 | |
paul@162 | 322 | #define Clock_desc_undefined {Reg_undefined, Clock_source_undefined, \ |
paul@162 | 323 | Reg_undefined, Clock_gate_undefined, \ |
paul@162 | 324 | Reg_undefined, Clock_change_enable_undefined, \ |
paul@162 | 325 | Reg_undefined, Clock_busy_undefined, \ |
paul@162 | 326 | Reg_undefined, Clock_divider_undefined, 0, \ |
paul@162 | 327 | 0, {}, \ |
paul@162 | 328 | Clock_undefined} |
paul@161 | 329 | |
paul@161 | 330 | static struct Clock_desc clock_desc[Clock_identifier_count] = { |
paul@161 | 331 | |
paul@162 | 332 | /* Clock_aic_bitclk */ Clock_desc_undefined, |
paul@161 | 333 | |
paul@162 | 334 | /* Clock_aic_pclk */ Clock_desc_undefined, |
paul@161 | 335 | |
paul@161 | 336 | /* Clock_can0 */ {Can_divider0, Clock_source_can0, |
paul@161 | 337 | Clock_gate1, Clock_gate_can0, |
paul@161 | 338 | Can_divider0, Clock_change_enable_can0, |
paul@161 | 339 | Can_divider0, Clock_busy_can0, |
paul@161 | 340 | Can_divider0, Clock_divider_can0, 0xff, |
paul@162 | 341 | 4, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E, Clock_input_external}, |
paul@162 | 342 | Clock_undefined}, |
paul@161 | 343 | |
paul@161 | 344 | /* Clock_can1 */ {Can_divider1, Clock_source_can1, |
paul@161 | 345 | Clock_gate1, Clock_gate_can1, |
paul@161 | 346 | Can_divider1, Clock_change_enable_can1, |
paul@161 | 347 | Can_divider1, Clock_busy_can1, |
paul@161 | 348 | Can_divider1, Clock_divider_can1, 0xff, |
paul@162 | 349 | 4, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E, Clock_input_external}, |
paul@162 | 350 | Clock_undefined}, |
paul@161 | 351 | |
paul@161 | 352 | /* Clock_cdbus */ {Cdbus_divider, Clock_source_cdbus, |
paul@161 | 353 | Clock_gate1, Clock_gate_cdbus, |
paul@161 | 354 | Cdbus_divider, Clock_change_enable_cdbus, |
paul@161 | 355 | Cdbus_divider, Clock_busy_cdbus, |
paul@161 | 356 | Cdbus_divider, Clock_divider_cdbus, 0xff, |
paul@162 | 357 | 3, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E}, |
paul@162 | 358 | Clock_undefined}, |
paul@161 | 359 | |
paul@161 | 360 | /* Clock_cim */ {Cim_divider, Clock_source_cim, |
paul@161 | 361 | Clock_gate0, Clock_gate_cim, |
paul@161 | 362 | Cim_divider, Clock_change_enable_cim, |
paul@161 | 363 | Cim_divider, Clock_busy_cim, |
paul@161 | 364 | Cim_divider, Clock_divider_cim, 0xff, |
paul@162 | 365 | 3, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E}, |
paul@162 | 366 | Clock_undefined}, |
paul@161 | 367 | |
paul@161 | 368 | /* Clock_cpu */ {Clock_control, Clock_source_cpu, |
paul@161 | 369 | Reg_undefined, Clock_gate_undefined, |
paul@161 | 370 | Clock_control, Clock_change_enable_cpu, |
paul@161 | 371 | Clock_status, Clock_busy_cpu, |
paul@161 | 372 | Clock_control, Clock_divider_cpu, 0x0f, |
paul@162 | 373 | 3, {Clock_input_none, Clock_input_main, Clock_input_pll_M}, |
paul@162 | 374 | Clock_undefined}, |
paul@161 | 375 | |
paul@161 | 376 | /* Clock_ddr */ {Ddr_divider, Clock_source_ddr, |
paul@161 | 377 | Clock_gate0, Clock_gate_ddr, |
paul@161 | 378 | Ddr_divider, Clock_change_enable_ddr, |
paul@161 | 379 | Ddr_divider, Clock_busy_ddr, |
paul@161 | 380 | Ddr_divider, Clock_divider_ddr, 0x0f, |
paul@162 | 381 | 3, {Clock_input_none, Clock_input_main, Clock_input_pll_M}, |
paul@162 | 382 | Clock_undefined}, |
paul@161 | 383 | |
paul@161 | 384 | /* Clock_dma */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 385 | Clock_gate0, Clock_gate_dma, |
paul@161 | 386 | Reg_undefined, Clock_change_enable_undefined, |
paul@161 | 387 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 388 | Reg_undefined, Clock_divider_undefined, 0, |
paul@162 | 389 | 0, {}, |
paul@162 | 390 | Clock_pclock}, |
paul@161 | 391 | |
paul@162 | 392 | /* Clock_emac */ Clock_desc_undefined, |
paul@161 | 393 | |
paul@161 | 394 | /* Clock_hclock0 */ {Clock_control, Clock_source_hclock0, |
paul@161 | 395 | Clock_gate0, Clock_gate_ahb0, |
paul@161 | 396 | Clock_control, Clock_change_enable_ahb0, |
paul@161 | 397 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 398 | Clock_control, Clock_divider_hclock0, 0x0f, |
paul@162 | 399 | 3, {Clock_input_none, Clock_input_main, Clock_input_pll_M}, |
paul@162 | 400 | Clock_undefined}, |
paul@161 | 401 | |
paul@161 | 402 | /* Clock_hclock2 */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 403 | Clock_gate0, Clock_gate_apb0, |
paul@161 | 404 | Clock_control, Clock_change_enable_ahb2, |
paul@161 | 405 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 406 | Clock_control, Clock_divider_hclock2, 0x0f, |
paul@162 | 407 | 1, {Clock_input_ahb2_apb}, |
paul@162 | 408 | Clock_undefined}, |
paul@161 | 409 | |
paul@162 | 410 | /* Clock_hdmi */ Clock_desc_undefined, |
paul@161 | 411 | |
paul@161 | 412 | /* Clock_i2c */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 413 | Clock_gate0, Clock_gate_i2c0, |
paul@161 | 414 | Reg_undefined, Clock_change_enable_undefined, |
paul@161 | 415 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 416 | Reg_undefined, Clock_divider_undefined, 0, |
paul@162 | 417 | 0, {}, |
paul@162 | 418 | Clock_pclock}, |
paul@161 | 419 | |
paul@161 | 420 | /* Clock_i2c0 */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 421 | Clock_gate0, Clock_gate_i2c0, |
paul@161 | 422 | Reg_undefined, Clock_change_enable_undefined, |
paul@161 | 423 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 424 | Reg_undefined, Clock_divider_undefined, 0, |
paul@162 | 425 | 0, {}, |
paul@162 | 426 | Clock_pclock}, |
paul@161 | 427 | |
paul@161 | 428 | /* Clock_i2c1 */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 429 | Clock_gate0, Clock_gate_i2c1, |
paul@161 | 430 | Reg_undefined, Clock_change_enable_undefined, |
paul@161 | 431 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 432 | Reg_undefined, Clock_divider_undefined, 0, |
paul@162 | 433 | 0, {}, |
paul@162 | 434 | Clock_pclock}, |
paul@161 | 435 | |
paul@162 | 436 | /* Clock_i2s */ Clock_desc_undefined, |
paul@161 | 437 | |
paul@161 | 438 | /* Clock_i2s0_rx */ {I2s_divider0, Clock_source_i2s, |
paul@161 | 439 | Clock_gate1, Clock_gate_i2s0_rx, |
paul@161 | 440 | I2s_divider0, Clock_change_enable_i2s, |
paul@161 | 441 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 442 | Reg_undefined, Clock_divider_undefined, 0, // NOTE: To define. |
paul@162 | 443 | 2, {Clock_input_main, Clock_input_pll_E}, |
paul@162 | 444 | Clock_undefined}, |
paul@161 | 445 | |
paul@161 | 446 | /* Clock_i2s0_tx */ {I2s_divider0, Clock_source_i2s, |
paul@161 | 447 | Clock_gate1, Clock_gate_i2s0_tx, |
paul@161 | 448 | I2s_divider0, Clock_change_enable_i2s, |
paul@161 | 449 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 450 | Reg_undefined, Clock_divider_undefined, 0, // NOTE: To define. |
paul@162 | 451 | 2, {Clock_input_main, Clock_input_pll_E}, |
paul@162 | 452 | Clock_undefined}, |
paul@161 | 453 | |
paul@162 | 454 | /* Clock_kbc */ Clock_desc_undefined, |
paul@161 | 455 | |
paul@162 | 456 | /* Clock_lcd */ Clock_desc_undefined, |
paul@161 | 457 | |
paul@161 | 458 | /* Clock_lcd_pixel */ {Lcd_divider, Clock_source_lcd, |
paul@161 | 459 | Clock_gate0, Clock_gate_lcd_pixel, |
paul@161 | 460 | Lcd_divider, Clock_change_enable_lcd, |
paul@161 | 461 | Lcd_divider, Clock_busy_lcd, |
paul@161 | 462 | Lcd_divider, Clock_divider_lcd, 0xff, |
paul@162 | 463 | 3, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E}, |
paul@162 | 464 | Clock_undefined}, |
paul@161 | 465 | |
paul@161 | 466 | /* Clock_mac */ {Mac_divider, Clock_source_mac, |
paul@161 | 467 | Clock_gate1, Clock_gate_gmac0, |
paul@161 | 468 | Mac_divider, Clock_change_enable_mac, |
paul@161 | 469 | Mac_divider, Clock_busy_mac, |
paul@161 | 470 | Mac_divider, Clock_divider_mac, 0xff, |
paul@162 | 471 | 3, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E}, |
paul@162 | 472 | Clock_undefined}, |
paul@161 | 473 | |
paul@161 | 474 | /* Clock_main */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 475 | Clock_control, Clock_gate_main, |
paul@161 | 476 | Reg_undefined, Clock_change_enable_undefined, |
paul@161 | 477 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 478 | Reg_undefined, Clock_divider_undefined, 0, |
paul@162 | 479 | 1, {Clock_input_main}, |
paul@162 | 480 | Clock_undefined}, |
paul@161 | 481 | |
paul@161 | 482 | /* Clock_msc */ {Msc_divider0, Clock_source_msc0, |
paul@161 | 483 | Clock_gate0, Clock_gate_msc0, |
paul@161 | 484 | Msc_divider0, Clock_change_enable_msc0, |
paul@161 | 485 | Msc_divider0, Clock_busy_msc0, |
paul@161 | 486 | Msc_divider0, Clock_divider_msc0, 0xff, |
paul@162 | 487 | 3, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E}, |
paul@162 | 488 | Clock_undefined}, |
paul@161 | 489 | |
paul@161 | 490 | /* Clock_msc0 */ {Msc_divider0, Clock_source_msc0, |
paul@161 | 491 | Clock_gate0, Clock_gate_msc0, |
paul@161 | 492 | Msc_divider0, Clock_change_enable_msc0, |
paul@161 | 493 | Msc_divider0, Clock_busy_msc0, |
paul@161 | 494 | Msc_divider0, Clock_divider_msc0, 0xff, |
paul@162 | 495 | 3, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E}, |
paul@162 | 496 | Clock_undefined}, |
paul@161 | 497 | |
paul@161 | 498 | /* Clock_msc1 */ {Msc_divider1, Clock_source_msc1, |
paul@161 | 499 | Clock_gate0, Clock_gate_msc1, |
paul@161 | 500 | Msc_divider1, Clock_change_enable_msc1, |
paul@161 | 501 | Msc_divider1, Clock_busy_msc1, |
paul@161 | 502 | Msc_divider1, Clock_divider_msc1, 0xff, |
paul@162 | 503 | 3, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E}, |
paul@162 | 504 | Clock_undefined}, |
paul@161 | 505 | |
paul@161 | 506 | /* Clock_pclock */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 507 | Clock_gate0, Clock_gate_apb0, |
paul@161 | 508 | Reg_undefined, Clock_change_enable_undefined, |
paul@161 | 509 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 510 | Clock_control, Clock_divider_pclock, 0x0f, |
paul@162 | 511 | 1, {Clock_input_ahb2_apb}, |
paul@162 | 512 | Clock_undefined}, |
paul@161 | 513 | |
paul@161 | 514 | /* Clock_pwm */ {Pwm_divider, Clock_source_pwm, |
paul@161 | 515 | Clock_gate1, Clock_gate_pwm, |
paul@161 | 516 | Pwm_divider, Clock_change_enable_pwm, |
paul@161 | 517 | Pwm_divider, Clock_busy_pwm, |
paul@161 | 518 | Pwm_divider, Clock_divider_pwm, 0x0f, |
paul@162 | 519 | 3, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E}, |
paul@162 | 520 | Clock_undefined}, |
paul@161 | 521 | |
paul@161 | 522 | /* Clock_pwm0 */ {Pwm_divider, Clock_source_pwm, |
paul@161 | 523 | Clock_gate1, Clock_gate_pwm, |
paul@161 | 524 | Pwm_divider, Clock_change_enable_pwm, |
paul@161 | 525 | Pwm_divider, Clock_busy_pwm, |
paul@161 | 526 | Pwm_divider, Clock_divider_pwm, 0x0f, |
paul@162 | 527 | 3, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E}, |
paul@162 | 528 | Clock_undefined}, |
paul@161 | 529 | |
paul@162 | 530 | /* Clock_pwm1 */ Clock_desc_undefined, |
paul@161 | 531 | |
paul@162 | 532 | /* Clock_scc */ Clock_desc_undefined, |
paul@161 | 533 | |
paul@161 | 534 | /* Clock_sfc */ {Sfc_divider, Clock_source_sfc, |
paul@161 | 535 | Clock_gate0, Clock_gate_sfc, |
paul@161 | 536 | Sfc_divider, Clock_change_enable_sfc, |
paul@161 | 537 | Sfc_divider, Clock_busy_sfc, |
paul@161 | 538 | Sfc_divider, Clock_divider_sfc, 0xff, |
paul@162 | 539 | 3, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E}, |
paul@162 | 540 | Clock_undefined}, |
paul@161 | 541 | |
paul@162 | 542 | /* Clock_smb0 */ Clock_desc_undefined, |
paul@161 | 543 | |
paul@162 | 544 | /* Clock_smb1 */ Clock_desc_undefined, |
paul@162 | 545 | |
paul@162 | 546 | /* Clock_smb2 */ Clock_desc_undefined, |
paul@161 | 547 | |
paul@162 | 548 | /* Clock_smb3 */ Clock_desc_undefined, |
paul@161 | 549 | |
paul@162 | 550 | /* Clock_smb4 */ Clock_desc_undefined, |
paul@161 | 551 | |
paul@161 | 552 | /* Clock_ssi */ {Ssi_divider, Clock_source_ssi, |
paul@161 | 553 | Clock_gate0, Clock_gate_ssi0, |
paul@161 | 554 | Ssi_divider, Clock_change_enable_ssi, |
paul@161 | 555 | Ssi_divider, Clock_busy_ssi, |
paul@161 | 556 | Ssi_divider, Clock_divider_ssi, 0xff, |
paul@162 | 557 | 3, {Clock_input_main, Clock_input_pll_M, Clock_input_pll_E}, |
paul@162 | 558 | Clock_undefined}, |
paul@161 | 559 | |
paul@161 | 560 | /* Clock_timer */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 561 | Clock_gate0, Clock_gate_timer, |
paul@161 | 562 | Reg_undefined, Clock_change_enable_undefined, |
paul@161 | 563 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 564 | Reg_undefined, Clock_divider_undefined, 0, |
paul@162 | 565 | 0, {}, |
paul@162 | 566 | Clock_pclock}, |
paul@161 | 567 | |
paul@161 | 568 | /* Clock_uart0 */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 569 | Clock_gate0, Clock_gate_uart0, |
paul@161 | 570 | Reg_undefined, Clock_change_enable_undefined, |
paul@161 | 571 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 572 | Reg_undefined, Clock_divider_undefined, 0, |
paul@163 | 573 | 1, {Clock_input_external}, |
paul@163 | 574 | Clock_undefined}, |
paul@161 | 575 | |
paul@161 | 576 | /* Clock_uart1 */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 577 | Clock_gate0, Clock_gate_uart1, |
paul@161 | 578 | Reg_undefined, Clock_change_enable_undefined, |
paul@161 | 579 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 580 | Reg_undefined, Clock_divider_undefined, 0, |
paul@163 | 581 | 1, {Clock_input_external}, |
paul@163 | 582 | Clock_undefined}, |
paul@161 | 583 | |
paul@161 | 584 | /* Clock_uart2 */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 585 | Clock_gate0, Clock_gate_uart2, |
paul@161 | 586 | Reg_undefined, Clock_change_enable_undefined, |
paul@161 | 587 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 588 | Reg_undefined, Clock_divider_undefined, 0, |
paul@163 | 589 | 1, {Clock_input_external}, |
paul@163 | 590 | Clock_undefined}, |
paul@161 | 591 | |
paul@161 | 592 | /* Clock_uart3 */ {Reg_undefined, Clock_source_undefined, |
paul@161 | 593 | Clock_gate1, Clock_gate_uart3, |
paul@161 | 594 | Reg_undefined, Clock_change_enable_undefined, |
paul@161 | 595 | Reg_undefined, Clock_busy_undefined, |
paul@161 | 596 | Reg_undefined, Clock_divider_undefined, 0, |
paul@163 | 597 | 1, {Clock_input_external}, |
paul@163 | 598 | Clock_undefined}, |
paul@161 | 599 | |
paul@162 | 600 | /* Clock_udc */ Clock_desc_undefined, |
paul@161 | 601 | |
paul@162 | 602 | /* Clock_uhc */ Clock_desc_undefined, |
paul@162 | 603 | |
paul@162 | 604 | /* Clock_uprt */ Clock_desc_undefined, |
paul@161 | 605 | }; |
paul@161 | 606 | |
paul@161 | 607 | |
paul@161 | 608 | |
paul@161 | 609 | // Convenience functions. |
paul@161 | 610 | |
paul@161 | 611 | static uint8_t get_clock_gate_bit(enum Clock_identifiers clock) |
paul@161 | 612 | { |
paul@161 | 613 | enum Clock_gate_bits bit = clock_desc[clock].gate_bit; |
paul@161 | 614 | |
paul@161 | 615 | return bit != Clock_gate_undefined ? (uint8_t) bit : 0; |
paul@161 | 616 | } |
paul@161 | 617 | |
paul@161 | 618 | static uint32_t get_clock_gate_mask(enum Clock_identifiers clock) |
paul@161 | 619 | { |
paul@161 | 620 | enum Clock_gate_bits bit = clock_desc[clock].gate_bit; |
paul@161 | 621 | |
paul@161 | 622 | return bit != Clock_gate_undefined ? 1 : 0; |
paul@161 | 623 | } |
paul@161 | 624 | |
paul@161 | 625 | |
paul@161 | 626 | |
paul@160 | 627 | // If implemented as a Hw::Device, various properties would be |
paul@160 | 628 | // initialised in the constructor and obtained from the device tree |
paul@160 | 629 | // definitions. |
paul@160 | 630 | |
paul@160 | 631 | Cpm_x1600_chip::Cpm_x1600_chip(l4_addr_t addr, uint32_t exclk_freq) |
paul@160 | 632 | : _exclk_freq(exclk_freq) |
paul@160 | 633 | { |
paul@160 | 634 | _regs = new Hw::Mmio_register_block<32>(addr); |
paul@160 | 635 | |
paul@160 | 636 | // add_cid("cpm"); |
paul@160 | 637 | // add_cid("cpm-x1600"); |
paul@160 | 638 | // register_property("exclk_freq", &_exclk_freq); |
paul@160 | 639 | } |
paul@160 | 640 | |
paul@160 | 641 | |
paul@160 | 642 | |
paul@160 | 643 | // Utility methods. |
paul@160 | 644 | |
paul@160 | 645 | uint32_t |
paul@160 | 646 | Cpm_x1600_chip::get_field(uint32_t reg, uint32_t mask, uint8_t shift) |
paul@160 | 647 | { |
paul@160 | 648 | return (_regs[reg] & (mask << shift)) >> shift; |
paul@160 | 649 | } |
paul@160 | 650 | |
paul@160 | 651 | void |
paul@160 | 652 | Cpm_x1600_chip::set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value) |
paul@160 | 653 | { |
paul@160 | 654 | _regs[reg] = (_regs[reg] & (~(mask << shift))) | ((mask & value) << shift); |
paul@160 | 655 | } |
paul@160 | 656 | |
paul@161 | 657 | |
paul@161 | 658 | |
paul@161 | 659 | // Clock/timer control. |
paul@161 | 660 | |
paul@161 | 661 | void |
paul@161 | 662 | Cpm_x1600_chip::change_disable(enum Clock_identifiers clock) |
paul@161 | 663 | { |
paul@161 | 664 | enum Clock_change_enable_bits bit = clock_desc[clock].change_enable_bit; |
paul@161 | 665 | |
paul@161 | 666 | if (bit != Clock_change_enable_undefined) |
paul@161 | 667 | set_field(clock_desc[clock].change_enable_reg, 1, bit, 0); |
paul@161 | 668 | } |
paul@161 | 669 | |
paul@161 | 670 | void |
paul@161 | 671 | Cpm_x1600_chip::change_enable(enum Clock_identifiers clock) |
paul@161 | 672 | { |
paul@161 | 673 | enum Clock_change_enable_bits bit = clock_desc[clock].change_enable_bit; |
paul@161 | 674 | |
paul@161 | 675 | if (bit != Clock_change_enable_undefined) |
paul@161 | 676 | set_field(clock_desc[clock].change_enable_reg, 1, bit, 1); |
paul@161 | 677 | } |
paul@160 | 678 | |
paul@161 | 679 | int |
paul@161 | 680 | Cpm_x1600_chip::have_clock(enum Clock_identifiers clock) |
paul@161 | 681 | { |
paul@161 | 682 | if (clock_desc[clock].gate_bit != Clock_gate_undefined) |
paul@161 | 683 | return !get_field(clock_desc[clock].gate_reg, get_clock_gate_mask(clock), |
paul@161 | 684 | get_clock_gate_bit(clock)); |
paul@161 | 685 | else |
paul@161 | 686 | return true; |
paul@161 | 687 | } |
paul@161 | 688 | |
paul@161 | 689 | void |
paul@161 | 690 | Cpm_x1600_chip::start_clock(enum Clock_identifiers clock) |
paul@160 | 691 | { |
paul@161 | 692 | if (clock_desc[clock].gate_bit != Clock_gate_undefined) |
paul@161 | 693 | set_field(clock_desc[clock].gate_reg, get_clock_gate_mask(clock), |
paul@161 | 694 | get_clock_gate_bit(clock), 0); |
paul@161 | 695 | } |
paul@161 | 696 | |
paul@161 | 697 | void |
paul@161 | 698 | Cpm_x1600_chip::stop_clock(enum Clock_identifiers clock) |
paul@161 | 699 | { |
paul@161 | 700 | if (clock_desc[clock].gate_bit != Clock_gate_undefined) |
paul@161 | 701 | set_field(clock_desc[clock].gate_reg, get_clock_gate_mask(clock), |
paul@161 | 702 | get_clock_gate_bit(clock), 1); |
paul@161 | 703 | } |
paul@161 | 704 | |
paul@161 | 705 | void |
paul@161 | 706 | Cpm_x1600_chip::wait_busy(enum Clock_identifiers clock) |
paul@161 | 707 | { |
paul@161 | 708 | enum Clock_busy_bits bit = clock_desc[clock].busy_bit; |
paul@161 | 709 | |
paul@161 | 710 | if (bit != Clock_busy_undefined) |
paul@161 | 711 | while (get_field(clock_desc[clock].busy_reg, 1, bit)); |
paul@160 | 712 | } |
paul@160 | 713 | |
paul@160 | 714 | |
paul@160 | 715 | |
paul@160 | 716 | // PLL control. |
paul@160 | 717 | |
paul@160 | 718 | // Return whether the PLL is stable. |
paul@160 | 719 | |
paul@160 | 720 | int |
paul@160 | 721 | Cpm_x1600_chip::have_pll(uint32_t pll_reg) |
paul@160 | 722 | { |
paul@161 | 723 | return get_field(pll_reg, 1, Pll_stable); |
paul@160 | 724 | } |
paul@160 | 725 | |
paul@160 | 726 | int |
paul@160 | 727 | Cpm_x1600_chip::pll_enabled(uint32_t pll_reg) |
paul@160 | 728 | { |
paul@161 | 729 | return get_field(pll_reg, 1, Pll_enabled); |
paul@160 | 730 | } |
paul@160 | 731 | |
paul@160 | 732 | int |
paul@160 | 733 | Cpm_x1600_chip::pll_bypassed(uint32_t pll_reg) |
paul@160 | 734 | { |
paul@161 | 735 | uint8_t bit; |
paul@161 | 736 | unsigned mask = 1; |
paul@160 | 737 | |
paul@160 | 738 | switch (pll_reg) |
paul@160 | 739 | { |
paul@161 | 740 | case Pll_control_A: bit = Pll_bypass_A; break; |
paul@161 | 741 | case Pll_control_M: bit = Pll_bypass_M; break; |
paul@161 | 742 | case Pll_control_E: bit = Pll_bypass_E; break; |
paul@161 | 743 | default: bit = 0; mask = 0; break; |
paul@160 | 744 | } |
paul@160 | 745 | |
paul@161 | 746 | return get_field(Pll_control, mask, bit); |
paul@160 | 747 | } |
paul@160 | 748 | |
paul@160 | 749 | void |
paul@160 | 750 | Cpm_x1600_chip::pll_enable(uint32_t pll_reg) |
paul@160 | 751 | { |
paul@161 | 752 | set_field(pll_reg, 1, Pll_enabled, 1); |
paul@161 | 753 | while (!have_pll(pll_reg)); |
paul@160 | 754 | } |
paul@160 | 755 | |
paul@160 | 756 | void |
paul@160 | 757 | Cpm_x1600_chip::pll_disable(uint32_t pll_reg) |
paul@160 | 758 | { |
paul@161 | 759 | set_field(pll_reg, 1, Pll_enabled, 0); |
paul@161 | 760 | while (have_pll(pll_reg)); |
paul@160 | 761 | } |
paul@160 | 762 | |
paul@160 | 763 | // Feedback (13-bit) multiplier. |
paul@160 | 764 | |
paul@160 | 765 | uint16_t |
paul@160 | 766 | Cpm_x1600_chip::get_multiplier(uint32_t pll_reg) |
paul@160 | 767 | { |
paul@160 | 768 | return get_field(pll_reg, 0x1fff, Pll_multiplier) + 1; |
paul@160 | 769 | } |
paul@160 | 770 | |
paul@160 | 771 | void |
paul@160 | 772 | Cpm_x1600_chip::set_multiplier(uint32_t pll_reg, uint16_t multiplier) |
paul@160 | 773 | { |
paul@160 | 774 | set_field(pll_reg, 0x1fff, Pll_multiplier, multiplier - 1); |
paul@160 | 775 | } |
paul@160 | 776 | |
paul@160 | 777 | // Input (6-bit) divider. |
paul@160 | 778 | |
paul@160 | 779 | uint8_t |
paul@160 | 780 | Cpm_x1600_chip::get_input_division(uint32_t pll_reg) |
paul@160 | 781 | { |
paul@160 | 782 | return get_field(pll_reg, 0x3f, Pll_input_division) + 1; |
paul@160 | 783 | } |
paul@160 | 784 | |
paul@160 | 785 | void |
paul@160 | 786 | Cpm_x1600_chip::set_input_division(uint32_t pll_reg, uint8_t divider) |
paul@160 | 787 | { |
paul@160 | 788 | set_field(pll_reg, 0x3f, Pll_input_division, divider - 1); |
paul@160 | 789 | } |
paul@160 | 790 | |
paul@160 | 791 | // Output (dual 3-bit) dividers. |
paul@160 | 792 | |
paul@160 | 793 | uint8_t |
paul@160 | 794 | Cpm_x1600_chip::get_output_division(uint32_t pll_reg) |
paul@160 | 795 | { |
paul@160 | 796 | uint8_t d0 = get_field(pll_reg, 0x07, Pll_output_division0); |
paul@160 | 797 | uint8_t d1 = get_field(pll_reg, 0x07, Pll_output_division1); |
paul@160 | 798 | |
paul@160 | 799 | return d0 * d1; |
paul@160 | 800 | } |
paul@160 | 801 | |
paul@160 | 802 | void |
paul@160 | 803 | Cpm_x1600_chip::set_output_division(uint32_t pll_reg, uint8_t divider) |
paul@160 | 804 | { |
paul@160 | 805 | // Assert 1 as a minimum. |
paul@160 | 806 | // Divider 0 must be less than or equal to divider 1. |
paul@160 | 807 | |
paul@160 | 808 | uint8_t d0 = (uint8_t) floor(sqrt(divider ? divider : 1)); |
paul@160 | 809 | uint8_t d1 = divider / d0; |
paul@160 | 810 | |
paul@160 | 811 | set_field(pll_reg, 0x07, Pll_output_division0, d0); |
paul@160 | 812 | set_field(pll_reg, 0x07, Pll_output_division1, d1); |
paul@160 | 813 | } |
paul@160 | 814 | |
paul@160 | 815 | uint32_t |
paul@160 | 816 | Cpm_x1600_chip::get_pll_frequency(uint32_t pll_reg) |
paul@160 | 817 | { |
paul@160 | 818 | // Test for PLL enable and not PLL bypass. |
paul@160 | 819 | |
paul@160 | 820 | if (pll_enabled(pll_reg) && !pll_bypassed(pll_reg)) |
paul@160 | 821 | return (_exclk_freq * get_multiplier(pll_reg)) / |
paul@160 | 822 | (get_input_division(pll_reg) * get_output_division(pll_reg)); |
paul@160 | 823 | else |
paul@160 | 824 | return _exclk_freq; |
paul@160 | 825 | } |
paul@160 | 826 | |
paul@160 | 827 | void |
paul@160 | 828 | Cpm_x1600_chip::set_pll_parameters(uint32_t pll_reg, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider) |
paul@160 | 829 | { |
paul@160 | 830 | set_multiplier(pll_reg, multiplier); |
paul@160 | 831 | set_input_division(pll_reg, in_divider); |
paul@160 | 832 | set_output_division(pll_reg, out_divider); |
paul@160 | 833 | |
paul@160 | 834 | if (pll_enabled(pll_reg) && !pll_bypassed(pll_reg)) |
paul@160 | 835 | while (!have_pll(pll_reg)); |
paul@160 | 836 | } |
paul@160 | 837 | |
paul@160 | 838 | |
paul@160 | 839 | |
paul@161 | 840 | // Clock dividers. |
paul@160 | 841 | |
paul@161 | 842 | uint32_t |
paul@161 | 843 | Cpm_x1600_chip::get_divider(enum Clock_identifiers clock) |
paul@160 | 844 | { |
paul@161 | 845 | if (clock_desc[clock].divider_bit != Clock_divider_undefined) |
paul@161 | 846 | return get_field(clock_desc[clock].divider_reg, clock_desc[clock].divider_mask, |
paul@161 | 847 | clock_desc[clock].divider_bit) + 1; |
paul@161 | 848 | else |
paul@161 | 849 | return 1; |
paul@160 | 850 | } |
paul@160 | 851 | |
paul@161 | 852 | void |
paul@161 | 853 | Cpm_x1600_chip::set_divider(enum Clock_identifiers clock, uint32_t division) |
paul@160 | 854 | { |
paul@161 | 855 | if (clock_desc[clock].divider_bit == Clock_divider_undefined) |
paul@160 | 856 | return; |
paul@160 | 857 | |
paul@161 | 858 | change_enable(clock); |
paul@161 | 859 | set_field(clock_desc[clock].divider_reg, clock_desc[clock].divider_mask, |
paul@161 | 860 | clock_desc[clock].divider_bit, division - 1); |
paul@161 | 861 | wait_busy(clock); |
paul@161 | 862 | change_disable(clock); |
paul@160 | 863 | } |
paul@160 | 864 | |
paul@160 | 865 | |
paul@160 | 866 | |
paul@160 | 867 | // Clock sources. |
paul@160 | 868 | |
paul@160 | 869 | uint8_t |
paul@161 | 870 | Cpm_x1600_chip::get_source(enum Clock_identifiers clock) |
paul@160 | 871 | { |
paul@161 | 872 | if (clock_desc[clock].source_bit != Clock_source_undefined) |
paul@161 | 873 | return get_field(clock_desc[clock].source_reg, Source_mask, clock_desc[clock].source_bit); |
paul@161 | 874 | else |
paul@161 | 875 | return 0; |
paul@160 | 876 | } |
paul@160 | 877 | |
paul@160 | 878 | void |
paul@161 | 879 | Cpm_x1600_chip::set_source(enum Clock_identifiers clock, uint8_t source) |
paul@160 | 880 | { |
paul@161 | 881 | if (clock_desc[clock].source_bit == Clock_source_undefined) |
paul@160 | 882 | return; |
paul@160 | 883 | |
paul@161 | 884 | change_enable(clock); |
paul@161 | 885 | set_field(clock_desc[clock].source_reg, Source_mask, clock_desc[clock].source_bit, source); |
paul@161 | 886 | wait_busy(clock); |
paul@161 | 887 | change_disable(clock); |
paul@160 | 888 | } |
paul@160 | 889 | |
paul@160 | 890 | |
paul@160 | 891 | |
paul@161 | 892 | // Clock source frequencies. |
paul@161 | 893 | |
paul@161 | 894 | uint32_t |
paul@161 | 895 | Cpm_x1600_chip::get_input_frequency(enum Clock_input_identifiers clock) |
paul@161 | 896 | { |
paul@161 | 897 | struct Clock_input_desc desc = clock_input_desc[clock]; |
paul@161 | 898 | |
paul@161 | 899 | // Clocks with no inputs provide a frequency. |
paul@161 | 900 | |
paul@161 | 901 | if (desc.num_inputs == 0) |
paul@161 | 902 | { |
paul@161 | 903 | switch (clock) |
paul@161 | 904 | { |
paul@161 | 905 | case Clock_input_external: return _exclk_freq; |
paul@161 | 906 | default: return 0; |
paul@161 | 907 | } |
paul@161 | 908 | } |
paul@161 | 909 | |
paul@161 | 910 | // Of the input clocks, only PLLs have a single input. |
paul@160 | 911 | |
paul@161 | 912 | else if (desc.num_inputs == 1) |
paul@161 | 913 | { |
paul@161 | 914 | switch (clock) |
paul@161 | 915 | { |
paul@161 | 916 | case Clock_input_pll_A: return get_pll_frequency(Pll_control_A); |
paul@161 | 917 | case Clock_input_pll_E: return get_pll_frequency(Pll_control_E); |
paul@161 | 918 | case Clock_input_pll_M: return get_pll_frequency(Pll_control_M); |
paul@161 | 919 | default: return 0; |
paul@161 | 920 | } |
paul@161 | 921 | } |
paul@161 | 922 | |
paul@161 | 923 | // With multiple sources, obtain the selected source for the clock. |
paul@161 | 924 | |
paul@161 | 925 | uint8_t source = get_field(desc.source_reg, Source_mask, desc.source_bit); |
paul@161 | 926 | |
paul@161 | 927 | // Return the frequency of the source. |
paul@161 | 928 | |
paul@161 | 929 | if (source < desc.num_inputs) |
paul@161 | 930 | return get_input_frequency(desc.inputs[source]); |
paul@161 | 931 | else |
paul@161 | 932 | return 0; |
paul@160 | 933 | } |
paul@160 | 934 | |
paul@160 | 935 | uint32_t |
paul@161 | 936 | Cpm_x1600_chip::get_source_frequency(enum Clock_identifiers clock) |
paul@160 | 937 | { |
paul@161 | 938 | struct Clock_desc desc = clock_desc[clock]; |
paul@160 | 939 | |
paul@162 | 940 | if (desc.num_inputs == 0) |
paul@162 | 941 | { |
paul@162 | 942 | // Clocks may reference other clocks. |
paul@160 | 943 | |
paul@162 | 944 | if (desc.clock_input != Clock_undefined) |
paul@162 | 945 | return get_frequency(desc.clock_input); |
paul@162 | 946 | |
paul@162 | 947 | // Undefined clocks return zero. |
paul@162 | 948 | |
paul@162 | 949 | else |
paul@162 | 950 | return 0; |
paul@162 | 951 | } |
paul@160 | 952 | |
paul@161 | 953 | // Clocks with one source yield that input frequency. |
paul@160 | 954 | |
paul@161 | 955 | else if (desc.num_inputs == 1) |
paul@161 | 956 | return get_input_frequency(desc.inputs[0]); |
paul@161 | 957 | |
paul@161 | 958 | // With multiple sources, obtain the selected source for the clock. |
paul@161 | 959 | |
paul@161 | 960 | uint8_t source = get_source(clock); |
paul@160 | 961 | |
paul@161 | 962 | // Return the frequency of the source. |
paul@160 | 963 | |
paul@161 | 964 | if (source < desc.num_inputs) |
paul@161 | 965 | return get_input_frequency(desc.inputs[source]); |
paul@161 | 966 | else |
paul@161 | 967 | return 0; |
paul@160 | 968 | } |
paul@160 | 969 | |
paul@160 | 970 | |
paul@160 | 971 | |
paul@161 | 972 | // Output clock frequencies. |
paul@161 | 973 | |
paul@160 | 974 | uint32_t |
paul@160 | 975 | Cpm_x1600_chip::get_frequency(enum Clock_identifiers clock) |
paul@160 | 976 | { |
paul@161 | 977 | return get_source_frequency(clock) / get_divider(clock); |
paul@160 | 978 | } |
paul@160 | 979 | |
paul@160 | 980 | void |
paul@160 | 981 | Cpm_x1600_chip::set_frequency(enum Clock_identifiers clock, uint32_t frequency) |
paul@160 | 982 | { |
paul@160 | 983 | switch (clock) |
paul@160 | 984 | { |
paul@160 | 985 | // The pixel frequency is based on the selected clock source (SCLK_A, MPLL or |
paul@160 | 986 | // EPLL). |
paul@160 | 987 | |
paul@160 | 988 | case Clock_lcd_pixel: |
paul@160 | 989 | |
paul@160 | 990 | // Switch to the MPLL and attempt to set the divider. |
paul@160 | 991 | |
paul@161 | 992 | set_source(Clock_lcd_pixel, Source_mME_pll_M); |
paul@160 | 993 | pll_enable(Pll_control_M); |
paul@161 | 994 | set_divider(Clock_lcd_pixel, get_source_frequency(clock) / frequency); |
paul@160 | 995 | break; |
paul@160 | 996 | |
paul@160 | 997 | default: |
paul@160 | 998 | break; |
paul@160 | 999 | } |
paul@160 | 1000 | } |
paul@160 | 1001 | |
paul@160 | 1002 | |
paul@160 | 1003 | |
paul@160 | 1004 | // C language interface functions. |
paul@160 | 1005 | |
paul@160 | 1006 | void |
paul@160 | 1007 | *x1600_cpm_init(l4_addr_t cpm_base) |
paul@160 | 1008 | { |
paul@160 | 1009 | /* Initialise the clock and power management peripheral with the |
paul@160 | 1010 | register memory region and a 24MHz EXCLK frequency. */ |
paul@160 | 1011 | |
paul@160 | 1012 | return (void *) new Cpm_x1600_chip(cpm_base, 24000000); |
paul@160 | 1013 | } |
paul@160 | 1014 | |
paul@160 | 1015 | int |
paul@160 | 1016 | x1600_cpm_have_clock(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1017 | { |
paul@160 | 1018 | return static_cast<Cpm_x1600_chip *>(cpm)->have_clock(clock); |
paul@160 | 1019 | } |
paul@160 | 1020 | |
paul@160 | 1021 | void |
paul@160 | 1022 | x1600_cpm_start_clock(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1023 | { |
paul@160 | 1024 | static_cast<Cpm_x1600_chip *>(cpm)->start_clock(clock); |
paul@160 | 1025 | } |
paul@160 | 1026 | |
paul@160 | 1027 | void |
paul@160 | 1028 | x1600_cpm_stop_clock(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1029 | { |
paul@160 | 1030 | static_cast<Cpm_x1600_chip *>(cpm)->stop_clock(clock); |
paul@160 | 1031 | } |
paul@160 | 1032 | |
paul@160 | 1033 | |
paul@160 | 1034 | |
paul@161 | 1035 | uint32_t |
paul@161 | 1036 | x1600_cpm_get_divider(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1037 | { |
paul@161 | 1038 | return static_cast<Cpm_x1600_chip *>(cpm)->get_divider(clock); |
paul@160 | 1039 | } |
paul@160 | 1040 | |
paul@161 | 1041 | void |
paul@161 | 1042 | x1600_cpm_set_divider(void *cpm, enum Clock_identifiers clock, uint32_t divider) |
paul@160 | 1043 | { |
paul@161 | 1044 | return static_cast<Cpm_x1600_chip *>(cpm)->set_divider(clock, divider); |
paul@160 | 1045 | } |
paul@160 | 1046 | |
paul@160 | 1047 | |
paul@160 | 1048 | |
paul@160 | 1049 | uint8_t |
paul@161 | 1050 | x1600_cpm_get_source(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1051 | { |
paul@161 | 1052 | return static_cast<Cpm_x1600_chip *>(cpm)->get_source(clock); |
paul@160 | 1053 | } |
paul@160 | 1054 | |
paul@160 | 1055 | void |
paul@161 | 1056 | x1600_cpm_set_source(void *cpm, enum Clock_identifiers clock, uint8_t source) |
paul@160 | 1057 | { |
paul@161 | 1058 | static_cast<Cpm_x1600_chip *>(cpm)->set_source(clock, source); |
paul@160 | 1059 | } |
paul@160 | 1060 | |
paul@160 | 1061 | |
paul@160 | 1062 | |
paul@160 | 1063 | uint32_t |
paul@161 | 1064 | x1600_cpm_get_source_frequency(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1065 | { |
paul@161 | 1066 | return static_cast<Cpm_x1600_chip *>(cpm)->get_source_frequency(clock); |
paul@160 | 1067 | } |
paul@160 | 1068 | |
paul@160 | 1069 | |
paul@160 | 1070 | |
paul@160 | 1071 | uint32_t |
paul@160 | 1072 | x1600_cpm_get_frequency(void *cpm, enum Clock_identifiers clock) |
paul@160 | 1073 | { |
paul@160 | 1074 | return static_cast<Cpm_x1600_chip *>(cpm)->get_frequency(clock); |
paul@160 | 1075 | } |
paul@160 | 1076 | |
paul@160 | 1077 | void |
paul@160 | 1078 | x1600_cpm_set_frequency(void *cpm, enum Clock_identifiers clock, uint32_t frequency) |
paul@160 | 1079 | { |
paul@160 | 1080 | static_cast<Cpm_x1600_chip *>(cpm)->set_frequency(clock, frequency); |
paul@160 | 1081 | } |
paul@160 | 1082 | |
paul@161 | 1083 | |
paul@161 | 1084 | |
paul@160 | 1085 | void |
paul@160 | 1086 | x1600_cpm_set_mpll_parameters(void *cpm, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider) |
paul@160 | 1087 | { |
paul@160 | 1088 | static_cast<Cpm_x1600_chip *>(cpm)->set_pll_parameters(Pll_control_M, multiplier, in_divider, out_divider); |
paul@160 | 1089 | } |