linux/drivers/cpufreq/s3c2410-cpufreq.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2006-2008 Simtec Electronics
   3 *      http://armlinux.simtec.co.uk/
   4 *      Ben Dooks <ben@simtec.co.uk>
   5 *
   6 * S3C2410 CPU Frequency scaling
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11*/
  12
  13#include <linux/init.h>
  14#include <linux/module.h>
  15#include <linux/interrupt.h>
  16#include <linux/ioport.h>
  17#include <linux/cpufreq.h>
  18#include <linux/device.h>
  19#include <linux/clk.h>
  20#include <linux/err.h>
  21#include <linux/io.h>
  22
  23#include <asm/mach/arch.h>
  24#include <asm/mach/map.h>
  25
  26#include <mach/regs-clock.h>
  27
  28#include <plat/cpu.h>
  29#include <plat/cpu-freq-core.h>
  30
  31/* Note, 2410A has an extra mode for 1:4:4 ratio, bit 2 of CLKDIV */
  32
  33static void s3c2410_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
  34{
  35        u32 clkdiv = 0;
  36
  37        if (cfg->divs.h_divisor == 2)
  38                clkdiv |= S3C2410_CLKDIVN_HDIVN;
  39
  40        if (cfg->divs.p_divisor != cfg->divs.h_divisor)
  41                clkdiv |= S3C2410_CLKDIVN_PDIVN;
  42
  43        __raw_writel(clkdiv, S3C2410_CLKDIVN);
  44}
  45
  46static int s3c2410_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
  47{
  48        unsigned long hclk, fclk, pclk;
  49        unsigned int hdiv, pdiv;
  50        unsigned long hclk_max;
  51
  52        fclk = cfg->freq.fclk;
  53        hclk_max = cfg->max.hclk;
  54
  55        cfg->freq.armclk = fclk;
  56
  57        s3c_freq_dbg("%s: fclk is %lu, max hclk %lu\n",
  58                      __func__, fclk, hclk_max);
  59
  60        hdiv = (fclk > cfg->max.hclk) ? 2 : 1;
  61        hclk = fclk / hdiv;
  62
  63        if (hclk > cfg->max.hclk) {
  64                s3c_freq_dbg("%s: hclk too big\n", __func__);
  65                return -EINVAL;
  66        }
  67
  68        pdiv = (hclk > cfg->max.pclk) ? 2 : 1;
  69        pclk = hclk / pdiv;
  70
  71        if (pclk > cfg->max.pclk) {
  72                s3c_freq_dbg("%s: pclk too big\n", __func__);
  73                return -EINVAL;
  74        }
  75
  76        pdiv *= hdiv;
  77
  78        /* record the result */
  79        cfg->divs.p_divisor = pdiv;
  80        cfg->divs.h_divisor = hdiv;
  81
  82        return 0;
  83}
  84
  85static struct s3c_cpufreq_info s3c2410_cpufreq_info = {
  86        .max            = {
  87                .fclk   = 200000000,
  88                .hclk   = 100000000,
  89                .pclk   =  50000000,
  90        },
  91
  92        /* transition latency is about 5ms worst-case, so
  93         * set 10ms to be sure */
  94        .latency        = 10000000,
  95
  96        .locktime_m     = 150,
  97        .locktime_u     = 150,
  98        .locktime_bits  = 12,
  99
 100        .need_pll       = 1,
 101
 102        .name           = "s3c2410",
 103        .calc_iotiming  = s3c2410_iotiming_calc,
 104        .set_iotiming   = s3c2410_iotiming_set,
 105        .get_iotiming   = s3c2410_iotiming_get,
 106
 107        .set_fvco       = s3c2410_set_fvco,
 108        .set_refresh    = s3c2410_cpufreq_setrefresh,
 109        .set_divs       = s3c2410_cpufreq_setdivs,
 110        .calc_divs      = s3c2410_cpufreq_calcdivs,
 111
 112        .debug_io_show  = s3c_cpufreq_debugfs_call(s3c2410_iotiming_debugfs),
 113};
 114
 115static int s3c2410_cpufreq_add(struct device *dev,
 116                               struct subsys_interface *sif)
 117{
 118        return s3c_cpufreq_register(&s3c2410_cpufreq_info);
 119}
 120
 121static struct subsys_interface s3c2410_cpufreq_interface = {
 122        .name           = "s3c2410_cpufreq",
 123        .subsys         = &s3c2410_subsys,
 124        .add_dev        = s3c2410_cpufreq_add,
 125};
 126
 127static int __init s3c2410_cpufreq_init(void)
 128{
 129        return subsys_interface_register(&s3c2410_cpufreq_interface);
 130}
 131arch_initcall(s3c2410_cpufreq_init);
 132
 133static int s3c2410a_cpufreq_add(struct device *dev,
 134                                struct subsys_interface *sif)
 135{
 136        /* alter the maximum freq settings for S3C2410A. If a board knows
 137         * it only has a maximum of 200, then it should register its own
 138         * limits. */
 139
 140        s3c2410_cpufreq_info.max.fclk = 266000000;
 141        s3c2410_cpufreq_info.max.hclk = 133000000;
 142        s3c2410_cpufreq_info.max.pclk =  66500000;
 143        s3c2410_cpufreq_info.name = "s3c2410a";
 144
 145        return s3c2410_cpufreq_add(dev, sif);
 146}
 147
 148static struct subsys_interface s3c2410a_cpufreq_interface = {
 149        .name           = "s3c2410a_cpufreq",
 150        .subsys         = &s3c2410a_subsys,
 151        .add_dev        = s3c2410a_cpufreq_add,
 152};
 153
 154static int __init s3c2410a_cpufreq_init(void)
 155{
 156        return subsys_interface_register(&s3c2410a_cpufreq_interface);
 157}
 158arch_initcall(s3c2410a_cpufreq_init);
 159