// SPDX-License-Identifier: GPL-2.0-only /* * linux/drivers/mfd/ucb1x00-core.c * * Copyright (C) 2001 Russell King, All Rights Reserved. * * The UCB1x00 core driver provides basic services for handling IO, * the ADC, interrupts, and accessing registers. It is designed * such that everything goes through this layer, thereby providing * a consistent locking methodology, as well as allowing the drivers * to be used on other non-MCP-enabled hardware platforms. * * Note that all locks are private to this file. Nothing else may * touch them.
*/ #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/device.h> #include <linux/mutex.h> #include <linux/mfd/ucb1x00.h> #include <linux/pm.h> #include <linux/gpio/driver.h>
/** * ucb1x00_io_set_dir - set IO direction * @ucb: UCB1x00 structure describing chip * @in: bitfield of IO pins to be set as inputs * @out: bitfield of IO pins to be set as outputs * * Set the IO direction of the ten general purpose IO pins on * the UCB1x00 chip. The @in bitfield has priority over the * @out bitfield, in that if you specify a pin as both input * and output, it will end up as an input. * * ucb1x00_enable must have been called to enable the comms * before using this function. * * This function takes a spinlock, disabling interrupts.
*/ void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsignedint in, unsignedint out)
{ unsignedlong flags;
/** * ucb1x00_io_write - set or clear IO outputs * @ucb: UCB1x00 structure describing chip * @set: bitfield of IO pins to set to logic '1' * @clear: bitfield of IO pins to set to logic '0' * * Set the IO output state of the specified IO pins. The value * is retained if the pins are subsequently configured as inputs. * The @clear bitfield has priority over the @set bitfield - * outputs will be cleared. * * ucb1x00_enable must have been called to enable the comms * before using this function. * * This function takes a spinlock, disabling interrupts.
*/ void ucb1x00_io_write(struct ucb1x00 *ucb, unsignedint set, unsignedint clear)
{ unsignedlong flags;
/** * ucb1x00_io_read - read the current state of the IO pins * @ucb: UCB1x00 structure describing chip * * Return a bitfield describing the logic state of the ten * general purpose IO pins. * * ucb1x00_enable must have been called to enable the comms * before using this function. * * This function does not take any mutexes or spinlocks.
*/ unsignedint ucb1x00_io_read(struct ucb1x00 *ucb)
{ return ucb1x00_reg_read(ucb, UCB_IO_DATA);
}
/** * ucb1x00_adc_enable - enable the ADC converter * @ucb: UCB1x00 structure describing chip * * Enable the ucb1x00 and ADC converter on the UCB1x00 for use. * Any code wishing to use the ADC converter must call this * function prior to using it. * * This function takes the ADC mutex to prevent two or more * concurrent uses, and therefore may sleep. As a result, it * can only be called from process context, not interrupt * context. * * You should release the ADC as soon as possible using * ucb1x00_adc_disable.
*/ void ucb1x00_adc_enable(struct ucb1x00 *ucb)
{
mutex_lock(&ucb->adc_mutex);
/** * ucb1x00_adc_read - read the specified ADC channel * @ucb: UCB1x00 structure describing chip * @adc_channel: ADC channel mask * @sync: wait for syncronisation pulse. * * Start an ADC conversion and wait for the result. Note that * synchronised ADC conversions (via the ADCSYNC pin) must wait * until the trigger is asserted and the conversion is finished. * * This function currently spins waiting for the conversion to * complete (2 frames max without sync). * * If called for a synchronised ADC conversion, it may sleep * with the ADC mutex held.
*/ unsignedint ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync)
{ unsignedint val;
for (;;) {
val = ucb1x00_reg_read(ucb, UCB_ADC_DATA); if (val & UCB_ADC_DAT_VAL) break; /* yield to other processes */
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(1);
}
return UCB_ADC_DAT(val);
}
/** * ucb1x00_adc_disable - disable the ADC converter * @ucb: UCB1x00 structure describing chip * * Disable the ADC converter and release the ADC mutex.
*/ 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);
mutex_unlock(&ucb->adc_mutex);
}
/* * 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.
*/ staticvoid ucb1x00_irq(struct irq_desc *desc)
{ struct ucb1x00 *ucb = irq_desc_get_handler_data(desc); unsignedint isr, i;
/* * Cause an ADC interrupt.
*/
ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA);
ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START);
/* * Wait for the conversion to complete.
*/ while ((ucb1x00_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VAL) == 0);
ucb1x00_reg_write(ucb, UCB_ADC_CR, 0);
staticint __init ucb1x00_init(void)
{ int ret = class_register(&ucb1x00_class); if (ret == 0) {
ret = mcp_driver_register(&ucb1x00_driver); if (ret)
class_unregister(&ucb1x00_class);
} return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.