blob: 1d0f894f9cb40e2200a474dfede6e46582036394 [file] [log] [blame]
Yeleswarapu, Nagaradheshbbba6e32013-12-20 18:09:17 +05301/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -07002 *
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
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/types.h>
16#include <linux/delay.h>
17#include <linux/slab.h>
18#include <linux/wait.h>
19#include <linux/sched.h>
20#include <linux/mfd/wcd9xxx/core-resource.h>
21
22
23static enum wcd9xxx_intf_status wcd9xxx_intf = -1;
24
25int wcd9xxx_core_irq_init(
26 struct wcd9xxx_core_resource *wcd9xxx_core_res)
27{
28 int ret = 0;
29
30 if (wcd9xxx_core_res->irq != 1) {
31 ret = wcd9xxx_irq_init(wcd9xxx_core_res);
32 if (ret)
33 pr_err("IRQ initialization failed\n");
34 }
35
36 return ret;
37}
38EXPORT_SYMBOL(wcd9xxx_core_irq_init);
39
40int wcd9xxx_initialize_irq(
41 struct wcd9xxx_core_resource *wcd9xxx_core_res,
42 unsigned int irq,
43 unsigned int irq_base)
44{
45 wcd9xxx_core_res->irq = irq;
46 wcd9xxx_core_res->irq_base = irq_base;
47
48 return 0;
49}
50EXPORT_SYMBOL(wcd9xxx_initialize_irq);
51
52int wcd9xxx_core_res_init(
53 struct wcd9xxx_core_resource *wcd9xxx_core_res,
54 int num_irqs, int num_irq_regs,
55 int (*codec_read)(struct wcd9xxx_core_resource*, unsigned short),
56 int (*codec_write)(struct wcd9xxx_core_resource*, unsigned short, u8),
57 int (*codec_bulk_read) (struct wcd9xxx_core_resource*, unsigned short,
Yeleswarapu, Nagaradheshbbba6e32013-12-20 18:09:17 +053058 int, u8*),
59 int (*codec_bulk_write) (struct wcd9xxx_core_resource*, unsigned short,
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -070060 int, u8*))
61{
62 mutex_init(&wcd9xxx_core_res->pm_lock);
63 wcd9xxx_core_res->wlock_holders = 0;
64 wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE;
65 init_waitqueue_head(&wcd9xxx_core_res->pm_wq);
66 pm_qos_add_request(&wcd9xxx_core_res->pm_qos_req,
67 PM_QOS_CPU_DMA_LATENCY,
68 PM_QOS_DEFAULT_VALUE);
69
70 wcd9xxx_core_res->codec_reg_read = codec_read;
71 wcd9xxx_core_res->codec_reg_write = codec_write;
72 wcd9xxx_core_res->codec_bulk_read = codec_bulk_read;
Yeleswarapu, Nagaradheshbbba6e32013-12-20 18:09:17 +053073 wcd9xxx_core_res->codec_bulk_write = codec_bulk_write;
Bhalchandra Gajare9b4eb3b2013-04-30 12:00:41 -070074 wcd9xxx_core_res->num_irqs = num_irqs;
75 wcd9xxx_core_res->num_irq_regs = num_irq_regs;
76
77 pr_info("%s: num_irqs = %d, num_irq_regs = %d\n",
78 __func__, wcd9xxx_core_res->num_irqs,
79 wcd9xxx_core_res->num_irq_regs);
80
81 return 0;
82}
83EXPORT_SYMBOL(wcd9xxx_core_res_init);
84
85void wcd9xxx_core_res_deinit(struct wcd9xxx_core_resource *wcd9xxx_core_res)
86{
87 pm_qos_remove_request(&wcd9xxx_core_res->pm_qos_req);
88 mutex_destroy(&wcd9xxx_core_res->pm_lock);
89 wcd9xxx_core_res->codec_reg_read = NULL;
90 wcd9xxx_core_res->codec_reg_write = NULL;
91 wcd9xxx_core_res->codec_bulk_read = NULL;
92}
93EXPORT_SYMBOL(wcd9xxx_core_res_deinit);
94
95enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(
96 struct wcd9xxx_core_resource *wcd9xxx_core_res,
97 enum wcd9xxx_pm_state o,
98 enum wcd9xxx_pm_state n)
99{
100 enum wcd9xxx_pm_state old;
101 mutex_lock(&wcd9xxx_core_res->pm_lock);
102 old = wcd9xxx_core_res->pm_state;
103 if (old == o)
104 wcd9xxx_core_res->pm_state = n;
105 mutex_unlock(&wcd9xxx_core_res->pm_lock);
106 return old;
107}
108EXPORT_SYMBOL(wcd9xxx_pm_cmpxchg);
109
110int wcd9xxx_core_res_suspend(
111 struct wcd9xxx_core_resource *wcd9xxx_core_res,
112 pm_message_t pmesg)
113{
114 int ret = 0;
115
116 pr_debug("%s: enter\n", __func__);
117 /*
118 * pm_qos_update_request() can be called after this suspend chain call
119 * started. thus suspend can be called while lock is being held
120 */
121 mutex_lock(&wcd9xxx_core_res->pm_lock);
122 if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_SLEEPABLE) {
123 pr_debug("%s: suspending system, state %d, wlock %d\n",
124 __func__, wcd9xxx_core_res->pm_state,
125 wcd9xxx_core_res->wlock_holders);
126 wcd9xxx_core_res->pm_state = WCD9XXX_PM_ASLEEP;
127 } else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_AWAKE) {
128 /*
129 * unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE
130 * then set to WCD9XXX_PM_ASLEEP
131 */
132 pr_debug("%s: waiting to suspend system, state %d, wlock %d\n",
133 __func__, wcd9xxx_core_res->pm_state,
134 wcd9xxx_core_res->wlock_holders);
135 mutex_unlock(&wcd9xxx_core_res->pm_lock);
136 if (!(wait_event_timeout(wcd9xxx_core_res->pm_wq,
137 wcd9xxx_pm_cmpxchg(wcd9xxx_core_res,
138 WCD9XXX_PM_SLEEPABLE,
139 WCD9XXX_PM_ASLEEP) ==
140 WCD9XXX_PM_SLEEPABLE,
141 HZ))) {
142 pr_debug("%s: suspend failed state %d, wlock %d\n",
143 __func__, wcd9xxx_core_res->pm_state,
144 wcd9xxx_core_res->wlock_holders);
145 ret = -EBUSY;
146 } else {
147 pr_debug("%s: done, state %d, wlock %d\n", __func__,
148 wcd9xxx_core_res->pm_state,
149 wcd9xxx_core_res->wlock_holders);
150 }
151 mutex_lock(&wcd9xxx_core_res->pm_lock);
152 } else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) {
153 pr_warn("%s: system is already suspended, state %d, wlock %dn",
154 __func__, wcd9xxx_core_res->pm_state,
155 wcd9xxx_core_res->wlock_holders);
156 }
157 mutex_unlock(&wcd9xxx_core_res->pm_lock);
158
159 return ret;
160}
161EXPORT_SYMBOL(wcd9xxx_core_res_suspend);
162
163int wcd9xxx_core_res_resume(
164 struct wcd9xxx_core_resource *wcd9xxx_core_res)
165{
166 int ret = 0;
167
168 pr_debug("%s: enter\n", __func__);
169 mutex_lock(&wcd9xxx_core_res->pm_lock);
170 if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) {
171 pr_debug("%s: resuming system, state %d, wlock %d\n", __func__,
172 wcd9xxx_core_res->pm_state,
173 wcd9xxx_core_res->wlock_holders);
174 wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE;
175 } else {
176 pr_warn("%s: system is already awake, state %d wlock %d\n",
177 __func__, wcd9xxx_core_res->pm_state,
178 wcd9xxx_core_res->wlock_holders);
179 }
180 mutex_unlock(&wcd9xxx_core_res->pm_lock);
181 wake_up_all(&wcd9xxx_core_res->pm_wq);
182
183 return ret;
184}
185EXPORT_SYMBOL(wcd9xxx_core_res_resume);
186
187enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void)
188{
189 return wcd9xxx_intf;
190}
191EXPORT_SYMBOL(wcd9xxx_get_intf_type);
192
193void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status intf_status)
194{
195 wcd9xxx_intf = intf_status;
196}
197EXPORT_SYMBOL(wcd9xxx_set_intf_type);