i2c驱动之i2c-s3c2410.c


#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/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>

#include <asm/irq.h>
#include <asm/io.h>

#include <plat/regs-iic.h>
#include <plat/iic.h>

/* i2c controller state */
//i2c控制器状态
enum s3c24xx_i2c_state {
 STATE_IDLE,
 STATE_START,
 STATE_READ,
 STATE_WRITE,
 STATE_STOP
};

struct s3c24xx_i2c {
 spinlock_t  lock;
 /*
 在数据发送函数s3c24xx_i2c_doxfer中wait_event_timeout,
 在数据发送完成函数s3c24xx_i2c_master_complete中wake_up
 */
 wait_queue_head_t wait;
 unsigned int  suspended:1;

 struct i2c_msg  *msg;//管理收发数据的结构体
 unsigned int  msg_num;//待收发msg的数目
 unsigned int  msg_idx;//当前正在处理的msg在msg_num个msg中的序号
 //每个msg都管理一段内存,msg_ptr是这段内存空间的偏移。
 unsigned int  msg_ptr;
//在数据写入IICDS后的等待时间,在数据写入前SCL保持低电平,写入后
//释放,这个状态的建立需要一定的时间。
 unsigned int  tx_setup;
 unsigned int  irq;

 enum s3c24xx_i2c_state state;
 //保存上一次通过clk_get_rate(i2c->clk);获取的频率。
 //当本次获取的频率与clkrate不等时说明系统时钟改变。
 unsigned long  clkrate;

 void __iomem  *regs;
 struct clk  *clk;
 struct device  *dev;
 struct resource  *ioarea;//
 struct i2c_adapter adap;//适配器结构体,这整个结构体都是对它的包装。

#ifdef CONFIG_CPU_FREQ
 struct notifier_block freq_transition;//通知链
#endif
};

/* default platform data removed, dev should always carry data. */

/* s3c24xx_i2c_is2440()
 *
 * return true is this is an s3c2440
*/
//如果是s3c2440则返回1
static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c)
{
 struct platform_device *pdev = to_platform_device(i2c->dev);

 return !strcmp(pdev->name, "s3c2440-i2c");
}

/* s3c24xx_i2c_master_complete
 *
 * complete the message and wake up the caller, using the given return code,
 * or zero to mean ok.
*/

static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret)
{
 dev_dbg(i2c->dev, "master_complete %d\n", ret);

 i2c->msg_ptr = 0;
 i2c->msg = NULL;
 i2c->msg_idx++;
 i2c->msg_num = 0;
 if (ret)
  i2c->msg_idx = ret;
//msg_num个msg都处理完则唤醒等待队列
 wake_up(&i2c->wait);
}
//应答禁能
static inline void s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c)
{
 unsigned long tmp;

 tmp = readl(i2c->regs + S3C2410_IICCON);
 writel(tmp & ~S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON);
}
//应答使能
static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c)
{
 unsigned long tmp;

 tmp = readl(i2c->regs + S3C2410_IICCON);
 writel(tmp | S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON);
}

/* irq enable/disable functions */
//中断禁止
static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c)
{
 unsigned long tmp;

 tmp = readl(i2c->regs + S3C2410_IICCON);
 writel(tmp & ~S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);
}
//中断使能
static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c)
{
 unsigned long tmp;

 tmp = readl(i2c->regs + S3C2410_IICCON);
 writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);
}


/* s3c24xx_i2c_message_start
 *
 * put the start of a message onto the bus
*/
//写入从器件地址,并发送起始信号
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
          struct i2c_msg *msg)
{
 unsigned int addr = (msg->addr & 0x7f) << 1;//最低位为读写标志
 unsigned long stat;
 unsigned long iiccon;

 stat = 0;
 stat |=  S3C2410_IICSTAT_TXRXEN;//数据收发使能

 if (msg->flags & I2C_M_RD) {
  stat |= S3C2410_IICSTAT_MASTER_RX;
  addr |= 1;//主机读
 } else
  stat |= S3C2410_IICSTAT_MASTER_TX;//主机写

 if (msg->flags & I2C_M_REV_DIR_ADDR)
  addr ^= 1;

