mach-osiris-dvs.c 4.2 KB

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