hwblk.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #include <linux/clk.h>
  2. #include <linux/compiler.h>
  3. #include <linux/io.h>
  4. #include <linux/spinlock.h>
  5. #include <asm/suspend.h>
  6. #include <asm/hwblk.h>
  7. #include <asm/clock.h>
  8. static DEFINE_SPINLOCK(hwblk_lock);
  9. static void hwblk_area_mod_cnt(struct hwblk_info *info,
  10. int area, int counter, int value, int goal)
  11. {
  12. struct hwblk_area *hap = info->areas + area;
  13. hap->cnt[counter] += value;
  14. if (hap->cnt[counter] != goal)
  15. return;
  16. if (hap->flags & HWBLK_AREA_FLAG_PARENT)
  17. hwblk_area_mod_cnt(info, hap->parent, counter, value, goal);
  18. }
  19. static int __hwblk_mod_cnt(struct hwblk_info *info, int hwblk,
  20. int counter, int value, int goal)
  21. {
  22. struct hwblk *hp = info->hwblks + hwblk;
  23. hp->cnt[counter] += value;
  24. if (hp->cnt[counter] == goal)
  25. hwblk_area_mod_cnt(info, hp->area, counter, value, goal);
  26. return hp->cnt[counter];
  27. }
  28. static void hwblk_mod_cnt(struct hwblk_info *info, int hwblk,
  29. int counter, int value, int goal)
  30. {
  31. unsigned long flags;
  32. spin_lock_irqsave(&hwblk_lock, flags);
  33. __hwblk_mod_cnt(info, hwblk, counter, value, goal);
  34. spin_unlock_irqrestore(&hwblk_lock, flags);
  35. }
  36. void hwblk_cnt_inc(struct hwblk_info *info, int hwblk, int counter)
  37. {
  38. hwblk_mod_cnt(info, hwblk, counter, 1, 1);
  39. }
  40. void hwblk_cnt_dec(struct hwblk_info *info, int hwblk, int counter)
  41. {
  42. hwblk_mod_cnt(info, hwblk, counter, -1, 0);
  43. }
  44. void hwblk_enable(struct hwblk_info *info, int hwblk)
  45. {
  46. struct hwblk *hp = info->hwblks + hwblk;
  47. unsigned long tmp;
  48. unsigned long flags;
  49. int ret;
  50. spin_lock_irqsave(&hwblk_lock, flags);
  51. ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, 1, 1);
  52. if (ret == 1) {
  53. tmp = __raw_readl(hp->mstp);
  54. tmp &= ~(1 << hp->bit);
  55. __raw_writel(tmp, hp->mstp);
  56. }
  57. spin_unlock_irqrestore(&hwblk_lock, flags);
  58. }
  59. void hwblk_disable(struct hwblk_info *info, int hwblk)
  60. {
  61. struct hwblk *hp = info->hwblks + hwblk;
  62. unsigned long tmp;
  63. unsigned long flags;
  64. int ret;
  65. spin_lock_irqsave(&hwblk_lock, flags);
  66. ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, -1, 0);
  67. if (ret == 0) {
  68. tmp = __raw_readl(hp->mstp);
  69. tmp |= 1 << hp->bit;
  70. __raw_writel(tmp, hp->mstp);
  71. }
  72. spin_unlock_irqrestore(&hwblk_lock, flags);
  73. }
  74. struct hwblk_info *hwblk_info;
  75. int __init hwblk_register(struct hwblk_info *info)
  76. {
  77. hwblk_info = info;
  78. return 0;
  79. }
  80. int __init __weak arch_hwblk_init(void)
  81. {
  82. return 0;
  83. }
  84. int __weak arch_hwblk_sleep_mode(void)
  85. {
  86. return SUSP_SH_SLEEP;
  87. }
  88. int __init hwblk_init(void)
  89. {
  90. return arch_hwblk_init();
  91. }
  92. /* allow clocks to enable and disable hardware blocks */
  93. static int sh_hwblk_clk_enable(struct clk *clk)
  94. {
  95. if (!hwblk_info)
  96. return -ENOENT;
  97. hwblk_enable(hwblk_info, clk->arch_flags);
  98. return 0;
  99. }
  100. static void sh_hwblk_clk_disable(struct clk *clk)
  101. {
  102. if (hwblk_info)
  103. hwblk_disable(hwblk_info, clk->arch_flags);
  104. }
  105. static struct clk_ops sh_hwblk_clk_ops = {
  106. .enable = sh_hwblk_clk_enable,
  107. .disable = sh_hwblk_clk_disable,
  108. .recalc = followparent_recalc,
  109. };
  110. int __init sh_hwblk_clk_register(struct clk *clks, int nr)
  111. {
  112. struct clk *clkp;
  113. int ret = 0;
  114. int k;
  115. for (k = 0; !ret && (k < nr); k++) {
  116. clkp = clks + k;
  117. /* skip over clocks using hwblk 0 (HWBLK_UNKNOWN) */
  118. if (!clkp->arch_flags)
  119. continue;
  120. clkp->ops = &sh_hwblk_clk_ops;
  121. ret |= clk_register(clkp);
  122. }
  123. return ret;
  124. }