mach-osiris-dvs.c 3.8 KB

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