paul@0 | 1 | /* |
paul@0 | 2 | * Clock and power management. This exposes the combined functionality |
paul@0 | 3 | * provided by the jz4780 and related SoCs. The power management |
paul@0 | 4 | * functionality could be exposed using a separate driver. |
paul@0 | 5 | * |
paul@277 | 6 | * Copyright (C) 2017, 2018, 2020, 2021, 2023, |
paul@277 | 7 | * 2024 Paul Boddie <paul@boddie.org.uk> |
paul@0 | 8 | * |
paul@0 | 9 | * This program is free software; you can redistribute it and/or |
paul@0 | 10 | * modify it under the terms of the GNU General Public License as |
paul@0 | 11 | * published by the Free Software Foundation; either version 2 of |
paul@0 | 12 | * the License, or (at your option) any later version. |
paul@0 | 13 | * |
paul@0 | 14 | * This program is distributed in the hope that it will be useful, |
paul@0 | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@0 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@0 | 17 | * GNU General Public License for more details. |
paul@0 | 18 | * |
paul@0 | 19 | * You should have received a copy of the GNU General Public License |
paul@0 | 20 | * along with this program; if not, write to the Free Software |
paul@0 | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@0 | 22 | * Boston, MA 02110-1301, USA |
paul@0 | 23 | */ |
paul@0 | 24 | |
paul@0 | 25 | #include <l4/devices/hw_mmio_register_block.h> |
paul@0 | 26 | #include "cpm-jz4780.h" |
paul@0 | 27 | |
paul@0 | 28 | |
paul@0 | 29 | |
paul@211 | 30 | // Register locations. |
paul@211 | 31 | |
paul@0 | 32 | enum Regs : unsigned |
paul@0 | 33 | { |
paul@0 | 34 | Clock_control = 0x000, // CPCCR |
paul@0 | 35 | Low_power_control = 0x004, // LCR |
paul@211 | 36 | Clock_gate0 = 0x020, // CLKGR0 |
paul@211 | 37 | Clock_gate1 = 0x028, // CLKGR1 |
paul@211 | 38 | Sleep_control = 0x024, // OPCR (oscillator and power control) |
paul@211 | 39 | Clock_status = 0x0d4, // CPCSR |
paul@211 | 40 | |
paul@211 | 41 | Divider_bch = 0x0ac, // BCHCDR |
paul@211 | 42 | Divider_cim = 0x07c, // CIMCDR |
paul@211 | 43 | Divider_ddr = 0x02c, // DDRCDR |
paul@211 | 44 | Divider_gpu = 0x088, // GPUCDR |
paul@211 | 45 | Divider_hdmi = 0x08c, // HDMICDR |
paul@211 | 46 | Divider_i2s0 = 0x060, // I2SCDR |
paul@211 | 47 | Divider_i2s1 = 0x0a0, // I2S1CDR |
paul@211 | 48 | Divider_lcd0 = 0x054, // LP0CDR |
paul@211 | 49 | Divider_lcd1 = 0x064, // LP1CDR |
paul@211 | 50 | Divider_msc0 = 0x068, // MSC0CDR |
paul@211 | 51 | Divider_msc1 = 0x0a4, // MSC1CDR |
paul@211 | 52 | Divider_msc2 = 0x0a8, // MSC2CDR |
paul@211 | 53 | Divider_pcm = 0x084, // PCMCDR |
paul@211 | 54 | Divider_ssi = 0x074, // SSICDR |
paul@211 | 55 | Divider_uhc = 0x06c, // UHCCDR |
paul@211 | 56 | Divider_vpu = 0x030, // VPUCDR |
paul@211 | 57 | |
paul@211 | 58 | Cpm_interrupt = 0x0b0, // CPM_INTR |
paul@211 | 59 | Cpm_interrupt_en = 0x0b4, // CPM_INTRE |
paul@211 | 60 | Cpm_scratch = 0x034, // CPSPR |
paul@211 | 61 | Cpm_scratch_prot = 0x038, // CPSPPR |
paul@211 | 62 | |
paul@211 | 63 | Usb_param_control0 = 0x03c, // USBPCR |
paul@211 | 64 | Usb_reset_detect = 0x040, // USBRDT |
paul@211 | 65 | Usb_vbus_jitter = 0x044, // USBVBFIL |
paul@211 | 66 | Usb_param_control1 = 0x048, // USBPCR1 |
paul@211 | 67 | |
paul@0 | 68 | Pll_control = 0x00c, // CPPCR |
paul@0 | 69 | Pll_control_A = 0x010, // CPAPCR |
paul@0 | 70 | Pll_control_M = 0x014, // CPMPCR |
paul@0 | 71 | Pll_control_E = 0x018, // CPEPCR |
paul@0 | 72 | Pll_control_V = 0x01c, // CPVPCR |
paul@0 | 73 | }; |
paul@0 | 74 | |
paul@0 | 75 | |
paul@0 | 76 | |
paul@211 | 77 | // Register field definitions. |
paul@0 | 78 | |
paul@211 | 79 | static Field Clock_source_main (Clock_control, 3, 30), // SEL_SRC (output to SCLK_A) |
paul@211 | 80 | Clock_source_cpu (Clock_control, 3, 28), // SEL_CPLL (output to CCLK) |
paul@211 | 81 | Clock_source_hclock0 (Clock_control, 3, 26), // SEL_H0PLL (output to AHB0) |
paul@211 | 82 | Clock_source_hclock2 (Clock_control, 3, 24), // SEL_H2PLL (output to AHB2) |
paul@211 | 83 | Clock_source_bch (Divider_bch, 3, 30), // BPCS |
paul@211 | 84 | Clock_source_cim (Divider_cim, 1, 31), // CIMPCS |
paul@211 | 85 | Clock_source_ddr (Divider_ddr, 3, 30), // DCS |
paul@211 | 86 | Clock_source_gpu (Divider_gpu, 3, 30), // GPCS |
paul@211 | 87 | Clock_source_hdmi (Divider_hdmi, 3, 30), // HPCS |
paul@211 | 88 | Clock_source_i2s0 (Divider_i2s0, 3, 30), // I2CS, I2PCS |
paul@211 | 89 | Clock_source_i2s1 (Divider_i2s1, 3, 30), // I2CS, I2PCS |
paul@211 | 90 | Clock_source_lcd0 (Divider_lcd0, 3, 30), // LPCS |
paul@211 | 91 | Clock_source_lcd1 (Divider_lcd1, 3, 30), // LPCS |
paul@211 | 92 | Clock_source_msc (Divider_msc0, 3, 30), // MPCS |
paul@211 | 93 | Clock_source_pcm (Divider_pcm, 7, 29), // PCMS, PCMPCS |
paul@294 | 94 | Clock_source_rtc (Sleep_control, 0x01, 2), // ERCS |
paul@219 | 95 | Clock_source_ssi (Divider_ssi, 3, 30), // SCS, SPCS |
paul@211 | 96 | Clock_source_uhc (Divider_uhc, 3, 30), // UHCS |
paul@243 | 97 | Clock_source_usb_phy (Usb_param_control1, 3, 24), // REFCLKDIV |
paul@211 | 98 | Clock_source_vpu (Divider_vpu, 3, 30), // VCS |
paul@128 | 99 | |
paul@211 | 100 | Clock_busy_cpu (Clock_status, 1, 0), |
paul@211 | 101 | Clock_busy_hclock0 (Clock_status, 1, 1), |
paul@211 | 102 | Clock_busy_hclock2 (Clock_status, 1, 2), |
paul@211 | 103 | Clock_busy_bch (Divider_bch, 1, 28), |
paul@211 | 104 | Clock_busy_cim (Divider_cim, 1, 29), |
paul@211 | 105 | Clock_busy_ddr (Divider_ddr, 1, 28), |
paul@211 | 106 | Clock_busy_gpu (Divider_gpu, 1, 28), |
paul@211 | 107 | Clock_busy_hdmi (Divider_hdmi, 1, 28), |
paul@211 | 108 | Clock_busy_i2s0 (Divider_i2s0, 1, 28), |
paul@211 | 109 | Clock_busy_i2s1 (Divider_i2s1, 1, 28), |
paul@211 | 110 | Clock_busy_lcd0 (Divider_lcd0, 1, 27), |
paul@211 | 111 | Clock_busy_lcd1 (Divider_lcd1, 1, 27), |
paul@211 | 112 | Clock_busy_msc0 (Divider_msc0, 1, 28), |
paul@211 | 113 | Clock_busy_msc1 (Divider_msc1, 1, 28), |
paul@211 | 114 | Clock_busy_msc2 (Divider_msc2, 1, 28), |
paul@211 | 115 | Clock_busy_pcm (Divider_pcm, 1, 27), |
paul@211 | 116 | Clock_busy_ssi (Divider_ssi, 1, 28), |
paul@211 | 117 | Clock_busy_uhc (Divider_uhc, 1, 28), |
paul@211 | 118 | Clock_busy_vpu (Divider_vpu, 1, 28), |
paul@128 | 119 | |
paul@211 | 120 | Clock_change_enable_cpu (Clock_control, 1, 22), |
paul@211 | 121 | Clock_change_enable_ahb0 (Clock_control, 1, 21), |
paul@211 | 122 | Clock_change_enable_ahb2 (Clock_control, 1, 20), |
paul@211 | 123 | Clock_change_enable_bch (Divider_bch, 1, 29), |
paul@211 | 124 | Clock_change_enable_cim (Divider_cim, 1, 30), |
paul@211 | 125 | Clock_change_enable_ddr (Divider_ddr, 1, 29), |
paul@211 | 126 | Clock_change_enable_gpu (Divider_gpu, 1, 29), |
paul@211 | 127 | Clock_change_enable_hdmi (Divider_hdmi, 1, 29), |
paul@211 | 128 | Clock_change_enable_i2s0 (Divider_i2s0, 1, 29), |
paul@211 | 129 | Clock_change_enable_i2s1 (Divider_i2s1, 1, 29), |
paul@211 | 130 | Clock_change_enable_lcd0 (Divider_lcd0, 1, 28), |
paul@211 | 131 | Clock_change_enable_lcd1 (Divider_lcd1, 1, 28), |
paul@211 | 132 | Clock_change_enable_msc0 (Divider_msc0, 1, 29), |
paul@211 | 133 | Clock_change_enable_msc1 (Divider_msc1, 1, 29), |
paul@211 | 134 | Clock_change_enable_msc2 (Divider_msc2, 1, 29), |
paul@211 | 135 | Clock_change_enable_pcm (Divider_pcm, 1, 28), |
paul@211 | 136 | Clock_change_enable_ssi (Divider_ssi, 1, 29), |
paul@211 | 137 | Clock_change_enable_uhc (Divider_uhc, 1, 29), |
paul@211 | 138 | Clock_change_enable_vpu (Divider_vpu, 1, 29), |
paul@0 | 139 | |
paul@211 | 140 | Clock_divider_cpu (Clock_control, 0x0f, 0), // CDIV |
paul@211 | 141 | Clock_divider_hclock0 (Clock_control, 0x0f, 8), // H0DIV (fast AHB peripherals) |
paul@211 | 142 | Clock_divider_hclock2 (Clock_control, 0x0f, 12), // H2DIV (fast AHB peripherals) |
paul@211 | 143 | Clock_divider_l2cache (Clock_control, 0x0f, 4), // L2CDIV |
paul@211 | 144 | Clock_divider_pclock (Clock_control, 0x0f, 16), // PDIV (slow APB peripherals) |
paul@211 | 145 | Clock_divider_bch (Divider_bch, 0x0f, 0), // BCHCDR |
paul@211 | 146 | Clock_divider_cim (Divider_cim, 0xff, 0), // CIMCDR |
paul@211 | 147 | Clock_divider_ddr (Divider_ddr, 0x0f, 0), // DDRCDR |
paul@211 | 148 | Clock_divider_gpu (Divider_gpu, 0x0f, 0), // GPUCDR |
paul@211 | 149 | Clock_divider_hdmi (Divider_hdmi, 0xff, 0), // HDMICDR |
paul@211 | 150 | Clock_divider_i2s0 (Divider_i2s0, 0xff, 0), // I2SCDR |
paul@211 | 151 | Clock_divider_i2s1 (Divider_i2s1, 0xff, 0), // I2SCDR |
paul@211 | 152 | Clock_divider_lcd0 (Divider_lcd0, 0xff, 0), // LPCDR |
paul@211 | 153 | Clock_divider_lcd1 (Divider_lcd1, 0xff, 0), // LPCDR |
paul@211 | 154 | Clock_divider_msc0 (Divider_msc0, 0xff, 0), // MSC0CDR |
paul@211 | 155 | Clock_divider_msc1 (Divider_msc1, 0xff, 0), // MSC1CDR |
paul@211 | 156 | Clock_divider_msc2 (Divider_msc2, 0xff, 0), // MSC2CDR |
paul@211 | 157 | Clock_divider_pcm (Divider_pcm, 0xff, 0), // PCMCDR |
paul@211 | 158 | Clock_divider_ssi (Divider_ssi, 0xff, 0), // SSICDR |
paul@211 | 159 | Clock_divider_uhc (Divider_uhc, 0xff, 0), // UHCCDR |
paul@211 | 160 | Clock_divider_vpu (Divider_vpu, 0x0f, 0), // VPUCDR |
paul@0 | 161 | |
paul@211 | 162 | Clock_gate_main (Clock_control, 1, 23, true), // GATE_SCLKA |
paul@211 | 163 | Clock_gate_ddr (Clock_gate0, 3, 30, true), // DDR1, DDR0 |
paul@211 | 164 | Clock_gate_ipu (Clock_gate0, 1, 29, true), // IPU |
paul@211 | 165 | Clock_gate_lcd (Clock_gate0, 3, 27, true), // LCD, TVE |
paul@211 | 166 | Clock_gate_cim (Clock_gate0, 1, 26, true), // CIM |
paul@211 | 167 | Clock_gate_i2c2 (Clock_gate0, 1, 25, true), // SMB2 |
paul@211 | 168 | Clock_gate_uhc (Clock_gate0, 1, 24, true), // UHC |
paul@211 | 169 | Clock_gate_mac (Clock_gate0, 1, 23, true), // MAC |
paul@211 | 170 | Clock_gate_gps (Clock_gate0, 1, 22, true), // GPS |
paul@211 | 171 | Clock_gate_dma (Clock_gate0, 1, 21, true), // PDMA |
paul@219 | 172 | //Clock_gate_ssi2 (Clock_gate0, 1, 20, true), // SSI2 |
paul@211 | 173 | Clock_gate_ssi1 (Clock_gate0, 1, 19, true), // SSI1 |
paul@211 | 174 | Clock_gate_uart3 (Clock_gate0, 1, 18, true), // UART3 |
paul@211 | 175 | Clock_gate_uart2 (Clock_gate0, 1, 17, true), // UART2 |
paul@211 | 176 | Clock_gate_uart1 (Clock_gate0, 1, 16, true), // UART1 |
paul@211 | 177 | Clock_gate_uart0 (Clock_gate0, 1, 15, true), // UART0 |
paul@211 | 178 | Clock_gate_sadc (Clock_gate0, 1, 14, true), // SADC |
paul@211 | 179 | Clock_gate_kbc (Clock_gate0, 1, 13, true), // KBC |
paul@211 | 180 | Clock_gate_msc2 (Clock_gate0, 1, 12, true), // MSC2 |
paul@211 | 181 | Clock_gate_msc1 (Clock_gate0, 1, 11, true), // MSC1 |
paul@211 | 182 | Clock_gate_owi (Clock_gate0, 1, 10, true), // OWI |
paul@211 | 183 | Clock_gate_tssi0 (Clock_gate0, 1, 9, true), // TSSI0 |
paul@211 | 184 | Clock_gate_aic0 (Clock_gate0, 1, 8, true), // AIC0 |
paul@211 | 185 | Clock_gate_scc (Clock_gate0, 1, 7, true), // SCC |
paul@211 | 186 | Clock_gate_i2c1 (Clock_gate0, 1, 6, true), // SMB1 |
paul@211 | 187 | Clock_gate_i2c0 (Clock_gate0, 1, 5, true), // SMB0 |
paul@211 | 188 | Clock_gate_ssi0 (Clock_gate0, 1, 4, true), // SSI0 |
paul@211 | 189 | Clock_gate_msc0 (Clock_gate0, 1, 3, true), // MSC0 |
paul@211 | 190 | Clock_gate_otg0 (Clock_gate0, 1, 2, true), // OTG0 |
paul@211 | 191 | Clock_gate_bch (Clock_gate0, 1, 1, true), // BCH |
paul@211 | 192 | Clock_gate_nemc (Clock_gate0, 1, 0, true), // NEMC |
paul@241 | 193 | Clock_gate_cpu1 (Clock_gate1, 1, 15, true), // P1 |
paul@211 | 194 | Clock_gate_x2d (Clock_gate1, 1, 14, true), // X2D |
paul@211 | 195 | Clock_gate_des (Clock_gate1, 1, 13, true), // DES |
paul@211 | 196 | Clock_gate_i2c4 (Clock_gate1, 1, 12, true), // SMB4 |
paul@211 | 197 | Clock_gate_ahb_mon (Clock_gate1, 1, 11, true), // AHB_MON |
paul@211 | 198 | Clock_gate_uart4 (Clock_gate1, 1, 10, true), // UART4 |
paul@211 | 199 | Clock_gate_hdmi (Clock_gate1, 1, 9, true), // HDMI |
paul@211 | 200 | Clock_gate_otg1 (Clock_gate1, 1, 8, true), // OTG1 |
paul@211 | 201 | Clock_gate_gpvlc (Clock_gate1, 1, 7, true), // GPVLC |
paul@211 | 202 | Clock_gate_aic1 (Clock_gate1, 1, 6, true), // AIC1 |
paul@211 | 203 | Clock_gate_compress (Clock_gate1, 1, 5, true), // COMPRESS |
paul@211 | 204 | Clock_gate_gpu (Clock_gate1, 1, 4, true), // GPU |
paul@211 | 205 | Clock_gate_pcm (Clock_gate1, 1, 3, true), // PCM |
paul@211 | 206 | Clock_gate_vpu (Clock_gate1, 1, 2, true), // VPU |
paul@211 | 207 | Clock_gate_tssi1 (Clock_gate1, 1, 1, true), // TSSI1 |
paul@211 | 208 | Clock_gate_i2c3 (Clock_gate1, 1, 0, true), // I2C3 |
paul@0 | 209 | |
paul@211 | 210 | Pll_enable_A (Pll_control_A, 1, 0), // APLLEN |
paul@211 | 211 | Pll_enable_E (Pll_control_E, 1, 0), // EPLLEN |
paul@211 | 212 | Pll_enable_M (Pll_control_M, 1, 0), // MPLLEN |
paul@211 | 213 | Pll_enable_V (Pll_control_V, 1, 0), // VPLLEN |
paul@0 | 214 | |
paul@211 | 215 | Pll_stable_A (Pll_control_A, 1, 4), // APLL_ON |
paul@211 | 216 | Pll_stable_E (Pll_control_E, 1, 4), // EPLL_ON |
paul@211 | 217 | Pll_stable_M (Pll_control_M, 1, 4), // MPLL_ON |
paul@211 | 218 | Pll_stable_V (Pll_control_V, 1, 4), // VPLL_ON |
paul@211 | 219 | |
paul@211 | 220 | Pll_bypass_A (Pll_control_A, 1, 1), // APLL_BP |
paul@211 | 221 | Pll_bypass_E (Pll_control_E, 1, 1), // EPLL_BP |
paul@211 | 222 | Pll_bypass_M (Pll_control_M, 1, 1), // MPLL_BP |
paul@211 | 223 | Pll_bypass_V (Pll_control_V, 1, 1), // VPLL_BP |
paul@0 | 224 | |
paul@247 | 225 | // Multipliers and dividers yield 1-based values. |
paul@247 | 226 | |
paul@277 | 227 | Pll_multiplier_A (Pll_control_A, 0x1fff, 19, false, 1), // APLLM |
paul@277 | 228 | Pll_multiplier_E (Pll_control_E, 0x1fff, 19, false, 1), // EPLLM |
paul@277 | 229 | Pll_multiplier_M (Pll_control_M, 0x1fff, 19, false, 1), // MPLLM |
paul@277 | 230 | Pll_multiplier_V (Pll_control_V, 0x1fff, 19, false, 1), // VPLLM |
paul@0 | 231 | |
paul@277 | 232 | Pll_input_division_A (Pll_control_A, 0x3f, 13, false, 1), // APLLN |
paul@277 | 233 | Pll_input_division_E (Pll_control_E, 0x3f, 13, false, 1), // EPLLN |
paul@277 | 234 | Pll_input_division_M (Pll_control_M, 0x3f, 13, false, 1), // MPLLN |
paul@277 | 235 | Pll_input_division_V (Pll_control_V, 0x3f, 13, false, 1), // VPLLN |
paul@0 | 236 | |
paul@277 | 237 | Pll_output_division_A (Pll_control_A, 0x0f, 9, false, 1), // APLLOD |
paul@277 | 238 | Pll_output_division_E (Pll_control_E, 0x0f, 9, false, 1), // EPLLOD |
paul@277 | 239 | Pll_output_division_M (Pll_control_M, 0x0f, 9, false, 1), // MPLLOD |
paul@277 | 240 | Pll_output_division_V (Pll_control_V, 0x0f, 9, false, 1); // VPLLOD |
paul@0 | 241 | |
paul@0 | 242 | |
paul@0 | 243 | |
paul@211 | 244 | // Multiplexer instances. |
paul@0 | 245 | |
paul@211 | 246 | #define Clocks(...) ((enum Clock_identifiers []) {__VA_ARGS__}) |
paul@243 | 247 | #define Specific(CLOCK) ((enum Clock_identifiers) (CLOCK)) |
paul@0 | 248 | |
paul@243 | 249 | static Mux mux_external (Clock_external), |
paul@0 | 250 | |
paul@211 | 251 | // Clocks being propagated to others. |
paul@0 | 252 | |
paul@211 | 253 | mux_clock_ssi (Clock_ssi), |
paul@211 | 254 | mux_clock_msc (Clock_msc), |
paul@211 | 255 | mux_hclock2 (Clock_hclock2), |
paul@211 | 256 | mux_hclock2_pclock (Clock_hclock2_pclock), |
paul@211 | 257 | mux_pclock (Clock_pclock), |
paul@67 | 258 | |
paul@211 | 259 | // Main bus and peripheral clock sources. |
paul@0 | 260 | |
paul@239 | 261 | mux_ahb2 (4, Clocks(Clock_none, Clock_main, Clock_pll_M, Clock_rtc_external)), |
paul@211 | 262 | mux_core (4, Clocks(Clock_none, Clock_main, Clock_pll_M, Clock_pll_E)), |
paul@239 | 263 | mux_main (4, Clocks(Clock_none, Clock_pll_A, Clock_external, Clock_rtc_external)), |
paul@0 | 264 | |
paul@211 | 265 | // Memory and device clock sources. |
paul@0 | 266 | |
paul@211 | 267 | mux_cim (2, Clocks(Clock_main, Clock_pll_M)), |
paul@211 | 268 | mux_dev (3, Clocks(Clock_none, Clock_main, Clock_pll_M)), |
paul@211 | 269 | mux_lcd (3, Clocks(Clock_main, Clock_pll_M, Clock_pll_V)), |
paul@211 | 270 | mux_usb (3, Clocks(Clock_main, Clock_pll_M, Clock_pll_E /* , OTG PHY */)), |
paul@243 | 271 | mux_usb_phy (4, Clocks(Specific(Clock_usb_phy_12MHz), Specific(Clock_usb_phy_24MHz), |
paul@243 | 272 | Specific(Clock_usb_phy_48MHz), Specific(Clock_usb_phy_19_2MHz))), |
paul@0 | 273 | |
paul@211 | 274 | // Clock selectors involving the external clock. |
paul@0 | 275 | |
paul@211 | 276 | mux_i2s (4, Clocks(Clock_external, Clock_external, Clock_main, Clock_pll_E)), |
paul@211 | 277 | mux_pcm (8, Clocks(Clock_external, Clock_external, Clock_external, Clock_external, |
paul@211 | 278 | Clock_main, Clock_pll_M, Clock_pll_E, Clock_pll_V)), |
paul@294 | 279 | mux_rtc (2, Clocks(Clock_external_div, Clock_rtc_external)), |
paul@211 | 280 | mux_ssi (4, Clocks(Clock_external, Clock_external, Clock_main, Clock_pll_M)); |
paul@0 | 281 | |
paul@0 | 282 | |
paul@0 | 283 | |
paul@211 | 284 | // Clock instances. |
paul@0 | 285 | |
paul@211 | 286 | static Clock_null clock_none; |
paul@62 | 287 | |
paul@239 | 288 | static Clock_passive clock_external(48000000), |
paul@243 | 289 | clock_rtc_external(32768), |
paul@243 | 290 | clock_usb_phy_12MHz(12000000), |
paul@243 | 291 | clock_usb_phy_19_2MHz(19200000), |
paul@243 | 292 | clock_usb_phy_24MHz(24000000), |
paul@243 | 293 | clock_usb_phy_48MHz(48000000); |
paul@0 | 294 | |
paul@0 | 295 | |
paul@0 | 296 | |
paul@211 | 297 | // Note the use of extra parentheses due to the annoying C++ "most vexing parse" |
paul@211 | 298 | // problem. See: https://en.wikipedia.org/wiki/Most_vexing_parse |
paul@0 | 299 | |
paul@211 | 300 | static Clock clock_ahb_mon((Source(mux_external)), (Control(Clock_gate_ahb_mon))), |
paul@211 | 301 | |
paul@211 | 302 | clock_compress((Source(mux_external)), (Control(Clock_gate_compress))), |
paul@0 | 303 | |
paul@211 | 304 | clock_des((Source(mux_external)), (Control(Clock_gate_des))), |
paul@211 | 305 | |
paul@211 | 306 | clock_dma((Source(mux_external)), (Control(Clock_gate_dma))), |
paul@211 | 307 | |
paul@211 | 308 | clock_gps((Source(mux_external)), (Control(Clock_gate_gps))), |
paul@211 | 309 | |
paul@211 | 310 | clock_gpvlc((Source(mux_external)), (Control(Clock_gate_gpvlc))), |
paul@211 | 311 | |
paul@211 | 312 | clock_i2c0((Source(mux_pclock)), (Control(Clock_gate_i2c0))), |
paul@0 | 313 | |
paul@211 | 314 | clock_i2c1((Source(mux_pclock)), (Control(Clock_gate_i2c1))), |
paul@211 | 315 | |
paul@211 | 316 | clock_i2c2((Source(mux_pclock)), (Control(Clock_gate_i2c2))), |
paul@211 | 317 | |
paul@211 | 318 | clock_i2c3((Source(mux_pclock)), (Control(Clock_gate_i2c3))), |
paul@211 | 319 | |
paul@211 | 320 | clock_i2c4((Source(mux_pclock)), (Control(Clock_gate_i2c4))), |
paul@0 | 321 | |
paul@211 | 322 | clock_i2s0(Source(mux_i2s, Clock_source_i2s0), Control(Clock_gate_aic0)), |
paul@211 | 323 | |
paul@211 | 324 | clock_i2s1(Source(mux_i2s, Clock_source_i2s1), Control(Clock_gate_aic1)), |
paul@211 | 325 | |
paul@211 | 326 | clock_ipu((Source(mux_external)), (Control(Clock_gate_ipu))), |
paul@211 | 327 | |
paul@211 | 328 | clock_kbc((Source(mux_external)), (Control(Clock_gate_kbc))), |
paul@211 | 329 | |
paul@211 | 330 | clock_lcd((Source(mux_external)), (Control(Clock_gate_lcd))), |
paul@0 | 331 | |
paul@211 | 332 | clock_main(Source(mux_main, Clock_source_main), Control(Clock_gate_main)), |
paul@211 | 333 | |
paul@211 | 334 | clock_mac((Source(mux_external)), (Control(Clock_gate_mac))), |
paul@211 | 335 | |
paul@211 | 336 | clock_msc((Source(mux_dev, Clock_source_msc))), |
paul@211 | 337 | |
paul@211 | 338 | clock_nemc((Source(mux_hclock2)), (Control(Clock_gate_nemc))), |
paul@0 | 339 | |
paul@211 | 340 | clock_otg0((Source(mux_external)), (Control(Clock_gate_otg0))), |
paul@211 | 341 | |
paul@211 | 342 | clock_otg1((Source(mux_external)), (Control(Clock_gate_otg1))), |
paul@211 | 343 | |
paul@211 | 344 | clock_owi((Source(mux_external)), (Control(Clock_gate_owi))), |
paul@211 | 345 | |
paul@294 | 346 | clock_rtc(Source(mux_rtc, Clock_source_rtc)), |
paul@294 | 347 | |
paul@211 | 348 | clock_sadc((Source(mux_external)), (Control(Clock_gate_sadc))), |
paul@211 | 349 | |
paul@211 | 350 | clock_scc((Source(mux_external)), (Control(Clock_gate_scc))), |
paul@0 | 351 | |
paul@211 | 352 | clock_tssi0((Source(mux_external)), (Control(Clock_gate_tssi0))), |
paul@211 | 353 | |
paul@211 | 354 | clock_tssi1((Source(mux_external)), (Control(Clock_gate_tssi1))), |
paul@211 | 355 | |
paul@211 | 356 | clock_uart0((Source(mux_external)), (Control(Clock_gate_uart0))), |
paul@211 | 357 | |
paul@211 | 358 | clock_uart1((Source(mux_external)), (Control(Clock_gate_uart1))), |
paul@211 | 359 | |
paul@211 | 360 | clock_uart2((Source(mux_external)), (Control(Clock_gate_uart2))), |
paul@0 | 361 | |
paul@211 | 362 | clock_uart3((Source(mux_external)), (Control(Clock_gate_uart3))), |
paul@211 | 363 | |
paul@215 | 364 | clock_uart4((Source(mux_external)), (Control(Clock_gate_uart4))), |
paul@215 | 365 | |
paul@243 | 366 | clock_usb_phy(Source(mux_usb_phy, Clock_source_usb_phy)), |
paul@243 | 367 | |
paul@211 | 368 | clock_x2d((Source(mux_external)), (Control(Clock_gate_x2d))), |
paul@0 | 369 | |
paul@211 | 370 | // Special parent clock for hclock2 and pclock. |
paul@211 | 371 | |
paul@211 | 372 | clock_hclock2_pclock(Source(mux_ahb2, Clock_source_hclock2)), |
paul@211 | 373 | |
paul@211 | 374 | // SSI channel clocks depending on a common parent divider. |
paul@0 | 375 | |
paul@211 | 376 | clock_ssi0((Source(mux_clock_ssi)), Control(Clock_gate_ssi0)), |
paul@211 | 377 | |
paul@219 | 378 | clock_ssi1((Source(mux_clock_ssi)), Control(Clock_gate_ssi1)); |
paul@62 | 379 | |
paul@211 | 380 | static Clock_divided |
paul@211 | 381 | clock_bch(Source(mux_core, Clock_source_bch), |
paul@211 | 382 | Control(Clock_gate_bch, Clock_change_enable_bch, Clock_busy_bch), |
paul@211 | 383 | Divider(Clock_divider_bch)), |
paul@211 | 384 | |
paul@211 | 385 | clock_cim(Source(mux_cim, Clock_source_cim), |
paul@211 | 386 | Control(Clock_gate_cim, Clock_change_enable_cim, Clock_busy_cim), |
paul@211 | 387 | Divider(Clock_divider_cim)), |
paul@62 | 388 | |
paul@211 | 389 | clock_cpu(Source(mux_core, Clock_source_cpu), |
paul@211 | 390 | Control(Field::undefined, Clock_change_enable_cpu, Clock_busy_cpu), |
paul@211 | 391 | Divider(Clock_divider_cpu)), |
paul@62 | 392 | |
paul@211 | 393 | clock_ddr(Source(mux_dev, Clock_source_ddr), |
paul@211 | 394 | Control(Clock_gate_ddr, Clock_change_enable_ddr, Clock_busy_ddr), |
paul@211 | 395 | Divider(Clock_divider_ddr)), |
paul@62 | 396 | |
paul@211 | 397 | clock_gpu(Source(mux_core, Clock_source_gpu), |
paul@211 | 398 | Control(Clock_gate_gpu, Clock_change_enable_gpu, Clock_busy_gpu), |
paul@211 | 399 | Divider(Clock_divider_gpu)), |
paul@62 | 400 | |
paul@211 | 401 | clock_hclock0(Source(mux_core, Clock_source_hclock0), |
paul@211 | 402 | Control(Field::undefined, Clock_change_enable_ahb0), |
paul@211 | 403 | Divider(Clock_divider_hclock0)), |
paul@211 | 404 | |
paul@211 | 405 | clock_hclock2(Source(mux_hclock2_pclock), |
paul@211 | 406 | Control(Field::undefined, Clock_change_enable_ahb2), |
paul@211 | 407 | Divider(Clock_divider_hclock2)), |
paul@0 | 408 | |
paul@211 | 409 | clock_hdmi(Source(mux_lcd, Clock_source_hdmi), |
paul@211 | 410 | Control(Clock_gate_hdmi, Clock_change_enable_hdmi, Clock_busy_hdmi), |
paul@211 | 411 | Divider(Clock_divider_hdmi)), |
paul@211 | 412 | |
paul@240 | 413 | clock_l2cache(Source(mux_core, Clock_source_cpu), |
paul@240 | 414 | Control(Field::undefined, Clock_change_enable_cpu, Clock_busy_cpu), |
paul@240 | 415 | Divider(Clock_divider_l2cache)), |
paul@240 | 416 | |
paul@211 | 417 | clock_lcd_pixel0(Source(mux_lcd, Clock_source_lcd0), |
paul@211 | 418 | Control(Clock_gate_lcd, Clock_change_enable_lcd0, Clock_busy_lcd0), |
paul@211 | 419 | Divider(Clock_divider_lcd0)), |
paul@0 | 420 | |
paul@211 | 421 | clock_lcd_pixel1(Source(mux_lcd, Clock_source_lcd1), |
paul@211 | 422 | Control(Clock_gate_lcd, Clock_change_enable_lcd1, Clock_busy_lcd1), |
paul@211 | 423 | Divider(Clock_divider_lcd1)), |
paul@67 | 424 | |
paul@211 | 425 | clock_msc0(Source(mux_clock_msc), |
paul@211 | 426 | Control(Clock_gate_msc0, Clock_change_enable_msc0, Clock_busy_msc0), |
paul@244 | 427 | Divider(Clock_divider_msc0, 2)), |
paul@0 | 428 | |
paul@211 | 429 | clock_msc1(Source(mux_clock_msc), |
paul@211 | 430 | Control(Clock_gate_msc1, Clock_change_enable_msc1, Clock_busy_msc1), |
paul@244 | 431 | Divider(Clock_divider_msc1, 2)), |
paul@0 | 432 | |
paul@211 | 433 | clock_msc2(Source(mux_clock_msc), |
paul@211 | 434 | Control(Clock_gate_msc2, Clock_change_enable_msc2, Clock_busy_msc2), |
paul@244 | 435 | Divider(Clock_divider_msc2, 2)), |
paul@0 | 436 | |
paul@211 | 437 | clock_pcm(Source(mux_pcm, Clock_source_pcm), |
paul@211 | 438 | Control(Clock_gate_pcm, Clock_change_enable_pcm, Clock_busy_pcm), |
paul@211 | 439 | Divider(Clock_divider_pcm)), |
paul@0 | 440 | |
paul@211 | 441 | clock_pclock(Source(mux_hclock2_pclock), |
paul@211 | 442 | Control(Field::undefined, Clock_change_enable_ahb2), |
paul@211 | 443 | Divider(Clock_divider_pclock)), |
paul@0 | 444 | |
paul@211 | 445 | clock_ssi(Source(mux_ssi, Clock_source_ssi), |
paul@211 | 446 | Control(Field::undefined, Clock_change_enable_ssi, Clock_busy_ssi), |
paul@211 | 447 | Divider(Clock_divider_ssi)), |
paul@0 | 448 | |
paul@211 | 449 | clock_uhc(Source(mux_usb, Clock_source_uhc), |
paul@211 | 450 | Control(Clock_gate_uhc, Clock_change_enable_uhc, Clock_busy_uhc), |
paul@213 | 451 | Divider(Clock_divider_uhc)), |
paul@0 | 452 | |
paul@213 | 453 | clock_vpu(Source(mux_core, Clock_source_vpu), |
paul@213 | 454 | Control(Clock_gate_vpu, Clock_change_enable_vpu, Clock_busy_vpu), |
paul@213 | 455 | Divider(Clock_divider_vpu)); |
paul@0 | 456 | |
paul@239 | 457 | static Clock_divided_fixed |
paul@284 | 458 | clock_external_div((Source(mux_external)), (Divider_fixed(512))); |
paul@239 | 459 | |
paul@211 | 460 | const double jz4780_pll_intermediate_min = 300000000, |
paul@211 | 461 | jz4780_pll_intermediate_max = 1500000000; |
paul@0 | 462 | |
paul@211 | 463 | static Pll clock_pll_A(Source(mux_external), |
paul@211 | 464 | Control_pll(Pll_enable_A, Pll_stable_A, Pll_bypass_A), |
paul@211 | 465 | Divider_pll(Pll_multiplier_A, Pll_input_division_A, |
paul@211 | 466 | Pll_output_division_A, |
paul@247 | 467 | jz4780_pll_intermediate_min, jz4780_pll_intermediate_max)), |
paul@0 | 468 | |
paul@211 | 469 | clock_pll_E(Source(mux_external), |
paul@211 | 470 | Control_pll(Pll_enable_E, Pll_stable_E, Pll_bypass_E), |
paul@211 | 471 | Divider_pll(Pll_multiplier_E, Pll_input_division_E, |
paul@211 | 472 | Pll_output_division_E, |
paul@247 | 473 | jz4780_pll_intermediate_min, jz4780_pll_intermediate_max)), |
paul@0 | 474 | |
paul@211 | 475 | clock_pll_M(Source(mux_external), |
paul@211 | 476 | Control_pll(Pll_enable_M, Pll_stable_M, Pll_bypass_M), |
paul@211 | 477 | Divider_pll(Pll_multiplier_M, Pll_input_division_M, |
paul@211 | 478 | Pll_output_division_M, |
paul@247 | 479 | jz4780_pll_intermediate_min, jz4780_pll_intermediate_max)), |
paul@0 | 480 | |
paul@211 | 481 | clock_pll_V(Source(mux_external), |
paul@211 | 482 | Control_pll(Pll_enable_V, Pll_stable_V, Pll_bypass_V), |
paul@211 | 483 | Divider_pll(Pll_multiplier_V, Pll_input_division_V, |
paul@211 | 484 | Pll_output_division_V, |
paul@247 | 485 | jz4780_pll_intermediate_min, jz4780_pll_intermediate_max)); |
paul@0 | 486 | |
paul@0 | 487 | |
paul@0 | 488 | |
paul@211 | 489 | // Clock register. |
paul@133 | 490 | |
paul@243 | 491 | static Clock_base *clocks[Clock_jz4780_identifier_count] = { |
paul@243 | 492 | &clock_none, |
paul@243 | 493 | |
paul@211 | 494 | &clock_none, // Clock_aic |
paul@211 | 495 | &clock_none, // Clock_aic_bitclk |
paul@211 | 496 | &clock_none, // Clock_aic_pclk |
paul@211 | 497 | &clock_none, // Clock_can0 |
paul@211 | 498 | &clock_none, // Clock_can1 |
paul@211 | 499 | &clock_none, // Clock_cdbus |
paul@211 | 500 | &clock_cim, |
paul@211 | 501 | &clock_cpu, |
paul@211 | 502 | &clock_ddr, |
paul@211 | 503 | &clock_dma, |
paul@211 | 504 | &clock_none, // Clock_emac |
paul@211 | 505 | &clock_external, |
paul@284 | 506 | &clock_external_div, |
paul@211 | 507 | &clock_hclock0, |
paul@211 | 508 | &clock_hclock2, |
paul@211 | 509 | &clock_hclock2_pclock, |
paul@211 | 510 | &clock_hdmi, |
paul@211 | 511 | &clock_i2c0, |
paul@211 | 512 | &clock_i2c1, |
paul@211 | 513 | &clock_i2c2, |
paul@211 | 514 | &clock_i2c3, |
paul@211 | 515 | &clock_i2c4, |
paul@211 | 516 | &clock_i2s0, |
paul@211 | 517 | &clock_none, // Clock_i2s0_rx |
paul@211 | 518 | &clock_none, // Clock_i2s0_tx |
paul@211 | 519 | &clock_i2s1, |
paul@211 | 520 | &clock_none, // Clock_i2s1_rx |
paul@211 | 521 | &clock_none, // Clock_i2s1_tx |
paul@211 | 522 | &clock_none, // Clock_kbc |
paul@240 | 523 | &clock_l2cache, |
paul@211 | 524 | &clock_lcd, |
paul@211 | 525 | &clock_lcd_pixel0, |
paul@211 | 526 | &clock_lcd_pixel1, |
paul@211 | 527 | &clock_mac, |
paul@211 | 528 | &clock_main, |
paul@284 | 529 | &clock_none, // Clock_mclock |
paul@211 | 530 | &clock_none, // Clock_mipi_csi |
paul@211 | 531 | &clock_msc, |
paul@211 | 532 | &clock_msc0, |
paul@211 | 533 | &clock_msc1, |
paul@211 | 534 | &clock_msc2, |
paul@244 | 535 | &clock_nemc, |
paul@211 | 536 | &clock_otg0, |
paul@211 | 537 | &clock_otg1, |
paul@211 | 538 | &clock_pclock, |
paul@215 | 539 | &clock_pcm, |
paul@284 | 540 | &clock_none, // Clock_pll |
paul@211 | 541 | &clock_pll_A, |
paul@211 | 542 | &clock_pll_E, |
paul@211 | 543 | &clock_pll_M, |
paul@211 | 544 | &clock_pll_V, |
paul@211 | 545 | &clock_none, // Clock_pwm0 |
paul@211 | 546 | &clock_none, // Clock_pwm1 |
paul@294 | 547 | &clock_rtc, |
paul@239 | 548 | &clock_rtc_external, |
paul@244 | 549 | &clock_sadc, |
paul@211 | 550 | &clock_scc, |
paul@211 | 551 | &clock_none, // Clock_sfc |
paul@211 | 552 | &clock_ssi, |
paul@211 | 553 | &clock_ssi0, |
paul@211 | 554 | &clock_ssi1, |
paul@219 | 555 | &clock_none, // Clock_ssi2 |
paul@211 | 556 | &clock_none, // Clock_timer |
paul@211 | 557 | &clock_uart0, |
paul@211 | 558 | &clock_uart1, |
paul@211 | 559 | &clock_uart2, |
paul@211 | 560 | &clock_uart3, |
paul@215 | 561 | &clock_uart4, |
paul@211 | 562 | &clock_none, // Clock_udc |
paul@211 | 563 | &clock_uhc, |
paul@211 | 564 | &clock_none, // Clock_uprt |
paul@243 | 565 | &clock_usb_phy, |
paul@213 | 566 | &clock_vpu, |
paul@243 | 567 | |
paul@243 | 568 | /* JZ4780-specific clocks. */ |
paul@243 | 569 | |
paul@243 | 570 | &clock_usb_phy_12MHz, |
paul@243 | 571 | &clock_usb_phy_19_2MHz, |
paul@243 | 572 | &clock_usb_phy_24MHz, |
paul@243 | 573 | &clock_usb_phy_48MHz, |
paul@211 | 574 | }; |
paul@133 | 575 | |
paul@211 | 576 | |
paul@133 | 577 | |
paul@211 | 578 | // Peripheral abstraction. |
paul@0 | 579 | |
paul@211 | 580 | Cpm_jz4780_chip::Cpm_jz4780_chip(l4_addr_t addr) |
paul@211 | 581 | : Cpm_chip(addr, clocks) |
paul@211 | 582 | { |
paul@0 | 583 | } |
paul@0 | 584 | |
paul@284 | 585 | Cpm_chip * |
paul@284 | 586 | jz4780_cpm_chip(l4_addr_t cpm_base) |
paul@284 | 587 | { |
paul@284 | 588 | return new Cpm_jz4780_chip(cpm_base); |
paul@284 | 589 | } |
paul@284 | 590 | |
paul@0 | 591 | |
paul@0 | 592 | |
paul@0 | 593 | // C language interface functions. |
paul@0 | 594 | |
paul@284 | 595 | void * |
paul@284 | 596 | jz4780_cpm_init(l4_addr_t cpm_base) |
paul@0 | 597 | { |
paul@284 | 598 | return (void *) jz4780_cpm_chip(cpm_base); |
paul@0 | 599 | } |
paul@0 | 600 | |
paul@214 | 601 | const char * |
paul@214 | 602 | jz4780_cpm_clock_type(void *cpm, enum Clock_identifiers clock) |
paul@214 | 603 | { |
paul@284 | 604 | return static_cast<Cpm_chip *>(cpm)->clock_type(clock); |
paul@214 | 605 | } |
paul@214 | 606 | |
paul@0 | 607 | int |
paul@128 | 608 | jz4780_cpm_have_clock(void *cpm, enum Clock_identifiers clock) |
paul@0 | 609 | { |
paul@284 | 610 | return static_cast<Cpm_chip *>(cpm)->have_clock(clock); |
paul@0 | 611 | } |
paul@0 | 612 | |
paul@0 | 613 | void |
paul@128 | 614 | jz4780_cpm_start_clock(void *cpm, enum Clock_identifiers clock) |
paul@0 | 615 | { |
paul@284 | 616 | static_cast<Cpm_chip *>(cpm)->start_clock(clock); |
paul@62 | 617 | } |
paul@62 | 618 | |
paul@62 | 619 | void |
paul@128 | 620 | jz4780_cpm_stop_clock(void *cpm, enum Clock_identifiers clock) |
paul@0 | 621 | { |
paul@284 | 622 | static_cast<Cpm_chip *>(cpm)->stop_clock(clock); |
paul@0 | 623 | } |
paul@0 | 624 | |
paul@211 | 625 | int |
paul@211 | 626 | jz4780_cpm_get_parameters(void *cpm, enum Clock_identifiers clock, uint32_t parameters[]) |
paul@211 | 627 | { |
paul@284 | 628 | return static_cast<Cpm_chip *>(cpm)->get_parameters(clock, parameters); |
paul@0 | 629 | } |
paul@0 | 630 | |
paul@211 | 631 | int |
paul@211 | 632 | jz4780_cpm_set_parameters(void *cpm, enum Clock_identifiers clock, int num_parameters, uint32_t parameters[]) |
paul@211 | 633 | { |
paul@284 | 634 | return static_cast<Cpm_chip *>(cpm)->set_parameters(clock, num_parameters, parameters); |
paul@0 | 635 | } |
paul@0 | 636 | |
paul@0 | 637 | uint8_t |
paul@211 | 638 | jz4780_cpm_get_source(void *cpm, enum Clock_identifiers clock) |
paul@211 | 639 | { |
paul@284 | 640 | return static_cast<Cpm_chip *>(cpm)->get_source(clock); |
paul@0 | 641 | } |
paul@0 | 642 | |
paul@0 | 643 | void |
paul@211 | 644 | jz4780_cpm_set_source(void *cpm, enum Clock_identifiers clock, uint8_t source) |
paul@211 | 645 | { |
paul@284 | 646 | static_cast<Cpm_chip *>(cpm)->set_source(clock, source); |
paul@0 | 647 | } |
paul@0 | 648 | |
paul@211 | 649 | enum Clock_identifiers |
paul@211 | 650 | jz4780_cpm_get_source_clock(void *cpm, enum Clock_identifiers clock) |
paul@211 | 651 | { |
paul@284 | 652 | return static_cast<Cpm_chip *>(cpm)->get_source_clock(clock); |
paul@0 | 653 | } |
paul@0 | 654 | |
paul@211 | 655 | void |
paul@211 | 656 | jz4780_cpm_set_source_clock(void *cpm, enum Clock_identifiers clock, enum Clock_identifiers source) |
paul@211 | 657 | { |
paul@284 | 658 | static_cast<Cpm_chip *>(cpm)->set_source_clock(clock, source); |
paul@0 | 659 | } |
paul@0 | 660 | |
paul@213 | 661 | uint64_t |
paul@211 | 662 | jz4780_cpm_get_source_frequency(void *cpm, enum Clock_identifiers clock) |
paul@211 | 663 | { |
paul@284 | 664 | return static_cast<Cpm_chip *>(cpm)->get_source_frequency(clock); |
paul@0 | 665 | } |
paul@0 | 666 | |
paul@213 | 667 | uint64_t |
paul@160 | 668 | jz4780_cpm_get_frequency(void *cpm, enum Clock_identifiers clock) |
paul@211 | 669 | { |
paul@284 | 670 | return static_cast<Cpm_chip *>(cpm)->get_frequency(clock); |
paul@62 | 671 | } |
paul@62 | 672 | |
paul@211 | 673 | int |
paul@213 | 674 | jz4780_cpm_set_frequency(void *cpm, enum Clock_identifiers clock, uint64_t frequency) |
paul@0 | 675 | { |
paul@284 | 676 | return static_cast<Cpm_chip *>(cpm)->set_frequency(clock, frequency); |
paul@64 | 677 | } |