| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Driver for Motorola PCAP2 as present in EZX phones |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2006 Harald Welte <laforge@openezx.org> |
|---|
| 5 | 6 | * Copyright (C) 2009 Daniel Ribeiro <drwyrm@gmail.com> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 9 | | - * published by the Free Software Foundation. |
|---|
| 10 | | - * |
|---|
| 11 | 7 | */ |
|---|
| 12 | 8 | |
|---|
| 13 | 9 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 39 | 35 | |
|---|
| 40 | 36 | /* IO */ |
|---|
| 41 | 37 | u32 buf; |
|---|
| 42 | | - struct mutex io_mutex; |
|---|
| 38 | + spinlock_t io_lock; |
|---|
| 43 | 39 | |
|---|
| 44 | 40 | /* IRQ */ |
|---|
| 45 | 41 | unsigned int irq_base; |
|---|
| .. | .. |
|---|
| 52 | 48 | struct pcap_adc_request *adc_queue[PCAP_ADC_MAXQ]; |
|---|
| 53 | 49 | u8 adc_head; |
|---|
| 54 | 50 | u8 adc_tail; |
|---|
| 55 | | - struct mutex adc_mutex; |
|---|
| 51 | + spinlock_t adc_lock; |
|---|
| 56 | 52 | }; |
|---|
| 57 | 53 | |
|---|
| 58 | 54 | /* IO */ |
|---|
| .. | .. |
|---|
| 80 | 76 | |
|---|
| 81 | 77 | int ezx_pcap_write(struct pcap_chip *pcap, u8 reg_num, u32 value) |
|---|
| 82 | 78 | { |
|---|
| 79 | + unsigned long flags; |
|---|
| 83 | 80 | int ret; |
|---|
| 84 | 81 | |
|---|
| 85 | | - mutex_lock(&pcap->io_mutex); |
|---|
| 82 | + spin_lock_irqsave(&pcap->io_lock, flags); |
|---|
| 86 | 83 | value &= PCAP_REGISTER_VALUE_MASK; |
|---|
| 87 | 84 | value |= PCAP_REGISTER_WRITE_OP_BIT |
|---|
| 88 | 85 | | (reg_num << PCAP_REGISTER_ADDRESS_SHIFT); |
|---|
| 89 | 86 | ret = ezx_pcap_putget(pcap, &value); |
|---|
| 90 | | - mutex_unlock(&pcap->io_mutex); |
|---|
| 87 | + spin_unlock_irqrestore(&pcap->io_lock, flags); |
|---|
| 91 | 88 | |
|---|
| 92 | 89 | return ret; |
|---|
| 93 | 90 | } |
|---|
| .. | .. |
|---|
| 95 | 92 | |
|---|
| 96 | 93 | int ezx_pcap_read(struct pcap_chip *pcap, u8 reg_num, u32 *value) |
|---|
| 97 | 94 | { |
|---|
| 95 | + unsigned long flags; |
|---|
| 98 | 96 | int ret; |
|---|
| 99 | 97 | |
|---|
| 100 | | - mutex_lock(&pcap->io_mutex); |
|---|
| 98 | + spin_lock_irqsave(&pcap->io_lock, flags); |
|---|
| 101 | 99 | *value = PCAP_REGISTER_READ_OP_BIT |
|---|
| 102 | 100 | | (reg_num << PCAP_REGISTER_ADDRESS_SHIFT); |
|---|
| 103 | 101 | |
|---|
| 104 | 102 | ret = ezx_pcap_putget(pcap, value); |
|---|
| 105 | | - mutex_unlock(&pcap->io_mutex); |
|---|
| 103 | + spin_unlock_irqrestore(&pcap->io_lock, flags); |
|---|
| 106 | 104 | |
|---|
| 107 | 105 | return ret; |
|---|
| 108 | 106 | } |
|---|
| .. | .. |
|---|
| 110 | 108 | |
|---|
| 111 | 109 | int ezx_pcap_set_bits(struct pcap_chip *pcap, u8 reg_num, u32 mask, u32 val) |
|---|
| 112 | 110 | { |
|---|
| 111 | + unsigned long flags; |
|---|
| 113 | 112 | int ret; |
|---|
| 114 | 113 | u32 tmp = PCAP_REGISTER_READ_OP_BIT | |
|---|
| 115 | 114 | (reg_num << PCAP_REGISTER_ADDRESS_SHIFT); |
|---|
| 116 | 115 | |
|---|
| 117 | | - mutex_lock(&pcap->io_mutex); |
|---|
| 116 | + spin_lock_irqsave(&pcap->io_lock, flags); |
|---|
| 118 | 117 | ret = ezx_pcap_putget(pcap, &tmp); |
|---|
| 119 | 118 | if (ret) |
|---|
| 120 | 119 | goto out_unlock; |
|---|
| .. | .. |
|---|
| 125 | 124 | |
|---|
| 126 | 125 | ret = ezx_pcap_putget(pcap, &tmp); |
|---|
| 127 | 126 | out_unlock: |
|---|
| 128 | | - mutex_unlock(&pcap->io_mutex); |
|---|
| 127 | + spin_unlock_irqrestore(&pcap->io_lock, flags); |
|---|
| 129 | 128 | |
|---|
| 130 | 129 | return ret; |
|---|
| 131 | 130 | } |
|---|
| .. | .. |
|---|
| 216 | 215 | /* ADC */ |
|---|
| 217 | 216 | void pcap_set_ts_bits(struct pcap_chip *pcap, u32 bits) |
|---|
| 218 | 217 | { |
|---|
| 218 | + unsigned long flags; |
|---|
| 219 | 219 | u32 tmp; |
|---|
| 220 | 220 | |
|---|
| 221 | | - mutex_lock(&pcap->adc_mutex); |
|---|
| 221 | + spin_lock_irqsave(&pcap->adc_lock, flags); |
|---|
| 222 | 222 | ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp); |
|---|
| 223 | 223 | tmp &= ~(PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR); |
|---|
| 224 | 224 | tmp |= bits & (PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR); |
|---|
| 225 | 225 | ezx_pcap_write(pcap, PCAP_REG_ADC, tmp); |
|---|
| 226 | | - mutex_unlock(&pcap->adc_mutex); |
|---|
| 226 | + spin_unlock_irqrestore(&pcap->adc_lock, flags); |
|---|
| 227 | 227 | } |
|---|
| 228 | 228 | EXPORT_SYMBOL_GPL(pcap_set_ts_bits); |
|---|
| 229 | 229 | |
|---|
| .. | .. |
|---|
| 238 | 238 | |
|---|
| 239 | 239 | static void pcap_adc_trigger(struct pcap_chip *pcap) |
|---|
| 240 | 240 | { |
|---|
| 241 | + unsigned long flags; |
|---|
| 241 | 242 | u32 tmp; |
|---|
| 242 | 243 | u8 head; |
|---|
| 243 | 244 | |
|---|
| 244 | | - mutex_lock(&pcap->adc_mutex); |
|---|
| 245 | + spin_lock_irqsave(&pcap->adc_lock, flags); |
|---|
| 245 | 246 | head = pcap->adc_head; |
|---|
| 246 | 247 | if (!pcap->adc_queue[head]) { |
|---|
| 247 | 248 | /* queue is empty, save power */ |
|---|
| 248 | 249 | pcap_disable_adc(pcap); |
|---|
| 249 | | - mutex_unlock(&pcap->adc_mutex); |
|---|
| 250 | + spin_unlock_irqrestore(&pcap->adc_lock, flags); |
|---|
| 250 | 251 | return; |
|---|
| 251 | 252 | } |
|---|
| 252 | 253 | /* start conversion on requested bank, save TS_M bits */ |
|---|
| .. | .. |
|---|
| 258 | 259 | tmp |= PCAP_ADC_AD_SEL1; |
|---|
| 259 | 260 | |
|---|
| 260 | 261 | ezx_pcap_write(pcap, PCAP_REG_ADC, tmp); |
|---|
| 261 | | - mutex_unlock(&pcap->adc_mutex); |
|---|
| 262 | + spin_unlock_irqrestore(&pcap->adc_lock, flags); |
|---|
| 262 | 263 | ezx_pcap_write(pcap, PCAP_REG_ADR, PCAP_ADR_ASC); |
|---|
| 263 | 264 | } |
|---|
| 264 | 265 | |
|---|
| .. | .. |
|---|
| 269 | 270 | u16 res[2]; |
|---|
| 270 | 271 | u32 tmp; |
|---|
| 271 | 272 | |
|---|
| 272 | | - mutex_lock(&pcap->adc_mutex); |
|---|
| 273 | + spin_lock(&pcap->adc_lock); |
|---|
| 273 | 274 | req = pcap->adc_queue[pcap->adc_head]; |
|---|
| 274 | 275 | |
|---|
| 275 | 276 | if (WARN(!req, "adc irq without pending request\n")) { |
|---|
| 276 | | - mutex_unlock(&pcap->adc_mutex); |
|---|
| 277 | + spin_unlock(&pcap->adc_lock); |
|---|
| 277 | 278 | return IRQ_HANDLED; |
|---|
| 278 | 279 | } |
|---|
| 279 | 280 | |
|---|
| .. | .. |
|---|
| 289 | 290 | |
|---|
| 290 | 291 | pcap->adc_queue[pcap->adc_head] = NULL; |
|---|
| 291 | 292 | pcap->adc_head = (pcap->adc_head + 1) & (PCAP_ADC_MAXQ - 1); |
|---|
| 292 | | - mutex_unlock(&pcap->adc_mutex); |
|---|
| 293 | + spin_unlock(&pcap->adc_lock); |
|---|
| 293 | 294 | |
|---|
| 294 | 295 | /* pass the results and release memory */ |
|---|
| 295 | 296 | req->callback(req->data, res); |
|---|
| .. | .. |
|---|
| 305 | 306 | void *callback, void *data) |
|---|
| 306 | 307 | { |
|---|
| 307 | 308 | struct pcap_adc_request *req; |
|---|
| 309 | + unsigned long irq_flags; |
|---|
| 308 | 310 | |
|---|
| 309 | 311 | /* This will be freed after we have a result */ |
|---|
| 310 | 312 | req = kmalloc(sizeof(struct pcap_adc_request), GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 318 | 320 | req->callback = callback; |
|---|
| 319 | 321 | req->data = data; |
|---|
| 320 | 322 | |
|---|
| 321 | | - mutex_lock(&pcap->adc_mutex); |
|---|
| 323 | + spin_lock_irqsave(&pcap->adc_lock, irq_flags); |
|---|
| 322 | 324 | if (pcap->adc_queue[pcap->adc_tail]) { |
|---|
| 323 | | - mutex_unlock(&pcap->adc_mutex); |
|---|
| 325 | + spin_unlock_irqrestore(&pcap->adc_lock, irq_flags); |
|---|
| 324 | 326 | kfree(req); |
|---|
| 325 | 327 | return -EBUSY; |
|---|
| 326 | 328 | } |
|---|
| 327 | 329 | pcap->adc_queue[pcap->adc_tail] = req; |
|---|
| 328 | 330 | pcap->adc_tail = (pcap->adc_tail + 1) & (PCAP_ADC_MAXQ - 1); |
|---|
| 329 | | - mutex_unlock(&pcap->adc_mutex); |
|---|
| 331 | + spin_unlock_irqrestore(&pcap->adc_lock, irq_flags); |
|---|
| 330 | 332 | |
|---|
| 331 | 333 | /* start conversion */ |
|---|
| 332 | 334 | pcap_adc_trigger(pcap); |
|---|
| .. | .. |
|---|
| 393 | 395 | static int ezx_pcap_remove(struct spi_device *spi) |
|---|
| 394 | 396 | { |
|---|
| 395 | 397 | struct pcap_chip *pcap = spi_get_drvdata(spi); |
|---|
| 398 | + unsigned long flags; |
|---|
| 396 | 399 | int i; |
|---|
| 397 | 400 | |
|---|
| 398 | 401 | /* remove all registered subdevs */ |
|---|
| 399 | 402 | device_for_each_child(&spi->dev, NULL, pcap_remove_subdev); |
|---|
| 400 | 403 | |
|---|
| 401 | 404 | /* cleanup ADC */ |
|---|
| 402 | | - mutex_lock(&pcap->adc_mutex); |
|---|
| 405 | + spin_lock_irqsave(&pcap->adc_lock, flags); |
|---|
| 403 | 406 | for (i = 0; i < PCAP_ADC_MAXQ; i++) |
|---|
| 404 | 407 | kfree(pcap->adc_queue[i]); |
|---|
| 405 | | - mutex_unlock(&pcap->adc_mutex); |
|---|
| 408 | + spin_unlock_irqrestore(&pcap->adc_lock, flags); |
|---|
| 406 | 409 | |
|---|
| 407 | 410 | /* cleanup irqchip */ |
|---|
| 408 | 411 | for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) |
|---|
| .. | .. |
|---|
| 430 | 433 | goto ret; |
|---|
| 431 | 434 | } |
|---|
| 432 | 435 | |
|---|
| 433 | | - mutex_init(&pcap->io_mutex); |
|---|
| 434 | | - mutex_init(&pcap->adc_mutex); |
|---|
| 436 | + spin_lock_init(&pcap->io_lock); |
|---|
| 437 | + spin_lock_init(&pcap->adc_lock); |
|---|
| 435 | 438 | INIT_WORK(&pcap->isr_work, pcap_isr_work); |
|---|
| 436 | 439 | INIT_WORK(&pcap->msr_work, pcap_msr_work); |
|---|
| 437 | 440 | spi_set_drvdata(spi, pcap); |
|---|