Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Copyright (c) 2009 Simtec Electronics
0004 //  http://armlinux.simtec.co.uk/
0005 //  Ben Dooks <ben@simtec.co.uk>
0006 //
0007 // Simtec Osiris Dynamic Voltage Scaling support.
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;  /* turn off in low-power mode */
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     /* at the moment, we assume ARMCLK = HCLK => DVS */
0044     return f->armclk == f->hclk;
0045 }
0046 
0047 /* keep track of current state */
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     /* start with dvs disabled */
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     /* disable any current dvs */
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 /* the CONFIG_PM block is so small, it isn't worth actually compiling it
0142  * out if the configuration isn't set. */
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");