Root/drivers/cpufreq/db8500-cpufreq.c

1/*
2 * Copyright (C) STMicroelectronics 2009
3 * Copyright (C) ST-Ericsson SA 2010
4 *
5 * License Terms: GNU General Public License v2
6 * Author: Sundar Iyer <sundar.iyer@stericsson.com>
7 * Author: Martin Persson <martin.persson@stericsson.com>
8 * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
9 *
10 */
11#include <linux/kernel.h>
12#include <linux/cpufreq.h>
13#include <linux/delay.h>
14#include <linux/slab.h>
15#include <linux/mfd/dbx500-prcmu.h>
16#include <mach/id.h>
17
18static struct cpufreq_frequency_table freq_table[] = {
19    [0] = {
20        .index = 0,
21        .frequency = 200000,
22    },
23    [1] = {
24        .index = 1,
25        .frequency = 400000,
26    },
27    [2] = {
28        .index = 2,
29        .frequency = 800000,
30    },
31    [3] = {
32        /* Used for MAX_OPP, if available */
33        .index = 3,
34        .frequency = CPUFREQ_TABLE_END,
35    },
36    [4] = {
37        .index = 4,
38        .frequency = CPUFREQ_TABLE_END,
39    },
40};
41
42static enum arm_opp idx2opp[] = {
43    ARM_EXTCLK,
44    ARM_50_OPP,
45    ARM_100_OPP,
46    ARM_MAX_OPP
47};
48
49static struct freq_attr *db8500_cpufreq_attr[] = {
50    &cpufreq_freq_attr_scaling_available_freqs,
51    NULL,
52};
53
54static int db8500_cpufreq_verify_speed(struct cpufreq_policy *policy)
55{
56    return cpufreq_frequency_table_verify(policy, freq_table);
57}
58
59static int db8500_cpufreq_target(struct cpufreq_policy *policy,
60                unsigned int target_freq,
61                unsigned int relation)
62{
63    struct cpufreq_freqs freqs;
64    unsigned int idx;
65
66    /* scale the target frequency to one of the extremes supported */
67    if (target_freq < policy->cpuinfo.min_freq)
68        target_freq = policy->cpuinfo.min_freq;
69    if (target_freq > policy->cpuinfo.max_freq)
70        target_freq = policy->cpuinfo.max_freq;
71
72    /* Lookup the next frequency */
73    if (cpufreq_frequency_table_target
74        (policy, freq_table, target_freq, relation, &idx)) {
75        return -EINVAL;
76    }
77
78    freqs.old = policy->cur;
79    freqs.new = freq_table[idx].frequency;
80
81    if (freqs.old == freqs.new)
82        return 0;
83
84    /* pre-change notification */
85    for_each_cpu(freqs.cpu, policy->cpus)
86        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
87
88    /* request the PRCM unit for opp change */
89    if (prcmu_set_arm_opp(idx2opp[idx])) {
90        pr_err("db8500-cpufreq: Failed to set OPP level\n");
91        return -EINVAL;
92    }
93
94    /* post change notification */
95    for_each_cpu(freqs.cpu, policy->cpus)
96        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
97
98    return 0;
99}
100
101static unsigned int db8500_cpufreq_getspeed(unsigned int cpu)
102{
103    int i;
104    /* request the prcm to get the current ARM opp */
105    for (i = 0; prcmu_get_arm_opp() != idx2opp[i]; i++)
106        ;
107    return freq_table[i].frequency;
108}
109
110static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
111{
112    int i, res;
113
114    BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table));
115
116    if (prcmu_has_arm_maxopp())
117        freq_table[3].frequency = 1000000;
118
119    pr_info("db8500-cpufreq : Available frequencies:\n");
120    for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
121        pr_info(" %d Mhz\n", freq_table[i].frequency/1000);
122
123    /* get policy fields based on the table */
124    res = cpufreq_frequency_table_cpuinfo(policy, freq_table);
125    if (!res)
126        cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
127    else {
128        pr_err("db8500-cpufreq : Failed to read policy table\n");
129        return res;
130    }
131
132    policy->min = policy->cpuinfo.min_freq;
133    policy->max = policy->cpuinfo.max_freq;
134    policy->cur = db8500_cpufreq_getspeed(policy->cpu);
135    policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
136
137    /*
138     * FIXME : Need to take time measurement across the target()
139     * function with no/some/all drivers in the notification
140     * list.
141     */
142    policy->cpuinfo.transition_latency = 20 * 1000; /* in ns */
143
144    /* policy sharing between dual CPUs */
145    cpumask_copy(policy->cpus, cpu_present_mask);
146
147    policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
148
149    return 0;
150}
151
152static struct cpufreq_driver db8500_cpufreq_driver = {
153    .flags = CPUFREQ_STICKY,
154    .verify = db8500_cpufreq_verify_speed,
155    .target = db8500_cpufreq_target,
156    .get = db8500_cpufreq_getspeed,
157    .init = db8500_cpufreq_init,
158    .name = "DB8500",
159    .attr = db8500_cpufreq_attr,
160};
161
162static int __init db8500_cpufreq_register(void)
163{
164    if (!cpu_is_u8500_family())
165        return -ENODEV;
166
167    pr_info("cpufreq for DB8500 started\n");
168    return cpufreq_register_driver(&db8500_cpufreq_driver);
169}
170device_initcall(db8500_cpufreq_register);
171

Archive Download this file



interactive