 /* todo - check for wether ack wanted or not */
 s3c24xx_i2c_enable_ack(i2c);

 iiccon = readl(i2c->regs + S3C2410_IICCON);
 writel(stat, i2c->regs + S3C2410_IICSTAT);

 dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
 writeb(addr, i2c->regs + S3C2410_IICDS);

 /* delay here to ensure the data byte has gotten onto the bus
  * before the transaction is started */

 ndelay(i2c->tx_setup);//延时等待SCL被释放

 dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
 writel(iiccon, i2c->regs + S3C2410_IICCON);

 stat |= S3C2410_IICSTAT_START;
 writel(stat, i2c->regs + S3C2410_IICSTAT);//发送起始信号
}

static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)
{
 unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);

 dev_dbg(i2c->dev, "STOP\n");

 /* stop the transfer */
 iicstat &= ~S3C2410_IICSTAT_START;
 writel(iicstat, i2c->regs + S3C2410_IICSTAT);

 i2c->state = STATE_STOP;
//数据发送结束,唤醒等待队列
 s3c24xx_i2c_master_complete(i2c, ret);
 s3c24xx_i2c_disable_irq(i2c);
}

/* helper functions to determine the current state in the set of
 * messages we are sending */

/* is_lastmsg()
 *
 * returns TRUE if the current message is the last in the set
*/
//msg_idx为当前msg在msg_num个发送msg中的偏移,
static inline int is_lastmsg(struct s3c24xx_i2c *i2c)
{
 return i2c->msg_idx >= (i2c->msg_num - 1);
}

/* is_msglast
 *
 * returns TRUE if we this is the last byte in the current message
*/
//msg_ptr为在msg中的len个数据中的偏移
static inline int is_msglast(struct s3c24xx_i2c *i2c)
{
 return i2c->msg_ptr == i2c->msg->len-1;
}

/* is_msgend
 *
 * returns TRUE if we reached the end of the current message
*/
//当前msg中的数据处理完
static inline int is_msgend(struct s3c24xx_i2c *i2c)
{
 return i2c->msg_ptr >= i2c->msg->len;
}

/* i2s_s3c_irq_nextbyte
 *
 * process an interrupt and work out what to do
 */

static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
{
 unsigned long tmp;
 unsigned char byte;
 int ret = 0;

 switch (i2c->state) {

 case STATE_IDLE:
  dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__);
  goto out;
  break;

 case STATE_STOP:
  dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__);
  s3c24xx_i2c_disable_irq(i2c);
  goto out_ack;//如果当前为停止状态则清除中断挂起条件

 case STATE_START://开始状态在开始数据传输函数s3c24xx_i2c_doxfer中设置
  /* last thing we did was send a start condition on the
   * bus, or started a new i2c message
   */

  if (iicstat & S3C2410_IICSTAT_LASTBIT &&
      !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
   /* ack was not received... */
      //没有收到应答信号则停止数据传输
   dev_dbg(i2c->dev, "ack was not received\n");
   s3c24xx_i2c_stop(i2c, -ENXIO);
   goto out_ack;
  }
//其实信号成功发送后决定接下来要做的事情
  if (i2c->msg->flags & I2C_M_RD)
   i2c->state = STATE_READ;
  else
   i2c->state = STATE_WRITE;

  /* terminate the transfer if there is nothing to do
   * as this is used by the i2c probe to find devices. */
//如果当前msg是队列中的最后一个而且没有要发送的数据则停止
  if (is_lastmsg(i2c) && i2c->msg->len == 0) {
   s3c24xx_i2c_stop(i2c, 0);
   goto out_ack;
  }
