/* 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 #include #include #include #include #include #include #include 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);