1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/stage2/cpm.c Tue Jul 11 00:03:30 2017 +0200
1.3 @@ -0,0 +1,260 @@
1.4 +/*
1.5 + * Clock and power management.
1.6 + *
1.7 + * Copyright (C) Xiangfu Liu <xiangfu@sharism.cc>
1.8 + * Copyright (C) 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
1.9 + *
1.10 + * This program is free software: you can redistribute it and/or modify
1.11 + * it under the terms of the GNU General Public License as published by
1.12 + * the Free Software Foundation, either version 3 of the License, or
1.13 + * (at your option) any later version.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful,
1.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.18 + * GNU General Public License for more details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License
1.21 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + */
1.23 +
1.24 +#include "xburst_types.h"
1.25 +#include "cpm.h"
1.26 +#include "board.h"
1.27 +
1.28 +static uint32_t cpm_ctrl_get(void *cpm_base, uint32_t reg)
1.29 +{
1.30 + return REG32(cpm_base + reg);
1.31 +}
1.32 +
1.33 +static void cpm_ctrl_set(void *cpm_base, uint32_t reg, uint32_t value)
1.34 +{
1.35 + REG32(cpm_base + reg) = value;
1.36 +}
1.37 +
1.38 +static int pll_enabled(void *cpm_base)
1.39 +{
1.40 + return cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLEN;
1.41 +}
1.42 +
1.43 +static int pll_bypassed(void *cpm_base)
1.44 +{
1.45 + return cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLBP;
1.46 +}
1.47 +
1.48 +// Feedback (9-bit) divider.
1.49 +
1.50 +static uint16_t get_multiplier(void *cpm_base)
1.51 +{
1.52 + return ((cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLM_MASK) >> CPM_CPPCR_PLLM_BIT) + 2;
1.53 +}
1.54 +
1.55 +// Input (5-bit) divider.
1.56 +
1.57 +static uint8_t get_input_divider(void *cpm_base)
1.58 +{
1.59 + return ((cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLN_MASK) >> CPM_CPPCR_PLLN_BIT) + 2;
1.60 +}
1.61 +
1.62 +// Output divider.
1.63 +
1.64 +static uint8_t get_output_divider(void *cpm_base)
1.65 +{
1.66 + uint8_t od[] = {1, 2, 2, 4};
1.67 + return od[(cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLOD_MASK) >> CPM_CPPCR_PLLOD_BIT];
1.68 +}
1.69 +
1.70 +// General clock divider.
1.71 +
1.72 +static uint8_t _get_divider(void *cpm_base, uint32_t reg, uint32_t mask, uint8_t shift)
1.73 +{
1.74 + uint8_t cd[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
1.75 + uint8_t d = (cpm_ctrl_get(cpm_base, reg) & mask) >> shift;
1.76 + return (d < 10) ? cd[d] : 1;
1.77 +}
1.78 +
1.79 +// CPU clock divider.
1.80 +
1.81 +static uint8_t get_cpu_divider(void *cpm_base)
1.82 +{
1.83 + return _get_divider(cpm_base, CPM_CPCCR, CPM_CPCCR_CDIV_MASK, CPM_CPCCR_CDIV_BIT);
1.84 +}
1.85 +
1.86 +// Memory clock divider.
1.87 +
1.88 +static uint8_t get_memory_divider(void *cpm_base)
1.89 +{
1.90 + return _get_divider(cpm_base, CPM_CPCCR, CPM_CPCCR_MDIV_MASK, CPM_CPCCR_MDIV_BIT);
1.91 +}
1.92 +
1.93 +// Clock source divider for MSC, I2S, LCD and USB.
1.94 +
1.95 +static uint8_t get_source_divider(void *cpm_base)
1.96 +{
1.97 +#ifdef CONFIG_CPU_JZ4730
1.98 + return 1;
1.99 +#else
1.100 + return cpm_ctrl_get(cpm_base, CPM_CPCCR) & CPM_CPCCR_PCS ? 1 : 2;
1.101 +#endif
1.102 +}
1.103 +
1.104 +// LCD device clock divider.
1.105 +
1.106 +static void set_lcd_device_divider(void *cpm_base, uint8_t division)
1.107 +{
1.108 + if (division == 0)
1.109 + division = 1;
1.110 +#ifdef CONFIG_CPU_JZ4730
1.111 + else if (division > 16)
1.112 + division = 16;
1.113 +#else
1.114 + else if (division > 32)
1.115 + division = 32;
1.116 +#endif
1.117 +
1.118 + cpm_ctrl_set(cpm_base, CPM_CPCCR,
1.119 + (cpm_ctrl_get(cpm_base, CPM_CPCCR) & ~CPM_CPCCR_LDIV_MASK) |
1.120 + ((division - 1) << CPM_CPCCR_LDIV_BIT));
1.121 +}
1.122 +
1.123 +// LCD pixel clock divider.
1.124 +
1.125 +static void set_lcd_pixel_divider(void *cpm_base, uint16_t division)
1.126 +{
1.127 +#ifndef CONFIG_CPU_JZ4730
1.128 + if (division == 0)
1.129 + division = 1;
1.130 + else if (division > 2048)
1.131 + division = 2048;
1.132 +
1.133 + cpm_ctrl_set(cpm_base, CPM_LPCDR,
1.134 + (cpm_ctrl_get(cpm_base, CPM_LPCDR) & ~CPM_LPCDR_PIXDIV_MASK) |
1.135 + (division - 1));
1.136 +#endif
1.137 +}
1.138 +
1.139 +static uint32_t get_pll_frequency(void *cpm_base)
1.140 +{
1.141 + // Test for PLL enable and not PLL bypass.
1.142 +
1.143 + if (pll_enabled(cpm_base) && !pll_bypassed(cpm_base))
1.144 + return (JZ_EXTAL * get_multiplier(cpm_base)) /
1.145 + (get_input_divider(cpm_base) * get_output_divider(cpm_base));
1.146 + else
1.147 + return JZ_EXTAL;
1.148 +}
1.149 +
1.150 +// Clock frequency for MSC, I2S, LCD and USB.
1.151 +
1.152 +static uint32_t get_output_frequency(void *cpm_base)
1.153 +{
1.154 + return get_pll_frequency(cpm_base) / get_source_divider(cpm_base);
1.155 +}
1.156 +
1.157 +
1.158 +
1.159 +/* Public functions. */
1.160 +
1.161 +// Clock frequency for the CPU.
1.162 +
1.163 +uint32_t jz4740_cpm_get_cpu_frequency(void *cpm_base)
1.164 +{
1.165 + return get_pll_frequency(cpm_base) / get_cpu_divider(cpm_base);
1.166 +}
1.167 +
1.168 +// Clock frequency for the memory.
1.169 +
1.170 +uint32_t jz4740_cpm_get_memory_frequency(void *cpm_base)
1.171 +{
1.172 + return get_pll_frequency(cpm_base) / get_memory_divider(cpm_base);
1.173 +}
1.174 +
1.175 +// Set the device and pixel frequencies, indicating the latter and
1.176 +// providing the device:pixel frequency ratio.
1.177 +
1.178 +void jz4740_cpm_set_lcd_frequencies(void *cpm_base, uint32_t pclk, uint8_t ratio)
1.179 +{
1.180 + uint32_t out = get_output_frequency(cpm_base), lcd = pclk * ratio;
1.181 +
1.182 + set_lcd_pixel_divider(cpm_base, out / pclk);
1.183 +
1.184 + // Limit the device frequency to 150MHz.
1.185 +
1.186 + if (lcd > 150000000) lcd = 150000000;
1.187 +
1.188 + set_lcd_device_divider(cpm_base, out / lcd);
1.189 +}
1.190 +
1.191 +// Update the clock output frequency.
1.192 +
1.193 +void jz4740_cpm_update_output_frequency(void *cpm_base)
1.194 +{
1.195 + cpm_ctrl_set(cpm_base, CPM_CPCCR, cpm_ctrl_get(cpm_base, CPM_CPCCR) | CPM_CPCCR_CE);
1.196 +}
1.197 +
1.198 +// General clock functions.
1.199 +
1.200 +int jz4740_cpm_have_clock(void *cpm_base)
1.201 +{
1.202 + // NOTE: To check.
1.203 +#ifdef CONFIG_CPU_JZ4730
1.204 + return cpm_ctrl_get(cpm_base, CPM_MSCR) != 0;
1.205 +#else
1.206 + return cpm_ctrl_get(cpm_base, CPM_CLKGR) != 0;
1.207 +#endif
1.208 +}
1.209 +
1.210 +int jz4740_cpm_have_pll(void *cpm_base)
1.211 +{
1.212 + return cpm_ctrl_get(cpm_base, CPM_CPPCR) & CPM_CPPCR_PLLS;
1.213 +}
1.214 +
1.215 +void jz4740_cpm_start_clock(void *cpm_base)
1.216 +{
1.217 +#ifdef CONFIG_CPU_JZ4730
1.218 + cpm_start_ost(cpm_base);
1.219 + cpm_ctrl_set(cpm_base, CPM_MSCR, cpm_ctrl_get(cpm_base, CPM_MSCR) & ~CPM_MSCR_MSTP_OST);
1.220 +#else
1.221 + cpm_ctrl_set(cpm_base, CPM_CLKGR, cpm_ctrl_get(cpm_base, CPM_CLKGR) & ~CPM_CLKGR_TCU);
1.222 +#endif
1.223 +}
1.224 +
1.225 +// Peripheral clock control.
1.226 +
1.227 +void jz4740_cpm_start_lcd(void *cpm_base)
1.228 +{
1.229 +#ifndef CONFIG_CPU_JZ4730
1.230 + cpm_ctrl_set(cpm_base, CPM_CLKGR, cpm_ctrl_get(cpm_base, CPM_CLKGR) & ~CPM_CLKGR_LCD);
1.231 +#endif
1.232 +}
1.233 +
1.234 +void jz4740_cpm_stop_lcd(void *cpm_base)
1.235 +{
1.236 +#ifndef CONFIG_CPU_JZ4730
1.237 + cpm_ctrl_set(cpm_base, CPM_CLKGR, cpm_ctrl_get(cpm_base, CPM_CLKGR) | CPM_CLKGR_LCD);
1.238 +#endif
1.239 +}
1.240 +
1.241 +// Register access.
1.242 +
1.243 +uint32_t jz4740_cpm_ctrl_get(void *cpm_base, uint32_t reg)
1.244 +{
1.245 + return cpm_ctrl_get(cpm_base, reg);
1.246 +}
1.247 +
1.248 +void jz4740_cpm_ctrl_set(void *cpm_base, uint32_t reg, uint32_t value)
1.249 +{
1.250 + cpm_ctrl_set(cpm_base, reg, value);
1.251 +}
1.252 +
1.253 +/* Top-level initialisation. */
1.254 +
1.255 +void cpm_init()
1.256 +{
1.257 +#ifdef CONFIG_CPU_JZ4730
1.258 + cpm_ctrl_set((void *) CPM_BASE, CPM_MSCR, 0xffffffff);
1.259 +#else
1.260 + cpm_ctrl_set((void *) CPM_BASE, CPM_CLKGR, 0x7fff);
1.261 +#endif
1.262 +}
1.263 +