/** * ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt * @ucb: UCB1x00 structure describing chip * @idx: interrupt index * @devid: device id. * * Disable the interrupt source and remove the handler. devid must * match the devid passed when hooking the interrupt. * * Returns zero on success, or one of the following errors: * -EINVAL if the interrupt index is invalid * -ENOENT if devid does not match */ int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid) { struct ucb1x00_irq *irq; int ret; if (idx >= 16) goto bad; irq = ucb->irq_handler + idx; ret = -ENOENT; spin_lock_irq(&ucb->lock); if (irq->devid == devid) { ucb->irq_ris_enbl &= ~(1 << idx); ucb->irq_fal_enbl &= ~(1 << idx); ucb1x00_enable(ucb); ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); ucb1x00_disable(ucb); irq->fn = NULL; irq->devid = NULL; ret = 0; } spin_unlock_irq(&ucb->lock); return ret; bad: printk(KERN_ERR "%s: freeing bad irq %d\n", __FUNCTION__, idx); return -EINVAL; }
/** * ucb1x00_adc_disable - disable the ADC converter * @ucb: UCB1x00 structure describing chip * * Disable the ADC converter and release the ADC semaphore. */ void ucb1x00_adc_disable(struct ucb1x00 *ucb) { ucb->adc_cr &= ~UCB_ADC_ENA; ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); ucb1x00_disable(ucb); up(&ucb->adc_sem); }
/* * Release touchscreen resources. Disable IRQs. */ static void ucb1x00_ts_close(struct input_dev *idev) { struct ucb1x00_ts *ts = input_get_drvdata(idev); if (ts->rtask) kthread_stop(ts->rtask); ucb1x00_enable(ts->ucb); ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); ucb1x00_disable(ts->ucb); }
static int ucb1x00_pm (struct pm_dev *dev, pm_request_t rqst, void *data) { struct ucb1x00 *ucb = (struct ucb1x00 *)dev->data; unsigned int isr; if (rqst == PM_RESUME) { ucb1x00_enable(ucb); isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); ucb1x00_disable(ucb); } return 0; }
/* * UCB1x00 Interrupt handling. * * The UCB1x00 can generate interrupts when the SIBCLK is stopped. * Since we need to read an internal register, we must re-enable * SIBCLK to talk to the chip. We leave the clock running until * we have finished processing all interrupts from the chip. */ static void ucb1x00_irq(int irqnr, void *devid, struct pt_regs *regs) { struct ucb1x00 *ucb = devid; struct ucb1x00_irq *irq; unsigned int isr, i; ucb1x00_enable(ucb); isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++) if (isr & 1 && irq->fn) irq->fn(i, irq->devid); ucb1x00_disable(ucb); }
/** * ucb1x00_disable_irq - disable an UCB1x00 interrupt source * @ucb: UCB1x00 structure describing chip * @edges: interrupt edges to disable * * Disable the specified interrupt triggering on the specified * (%UCB_RISING, %UCB_FALLING or both) edges. */ void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) { unsigned long flags; if (idx < 16) { spin_lock_irqsave(&ucb->lock, flags); ucb1x00_enable(ucb); if (edges & UCB_RISING) { ucb->irq_ris_enbl &= ~(1 << idx); ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); } if (edges & UCB_FALLING) { ucb->irq_fal_enbl &= ~(1 << idx); ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); } ucb1x00_disable(ucb); spin_unlock_irqrestore(&ucb->lock, flags); } }
/* * This is a RT kernel thread that handles the ADC accesses * (mainly so we can use semaphores in the UCB1200 core code * to serialise accesses to the ADC). */ static int ucb1x00_thread(void *_ts) { struct ucb1x00_ts *ts = _ts; DECLARE_WAITQUEUE(wait, current); int valid = 0; set_freezable(); add_wait_queue(&ts->irq_wait, &wait); while (!kthread_should_stop()) { unsigned int x, y, p; signed long timeout; ts->restart = 0; ucb1x00_adc_enable(ts->ucb); x = ucb1x00_ts_read_xpos(ts); y = ucb1x00_ts_read_ypos(ts); p = ucb1x00_ts_read_pressure(ts); /* * Switch back to interrupt mode. */ ucb1x00_ts_mode_int(ts); ucb1x00_adc_disable(ts->ucb); msleep(10); ucb1x00_enable(ts->ucb); if (ucb1x00_ts_pen_down(ts)) { set_current_state(TASK_INTERRUPTIBLE); ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING); ucb1x00_disable(ts->ucb); /* * If we spat out a valid sample set last time, * spit out a "pen off" sample here. */ if (valid) { ucb1x00_ts_event_release(ts); valid = 0; } timeout = MAX_SCHEDULE_TIMEOUT; } else { ucb1x00_disable(ts->ucb); /* * Filtering is policy. Policy belongs in user * space. We therefore leave it to user space * to do any filtering they please. */ if (!ts->restart) { ucb1x00_ts_evt_add(ts, p, x, y); valid = 1; } set_current_state(TASK_INTERRUPTIBLE); timeout = HZ / 100; } try_to_freeze(); schedule_timeout(timeout); } remove_wait_queue(&ts->irq_wait, &wait); ts->rtask = NULL; return 0; }