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> | |
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 | ||
351a102d | 96 | static int osiris_dvs_probe(struct platform_device *pdev) |
4fa084af BD |
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 | ||
351a102d | 129 | static int osiris_dvs_remove(struct platform_device *pdev) |
4fa084af BD |
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, | |
351a102d | 170 | .remove = osiris_dvs_remove, |
4fa084af BD |
171 | .driver = { |
172 | .name = "osiris-dvs", | |
173 | .owner = THIS_MODULE, | |
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"); |