forked from Minki/linux
mfd: ezx-pcap: Replace mutex_lock with spin_lock
As mutex_lock might sleep. Function pcap_adc_irq is an interrupt handler. The use of mutex_lock in pcap_adc_irq may cause sleep in IRQ context. Replace mutex_lock with spin_lock to avoid this. Signed-off-by: Fuqian Huang <huangfq.daxian@gmail.com> Signed-off-by: Lee Jones <lee.jones@linaro.org>
This commit is contained in:
parent
5cd690a308
commit
b65dc4f6b3
@ -35,7 +35,7 @@ struct pcap_chip {
|
|||||||
|
|
||||||
/* IO */
|
/* IO */
|
||||||
u32 buf;
|
u32 buf;
|
||||||
struct mutex io_mutex;
|
spinlock_t io_lock;
|
||||||
|
|
||||||
/* IRQ */
|
/* IRQ */
|
||||||
unsigned int irq_base;
|
unsigned int irq_base;
|
||||||
@ -48,7 +48,7 @@ struct pcap_chip {
|
|||||||
struct pcap_adc_request *adc_queue[PCAP_ADC_MAXQ];
|
struct pcap_adc_request *adc_queue[PCAP_ADC_MAXQ];
|
||||||
u8 adc_head;
|
u8 adc_head;
|
||||||
u8 adc_tail;
|
u8 adc_tail;
|
||||||
struct mutex adc_mutex;
|
spinlock_t adc_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* IO */
|
/* IO */
|
||||||
@ -76,14 +76,15 @@ static int ezx_pcap_putget(struct pcap_chip *pcap, u32 *data)
|
|||||||
|
|
||||||
int ezx_pcap_write(struct pcap_chip *pcap, u8 reg_num, u32 value)
|
int ezx_pcap_write(struct pcap_chip *pcap, u8 reg_num, u32 value)
|
||||||
{
|
{
|
||||||
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&pcap->io_mutex);
|
spin_lock_irqsave(&pcap->io_lock, flags);
|
||||||
value &= PCAP_REGISTER_VALUE_MASK;
|
value &= PCAP_REGISTER_VALUE_MASK;
|
||||||
value |= PCAP_REGISTER_WRITE_OP_BIT
|
value |= PCAP_REGISTER_WRITE_OP_BIT
|
||||||
| (reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
|
| (reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
|
||||||
ret = ezx_pcap_putget(pcap, &value);
|
ret = ezx_pcap_putget(pcap, &value);
|
||||||
mutex_unlock(&pcap->io_mutex);
|
spin_unlock_irqrestore(&pcap->io_lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -91,14 +92,15 @@ EXPORT_SYMBOL_GPL(ezx_pcap_write);
|
|||||||
|
|
||||||
int ezx_pcap_read(struct pcap_chip *pcap, u8 reg_num, u32 *value)
|
int ezx_pcap_read(struct pcap_chip *pcap, u8 reg_num, u32 *value)
|
||||||
{
|
{
|
||||||
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&pcap->io_mutex);
|
spin_lock_irqsave(&pcap->io_lock, flags);
|
||||||
*value = PCAP_REGISTER_READ_OP_BIT
|
*value = PCAP_REGISTER_READ_OP_BIT
|
||||||
| (reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
|
| (reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
|
||||||
|
|
||||||
ret = ezx_pcap_putget(pcap, value);
|
ret = ezx_pcap_putget(pcap, value);
|
||||||
mutex_unlock(&pcap->io_mutex);
|
spin_unlock_irqrestore(&pcap->io_lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -106,11 +108,12 @@ EXPORT_SYMBOL_GPL(ezx_pcap_read);
|
|||||||
|
|
||||||
int ezx_pcap_set_bits(struct pcap_chip *pcap, u8 reg_num, u32 mask, u32 val)
|
int ezx_pcap_set_bits(struct pcap_chip *pcap, u8 reg_num, u32 mask, u32 val)
|
||||||
{
|
{
|
||||||
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
u32 tmp = PCAP_REGISTER_READ_OP_BIT |
|
u32 tmp = PCAP_REGISTER_READ_OP_BIT |
|
||||||
(reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
|
(reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
|
||||||
|
|
||||||
mutex_lock(&pcap->io_mutex);
|
spin_lock_irqsave(&pcap->io_lock, flags);
|
||||||
ret = ezx_pcap_putget(pcap, &tmp);
|
ret = ezx_pcap_putget(pcap, &tmp);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
@ -121,7 +124,7 @@ int ezx_pcap_set_bits(struct pcap_chip *pcap, u8 reg_num, u32 mask, u32 val)
|
|||||||
|
|
||||||
ret = ezx_pcap_putget(pcap, &tmp);
|
ret = ezx_pcap_putget(pcap, &tmp);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&pcap->io_mutex);
|
spin_unlock_irqrestore(&pcap->io_lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -212,14 +215,15 @@ static void pcap_irq_handler(struct irq_desc *desc)
|
|||||||
/* ADC */
|
/* ADC */
|
||||||
void pcap_set_ts_bits(struct pcap_chip *pcap, u32 bits)
|
void pcap_set_ts_bits(struct pcap_chip *pcap, u32 bits)
|
||||||
{
|
{
|
||||||
|
unsigned long flags;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
|
||||||
mutex_lock(&pcap->adc_mutex);
|
spin_lock_irqsave(&pcap->adc_lock, flags);
|
||||||
ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp);
|
ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp);
|
||||||
tmp &= ~(PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR);
|
tmp &= ~(PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR);
|
||||||
tmp |= bits & (PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR);
|
tmp |= bits & (PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR);
|
||||||
ezx_pcap_write(pcap, PCAP_REG_ADC, tmp);
|
ezx_pcap_write(pcap, PCAP_REG_ADC, tmp);
|
||||||
mutex_unlock(&pcap->adc_mutex);
|
spin_unlock_irqrestore(&pcap->adc_lock, flags);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(pcap_set_ts_bits);
|
EXPORT_SYMBOL_GPL(pcap_set_ts_bits);
|
||||||
|
|
||||||
@ -234,15 +238,16 @@ static void pcap_disable_adc(struct pcap_chip *pcap)
|
|||||||
|
|
||||||
static void pcap_adc_trigger(struct pcap_chip *pcap)
|
static void pcap_adc_trigger(struct pcap_chip *pcap)
|
||||||
{
|
{
|
||||||
|
unsigned long flags;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
u8 head;
|
u8 head;
|
||||||
|
|
||||||
mutex_lock(&pcap->adc_mutex);
|
spin_lock_irqsave(&pcap->adc_lock, flags);
|
||||||
head = pcap->adc_head;
|
head = pcap->adc_head;
|
||||||
if (!pcap->adc_queue[head]) {
|
if (!pcap->adc_queue[head]) {
|
||||||
/* queue is empty, save power */
|
/* queue is empty, save power */
|
||||||
pcap_disable_adc(pcap);
|
pcap_disable_adc(pcap);
|
||||||
mutex_unlock(&pcap->adc_mutex);
|
spin_unlock_irqrestore(&pcap->adc_lock, flags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* start conversion on requested bank, save TS_M bits */
|
/* start conversion on requested bank, save TS_M bits */
|
||||||
@ -254,7 +259,7 @@ static void pcap_adc_trigger(struct pcap_chip *pcap)
|
|||||||
tmp |= PCAP_ADC_AD_SEL1;
|
tmp |= PCAP_ADC_AD_SEL1;
|
||||||
|
|
||||||
ezx_pcap_write(pcap, PCAP_REG_ADC, tmp);
|
ezx_pcap_write(pcap, PCAP_REG_ADC, tmp);
|
||||||
mutex_unlock(&pcap->adc_mutex);
|
spin_unlock_irqrestore(&pcap->adc_lock, flags);
|
||||||
ezx_pcap_write(pcap, PCAP_REG_ADR, PCAP_ADR_ASC);
|
ezx_pcap_write(pcap, PCAP_REG_ADR, PCAP_ADR_ASC);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,11 +270,11 @@ static irqreturn_t pcap_adc_irq(int irq, void *_pcap)
|
|||||||
u16 res[2];
|
u16 res[2];
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
|
||||||
mutex_lock(&pcap->adc_mutex);
|
spin_lock(&pcap->adc_lock);
|
||||||
req = pcap->adc_queue[pcap->adc_head];
|
req = pcap->adc_queue[pcap->adc_head];
|
||||||
|
|
||||||
if (WARN(!req, "adc irq without pending request\n")) {
|
if (WARN(!req, "adc irq without pending request\n")) {
|
||||||
mutex_unlock(&pcap->adc_mutex);
|
spin_unlock(&pcap->adc_lock);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +290,7 @@ static irqreturn_t pcap_adc_irq(int irq, void *_pcap)
|
|||||||
|
|
||||||
pcap->adc_queue[pcap->adc_head] = NULL;
|
pcap->adc_queue[pcap->adc_head] = NULL;
|
||||||
pcap->adc_head = (pcap->adc_head + 1) & (PCAP_ADC_MAXQ - 1);
|
pcap->adc_head = (pcap->adc_head + 1) & (PCAP_ADC_MAXQ - 1);
|
||||||
mutex_unlock(&pcap->adc_mutex);
|
spin_unlock(&pcap->adc_lock);
|
||||||
|
|
||||||
/* pass the results and release memory */
|
/* pass the results and release memory */
|
||||||
req->callback(req->data, res);
|
req->callback(req->data, res);
|
||||||
@ -301,6 +306,7 @@ int pcap_adc_async(struct pcap_chip *pcap, u8 bank, u32 flags, u8 ch[],
|
|||||||
void *callback, void *data)
|
void *callback, void *data)
|
||||||
{
|
{
|
||||||
struct pcap_adc_request *req;
|
struct pcap_adc_request *req;
|
||||||
|
unsigned long irq_flags;
|
||||||
|
|
||||||
/* This will be freed after we have a result */
|
/* This will be freed after we have a result */
|
||||||
req = kmalloc(sizeof(struct pcap_adc_request), GFP_KERNEL);
|
req = kmalloc(sizeof(struct pcap_adc_request), GFP_KERNEL);
|
||||||
@ -314,15 +320,15 @@ int pcap_adc_async(struct pcap_chip *pcap, u8 bank, u32 flags, u8 ch[],
|
|||||||
req->callback = callback;
|
req->callback = callback;
|
||||||
req->data = data;
|
req->data = data;
|
||||||
|
|
||||||
mutex_lock(&pcap->adc_mutex);
|
spin_lock_irqsave(&pcap->adc_lock, irq_flags);
|
||||||
if (pcap->adc_queue[pcap->adc_tail]) {
|
if (pcap->adc_queue[pcap->adc_tail]) {
|
||||||
mutex_unlock(&pcap->adc_mutex);
|
spin_unlock_irqrestore(&pcap->adc_lock, irq_flags);
|
||||||
kfree(req);
|
kfree(req);
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
pcap->adc_queue[pcap->adc_tail] = req;
|
pcap->adc_queue[pcap->adc_tail] = req;
|
||||||
pcap->adc_tail = (pcap->adc_tail + 1) & (PCAP_ADC_MAXQ - 1);
|
pcap->adc_tail = (pcap->adc_tail + 1) & (PCAP_ADC_MAXQ - 1);
|
||||||
mutex_unlock(&pcap->adc_mutex);
|
spin_unlock_irqrestore(&pcap->adc_lock, irq_flags);
|
||||||
|
|
||||||
/* start conversion */
|
/* start conversion */
|
||||||
pcap_adc_trigger(pcap);
|
pcap_adc_trigger(pcap);
|
||||||
@ -389,16 +395,17 @@ static int pcap_add_subdev(struct pcap_chip *pcap,
|
|||||||
static int ezx_pcap_remove(struct spi_device *spi)
|
static int ezx_pcap_remove(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct pcap_chip *pcap = spi_get_drvdata(spi);
|
struct pcap_chip *pcap = spi_get_drvdata(spi);
|
||||||
|
unsigned long flags;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* remove all registered subdevs */
|
/* remove all registered subdevs */
|
||||||
device_for_each_child(&spi->dev, NULL, pcap_remove_subdev);
|
device_for_each_child(&spi->dev, NULL, pcap_remove_subdev);
|
||||||
|
|
||||||
/* cleanup ADC */
|
/* cleanup ADC */
|
||||||
mutex_lock(&pcap->adc_mutex);
|
spin_lock_irqsave(&pcap->adc_lock, flags);
|
||||||
for (i = 0; i < PCAP_ADC_MAXQ; i++)
|
for (i = 0; i < PCAP_ADC_MAXQ; i++)
|
||||||
kfree(pcap->adc_queue[i]);
|
kfree(pcap->adc_queue[i]);
|
||||||
mutex_unlock(&pcap->adc_mutex);
|
spin_unlock_irqrestore(&pcap->adc_lock, flags);
|
||||||
|
|
||||||
/* cleanup irqchip */
|
/* cleanup irqchip */
|
||||||
for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++)
|
for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++)
|
||||||
@ -426,8 +433,8 @@ static int ezx_pcap_probe(struct spi_device *spi)
|
|||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_init(&pcap->io_mutex);
|
spin_lock_init(&pcap->io_lock);
|
||||||
mutex_init(&pcap->adc_mutex);
|
spin_lock_init(&pcap->adc_lock);
|
||||||
INIT_WORK(&pcap->isr_work, pcap_isr_work);
|
INIT_WORK(&pcap->isr_work, pcap_isr_work);
|
||||||
INIT_WORK(&pcap->msr_work, pcap_msr_work);
|
INIT_WORK(&pcap->msr_work, pcap_msr_work);
|
||||||
spi_set_drvdata(spi, pcap);
|
spi_set_drvdata(spi, pcap);
|
||||||
|
Loading…
Reference in New Issue
Block a user