Linux3.5—IIC学习分析

I2C控制器的设备对象内核已经实现并关联到platform总线。

I2C控制器的驱动对象内核已经实现。

看mach-tiny4412.h

/plat-samsung/目录下

/drivers/i2c/   看 *.o 文件

看i2c-s3c2410.c   从下往上看。

.id_table 

匹配成功后看 probe函数:

一个I2C控制器对应一个struct s3c24xx_i2c结构体对象:

  struct s3c24xx_i2c *i2c; 

struct s3c24xx_i2c {
        wait_queue_head_t       wait;
        unsigned int            quirks;
        unsigned int            suspended:1;

        struct i2c_msg          *msg;       //IIC要传输的数据,
        unsigned int            msg_num;     //数组元素格式
        unsigned int            msg_idx;
        unsigned int            msg_ptr;

        unsigned int            tx_setup;
        unsigned int            irq;        //中断号

        enum s3c24xx_i2c_state  state;
        unsigned long           clkrate;

        void __iomem            *regs;      //通过platform_get_resource拿到物理基地址,映射完后赋值
        struct clk              *clk;
        struct device           *dev;
        struct resource         *ioarea;
        struct i2c_adapter      adap;      //读写数据的算法

        struct s3c2410_platform_i2c     *pdata;
        int                     gpios[2];
#ifdef CONFIG_CPU_FREQ
        struct notifier_block   freq_transition;
#endif
};
struct i2c_msg { 
        __u16 addr;     /* slave address                        */
        __u16 flags;
#define I2C_M_TEN               0x0010  /* this is a ten bit chip address */
#define I2C_M_RD                0x0001  /* read data, from slave to master */
#define I2C_M_NOSTART           0x4000  /* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR      0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK        0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK         0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN          0x0400  /* length will be first received byte */
        __u16 len;              /* msg length                           */
        __u8 *buf;              /* pointer to msg data                  */
};
/*
 * i2c_adapter is the structure used to identify a physical i2c bus along
 * with the access algorithms necessary to access it.
 */
struct i2c_adapter {
        struct module *owner;
        unsigned int class;               /* classes to allow probing for */
        const struct i2c_algorithm *algo; /* the algorithm to access the bus */
        void *algo_data;

        /* data fields that are valid for all devices   */
        struct rt_mutex bus_lock;

        int timeout;                    /* in jiffies */
        int retries;
        struct device dev;              /* the adapter device */

        int nr;
        char name[48];
        struct completion dev_released;

        struct mutex userspace_clients_lock;
        struct list_head userspace_clients;
};
/*
 * The following structs are for those who like to implement new bus drivers:
 * i2c_algorithm is the interface to a class of hardware solutions which can
 * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
 * to name two of the most common.
 */
struct i2c_algorithm {
        /* If an adapter algorithm can't do I2C-level access, set master_xfer
           to NULL. If an adapter algorithm can do SMBus access, set
           smbus_xfer. If set to NULL, the SMBus protocol is simulated
           using common I2C messages */
        /* master_xfer should return the number of messages successfully
           processed, or a negative value on error */
        int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
                           int num);
        int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
                           unsigned short flags, char read_write,
                           u8 command, int size, union i2c_smbus_data *data);

        /* To determine what the adapter supports */
        u32 (*functionality) (struct i2c_adapter *);
};

tiny4412一共是9个IIC控制器接口,如果都加入的话,probe函数最多可以被调用9次。

probe.c 部分代码:   I2C控制器的初始化,访问总线的读写算法的实现。

        strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
        i2c->adap.owner   = THIS_MODULE;
        i2c->adap.algo    = &s3c24xx_i2c_algorithm;  //I2C控制访问总线的读写算法
        i2c->adap.retries = 2;               //尝试次数,最多两次 
        i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
        i2c->tx_setup     = 50;


    

      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  //获取资源

      

     

      i2c->ioarea = request_mem_region(res->start, resource_size(res),
                  pdev->name);

      i2c->regs = ioremap(res->start, resource_size(res));

      

      ret = s3c24xx_i2c_init(i2c);

      

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

    ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0,
                  dev_name(&pdev->dev), i2c);

    

    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;
    }

s3c24xx_i2c_algorithm中的 .master_xfer
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;

        pm_runtime_get_sync(&adap->dev);
        clk_enable(i2c->clk);

        for (retry = 0; retry < adap->retries; retry++) {

                ret = s3c24xx_i2c_doxfer(i2c, msgs, num);   //真正的从总线上收发数据

                if (ret != -EAGAIN) {
                        clk_disable(i2c->clk);
                        pm_runtime_put_sync(&adap->dev);
                        return ret;
                }

                dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);

                udelay(100);
        }

        clk_disable(i2c->clk);
        pm_runtime_put_sync(&adap->dev);
        return -EREMOTEIO;
}
/* 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;

        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;
        }

        i2c->msg     = msgs;
        i2c->msg_num = num;
        i2c->msg_ptr = 0;
        i2c->msg_idx = 0;
        i2c->state   = STATE_START;

        s3c24xx_i2c_enable_irq(i2c);      //使能I2C中断
        s3c24xx_i2c_message_start(i2c, msgs);

        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);

        /* For QUIRK_HDMIPHY, bus is already disabled */
        if (i2c->quirks & QUIRK_HDMIPHY)
                goto out;

        s3c24xx_i2c_wait_idle(i2c);

 out:
        return ret;
}
/* 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;        //检查400次

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

                if (!(iicstat & S3C2410_IICSTAT_BUSBUSY))
                        return 0;

                msleep(1);
        }

        return -ETIMEDOUT;
}
 
/* 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;    //7位地址,左移一位。
        unsigned long stat;
        unsigned long iiccon;

        stat = 0;
        stat |=  S3C2410_IICSTAT_TXRXEN;

        if (msg->flags & I2C_M_RD) {
                stat |= S3C2410_IICSTAT_MASTER_RX;    // 2<<6,对应配置。
                addr |= 1;
        } else
                stat |= S3C2410_IICSTAT_MASTER_TX;  //3<<6 对应上图数据手册截图

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

        /* todo - check for whether ack wanted or not */
        s3c24xx_i2c_enable_ack(i2c);  //使能ACK

        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);

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

        stat |= S3C2410_IICSTAT_START;      //1<<5
        writel(stat, i2c->regs + S3C2410_IICSTAT);
}

猜你喜欢

转载自www.cnblogs.com/jason-linux/p/10520466.html
IIC