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 #ifdef CONFIG_CPU_JZ4730 200 return !(cpm_ctrl_get(cpm_base, CPM_MSCR) & CPM_MSCR_MSTP_OST); 201 #else 202 return !(cpm_ctrl_get(cpm_base, CPM_CLKGR) & CPM_CLKGR_TCU); 203 #endif 204 } 205 206 int jz4740_cpm_have_pll(void *cpm_base) 207 { 208 return cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLS; 209 } 210 211 void jz4740_cpm_start_clock(void *cpm_base) 212 { 213 #ifdef CONFIG_CPU_JZ4730 214 cpm_start_ost(cpm_base); 215 cpm_ctrl_set(cpm_base, CPM_MSCR, cpm_ctrl_get(cpm_base, CPM_MSCR) & ~CPM_MSCR_MSTP_OST); 216 #else 217 cpm_ctrl_set(cpm_base, CPM_CLKGR, cpm_ctrl_get(cpm_base, CPM_CLKGR) & ~CPM_CLKGR_TCU); 218 #endif 219 } 220 221 // Peripheral clock control. 222 223 void jz4740_cpm_start_lcd(void *cpm_base) 224 { 225 #ifndef CONFIG_CPU_JZ4730 226 cpm_ctrl_set(cpm_base, CPM_CLKGR, cpm_ctrl_get(cpm_base, CPM_CLKGR) & ~CPM_CLKGR_LCD); 227 #endif 228 } 229 230 void jz4740_cpm_stop_lcd(void *cpm_base) 231 { 232 #ifndef CONFIG_CPU_JZ4730 233 cpm_ctrl_set(cpm_base, CPM_CLKGR, cpm_ctrl_get(cpm_base, CPM_CLKGR) | CPM_CLKGR_LCD); 234 #endif 235 } 236 237 // Register access. 238 239 uint32_t jz4740_cpm_ctrl_get(void *cpm_base, uint32_t reg) 240 { 241 return cpm_ctrl_get(cpm_base, reg); 242 } 243 244 void jz4740_cpm_ctrl_set(void *cpm_base, uint32_t reg, uint32_t value) 245 { 246 cpm_ctrl_set(cpm_base, reg, value); 247 } 248 249 /* Top-level initialisation. */ 250 251 void cpm_init() 252 { 253 #ifdef CONFIG_CPU_JZ4730 254 cpm_ctrl_set((void *) CPM_BASE, CPM_MSCR, 0xffffffff); 255 #else 256 cpm_ctrl_set((void *) CPM_BASE, CPM_CLKGR, 0x7fff); 257 #endif 258 } 259