i2c-omap24xx.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 631 行 · 第 1/2 页

C
631
字号
/* * drivers/i2c/busses/i2c-omap24xx.c * * Unified algorithm/adapter I2C driver for OMAP24xx I2C controller. * * Author: Andy Lowe (source@mvista.com) * * Copyright (C) 2004 MontaVista Software, Inc. * Copyright (C) 2004 Texas Instruments. * * This file is licensed under the terms of the GNU General Public License  * version 2. This program is licensed "as is" without any warranty of any  * kind, whether express or implied. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/i2c.h>#include <linux/smp_lock.h>#include <linux/interrupt.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/arch/mux.h>#include "i2c-omap24xx.h"/* ----- configuration macros ----------------------------------------- */#define NUM_I2C_ADAP 2	/* number of I2C adapters */#define I2C_NAME "i2c-omap24xx"/* The default timeout is for the time allowed for each word (2 bytes) of  * an I2C transfer to complete.  This value is converted to ticks and  * used to initialize the timeout member of the i2c_adapter struct. */#define I2C_DEFAULT_TIMEOUT_MS	20/* The reset timeout is the time allowed for a reset of the I2C controller  * to complete. */#define I2C_RESET_TIMEOUT_MS 100/* The bus busy timeout is the maximum time we will wait for the bus to  * become idle before initiating a transfer. */#define I2C_BUS_BUSY_TIMEOUT_MS 200/* This driver doesn't support I2C slave mode, so we set our own address  * to zero to make sure it won't conflict with any slave devices on the  * bus. */#define I2C_OWN_ADDRESS 0/* ----- global defines ----------------------------------------------- */#undef I2C_OMAP24XX_DEBUG#ifdef I2C_OMAP24XX_DEBUG#define LOG_LEVEL KERN_DEBUG#define DEB0(format, arg...)	\	printk(LOG_LEVEL I2C_NAME " DEBUG: " format "\n",  ## arg )#define DEB1(format, arg...)	\	if (i2c_debug>=1) {	\		printk(LOG_LEVEL I2C_NAME " DEBUG: " format "\n",  ## arg ); \	}#define DEB2(format, arg...)	\	if (i2c_debug>=2) {	\		printk(LOG_LEVEL I2C_NAME " DEBUG: " format "\n",  ## arg ); \	}#define DEB3(format, arg...)	\	if (i2c_debug>=3) {	\		printk(LOG_LEVEL I2C_NAME " DEBUG: " format "\n",  ## arg ); \	}#define DEB9(format, arg...)	\	/* debug the protocol by showing transferred bits */	\	if (i2c_debug>=9) {	\		printk(LOG_LEVEL I2C_NAME " DEBUG: " format "\n",  ## arg ); \	}#else#define DEB0(fmt, args...)#define DEB1(fmt, args...)#define DEB2(fmt, args...)#define DEB3(fmt, args...)#define DEB9(fmt, args...)#endif/* ----- global variables ---------------------------------------------	*/struct omap24xx_i2c_adapter {	struct i2c_adapter adap;	unsigned long mmio_base_phys;	unsigned long mmio_base;	unsigned long mmio_size;	int irq;	wait_queue_head_t i2c_wait_queue;};static struct omap24xx_i2c_adapter *saved_oadap[NUM_I2C_ADAP];/* ----- module parameters --------------------------------------------	*/static int i2c_clock;	/* Fast Mode = 400 KHz, Standard Mode = 100 KHz */#ifdef I2C_OMAP24XX_DEBUGstatic int i2c_debug;#endif/* * I2C register I/O routines */static __inline__ u16 i2c_reg_in(const struct omap24xx_i2c_adapter *oadap, u32 offset){	return readw(oadap->mmio_base + offset);}static __inline__ u16 i2c_reg_out(const struct omap24xx_i2c_adapter *oadap, u32 offset, u16 val){	writew(val, oadap->mmio_base + offset);	return val;}/* ----- Utility functions --------------------------------------------	*//* Initialize the I2C controller.  Returns zero if successful,  * non-zero otherwise. */static inti2c_hw_init(struct omap24xx_i2c_adapter *oadap){	unsigned long timeout;	/* disable the I2C controller */	i2c_reg_out(oadap, I2C_CON, 0);	/* reset the I2C controller */	i2c_reg_out(oadap, I2C_SYSC, I2C_SYSC_SRST);	/* initialize clock prescaler for 12MHz ICLK */	i2c_reg_out(oadap, I2C_PSC, 0);	/* Initialize the I2C clock timers to generate an I2C bus clock 	 * frequency of i2c_clock kilohertz (default is 100 KHz).	 */	i2c_reg_out(oadap, I2C_SCLL, (12000/(i2c_clock*2)) - 7);	i2c_reg_out(oadap, I2C_SCLH, (12000/(i2c_clock*2)) - 7);	/* set our own slave address */	i2c_reg_out(oadap, I2C_OA, I2C_OWN_ADDRESS);	/* enable the I2C controller */	i2c_reg_out(oadap, I2C_CON, I2C_CON_I2C_EN);	/* wait for reset to complete */	timeout = jiffies + (I2C_RESET_TIMEOUT_MS*HZ)/1000;	while (!(i2c_reg_in(oadap, I2C_SYSS) & I2C_SYSS_RDONE)		&& time_before(jiffies, timeout))	{		if (!in_atomic()) {			set_current_state(TASK_INTERRUPTIBLE);			schedule_timeout(1);		} else			udelay(100);	}	if (!(i2c_reg_in(oadap, I2C_SYSS) & I2C_SYSS_RDONE))	{		DEB0("timeout waiting for I2C controller to reset");		return -ETIMEDOUT;	}	return 0;}static voidi2c_hw_release(struct omap24xx_i2c_adapter *oadap){	/* disable the I2C controller */	i2c_reg_out(oadap, I2C_CON, 0);}/* * The I2C bus protocol supports multiple masters.  This driver only supports  * using the OMAP I2C bus controller as a master, but we want to try to  * accommodate situations where there are other I2C masters sharing the bus.   * In order to allow for other bus masters, we have to check if the bus is  * busy before initiating a transfer.  This functions returns 0 if the bus is  * idle, or -ETIMEDOUT if a timeout occurs before the bus becomes idle. */static inti2c_wait_while_bb(const struct omap24xx_i2c_adapter *oadap){	unsigned long timeout = jiffies + (I2C_BUS_BUSY_TIMEOUT_MS*HZ)/1000;	while ((i2c_reg_in(oadap, I2C_STAT) & I2C_STAT_BB)		&& time_before(jiffies, timeout)) 	{		if (!in_atomic()) {			set_current_state(TASK_INTERRUPTIBLE);			schedule_timeout(1);		} else			udelay(100);	}	if (i2c_reg_in(oadap, I2C_STAT) & I2C_STAT_BB) {		DEB0("timeout waiting for bus to be idle");		return -ETIMEDOUT;	}	return 0;}/* * Wait for status register update.  Returns a negative error code or  * the value of the status register. */static inti2c_wait_for_status(struct omap24xx_i2c_adapter *oadap){	struct i2c_adapter *adap = &oadap->adap;	wait_queue_t i2c_wait;	u16 status;	const u16 ie_mask   = I2C_IE_XRDY_IE | I2C_IE_RRDY_IE 			    | I2C_IE_ARDY_IE | I2C_IE_NACK_IE | I2C_IE_AL_IE;	const u16 stat_mask = I2C_STAT_XRDY  | I2C_STAT_RRDY 			    | I2C_STAT_ARDY  | I2C_STAT_NACK  | I2C_STAT_AL;	status = i2c_reg_in(oadap, I2C_STAT);	if (!(status & stat_mask)) {		init_wait((&i2c_wait));		prepare_to_wait(&oadap->i2c_wait_queue, &i2c_wait, 			TASK_INTERRUPTIBLE);		/* enable I2C interrupts */		i2c_reg_out(oadap, I2C_IE, ie_mask);		status = i2c_reg_in(oadap, I2C_STAT);		if (!(status & stat_mask))			schedule_timeout(adap->timeout);				finish_wait(&oadap->i2c_wait_queue, &i2c_wait);				status = i2c_reg_in(oadap, I2C_STAT);		if (!(status & stat_mask)) {			/* still no status update */			if (signal_pending(current))				return -ERESTARTSYS;			else				return -ETIMEDOUT;		}	}	return status;}static irqreturn_t omap24xx_i2c_handler(int this_irq, void *dev_id, struct pt_regs *regs){	struct omap24xx_i2c_adapter *oadap = 		(struct omap24xx_i2c_adapter *) dev_id;		DEB3("in interrupt handler");		/* disable I2C interrupts */	i2c_reg_out(oadap, I2C_IE, 0);	wake_up_interruptible(&oadap->i2c_wait_queue);	return IRQ_HANDLED;}/* * Read or write a single message.  Returns the number of bytes  * successfully read or written.  A start (or restart) condition is always  * generated first, followed by msg->len data bytes.  A stop condition is  * generated if the stop parameter is non-zero.  Otherwise, the bus is left  * busy (clock line pulled low) so that the next transfer can begin with a  * restart. */static intomap24xx_i2c_xfer_msg(struct omap24xx_i2c_adapter *oadap, 	struct i2c_msg *msg, int stop){	struct i2c_adapter *adap = &oadap->adap;	u16 data;	int status;	int retries;	int count;	DEB0("bus: %d, addr: 0x%04x, len: %d, flags: 0x%x, stop: %d",	     adap->nr, msg->addr, msg->len, msg->flags, stop);	/* The OMAP controller can't generate a message with zero data bytes, so	 * return without doing anything in that case.	 */	if (!msg->len) {		DEB0("Skipping message with zero-length payload at addr"			" 0x%04x.", msg->addr);		return 0;	}	for (retries = 0; retries <= adap->retries; retries++) {		i2c_reg_out(oadap, I2C_SA, msg->addr);		i2c_reg_out(oadap, I2C_CNT, msg->len);		i2c_reg_out(oadap, I2C_CON, 			(I2C_CON_I2C_EN | I2C_CON_MST | I2C_CON_STT |			((msg->flags & I2C_M_TEN) ? I2C_CON_XA : 0) |			((msg->flags & I2C_M_RD) ? 0 : I2C_CON_TRX) |			(stop ? I2C_CON_STP : 0)));		count = 0;		while (count <= msg->len) {			status = i2c_wait_for_status(oadap);			if (status < 0) {				DEB0("I2C transfer aborted with error %d", 					status);				return status;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?