1 /* 2 * Clock and power management. 3 * 4 * Copyright (C) Xiangfu Liu <xiangfu@sharism.cc> 5 * Copyright (C) 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk> 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include "xburst_types.h" 22 #include "cpm.h" 23 #include "board.h" 24 25 static uint32_t cpm_ctrl_get(void *cpm_base, uint32_t reg) 26 { 27 return REG32(cpm_base + reg); 28 } 29 30 static void cpm_ctrl_set(void *cpm_base, uint32_t reg, uint32_t value) 31 { 32 REG32(cpm_base + reg) = value; 33 } 34 35 static int pll_enabled(void *cpm_base) 36 { 37 return cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLEN; 38 } 39 40 static int pll_bypassed(void *cpm_base) 41 { 42 return cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLBP; 43 } 44 45 // Feedback (9-bit) divider. 46 47 static uint16_t get_multiplier(void *cpm_base) 48 { 49 return ((cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLM_MASK) >> CPM_CPPCR_PLLM_BIT) + 2; 50 } 51 52 // Input (5-bit) divider. 53 54 static uint8_t get_input_divider(void *cpm_base) 55 { 56 return ((cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLN_MASK) >> CPM_CPPCR_PLLN_BIT) + 2; 57 } 58 59 // Output divider. 60 61 static uint8_t get_output_divider(void *cpm_base) 62 { 63 uint8_t od[] = {1, 2, 2, 4}; 64 return od[(cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLOD_MASK) >> CPM_CPPCR_PLLOD_BIT]; 65 } 66 67 // General clock divider. 68 69 static uint8_t _get_divider(void *cpm_base, uint32_t reg, uint32_t mask, uint8_t shift) 70 { 71 uint8_t cd[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; 72 uint8_t d = (cpm_ctrl_get(cpm_base, reg) & mask) >> shift; 73 return (d < 10) ? cd[d] : 1; 74 } 75 76 // CPU clock divider. 77 78 static uint8_t get_cpu_divider(void *cpm_base) 79 { 80 return _get_divider(cpm_base, CPM_CPCCR, CPM_CPCCR_CDIV_MASK, CPM_CPCCR_CDIV_BIT); 81 } 82 83 // Memory clock divider. 84 85 static uint8_t get_memory_divider(void *cpm_base) 86 { 87 return _get_divider(cpm_base, CPM_CPCCR, CPM_CPCCR_MDIV_MASK, CPM_CPCCR_MDIV_BIT); 88 } 89 90 // Clock source divider for MSC, I2S, LCD and USB. 91 92 static uint8_t get_source_divider(void *cpm_base) 93 { 94 #ifdef CONFIG_CPU_JZ4730 95 return 1; 96 #else 97 return cpm_ctrl_get(cpm_base, CPM_CPCCR) & CPM_CPCCR_PCS ? 1 : 2; 98 #endif 99 } 100 101 // LCD device clock divider. 102 103 static void set_lcd_device_divider(void *cpm_base, uint8_t division) 104 { 105 if (division == 0) 106 division = 1; 107 #ifdef CONFIG_CPU_JZ4730 108 else if (division > 16) 109 division = 16; 110 #else 111 else if (division > 32) 112 division = 32; 113 #endif 114 115 cpm_ctrl_set(cpm_base, CPM_CPCCR, 116 (cpm_ctrl_get(cpm_base, CPM_CPCCR) & ~CPM_CPCCR_LDIV_MASK) | 117 ((division - 1) << CPM_CPCCR_LDIV_BIT)); 118 } 119 120 // LCD pixel clock divider. 121 122 static void set_lcd_pixel_divider(void *cpm_base, uint16_t division) 123 { 124 #ifndef CONFIG_CPU_JZ4730 125 if (division == 0) 126 division = 1; 127 else if (division > 2048) 128 division = 2048; 129 130 cpm_ctrl_set(cpm_base, CPM_LPCDR, 131 (cpm_ctrl_get(cpm_base, CPM_LPCDR) & ~CPM_LPCDR_PIXDIV_MASK) | 132 (division - 1)); 133 #endif 134 } 135 136 static uint32_t get_pll_frequency(void *cpm_base) 137 { 138 // Test for PLL enable and not PLL bypass. 139 140 if (pll_enabled(cpm_base) && !pll_bypassed(cpm_base)) 141 return (JZ_EXTAL * get_multiplier(cpm_base)) / 142 (get_input_divider(cpm_base) * get_output_divider(cpm_base)); 143 else 144 return JZ_EXTAL; 145 } 146 147 // Clock frequency for MSC, I2S, LCD and USB. 148 149 static uint32_t get_output_frequency(void *cpm_base) 150 { 151 return get_pll_frequency(cpm_base) / get_source_divider(cpm_base); 152 } 153 154 155 156 /* Public functions. */ 157 158 // Clock frequency for the CPU. 159 160 uint32_t jz4740_cpm_get_cpu_frequency(void *cpm_base) 161 { 162 return get_pll_frequency(cpm_base) / get_cpu_divider(cpm_base); 163 } 164 165 // Clock frequency for the memory. 166 167 uint32_t jz4740_cpm_get_memory_frequency(void *cpm_base) 168 { 169 return get_pll_frequency(cpm_base) / get_memory_divider(cpm_base); 170 } 171 172 // Set the device and pixel frequencies, indicating the latter and 173 // providing the device:pixel frequency ratio. 174 175 void jz4740_cpm_set_lcd_frequencies(void *cpm_base, uint32_t pclk, uint8_t ratio) 176 { 177 uint32_t out = get_output_frequency(cpm_base), lcd = pclk * ratio; 178 179 set_lcd_pixel_divider(cpm_base, out / pclk); 180 181 // Limit the device frequency to 150MHz. 182 183 if (lcd > 150000000) lcd = 150000000; 184 185 set_lcd_device_divider(cpm_base, out / lcd); 186 } 187 188 // Update the clock output frequency. 189 190 void jz4740_cpm_update_output_frequency(void *cpm_base) 191 { 192 cpm_ctrl_set(cpm_base, CPM_CPCCR, cpm_ctrl_get(cpm_base, CPM_CPCCR) | CPM_CPCCR_CE); 193 } 194 195 // General clock functions. 196 197 int jz4740_cpm_have_clock(void *cpm_base) 198 { 199 // NOTE: To check. 200 #ifdef CONFIG_CPU_JZ4730 201 return cpm_ctrl_get(cpm_base, CPM_MSCR) != 0; 202 #else 203 return cpm_ctrl_get(cpm_base, CPM_CLKGR) != 0; 204 #endif 205 } 206 207 int jz4740_cpm_have_pll(void *cpm_base) 208 { 209 return cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLS; 210 } 211 212 void jz4740_cpm_start_clock(void *cpm_base) 213 { 214 #ifdef CONFIG_CPU_JZ4730 215 cpm_start_ost(cpm_base); 216 cpm_ctrl_set(cpm_base, CPM_MSCR, cpm_ctrl_get(cpm_base, CPM_MSCR) & ~CPM_MSCR_MSTP_OST); 217 #else 218 cpm_ctrl_set(cpm_base, CPM_CLKGR, cpm_ctrl_get(cpm_base, CPM_CLKGR) & ~CPM_CLKGR_TCU); 219 #endif 220 } 221 222 // Peripheral clock control. 223 224 void jz4740_cpm_start_lcd(void *cpm_base) 225 { 226 #ifndef CONFIG_CPU_JZ4730 227 cpm_ctrl_set(cpm_base, CPM_CLKGR, cpm_ctrl_get(cpm_base, CPM_CLKGR) & ~CPM_CLKGR_LCD); 228 #endif 229 } 230 231 void jz4740_cpm_stop_lcd(void *cpm_base) 232 { 233 #ifndef CONFIG_CPU_JZ4730 234 cpm_ctrl_set(cpm_base, CPM_CLKGR, cpm_ctrl_get(cpm_base, CPM_CLKGR) | CPM_CLKGR_LCD); 235 #endif 236 } 237 238 // Register access. 239 240 uint32_t jz4740_cpm_ctrl_get(void *cpm_base, uint32_t reg) 241 { 242 return cpm_ctrl_get(cpm_base, reg); 243 } 244 245 void jz4740_cpm_ctrl_set(void *cpm_base, uint32_t reg, uint32_t value) 246 { 247 cpm_ctrl_set(cpm_base, reg, value); 248 } 249 250 /* Top-level initialisation. */ 251 252 void cpm_init() 253 { 254 #ifdef CONFIG_CPU_JZ4730 255 cpm_ctrl_set((void *) CPM_BASE, CPM_MSCR, 0xffffffff); 256 #else 257 cpm_ctrl_set((void *) CPM_BASE, CPM_CLKGR, 0x7fff); 258 #endif 259 } 260