//当前中断是起始信号成功发送,下一次中断则是进行数据接收,
//为接收数据准备可用的缓存
  if (i2c->state == STATE_READ)
   goto prepare_read;

  /* fall through to the write state, as we will need to
   * send a byte as well */

 case STATE_WRITE:
  /* we are writing data to the device... check for the
   * end of the message, and if so, work out what to do
   */

  if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
   if (iicstat & S3C2410_IICSTAT_LASTBIT) {
    dev_dbg(i2c->dev, "WRITE: No Ack\n");

    s3c24xx_i2c_stop(i2c, -ECONNREFUSED);
    goto out_ack;
   }
  }

 retry_write:

  if (!is_msgend(i2c)) {
   byte = i2c->msg->buf[i2c->msg_ptr++];
   //i2c->msg_ptr++表明它总是指向第一个未被发送的数据
   writeb(byte, i2c->regs + S3C2410_IICDS);
//如果当前msg缓存中的数据没有全部发送则继续发送
   /* delay after writing the byte to allow the
    * data setup time on the bus, as writing the
    * data to the register causes the first bit
    * to appear on SDA, and SCL will change as
    * soon as the interrupt is acknowledged */

   ndelay(i2c->tx_setup);

  } else if (!is_lastmsg(i2c)) {
   /* we need to go to the next i2c message */
//如果当前msg中的数据发送完,而且msg不是msg队列中的最后一个
//则转向下一个待发送的msg
   dev_dbg(i2c->dev, "WRITE: Next Message\n");

   i2c->msg_ptr = 0;
   i2c->msg_idx++;
   i2c->msg++;

   /* check to see if we need to do another message */
   if (i2c->msg->flags & I2C_M_NOSTART) {

    if (i2c->msg->flags & I2C_M_RD) {
     /* cannot do this, the controller
      * forces us to send a new START
      * when we change direction */
//如果这一个msg处理的不是写而是读,数据传输方向变化了,则发送停止信号,
//这个停止信号时异常停止,将导致数据的再次发送,即改变数据方向进行读取操作。
//在函数s3c24xx_i2c_xfer中可以看出,adap->retries决定了数据发送不成功
//可再次发送的次数。
     s3c24xx_i2c_stop(i2c, -EINVAL);
    }
//I2C_M_NOSTART如果是连续写则retry_write
    goto retry_write;
   } else {
    /* send the new start */
//如果数据不是连续写则每次写操作都要发送起始信号,写地址从器件地址。
    s3c24xx_i2c_message_start(i2c, i2c->msg);
    i2c->state = STATE_START;
   }

  } else {
   /* send stop */
//如果当前msg中的数据发送完而且是msg队列中的最后一个msg则停止
   s3c24xx_i2c_stop(i2c, 0);
  }
  break;

 case STATE_READ:
  /* we have a byte of data in the data register, do
   * something with it, and then work out wether we are
   * going to do any more read/write
   */

  byte = readb(i2c->regs + S3C2410_IICDS);
  i2c->msg->buf[i2c->msg_ptr++] = byte;
//i2c->msg_ptr++表明它总是指向缓存空间的下一个空的位置
 prepare_read://每次数据读取都要为下一次读取做准备
  if (is_msglast(i2c)) {
   /* last byte of buffer */
//如果当前msg只剩下最后一个字节空间,则还可以进行一次数据读取,
//如果当前msg是msg队列中最后一个,则整个数据操作还能进行最后一次,
//在这最后一次读取数据后不再发送应答信号。
   if (is_lastmsg(i2c))
    s3c24xx_i2c_disable_ack(i2c);

  } else if (is_msgend(i2c)) {
   /* ok, we've read the entire buffer, see if there
    * is anything else we need to do */

   if (is_lastmsg(i2c)) {
    /* last message, send stop and complete */
    dev_dbg(i2c->dev, "READ: Send Stop\n");
//如果当前msg中的缓存已用完,而且当前msg是msg队列中的最后一个则stop
    s3c24xx_i2c_stop(i2c, 0);
   } else {
    /* go to the next transfer */
    dev_dbg(i2c->dev, "READ: Next Transfer\n");
//如果当前msg的缓存已耗尽,但msg不是msg队列的最后一个则转向下一个
    i2c->msg_ptr = 0;
    i2c->msg_idx++;
    i2c->msg++;
   }
  }

  break;
 }

 /* acknowlegde the IRQ and get back on with the work */

 out_ack:
 tmp = readl(i2c->regs + S3C2410_IICCON);
 tmp &= ~S3C2410_IICCON_IRQPEND;
 writel(tmp, i2c->regs + S3C2410_IICCON);//清除中断挂起条件
 out:
 return ret;
}

