Linux i2c driver


1. i2c bus driver - platform device
/* Base SAMSUNG platform device definitions */
FILE:  arch/arm/plat-samsung/devs.c
--------------------------------------------
static struct resource s3c_i2c0_resource[] = {
    [0] = DEFINE_RES_MEM(S3C_PA_IIC, SZ_4K),
    [1] = DEFINE_RES_IRQ(IRQ_IIC),
};  
        
struct platform_device s3c_device_i2c0 = {
    .name       = "s3c2410-i2c",
    .id     = 0,
    .num_resources  = ARRAY_SIZE(s3c_i2c0_resource),
    .resource   = s3c_i2c0_resource,
};


FILE:  arch/arm/mach-s3c24xx/mach-mini2440.c
--------------------------------------------
static struct platform_device *mini2440_devices[] __initdata = {

    &s3c_device_i2c0,

};  


static void __init mini2440_init(void)
{

    platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));

}


2. i2c bus driver - platform driver
/* S3C2410 I2C Controller */
FILE:  drivers/i2c/busses/i2c-s3c2410.c
--------------------------------------------
static struct platform_device_id s3c24xx_driver_ids[] = {
    {
        .name       = "s3c2410-i2c",
        .driver_data    = 0,
    }, {
        .name       = "s3c2440-i2c",
        .driver_data    = QUIRK_S3C2440,
    }, {
        .name       = "s3c2440-hdmiphy-i2c",
        .driver_data    = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
    }, { },
};


/* device driver for platform bus bits */
static struct platform_driver s3c24xx_i2c_driver = {
    .probe      = s3c24xx_i2c_probe,
    .remove     = s3c24xx_i2c_remove,
    .id_table   = s3c24xx_driver_ids,
    .driver     = {
        .owner  = THIS_MODULE,
        .name   = "s3c-i2c",
        .pm = S3C24XX_DEV_PM_OPS,
        .of_match_table = of_match_ptr(s3c24xx_i2c_match),
    },
};


static int __init i2c_adap_s3c_init(void)
{
    return platform_driver_register(&s3c24xx_i2c_driver);
}


3. i2c bus driver - i2c adapter & algorithm
FILE:  include/linux/i2c.h
--------------------------------------------
/*  
 * 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;
    
    struct i2c_bus_recovery_info *bus_recovery_info;
};  


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




FILE:  drivers/i2c/busses/i2c-s3c2410.c
--------------------------------------------
struct s3c24xx_i2c {
{
    wait_queue_head_t   wait;
    struct i2c_msg      *msg;
    unsigned int        irq;
    enum s3c24xx_i2c_state  state;
    void __iomem        *regs;
    struct device       *dev;
    struct s3c2410_platform_i2c *pdata;
...
    struct i2c_adapter  adap; 
...
};


/* i2c bus registration info */
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
    .master_xfer        = s3c24xx_i2c_xfer,
    .functionality      = s3c24xx_i2c_func,
};  


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

    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;

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

    i2c->adap.nr = i2c->pdata->bus_num;
    i2c->adap.dev.of_node = pdev->dev.of_node;
    ret = i2c_add_numbered_adapter(&i2c->adap);
}


/* 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->pdata;
        
    /* write slave address */        
    writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);
        
    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);
    }
    …
}


/* 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)
{

  ret = s3c24xx_i2c_doxfer(i2c, msgs, num);

}


/* s3c24xx_i2c_doxfer
 *  
 * this starts an i2c transfer
*/
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
                  struct i2c_msg *msgs, int num)
{

    ret = s3c24xx_i2c_set_master(i2c);
    
    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);
    s3c24xx_i2c_wait_idle(i2c);

}


/* 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 whether 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);


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


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


4. i2c device driver - board specific
FILE:  include/linux/i2c.h
--------------------------------------------
/**
 * struct i2c_board_info - template for device creation
 *
 * i2c_board_info is used to build tables of information listing I2C devices
 * that are present.  This information is used to grow the driver model tree.
 * For mainboards this is done statically using i2c_register_board_info();
 * bus numbers identify adapters that aren't yet available.  For add-on boards,
 * i2c_new_device() does this dynamically with the adapter already known.
 */
struct i2c_board_info {
    char        type[I2C_NAME_SIZE];
    unsigned short  flags;
    unsigned short  addr;
    void        *platform_data;
    struct dev_archdata *archdata;
    struct device_node *of_node;
    struct acpi_dev_node acpi_node;
    int     irq;
};


