aboutsummaryrefslogtreecommitdiff
path: root/drivers/media
diff options
context:
space:
mode:
authorHans Verkuil2017-08-16 03:13:02 -0400
committerMauro Carvalho Chehab2017-08-20 08:15:00 -0400
commitcb7474949371ba8d7591a62924f05b627a2601d9 (patch)
treeb955604c97aee25488af1724241cf15dd3ed9357 /drivers/media
parent9a6b2a87405a5022660022722d4a830b768e8033 (diff)
media: cec-pin: fix irq handling
The free_irq() function could be called from interrupt context, which is invalid. Move this to the thread. In the interrupt handler we just request that the thread disables the irq. This is done through an atomic so we don't need to add any spinlocks. Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/cec/cec-pin.c34
1 files changed, 21 insertions, 13 deletions
diff --git a/drivers/media/cec/cec-pin.c b/drivers/media/cec/cec-pin.c
index 31a26d3b8bd8..970774de3dcd 100644
--- a/drivers/media/cec/cec-pin.c
+++ b/drivers/media/cec/cec-pin.c
@@ -557,7 +557,7 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
adap->is_configured || adap->monitor_all_cnt)
break;
/* Switch to interrupt mode */
- pin->work_enable_irq = true;
+ atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_ENABLE);
pin->state = CEC_ST_RX_IRQ;
wake_up_interruptible(&pin->kthread_waitq);
return HRTIMER_NORESTART;
@@ -591,7 +591,7 @@ static int cec_pin_thread_func(void *_adap)
kthread_should_stop() ||
pin->work_rx_msg.len ||
pin->work_tx_status ||
- pin->work_enable_irq ||
+ atomic_read(&pin->work_irq_change) ||
atomic_read(&pin->work_pin_events));
if (pin->work_rx_msg.len) {
@@ -606,6 +606,7 @@ static int cec_pin_thread_func(void *_adap)
cec_transmit_attempt_done_ts(adap, tx_status,
pin->work_tx_ts);
}
+
while (atomic_read(&pin->work_pin_events)) {
unsigned int idx = pin->work_pin_events_rd;
@@ -615,14 +616,26 @@ static int cec_pin_thread_func(void *_adap)
pin->work_pin_events_rd = (idx + 1) % CEC_NUM_PIN_EVENTS;
atomic_dec(&pin->work_pin_events);
}
- if (pin->work_enable_irq) {
- pin->work_enable_irq = false;
+
+ switch (atomic_xchg(&pin->work_irq_change,
+ CEC_PIN_IRQ_UNCHANGED)) {
+ case CEC_PIN_IRQ_DISABLE:
+ pin->ops->disable_irq(adap);
+ cec_pin_high(pin);
+ cec_pin_to_idle(pin);
+ hrtimer_start(&pin->timer, 0, HRTIMER_MODE_REL);
+ break;
+ case CEC_PIN_IRQ_ENABLE:
pin->enable_irq_failed = !pin->ops->enable_irq(adap);
if (pin->enable_irq_failed) {
cec_pin_to_idle(pin);
hrtimer_start(&pin->timer, 0, HRTIMER_MODE_REL);
}
+ break;
+ default:
+ break;
}
+
if (kthread_should_stop())
break;
}
@@ -641,7 +654,7 @@ static int cec_pin_adap_enable(struct cec_adapter *adap, bool enable)
cec_pin_to_idle(pin);
pin->tx_msg.len = 0;
pin->timer_ts = 0;
- pin->work_enable_irq = false;
+ atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_UNCHANGED);
pin->kthread = kthread_run(cec_pin_thread_func, adap,
"cec-pin");
if (IS_ERR(pin->kthread)) {
@@ -682,7 +695,7 @@ static int cec_pin_adap_transmit(struct cec_adapter *adap, u8 attempts,
pin->work_tx_status = 0;
pin->tx_bit = 0;
if (pin->state == CEC_ST_RX_IRQ) {
- pin->work_enable_irq = false;
+ atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_UNCHANGED);
pin->ops->disable_irq(adap);
cec_pin_high(pin);
cec_pin_to_idle(pin);
@@ -745,13 +758,8 @@ void cec_pin_changed(struct cec_adapter *adap, bool value)
cec_pin_update(pin, value, false);
if (!value && (adap->is_configuring || adap->is_configured ||
- adap->monitor_all_cnt)) {
- pin->work_enable_irq = false;
- pin->ops->disable_irq(adap);
- cec_pin_high(pin);
- cec_pin_to_idle(pin);
- hrtimer_start(&pin->timer, 0, HRTIMER_MODE_REL);
- }
+ adap->monitor_all_cnt))
+ atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_DISABLE);
}
EXPORT_SYMBOL_GPL(cec_pin_changed);