Commit | Line | Data |
---|---|---|
4fa084af BD |
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> | |
b0161caa | 23 | #include <mach/gpio-samsung.h> |
4fa084af BD |
24 | |
25 | #define OSIRIS_GPIO_DVS S3C2410_GPB(5) | |
26 | ||
27 | static bool dvs_en; | |
28 | ||
29 | static void osiris_dvs_tps_setdvs(bool on) | |
30 | { | |
31 | unsigned vregs1 = 0, vdcdc2 = 0; | |
32 | ||
33 | if (!on) { | |
34 | vdcdc2 = TPS_VCORE_DISCH | TPS_LP_COREOFF; | |
35 | vregs1 = TPS_LDO1_OFF; /* turn off in low-power mode */ | |
36 | } | |
37 | ||
38 | dvs_en = on; | |
39 | vdcdc2 |= TPS_VCORE_1_3V | TPS_VCORE_LP_1_0V; | |
40 | vregs1 |= TPS_LDO2_ENABLE | TPS_LDO1_ENABLE; | |
41 | ||
42 | tps65010_config_vregs1(vregs1); | |
43 | tps65010_config_vdcdc2(vdcdc2); | |
44 | } | |
45 | ||
46 | static bool is_dvs(struct s3c_freq *f) | |
47 | { | |
48 | /* at the moment, we assume ARMCLK = HCLK => DVS */ | |
49 | return f->armclk == f->hclk; | |
50 | } | |
51 | ||
52 | /* keep track of current state */ | |
53 | static bool cur_dvs = false; | |
54 | ||
55 | static int osiris_dvs_notify(struct notifier_block *nb, | |
56 | unsigned long val, void *data) | |
57 | { | |
58 | struct cpufreq_freqs *cf = data; | |
59 | struct s3c_cpufreq_freqs *freqs = to_s3c_cpufreq(cf); | |
60 | bool old_dvs = is_dvs(&freqs->old); | |
61 | bool new_dvs = is_dvs(&freqs->new); | |
62 | int ret = 0; | |
63 | ||
64 | if (!dvs_en) | |
65 | return 0; | |
66 | ||
67 | printk(KERN_DEBUG "%s: old %ld,%ld new %ld,%ld\n", __func__, | |
68 | freqs->old.armclk, freqs->old.hclk, | |
69 | freqs->new.armclk, freqs->new.hclk); | |
70 | ||
71 | switch (val) { | |
72 | case CPUFREQ_PRECHANGE: | |
73 | if (old_dvs & !new_dvs || | |
74 | cur_dvs & !new_dvs) { | |
75 | pr_debug("%s: exiting dvs\n", __func__); | |
76 | cur_dvs = false; | |
77 | gpio_set_value(OSIRIS_GPIO_DVS, 1); | |
78 | } | |
79 | break; | |
80 | case CPUFREQ_POSTCHANGE: | |
81 | if (!old_dvs & new_dvs || | |
82 | !cur_dvs & new_dvs) { | |
83 | pr_debug("entering dvs\n"); | |
84 | cur_dvs = true; | |
85 | gpio_set_value(OSIRIS_GPIO_DVS, 0); | |
86 | } | |
87 | break; | |
88 | } | |
89 | ||
90 | return ret; | |
91 | } | |
92 | ||
93 | static struct notifier_block osiris_dvs_nb = { | |
94 | .notifier_call = osiris_dvs_notify, | |
95 | }; | |
96 | ||
351a102d | 97 | static int osiris_dvs_probe(struct platform_device *pdev) |
4fa084af BD |
98 | { |
99 | int ret; | |
100 | ||
101 | dev_info(&pdev->dev, "initialising\n"); | |
102 | ||
103 | ret = gpio_request(OSIRIS_GPIO_DVS, "osiris-dvs"); | |
104 | if (ret) { | |
105 | dev_err(&pdev->dev, "cannot claim gpio\n"); | |
106 | goto err_nogpio; | |
107 | } | |
108 | ||
109 | /* start with dvs disabled */ | |
110 | gpio_direction_output(OSIRIS_GPIO_DVS, 1); | |
111 | ||
112 | ret = cpufreq_register_notifier(&osiris_dvs_nb, | |
113 | CPUFREQ_TRANSITION_NOTIFIER); | |
114 | if (ret) { | |
115 | dev_err(&pdev->dev, "failed to register with cpufreq\n"); | |
116 | goto err_nofreq; | |
117 | } | |
118 | ||
119 | osiris_dvs_tps_setdvs(true); | |
120 | ||
121 | return 0; | |
122 | ||
123 | err_nofreq: | |
124 | gpio_free(OSIRIS_GPIO_DVS); | |
125 | ||
126 | err_nogpio: | |
127 | return ret; | |
128 | } | |
129 | ||
351a102d | 130 | static int osiris_dvs_remove(struct platform_device *pdev) |
4fa084af BD |
131 | { |
132 | dev_info(&pdev->dev, "exiting\n"); | |
133 | ||
134 | /* disable any current dvs */ | |
135 | gpio_set_value(OSIRIS_GPIO_DVS, 1); | |
136 | osiris_dvs_tps_setdvs(false); | |
137 | ||
138 | cpufreq_unregister_notifier(&osiris_dvs_nb, | |
139 | CPUFREQ_TRANSITION_NOTIFIER); | |
140 | ||
141 | gpio_free(OSIRIS_GPIO_DVS); | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | /* the CONFIG_PM block is so small, it isn't worth actaully compiling it | |
147 | * out if the configuration isn't set. */ | |
148 | ||
149 | static int osiris_dvs_suspend(struct device *dev) | |
150 | { | |
151 | gpio_set_value(OSIRIS_GPIO_DVS, 1); | |
152 | osiris_dvs_tps_setdvs(false); | |
153 | cur_dvs = false; | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
158 | static int osiris_dvs_resume(struct device *dev) | |
159 | { | |
160 | osiris_dvs_tps_setdvs(true); | |
161 | return 0; | |
162 | } | |
163 | ||
164 | static const struct dev_pm_ops osiris_dvs_pm = { | |
165 | .suspend = osiris_dvs_suspend, | |
166 | .resume = osiris_dvs_resume, | |
167 | }; | |
168 | ||
169 | static struct platform_driver osiris_dvs_driver = { | |
170 | .probe = osiris_dvs_probe, | |
351a102d | 171 | .remove = osiris_dvs_remove, |
4fa084af BD |
172 | .driver = { |
173 | .name = "osiris-dvs", | |
4fa084af BD |
174 | .pm = &osiris_dvs_pm, |
175 | }, | |
176 | }; | |
177 | ||
ef97fa65 | 178 | module_platform_driver(osiris_dvs_driver); |
4fa084af BD |
179 | |
180 | MODULE_DESCRIPTION("Simtec OSIRIS DVS support"); | |
181 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); | |
182 | MODULE_LICENSE("GPL"); | |
183 | MODULE_ALIAS("platform:osiris-dvs"); |