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