162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/mfd/ucb1x00-core.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001 Russell King, All Rights Reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * The UCB1x00 core driver provides basic services for handling IO, 862306a36Sopenharmony_ci * the ADC, interrupts, and accessing registers. It is designed 962306a36Sopenharmony_ci * such that everything goes through this layer, thereby providing 1062306a36Sopenharmony_ci * a consistent locking methodology, as well as allowing the drivers 1162306a36Sopenharmony_ci * to be used on other non-MCP-enabled hardware platforms. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Note that all locks are private to this file. Nothing else may 1462306a36Sopenharmony_ci * touch them. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/errno.h> 2262306a36Sopenharmony_ci#include <linux/interrupt.h> 2362306a36Sopenharmony_ci#include <linux/irq.h> 2462306a36Sopenharmony_ci#include <linux/device.h> 2562306a36Sopenharmony_ci#include <linux/mutex.h> 2662306a36Sopenharmony_ci#include <linux/mfd/ucb1x00.h> 2762306a36Sopenharmony_ci#include <linux/pm.h> 2862306a36Sopenharmony_ci#include <linux/gpio/driver.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic DEFINE_MUTEX(ucb1x00_mutex); 3162306a36Sopenharmony_cistatic LIST_HEAD(ucb1x00_drivers); 3262306a36Sopenharmony_cistatic LIST_HEAD(ucb1x00_devices); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/** 3562306a36Sopenharmony_ci * ucb1x00_io_set_dir - set IO direction 3662306a36Sopenharmony_ci * @ucb: UCB1x00 structure describing chip 3762306a36Sopenharmony_ci * @in: bitfield of IO pins to be set as inputs 3862306a36Sopenharmony_ci * @out: bitfield of IO pins to be set as outputs 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Set the IO direction of the ten general purpose IO pins on 4162306a36Sopenharmony_ci * the UCB1x00 chip. The @in bitfield has priority over the 4262306a36Sopenharmony_ci * @out bitfield, in that if you specify a pin as both input 4362306a36Sopenharmony_ci * and output, it will end up as an input. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * ucb1x00_enable must have been called to enable the comms 4662306a36Sopenharmony_ci * before using this function. 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * This function takes a spinlock, disabling interrupts. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_civoid ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int in, unsigned int out) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci unsigned long flags; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci spin_lock_irqsave(&ucb->io_lock, flags); 5562306a36Sopenharmony_ci ucb->io_dir |= out; 5662306a36Sopenharmony_ci ucb->io_dir &= ~in; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); 5962306a36Sopenharmony_ci spin_unlock_irqrestore(&ucb->io_lock, flags); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/** 6362306a36Sopenharmony_ci * ucb1x00_io_write - set or clear IO outputs 6462306a36Sopenharmony_ci * @ucb: UCB1x00 structure describing chip 6562306a36Sopenharmony_ci * @set: bitfield of IO pins to set to logic '1' 6662306a36Sopenharmony_ci * @clear: bitfield of IO pins to set to logic '0' 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * Set the IO output state of the specified IO pins. The value 6962306a36Sopenharmony_ci * is retained if the pins are subsequently configured as inputs. 7062306a36Sopenharmony_ci * The @clear bitfield has priority over the @set bitfield - 7162306a36Sopenharmony_ci * outputs will be cleared. 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * ucb1x00_enable must have been called to enable the comms 7462306a36Sopenharmony_ci * before using this function. 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * This function takes a spinlock, disabling interrupts. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_civoid ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci unsigned long flags; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci spin_lock_irqsave(&ucb->io_lock, flags); 8362306a36Sopenharmony_ci ucb->io_out |= set; 8462306a36Sopenharmony_ci ucb->io_out &= ~clear; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); 8762306a36Sopenharmony_ci spin_unlock_irqrestore(&ucb->io_lock, flags); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/** 9162306a36Sopenharmony_ci * ucb1x00_io_read - read the current state of the IO pins 9262306a36Sopenharmony_ci * @ucb: UCB1x00 structure describing chip 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * Return a bitfield describing the logic state of the ten 9562306a36Sopenharmony_ci * general purpose IO pins. 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * ucb1x00_enable must have been called to enable the comms 9862306a36Sopenharmony_ci * before using this function. 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * This function does not take any mutexes or spinlocks. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ciunsigned int ucb1x00_io_read(struct ucb1x00 *ucb) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci return ucb1x00_reg_read(ucb, UCB_IO_DATA); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void ucb1x00_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct ucb1x00 *ucb = gpiochip_get_data(chip); 11062306a36Sopenharmony_ci unsigned long flags; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci spin_lock_irqsave(&ucb->io_lock, flags); 11362306a36Sopenharmony_ci if (value) 11462306a36Sopenharmony_ci ucb->io_out |= 1 << offset; 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci ucb->io_out &= ~(1 << offset); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci ucb1x00_enable(ucb); 11962306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); 12062306a36Sopenharmony_ci ucb1x00_disable(ucb); 12162306a36Sopenharmony_ci spin_unlock_irqrestore(&ucb->io_lock, flags); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int ucb1x00_gpio_get(struct gpio_chip *chip, unsigned offset) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct ucb1x00 *ucb = gpiochip_get_data(chip); 12762306a36Sopenharmony_ci unsigned val; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ucb1x00_enable(ucb); 13062306a36Sopenharmony_ci val = ucb1x00_reg_read(ucb, UCB_IO_DATA); 13162306a36Sopenharmony_ci ucb1x00_disable(ucb); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return !!(val & (1 << offset)); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct ucb1x00 *ucb = gpiochip_get_data(chip); 13962306a36Sopenharmony_ci unsigned long flags; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci spin_lock_irqsave(&ucb->io_lock, flags); 14262306a36Sopenharmony_ci ucb->io_dir &= ~(1 << offset); 14362306a36Sopenharmony_ci ucb1x00_enable(ucb); 14462306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); 14562306a36Sopenharmony_ci ucb1x00_disable(ucb); 14662306a36Sopenharmony_ci spin_unlock_irqrestore(&ucb->io_lock, flags); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset 15262306a36Sopenharmony_ci , int value) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct ucb1x00 *ucb = gpiochip_get_data(chip); 15562306a36Sopenharmony_ci unsigned long flags; 15662306a36Sopenharmony_ci unsigned old, mask = 1 << offset; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci spin_lock_irqsave(&ucb->io_lock, flags); 15962306a36Sopenharmony_ci old = ucb->io_out; 16062306a36Sopenharmony_ci if (value) 16162306a36Sopenharmony_ci ucb->io_out |= mask; 16262306a36Sopenharmony_ci else 16362306a36Sopenharmony_ci ucb->io_out &= ~mask; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci ucb1x00_enable(ucb); 16662306a36Sopenharmony_ci if (old != ucb->io_out) 16762306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (!(ucb->io_dir & mask)) { 17062306a36Sopenharmony_ci ucb->io_dir |= mask; 17162306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci ucb1x00_disable(ucb); 17462306a36Sopenharmony_ci spin_unlock_irqrestore(&ucb->io_lock, flags); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int ucb1x00_to_irq(struct gpio_chip *chip, unsigned offset) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct ucb1x00 *ucb = gpiochip_get_data(chip); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return ucb->irq_base > 0 ? ucb->irq_base + offset : -ENXIO; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* 18762306a36Sopenharmony_ci * UCB1300 data sheet says we must: 18862306a36Sopenharmony_ci * 1. enable ADC => 5us (including reference startup time) 18962306a36Sopenharmony_ci * 2. select input => 51*tsibclk => 4.3us 19062306a36Sopenharmony_ci * 3. start conversion => 102*tsibclk => 8.5us 19162306a36Sopenharmony_ci * (tsibclk = 1/11981000) 19262306a36Sopenharmony_ci * Period between SIB 128-bit frames = 10.7us 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/** 19662306a36Sopenharmony_ci * ucb1x00_adc_enable - enable the ADC converter 19762306a36Sopenharmony_ci * @ucb: UCB1x00 structure describing chip 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * Enable the ucb1x00 and ADC converter on the UCB1x00 for use. 20062306a36Sopenharmony_ci * Any code wishing to use the ADC converter must call this 20162306a36Sopenharmony_ci * function prior to using it. 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * This function takes the ADC mutex to prevent two or more 20462306a36Sopenharmony_ci * concurrent uses, and therefore may sleep. As a result, it 20562306a36Sopenharmony_ci * can only be called from process context, not interrupt 20662306a36Sopenharmony_ci * context. 20762306a36Sopenharmony_ci * 20862306a36Sopenharmony_ci * You should release the ADC as soon as possible using 20962306a36Sopenharmony_ci * ucb1x00_adc_disable. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_civoid ucb1x00_adc_enable(struct ucb1x00 *ucb) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci mutex_lock(&ucb->adc_mutex); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci ucb->adc_cr |= UCB_ADC_ENA; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci ucb1x00_enable(ucb); 21862306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/** 22262306a36Sopenharmony_ci * ucb1x00_adc_read - read the specified ADC channel 22362306a36Sopenharmony_ci * @ucb: UCB1x00 structure describing chip 22462306a36Sopenharmony_ci * @adc_channel: ADC channel mask 22562306a36Sopenharmony_ci * @sync: wait for syncronisation pulse. 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * Start an ADC conversion and wait for the result. Note that 22862306a36Sopenharmony_ci * synchronised ADC conversions (via the ADCSYNC pin) must wait 22962306a36Sopenharmony_ci * until the trigger is asserted and the conversion is finished. 23062306a36Sopenharmony_ci * 23162306a36Sopenharmony_ci * This function currently spins waiting for the conversion to 23262306a36Sopenharmony_ci * complete (2 frames max without sync). 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * If called for a synchronised ADC conversion, it may sleep 23562306a36Sopenharmony_ci * with the ADC mutex held. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ciunsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci unsigned int val; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (sync) 24262306a36Sopenharmony_ci adc_channel |= UCB_ADC_SYNC_ENA; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel); 24562306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci for (;;) { 24862306a36Sopenharmony_ci val = ucb1x00_reg_read(ucb, UCB_ADC_DATA); 24962306a36Sopenharmony_ci if (val & UCB_ADC_DAT_VAL) 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci /* yield to other processes */ 25262306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 25362306a36Sopenharmony_ci schedule_timeout(1); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return UCB_ADC_DAT(val); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci/** 26062306a36Sopenharmony_ci * ucb1x00_adc_disable - disable the ADC converter 26162306a36Sopenharmony_ci * @ucb: UCB1x00 structure describing chip 26262306a36Sopenharmony_ci * 26362306a36Sopenharmony_ci * Disable the ADC converter and release the ADC mutex. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_civoid ucb1x00_adc_disable(struct ucb1x00 *ucb) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci ucb->adc_cr &= ~UCB_ADC_ENA; 26862306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); 26962306a36Sopenharmony_ci ucb1x00_disable(ucb); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci mutex_unlock(&ucb->adc_mutex); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/* 27562306a36Sopenharmony_ci * UCB1x00 Interrupt handling. 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * The UCB1x00 can generate interrupts when the SIBCLK is stopped. 27862306a36Sopenharmony_ci * Since we need to read an internal register, we must re-enable 27962306a36Sopenharmony_ci * SIBCLK to talk to the chip. We leave the clock running until 28062306a36Sopenharmony_ci * we have finished processing all interrupts from the chip. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_cistatic void ucb1x00_irq(struct irq_desc *desc) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct ucb1x00 *ucb = irq_desc_get_handler_data(desc); 28562306a36Sopenharmony_ci unsigned int isr, i; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ucb1x00_enable(ucb); 28862306a36Sopenharmony_ci isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); 28962306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); 29062306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci for (i = 0; i < 16 && isr; i++, isr >>= 1) 29362306a36Sopenharmony_ci if (isr & 1) 29462306a36Sopenharmony_ci generic_handle_irq(ucb->irq_base + i); 29562306a36Sopenharmony_ci ucb1x00_disable(ucb); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void ucb1x00_irq_update(struct ucb1x00 *ucb, unsigned mask) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci ucb1x00_enable(ucb); 30162306a36Sopenharmony_ci if (ucb->irq_ris_enbl & mask) 30262306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & 30362306a36Sopenharmony_ci ucb->irq_mask); 30462306a36Sopenharmony_ci if (ucb->irq_fal_enbl & mask) 30562306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & 30662306a36Sopenharmony_ci ucb->irq_mask); 30762306a36Sopenharmony_ci ucb1x00_disable(ucb); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic void ucb1x00_irq_noop(struct irq_data *data) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void ucb1x00_irq_mask(struct irq_data *data) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); 31762306a36Sopenharmony_ci unsigned mask = 1 << (data->irq - ucb->irq_base); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci raw_spin_lock(&ucb->irq_lock); 32062306a36Sopenharmony_ci ucb->irq_mask &= ~mask; 32162306a36Sopenharmony_ci ucb1x00_irq_update(ucb, mask); 32262306a36Sopenharmony_ci raw_spin_unlock(&ucb->irq_lock); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void ucb1x00_irq_unmask(struct irq_data *data) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); 32862306a36Sopenharmony_ci unsigned mask = 1 << (data->irq - ucb->irq_base); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci raw_spin_lock(&ucb->irq_lock); 33162306a36Sopenharmony_ci ucb->irq_mask |= mask; 33262306a36Sopenharmony_ci ucb1x00_irq_update(ucb, mask); 33362306a36Sopenharmony_ci raw_spin_unlock(&ucb->irq_lock); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); 33962306a36Sopenharmony_ci unsigned mask = 1 << (data->irq - ucb->irq_base); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci raw_spin_lock(&ucb->irq_lock); 34262306a36Sopenharmony_ci if (type & IRQ_TYPE_EDGE_RISING) 34362306a36Sopenharmony_ci ucb->irq_ris_enbl |= mask; 34462306a36Sopenharmony_ci else 34562306a36Sopenharmony_ci ucb->irq_ris_enbl &= ~mask; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (type & IRQ_TYPE_EDGE_FALLING) 34862306a36Sopenharmony_ci ucb->irq_fal_enbl |= mask; 34962306a36Sopenharmony_ci else 35062306a36Sopenharmony_ci ucb->irq_fal_enbl &= ~mask; 35162306a36Sopenharmony_ci if (ucb->irq_mask & mask) { 35262306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & 35362306a36Sopenharmony_ci ucb->irq_mask); 35462306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & 35562306a36Sopenharmony_ci ucb->irq_mask); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci raw_spin_unlock(&ucb->irq_lock); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int ucb1x00_irq_set_wake(struct irq_data *data, unsigned int on) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); 36562306a36Sopenharmony_ci struct ucb1x00_plat_data *pdata = ucb->mcp->attached_device.platform_data; 36662306a36Sopenharmony_ci unsigned mask = 1 << (data->irq - ucb->irq_base); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (!pdata || !pdata->can_wakeup) 36962306a36Sopenharmony_ci return -EINVAL; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci raw_spin_lock(&ucb->irq_lock); 37262306a36Sopenharmony_ci if (on) 37362306a36Sopenharmony_ci ucb->irq_wake |= mask; 37462306a36Sopenharmony_ci else 37562306a36Sopenharmony_ci ucb->irq_wake &= ~mask; 37662306a36Sopenharmony_ci raw_spin_unlock(&ucb->irq_lock); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic struct irq_chip ucb1x00_irqchip = { 38262306a36Sopenharmony_ci .name = "ucb1x00", 38362306a36Sopenharmony_ci .irq_ack = ucb1x00_irq_noop, 38462306a36Sopenharmony_ci .irq_mask = ucb1x00_irq_mask, 38562306a36Sopenharmony_ci .irq_unmask = ucb1x00_irq_unmask, 38662306a36Sopenharmony_ci .irq_set_type = ucb1x00_irq_set_type, 38762306a36Sopenharmony_ci .irq_set_wake = ucb1x00_irq_set_wake, 38862306a36Sopenharmony_ci}; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct ucb1x00_dev *dev; 39362306a36Sopenharmony_ci int ret; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci dev = kmalloc(sizeof(struct ucb1x00_dev), GFP_KERNEL); 39662306a36Sopenharmony_ci if (!dev) 39762306a36Sopenharmony_ci return -ENOMEM; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci dev->ucb = ucb; 40062306a36Sopenharmony_ci dev->drv = drv; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci ret = drv->add(dev); 40362306a36Sopenharmony_ci if (ret) { 40462306a36Sopenharmony_ci kfree(dev); 40562306a36Sopenharmony_ci return ret; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci list_add_tail(&dev->dev_node, &ucb->devs); 40962306a36Sopenharmony_ci list_add_tail(&dev->drv_node, &drv->devs); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return ret; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic void ucb1x00_remove_dev(struct ucb1x00_dev *dev) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci dev->drv->remove(dev); 41762306a36Sopenharmony_ci list_del(&dev->dev_node); 41862306a36Sopenharmony_ci list_del(&dev->drv_node); 41962306a36Sopenharmony_ci kfree(dev); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/* 42362306a36Sopenharmony_ci * Try to probe our interrupt, rather than relying on lots of 42462306a36Sopenharmony_ci * hard-coded machine dependencies. For reference, the expected 42562306a36Sopenharmony_ci * IRQ mappings are: 42662306a36Sopenharmony_ci * 42762306a36Sopenharmony_ci * Machine Default IRQ 42862306a36Sopenharmony_ci * adsbitsy IRQ_GPCIN4 42962306a36Sopenharmony_ci * cerf IRQ_GPIO_UCB1200_IRQ 43062306a36Sopenharmony_ci * flexanet IRQ_GPIO_GUI 43162306a36Sopenharmony_ci * freebird IRQ_GPIO_FREEBIRD_UCB1300_IRQ 43262306a36Sopenharmony_ci * graphicsclient ADS_EXT_IRQ(8) 43362306a36Sopenharmony_ci * graphicsmaster ADS_EXT_IRQ(8) 43462306a36Sopenharmony_ci * lart LART_IRQ_UCB1200 43562306a36Sopenharmony_ci * omnimeter IRQ_GPIO23 43662306a36Sopenharmony_ci * pfs168 IRQ_GPIO_UCB1300_IRQ 43762306a36Sopenharmony_ci * simpad IRQ_GPIO_UCB1300_IRQ 43862306a36Sopenharmony_ci * shannon SHANNON_IRQ_GPIO_IRQ_CODEC 43962306a36Sopenharmony_ci * yopy IRQ_GPIO_UCB1200_IRQ 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_cistatic int ucb1x00_detect_irq(struct ucb1x00 *ucb) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci unsigned long mask; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci mask = probe_irq_on(); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* 44862306a36Sopenharmony_ci * Enable the ADC interrupt. 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC); 45162306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC); 45262306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); 45362306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* 45662306a36Sopenharmony_ci * Cause an ADC interrupt. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); 45962306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* 46262306a36Sopenharmony_ci * Wait for the conversion to complete. 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_ci while ((ucb1x00_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VAL) == 0); 46562306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_ADC_CR, 0); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* 46862306a36Sopenharmony_ci * Disable and clear interrupt. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_RIS, 0); 47162306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_FAL, 0); 47262306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); 47362306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* 47662306a36Sopenharmony_ci * Read triggered interrupt. 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ci return probe_irq_off(mask); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic void ucb1x00_release(struct device *dev) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); 48462306a36Sopenharmony_ci kfree(ucb); 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic struct class ucb1x00_class = { 48862306a36Sopenharmony_ci .name = "ucb1x00", 48962306a36Sopenharmony_ci .dev_release = ucb1x00_release, 49062306a36Sopenharmony_ci}; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int ucb1x00_probe(struct mcp *mcp) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data; 49562306a36Sopenharmony_ci struct ucb1x00_driver *drv; 49662306a36Sopenharmony_ci struct ucb1x00 *ucb; 49762306a36Sopenharmony_ci unsigned id, i, irq_base; 49862306a36Sopenharmony_ci int ret = -ENODEV; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Tell the platform to deassert the UCB1x00 reset */ 50162306a36Sopenharmony_ci if (pdata && pdata->reset) 50262306a36Sopenharmony_ci pdata->reset(UCB_RST_PROBE); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci mcp_enable(mcp); 50562306a36Sopenharmony_ci id = mcp_reg_read(mcp, UCB_ID); 50662306a36Sopenharmony_ci mcp_disable(mcp); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143) { 50962306a36Sopenharmony_ci printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id); 51062306a36Sopenharmony_ci goto out; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci ucb = kzalloc(sizeof(struct ucb1x00), GFP_KERNEL); 51462306a36Sopenharmony_ci ret = -ENOMEM; 51562306a36Sopenharmony_ci if (!ucb) 51662306a36Sopenharmony_ci goto out; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci device_initialize(&ucb->dev); 51962306a36Sopenharmony_ci ucb->dev.class = &ucb1x00_class; 52062306a36Sopenharmony_ci ucb->dev.parent = &mcp->attached_device; 52162306a36Sopenharmony_ci dev_set_name(&ucb->dev, "ucb1x00"); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci raw_spin_lock_init(&ucb->irq_lock); 52462306a36Sopenharmony_ci spin_lock_init(&ucb->io_lock); 52562306a36Sopenharmony_ci mutex_init(&ucb->adc_mutex); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci ucb->id = id; 52862306a36Sopenharmony_ci ucb->mcp = mcp; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci ret = device_add(&ucb->dev); 53162306a36Sopenharmony_ci if (ret) 53262306a36Sopenharmony_ci goto err_dev_add; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci ucb1x00_enable(ucb); 53562306a36Sopenharmony_ci ucb->irq = ucb1x00_detect_irq(ucb); 53662306a36Sopenharmony_ci ucb1x00_disable(ucb); 53762306a36Sopenharmony_ci if (!ucb->irq) { 53862306a36Sopenharmony_ci dev_err(&ucb->dev, "IRQ probe failed\n"); 53962306a36Sopenharmony_ci ret = -ENODEV; 54062306a36Sopenharmony_ci goto err_no_irq; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci ucb->gpio.base = -1; 54462306a36Sopenharmony_ci irq_base = pdata ? pdata->irq_base : 0; 54562306a36Sopenharmony_ci ucb->irq_base = irq_alloc_descs(-1, irq_base, 16, -1); 54662306a36Sopenharmony_ci if (ucb->irq_base < 0) { 54762306a36Sopenharmony_ci dev_err(&ucb->dev, "unable to allocate 16 irqs: %d\n", 54862306a36Sopenharmony_ci ucb->irq_base); 54962306a36Sopenharmony_ci ret = ucb->irq_base; 55062306a36Sopenharmony_ci goto err_irq_alloc; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 55462306a36Sopenharmony_ci unsigned irq = ucb->irq_base + i; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci irq_set_chip_and_handler(irq, &ucb1x00_irqchip, handle_edge_irq); 55762306a36Sopenharmony_ci irq_set_chip_data(irq, ucb); 55862306a36Sopenharmony_ci irq_clear_status_flags(irq, IRQ_NOREQUEST); 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci irq_set_irq_type(ucb->irq, IRQ_TYPE_EDGE_RISING); 56262306a36Sopenharmony_ci irq_set_chained_handler_and_data(ucb->irq, ucb1x00_irq, ucb); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (pdata && pdata->gpio_base) { 56562306a36Sopenharmony_ci ucb->gpio.label = dev_name(&ucb->dev); 56662306a36Sopenharmony_ci ucb->gpio.parent = &ucb->dev; 56762306a36Sopenharmony_ci ucb->gpio.owner = THIS_MODULE; 56862306a36Sopenharmony_ci ucb->gpio.base = pdata->gpio_base; 56962306a36Sopenharmony_ci ucb->gpio.ngpio = 10; 57062306a36Sopenharmony_ci ucb->gpio.set = ucb1x00_gpio_set; 57162306a36Sopenharmony_ci ucb->gpio.get = ucb1x00_gpio_get; 57262306a36Sopenharmony_ci ucb->gpio.direction_input = ucb1x00_gpio_direction_input; 57362306a36Sopenharmony_ci ucb->gpio.direction_output = ucb1x00_gpio_direction_output; 57462306a36Sopenharmony_ci ucb->gpio.to_irq = ucb1x00_to_irq; 57562306a36Sopenharmony_ci ret = gpiochip_add_data(&ucb->gpio, ucb); 57662306a36Sopenharmony_ci if (ret) 57762306a36Sopenharmony_ci goto err_gpio_add; 57862306a36Sopenharmony_ci } else 57962306a36Sopenharmony_ci dev_info(&ucb->dev, "gpio_base not set so no gpiolib support"); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci mcp_set_drvdata(mcp, ucb); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (pdata) 58462306a36Sopenharmony_ci device_set_wakeup_capable(&ucb->dev, pdata->can_wakeup); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci INIT_LIST_HEAD(&ucb->devs); 58762306a36Sopenharmony_ci mutex_lock(&ucb1x00_mutex); 58862306a36Sopenharmony_ci list_add_tail(&ucb->node, &ucb1x00_devices); 58962306a36Sopenharmony_ci list_for_each_entry(drv, &ucb1x00_drivers, node) { 59062306a36Sopenharmony_ci ucb1x00_add_dev(ucb, drv); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci mutex_unlock(&ucb1x00_mutex); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci return ret; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci err_gpio_add: 59762306a36Sopenharmony_ci irq_set_chained_handler(ucb->irq, NULL); 59862306a36Sopenharmony_ci err_irq_alloc: 59962306a36Sopenharmony_ci if (ucb->irq_base > 0) 60062306a36Sopenharmony_ci irq_free_descs(ucb->irq_base, 16); 60162306a36Sopenharmony_ci err_no_irq: 60262306a36Sopenharmony_ci device_del(&ucb->dev); 60362306a36Sopenharmony_ci err_dev_add: 60462306a36Sopenharmony_ci put_device(&ucb->dev); 60562306a36Sopenharmony_ci out: 60662306a36Sopenharmony_ci if (pdata && pdata->reset) 60762306a36Sopenharmony_ci pdata->reset(UCB_RST_PROBE_FAIL); 60862306a36Sopenharmony_ci return ret; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic void ucb1x00_remove(struct mcp *mcp) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data; 61462306a36Sopenharmony_ci struct ucb1x00 *ucb = mcp_get_drvdata(mcp); 61562306a36Sopenharmony_ci struct list_head *l, *n; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci mutex_lock(&ucb1x00_mutex); 61862306a36Sopenharmony_ci list_del(&ucb->node); 61962306a36Sopenharmony_ci list_for_each_safe(l, n, &ucb->devs) { 62062306a36Sopenharmony_ci struct ucb1x00_dev *dev = list_entry(l, struct ucb1x00_dev, dev_node); 62162306a36Sopenharmony_ci ucb1x00_remove_dev(dev); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci mutex_unlock(&ucb1x00_mutex); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (ucb->gpio.base != -1) 62662306a36Sopenharmony_ci gpiochip_remove(&ucb->gpio); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci irq_set_chained_handler(ucb->irq, NULL); 62962306a36Sopenharmony_ci irq_free_descs(ucb->irq_base, 16); 63062306a36Sopenharmony_ci device_unregister(&ucb->dev); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (pdata && pdata->reset) 63362306a36Sopenharmony_ci pdata->reset(UCB_RST_REMOVE); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ciint ucb1x00_register_driver(struct ucb1x00_driver *drv) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct ucb1x00 *ucb; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci INIT_LIST_HEAD(&drv->devs); 64162306a36Sopenharmony_ci mutex_lock(&ucb1x00_mutex); 64262306a36Sopenharmony_ci list_add_tail(&drv->node, &ucb1x00_drivers); 64362306a36Sopenharmony_ci list_for_each_entry(ucb, &ucb1x00_devices, node) { 64462306a36Sopenharmony_ci ucb1x00_add_dev(ucb, drv); 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci mutex_unlock(&ucb1x00_mutex); 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_civoid ucb1x00_unregister_driver(struct ucb1x00_driver *drv) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct list_head *n, *l; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci mutex_lock(&ucb1x00_mutex); 65562306a36Sopenharmony_ci list_del(&drv->node); 65662306a36Sopenharmony_ci list_for_each_safe(l, n, &drv->devs) { 65762306a36Sopenharmony_ci struct ucb1x00_dev *dev = list_entry(l, struct ucb1x00_dev, drv_node); 65862306a36Sopenharmony_ci ucb1x00_remove_dev(dev); 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci mutex_unlock(&ucb1x00_mutex); 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic int ucb1x00_suspend(struct device *dev) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci struct ucb1x00_plat_data *pdata = dev_get_platdata(dev); 66662306a36Sopenharmony_ci struct ucb1x00 *ucb = dev_get_drvdata(dev); 66762306a36Sopenharmony_ci struct ucb1x00_dev *udev; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci mutex_lock(&ucb1x00_mutex); 67062306a36Sopenharmony_ci list_for_each_entry(udev, &ucb->devs, dev_node) { 67162306a36Sopenharmony_ci if (udev->drv->suspend) 67262306a36Sopenharmony_ci udev->drv->suspend(udev); 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci mutex_unlock(&ucb1x00_mutex); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (ucb->irq_wake) { 67762306a36Sopenharmony_ci unsigned long flags; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci raw_spin_lock_irqsave(&ucb->irq_lock, flags); 68062306a36Sopenharmony_ci ucb1x00_enable(ucb); 68162306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & 68262306a36Sopenharmony_ci ucb->irq_wake); 68362306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & 68462306a36Sopenharmony_ci ucb->irq_wake); 68562306a36Sopenharmony_ci ucb1x00_disable(ucb); 68662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&ucb->irq_lock, flags); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci enable_irq_wake(ucb->irq); 68962306a36Sopenharmony_ci } else if (pdata && pdata->reset) 69062306a36Sopenharmony_ci pdata->reset(UCB_RST_SUSPEND); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return 0; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic int ucb1x00_resume(struct device *dev) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct ucb1x00_plat_data *pdata = dev_get_platdata(dev); 69862306a36Sopenharmony_ci struct ucb1x00 *ucb = dev_get_drvdata(dev); 69962306a36Sopenharmony_ci struct ucb1x00_dev *udev; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (!ucb->irq_wake && pdata && pdata->reset) 70262306a36Sopenharmony_ci pdata->reset(UCB_RST_RESUME); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci ucb1x00_enable(ucb); 70562306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); 70662306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (ucb->irq_wake) { 70962306a36Sopenharmony_ci unsigned long flags; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci raw_spin_lock_irqsave(&ucb->irq_lock, flags); 71262306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl & 71362306a36Sopenharmony_ci ucb->irq_mask); 71462306a36Sopenharmony_ci ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl & 71562306a36Sopenharmony_ci ucb->irq_mask); 71662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&ucb->irq_lock, flags); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci disable_irq_wake(ucb->irq); 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci ucb1x00_disable(ucb); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci mutex_lock(&ucb1x00_mutex); 72362306a36Sopenharmony_ci list_for_each_entry(udev, &ucb->devs, dev_node) { 72462306a36Sopenharmony_ci if (udev->drv->resume) 72562306a36Sopenharmony_ci udev->drv->resume(udev); 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci mutex_unlock(&ucb1x00_mutex); 72862306a36Sopenharmony_ci return 0; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(ucb1x00_pm_ops, 73262306a36Sopenharmony_ci ucb1x00_suspend, ucb1x00_resume); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic struct mcp_driver ucb1x00_driver = { 73562306a36Sopenharmony_ci .drv = { 73662306a36Sopenharmony_ci .name = "ucb1x00", 73762306a36Sopenharmony_ci .owner = THIS_MODULE, 73862306a36Sopenharmony_ci .pm = pm_sleep_ptr(&ucb1x00_pm_ops), 73962306a36Sopenharmony_ci }, 74062306a36Sopenharmony_ci .probe = ucb1x00_probe, 74162306a36Sopenharmony_ci .remove = ucb1x00_remove, 74262306a36Sopenharmony_ci}; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int __init ucb1x00_init(void) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci int ret = class_register(&ucb1x00_class); 74762306a36Sopenharmony_ci if (ret == 0) { 74862306a36Sopenharmony_ci ret = mcp_driver_register(&ucb1x00_driver); 74962306a36Sopenharmony_ci if (ret) 75062306a36Sopenharmony_ci class_unregister(&ucb1x00_class); 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci return ret; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic void __exit ucb1x00_exit(void) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci mcp_driver_unregister(&ucb1x00_driver); 75862306a36Sopenharmony_ci class_unregister(&ucb1x00_class); 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cimodule_init(ucb1x00_init); 76262306a36Sopenharmony_cimodule_exit(ucb1x00_exit); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ciEXPORT_SYMBOL(ucb1x00_io_set_dir); 76562306a36Sopenharmony_ciEXPORT_SYMBOL(ucb1x00_io_write); 76662306a36Sopenharmony_ciEXPORT_SYMBOL(ucb1x00_io_read); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ciEXPORT_SYMBOL(ucb1x00_adc_enable); 76962306a36Sopenharmony_ciEXPORT_SYMBOL(ucb1x00_adc_read); 77062306a36Sopenharmony_ciEXPORT_SYMBOL(ucb1x00_adc_disable); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ciEXPORT_SYMBOL(ucb1x00_register_driver); 77362306a36Sopenharmony_ciEXPORT_SYMBOL(ucb1x00_unregister_driver); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ciMODULE_ALIAS("mcp:ucb1x00"); 77662306a36Sopenharmony_ciMODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); 77762306a36Sopenharmony_ciMODULE_DESCRIPTION("UCB1x00 core driver"); 77862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 779