FILE:  arch/arm/mach-s3c24xx/mach-mini2440.c
--------------------------------------------
/*          
 * I2C devices  
 */                 
static struct at24_platform_data at24c08 = {
    .byte_len   = SZ_8K / 8,
    .page_size  = 16,
};  


static struct i2c_board_info mini2440_i2c_devs[] __initdata = {
    {   
        I2C_BOARD_INFO("24c08", 0x50),
        .platform_data = &at24c08,
    },  
};      


static void __init mini2440_init(void)
{

    i2c_register_board_info(0, mini2440_i2c_devs,
                ARRAY_SIZE(mini2440_i2c_devs));

}


5. i2c bus driver - i2c client & driver
FILE:  include/linux/i2c.h
--------------------------------------------
/**
 * struct i2c_client - represent an I2C slave device
 */
struct i2c_client {
    unsigned short flags;       /* div., see below      */
    unsigned short addr;        /* chip address - NOTE: 7bit    */
                    /* addresses are stored in the  */
                    /* _LOWER_ 7 bits       */
    char name[I2C_NAME_SIZE];
    struct i2c_adapter *adapter;    /* the adapter we sit on    */
    struct i2c_driver *driver;  /* and our access routines  */
    struct device dev;      /* the device structure     */
    int irq;            /* irq issued by device     */
    struct list_head detected;
};


/**
 * struct i2c_driver - represent an I2C device driver
 */
struct i2c_driver {
    unsigned int class;


    /* Standard driver model interfaces */
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    int (*remove)(struct i2c_client *);


    /* driver model interfaces that don't relate to enumeration  */
    void (*shutdown)(struct i2c_client *);
    int (*suspend)(struct i2c_client *, pm_message_t mesg);
    int (*resume)(struct i2c_client *);


    /* a ioctl like command that can be used to perform specific functions
     * with the device.
     */
    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);


    struct device_driver driver;
    const struct i2c_device_id *id_table;


    /* Device detection callback for automatic device creation */
    int (*detect)(struct i2c_client *, struct i2c_board_info *);
    const unsigned short *address_list;
    struct list_head clients;
};


FILE:  drivers/misc/eeprom/at24.c
--------------------------------------------
struct at24_data {

    struct i2c_client *client[];

};


tatic const struct i2c_device_id at24_ids[] = {
    /* needs 8 addresses as A0-A2 are ignored */
    { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
    /* old variants can't be handled with this generic entry! */
    { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
    { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
    /* spd is a 24c02 in memory DIMMs */
    { "spd", AT24_DEVICE_MAGIC(2048 / 8,
        AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
    { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
    /* 24rf08 quirk is handled at i2c-core */
    { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
    { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
    { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
    { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
    { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
    { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
    { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
    { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
    { "at24", 0 },
    { /* END OF LIST */ }
};
MODULE_DEVICE_TABLE(i2c, at24_ids);


static struct i2c_driver at24_driver = {
    .driver = {
        .name = "at24",
        .owner = THIS_MODULE,
    },
    .probe = at24_probe,
    .remove = at24_remove,
    .id_table = at24_ids,
};


static int __init at24_init(void)
{

    return i2c_add_driver(&at24_driver);

}
module_init(at24_init);


6. i2c core
FILE:  drivers/i2c/i2c-core.c
--------------------------------------------
/* add/del i2c adapter */
int i2c_add_adapter(struct i2c_adapter *adapter)
  static int i2c_register_adapter(struct i2c_adapter *adap)
    device_register(&adap->dev);
void i2c_del_adapter(struct i2c_adapter *adap)


/* add/del i2c driver */
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
  driver_register(&driver->driver);
void i2c_del_driver(struct i2c_driver *driver)


/* add/del i2c device */
struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
  device_register(&client->dev);
void i2c_unregister_device(struct i2c_client *client)


/* i2c send/receive */
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
  int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
    adap->algo->master_xfer(adap, msgs, num);
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)


7. Write() System Call - Work Flow
**************************************************
write()
  at24_bin_write()/at24_macc_write()
    at24_write()
      at24_eeprom_write()
        i2c_transfer(client->adapter, &msg, 1);
          adap->algo->master_xfer(adap, msgs, num);
**************************************************


参考资料
1. Linux I2C驱动完全分析(一)
2. Linux I2C驱动完全分析(二)
3. S3C2410看门狗驱动分析 

4. Linux i2c Wiki




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值