📄 i2c-mv64xxx.c
字号:
/* * 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 + -