0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/kernel.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/cpufreq.h>
0013 #include <linux/gpio.h>
0014
0015 #include <linux/mfd/tps65010.h>
0016
0017 #include <linux/soc/samsung/s3c-cpu-freq.h>
0018 #include "gpio-samsung.h"
0019
0020 #define OSIRIS_GPIO_DVS S3C2410_GPB(5)
0021
0022 static bool dvs_en;
0023
0024 static void osiris_dvs_tps_setdvs(bool on)
0025 {
0026 unsigned vregs1 = 0, vdcdc2 = 0;
0027
0028 if (!on) {
0029 vdcdc2 = TPS_VCORE_DISCH | TPS_LP_COREOFF;
0030 vregs1 = TPS_LDO1_OFF;
0031 }
0032
0033 dvs_en = on;
0034 vdcdc2 |= TPS_VCORE_1_3V | TPS_VCORE_LP_1_0V;
0035 vregs1 |= TPS_LDO2_ENABLE | TPS_LDO1_ENABLE;
0036
0037 tps65010_config_vregs1(vregs1);
0038 tps65010_config_vdcdc2(vdcdc2);
0039 }
0040
0041 static bool is_dvs(struct s3c_freq *f)
0042 {
0043
0044 return f->armclk == f->hclk;
0045 }
0046
0047
0048 static bool cur_dvs = false;
0049
0050 static int osiris_dvs_notify(struct notifier_block *nb,
0051 unsigned long val, void *data)
0052 {
0053 struct cpufreq_freqs *cf = data;
0054 struct s3c_cpufreq_freqs *freqs = to_s3c_cpufreq(cf);
0055 bool old_dvs = is_dvs(&freqs->old);
0056 bool new_dvs = is_dvs(&freqs->new);
0057 int ret = 0;
0058
0059 if (!dvs_en)
0060 return 0;
0061
0062 printk(KERN_DEBUG "%s: old %ld,%ld new %ld,%ld\n", __func__,
0063 freqs->old.armclk, freqs->old.hclk,
0064 freqs->new.armclk, freqs->new.hclk);
0065
0066 switch (val) {
0067 case CPUFREQ_PRECHANGE:
0068 if ((old_dvs && !new_dvs) ||
0069 (cur_dvs && !new_dvs)) {
0070 pr_debug("%s: exiting dvs\n", __func__);
0071 cur_dvs = false;
0072 gpio_set_value(OSIRIS_GPIO_DVS, 1);
0073 }
0074 break;
0075 case CPUFREQ_POSTCHANGE:
0076 if ((!old_dvs && new_dvs) ||
0077 (!cur_dvs && new_dvs)) {
0078 pr_debug("entering dvs\n");
0079 cur_dvs = true;
0080 gpio_set_value(OSIRIS_GPIO_DVS, 0);
0081 }
0082 break;
0083 }
0084
0085 return ret;
0086 }
0087
0088 static struct notifier_block osiris_dvs_nb = {
0089 .notifier_call = osiris_dvs_notify,
0090 };
0091
0092 static int osiris_dvs_probe(struct platform_device *pdev)
0093 {
0094 int ret;
0095
0096 dev_info(&pdev->dev, "initialising\n");
0097
0098 ret = gpio_request(OSIRIS_GPIO_DVS, "osiris-dvs");
0099 if (ret) {
0100 dev_err(&pdev->dev, "cannot claim gpio\n");
0101 goto err_nogpio;
0102 }
0103
0104
0105 gpio_direction_output(OSIRIS_GPIO_DVS, 1);
0106
0107 ret = cpufreq_register_notifier(&osiris_dvs_nb,
0108 CPUFREQ_TRANSITION_NOTIFIER);
0109 if (ret) {
0110 dev_err(&pdev->dev, "failed to register with cpufreq\n");
0111 goto err_nofreq;
0112 }
0113
0114 osiris_dvs_tps_setdvs(true);
0115
0116 return 0;
0117
0118 err_nofreq:
0119 gpio_free(OSIRIS_GPIO_DVS);
0120
0121 err_nogpio:
0122 return ret;
0123 }
0124
0125 static int osiris_dvs_remove(struct platform_device *pdev)
0126 {
0127 dev_info(&pdev->dev, "exiting\n");
0128
0129
0130 gpio_set_value(OSIRIS_GPIO_DVS, 1);
0131 osiris_dvs_tps_setdvs(false);
0132
0133 cpufreq_unregister_notifier(&osiris_dvs_nb,
0134 CPUFREQ_TRANSITION_NOTIFIER);
0135
0136 gpio_free(OSIRIS_GPIO_DVS);
0137
0138 return 0;
0139 }
0140
0141
0142
0143
0144 static int osiris_dvs_suspend(struct device *dev)
0145 {
0146 gpio_set_value(OSIRIS_GPIO_DVS, 1);
0147 osiris_dvs_tps_setdvs(false);
0148 cur_dvs = false;
0149
0150 return 0;
0151 }
0152
0153 static int osiris_dvs_resume(struct device *dev)
0154 {
0155 osiris_dvs_tps_setdvs(true);
0156 return 0;
0157 }
0158
0159 static const struct dev_pm_ops osiris_dvs_pm = {
0160 .suspend = osiris_dvs_suspend,
0161 .resume = osiris_dvs_resume,
0162 };
0163
0164 static struct platform_driver osiris_dvs_driver = {
0165 .probe = osiris_dvs_probe,
0166 .remove = osiris_dvs_remove,
0167 .driver = {
0168 .name = "osiris-dvs",
0169 .pm = &osiris_dvs_pm,
0170 },
0171 };
0172
0173 module_platform_driver(osiris_dvs_driver);
0174
0175 MODULE_DESCRIPTION("Simtec OSIRIS DVS support");
0176 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
0177 MODULE_LICENSE("GPL");
0178 MODULE_ALIAS("platform:osiris-dvs");