/* s3c24xx_i2c_irq
 *
 * top level IRQ servicing routine
*/
//中断处理函数,从这个中断处理函数可以看出这个适配器不能工作在从机模式
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
{
 struct s3c24xx_i2c *i2c = dev_id;
 unsigned long status;
 unsigned long tmp;

 status = readl(i2c->regs + S3C2410_IICSTAT);

 if (status & S3C2410_IICSTAT_ARBITR) {
  /* deal with arbitration loss */
  dev_err(i2c->dev, "deal with arbitration loss\n");
 }

 if (i2c->state == STATE_IDLE) {
  dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");

  tmp = readl(i2c->regs + S3C2410_IICCON);
  tmp &= ~S3C2410_IICCON_IRQPEND;
  writel(tmp, i2c->regs +  S3C2410_IICCON);//清除中断挂起条件
  goto out;
 }

 /* pretty much this leaves us with the fact that we've
  * transmitted or received whatever byte we last sent */

 i2s_s3c_irq_nextbyte(i2c, status);//up

 out:
 return IRQ_HANDLED;
}


/* s3c24xx_i2c_set_master
 *
 * get the i2c bus for a master transaction
*/

static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
{
 unsigned long iicstat;
 int timeout = 400;

 while (timeout-- > 0) {
  iicstat = readl(i2c->regs + S3C2410_IICSTAT);

  if (!(iicstat & S3C2410_IICSTAT_BUSBUSY))
   return 0;
//等待SCL被释放,只有在此种状态下才可以设自己为主机状态
  msleep(1);
 }

 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;
//在函数s3c24xx_i2c_probe中是用kzalloc分配的i2c的内存空间
//初始状态下i2c->suspended是为0的。不知道这样理解是否正确?
 if (i2c->suspended)//如果处于挂起状态则不能进行数据传输
  return -EIO;

 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);
//获取待处理的msg队列
 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);
//如果数据处理异常结束,它将返回错误标识。s3c24xx_i2c_stop
 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.
*/
//对函数s3c24xx_i2c_doxfer的包装
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);
//如果数据发送不成功可再次发送。
//比如在msg队列中有些是读取操作有些是数据写操作。
  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)
{//申明i2c支持的功能
 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}

/* i2c bus registration info */
//i2c_algorithm适配器的操作函数
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
 .master_xfer  = s3c24xx_i2c_xfer,
 .functionality  = s3c24xx_i2c_func,
};

/* s3c24xx_i2c_calcdivisor
 *
 * return the divisor settings for a given frequency
*/
//IIC能进行两处分频,一处是16倍和512倍,另一处是0~16分频
//clkin存放IICCLK,wanted为想要设置的频率。
//经计算后div1放第一处分频值16或512,divs放第二处分频值,
//函数的返回值是经分频后实际的频率值
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);
}

/* s3c24xx_i2c_clockrate
 *
 * 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_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
{
 struct s3c2410_platform_i2c *pdata = i2c->dev->platform_data;
 unsigned long clkin = clk_get_rate(i2c->clk);
 unsigned int divs, div1;
 unsigned long target_frequency;
 u32 iiccon;
 int freq;

 i2c->clkrate = clkin;// 存放IICCLK
 clkin /= 1000;  /* clkin now in KHz */

 dev_dbg(i2c->dev, "pdata desired frequency %lu\n", pdata->frequency);
//pdata->frequency在移植时可在文件dev-i2c.c中设置
 target_frequency = pdata->frequency ? pdata->frequency : 100000;
//
 target_frequency /= 1000; /* Target frequency now in KHz */
//根据IICCLK(clkin)和想要设置的频率(target_frequency),计算出
//第一次分频和第二次分频的值,返回实际得到的频率值
 freq = s3c24xx_i2c_calcdivisor(clkin, target_frequency, &div1, &divs);

 if (freq > target_frequency) {
  dev_err(i2c->dev,
   "Unable to achieve desired frequency %luKHz." \
   " Lowest achievable %dKHz\n", target_frequency, freq);
  return -EINVAL;
 }

 *got = freq;

 iiccon = readl(i2c->regs + S3C2410_IICCON);
 iiccon &= ~(S3C2410_IICCON_SCALEMASK | S3C2410_IICCON_TXDIV_512);
 iiccon |= (divs-1);

 if (div1 == 512)
  iiccon |= S3C2410_IICCON_TXDIV_512;
