wcd9xxx-core-resource.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. */
  12. #include <linux/kernel.h>
  13. #include <linux/module.h>
  14. #include <linux/types.h>
  15. #include <linux/delay.h>
  16. #include <linux/slab.h>
  17. #include <linux/wait.h>
  18. #include <linux/sched.h>
  19. #include <linux/mfd/wcd9xxx/core-resource.h>
  20. static enum wcd9xxx_intf_status wcd9xxx_intf = -1;
  21. int wcd9xxx_core_irq_init(
  22. struct wcd9xxx_core_resource *wcd9xxx_core_res)
  23. {
  24. int ret = 0;
  25. if (wcd9xxx_core_res->irq != 1) {
  26. ret = wcd9xxx_irq_init(wcd9xxx_core_res);
  27. if (ret)
  28. pr_err("IRQ initialization failed\n");
  29. }
  30. return ret;
  31. }
  32. EXPORT_SYMBOL(wcd9xxx_core_irq_init);
  33. int wcd9xxx_initialize_irq(
  34. struct wcd9xxx_core_resource *wcd9xxx_core_res,
  35. unsigned int irq,
  36. unsigned int irq_base)
  37. {
  38. wcd9xxx_core_res->irq = irq;
  39. wcd9xxx_core_res->irq_base = irq_base;
  40. return 0;
  41. }
  42. EXPORT_SYMBOL(wcd9xxx_initialize_irq);
  43. int wcd9xxx_core_res_init(
  44. struct wcd9xxx_core_resource *wcd9xxx_core_res,
  45. int num_irqs, int num_irq_regs,
  46. int (*codec_read)(struct wcd9xxx_core_resource*, unsigned short),
  47. int (*codec_write)(struct wcd9xxx_core_resource*, unsigned short, u8),
  48. int (*codec_bulk_read) (struct wcd9xxx_core_resource*, unsigned short,
  49. int, u8*),
  50. int (*codec_bulk_write) (struct wcd9xxx_core_resource*, unsigned short,
  51. int, u8*))
  52. {
  53. mutex_init(&wcd9xxx_core_res->pm_lock);
  54. wcd9xxx_core_res->wlock_holders = 0;
  55. wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE;
  56. init_waitqueue_head(&wcd9xxx_core_res->pm_wq);
  57. pm_qos_add_request(&wcd9xxx_core_res->pm_qos_req,
  58. PM_QOS_CPU_DMA_LATENCY,
  59. PM_QOS_DEFAULT_VALUE);
  60. wcd9xxx_core_res->codec_reg_read = codec_read;
  61. wcd9xxx_core_res->codec_reg_write = codec_write;
  62. wcd9xxx_core_res->codec_bulk_read = codec_bulk_read;
  63. wcd9xxx_core_res->codec_bulk_write = codec_bulk_write;
  64. wcd9xxx_core_res->num_irqs = num_irqs;
  65. wcd9xxx_core_res->num_irq_regs = num_irq_regs;
  66. pr_info("%s: num_irqs = %d, num_irq_regs = %d\n",
  67. __func__, wcd9xxx_core_res->num_irqs,
  68. wcd9xxx_core_res->num_irq_regs);
  69. return 0;
  70. }
  71. EXPORT_SYMBOL(wcd9xxx_core_res_init);
  72. void wcd9xxx_core_res_deinit(struct wcd9xxx_core_resource *wcd9xxx_core_res)
  73. {
  74. pm_qos_remove_request(&wcd9xxx_core_res->pm_qos_req);
  75. mutex_destroy(&wcd9xxx_core_res->pm_lock);
  76. wcd9xxx_core_res->codec_reg_read = NULL;
  77. wcd9xxx_core_res->codec_reg_write = NULL;
  78. wcd9xxx_core_res->codec_bulk_read = NULL;
  79. }
  80. EXPORT_SYMBOL(wcd9xxx_core_res_deinit);
  81. enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(
  82. struct wcd9xxx_core_resource *wcd9xxx_core_res,
  83. enum wcd9xxx_pm_state o,
  84. enum wcd9xxx_pm_state n)
  85. {
  86. enum wcd9xxx_pm_state old;
  87. mutex_lock(&wcd9xxx_core_res->pm_lock);
  88. old = wcd9xxx_core_res->pm_state;
  89. if (old == o)
  90. wcd9xxx_core_res->pm_state = n;
  91. mutex_unlock(&wcd9xxx_core_res->pm_lock);
  92. return old;
  93. }
  94. EXPORT_SYMBOL(wcd9xxx_pm_cmpxchg);
  95. int wcd9xxx_core_res_suspend(
  96. struct wcd9xxx_core_resource *wcd9xxx_core_res,
  97. pm_message_t pmesg)
  98. {
  99. int ret = 0;
  100. pr_debug("%s: enter\n", __func__);
  101. /*
  102. * pm_qos_update_request() can be called after this suspend chain call
  103. * started. thus suspend can be called while lock is being held
  104. */
  105. mutex_lock(&wcd9xxx_core_res->pm_lock);
  106. if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_SLEEPABLE) {
  107. pr_debug("%s: suspending system, state %d, wlock %d\n",
  108. __func__, wcd9xxx_core_res->pm_state,
  109. wcd9xxx_core_res->wlock_holders);
  110. wcd9xxx_core_res->pm_state = WCD9XXX_PM_ASLEEP;
  111. } else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_AWAKE) {
  112. /*
  113. * unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE
  114. * then set to WCD9XXX_PM_ASLEEP
  115. */
  116. pr_debug("%s: waiting to suspend system, state %d, wlock %d\n",
  117. __func__, wcd9xxx_core_res->pm_state,
  118. wcd9xxx_core_res->wlock_holders);
  119. mutex_unlock(&wcd9xxx_core_res->pm_lock);
  120. if (!(wait_event_timeout(wcd9xxx_core_res->pm_wq,
  121. wcd9xxx_pm_cmpxchg(wcd9xxx_core_res,
  122. WCD9XXX_PM_SLEEPABLE,
  123. WCD9XXX_PM_ASLEEP) ==
  124. WCD9XXX_PM_SLEEPABLE,
  125. HZ))) {
  126. pr_debug("%s: suspend failed state %d, wlock %d\n",
  127. __func__, wcd9xxx_core_res->pm_state,
  128. wcd9xxx_core_res->wlock_holders);
  129. ret = -EBUSY;
  130. } else {
  131. pr_debug("%s: done, state %d, wlock %d\n", __func__,
  132. wcd9xxx_core_res->pm_state,
  133. wcd9xxx_core_res->wlock_holders);
  134. }
  135. mutex_lock(&wcd9xxx_core_res->pm_lock);
  136. } else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) {
  137. pr_warn("%s: system is already suspended, state %d, wlock %dn",
  138. __func__, wcd9xxx_core_res->pm_state,
  139. wcd9xxx_core_res->wlock_holders);
  140. }
  141. mutex_unlock(&wcd9xxx_core_res->pm_lock);
  142. return ret;
  143. }
  144. EXPORT_SYMBOL(wcd9xxx_core_res_suspend);
  145. int wcd9xxx_core_res_resume(
  146. struct wcd9xxx_core_resource *wcd9xxx_core_res)
  147. {
  148. int ret = 0;
  149. pr_debug("%s: enter\n", __func__);
  150. mutex_lock(&wcd9xxx_core_res->pm_lock);
  151. if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) {
  152. pr_debug("%s: resuming system, state %d, wlock %d\n", __func__,
  153. wcd9xxx_core_res->pm_state,
  154. wcd9xxx_core_res->wlock_holders);
  155. wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE;
  156. } else {
  157. pr_warn("%s: system is already awake, state %d wlock %d\n",
  158. __func__, wcd9xxx_core_res->pm_state,
  159. wcd9xxx_core_res->wlock_holders);
  160. }
  161. mutex_unlock(&wcd9xxx_core_res->pm_lock);
  162. wake_up_all(&wcd9xxx_core_res->pm_wq);
  163. return ret;
  164. }
  165. EXPORT_SYMBOL(wcd9xxx_core_res_resume);
  166. enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void)
  167. {
  168. return wcd9xxx_intf;
  169. }
  170. EXPORT_SYMBOL(wcd9xxx_get_intf_type);
  171. void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status intf_status)
  172. {
  173. wcd9xxx_intf = intf_status;
  174. }
  175. EXPORT_SYMBOL(wcd9xxx_set_intf_type);