mach-osiris-dvs.c 3.9 KB

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