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