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

📄 i2c-mv64xxx.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * drivers/i2c/busses/i2c-mv64xxx.c *  * Driver for the i2c controller on the Marvell line of host bridges for MIPS * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0). * * Author: Mark A. Greer <mgreer@mvista.com> * * 2005 (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/kernel.h>#include <linux/module.h>#include <linux/spinlock.h>#include <linux/i2c.h>#include <linux/interrupt.h>#include <linux/mv643xx.h>#include <linux/platform_device.h>#include <asm/io.h>/* Register defines */#define	MV64XXX_I2C_REG_SLAVE_ADDR			0x00#define	MV64XXX_I2C_REG_DATA				0x04#define	MV64XXX_I2C_REG_CONTROL				0x08#define	MV64XXX_I2C_REG_STATUS				0x0c#define	MV64XXX_I2C_REG_BAUD				0x0c#define	MV64XXX_I2C_REG_EXT_SLAVE_ADDR			0x10#define	MV64XXX_I2C_REG_SOFT_RESET			0x1c#define	MV64XXX_I2C_REG_CONTROL_ACK			0x00000004#define	MV64XXX_I2C_REG_CONTROL_IFLG			0x00000008#define	MV64XXX_I2C_REG_CONTROL_STOP			0x00000010#define	MV64XXX_I2C_REG_CONTROL_START			0x00000020#define	MV64XXX_I2C_REG_CONTROL_TWSIEN			0x00000040#define	MV64XXX_I2C_REG_CONTROL_INTEN			0x00000080/* Ctlr status values */#define	MV64XXX_I2C_STATUS_BUS_ERR			0x00#define	MV64XXX_I2C_STATUS_MAST_START			0x08#define	MV64XXX_I2C_STATUS_MAST_REPEAT_START		0x10#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK		0x18#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK		0x20#define	MV64XXX_I2C_STATUS_MAST_WR_ACK			0x28#define	MV64XXX_I2C_STATUS_MAST_WR_NO_ACK		0x30#define	MV64XXX_I2C_STATUS_MAST_LOST_ARB		0x38#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK		0x40#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK		0x48#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK		0x50#define	MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK		0x58#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK		0xd0#define	MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK	0xd8#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK		0xe0#define	MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK	0xe8#define	MV64XXX_I2C_STATUS_NO_STATUS			0xf8/* Driver states */enum {	MV64XXX_I2C_STATE_INVALID,	MV64XXX_I2C_STATE_IDLE,	MV64XXX_I2C_STATE_WAITING_FOR_START_COND,	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,	MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,	MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,	MV64XXX_I2C_STATE_ABORTING,};/* Driver actions */enum {	MV64XXX_I2C_ACTION_INVALID,	MV64XXX_I2C_ACTION_CONTINUE,	MV64XXX_I2C_ACTION_SEND_START,	MV64XXX_I2C_ACTION_SEND_ADDR_1,	MV64XXX_I2C_ACTION_SEND_ADDR_2,	MV64XXX_I2C_ACTION_SEND_DATA,	MV64XXX_I2C_ACTION_RCV_DATA,	MV64XXX_I2C_ACTION_RCV_DATA_STOP,	MV64XXX_I2C_ACTION_SEND_STOP,};struct mv64xxx_i2c_data {	int			irq;	u32			state;	u32			action;	u32			cntl_bits;	void __iomem		*reg_base;	u32			reg_base_p;	u32			addr1;	u32			addr2;	u32			bytes_left;	u32			byte_posn;	u32			block;	int			rc;	u32			freq_m;	u32			freq_n;	wait_queue_head_t	waitq;	spinlock_t		lock;	struct i2c_msg		*msg;	struct i2c_adapter	adapter;};/* ***************************************************************************** * *	Finite State Machine & Interrupt Routines * ***************************************************************************** */static voidmv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status){	/*	 * If state is idle, then this is likely the remnants of an old	 * operation that driver has given up on or the user has killed.	 * If so, issue the stop condition and go to idle.	 */	if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;		return;	}	if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;		drv_data->state = MV64XXX_I2C_STATE_IDLE;		return;	}	/* The status from the ctlr [mostly] tells us what to do next */	switch (status) {	/* Start condition interrupt */	case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */	case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */		drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;		break;	/* Performing a write */	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */		if (drv_data->msg->flags & I2C_M_TEN) {			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;			drv_data->state =				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;			break;		}		/* FALLTHRU */	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */	case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */		if (drv_data->bytes_left > 0) {			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;			drv_data->state =				MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;			drv_data->bytes_left--;		} else {			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;			drv_data->state = MV64XXX_I2C_STATE_IDLE;		}		break;	/* Performing a read */	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */		if (drv_data->msg->flags & I2C_M_TEN) {			drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;			drv_data->state =				MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;			break;		}		/* FALLTHRU */	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */		if (drv_data->bytes_left == 0) {			drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;			drv_data->state = MV64XXX_I2C_STATE_IDLE;			break;		}		/* FALLTHRU */	case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */		if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK)			drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;		else {			drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA;			drv_data->bytes_left--;		}		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;		if (drv_data->bytes_left == 1)			drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;		break;	case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */		drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;		drv_data->state = MV64XXX_I2C_STATE_IDLE;		break;	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */	case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */	case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */		/* Doesn't seem to be a device at other end */		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;		drv_data->state = MV64XXX_I2C_STATE_IDLE;		drv_data->rc = -ENODEV;		break;	default:		dev_err(&drv_data->adapter.dev,			"mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, "			"status: 0x%x, addr: 0x%x, flags: 0x%x\n",			 drv_data->state, status, drv_data->msg->addr,			 drv_data->msg->flags);		drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;		drv_data->state = MV64XXX_I2C_STATE_IDLE;		drv_data->rc = -EIO;	}}static voidmv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data){	switch(drv_data->action) {	case MV64XXX_I2C_ACTION_CONTINUE:		writel(drv_data->cntl_bits,			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);		break;	case MV64XXX_I2C_ACTION_SEND_START:		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);		break;	case MV64XXX_I2C_ACTION_SEND_ADDR_1:		writel(drv_data->addr1,			drv_data->reg_base + MV64XXX_I2C_REG_DATA);		writel(drv_data->cntl_bits,			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);		break;	case MV64XXX_I2C_ACTION_SEND_ADDR_2:		writel(drv_data->addr2,			drv_data->reg_base + MV64XXX_I2C_REG_DATA);		writel(drv_data->cntl_bits,			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);		break;	case MV64XXX_I2C_ACTION_SEND_DATA:		writel(drv_data->msg->buf[drv_data->byte_posn++],			drv_data->reg_base + MV64XXX_I2C_REG_DATA);		writel(drv_data->cntl_bits,			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);		break;	case MV64XXX_I2C_ACTION_RCV_DATA:		drv_data->msg->buf[drv_data->byte_posn++] =			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);		writel(drv_data->cntl_bits,			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);		break;	case MV64XXX_I2C_ACTION_RCV_DATA_STOP:		drv_data->msg->buf[drv_data->byte_posn++] =			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);		drv_data->block = 0;		wake_up_interruptible(&drv_data->waitq);		break;	case MV64XXX_I2C_ACTION_INVALID:	default:		dev_err(&drv_data->adapter.dev,			"mv64xxx_i2c_do_action: Invalid action: %d\n",			drv_data->action);		drv_data->rc = -EIO;		/* FALLTHRU */	case MV64XXX_I2C_ACTION_SEND_STOP:		drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);		drv_data->block = 0;		wake_up_interruptible(&drv_data->waitq);		break;	}}static intmv64xxx_i2c_intr(int irq, void *dev_id, struct pt_regs *regs){	struct mv64xxx_i2c_data	*drv_data = dev_id;	unsigned long	flags;	u32		status;	int		rc = IRQ_NONE;	spin_lock_irqsave(&drv_data->lock, flags);	while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &						MV64XXX_I2C_REG_CONTROL_IFLG) {		status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS);		mv64xxx_i2c_fsm(drv_data, status);		mv64xxx_i2c_do_action(drv_data);

⌨️ 快捷键说明

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