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

📄 i2c_driver.txt

📁 嵌入式arm-linux 2.6内核 s3c2410 的i2c总线驱动程序
💻 TXT
📖 第 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_PM 
static 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 + -