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