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

📄 i2c-pxa.c

📁 i2c 在linux下的驱动设计
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  i2c_adap_pxa.c * *  I2C adapter for the PXA I2C bus access. * *  Copyright (C) 2002 Intrinsyc Software Inc. *  Copyright (C) 2004-2005 Deep Blue Solutions Ltd. * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License version 2 as *  published by the Free Software Foundation. * *  History: *    Apr 2002: Initial version [CS] *    Jun 2002: Properly seperated algo/adap [FB] *    Jan 2003: Fixed several bugs concerning interrupt handling [Kai-Uwe Bloem] *    Jan 2003: added limited signal handling [Kai-Uwe Bloem] *    Sep 2004: Major rework to ensure efficient bus handling [RMK] *    Dec 2004: Added support for PXA27x and slave device probing [Liam Girdwood] *    Feb 2005: Rework slave mode handling [RMK] */#include <linux/kernel.h>#include <linux/module.h>#include <linux/i2c.h>#include <linux/i2c-id.h>#include <linux/init.h>#include <linux/time.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/i2c-pxa.h>#include <linux/platform_device.h>#include <asm/hardware.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/arch/i2c.h>#include <asm/arch/pxa-regs.h>struct pxa_i2c {	spinlock_t		lock;	wait_queue_head_t	wait;	struct i2c_msg		*msg;	unsigned int		msg_num;	unsigned int		msg_idx;	unsigned int		msg_ptr;	unsigned int		slave_addr;	struct i2c_adapter	adap;#ifdef CONFIG_I2C_PXA_SLAVE	struct i2c_slave_client *slave;#endif	unsigned int		irqlogidx;	u32			isrlog[32];	u32			icrlog[32];	void __iomem		*reg_base;	unsigned long		iobase;	unsigned long		iosize;	int			irq;};#define _IBMR(i2c)	((i2c)->reg_base + 0)#define _IDBR(i2c)	((i2c)->reg_base + 8)#define _ICR(i2c)	((i2c)->reg_base + 0x10)#define _ISR(i2c)	((i2c)->reg_base + 0x18)#define _ISAR(i2c)	((i2c)->reg_base + 0x20)/* * I2C Slave mode address */#define I2C_PXA_SLAVE_ADDR      0x1#ifdef DEBUGstruct bits {	u32	mask;	const char *set;	const char *unset;};#define BIT(m, s, u)	{ .mask = m, .set = s, .unset = u }static inline voiddecode_bits(const char *prefix, const struct bits *bits, int num, u32 val){	printk("%s %08x: ", prefix, val);	while (num--) {		const char *str = val & bits->mask ? bits->set : bits->unset;		if (str)			printk("%s ", str);		bits++;	}}static const struct bits isr_bits[] = {	BIT(ISR_RWM,	"RX",		"TX"),	BIT(ISR_ACKNAK,	"NAK",		"ACK"),	BIT(ISR_UB,	"Bsy",		"Rdy"),	BIT(ISR_IBB,	"BusBsy",	"BusRdy"),	BIT(ISR_SSD,	"SlaveStop",	NULL),	BIT(ISR_ALD,	"ALD",		NULL),	BIT(ISR_ITE,	"TxEmpty",	NULL),	BIT(ISR_IRF,	"RxFull",	NULL),	BIT(ISR_GCAD,	"GenCall",	NULL),	BIT(ISR_SAD,	"SlaveAddr",	NULL),	BIT(ISR_BED,	"BusErr",	NULL),};static void decode_ISR(unsigned int val){	decode_bits(KERN_DEBUG "ISR", isr_bits, ARRAY_SIZE(isr_bits), val);	printk("\n");}static const struct bits icr_bits[] = {	BIT(ICR_START,  "START",	NULL),	BIT(ICR_STOP,   "STOP",		NULL),	BIT(ICR_ACKNAK, "ACKNAK",	NULL),	BIT(ICR_TB,     "TB",		NULL),	BIT(ICR_MA,     "MA",		NULL),	BIT(ICR_SCLE,   "SCLE",		"scle"),	BIT(ICR_IUE,    "IUE",		"iue"),	BIT(ICR_GCD,    "GCD",		NULL),	BIT(ICR_ITEIE,  "ITEIE",	NULL),	BIT(ICR_IRFIE,  "IRFIE",	NULL),	BIT(ICR_BEIE,   "BEIE",		NULL),	BIT(ICR_SSDIE,  "SSDIE",	NULL),	BIT(ICR_ALDIE,  "ALDIE",	NULL),	BIT(ICR_SADIE,  "SADIE",	NULL),	BIT(ICR_UR,     "UR",		"ur"),};static void decode_ICR(unsigned int val){	decode_bits(KERN_DEBUG "ICR", icr_bits, ARRAY_SIZE(icr_bits), val);	printk("\n");}static unsigned int i2c_debug = DEBUG;static void i2c_pxa_show_state(struct pxa_i2c *i2c, int lno, const char *fname){	dev_dbg(&i2c->adap.dev, "state:%s:%d: ISR=%08x, ICR=%08x, IBMR=%02x\n", fname, lno,		readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c)));}#define show_state(i2c) i2c_pxa_show_state(i2c, __LINE__, __FUNCTION__)#else#define i2c_debug	0#define show_state(i2c) do { } while (0)#define decode_ISR(val) do { } while (0)#define decode_ICR(val) do { } while (0)#endif#define eedbg(lvl, x...) do { if ((lvl) < 1) { printk(KERN_DEBUG "" x); } } while(0)static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret);static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why){	unsigned int i;	printk("i2c: error: %s\n", why);	printk("i2c: msg_num: %d msg_idx: %d msg_ptr: %d\n",		i2c->msg_num, i2c->msg_idx, i2c->msg_ptr);	printk("i2c: ICR: %08x ISR: %08x\n"	       "i2c: log: ", readl(_ICR(i2c)), readl(_ISR(i2c)));	for (i = 0; i < i2c->irqlogidx; i++)		printk("[%08x:%08x] ", i2c->isrlog[i], i2c->icrlog[i]);	printk("\n");}static inline int i2c_pxa_is_slavemode(struct pxa_i2c *i2c){	return !(readl(_ICR(i2c)) & ICR_SCLE);}static void i2c_pxa_abort(struct pxa_i2c *i2c){	unsigned long timeout = jiffies + HZ/4;	if (i2c_pxa_is_slavemode(i2c)) {		dev_dbg(&i2c->adap.dev, "%s: called in slave mode\n", __func__);		return;	}	while (time_before(jiffies, timeout) && (readl(_IBMR(i2c)) & 0x1) == 0) {		unsigned long icr = readl(_ICR(i2c));		icr &= ~ICR_START;		icr |= ICR_ACKNAK | ICR_STOP | ICR_TB;		writel(icr, _ICR(i2c));		show_state(i2c);		msleep(1);	}	writel(readl(_ICR(i2c)) & ~(ICR_MA | ICR_START | ICR_STOP),	       _ICR(i2c));}static int i2c_pxa_wait_bus_not_busy(struct pxa_i2c *i2c){	int timeout = DEF_TIMEOUT;	while (timeout-- && readl(_ISR(i2c)) & (ISR_IBB | ISR_UB)) {		if ((readl(_ISR(i2c)) & ISR_SAD) != 0)			timeout += 4;		msleep(2);		show_state(i2c);	}	if (timeout <= 0)		show_state(i2c);	return timeout <= 0 ? I2C_RETRY : 0;}static int i2c_pxa_wait_master(struct pxa_i2c *i2c){	unsigned long timeout = jiffies + HZ*4;	while (time_before(jiffies, timeout)) {		if (i2c_debug > 1)			dev_dbg(&i2c->adap.dev, "%s: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n",				__func__, (long)jiffies, readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c)));		if (readl(_ISR(i2c)) & ISR_SAD) {			if (i2c_debug > 0)				dev_dbg(&i2c->adap.dev, "%s: Slave detected\n", __func__);			goto out;		}		/* wait for unit and bus being not busy, and we also do a		 * quick check of the i2c lines themselves to ensure they've		 * gone high...		 */		if ((readl(_ISR(i2c)) & (ISR_UB | ISR_IBB)) == 0 && readl(_IBMR(i2c)) == 3) {			if (i2c_debug > 0)				dev_dbg(&i2c->adap.dev, "%s: done\n", __func__);			return 1;		}		msleep(1);	}	if (i2c_debug > 0)		dev_dbg(&i2c->adap.dev, "%s: did not free\n", __func__); out:	return 0;}static int i2c_pxa_set_master(struct pxa_i2c *i2c){	if (i2c_debug)		dev_dbg(&i2c->adap.dev, "setting to bus master\n");	if ((readl(_ISR(i2c)) & (ISR_UB | ISR_IBB)) != 0) {		dev_dbg(&i2c->adap.dev, "%s: unit is busy\n", __func__);		if (!i2c_pxa_wait_master(i2c)) {			dev_dbg(&i2c->adap.dev, "%s: error: unit busy\n", __func__);			return I2C_RETRY;		}	}	writel(readl(_ICR(i2c)) | ICR_SCLE, _ICR(i2c));	return 0;}#ifdef CONFIG_I2C_PXA_SLAVEstatic int i2c_pxa_wait_slave(struct pxa_i2c *i2c){	unsigned long timeout = jiffies + HZ*1;	/* wait for stop */	show_state(i2c);	while (time_before(jiffies, timeout)) {		if (i2c_debug > 1)			dev_dbg(&i2c->adap.dev, "%s: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n",				__func__, (long)jiffies, readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c)));		if ((readl(_ISR(i2c)) & (ISR_UB|ISR_IBB)) == 0 ||		    (readl(_ISR(i2c)) & ISR_SAD) != 0 ||		    (readl(_ICR(i2c)) & ICR_SCLE) == 0) {			if (i2c_debug > 1)				dev_dbg(&i2c->adap.dev, "%s: done\n", __func__);			return 1;		}		msleep(1);	}	if (i2c_debug > 0)		dev_dbg(&i2c->adap.dev, "%s: did not free\n", __func__);	return 0;}/* * clear the hold on the bus, and take of anything else * that has been configured */static void i2c_pxa_set_slave(struct pxa_i2c *i2c, int errcode){	show_state(i2c);	if (errcode < 0) {		udelay(100);   /* simple delay */	} else {		/* we need to wait for the stop condition to end */		/* if we where in stop, then clear... */		if (readl(_ICR(i2c)) & ICR_STOP) {			udelay(100);			writel(readl(_ICR(i2c)) & ~ICR_STOP, _ICR(i2c));		}		if (!i2c_pxa_wait_slave(i2c)) {			dev_err(&i2c->adap.dev, "%s: wait timedout\n",				__func__);			return;		}	}	writel(readl(_ICR(i2c)) & ~(ICR_STOP|ICR_ACKNAK|ICR_MA), _ICR(i2c));	writel(readl(_ICR(i2c)) & ~ICR_SCLE, _ICR(i2c));	if (i2c_debug) {		dev_dbg(&i2c->adap.dev, "ICR now %08x, ISR %08x\n", readl(_ICR(i2c)), readl(_ISR(i2c)));		decode_ICR(readl(_ICR(i2c)));	}}#else#define i2c_pxa_set_slave(i2c, err)	do { } while (0)#endifstatic void i2c_pxa_reset(struct pxa_i2c *i2c){	pr_debug("Resetting I2C Controller Unit\n");	/* abort any transfer currently under way */	i2c_pxa_abort(i2c);	/* reset according to 9.8 */	writel(ICR_UR, _ICR(i2c));	writel(I2C_ISR_INIT, _ISR(i2c));	writel(readl(_ICR(i2c)) & ~ICR_UR, _ICR(i2c));	writel(i2c->slave_addr, _ISAR(i2c));	/* set control register values */	writel(I2C_ICR_INIT, _ICR(i2c));#ifdef CONFIG_I2C_PXA_SLAVE	dev_info(&i2c->adap.dev, "Enabling slave mode\n");	writel(readl(_ICR(i2c)) | ICR_SADIE | ICR_ALDIE | ICR_SSDIE, _ICR(i2c));#endif	i2c_pxa_set_slave(i2c, 0);	/* enable unit */	writel(readl(_ICR(i2c)) | ICR_IUE, _ICR(i2c));	udelay(100);}#ifdef CONFIG_I2C_PXA_SLAVE/* * PXA I2C Slave mode */static void i2c_pxa_slave_txempty(struct pxa_i2c *i2c, u32 isr){	if (isr & ISR_BED) {		/* what should we do here? */	} else {		int ret = 0;		if (i2c->slave != NULL)			ret = i2c->slave->read(i2c->slave->data);		writel(ret, _IDBR(i2c));		writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c));   /* allow next byte */	}}static void i2c_pxa_slave_rxfull(struct pxa_i2c *i2c, u32 isr){	unsigned int byte = readl(_IDBR(i2c));	if (i2c->slave != NULL)		i2c->slave->write(i2c->slave->data, byte);	writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c));}static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr){	int timeout;	if (i2c_debug > 0)		dev_dbg(&i2c->adap.dev, "SAD, mode is slave-%cx\n",		       (isr & ISR_RWM) ? 'r' : 't');	if (i2c->slave != NULL)		i2c->slave->event(i2c->slave->data,				 (isr & ISR_RWM) ? I2C_SLAVE_EVENT_START_READ : I2C_SLAVE_EVENT_START_WRITE);	/*	 * slave could interrupt in the middle of us generating a	 * start condition... if this happens, we'd better back off	 * and stop holding the poor thing up	 */	writel(readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP), _ICR(i2c));	writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c));	timeout = 0x10000;	while (1) {		if ((readl(_IBMR(i2c)) & 2) == 2)			break;		timeout--;		if (timeout <= 0) {			dev_err(&i2c->adap.dev, "timeout waiting for SCL high\n");			break;		}	}	writel(readl(_ICR(i2c)) & ~ICR_SCLE, _ICR(i2c));}static void i2c_pxa_slave_stop(struct pxa_i2c *i2c){	if (i2c_debug > 2)		dev_dbg(&i2c->adap.dev, "ISR: SSD (Slave Stop)\n");	if (i2c->slave != NULL)		i2c->slave->event(i2c->slave->data, I2C_SLAVE_EVENT_STOP);	if (i2c_debug > 2)		dev_dbg(&i2c->adap.dev, "ISR: SSD (Slave Stop) acked\n");	/*	 * If we have a master-mode message waiting,	 * kick it off now that the slave has completed.	 */	if (i2c->msg)		i2c_pxa_master_complete(i2c, I2C_RETRY);}#elsestatic void i2c_pxa_slave_txempty(struct pxa_i2c *i2c, u32 isr){	if (isr & ISR_BED) {		/* what should we do here? */	} else {		writel(0, _IDBR(i2c));		writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c));	}}static void i2c_pxa_slave_rxfull(struct pxa_i2c *i2c, u32 isr){	writel(readl(_ICR(i2c)) | ICR_TB | ICR_ACKNAK, _ICR(i2c));}static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr){	int timeout;	/*	 * slave could interrupt in the middle of us generating a	 * start condition... if this happens, we'd better back off	 * and stop holding the poor thing up	 */	writel(readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP), _ICR(i2c));	writel(readl(_ICR(i2c)) | ICR_TB | ICR_ACKNAK, _ICR(i2c));	timeout = 0x10000;	while (1) {		if ((readl(_IBMR(i2c)) & 2) == 2)			break;		timeout--;		if (timeout <= 0) {			dev_err(&i2c->adap.dev, "timeout waiting for SCL high\n");			break;		}	}

⌨️ 快捷键说明

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