//写入分频值
 writel(iiccon, i2c->regs + S3C2410_IICCON);

 if (s3c24xx_i2c_is2440(i2c)) {
  unsigned long sda_delay;

  if (pdata->sda_delay) {
   sda_delay = (freq / 1000) * pdata->sda_delay;
   sda_delay /= 1000000;
   sda_delay = DIV_ROUND_UP(sda_delay, 5);
   if (sda_delay > 3)
    sda_delay = 3;//不是很明白SDA线路延时长度选择是什么意思
   sda_delay |= S3C2410_IICLC_FILTER_ON;
  } else
   sda_delay = 0;

  dev_dbg(i2c->dev, "IICLC=%08lx\n", sda_delay);
  writel(sda_delay, i2c->regs + S3C2440_IICLC);
 }

 return 0;
}

#ifdef CONFIG_CPU_FREQ

#define freq_to_i2c(_n) container_of(_n, struct s3c24xx_i2c, freq_transition)
//通知链中事件发生时的回调函数
static int s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb,
       unsigned long val, void *data)
{
 struct s3c24xx_i2c *i2c = freq_to_i2c(nb);
 unsigned long flags;
 unsigned int got;
 int delta_f;
 int ret;

 delta_f = clk_get_rate(i2c->clk) - i2c->clkrate;

 /* if we're post-change and the input clock has slowed down
  * or at pre-change and the clock is about to speed up, then
  * adjust our clock rate. <0 is slow, >0 speedup.
  */

 if ((val == CPUFREQ_POSTCHANGE && delta_f < 0) ||
     (val == CPUFREQ_PRECHANGE && delta_f > 0)) {
  spin_lock_irqsave(&i2c->lock, flags);
  ret = s3c24xx_i2c_clockrate(i2c, &got);//重新计算并设置频率
  spin_unlock_irqrestore(&i2c->lock, flags);

  if (ret < 0)
   dev_err(i2c->dev, "cannot find frequency\n");
  else
   dev_info(i2c->dev, "setting freq %d\n", got);
 }

 return 0;
}
//将节点i2c->freq_transition加入通知链
static inline int s3c24xx_i2c_register_cpufreq(struct s3c24xx_i2c *i2c)
{
 i2c->freq_transition.notifier_call = s3c24xx_i2c_cpufreq_transition;

 return cpufreq_register_notifier(&i2c->freq_transition,
      CPUFREQ_TRANSITION_NOTIFIER);
}

static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c)
{
 cpufreq_unregister_notifier(&i2c->freq_transition,
        CPUFREQ_TRANSITION_NOTIFIER);
}

#else
static inline int s3c24xx_i2c_register_cpufreq(struct s3c24xx_i2c *i2c)
{
 return 0;
}

static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c)
{
}
#endif

/* 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 = i2c->dev->platform_data;

 /* inititalise the gpio */

 if (pdata->cfg_gpio)
  pdata->cfg_gpio(to_platform_device(i2c->dev));

 /* write slave address */
//写入本机的从器件地址
 writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);

 dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);

 writel(iicon, i2c->regs + S3C2410_IICCON);

 /* we need to work out the divisors for the clock... */
//设置频率
 if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
  writel(0, i2c->regs + S3C2410_IICCON);
  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);

 /* check for s3c2440 i2c controller  */

 if (s3c24xx_i2c_is2440(i2c))//如果是2440则。。。不太明白
  writel(0x0, i2c->regs + S3C2440_IICLC);

 return 0;
}

