198 lines
5.9 KiB
C
198 lines
5.9 KiB
C
|
/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 and
|
||
|
* only version 2 as published by the Free Software Foundation.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*/
|
||
|
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/types.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/wait.h>
|
||
|
#include <linux/sched.h>
|
||
|
#include <linux/mfd/wcd9xxx/core-resource.h>
|
||
|
|
||
|
|
||
|
static enum wcd9xxx_intf_status wcd9xxx_intf = -1;
|
||
|
|
||
|
int wcd9xxx_core_irq_init(
|
||
|
struct wcd9xxx_core_resource *wcd9xxx_core_res)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
if (wcd9xxx_core_res->irq != 1) {
|
||
|
ret = wcd9xxx_irq_init(wcd9xxx_core_res);
|
||
|
if (ret)
|
||
|
pr_err("IRQ initialization failed\n");
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(wcd9xxx_core_irq_init);
|
||
|
|
||
|
int wcd9xxx_initialize_irq(
|
||
|
struct wcd9xxx_core_resource *wcd9xxx_core_res,
|
||
|
unsigned int irq,
|
||
|
unsigned int irq_base)
|
||
|
{
|
||
|
wcd9xxx_core_res->irq = irq;
|
||
|
wcd9xxx_core_res->irq_base = irq_base;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(wcd9xxx_initialize_irq);
|
||
|
|
||
|
int wcd9xxx_core_res_init(
|
||
|
struct wcd9xxx_core_resource *wcd9xxx_core_res,
|
||
|
int num_irqs, int num_irq_regs,
|
||
|
int (*codec_read)(struct wcd9xxx_core_resource*, unsigned short),
|
||
|
int (*codec_write)(struct wcd9xxx_core_resource*, unsigned short, u8),
|
||
|
int (*codec_bulk_read) (struct wcd9xxx_core_resource*, unsigned short,
|
||
|
int, u8*),
|
||
|
int (*codec_bulk_write) (struct wcd9xxx_core_resource*, unsigned short,
|
||
|
int, u8*))
|
||
|
{
|
||
|
mutex_init(&wcd9xxx_core_res->pm_lock);
|
||
|
wcd9xxx_core_res->wlock_holders = 0;
|
||
|
wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE;
|
||
|
init_waitqueue_head(&wcd9xxx_core_res->pm_wq);
|
||
|
pm_qos_add_request(&wcd9xxx_core_res->pm_qos_req,
|
||
|
PM_QOS_CPU_DMA_LATENCY,
|
||
|
PM_QOS_DEFAULT_VALUE);
|
||
|
|
||
|
wcd9xxx_core_res->codec_reg_read = codec_read;
|
||
|
wcd9xxx_core_res->codec_reg_write = codec_write;
|
||
|
wcd9xxx_core_res->codec_bulk_read = codec_bulk_read;
|
||
|
wcd9xxx_core_res->codec_bulk_write = codec_bulk_write;
|
||
|
wcd9xxx_core_res->num_irqs = num_irqs;
|
||
|
wcd9xxx_core_res->num_irq_regs = num_irq_regs;
|
||
|
|
||
|
pr_info("%s: num_irqs = %d, num_irq_regs = %d\n",
|
||
|
__func__, wcd9xxx_core_res->num_irqs,
|
||
|
wcd9xxx_core_res->num_irq_regs);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(wcd9xxx_core_res_init);
|
||
|
|
||
|
void wcd9xxx_core_res_deinit(struct wcd9xxx_core_resource *wcd9xxx_core_res)
|
||
|
{
|
||
|
pm_qos_remove_request(&wcd9xxx_core_res->pm_qos_req);
|
||
|
mutex_destroy(&wcd9xxx_core_res->pm_lock);
|
||
|
wcd9xxx_core_res->codec_reg_read = NULL;
|
||
|
wcd9xxx_core_res->codec_reg_write = NULL;
|
||
|
wcd9xxx_core_res->codec_bulk_read = NULL;
|
||
|
}
|
||
|
EXPORT_SYMBOL(wcd9xxx_core_res_deinit);
|
||
|
|
||
|
enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(
|
||
|
struct wcd9xxx_core_resource *wcd9xxx_core_res,
|
||
|
enum wcd9xxx_pm_state o,
|
||
|
enum wcd9xxx_pm_state n)
|
||
|
{
|
||
|
enum wcd9xxx_pm_state old;
|
||
|
mutex_lock(&wcd9xxx_core_res->pm_lock);
|
||
|
old = wcd9xxx_core_res->pm_state;
|
||
|
if (old == o)
|
||
|
wcd9xxx_core_res->pm_state = n;
|
||
|
mutex_unlock(&wcd9xxx_core_res->pm_lock);
|
||
|
return old;
|
||
|
}
|
||
|
EXPORT_SYMBOL(wcd9xxx_pm_cmpxchg);
|
||
|
|
||
|
int wcd9xxx_core_res_suspend(
|
||
|
struct wcd9xxx_core_resource *wcd9xxx_core_res,
|
||
|
pm_message_t pmesg)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_debug("%s: enter\n", __func__);
|
||
|
/*
|
||
|
* pm_qos_update_request() can be called after this suspend chain call
|
||
|
* started. thus suspend can be called while lock is being held
|
||
|
*/
|
||
|
mutex_lock(&wcd9xxx_core_res->pm_lock);
|
||
|
if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_SLEEPABLE) {
|
||
|
pr_debug("%s: suspending system, state %d, wlock %d\n",
|
||
|
__func__, wcd9xxx_core_res->pm_state,
|
||
|
wcd9xxx_core_res->wlock_holders);
|
||
|
wcd9xxx_core_res->pm_state = WCD9XXX_PM_ASLEEP;
|
||
|
} else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_AWAKE) {
|
||
|
/*
|
||
|
* unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE
|
||
|
* then set to WCD9XXX_PM_ASLEEP
|
||
|
*/
|
||
|
pr_debug("%s: waiting to suspend system, state %d, wlock %d\n",
|
||
|
__func__, wcd9xxx_core_res->pm_state,
|
||
|
wcd9xxx_core_res->wlock_holders);
|
||
|
mutex_unlock(&wcd9xxx_core_res->pm_lock);
|
||
|
if (!(wait_event_timeout(wcd9xxx_core_res->pm_wq,
|
||
|
wcd9xxx_pm_cmpxchg(wcd9xxx_core_res,
|
||
|
WCD9XXX_PM_SLEEPABLE,
|
||
|
WCD9XXX_PM_ASLEEP) ==
|
||
|
WCD9XXX_PM_SLEEPABLE,
|
||
|
HZ))) {
|
||
|
pr_debug("%s: suspend failed state %d, wlock %d\n",
|
||
|
__func__, wcd9xxx_core_res->pm_state,
|
||
|
wcd9xxx_core_res->wlock_holders);
|
||
|
ret = -EBUSY;
|
||
|
} else {
|
||
|
pr_debug("%s: done, state %d, wlock %d\n", __func__,
|
||
|
wcd9xxx_core_res->pm_state,
|
||
|
wcd9xxx_core_res->wlock_holders);
|
||
|
}
|
||
|
mutex_lock(&wcd9xxx_core_res->pm_lock);
|
||
|
} else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) {
|
||
|
pr_warn("%s: system is already suspended, state %d, wlock %dn",
|
||
|
__func__, wcd9xxx_core_res->pm_state,
|
||
|
wcd9xxx_core_res->wlock_holders);
|
||
|
}
|
||
|
mutex_unlock(&wcd9xxx_core_res->pm_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(wcd9xxx_core_res_suspend);
|
||
|
|
||
|
int wcd9xxx_core_res_resume(
|
||
|
struct wcd9xxx_core_resource *wcd9xxx_core_res)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_debug("%s: enter\n", __func__);
|
||
|
mutex_lock(&wcd9xxx_core_res->pm_lock);
|
||
|
if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) {
|
||
|
pr_debug("%s: resuming system, state %d, wlock %d\n", __func__,
|
||
|
wcd9xxx_core_res->pm_state,
|
||
|
wcd9xxx_core_res->wlock_holders);
|
||
|
wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE;
|
||
|
} else {
|
||
|
pr_warn("%s: system is already awake, state %d wlock %d\n",
|
||
|
__func__, wcd9xxx_core_res->pm_state,
|
||
|
wcd9xxx_core_res->wlock_holders);
|
||
|
}
|
||
|
mutex_unlock(&wcd9xxx_core_res->pm_lock);
|
||
|
wake_up_all(&wcd9xxx_core_res->pm_wq);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(wcd9xxx_core_res_resume);
|
||
|
|
||
|
enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void)
|
||
|
{
|
||
|
return wcd9xxx_intf;
|
||
|
}
|
||
|
EXPORT_SYMBOL(wcd9xxx_get_intf_type);
|
||
|
|
||
|
void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status intf_status)
|
||
|
{
|
||
|
wcd9xxx_intf = intf_status;
|
||
|
}
|
||
|
EXPORT_SYMBOL(wcd9xxx_set_intf_type);
|