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

📄 i2c-s3c2410.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
		if (!(iicstat & S3C2410_IICSTAT_BUSBUSY))			return 0;		msleep(1);	}	dev_dbg(i2c->dev, "timeout: GPEDAT is %08x\n",		__raw_readl(S3C2410_GPEDAT));	return -ETIMEDOUT;}/* s3c24xx_i2c_doxfer * * this starts an i2c transfer*/static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num){	unsigned long timeout;	int ret;	ret = s3c24xx_i2c_set_master(i2c);	if (ret != 0) {		dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);		ret = -EAGAIN;		goto out;	}	spin_lock_irq(&i2c->lock);	i2c->msg     = msgs;	i2c->msg_num = num;	i2c->msg_ptr = 0;	i2c->msg_idx = 0;	i2c->state   = STATE_START;	s3c24xx_i2c_enable_irq(i2c);	s3c24xx_i2c_message_start(i2c, msgs);	spin_unlock_irq(&i2c->lock);		timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);	ret = i2c->msg_idx;	/* having these next two as dev_err() makes life very 	 * noisy when doing an i2cdetect */	if (timeout == 0)		dev_dbg(i2c->dev, "timeout\n");	else if (ret != num)		dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);	/* ensure the stop has been through the bus */	msleep(1); out:	return ret;}/* s3c24xx_i2c_xfer * * first port of call from the i2c bus code when an message needs * transferring across the i2c bus.*/static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,			struct i2c_msg *msgs, int num){	struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;	int retry;	int ret;	for (retry = 0; retry < adap->retries; retry++) {		ret = s3c24xx_i2c_doxfer(i2c, msgs, num);		if (ret != -EAGAIN)			return ret;		dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);		udelay(100);	}	return -EREMOTEIO;}/* declare our i2c functionality */static u32 s3c24xx_i2c_func(struct i2c_adapter *adap){	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;}/* i2c bus registration info */static struct i2c_algorithm s3c24xx_i2c_algorithm = {	.master_xfer		= s3c24xx_i2c_xfer,	.functionality		= s3c24xx_i2c_func,};static struct s3c24xx_i2c s3c24xx_i2c = {	.lock	= SPIN_LOCK_UNLOCKED,	.wait	= __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),	.adap	= {		.name			= "s3c2410-i2c",		.owner			= THIS_MODULE,		.algo			= &s3c24xx_i2c_algorithm,		.retries		= 2,		.class			= I2C_CLASS_HWMON,	},};/* s3c24xx_i2c_calcdivisor * * return the divisor settings for a given frequency*/static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,				   unsigned int *div1, unsigned int *divs){	unsigned int calc_divs = clkin / wanted;	unsigned int calc_div1;	if (calc_divs > (16*16))		calc_div1 = 512;	else		calc_div1 = 16;	calc_divs += calc_div1-1;	calc_divs /= calc_div1;	if (calc_divs == 0)		calc_divs = 1;	if (calc_divs > 17)		calc_divs = 17;	*divs = calc_divs;	*div1 = calc_div1;	return clkin / (calc_divs * calc_div1);}/* freq_acceptable * * test wether a frequency is within the acceptable range of error*/static inline int freq_acceptable(unsigned int freq, unsigned int wanted){	int diff = freq - wanted;	return (diff >= -2 && diff <= 2);}/* s3c24xx_i2c_getdivisor * * work out a divisor for the user requested frequency setting, * either by the requested frequency, or scanning the acceptable * range of frequencies until something is found*/static int s3c24xx_i2c_getdivisor(struct s3c24xx_i2c *i2c,				  struct s3c2410_platform_i2c *pdata,				  unsigned long *iicon,				  unsigned int *got){	unsigned long clkin = clk_get_rate(i2c->clk);		unsigned int divs, div1;	int freq;	int start, end;	clkin /= 1000;		/* clkin now in KHz */     	dev_dbg(i2c->dev,  "pdata %p, freq %lu %lu..%lu\n",		 pdata, pdata->bus_freq, pdata->min_freq, pdata->max_freq);	if (pdata->bus_freq != 0) {		freq = s3c24xx_i2c_calcdivisor(clkin, pdata->bus_freq/1000,					       &div1, &divs);		if (freq_acceptable(freq, pdata->bus_freq/1000))			goto found;	}	/* ok, we may have to search for something suitable... */	start = (pdata->max_freq == 0) ? pdata->bus_freq : pdata->max_freq;	end = pdata->min_freq;	start /= 1000;	end /= 1000;	/* search loop... */	for (; start > end; start--) {		freq = s3c24xx_i2c_calcdivisor(clkin, start, &div1, &divs);		if (freq_acceptable(freq, start))			goto found;	}	/* cannot find frequency spec */	return -EINVAL; found:	*got = freq;	*iicon |= (divs-1);	*iicon |= (div1 == 512) ? S3C2410_IICCON_TXDIV_512 : 0;	return 0;}/* s3c24xx_i2c_init * * initialise the controller, set the IO lines and frequency */static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c){	unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;	struct s3c2410_platform_i2c *pdata;	unsigned int freq;	/* get the plafrom data */	pdata = s3c24xx_i2c_get_platformdata(i2c->adap.dev.parent);	/* inititalise the gpio */	s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_IICSDA);	s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_IICSCL);	/* write slave address */		writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);	dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);	/* we need to work out the divisors for the clock... */	if (s3c24xx_i2c_getdivisor(i2c, pdata, &iicon, &freq) != 0) {		dev_err(i2c->dev, "cannot meet bus frequency required\n");		return -EINVAL;	}	/* todo - check that the i2c lines aren't being dragged anywhere */	dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);	dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);		writel(iicon, i2c->regs + S3C2410_IICCON);	/* check for s3c2440 i2c controller  */	if (s3c24xx_i2c_is2440(i2c)) {		dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);		writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC);	}	return 0;}static void s3c24xx_i2c_free(struct s3c24xx_i2c *i2c){	if (i2c->clk != NULL && !IS_ERR(i2c->clk)) {		clk_disable(i2c->clk);		clk_unuse(i2c->clk);		clk_put(i2c->clk);		i2c->clk = NULL;	}	if (i2c->regs != NULL) {		iounmap(i2c->regs);		i2c->regs = NULL;	}	if (i2c->ioarea != NULL) {		release_resource(i2c->ioarea);		kfree(i2c->ioarea);		i2c->ioarea = NULL;	}}/* s3c24xx_i2c_probe * * called by the bus driver when a suitable device is found*/static int s3c24xx_i2c_probe(struct platform_device *pdev){	struct s3c24xx_i2c *i2c = &s3c24xx_i2c;	struct resource *res;	int ret;	/* find the clock and enable it */	i2c->dev = &pdev->dev;	i2c->clk = clk_get(&pdev->dev, "i2c");	if (IS_ERR(i2c->clk)) {		dev_err(&pdev->dev, "cannot get clock\n");		ret = -ENOENT;		goto out;	}	dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);	clk_use(i2c->clk);	clk_enable(i2c->clk);	/* map the registers */	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);	if (res == NULL) {		dev_err(&pdev->dev, "cannot find IO resource\n");		ret = -ENOENT;		goto out;	}	i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,					 pdev->name);	if (i2c->ioarea == NULL) {		dev_err(&pdev->dev, "cannot request IO\n");		ret = -ENXIO;		goto out;	}	i2c->regs = ioremap(res->start, (res->end-res->start)+1);	if (i2c->regs == NULL) {		dev_err(&pdev->dev, "cannot map IO\n");		ret = -ENXIO;		goto out;	}	dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res);	/* setup info block for the i2c core */	i2c->adap.algo_data = i2c;	i2c->adap.dev.parent = &pdev->dev;	/* initialise the i2c controller */	ret = s3c24xx_i2c_init(i2c);	if (ret != 0)		goto out;	/* find the IRQ for this unit (note, this relies on the init call to	 * ensure no current IRQs pending 	 */	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);	if (res == NULL) {		dev_err(&pdev->dev, "cannot find IRQ\n");		ret = -ENOENT;		goto out;	}	ret = request_irq(res->start, s3c24xx_i2c_irq, SA_INTERRUPT,			  pdev->name, i2c);	if (ret != 0) {		dev_err(&pdev->dev, "cannot claim IRQ\n");		goto out;	}	i2c->irq = res;			dev_dbg(&pdev->dev, "irq resource %p (%ld)\n", res, res->start);	ret = i2c_add_adapter(&i2c->adap);	if (ret < 0) {		dev_err(&pdev->dev, "failed to add bus to i2c core\n");		goto out;	}	platform_set_drvdata(pdev, i2c);	dev_info(&pdev->dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id); out:	if (ret < 0)		s3c24xx_i2c_free(i2c);	return ret;}/* s3c24xx_i2c_remove * * called when device is removed from the bus*/static int s3c24xx_i2c_remove(struct platform_device *pdev){	struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);		if (i2c != NULL) {		s3c24xx_i2c_free(i2c);		platform_set_drvdata(pdev, NULL);	}	return 0;}#ifdef CONFIG_PMstatic int s3c24xx_i2c_resume(struct platform_device *dev){	struct s3c24xx_i2c *i2c = platform_get_drvdata(dev);	if (i2c != NULL)		s3c24xx_i2c_init(i2c);	return 0;}#else#define s3c24xx_i2c_resume NULL#endif/* device driver for platform bus bits */static struct platform_driver s3c2410_i2c_driver = {	.probe		= s3c24xx_i2c_probe,	.remove		= s3c24xx_i2c_remove,	.resume		= s3c24xx_i2c_resume,	.driver		= {		.owner	= THIS_MODULE,		.name	= "s3c2410-i2c",	},};static struct platform_driver s3c2440_i2c_driver = {	.probe		= s3c24xx_i2c_probe,	.remove		= s3c24xx_i2c_remove,	.resume		= s3c24xx_i2c_resume,	.driver		= {		.owner	= THIS_MODULE,		.name	= "s3c2440-i2c",	},};static int __init i2c_adap_s3c_init(void){	int ret;	ret = platform_driver_register(&s3c2410_i2c_driver);	if (ret == 0) {		ret = platform_driver_register(&s3c2440_i2c_driver);		if (ret)			platform_driver_unregister(&s3c2410_i2c_driver);	}	return ret;}static void __exit i2c_adap_s3c_exit(void){	platform_driver_unregister(&s3c2410_i2c_driver);	platform_driver_unregister(&s3c2440_i2c_driver);}module_init(i2c_adap_s3c_init);module_exit(i2c_adap_s3c_exit);MODULE_DESCRIPTION("S3C24XX I2C Bus driver");MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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