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

📄 i2c-pxa.c

📁 linux-2.6.15.6
💻 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/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];};/* * 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, ISR, ICR, IBMR);}#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: ", ICR, ISR);	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 !(ICR & 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) && (IBMR & 0x1) == 0) {		unsigned long icr = ICR;		icr &= ~ICR_START;		icr |= ICR_ACKNAK | ICR_STOP | ICR_TB;		ICR = icr;		show_state(i2c);		msleep(1);	}	ICR &= ~(ICR_MA | ICR_START | ICR_STOP);}static int i2c_pxa_wait_bus_not_busy(struct pxa_i2c *i2c){	int timeout = DEF_TIMEOUT;	while (timeout-- && ISR & (ISR_IBB | ISR_UB)) {		if ((ISR & 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, ISR, ICR, IBMR);		if (ISR & 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 ((ISR & (ISR_UB | ISR_IBB)) == 0 && IBMR == 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 ((ISR & (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;		}	}	ICR |= ICR_SCLE;	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, ISR, ICR, IBMR);		if ((ISR & (ISR_UB|ISR_IBB|ISR_SAD)) == ISR_SAD ||		    (ICR & 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 (ICR & ICR_STOP) {			udelay(100);			ICR &= ~ICR_STOP;		}		if (!i2c_pxa_wait_slave(i2c)) {			dev_err(&i2c->adap.dev, "%s: wait timedout\n",				__func__);			return;		}	}	ICR &= ~(ICR_STOP|ICR_ACKNAK|ICR_MA);	ICR &= ~ICR_SCLE;	if (i2c_debug) {		dev_dbg(&i2c->adap.dev, "ICR now %08x, ISR %08x\n", ICR, ISR);		decode_ICR(ICR);	}}#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 */	ICR = ICR_UR;	ISR = I2C_ISR_INIT;	ICR &= ~ICR_UR;	ISAR = i2c->slave_addr;	/* set control register values */	ICR = I2C_ICR_INIT;#ifdef CONFIG_I2C_PXA_SLAVE	dev_info(&i2c->adap.dev, "Enabling slave mode\n");	ICR |= ICR_SADIE | ICR_ALDIE | ICR_SSDIE;#endif	i2c_pxa_set_slave(i2c, 0);	/* enable unit */	ICR |= ICR_IUE;	udelay(100);}#ifdef CONFIG_I2C_PXA_SLAVE/* * I2C EEPROM emulation. */static struct i2c_eeprom_emu eeprom = {	.size = I2C_EEPROM_EMU_SIZE,	.watch = LIST_HEAD_INIT(eeprom.watch),};struct i2c_eeprom_emu *i2c_pxa_get_eeprom(void){	return &eeprom;}int i2c_eeprom_emu_addwatcher(struct i2c_eeprom_emu *emu, void *data,			      unsigned int addr, unsigned int size,			      struct i2c_eeprom_emu_watcher *watcher){	struct i2c_eeprom_emu_watch *watch;	unsigned long flags;	if (addr + size > emu->size)		return -EINVAL;	watch = kmalloc(sizeof(struct i2c_eeprom_emu_watch), GFP_KERNEL);	if (watch) {		watch->start = addr;		watch->end = addr + size - 1;		watch->ops = watcher;		watch->data = data;		local_irq_save(flags);		list_add(&watch->node, &emu->watch);		local_irq_restore(flags);	}	return watch ? 0 : -ENOMEM;}void i2c_eeprom_emu_delwatcher(struct i2c_eeprom_emu *emu, void *data,			       struct i2c_eeprom_emu_watcher *watcher){	struct i2c_eeprom_emu_watch *watch, *n;	unsigned long flags;	list_for_each_entry_safe(watch, n, &emu->watch, node) {		if (watch->ops == watcher && watch->data == data) {			local_irq_save(flags);			list_del(&watch->node);			local_irq_restore(flags);			kfree(watch);		}	}}static void i2c_eeprom_emu_event(void *ptr, i2c_slave_event_t event){	struct i2c_eeprom_emu *emu = ptr;	eedbg(3, "i2c_eeprom_emu_event: %d\n", event);	switch (event) {	case I2C_SLAVE_EVENT_START_WRITE:		emu->seen_start = 1;		eedbg(2, "i2c_eeprom: write initiated\n");		break;	case I2C_SLAVE_EVENT_START_READ:		emu->seen_start = 0;		eedbg(2, "i2c_eeprom: read initiated\n");		break;	case I2C_SLAVE_EVENT_STOP:		emu->seen_start = 0;		eedbg(2, "i2c_eeprom: received stop\n");		break;	default:		eedbg(0, "i2c_eeprom: unhandled event\n");		break;	}}static int i2c_eeprom_emu_read(void *ptr){	struct i2c_eeprom_emu *emu = ptr;	int ret;	ret = emu->bytes[emu->ptr];	emu->ptr = (emu->ptr + 1) % emu->size;	return ret;}static void i2c_eeprom_emu_write(void *ptr, unsigned int val){	struct i2c_eeprom_emu *emu = ptr;	struct i2c_eeprom_emu_watch *watch;	if (emu->seen_start != 0) {		eedbg(2, "i2c_eeprom_emu_write: setting ptr %02x\n", val);		emu->ptr = val;		emu->seen_start = 0;		return;	}	emu->bytes[emu->ptr] = val;	eedbg(1, "i2c_eeprom_emu_write: ptr=0x%02x, val=0x%02x\n",	      emu->ptr, val);	list_for_each_entry(watch, &emu->watch, node) {		if (!watch->ops || !watch->ops->write)			continue;		if (watch->start <= emu->ptr && watch->end >= emu->ptr)			watch->ops->write(watch->data, emu->ptr, val);	}	emu->ptr = (emu->ptr + 1) % emu->size;}struct i2c_slave_client eeprom_client = {	.data	= &eeprom,	.event	= i2c_eeprom_emu_event,	.read	= i2c_eeprom_emu_read,	.write	= i2c_eeprom_emu_write};/* * 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 = i2c->slave->read(i2c->slave->data);		IDBR = ret;		ICR |= ICR_TB;   /* allow next byte */	}}static void i2c_pxa_slave_rxfull(struct pxa_i2c *i2c, u32 isr){	unsigned int byte = IDBR;	if (i2c->slave != NULL)		i2c->slave->write(i2c->slave->data, byte);	ICR |= ICR_TB;}static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr){	int timeout;

⌨️ 快捷键说明

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