Skip to content
1 change: 1 addition & 0 deletions drivers/misc/mei/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ config INTEL_MEI_GSC

config INTEL_MEI_VSC_HW
tristate "Intel visual sensing controller device transport driver"
depends on INTEL_MEI
depends on ACPI && SPI
depends on GPIOLIB || COMPILE_TEST
help
Expand Down
8 changes: 7 additions & 1 deletion drivers/misc/mei/platform-vsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ static int mei_vsc_hw_reset(struct mei_device *mei_dev, bool intr_enable)

vsc_tp_reset(hw->tp);

vsc_tp_intr_disable(hw->tp);
if (!intr_enable)
return 0;

return vsc_tp_init(hw->tp, mei_dev->dev);
}
Expand Down Expand Up @@ -379,6 +380,8 @@ static int mei_vsc_probe(struct platform_device *pdev)
err_cancel:
mei_cancel_work(mei_dev);

vsc_tp_register_event_cb(tp, NULL, NULL);

mei_disable_interrupts(mei_dev);

return ret;
Expand All @@ -387,11 +390,14 @@ static int mei_vsc_probe(struct platform_device *pdev)
static void mei_vsc_remove(struct platform_device *pdev)
{
struct mei_device *mei_dev = platform_get_drvdata(pdev);
struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev);

pm_runtime_disable(mei_dev->dev);

mei_stop(mei_dev);

vsc_tp_register_event_cb(hw->tp, NULL, NULL);

mei_disable_interrupts(mei_dev);

mei_deregister(mei_dev);
Expand Down
41 changes: 25 additions & 16 deletions drivers/misc/mei/vsc-tp.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include <linux/workqueue.h>

#include "vsc-tp.h"

Expand Down Expand Up @@ -72,12 +73,12 @@ struct vsc_tp {

atomic_t assert_cnt;
wait_queue_head_t xfer_wait;
struct work_struct event_work;

vsc_tp_event_cb_t event_notify;
void *event_notify_context;

/* used to protect command download */
struct mutex mutex;
struct mutex event_notify_mutex; /* protects event_notify + context */
struct mutex mutex; /* protects command download */
};

/* GPIO resources */
Expand All @@ -102,17 +103,19 @@ static irqreturn_t vsc_tp_isr(int irq, void *data)

wake_up(&tp->xfer_wait);

return IRQ_WAKE_THREAD;
schedule_work(&tp->event_work);

return IRQ_HANDLED;
}

static irqreturn_t vsc_tp_thread_isr(int irq, void *data)
static void vsc_tp_event_work(struct work_struct *work)
{
struct vsc_tp *tp = data;
struct vsc_tp *tp = container_of(work, struct vsc_tp, event_work);

guard(mutex)(&tp->event_notify_mutex);

if (tp->event_notify)
tp->event_notify(tp->event_notify_context);

return IRQ_HANDLED;
}

/* wakeup firmware and wait for response */
Expand Down Expand Up @@ -364,8 +367,6 @@ void vsc_tp_reset(struct vsc_tp *tp)
gpiod_set_value_cansleep(tp->wakeupfw, 1);

atomic_set(&tp->assert_cnt, 0);

enable_irq(tp->spi->irq);
}
EXPORT_SYMBOL_NS_GPL(vsc_tp_reset, VSC_TP);

Expand Down Expand Up @@ -397,6 +398,8 @@ EXPORT_SYMBOL_NS_GPL(vsc_tp_need_read, VSC_TP);
int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb,
void *context)
{
guard(mutex)(&tp->event_notify_mutex);

tp->event_notify = event_cb;
tp->event_notify_context = context;

Expand Down Expand Up @@ -504,7 +507,7 @@ static int vsc_tp_probe(struct spi_device *spi)
if (ret)
return ret;

tp->wakeuphost = devm_gpiod_get(dev, "wakeuphost", GPIOD_IN);
tp->wakeuphost = devm_gpiod_get(dev, "wakeuphostint", GPIOD_IN);
if (IS_ERR(tp->wakeuphost))
return PTR_ERR(tp->wakeuphost);

Expand All @@ -521,13 +524,15 @@ static int vsc_tp_probe(struct spi_device *spi)
tp->spi = spi;

irq_set_status_flags(spi->irq, IRQ_DISABLE_UNLAZY);
ret = request_threaded_irq(spi->irq, vsc_tp_isr, vsc_tp_thread_isr,
ret = request_threaded_irq(spi->irq, NULL, vsc_tp_isr,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
dev_name(dev), tp);
if (ret)
return ret;

mutex_init(&tp->mutex);
mutex_init(&tp->event_notify_mutex);
INIT_WORK(&tp->event_work, vsc_tp_event_work);

/* only one child acpi device */
ret = acpi_dev_for_each_child(ACPI_COMPANION(dev),
Expand All @@ -550,10 +555,12 @@ static int vsc_tp_probe(struct spi_device *spi)
return 0;

err_destroy_lock:
mutex_destroy(&tp->mutex);

free_irq(spi->irq, tp);

cancel_work_sync(&tp->event_work);
mutex_destroy(&tp->event_notify_mutex);
mutex_destroy(&tp->mutex);

return ret;
}

Expand All @@ -563,9 +570,11 @@ static void vsc_tp_remove(struct spi_device *spi)

platform_device_unregister(tp->pdev);

mutex_destroy(&tp->mutex);

free_irq(spi->irq, tp);

cancel_work_sync(&tp->event_work);
mutex_destroy(&tp->event_notify_mutex);
mutex_destroy(&tp->mutex);
}

static const struct acpi_device_id vsc_tp_acpi_ids[] = {
Expand Down
Loading