Commit f6085a96 authored by Sakari Ailus's avatar Sakari Ailus Committed by Greg Kroah-Hartman

mei: vsc: Unregister interrupt handler for system suspend

Unregister the MEI VSC interrupt handler before system suspend and
re-register it at system resume time. This mirrors implementation of other
MEI devices.

This patch fixes the bug that causes continuous stream of MEI VSC errors
after system resume.

Fixes: 386a766c ("mei: Add MEI hardware support for IVSC device")
Cc: stable@vger.kernel.org # for 6.8
Reported-by: default avatarDominik Brodowski <linux@dominikbrodowski.net>
Signed-off-by: default avatarWentong Wu <wentong.wu@intel.com>
Signed-off-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: default avatarTomas Winkler <tomas.winkler@intel.com>
Link: https://lore.kernel.org/r/20240403051341.3534650-2-wentong.wu@intel.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e3dc66d9
...@@ -400,25 +400,40 @@ static void mei_vsc_remove(struct platform_device *pdev) ...@@ -400,25 +400,40 @@ static void mei_vsc_remove(struct platform_device *pdev)
static int mei_vsc_suspend(struct device *dev) static int mei_vsc_suspend(struct device *dev)
{ {
struct mei_device *mei_dev = dev_get_drvdata(dev); struct mei_device *mei_dev = dev_get_drvdata(dev);
struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev);
mei_stop(mei_dev); mei_stop(mei_dev);
mei_disable_interrupts(mei_dev);
vsc_tp_free_irq(hw->tp);
return 0; return 0;
} }
static int mei_vsc_resume(struct device *dev) static int mei_vsc_resume(struct device *dev)
{ {
struct mei_device *mei_dev = dev_get_drvdata(dev); struct mei_device *mei_dev = dev_get_drvdata(dev);
struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev);
int ret; int ret;
ret = mei_restart(mei_dev); ret = vsc_tp_request_irq(hw->tp);
if (ret) if (ret)
return ret; return ret;
ret = mei_restart(mei_dev);
if (ret)
goto err_free;
/* start timer if stopped in suspend */ /* start timer if stopped in suspend */
schedule_delayed_work(&mei_dev->timer_work, HZ); schedule_delayed_work(&mei_dev->timer_work, HZ);
return 0; return 0;
err_free:
vsc_tp_free_irq(hw->tp);
return ret;
} }
static DEFINE_SIMPLE_DEV_PM_OPS(mei_vsc_pm_ops, mei_vsc_suspend, mei_vsc_resume); static DEFINE_SIMPLE_DEV_PM_OPS(mei_vsc_pm_ops, mei_vsc_suspend, mei_vsc_resume);
......
...@@ -94,6 +94,27 @@ static const struct acpi_gpio_mapping vsc_tp_acpi_gpios[] = { ...@@ -94,6 +94,27 @@ static const struct acpi_gpio_mapping vsc_tp_acpi_gpios[] = {
{} {}
}; };
static irqreturn_t vsc_tp_isr(int irq, void *data)
{
struct vsc_tp *tp = data;
atomic_inc(&tp->assert_cnt);
wake_up(&tp->xfer_wait);
return IRQ_WAKE_THREAD;
}
static irqreturn_t vsc_tp_thread_isr(int irq, void *data)
{
struct vsc_tp *tp = data;
if (tp->event_notify)
tp->event_notify(tp->event_notify_context);
return IRQ_HANDLED;
}
/* wakeup firmware and wait for response */ /* wakeup firmware and wait for response */
static int vsc_tp_wakeup_request(struct vsc_tp *tp) static int vsc_tp_wakeup_request(struct vsc_tp *tp)
{ {
...@@ -383,6 +404,37 @@ int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb, ...@@ -383,6 +404,37 @@ int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb,
} }
EXPORT_SYMBOL_NS_GPL(vsc_tp_register_event_cb, VSC_TP); EXPORT_SYMBOL_NS_GPL(vsc_tp_register_event_cb, VSC_TP);
/**
* vsc_tp_request_irq - request irq for vsc_tp device
* @tp: vsc_tp device handle
*/
int vsc_tp_request_irq(struct vsc_tp *tp)
{
struct spi_device *spi = tp->spi;
struct device *dev = &spi->dev;
int ret;
irq_set_status_flags(spi->irq, IRQ_DISABLE_UNLAZY);
ret = request_threaded_irq(spi->irq, vsc_tp_isr, vsc_tp_thread_isr,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
dev_name(dev), tp);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL_NS_GPL(vsc_tp_request_irq, VSC_TP);
/**
* vsc_tp_free_irq - free irq for vsc_tp device
* @tp: vsc_tp device handle
*/
void vsc_tp_free_irq(struct vsc_tp *tp)
{
free_irq(tp->spi->irq, tp);
}
EXPORT_SYMBOL_NS_GPL(vsc_tp_free_irq, VSC_TP);
/** /**
* vsc_tp_intr_synchronize - synchronize vsc_tp interrupt * vsc_tp_intr_synchronize - synchronize vsc_tp interrupt
* @tp: vsc_tp device handle * @tp: vsc_tp device handle
...@@ -413,27 +465,6 @@ void vsc_tp_intr_disable(struct vsc_tp *tp) ...@@ -413,27 +465,6 @@ void vsc_tp_intr_disable(struct vsc_tp *tp)
} }
EXPORT_SYMBOL_NS_GPL(vsc_tp_intr_disable, VSC_TP); EXPORT_SYMBOL_NS_GPL(vsc_tp_intr_disable, VSC_TP);
static irqreturn_t vsc_tp_isr(int irq, void *data)
{
struct vsc_tp *tp = data;
atomic_inc(&tp->assert_cnt);
wake_up(&tp->xfer_wait);
return IRQ_WAKE_THREAD;
}
static irqreturn_t vsc_tp_thread_isr(int irq, void *data)
{
struct vsc_tp *tp = data;
if (tp->event_notify)
tp->event_notify(tp->event_notify_context);
return IRQ_HANDLED;
}
static int vsc_tp_match_any(struct acpi_device *adev, void *data) static int vsc_tp_match_any(struct acpi_device *adev, void *data)
{ {
struct acpi_device **__adev = data; struct acpi_device **__adev = data;
...@@ -490,10 +521,9 @@ static int vsc_tp_probe(struct spi_device *spi) ...@@ -490,10 +521,9 @@ static int vsc_tp_probe(struct spi_device *spi)
tp->spi = spi; tp->spi = spi;
irq_set_status_flags(spi->irq, IRQ_DISABLE_UNLAZY); irq_set_status_flags(spi->irq, IRQ_DISABLE_UNLAZY);
ret = devm_request_threaded_irq(dev, spi->irq, vsc_tp_isr, ret = request_threaded_irq(spi->irq, vsc_tp_isr, vsc_tp_thread_isr,
vsc_tp_thread_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, dev_name(dev), tp);
dev_name(dev), tp);
if (ret) if (ret)
return ret; return ret;
...@@ -522,6 +552,8 @@ static int vsc_tp_probe(struct spi_device *spi) ...@@ -522,6 +552,8 @@ static int vsc_tp_probe(struct spi_device *spi)
err_destroy_lock: err_destroy_lock:
mutex_destroy(&tp->mutex); mutex_destroy(&tp->mutex);
free_irq(spi->irq, tp);
return ret; return ret;
} }
...@@ -532,6 +564,8 @@ static void vsc_tp_remove(struct spi_device *spi) ...@@ -532,6 +564,8 @@ static void vsc_tp_remove(struct spi_device *spi)
platform_device_unregister(tp->pdev); platform_device_unregister(tp->pdev);
mutex_destroy(&tp->mutex); mutex_destroy(&tp->mutex);
free_irq(spi->irq, tp);
} }
static const struct acpi_device_id vsc_tp_acpi_ids[] = { static const struct acpi_device_id vsc_tp_acpi_ids[] = {
......
...@@ -37,6 +37,9 @@ int vsc_tp_xfer(struct vsc_tp *tp, u8 cmd, const void *obuf, size_t olen, ...@@ -37,6 +37,9 @@ int vsc_tp_xfer(struct vsc_tp *tp, u8 cmd, const void *obuf, size_t olen,
int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb, int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb,
void *context); void *context);
int vsc_tp_request_irq(struct vsc_tp *tp);
void vsc_tp_free_irq(struct vsc_tp *tp);
void vsc_tp_intr_enable(struct vsc_tp *tp); void vsc_tp_intr_enable(struct vsc_tp *tp);
void vsc_tp_intr_disable(struct vsc_tp *tp); void vsc_tp_intr_disable(struct vsc_tp *tp);
void vsc_tp_intr_synchronize(struct vsc_tp *tp); void vsc_tp_intr_synchronize(struct vsc_tp *tp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment