diff options
Diffstat (limited to 'arch/arm/mach-s3c24xx/mach-osiris-dvs.c')
-rw-r--r-- | arch/arm/mach-s3c24xx/mach-osiris-dvs.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c24xx/mach-osiris-dvs.c b/arch/arm/mach-s3c24xx/mach-osiris-dvs.c new file mode 100644 index 000000000000..ad2792dfbee1 --- /dev/null +++ b/arch/arm/mach-s3c24xx/mach-osiris-dvs.c | |||
@@ -0,0 +1,194 @@ | |||
1 | /* linux/arch/arm/mach-s3c2440/mach-osiris-dvs.c | ||
2 | * | ||
3 | * Copyright (c) 2009 Simtec Electronics | ||
4 | * http://armlinux.simtec.co.uk/ | ||
5 | * Ben Dooks <ben@simtec.co.uk> | ||
6 | * | ||
7 | * Simtec Osiris Dynamic Voltage Scaling support. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/cpufreq.h> | ||
18 | #include <linux/gpio.h> | ||
19 | |||
20 | #include <linux/i2c/tps65010.h> | ||
21 | |||
22 | #include <plat/cpu-freq.h> | ||
23 | |||
24 | #define OSIRIS_GPIO_DVS S3C2410_GPB(5) | ||
25 | |||
26 | static bool dvs_en; | ||
27 | |||
28 | static void osiris_dvs_tps_setdvs(bool on) | ||
29 | { | ||
30 | unsigned vregs1 = 0, vdcdc2 = 0; | ||
31 | |||
32 | if (!on) { | ||
33 | vdcdc2 = TPS_VCORE_DISCH | TPS_LP_COREOFF; | ||
34 | vregs1 = TPS_LDO1_OFF; /* turn off in low-power mode */ | ||
35 | } | ||
36 | |||
37 | dvs_en = on; | ||
38 | vdcdc2 |= TPS_VCORE_1_3V | TPS_VCORE_LP_1_0V; | ||
39 | vregs1 |= TPS_LDO2_ENABLE | TPS_LDO1_ENABLE; | ||
40 | |||
41 | tps65010_config_vregs1(vregs1); | ||
42 | tps65010_config_vdcdc2(vdcdc2); | ||
43 | } | ||
44 | |||
45 | static bool is_dvs(struct s3c_freq *f) | ||
46 | { | ||
47 | /* at the moment, we assume ARMCLK = HCLK => DVS */ | ||
48 | return f->armclk == f->hclk; | ||
49 | } | ||
50 | |||
51 | /* keep track of current state */ | ||
52 | static bool cur_dvs = false; | ||
53 | |||
54 | static int osiris_dvs_notify(struct notifier_block *nb, | ||
55 | unsigned long val, void *data) | ||
56 | { | ||
57 | struct cpufreq_freqs *cf = data; | ||
58 | struct s3c_cpufreq_freqs *freqs = to_s3c_cpufreq(cf); | ||
59 | bool old_dvs = is_dvs(&freqs->old); | ||
60 | bool new_dvs = is_dvs(&freqs->new); | ||
61 | int ret = 0; | ||
62 | |||
63 | if (!dvs_en) | ||
64 | return 0; | ||
65 | |||
66 | printk(KERN_DEBUG "%s: old %ld,%ld new %ld,%ld\n", __func__, | ||
67 | freqs->old.armclk, freqs->old.hclk, | ||
68 | freqs->new.armclk, freqs->new.hclk); | ||
69 | |||
70 | switch (val) { | ||
71 | case CPUFREQ_PRECHANGE: | ||
72 | if (old_dvs & !new_dvs || | ||
73 | cur_dvs & !new_dvs) { | ||
74 | pr_debug("%s: exiting dvs\n", __func__); | ||
75 | cur_dvs = false; | ||
76 | gpio_set_value(OSIRIS_GPIO_DVS, 1); | ||
77 | } | ||
78 | break; | ||
79 | case CPUFREQ_POSTCHANGE: | ||
80 | if (!old_dvs & new_dvs || | ||
81 | !cur_dvs & new_dvs) { | ||
82 | pr_debug("entering dvs\n"); | ||
83 | cur_dvs = true; | ||
84 | gpio_set_value(OSIRIS_GPIO_DVS, 0); | ||
85 | } | ||
86 | break; | ||
87 | } | ||
88 | |||
89 | return ret; | ||
90 | } | ||
91 | |||
92 | static struct notifier_block osiris_dvs_nb = { | ||
93 | .notifier_call = osiris_dvs_notify, | ||
94 | }; | ||
95 | |||
96 | static int __devinit osiris_dvs_probe(struct platform_device *pdev) | ||
97 | { | ||
98 | int ret; | ||
99 | |||
100 | dev_info(&pdev->dev, "initialising\n"); | ||
101 | |||
102 | ret = gpio_request(OSIRIS_GPIO_DVS, "osiris-dvs"); | ||
103 | if (ret) { | ||
104 | dev_err(&pdev->dev, "cannot claim gpio\n"); | ||
105 | goto err_nogpio; | ||
106 | } | ||
107 | |||
108 | /* start with dvs disabled */ | ||
109 | gpio_direction_output(OSIRIS_GPIO_DVS, 1); | ||
110 | |||
111 | ret = cpufreq_register_notifier(&osiris_dvs_nb, | ||
112 | CPUFREQ_TRANSITION_NOTIFIER); | ||
113 | if (ret) { | ||
114 | dev_err(&pdev->dev, "failed to register with cpufreq\n"); | ||
115 | goto err_nofreq; | ||
116 | } | ||
117 | |||
118 | osiris_dvs_tps_setdvs(true); | ||
119 | |||
120 | return 0; | ||
121 | |||
122 | err_nofreq: | ||
123 | gpio_free(OSIRIS_GPIO_DVS); | ||
124 | |||
125 | err_nogpio: | ||
126 | return ret; | ||
127 | } | ||
128 | |||
129 | static int __devexit osiris_dvs_remove(struct platform_device *pdev) | ||
130 | { | ||
131 | dev_info(&pdev->dev, "exiting\n"); | ||
132 | |||
133 | /* disable any current dvs */ | ||
134 | gpio_set_value(OSIRIS_GPIO_DVS, 1); | ||
135 | osiris_dvs_tps_setdvs(false); | ||
136 | |||
137 | cpufreq_unregister_notifier(&osiris_dvs_nb, | ||
138 | CPUFREQ_TRANSITION_NOTIFIER); | ||
139 | |||
140 | gpio_free(OSIRIS_GPIO_DVS); | ||
141 | |||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | /* the CONFIG_PM block is so small, it isn't worth actaully compiling it | ||
146 | * out if the configuration isn't set. */ | ||
147 | |||
148 | static int osiris_dvs_suspend(struct device *dev) | ||
149 | { | ||
150 | gpio_set_value(OSIRIS_GPIO_DVS, 1); | ||
151 | osiris_dvs_tps_setdvs(false); | ||
152 | cur_dvs = false; | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static int osiris_dvs_resume(struct device *dev) | ||
158 | { | ||
159 | osiris_dvs_tps_setdvs(true); | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static const struct dev_pm_ops osiris_dvs_pm = { | ||
164 | .suspend = osiris_dvs_suspend, | ||
165 | .resume = osiris_dvs_resume, | ||
166 | }; | ||
167 | |||
168 | static struct platform_driver osiris_dvs_driver = { | ||
169 | .probe = osiris_dvs_probe, | ||
170 | .remove = __devexit_p(osiris_dvs_remove), | ||
171 | .driver = { | ||
172 | .name = "osiris-dvs", | ||
173 | .owner = THIS_MODULE, | ||
174 | .pm = &osiris_dvs_pm, | ||
175 | }, | ||
176 | }; | ||
177 | |||
178 | static int __init osiris_dvs_init(void) | ||
179 | { | ||
180 | return platform_driver_register(&osiris_dvs_driver); | ||
181 | } | ||
182 | |||
183 | static void __exit osiris_dvs_exit(void) | ||
184 | { | ||
185 | platform_driver_unregister(&osiris_dvs_driver); | ||
186 | } | ||
187 | |||
188 | module_init(osiris_dvs_init); | ||
189 | module_exit(osiris_dvs_exit); | ||
190 | |||
191 | MODULE_DESCRIPTION("Simtec OSIRIS DVS support"); | ||
192 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); | ||
193 | MODULE_LICENSE("GPL"); | ||
194 | MODULE_ALIAS("platform:osiris-dvs"); | ||