/* 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;
 struct s3c2410_platform_i2c *pdata;
 struct resource *res;
 int ret;
//这个私有成员是器件移植的关键
 pdata = pdev->dev.platform_data;
 if (!pdata) {
  dev_err(&pdev->dev, "no platform data\n");
  return -EINVAL;
 }
//为结构体i2c分配内存空间并清零。
 i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
 if (!i2c) {
  dev_err(&pdev->dev, "no memory for state\n");
  return -ENOMEM;
 }
//这整个驱动程序就是在向内核加一个适配器。
 strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
 i2c->adap.owner   = THIS_MODULE;
 i2c->adap.algo    = &s3c24xx_i2c_algorithm;//适配器操作方法
 i2c->adap.retries = 2;//数据传输异常打断后,重复尝试的次数
 i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
 i2c->tx_setup     = 50;//数据写入IICDS后延时时间

 spin_lock_init(&i2c->lock);
 init_waitqueue_head(&i2c->wait);//初始化等待队列头

 /* 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 err_noclk;
 }

 dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);

 clk_enable(i2c->clk);//使能时钟

 /* map the registers */
//获取I/O资源
 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 if (res == NULL) {
  dev_err(&pdev->dev, "cannot find IO resource\n");
  ret = -ENOENT;
  goto err_clk;
 }
//申请IO内存
 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 err_clk;
 }
//IO映射
 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 err_ioarea;
 }

 dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
  i2c->regs, i2c->ioarea, res);

 /* setup info block for the i2c core */
//将i2c结构体设为adap的私有数据成员
 i2c->adap.algo_data = i2c;
 i2c->adap.dev.parent = &pdev->dev;

 /* initialise the i2c controller */

 ret = s3c24xx_i2c_init(i2c);//如上
 if (ret != 0)
  goto err_iomap;

 /* find the IRQ for this unit (note, this relies on the init call to
  * ensure no current IRQs pending
  */

 i2c->irq = ret = platform_get_irq(pdev, 0);
 if (ret <= 0) {
  dev_err(&pdev->dev, "cannot find IRQ\n");
  goto err_iomap;
 }
//申请一个中断并关联它的处理函数,IRQF_DISABLED该中断不被共享(我认为)
 ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
     dev_name(&pdev->dev), i2c);

 if (ret != 0) {
  dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
  goto err_iomap;
 }
//将通知节点注册到通知链。
 ret = s3c24xx_i2c_register_cpufreq(i2c);
 if (ret < 0) {
  dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
  goto err_irq;
 }

 /* Note, previous versions of the driver used i2c_add_adapter()
  * to add the bus at any number. We now pass the bus number via
  * the platform data, so if unset it will now default to always
  * being bus 0.
  */

 i2c->adap.nr = pdata->bus_num;//设置总线号
//添加一个适配器设备
 ret = i2c_add_numbered_adapter(&i2c->adap);
 if (ret < 0) {
  dev_err(&pdev->dev, "failed to add bus to i2c core\n");
  goto err_cpufreq;
 }
//将i2c设为pdev的drvdata。
 platform_set_drvdata(pdev, i2c);

 dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
 return 0;

 err_cpufreq:
 s3c24xx_i2c_deregister_cpufreq(i2c);

 err_irq:
 free_irq(i2c->irq, i2c);

 err_iomap:
 iounmap(i2c->regs);

 err_ioarea:
 release_resource(i2c->ioarea);
 kfree(i2c->ioarea);

 err_clk:
 clk_disable(i2c->clk);
 clk_put(i2c->clk);

 err_noclk:
 kfree(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);

 s3c24xx_i2c_deregister_cpufreq(i2c);

 i2c_del_adapter(&i2c->adap);
 free_irq(i2c->irq, i2c);

 clk_disable(i2c->clk);
 clk_put(i2c->clk);

 iounmap(i2c->regs);

 release_resource(i2c->ioarea);
 kfree(i2c->ioarea);
 kfree(i2c);

 return 0;
}

#ifdef CONFIG_PM
static int s3c24xx_i2c_suspend_late(struct platform_device *dev,
        pm_message_t msg)
{
 struct s3c24xx_i2c *i2c = platform_get_drvdata(dev);
 i2c->suspended = 1;
 return 0;
}

static int s3c24xx_i2c_resume(struct platform_device *dev)
{
 struct s3c24xx_i2c *i2c = platform_get_drvdata(dev);

 i2c->suspended = 0;
 s3c24xx_i2c_init(i2c);

 return 0;
}

