⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i2c-pnx.c

📁 i2c 在linux下的驱动设计
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Provides I2C support for Philips PNX010x/PNX4008 boards. * * Authors: Dennis Kovalev <dkovalev@ru.mvista.com> *	    Vitaly Wool <vwool@ru.mvista.com> * * 2004-2006 (c) MontaVista Software, Inc. 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/module.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/i2c.h>#include <linux/timer.h>#include <linux/completion.h>#include <linux/platform_device.h>#include <linux/i2c-pnx.h>#include <asm/hardware.h>#include <asm/irq.h>#include <asm/uaccess.h>#define I2C_PNX_TIMEOUT		10 /* msec */#define I2C_PNX_SPEED_KHZ	100#define I2C_PNX_REGION_SIZE	0x100#define PNX_DEFAULT_FREQ	13 /* MHz */static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data){	while (timeout > 0 &&			(ioread32(I2C_REG_STS(data)) & mstatus_active)) {		mdelay(1);		timeout--;	}	return (timeout <= 0);}static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data){	while (timeout > 0 &&			(ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) {		mdelay(1);		timeout--;	}	return (timeout <= 0);}static inline void i2c_pnx_arm_timer(struct i2c_adapter *adap){	struct i2c_pnx_algo_data *data = adap->algo_data;	struct timer_list *timer = &data->mif.timer;	int expires = I2C_PNX_TIMEOUT / (1000 / HZ);	del_timer_sync(timer);	dev_dbg(&adap->dev, "Timer armed at %lu plus %u jiffies.\n",		jiffies, expires);	timer->expires = jiffies + expires;	timer->data = (unsigned long)adap;	add_timer(timer);}/** * i2c_pnx_start - start a device * @slave_addr:		slave address * @adap:		pointer to adapter structure * * Generate a START signal in the desired mode. */static int i2c_pnx_start(unsigned char slave_addr, struct i2c_adapter *adap){	struct i2c_pnx_algo_data *alg_data = adap->algo_data;	dev_dbg(&adap->dev, "%s(): addr 0x%x mode %d\n", __FUNCTION__,		slave_addr, alg_data->mif.mode);	/* Check for 7 bit slave addresses only */	if (slave_addr & ~0x7f) {		dev_err(&adap->dev, "%s: Invalid slave address %x. "		       "Only 7-bit addresses are supported\n",		       adap->name, slave_addr);		return -EINVAL;	}	/* First, make sure bus is idle */	if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) {		/* Somebody else is monopolizing the bus */		dev_err(&adap->dev, "%s: Bus busy. Slave addr = %02x, "		       "cntrl = %x, stat = %x\n",		       adap->name, slave_addr,		       ioread32(I2C_REG_CTL(alg_data)),		       ioread32(I2C_REG_STS(alg_data)));		return -EBUSY;	} else if (ioread32(I2C_REG_STS(alg_data)) & mstatus_afi) {		/* Sorry, we lost the bus */		dev_err(&adap->dev, "%s: Arbitration failure. "		       "Slave addr = %02x\n", adap->name, slave_addr);		return -EIO;	}	/*	 * OK, I2C is enabled and we have the bus.	 * Clear the current TDI and AFI status flags.	 */	iowrite32(ioread32(I2C_REG_STS(alg_data)) | mstatus_tdi | mstatus_afi,		  I2C_REG_STS(alg_data));	dev_dbg(&adap->dev, "%s(): sending %#x\n", __FUNCTION__,		(slave_addr << 1) | start_bit | alg_data->mif.mode);	/* Write the slave address, START bit and R/W bit */	iowrite32((slave_addr << 1) | start_bit | alg_data->mif.mode,		  I2C_REG_TX(alg_data));	dev_dbg(&adap->dev, "%s(): exit\n", __FUNCTION__);	return 0;}/** * i2c_pnx_stop - stop a device * @adap:		pointer to I2C adapter structure * * Generate a STOP signal to terminate the master transaction. */static void i2c_pnx_stop(struct i2c_adapter *adap){	struct i2c_pnx_algo_data *alg_data = adap->algo_data;	/* Only 1 msec max timeout due to interrupt context */	long timeout = 1000;	dev_dbg(&adap->dev, "%s(): entering: stat = %04x.\n",		__FUNCTION__, ioread32(I2C_REG_STS(alg_data)));	/* Write a STOP bit to TX FIFO */	iowrite32(0xff | stop_bit, I2C_REG_TX(alg_data));	/* Wait until the STOP is seen. */	while (timeout > 0 &&	       (ioread32(I2C_REG_STS(alg_data)) & mstatus_active)) {		/* may be called from interrupt context */		udelay(1);		timeout--;	}	dev_dbg(&adap->dev, "%s(): exiting: stat = %04x.\n",		__FUNCTION__, ioread32(I2C_REG_STS(alg_data)));}/** * i2c_pnx_master_xmit - transmit data to slave * @adap:		pointer to I2C adapter structure * * Sends one byte of data to the slave */static int i2c_pnx_master_xmit(struct i2c_adapter *adap){	struct i2c_pnx_algo_data *alg_data = adap->algo_data;	u32 val;	dev_dbg(&adap->dev, "%s(): entering: stat = %04x.\n",		__FUNCTION__, ioread32(I2C_REG_STS(alg_data)));	if (alg_data->mif.len > 0) {		/* We still have something to talk about... */		val = *alg_data->mif.buf++;		if (alg_data->mif.len == 1) {			val |= stop_bit;			if (!alg_data->last)				val |= start_bit;		}		alg_data->mif.len--;		iowrite32(val, I2C_REG_TX(alg_data));		dev_dbg(&adap->dev, "%s(): xmit %#x [%d]\n", __FUNCTION__,			val, alg_data->mif.len + 1);		if (alg_data->mif.len == 0) {			if (alg_data->last) {				/* Wait until the STOP is seen. */				if (wait_timeout(I2C_PNX_TIMEOUT, alg_data))					dev_err(&adap->dev, "The bus is still "						"active after timeout\n");			}			/* Disable master interrupts */			iowrite32(ioread32(I2C_REG_CTL(alg_data)) &				~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),				  I2C_REG_CTL(alg_data));			del_timer_sync(&alg_data->mif.timer);			dev_dbg(&adap->dev, "%s(): Waking up xfer routine.\n",				__FUNCTION__);			complete(&alg_data->mif.complete);		}	} else if (alg_data->mif.len == 0) {		/* zero-sized transfer */		i2c_pnx_stop(adap);		/* Disable master interrupts. */		iowrite32(ioread32(I2C_REG_CTL(alg_data)) &			~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),			  I2C_REG_CTL(alg_data));		/* Stop timer. */		del_timer_sync(&alg_data->mif.timer);		dev_dbg(&adap->dev, "%s(): Waking up xfer routine after "			"zero-xfer.\n", __FUNCTION__);		complete(&alg_data->mif.complete);	}	dev_dbg(&adap->dev, "%s(): exiting: stat = %04x.\n",		__FUNCTION__, ioread32(I2C_REG_STS(alg_data)));	return 0;}/** * i2c_pnx_master_rcv - receive data from slave * @adap:		pointer to I2C adapter structure * * Reads one byte data from the slave */static int i2c_pnx_master_rcv(struct i2c_adapter *adap){	struct i2c_pnx_algo_data *alg_data = adap->algo_data;	unsigned int val = 0;	u32 ctl = 0;	dev_dbg(&adap->dev, "%s(): entering: stat = %04x.\n",		__FUNCTION__, ioread32(I2C_REG_STS(alg_data)));	/* Check, whether there is already data,	 * or we didn't 'ask' for it yet.	 */	if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) {		dev_dbg(&adap->dev, "%s(): Write dummy data to fill "			"Rx-fifo...\n", __FUNCTION__);		if (alg_data->mif.len == 1) {			/* Last byte, do not acknowledge next rcv. */			val |= stop_bit;			if (!alg_data->last)				val |= start_bit;			/*			 * Enable interrupt RFDAIE (data in Rx fifo),			 * and disable DRMIE (need data for Tx)			 */			ctl = ioread32(I2C_REG_CTL(alg_data));			ctl |= mcntrl_rffie | mcntrl_daie;			ctl &= ~mcntrl_drmie;			iowrite32(ctl, I2C_REG_CTL(alg_data));		}		/*		 * Now we'll 'ask' for data:		 * For each byte we want to receive, we must		 * write a (dummy) byte to the Tx-FIFO.		 */		iowrite32(val, I2C_REG_TX(alg_data));		return 0;	}	/* Handle data. */	if (alg_data->mif.len > 0) {		val = ioread32(I2C_REG_RX(alg_data));		*alg_data->mif.buf++ = (u8) (val & 0xff);		dev_dbg(&adap->dev, "%s(): rcv 0x%x [%d]\n", __FUNCTION__, val,			alg_data->mif.len);		alg_data->mif.len--;		if (alg_data->mif.len == 0) {			if (alg_data->last)				/* Wait until the STOP is seen. */				if (wait_timeout(I2C_PNX_TIMEOUT, alg_data))					dev_err(&adap->dev, "The bus is still "						"active after timeout\n");			/* Disable master interrupts */			ctl = ioread32(I2C_REG_CTL(alg_data));			ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie |				 mcntrl_drmie | mcntrl_daie);			iowrite32(ctl, I2C_REG_CTL(alg_data));			/* Kill timer. */			del_timer_sync(&alg_data->mif.timer);			complete(&alg_data->mif.complete);		}	}	dev_dbg(&adap->dev, "%s(): exiting: stat = %04x.\n",		__FUNCTION__, ioread32(I2C_REG_STS(alg_data)));	return 0;}static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id){	u32 stat, ctl;	struct i2c_adapter *adap = dev_id;	struct i2c_pnx_algo_data *alg_data = adap->algo_data;	dev_dbg(&adap->dev, "%s(): mstat = %x mctrl = %x, mode = %d\n",		__FUNCTION__,		ioread32(I2C_REG_STS(alg_data)),		ioread32(I2C_REG_CTL(alg_data)),		alg_data->mif.mode);	stat = ioread32(I2C_REG_STS(alg_data));	/* let's see what kind of event this is */	if (stat & mstatus_afi) {		/* We lost arbitration in the midst of a transfer */		alg_data->mif.ret = -EIO;		/* Disable master interrupts. */		ctl = ioread32(I2C_REG_CTL(alg_data));		ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie |			 mcntrl_drmie);		iowrite32(ctl, I2C_REG_CTL(alg_data));		/* Stop timer, to prevent timeout. */		del_timer_sync(&alg_data->mif.timer);		complete(&alg_data->mif.complete);	} else if (stat & mstatus_nai) {		/* Slave did not acknowledge, generate a STOP */		dev_dbg(&adap->dev, "%s(): "			"Slave did not acknowledge, generating a STOP.\n",			__FUNCTION__);		i2c_pnx_stop(adap);		/* Disable master interrupts. */		ctl = ioread32(I2C_REG_CTL(alg_data));		ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie |			 mcntrl_drmie);		iowrite32(ctl, I2C_REG_CTL(alg_data));		/* Our return value. */		alg_data->mif.ret = -EIO;		/* Stop timer, to prevent timeout. */		del_timer_sync(&alg_data->mif.timer);

⌨️ 快捷键说明

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