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

📄 i2c-pnx.c

📁 i2c 在linux下的驱动设计
💻 C
📖 第 1 页 / 共 2 页
字号:
		complete(&alg_data->mif.complete);	} else {		/*		 * Two options:		 * - Master Tx needs data.		 * - There is data in the Rx-fifo		 * The latter is only the case if we have requested for data,		 * via a dummy write. (See 'i2c_pnx_master_rcv'.)		 * We therefore check, as a sanity check, whether that interrupt		 * has been enabled.		 */		if ((stat & mstatus_drmi) || !(stat & mstatus_rfe)) {			if (alg_data->mif.mode == I2C_SMBUS_WRITE) {				i2c_pnx_master_xmit(adap);			} else if (alg_data->mif.mode == I2C_SMBUS_READ) {				i2c_pnx_master_rcv(adap);			}		}	}	/* Clear TDI and AFI bits */	stat = ioread32(I2C_REG_STS(alg_data));	iowrite32(stat | mstatus_tdi | mstatus_afi, I2C_REG_STS(alg_data));	dev_dbg(&adap->dev, "%s(): exiting, stat = %x ctrl = %x.\n",		 __FUNCTION__, ioread32(I2C_REG_STS(alg_data)),		 ioread32(I2C_REG_CTL(alg_data)));	return IRQ_HANDLED;}static void i2c_pnx_timeout(unsigned long data){	struct i2c_adapter *adap = (struct i2c_adapter *)data;	struct i2c_pnx_algo_data *alg_data = adap->algo_data;	u32 ctl;	dev_err(&adap->dev, "Master timed out. stat = %04x, cntrl = %04x. "	       "Resetting master...\n",	       ioread32(I2C_REG_STS(alg_data)),	       ioread32(I2C_REG_CTL(alg_data)));	/* Reset master and disable interrupts */	ctl = ioread32(I2C_REG_CTL(alg_data));	ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | mcntrl_drmie);	iowrite32(ctl, I2C_REG_CTL(alg_data));	ctl |= mcntrl_reset;	iowrite32(ctl, I2C_REG_CTL(alg_data));	wait_reset(I2C_PNX_TIMEOUT, alg_data);	alg_data->mif.ret = -EIO;	complete(&alg_data->mif.complete);}static inline void bus_reset_if_active(struct i2c_adapter *adap){	struct i2c_pnx_algo_data *alg_data = adap->algo_data;	u32 stat;	if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_active) {		dev_err(&adap->dev,			"%s: Bus is still active after xfer. Reset it...\n",		       adap->name);		iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,			  I2C_REG_CTL(alg_data));		wait_reset(I2C_PNX_TIMEOUT, alg_data);	} else if (!(stat & mstatus_rfe) || !(stat & mstatus_tfe)) {		/* If there is data in the fifo's after transfer,		 * flush fifo's by reset.		 */		iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,			  I2C_REG_CTL(alg_data));		wait_reset(I2C_PNX_TIMEOUT, alg_data);	} else if (stat & mstatus_nai) {		iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,			  I2C_REG_CTL(alg_data));		wait_reset(I2C_PNX_TIMEOUT, alg_data);	}}/** * i2c_pnx_xfer - generic transfer entry point * @adap:		pointer to I2C adapter structure * @msgs:		array of messages * @num:		number of messages * * Initiates the transfer */static inti2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num){	struct i2c_msg *pmsg;	int rc = 0, completed = 0, i;	struct i2c_pnx_algo_data *alg_data = adap->algo_data;	u32 stat = ioread32(I2C_REG_STS(alg_data));	dev_dbg(&adap->dev, "%s(): entering: %d messages, stat = %04x.\n",		__FUNCTION__, num, ioread32(I2C_REG_STS(alg_data)));	bus_reset_if_active(adap);	/* Process transactions in a loop. */	for (i = 0; rc >= 0 && i < num; i++) {		u8 addr;		pmsg = &msgs[i];		addr = pmsg->addr;		if (pmsg->flags & I2C_M_TEN) {			dev_err(&adap->dev,				"%s: 10 bits addr not supported!\n",				adap->name);			rc = -EINVAL;			break;		}		alg_data->mif.buf = pmsg->buf;		alg_data->mif.len = pmsg->len;		alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ?			I2C_SMBUS_READ : I2C_SMBUS_WRITE;		alg_data->mif.ret = 0;		alg_data->last = (i == num - 1);		dev_dbg(&adap->dev, "%s(): mode %d, %d bytes\n", __FUNCTION__,			alg_data->mif.mode,			alg_data->mif.len);		i2c_pnx_arm_timer(adap);		/* initialize the completion var */		init_completion(&alg_data->mif.complete);		/* Enable master interrupt */		iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_afie |				mcntrl_naie | mcntrl_drmie,			  I2C_REG_CTL(alg_data));		/* Put start-code and slave-address on the bus. */		rc = i2c_pnx_start(addr, adap);		if (rc < 0)			break;		/* Wait for completion */		wait_for_completion(&alg_data->mif.complete);		if (!(rc = alg_data->mif.ret))			completed++;		dev_dbg(&adap->dev, "%s(): Complete, return code = %d.\n",			__FUNCTION__, rc);		/* Clear TDI and AFI bits in case they are set. */		if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_tdi) {			dev_dbg(&adap->dev,				"%s: TDI still set... clearing now.\n",			       adap->name);			iowrite32(stat, I2C_REG_STS(alg_data));		}		if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_afi) {			dev_dbg(&adap->dev,				"%s: AFI still set... clearing now.\n",			       adap->name);			iowrite32(stat, I2C_REG_STS(alg_data));		}	}	bus_reset_if_active(adap);	/* Cleanup to be sure... */	alg_data->mif.buf = NULL;	alg_data->mif.len = 0;	dev_dbg(&adap->dev, "%s(): exiting, stat = %x\n",		__FUNCTION__, ioread32(I2C_REG_STS(alg_data)));	if (completed != num)		return ((rc < 0) ? rc : -EREMOTEIO);	return num;}static u32 i2c_pnx_func(struct i2c_adapter *adapter){	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;}static struct i2c_algorithm pnx_algorithm = {	.master_xfer = i2c_pnx_xfer,	.functionality = i2c_pnx_func,};static int i2c_pnx_controller_suspend(struct platform_device *pdev,				      pm_message_t state){	struct i2c_pnx_data *i2c_pnx = platform_get_drvdata(pdev);	return i2c_pnx->suspend(pdev, state);}static int i2c_pnx_controller_resume(struct platform_device *pdev){	struct i2c_pnx_data *i2c_pnx = platform_get_drvdata(pdev);	return i2c_pnx->resume(pdev);}static int __devinit i2c_pnx_probe(struct platform_device *pdev){	unsigned long tmp;	int ret = 0;	struct i2c_pnx_algo_data *alg_data;	int freq_mhz;	struct i2c_pnx_data *i2c_pnx = pdev->dev.platform_data;	if (!i2c_pnx || !i2c_pnx->adapter) {		dev_err(&pdev->dev, "%s: no platform data supplied\n",		       __FUNCTION__);		ret = -EINVAL;		goto out;	}	platform_set_drvdata(pdev, i2c_pnx);	if (i2c_pnx->calculate_input_freq)		freq_mhz = i2c_pnx->calculate_input_freq(pdev);	else {		freq_mhz = PNX_DEFAULT_FREQ;		dev_info(&pdev->dev, "Setting bus frequency to default value: "		       "%d MHz", freq_mhz);	}	i2c_pnx->adapter->algo = &pnx_algorithm;	alg_data = i2c_pnx->adapter->algo_data;	init_timer(&alg_data->mif.timer);	alg_data->mif.timer.function = i2c_pnx_timeout;	alg_data->mif.timer.data = (unsigned long)i2c_pnx->adapter;	/* Register I/O resource */	if (!request_region(alg_data->base, I2C_PNX_REGION_SIZE, pdev->name)) {		dev_err(&pdev->dev,		       "I/O region 0x%08x for I2C already in use.\n",		       alg_data->base);		ret = -ENODEV;		goto out_drvdata;	}	if (!(alg_data->ioaddr =			(u32)ioremap(alg_data->base, I2C_PNX_REGION_SIZE))) {		dev_err(&pdev->dev, "Couldn't ioremap I2C I/O region\n");		ret = -ENOMEM;		goto out_release;	}	i2c_pnx->set_clock_run(pdev);	/*	 * Clock Divisor High This value is the number of system clocks	 * the serial clock (SCL) will be high.	 * For example, if the system clock period is 50 ns and the maximum	 * desired serial period is 10000 ns (100 kHz), then CLKHI would be	 * set to 0.5*(f_sys/f_i2c)-2=0.5*(20e6/100e3)-2=98. The actual value	 * programmed into CLKHI will vary from this slightly due to	 * variations in the output pad's rise and fall times as well as	 * the deglitching filter length.	 */	tmp = ((freq_mhz * 1000) / I2C_PNX_SPEED_KHZ) / 2 - 2;	iowrite32(tmp, I2C_REG_CKH(alg_data));	iowrite32(tmp, I2C_REG_CKL(alg_data));	iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data));	if (wait_reset(I2C_PNX_TIMEOUT, alg_data)) {		ret = -ENODEV;		goto out_unmap;	}	init_completion(&alg_data->mif.complete);	ret = request_irq(alg_data->irq, i2c_pnx_interrupt,			0, pdev->name, i2c_pnx->adapter);	if (ret)		goto out_clock;	/* Register this adapter with the I2C subsystem */	i2c_pnx->adapter->dev.parent = &pdev->dev;	ret = i2c_add_adapter(i2c_pnx->adapter);	if (ret < 0) {		dev_err(&pdev->dev, "I2C: Failed to add bus\n");		goto out_irq;	}	dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n",	       i2c_pnx->adapter->name, alg_data->base, alg_data->irq);	return 0;out_irq:	free_irq(alg_data->irq, alg_data);out_clock:	i2c_pnx->set_clock_stop(pdev);out_unmap:	iounmap((void *)alg_data->ioaddr);out_release:	release_region(alg_data->base, I2C_PNX_REGION_SIZE);out_drvdata:	platform_set_drvdata(pdev, NULL);out:	return ret;}static int __devexit i2c_pnx_remove(struct platform_device *pdev){	struct i2c_pnx_data *i2c_pnx = platform_get_drvdata(pdev);	struct i2c_adapter *adap = i2c_pnx->adapter;	struct i2c_pnx_algo_data *alg_data = adap->algo_data;	free_irq(alg_data->irq, alg_data);	i2c_del_adapter(adap);	i2c_pnx->set_clock_stop(pdev);	iounmap((void *)alg_data->ioaddr);	release_region(alg_data->base, I2C_PNX_REGION_SIZE);	platform_set_drvdata(pdev, NULL);	return 0;}static struct platform_driver i2c_pnx_driver = {	.driver = {		.name = "pnx-i2c",		.owner = THIS_MODULE,	},	.probe = i2c_pnx_probe,	.remove = __devexit_p(i2c_pnx_remove),	.suspend = i2c_pnx_controller_suspend,	.resume = i2c_pnx_controller_resume,};static int __init i2c_adap_pnx_init(void){	return platform_driver_register(&i2c_pnx_driver);}static void __exit i2c_adap_pnx_exit(void){	platform_driver_unregister(&i2c_pnx_driver);}MODULE_AUTHOR("Vitaly Wool, Dennis Kovalev <source@mvista.com>");MODULE_DESCRIPTION("I2C driver for Philips IP3204-based I2C busses");MODULE_LICENSE("GPL");/* We need to make sure I2C is initialized before USB */subsys_initcall(i2c_adap_pnx_init);module_exit(i2c_adap_pnx_exit);

⌨️ 快捷键说明

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