#else
#define s3c24xx_i2c_suspend_late NULL
#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,
 .suspend_late = s3c24xx_i2c_suspend_late,
 .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,
 .suspend_late = s3c24xx_i2c_suspend_late,
 .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;
}
subsys_initcall(i2c_adap_s3c_init);

static void __exit i2c_adap_s3c_exit(void)
{
 platform_driver_unregister(&s3c2410_i2c_driver);
 platform_driver_unregister(&s3c2440_i2c_driver);
}
module_exit(i2c_adap_s3c_exit);

MODULE_DESCRIPTION("S3C24XX I2C Bus driver");
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:s3c2410-i2c");
MODULE_ALIAS("platform:s3c2440-i2c");

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
接口电路: 首先,将74HC595与S3C2410单片机连接。将S3C2410的P1口接到74HC595的SER引脚上,将S3C2410的P2口接到74HC595的SRCLK引脚上,将S3C2410的P3口接到74HC595的RCLK引脚上。然后将74HC595的Q0-Q7引脚接到数码管的A-G、DP引脚上,数码管的COM0-COM3引脚接到S3C2410的P4-P7口上。 显示程序: 首先定义数码管的字符集,比如0-F,DP等字符的编码。然后定义一个数组,存储要显示的数字的编码。使用S3C2410的GPIO口控制74HC595的引脚,将编码依次输出到74HC595,实现数码管的显示。在不同的时间段内轮流输出不同的数码管,实现多位数码管的显示。具体代码如下(仅供参考): ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #define GPIO_BASE 0x56000000 #define GPIO_SIZE 0x1000 #define P1CON 0x7F008000 // P1口控制寄存器 #define P2CON 0x7F008004 // P2口控制寄存器 #define P3CON 0x7F008008 // P3口控制寄存器 #define P4CON 0x7F00800C // P4口控制寄存器 #define P5CON 0x7F008010 // P5口控制寄存器 #define P6CON 0x7F008014 // P6口控制寄存器 #define P7CON 0x7F008018 // P7口控制寄存器 #define HIGH(x) (1 << (x)) // 置高 #define LOW(x) (~(1 << (x))) // 置低 // 数码管的字符集 const unsigned char num_codes[] = { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71 }; // 数字数组 unsigned char nums[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; // GPIO控制结构体 struct gpio_reg { unsigned long con[4]; // 控制寄存器 unsigned long dat; // 数据寄存器 unsigned long pad[3]; // 填充 }; // 映射GPIO寄存器到内存 struct gpio_reg *gpio = NULL; // 初始化GPIO void init_gpio() { int fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd < 0) { fprintf(stderr, "open /dev/mem failed!\n"); exit(-1); } gpio = (struct gpio_reg *) mmap(NULL, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO_BASE); if (gpio == MAP_FAILED) { fprintf(stderr, "mmap failed!\n"); exit(-1); } close(fd); } // GPIO口置高或置低 void gpio_set(int pin, int value) { if (value == 1) { gpio->dat |= (1 << pin); } else { gpio->dat &= ~(1 << pin); } } // 数码管显示 void display(unsigned char *nums, int num_cnt, int delay_time) { int i, j; unsigned char code; while (1) { for (i = 0; i < num_cnt; i++) { // 输出编码 for (j = 0; j < 8; j++) { code = num_codes[nums[i]] & (1 << j); if (code == 0) { gpio_set(j, 0); } else { gpio_set(j, 1); } } // 选中数码管 gpio->dat = LOW(4); gpio_set(i, 1); // 延时 usleep(delay_time); } } } int main() { init_gpio(); // 设置P1-P7口为输出模式 gpio->con[1] &= HIGH(0) & HIGH(1) & HIGH(2) & HIGH(3) & HIGH(4) & HIGH(5) & HIGH(6); gpio->con[2] &= HIGH(0) & HIGH(1) & HIGH(2) & HIGH(3) & HIGH(4) & HIGH(5) & HIGH(6); gpio->con[3] &= HIGH(0) & HIGH(1) & HIGH(2) & HIGH(3); // 数码管显示 display(nums, sizeof(nums) / sizeof(nums[0]), 1000); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值