soc: swr-mstr: add support for hardware wakeup interrupt

Add changes to register hardware wake up interrupt
from soundwire data line or ipc wake up interrupt
from LPASS.

Change-Id: Ibe4277db79aa62f34e9b325ae656b3f3fa7a2bcb
Signed-off-by: Aditya Bavanari <abavanar@codeaurora.org>
This commit is contained in:
Aditya Bavanari 2018-11-12 22:55:11 +05:30
commit c034fadb2c
3 changed files with 93 additions and 16 deletions

View file

@ -26,6 +26,7 @@ enum {
SWR_CLK_FREQ,
SWR_DEVICE_SSR_DOWN,
SWR_DEVICE_SSR_UP,
SWR_REGISTER_WAKE_IRQ,
};
struct swr_mstr_port {

View file

@ -1414,6 +1414,32 @@ handle_irq:
return ret;
}
static irqreturn_t swrm_wakeup_interrupt(int irq, void *dev)
{
struct swr_mstr_ctrl *swrm = dev;
int ret = IRQ_HANDLED;
if (!swrm || !(swrm->dev)) {
pr_err("%s: swrm or dev is null\n", __func__);
return IRQ_NONE;
}
mutex_lock(&swrm->devlock);
if (!swrm->dev_up) {
if (swrm->wake_irq > 0)
disable_irq_nosync(swrm->wake_irq);
mutex_unlock(&swrm->devlock);
return ret;
}
mutex_unlock(&swrm->devlock);
if (swrm->wake_irq > 0)
disable_irq_nosync(swrm->wake_irq);
pm_runtime_get_sync(swrm->dev);
pm_runtime_mark_last_busy(swrm->dev);
pm_runtime_put_autosuspend(swrm->dev);
return ret;
}
static void swrm_wakeup_work(struct work_struct *work)
{
struct swr_mstr_ctrl *swrm;
@ -1585,8 +1611,8 @@ static int swrm_event_notify(struct notifier_block *self,
schedule_work(&(swrm->dc_presence_work));
break;
case SWR_WAKE_IRQ_EVENT:
if (swrm->wakeup_req && !swrm->wakeup_triggered) {
swrm->wakeup_triggered = true;
if (swrm->ipc_wakeup && !swrm->ipc_wakeup_triggered) {
swrm->ipc_wakeup_triggered = true;
schedule_work(&swrm->wakeup_work);
}
break;
@ -1762,6 +1788,8 @@ static int swrm_probe(struct platform_device *pdev)
swrm->mclk_freq = MCLK_FREQ;
swrm->dev_up = true;
swrm->state = SWR_MSTR_UP;
swrm->ipc_wakeup = false;
swrm->ipc_wakeup_triggered = false;
init_completion(&swrm->reset);
init_completion(&swrm->broadcast);
init_completion(&swrm->clk_off_complete);
@ -1789,11 +1817,6 @@ static int swrm_probe(struct platform_device *pdev)
}
}
if (of_property_read_u32(swrm->dev->of_node,
"qcom,swr-wakeup-required", &swrm->wakeup_req)) {
swrm->wakeup_req = false;
}
if (swrm->reg_irq) {
ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm,
SWR_IRQ_REGISTER);
@ -1903,6 +1926,9 @@ static int swrm_remove(struct platform_device *pdev)
swrm, SWR_IRQ_FREE);
else if (swrm->irq)
free_irq(swrm->irq, swrm);
else if (swrm->wake_irq > 0)
free_irq(swrm->wake_irq, swrm);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
swr_unregister_master(&swrm->master);
@ -1944,9 +1970,10 @@ static int swrm_runtime_resume(struct device *dev)
if ((swrm->state == SWR_MSTR_DOWN) ||
(swrm->state == SWR_MSTR_SSR && swrm->dev_up)) {
if (swrm->clk_stop_mode0_supp && swrm->wakeup_req) {
msm_aud_evt_blocking_notifier_call_chain(
SWR_WAKE_IRQ_DEREGISTER, (void *)swrm);
if (swrm->clk_stop_mode0_supp) {
if (swrm->ipc_wakeup)
msm_aud_evt_blocking_notifier_call_chain(
SWR_WAKE_IRQ_DEREGISTER, (void *)swrm);
}
if (swrm_clk_request(swrm, true))
@ -2025,10 +2052,14 @@ static int swrm_runtime_suspend(struct device *dev)
}
swrm_clk_request(swrm, false);
if (swrm->clk_stop_mode0_supp && swrm->wakeup_req) {
msm_aud_evt_blocking_notifier_call_chain(
SWR_WAKE_IRQ_REGISTER, (void *)swrm);
swrm->wakeup_triggered = false;
if (swrm->clk_stop_mode0_supp) {
if (swrm->wake_irq > 0) {
enable_irq(swrm->wake_irq);
} else if (swrm->ipc_wakeup) {
msm_aud_evt_blocking_notifier_call_chain(
SWR_WAKE_IRQ_REGISTER, (void *)swrm);
swrm->ipc_wakeup_triggered = false;
}
}
}
@ -2064,6 +2095,35 @@ static int swrm_device_down(struct device *dev)
return 0;
}
int swrm_register_wake_irq(struct swr_mstr_ctrl *swrm)
{
int ret = 0;
if (!swrm->ipc_wakeup) {
swrm->wake_irq = platform_get_irq_byname(swrm->pdev,
"swr_wake_irq");
if (swrm->wake_irq < 0) {
dev_err(swrm->dev,
"%s() error getting wake irq handle: %d\n",
__func__, swrm->wake_irq);
return -EINVAL;
}
ret = request_threaded_irq(swrm->wake_irq, NULL,
swrm_wakeup_interrupt,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"swr_wake_irq", swrm);
if (ret) {
dev_err(swrm->dev, "%s: Failed to request irq %d\n",
__func__, ret);
return -EINVAL;
}
/* Disable wake irq - enable it after clock stop */
disable_irq(swrm->wake_irq);
}
return ret;
}
/**
* swrm_wcd_notify - parent device can notify to soundwire master through
* this function
@ -2181,6 +2241,21 @@ int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data)
mutex_unlock(&swrm->mlock);
}
break;
case SWR_REGISTER_WAKE_IRQ:
if (!data) {
dev_err(swrm->dev, "%s: reg wake irq data is NULL\n",
__func__);
ret = -EINVAL;
} else {
mutex_lock(&swrm->mlock);
swrm->ipc_wakeup = *(u32 *)data;
ret = swrm_register_wake_irq(swrm);
if (ret)
dev_err(swrm->dev, "%s: register wake_irq failed\n",
__func__);
mutex_unlock(&swrm->mlock);
}
break;
default:
dev_err(swrm->dev, "%s: swr master unknown id %d\n",
__func__, id);

View file

@ -131,6 +131,7 @@ struct swr_mstr_ctrl {
int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
void *data), void *swr_handle, int type);
int irq;
int wake_irq;
int version;
int mclk_freq;
u32 num_dev;
@ -152,9 +153,9 @@ struct swr_mstr_ctrl {
int swr_irq;
u32 clk_stop_mode0_supp;
struct work_struct wakeup_work;
u32 wakeup_req;
u32 ipc_wakeup;
bool dev_up;
bool wakeup_triggered;
bool ipc_wakeup_triggered;
};
#endif /* _SWR_WCD_CTRL_H */