0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0012
0013 #include <linux/init.h>
0014 #include <linux/module.h>
0015 #include <linux/interrupt.h>
0016 #include <linux/ioport.h>
0017 #include <linux/cpufreq.h>
0018 #include <linux/device.h>
0019 #include <linux/delay.h>
0020 #include <linux/clk.h>
0021 #include <linux/err.h>
0022 #include <linux/io.h>
0023 #include <linux/soc/samsung/s3c-cpufreq-core.h>
0024 #include <linux/soc/samsung/s3c-pm.h>
0025
0026 #include <asm/mach/arch.h>
0027 #include <asm/mach/map.h>
0028
0029 #define S3C2440_CLKDIVN_PDIVN (1<<0)
0030 #define S3C2440_CLKDIVN_HDIVN_MASK (3<<1)
0031 #define S3C2440_CLKDIVN_HDIVN_1 (0<<1)
0032 #define S3C2440_CLKDIVN_HDIVN_2 (1<<1)
0033 #define S3C2440_CLKDIVN_HDIVN_4_8 (2<<1)
0034 #define S3C2440_CLKDIVN_HDIVN_3_6 (3<<1)
0035 #define S3C2440_CLKDIVN_UCLK (1<<3)
0036
0037 #define S3C2440_CAMDIVN_CAMCLK_MASK (0xf<<0)
0038 #define S3C2440_CAMDIVN_CAMCLK_SEL (1<<4)
0039 #define S3C2440_CAMDIVN_HCLK3_HALF (1<<8)
0040 #define S3C2440_CAMDIVN_HCLK4_HALF (1<<9)
0041 #define S3C2440_CAMDIVN_DVSEN (1<<12)
0042
0043 #define S3C2442_CAMDIVN_CAMCLK_DIV3 (1<<5)
0044
0045 static struct clk *xtal;
0046 static struct clk *fclk;
0047 static struct clk *hclk;
0048 static struct clk *armclk;
0049
0050
0051
0052 static inline int within_khz(unsigned long a, unsigned long b)
0053 {
0054 long diff = a - b;
0055
0056 return (diff >= -1000 && diff <= 1000);
0057 }
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067 static int s3c2440_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
0068 {
0069 unsigned int hdiv, pdiv;
0070 unsigned long hclk, fclk, armclk;
0071 unsigned long hclk_max;
0072
0073 fclk = cfg->freq.fclk;
0074 armclk = cfg->freq.armclk;
0075 hclk_max = cfg->max.hclk;
0076
0077 s3c_freq_dbg("%s: fclk is %lu, armclk %lu, max hclk %lu\n",
0078 __func__, fclk, armclk, hclk_max);
0079
0080 if (armclk > fclk) {
0081 pr_warn("%s: armclk > fclk\n", __func__);
0082 armclk = fclk;
0083 }
0084
0085
0086 if (armclk < fclk && armclk < hclk_max)
0087 hclk_max = armclk;
0088
0089 for (hdiv = 1; hdiv < 9; hdiv++) {
0090 if (hdiv == 5 || hdiv == 7)
0091 hdiv++;
0092
0093 hclk = (fclk / hdiv);
0094 if (hclk <= hclk_max || within_khz(hclk, hclk_max))
0095 break;
0096 }
0097
0098 s3c_freq_dbg("%s: hclk %lu, div %d\n", __func__, hclk, hdiv);
0099
0100 if (hdiv > 8)
0101 goto invalid;
0102
0103 pdiv = (hclk > cfg->max.pclk) ? 2 : 1;
0104
0105 if ((hclk / pdiv) > cfg->max.pclk)
0106 pdiv++;
0107
0108 s3c_freq_dbg("%s: pdiv %d\n", __func__, pdiv);
0109
0110 if (pdiv > 2)
0111 goto invalid;
0112
0113 pdiv *= hdiv;
0114
0115
0116
0117 if (armclk < hclk)
0118 armclk = hclk;
0119
0120
0121
0122
0123 if (armclk < fclk) {
0124 cfg->divs.dvs = 1;
0125 armclk = hclk;
0126 } else
0127 cfg->divs.dvs = 0;
0128
0129 cfg->freq.armclk = armclk;
0130
0131
0132
0133 cfg->divs.h_divisor = hdiv;
0134 cfg->divs.p_divisor = pdiv;
0135
0136 return 0;
0137
0138 invalid:
0139 return -EINVAL;
0140 }
0141
0142 #define CAMDIVN_HCLK_HALF (S3C2440_CAMDIVN_HCLK3_HALF | \
0143 S3C2440_CAMDIVN_HCLK4_HALF)
0144
0145
0146
0147
0148
0149
0150
0151
0152 static void s3c2440_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
0153 {
0154 unsigned long clkdiv, camdiv;
0155
0156 s3c_freq_dbg("%s: divisors: h=%d, p=%d\n", __func__,
0157 cfg->divs.h_divisor, cfg->divs.p_divisor);
0158
0159 clkdiv = s3c24xx_read_clkdivn();
0160 camdiv = s3c2440_read_camdivn();
0161
0162 clkdiv &= ~(S3C2440_CLKDIVN_HDIVN_MASK | S3C2440_CLKDIVN_PDIVN);
0163 camdiv &= ~CAMDIVN_HCLK_HALF;
0164
0165 switch (cfg->divs.h_divisor) {
0166 case 1:
0167 clkdiv |= S3C2440_CLKDIVN_HDIVN_1;
0168 break;
0169
0170 case 2:
0171 clkdiv |= S3C2440_CLKDIVN_HDIVN_2;
0172 break;
0173
0174 case 6:
0175 camdiv |= S3C2440_CAMDIVN_HCLK3_HALF;
0176 fallthrough;
0177 case 3:
0178 clkdiv |= S3C2440_CLKDIVN_HDIVN_3_6;
0179 break;
0180
0181 case 8:
0182 camdiv |= S3C2440_CAMDIVN_HCLK4_HALF;
0183 fallthrough;
0184 case 4:
0185 clkdiv |= S3C2440_CLKDIVN_HDIVN_4_8;
0186 break;
0187
0188 default:
0189 BUG();
0190 }
0191
0192 if (cfg->divs.p_divisor != cfg->divs.h_divisor)
0193 clkdiv |= S3C2440_CLKDIVN_PDIVN;
0194
0195
0196
0197
0198
0199
0200
0201
0202 s3c2440_write_camdivn(camdiv | CAMDIVN_HCLK_HALF);
0203 s3c24xx_write_clkdivn(clkdiv);
0204
0205 ndelay(20);
0206 s3c2440_write_camdivn(camdiv);
0207
0208 clk_set_parent(armclk, cfg->divs.dvs ? hclk : fclk);
0209 }
0210
0211 static int run_freq_for(unsigned long max_hclk, unsigned long fclk,
0212 int *divs,
0213 struct cpufreq_frequency_table *table,
0214 size_t table_size)
0215 {
0216 unsigned long freq;
0217 int index = 0;
0218 int div;
0219
0220 for (div = *divs; div > 0; div = *divs++) {
0221 freq = fclk / div;
0222
0223 if (freq > max_hclk && div != 1)
0224 continue;
0225
0226 freq /= 1000;
0227 index = s3c_cpufreq_addfreq(table, index, table_size, freq);
0228 if (index < 0)
0229 break;
0230 }
0231
0232 return index;
0233 }
0234
0235 static int hclk_divs[] = { 1, 2, 3, 4, 6, 8, -1 };
0236
0237 static int s3c2440_cpufreq_calctable(struct s3c_cpufreq_config *cfg,
0238 struct cpufreq_frequency_table *table,
0239 size_t table_size)
0240 {
0241 int ret;
0242
0243 WARN_ON(cfg->info == NULL);
0244 WARN_ON(cfg->board == NULL);
0245
0246 ret = run_freq_for(cfg->info->max.hclk,
0247 cfg->info->max.fclk,
0248 hclk_divs,
0249 table, table_size);
0250
0251 s3c_freq_dbg("%s: returning %d\n", __func__, ret);
0252
0253 return ret;
0254 }
0255
0256 static struct s3c_cpufreq_info s3c2440_cpufreq_info = {
0257 .max = {
0258 .fclk = 400000000,
0259 .hclk = 133333333,
0260 .pclk = 66666666,
0261 },
0262
0263 .locktime_m = 300,
0264 .locktime_u = 300,
0265 .locktime_bits = 16,
0266
0267 .name = "s3c244x",
0268 .calc_iotiming = s3c2410_iotiming_calc,
0269 .set_iotiming = s3c2410_iotiming_set,
0270 .get_iotiming = s3c2410_iotiming_get,
0271 .set_fvco = s3c2410_set_fvco,
0272
0273 .set_refresh = s3c2410_cpufreq_setrefresh,
0274 .set_divs = s3c2440_cpufreq_setdivs,
0275 .calc_divs = s3c2440_cpufreq_calcdivs,
0276 .calc_freqtable = s3c2440_cpufreq_calctable,
0277
0278 .debug_io_show = s3c_cpufreq_debugfs_call(s3c2410_iotiming_debugfs),
0279 };
0280
0281 static int s3c2440_cpufreq_add(struct device *dev,
0282 struct subsys_interface *sif)
0283 {
0284 xtal = s3c_cpufreq_clk_get(NULL, "xtal");
0285 hclk = s3c_cpufreq_clk_get(NULL, "hclk");
0286 fclk = s3c_cpufreq_clk_get(NULL, "fclk");
0287 armclk = s3c_cpufreq_clk_get(NULL, "armclk");
0288
0289 if (IS_ERR(xtal) || IS_ERR(hclk) || IS_ERR(fclk) || IS_ERR(armclk)) {
0290 pr_err("%s: failed to get clocks\n", __func__);
0291 return -ENOENT;
0292 }
0293
0294 return s3c_cpufreq_register(&s3c2440_cpufreq_info);
0295 }
0296
0297 static struct subsys_interface s3c2440_cpufreq_interface = {
0298 .name = "s3c2440_cpufreq",
0299 .subsys = &s3c2440_subsys,
0300 .add_dev = s3c2440_cpufreq_add,
0301 };
0302
0303 static int s3c2440_cpufreq_init(void)
0304 {
0305 return subsys_interface_register(&s3c2440_cpufreq_interface);
0306 }
0307
0308
0309 subsys_initcall(s3c2440_cpufreq_init);
0310
0311 static struct subsys_interface s3c2442_cpufreq_interface = {
0312 .name = "s3c2442_cpufreq",
0313 .subsys = &s3c2442_subsys,
0314 .add_dev = s3c2440_cpufreq_add,
0315 };
0316
0317 static int s3c2442_cpufreq_init(void)
0318 {
0319 return subsys_interface_register(&s3c2442_cpufreq_interface);
0320 }
0321 subsys_initcall(s3c2442_cpufreq_init);