找回密码
 注册
关于网站域名变更的通知
查看: 266|回复: 1
打印 上一主题 下一主题

基于S3C2440的嵌入式Linux驱动——AT24C02(EEPROM I2C接口)驱动解读

[复制链接]
  • TA的每日心情

    2019-11-20 15:22
  • 签到天数: 2 天

    [LV.1]初来乍到

    跳转到指定楼层
    1#
    发表于 2020-5-29 11:30 | 只看该作者 回帖奖励 |正序浏览 |阅读模式

    EDA365欢迎您登录!

    您需要 登录 才可以下载或查看,没有帐号?注册

    x
    本文将介绍Linux中AT24C02驱动。AT24C02是一种EEPROM,使用I2C接口来访问。3 p1 Z1 o6 l5 X$ M$ }

    $ t  N- o: f' b在开发板中,使用I2C控制器0和AT24C02连接,这里就不给出原理图了,如需要,可以搜索TQ2440开发板的原理图。, @$ g( {% g4 D3 Z9 ?

    & s3 n1 x0 H8 b. H6 _" V- [. r目标平台:TQ2440
    9 P# E3 f# H4 R& U& \$ p: w* g6 O& L1 H6 A  j
    CPU:s3c2440
    7 a. r" d+ P  ^' t9 o& B( ?0 C' ^, \4 ~
    内核版本:2.6.32! \/ Z3 ]% Z2 j  B6 \( U
    $ K/ {. X. G" y& {$ H) K
    本文所有的代码均位于内核源码:linux/drivers/misc/eeprom/at24.c中。& C  i: w1 U% \+ ]
    0 w+ P4 s8 g+ l7 D5 l# j; g8 ?6 r1 b
    1. 模块注册和注销8 P+ a) r# p! m8 A5 C
    static int __init at24_init(void)0 n+ L) u5 d( d
    {
    ! ~* f* m- M4 o4 q7 m  @- a; x        /* 将io_limit向下圆整到最近的2的幂*/+ h+ l8 O# d4 E, B+ U2 q
            io_limit = rounddown_pow_of_two(io_limit);( _. r2 Q% Z7 q$ H2 v
            return i2c_add_driver(&at24_driver); /* i2c 驱动注册*/
    8 y- G: y: z$ q3 F* Z# s% ^8 z9 a}4 C3 }& Q4 G, `! {. y- a6 F
    module_init(at24_init);
    5 a# e5 z: k9 g+ L! m* ~" I( ?" z* j3 |  L( U8 }3 Y; u! S
    static void __exit at24_exit(void)
      \. Z* u8 `/ R. P( P{* B2 z* S6 c2 Z/ X0 M3 U) N
            i2c_del_driver(&at24_driver);
    ) j/ A2 ]0 `( Q0 ?: b}" N+ _% f0 q6 ^
    module_exit(at24_exit);1 u1 j. o7 @2 v2 S6 {

    & C, d& _" ]- `! ^6 T/ H1 p+ QMODULE_DESCRIPTION("Driver for most I2C EEPROMs");
    & ~0 R( r7 k9 N) d6 m8 r5 Y5 BMODULE_AUTHOR("David Brownell and Wolfram Sang");& c% a/ `: }, j! b% M
    MODULE_LICENSE("GPL");6 K+ P& _5 }+ b% j) |
    : H- {4 b4 o2 G) M- `) c* Z
    3 }3 q2 k" G' q9 D7 x
    注册函数很简单。io_limit为写入时允许一次写入的最大字节,该参数为驱动模块参数,可由用户设置,默认值为128字节。3 x# [% [% i! B$ _2 A( w3 W/ P
    * G6 {' g3 k2 W- e6 Q& I  a  F  D
    首先对io_limit向下圆整到最近的2的幂,接着直接调用了i2c_add_driver来注册一个i2c驱动。  M  K) B$ p: _7 a. p6 ~
    % g5 X$ o, x9 b# K6 T
    注销函数更简单。注销之前注册的i2c驱动。8 \3 @$ t; L; O/ i+ N& B# F  C
    7 W7 ~! n% m# H1 z

    7 }3 v6 i& ~) m, w8 O: w8 J
    + P& Z0 c) h! S5 ^8 s2. 设备驱动绑定0 R; L" {/ N0 H# Z
    熟悉I2C驱动架构的可能会知道I2C驱动的match函数,该函数将使用id表(struct i2c_device_id)和i2c设备(struct i2c_client)进行匹配,判断是否有name字段相同,如果相同则匹配完成,即可完成设备和驱动的绑定,接着便会调用驱动提供的probe方法。我们来看下驱动提供的id表。
    0 D/ @( I3 G3 u) ?* G4 n3 N
    ( {4 Y, r& \. ]8 E  _1 h
    1 O6 u  x9 z  `static struct i2c_driver at24_driver = {9 e5 ?/ g& m1 {" t! R
            .driver = {$ y! j2 s4 j& r0 |
                    .name = "at24",
    # _4 \. X9 P* z8 q7 V                .owner = THIS_MODULE,
    5 ^9 B# A/ W% Y& ]        },# E3 O( s' t$ N, G
            .probe = at24_probe,
    - E/ s' I0 q) H, Z( [        .remove = __devexit_p(at24_remove),1 c% E- y+ Z, C% J" [
            .id_table = at24_ids,
    ) l# i. j. m+ @8 i};
    - k" y2 {( L0 W/ }驱动提供的id为at24_ids,如下:  ^/ o5 z) M+ }' l1 s
    ! J: C& p1 @5 m$ X' }& k

    ; g% Y* Y" \6 S2 I2 tstatic const struct i2c_device_id at24_ids[] = {
    , e4 }3 @+ F# \6 X( ^. E' Q        /* needs 8 addresses as A0-A2 are ignored */- v3 Q, Q9 v& N" R* R" Z
            { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },( D6 U: q8 s  W, x
            /* old variants can't be handled with this generic entry! */9 Y1 l; c% |) N3 o4 s  b8 {
            { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
    - d5 V% z+ D( j; H. m        { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },7 n  @2 ~7 @5 s; L9 {: B1 }$ B
            /* spd is a 24c02 in memory DIMMs */
    7 Y2 m! d1 n) o* {  ]        { "spd", AT24_DEVICE_MAGIC(2048 / 8,
    ; x2 |6 C9 N) g' v; m. b                AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },8 ^* m* w( Q& f- @; Z- m
            { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
    - u: a7 Q4 B2 W# h" G1 P        /* 24RF08 quirk is handled at i2c-core */
    - ?4 `5 L( x+ M        { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },0 n& L" M( `# o, @1 y; Z$ F5 T
            { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
    6 u/ |1 o3 Y: V$ z, i6 k6 S        { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
    3 J( u( D- X5 ?        { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) }," p4 i8 r1 V0 ?. J2 b! B
            { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
    4 i  H0 Z5 Z3 `. w# g        { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
    / \- I6 E0 F! B        { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
    ' {' i7 r# m$ c, R        { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },& Q3 ~- ~+ z+ ]) X) I* x$ V
            { "at24", 0 },2 `0 {2 F" G4 n5 M
            { /* END OF LIST */ }3 l9 U' `) T9 y5 O: R! z. s3 V1 Z. E
    };5 t2 s1 q% X$ V; k9 L; t; z
    结构体成员的第一个参数即为name,表示设备的名字。第二个参数,在该驱动中,为一个幻术(magic),通过AT24_DEVICE_MAGIC宏计算。
    % Z: g+ m% _1 N/ m8 N
    + c: \; e! _, h  N4 L! U! K7 Q' D/ |宏第一个参数为eeprom的大小,第二参数为一些标志位。我们看下这个宏:
    % d% f9 M9 _' _" x* z9 o# n+ |9 {. I  n( c* ?' O3 S: P

    # c  j, }2 ?! _5 m% W" [, s#define AT24_SIZE_BYTELEN 5$ A$ o: v2 A3 `4 M
    #define AT24_SIZE_FLAGS 8
    0 @, y# Q! e5 B$ U9 ^; a2 ?+ Q+ u! j4 V# M& |# n& B5 X
    /* create non-zero magic value for given eeprom parameters */
    : T5 {. S) q. L) i+ S$ ?#define AT24_DEVICE_MAGIC(_len, _flags)                 \# b0 t5 ]# D: D' I8 @
            ((1 << AT24_SIZE_FLAGS | (_flags))                 \
    3 w9 m0 {5 H& t            << AT24_SIZE_BYTELEN | ilog2(_len))
    # @7 P$ P/ C1 X在这个表中,针对这里讲解的24c02,其大小为256字节,标志位为空。4 e+ V1 t2 `8 V, R" _; y3 w' {
    $ J) b8 ]' ~* U+ v1 S
    % @6 K0 f& [0 w
    9 c4 G* U, J$ a/ Q
    3.probe函数8 n) o) F( ]! E- J
        当i2c总线完成设备驱动绑定后,就会调用probe方法了。具体看下这个函数。
    - Y( P, G$ A( Q/ }4 P8 a. S& v0 y' n5 |3 `$ F+ l
    ; \6 Z0 G! N) z3 O4 ^
    static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)% r- s) s- v! B9 z/ }5 d
    {/ Y" M+ k( {1 ~+ Y' m+ l
            struct at24_platform_data chip;$ D8 `0 s! j' D4 z
            bool writable;7 O+ r: d( v, ?3 e. i4 W7 ^6 q2 J- L
            bool use_smbus = false;) t$ T; S3 s' W8 `4 L4 P8 h
            struct at24_data *at24;
    5 ?2 ?8 @1 s% b3 D4 ?  G8 L        int err;0 q! D+ G- ~% ^7 K1 X7 D. Z
            unsigned i, num_addresses;4 R/ E; a8 J& l* [4 {- [* K2 M
            kernel_ulong_t magic;; p9 P: J3 k  W9 `! u: Z1 F# h. y

    8 X, m1 }8 a3 T$ f! t5 J6 E8 x        /* 获取板级设备信息*/
    % T  R' Y. m/ Q, E; K8 U- |, {        if (client->dev.platform_data) {7 _" j9 P9 s' i7 D
                    chip = *(struct at24_platform_data *)client->dev.platform_data;% r3 W% g2 h- E' C
            } else {
    ! J5 ?/ v' R' M% u2 b# g( v                /* 没有板级设备信息,也没有driver_data,直接出错*/
    , u5 t. K9 Z+ H; ?1 r                if (!id->driver_data) {! n+ Q% a2 I# d4 M. Y1 l
                            err = -ENODEV;. k6 t2 Q! R. s# S8 \% Q
                            goto err_out;
    % ?  Q, ^. P+ K' W                }
    6 Z6 ]' S  q7 }  N                magic = id->driver_data;
    # N& i6 v% k2 e4 ?                chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));2 x  K1 ]8 ^' T% e& E2 x
                    magic >>= AT24_SIZE_BYTELEN;
    - r6 Z% Z) [/ c6 ^                chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);/ z, k2 ?7 b8 m
                    /*
    / z+ `, D8 ^7 }# g' {                 * This is slow, but we can't know all eeproms, so we better, A& Q3 P8 ~4 x  _3 L/ _- ~% Y0 ~3 S
                     * play safe. Specifying custom eeprom-types via platform_data
    ) C. N6 R  y. P; Y$ p5 c                 * is recommended anyhow.
    + l& c" g. R7 K                 */3 {6 N# p, p) V
                    chip.page_size = 1;' w* H+ V% h8 n( |* z
    6 B) \( r1 d: y  f, K- m# }$ z
                    chip.setup = NULL;
    & ~! A' C4 g" a9 W( Z                chip.context = NULL;, S9 v1 N( j6 P5 c3 m
            }
    / T2 ^* E; j6 E" B+ d# |$ f, h8 i
            /* 检查参数,
    % j' x- w9 v# e$ Z+ _5 P            byte_len和page_size必须为2的幂,不是则打印警告*/# H3 u1 F& Q% J
            if (!is_power_of_2(chip.byte_len))
    $ t4 l. v7 K0 I8 v  B  U4 ?                dev_warn(&client->dev,
    1 n! G, x5 j! Q" b3 ^                        "byte_len looks suspicious (no power of 2)!\n");9 b8 w' ]% M. P) K3 A; E* Z9 e6 l
            if (!is_power_of_2(chip.page_size))
      H" h6 w6 @% V- S- w* h1 p                dev_warn(&client->dev,, u  v4 Y( G5 Z+ t1 U3 t
                            "page_size looks suspicious (no power of 2)!\n");
    : Z: p$ @0 F5 N8 P# o( b2 q
    6 o5 F+ z8 c' c/ N, X* N        /* Use I2C operations unless we're stuck with SMBus extensions. */
    2 x  F& M* n- w+ H/ u/ }" m        /* 检查是否支持I2C协议,8 W) c# x$ a$ L1 L' D, o
                如果不支持,则检查是否使用SMBUS协议*/1 I( P- _& o& a4 V! [
            if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
    4 g2 ^% K( }- G9 |                /* 不支持I2C协议,但是使用16位地址,出错*/' l9 I: Q' u/ d# O- K0 Z/ W
                    if (chip.flags & AT24_FLAG_ADDR16) {+ d) D3 Y0 l) p6 c! e
                            err = -EPFNOSUPPORT;  v) }' I4 c3 r6 H4 d* W
                            goto err_out;
    5 R! s. {- @  }6 o$ V' w3 V( H                }: W" \/ k! r& _3 s) }
                    /*  不支持I2C协议,使用8位地址,7 c& A0 M0 Z% ^; Y" V: a
                       但是不支持I2C_FUNC_SMBUS_READ_I2C_BLOCK,出错*/% A' I% j8 Q2 C" z
                    if (!i2c_check_functionality(client->adapter,
    5 R4 f9 t9 j# c7 N2 G$ K                                I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {0 \6 L5 Y8 b( f1 Y6 X
                            err = -EPFNOSUPPORT;
      t& v% h- i1 `' B& O                        goto err_out;$ L' u5 c1 q* T+ R6 B, p
                    }1 c: J- M! Q8 y
                    use_smbus = true; /*使用 SMBUS协议*/
    8 ?+ t+ ~8 t: f' K: w* p7 d# R+ ?9 s        }4 @, V4 t2 k( O- t; L; P5 a
    / ^0 a2 C! t1 ]! \
            /*是否使用8个地址,根据id表,
    5 O' }4 x3 H7 o/ _4 {4 v% D            目前只有AT24C00使用8个地址,其他都为1个*/
      M: ^9 \7 M( C6 U        if (chip.flags & AT24_FLAG_TAKE8ADDR)  @2 z8 C; M2 }* I, z
                    num_addresses = 8;/ s) |3 K# A) U9 z; F
            else
    # o, m& a( h) s$ w+ ~8 @# P' P                /* 24C02需要1个地址,24C04为2个,以此类推*/- j- O: y* y$ B! W) M$ R
                    num_addresses =        DIV_ROUND_UP(chip.byte_len,
    " ^3 @1 X9 t, U                        (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);* A' ~- p# `4 i) W: m& m  o
    . E. O) {: y) G- L. ^. [8 ]
            /* 分配struct at24_data,同时根据地址个数分配struct i2c_client*/4 Z  u0 P* F3 L9 A7 X
            at24 = kzalloc(sizeof(struct at24_data) +) z4 \; a- ]* w4 \& c! I4 Z
                    num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);: B# I. P4 j7 W- p
            if (!at24) {. ?; L4 L0 }9 n/ i8 ?8 W% N
                    err = -ENOMEM;3 i' X  G& Q; o# C. R
                    goto err_out;
    ) j" Z/ Z& c2 Q. Q2 t8 Y        }. U0 p  R% @5 ?3 ^6 [

    . v4 X  W& z1 k4 `, F7 t) c" t        /* 初始化struct at24_data*/
    2 _8 o% P" D( z5 s; H# Z        mutex_init(&at24->lock);0 z* C' ]3 `1 @5 Y( S! S0 L" Y& H# w
            at24->use_smbus = use_smbus;
      n$ M2 F$ d9 Y7 [: V& c        at24->chip = chip;6 a7 I/ c0 K! m  d" U
            at24->num_addresses = num_addresses;  _7 v* y" M: R
    . f) o3 J0 t  V# p3 G5 k
            /*
    # x; j+ J; r+ N, z$ i0 ?         * Export the EEPROM bytes through sysfs, since that's convenient.# U+ X" m( [7 x2 Z- r/ H! D
             * By default, only root should see the data (maybe passwords etc)
    9 ~1 G; r9 i5 f5 O! @         */. V( W& V  E/ N" S& t( q
             /* 设置bin_attribute字段,二进制文件名为eeprom,
    0 D; l9 r$ J) k. y5 ^  v1 P- v9 X% g             通过它即可读写设备 */
    ) v" }  J7 _4 F* o# I        at24->bin.attr.name = "eeprom";( ~0 M8 O  C$ S7 W: m, ?: ]5 ^
            at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;# `$ d5 b/ L! q( R( y% A
            at24->bin.read = at24_bin_read;
    8 q8 X2 C3 n; d+ x* a+ q- f        at24->bin.size = chip.byte_len;' ]* ?4 ^5 [. K) u& w7 f! o

    7 L* P2 q" ~( i& ]2 h( B3 R        at24->macc.read = at24_macc_read;  /***  先忽略***/
    5 n, h' D! k$ o1 P+ E4 G& b* k3 W  h' p
            /* 判断设备是否可写*/
    7 K1 ]4 p7 X9 q        writable = !(chip.flags & AT24_FLAG_READONLY);
    : X- M( w3 L) A% g8 l1 P: d        if (writable) {7 U. H+ G1 r+ Z+ B  X
                    if (!use_smbus || i2c_check_functionality(client->adapter,
    5 S* @1 i5 D6 E5 S9 }2 Z, I                                I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
    $ [+ y4 z1 K6 I: r; u
    3 N0 Q; |, z6 q                        unsigned write_max = chip.page_size;: B7 K9 q! [1 @$ M7 p' O3 N% Y

    * U9 p, P* q6 k4 I( Q; _0 _                        at24->macc.write = at24_macc_write;  /***  先忽略***// }9 _5 @4 r* q( S6 ]5 k, X/ u, z

    6 a2 w7 P. M. t# s3 V3 U# c5 U+ |                        at24->bin.write = at24_bin_write;    /* 写函数*/6 w# U! @0 d7 q$ U5 Y
                            at24->bin.attr.mode |= S_IWUSR;    /* 文件拥有者可写*/
    6 Q7 V5 Y( |* C' H6 a; E7 S+ S' B# U% n6 x7 Y0 v5 S6 p; |
                            if (write_max > io_limit)  /* 一次最多写io_limit个字节*/
    ( w4 i% s6 L0 m! b  [7 x                                write_max = io_limit;$ t! K- i% {! m! |* V( L
                            /* 如果使用smbus,对write_max检查*/
    ; V1 f; b7 T5 m' A/ E                        if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
    2 I6 m6 H8 u# @- F6 A9 @6 u                                write_max = I2C_SMBUS_BLOCK_MAX;7 K2 d8 s2 [: T1 J5 N( }" B9 a
                            at24->write_max = write_max;  1 o, v3 @7 q; H* d
    $ s, O! k- [: ~2 M3 p3 @
                            /* buffer (data + address at the beginning) */
    7 t3 q- v+ T" g3 r) a3 R& M                        /* 分配写缓冲区,多余两个字节用于保存寄存器地址*/; B2 f% m1 k# N& j1 H4 w
                            at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
    - `$ S2 Q- X  e                        if (!at24->writebuf) {
    # M" B* v- j  J+ |$ Y                                err = -ENOMEM;
    ! ]/ r% B: F9 C. `' f. ^                                goto err_struct;
      |7 x. ^* e+ m8 S: ^. o; i5 F                        }
    ( u( u5 f2 P, h1 S$ ]% O                } else {
    2 q* S2 _/ e9 Y, X, z                        dev_warn(&client->dev,
    8 O0 F/ d! S1 J$ p+ o                                "cannot write due to controller restrictions.");8 P5 |3 J% [4 v1 t* C# H
                    }
    . T3 V, \3 q5 ~+ X# h, \        }  n& H0 G- b" r2 @" i

    ; d. j. Z# j  _" ]        at24->client[0] = client;  /* 保存i2c设备client*/
    7 G8 l! W) s  A$ u: l. n0 B9 I; m$ T7 S$ L4 Q8 M
            /* use dummy devices for multiple-address chips */
    5 d# C) L2 K; ~; t0 t7 V  V        /* 为其余设备地址注册一个dummy设备*/9 E" T7 F+ z, W3 w  C6 }
            for (i = 1; i < num_addresses; i++) {
    : V2 j4 m; D- m                at24->client[i] = i2c_new_dummy(client->adapter,5 d0 M# N: `& B9 t) q
                                            client->addr + i);     /* 设备地址每次加1 */
      D1 h; u( a% h: P                if (!at24->client[i]) {/ d' q9 D% m+ P- {
                            dev_err(&client->dev, "address 0x%02x unavailable\n",
    9 D. x+ f& x# V: u& r8 G                                        client->addr + i);3 I# {' f- k* F
                            err = -EADDRINUSE;" J& u( y  T+ T# R
                            goto err_clients;  Q9 J/ n2 X; T, W
                    }
    $ S0 D& q5 Z8 ~# z- v5 e        }
    ! C/ k- T0 E2 M: n
    8 B' @2 [. C) s1 Y: g        /* 创建二进制属性*/
    ! m6 N3 ?( o' l0 j) y# z        err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
    & e+ U% P( c( ~9 s        if (err)
    * \* c, {* U, }3 [" J6 W7 w                goto err_clients;$ _1 R3 {( }8 Z5 @" }- g$ @
    # R& l8 W( U. K; f. B$ q" V
            i2c_set_clientdata(client, at24);  /* 保存驱动数据*/7 x; y; _; Q) O& T5 M" Q

    $ G" e3 g% Z* I% T        /* 打印设备信息*/+ p% H$ I  }' ?# H$ a9 c% g9 }
            dev_info(&client->dev, "%zu byte %s EEPROM %s\n",
    * l/ Z4 p& P4 G2 Y6 g' S5 V3 T' p                at24->bin.size, client->name,
    : M9 [1 t% H5 i3 d3 h                writable ? "(writable)" : "(read-only)");2 j$ u2 c7 i  o" Z3 \$ `+ X( ?
            dev_dbg(&client->dev,9 z' M- i. }) ]
                    "page_size %d, num_addresses %d, write_max %d%s\n",
    # e9 p2 c( R( C% p7 s) G                chip.page_size, num_addresses,
    1 ]* C+ ^- c) R& @4 x                at24->write_max,  A" v0 L9 ]+ x6 P" O9 q4 s: H
                    use_smbus ? ", use_smbus" : "");
    & D, c0 F2 x- y' n. }. M( K+ ?3 j# q( i! x
            /* export data to kernel code */
    % H0 M9 O" v+ T        if (chip.setup)' I: _: F9 d$ _! m
                    chip.setup(&at24->macc, chip.context);5 k6 D$ K1 g: D1 ]( l
    $ {! B9 J. y. c8 K) l
            return 0;" e, t% o  h* p! j2 P

    ; h6 t8 i, N3 `, r. t+ L4 s: D; Nerr_clients:. K, q( z& X" y; Z1 n, h* L2 l
            for (i = 1; i < num_addresses; i++)5 d, r8 U+ h0 }" ?% @5 V* F6 [1 u8 J
                    if (at24->client[i])
    ) ~1 a, t9 s1 Y( e- _+ @1 ^5 @) g                        i2c_unregister_device(at24->client[i]);
      J6 L, x! f) O  u; Y6 T2 E6 O# @9 d3 e4 t+ D( V
            kfree(at24->writebuf);
    ( U" R0 Z# k8 y1 U3 o/ E- Zerr_struct:
    . s6 U) O- B! [        kfree(at24);4 ^! {: K2 X+ x; Z7 v1 t! D6 C" w: s
    }
    + }8 q' j+ g5 j' [. Y- F4 ?驱动首先获取板级设备信息(client->dev.platform_data),我们假设驱动移植时,添加了该板级设备信息。0 p% t7 m! z; W0 V$ R( }: o1 k$ [
    判断是使用I2C协议还是SMBus协议。在这里,I2C adpater使用I2C协议。
    0 K6 b& y( r! `* `3 z  t
    9 w. N1 M0 F2 p! \* l/ I然后,判断设备需要多少个i2c设备地址。! I2 I- r  }+ j& E" N( b; a
    8 X, S( l# v6 u' X7 L) h2 Y* a
    这里补充下:根据at24c02的datasheet,设备地址的第1位到第3位,将根据不同的设备来进行设置。# u$ I' D! Q0 z. _  d

    1 B6 w" ?7 q: o例如,如果是at24c04,则设备地址的第1位将用来表示寄存器地址,因为内存大小为512字节,而寄存器地址只有8位(256字节),7 a* z+ E% T$ U0 {% ^$ A; W

    + y8 Q# h" U3 G2 j; z# ~9 ?需要额外的一位用来表示512字节,因此使用了设备地址当中的一位来实现此目的。具体的请看datasheet。
    7 J' P" M3 c8 U/ T% \5 n" M" V: S% O; a; [, m$ a5 j1 q
    这里使用at24c02,num_addresses将为1。
      I9 w3 s/ G  \* e$ y' Q9 {2 [' E" N
    5 S; P5 i) c1 M接着分配struct at24_data和struct i2c_client指针数组空间。
    0 q5 I; C; f8 R- a- X! }* h* `, {% Z
    然后对struct at24_data进行了初始化工作。" b  F8 z, n. v) y, X

    - @. }8 O! t# h0 M& W接着,对二进制属性进行了配置。名字为eeprom,同时配置了其读方法(at24_bin_read),如果设备可写,还将配置其写方法(at24_bin_write)。1 d3 V7 Z8 u  E: E/ U" h
    . q) w1 q1 i6 |) q
    接下来很重要的一步,如果设备使用多个地址,则需要为所有地址(除了第一个地址)分配一个dummy device,这样这些地址就不会被其他的I2C设备占用了。
    9 W2 b! G  Z+ m# t; A4 X' P3 ^6 Q& ?) P  N$ r% o$ B  Q
    最后,向sys文件系统注册了二进制属性文件,通过该二进制文件,用户即可访问该设备。- {7 x2 x9 z4 Z" W+ T( S* S% Z. ?

    + s; L3 w0 B5 ]! y) \9 i注意:驱动使用了struct memory_accessor的东东,对这个东东不是太了解,所以先忽略,这个东西不影响驱动整体的架构。
    + f/ l$ j/ L& {; [  r. K3 N6 g  }; y

    / X5 U  v$ ]  B7 X+ s6 A* S. x
    / e* ?4 q1 D, p, ~! j4.设备访问方法# [# ~3 J1 @+ b; l3 @* j
       从第3结的分析可知,驱动并没有注册任何字符设备或者杂项设备,只是向sys文件系统注册了一个二进制属性文件。因此要访问设备,必须通过该文件的读写函数来。
    , W# Y* k8 t% B2 Y# G5 H" i  `# C7 j) r
    读写函数在probe函数中指定为at24_bin_write和at24_bin_read,我们来分别看下。
    ' I: i. G  Q5 y5 a7 L$ v+ o; b/ u* O- ^
    4.1 写函数(at24_bin_write)2 h' H8 A; h' c% f
    static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr,
      M$ H! T3 k$ U& C6 {2 B                char *buf, loff_t off, size_t count)0 i" g# G/ \, v0 c9 I) r4 N
    {: H  L# ^9 R  K+ T8 _: o6 c. j
            struct at24_data *at24;
    " K6 c! [8 r3 N* H. `( E! L9 e7 U7 K* G* i& e/ S' w$ j3 J& v; U; C! o
            /* 通过kobj获取device,再获取driver_data */
    ' ?& u5 F' H4 n% j3 O& I) e        at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
    7 S. z+ ~+ e2 w6 i; c* z. V        return at24_write(at24, buf, off, count);# |( D6 I/ M# s3 P
    }5 N9 z$ Q4 g4 a1 P7 \' o
    该函数首先通过kobj获取了struct device的指针,再获取了at24。
    7 |7 U( _; ~7 P接着直接调用了at24_write。如下:
    7 S6 {+ D! V9 s2 A/ C! i+ f. ~) ]5 m( H* y
    tatic ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
    - c* F. `7 W  H% P1 A3 g                          size_t count)
    2 e( B0 z: ^9 \! {- ~{
    ) N- B7 u( [. t9 ^7 l/ \        ssize_t retval = 0;
    $ N- k5 D! K4 o8 t! ]/ P, T3 L/ e+ D$ U
            if (unlikely(!count))
    # S2 ^3 M$ R+ S                return count;* {7 b! m8 Z* m6 L

    $ j$ ]! t  \; G7 f8 X3 k% w8 u        /*
    2 k9 C2 h/ }! x, T  X8 B: m         * Write data to chip, protecting against concurrent updates6 u* G" n, ^5 @
             * from this host, but not from other I2C masters.
    - h; B  H- x1 D: {8 i: q, o2 o         */
    6 E, t6 e9 m" [5 M0 U" D          /* 访问设备前,加锁*/. J! Z) i% u& m& i) X; e+ H
            mutex_lock(&at24->lock);
    - W& G6 |, o( s, N* ~( F% W
    $ D4 b. D. r& v        while (count) {
    : T* q9 Y3 O/ }) j3 }5 w4 r/ C                ssize_t        status;
    $ ^& @$ h9 j- b+ Z- }- g0 [6 q8 ?  i& z& F
                    status = at24_eeprom_write(at24, buf, off, count);+ l% }6 \- m; I# z) g" n
                    if (status <= 0) {: j2 |3 O5 k- |& o% W
                            if (retval == 0)
    ; S2 X9 g3 O2 |$ K4 R# O                                retval = status;
    % t( o" e! i1 Z4 d                        break;) C8 {, Z0 O6 S' J$ E. X. _+ {+ @
                    }/ t: e+ j7 W! w- z! G! p
                    buf += status;$ a- m* F$ }6 J1 M$ U2 g3 z* [6 C. A
                    off += status;* g5 \1 f" r! H1 Z7 p1 h
                    count -= status;, C- N9 s; j/ N
                    retval += status;( G4 R& t  _" h8 L' r
            }: ~9 I; P3 k  S5 ^" P1 r* O1 g9 F
    ( Z0 v+ j3 m. g  u+ {
            mutex_unlock(&at24->lock);/ i% c( A, G! S- `. ^" m# ?
    0 @5 @  U5 `( a" p( e  a% ~, h4 n
            return retval;! S* k" C! x% l; t' e2 }
    }& w9 M2 E3 d! v% L3 }/ x; K

    / _& a1 B% F/ q* U该函数不复杂。在访问设备前,首先加锁互斥体,以防止竞态。然后根据count来调用at24_eeprom_write函数将数据写入设备。4 a+ s: z+ |3 b, a' V
    写入成功后,更新偏移量等信息,如果还需要写入,则再次调用at24_eeprom_write函数。
    ! ?( h9 u- f/ P: `5 C/ t
    / M% }. V' Z  ?' E看下at24_eeprom_write函数:
    ' M/ O- \- Y) b, y. g$ @$ y5 K" K
    " \" ^0 H3 t. S2 N! T/*
    # |& l' J( f& D  p6 ]7 H * Note that if the hardware write-protect pin is pulled high, the whole' m+ T. r# W9 t, _
    * chip is normally write protected. But there are plenty of product
    2 t1 p& W+ W+ i$ D5 ?( c/ L& _$ F * variants here, including OTP fuses and partial chip protect.
    # P* I! [  x3 |0 g *
    , ?0 V  G8 q* V4 ]  y * We only use page mode writes; the alternative is sloooow. This routine- q- u) d- B1 b+ O/ N3 B' {& W3 T
    * writes at most one page.- N. l( Y8 s8 ]: [" @; J2 u6 @
    */8 s; P. Q$ C) x5 I3 s5 `" e) h
    static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,# `& J5 m1 U+ T' U( O
                    unsigned offset, size_t count)/ R3 F3 k$ j$ k( G7 U' E
    {* Q* j/ v' j- @: N
            struct i2c_client *client;/ G' r8 v3 c1 y( W8 x0 \5 h
            struct i2c_msg msg;
    ) n7 f! n# _; f        ssize_t status;# u. Z" o0 }! x, u3 Q
            unsigned long timeout, write_time;
    ' b! l- d4 a- W9 Y( u        unsigned next_page;5 R9 f  ~$ C% P8 E+ g
    6 i7 n% r: m6 L. H* e. c
            /* Get corresponding I2C address and adjust offset */
    5 y) W. M" `+ q  _6 G1 d8 w% C1 X        client = at24_translate_offset(at24, &offset);
    % f- I% n: H% }6 ~. c) k9 q3 P0 z6 G
            /* write_max is at most a page */
    5 H2 V' H; K, o1 C- F! Z( E0 a) n1 c        /* 检查写入的字节数*/$ m# p' S$ k- z/ p+ T/ K# j
            if (count > at24->write_max)
    9 }# ?6 X4 d8 U+ F/ |2 @3 d                count = at24->write_max;
    . Z7 P. s$ f+ z2 t8 q; x: x6 o7 `# i/ R; y
            /* Never roll over backwards, to the start of this page */
    7 [! c# F* S; Q0 W( w% F- E        /* 写入不会超过下一页的边界*/5 k$ p. i5 O* d3 O6 |$ K) C' h
            next_page = roundup(offset + 1, at24->chip.page_size);
    + V# c. V- l0 }3 @2 E        /* 根据页大小调整count*/
    4 f# n" X2 O' {6 R        if (offset + count > next_page)
      D- ^' `7 s4 l                count = next_page - offset;
    * f6 v; W6 J7 R) x2 i* {8 \: a1 @8 j
            /* If we'll use I2C calls for I/O, set up the message */
    6 E: r: q6 ?$ h# C: i9 H        /* 使用I2C协议,需要填充msg*/
    * O- T, s; h: I, `" Q' G" i& a        if (!at24->use_smbus) {
    3 T) ]7 o$ V; S5 ?                int i = 0;
    6 i! |8 C9 V2 X; O! q" F. F3 q+ i- p2 I$ m
                    msg.addr = client->addr;  /*设备地址*/
    " m* Z2 s1 _. \& {                msg.flags = 0;- B! S- W% [5 ^
    0 K6 N; z2 s) a5 {8 w1 d4 a
                    /* msg.buf is u8 and casts will mask the values */
    9 f6 \; j! {9 f9 ^' @, I1 Z0 ?                /* 使用writebuf作为发送缓冲区 */4 v7 h" E3 N# x/ H9 m; N% g. C) Y
                    msg.buf = at24->writebuf;
    , w" _* J3 a: Q* s) _  K0 O               
    * }' X1 q( X$ D                /* 根据是8位还是16位地址,msg.buf的前一(两)个字节( Y) P4 p3 |0 U; h
                         为设备内部的寄存器地址*/8 ?) x- F* E1 P+ e5 A$ t
                    if (at24->chip.flags & AT24_FLAG_ADDR16)
    7 z9 a. o! V& H& _8 ^7 f6 n7 e6 y                        msg.buf[i++] = offset >> 8;  /* 16位地址,先写高位地址*/
    ( |- j+ @# X3 B) G6 z  E8 J: Q; v) n+ _9 s
                    msg.buf[i++] = offset;  t5 P. c2 L2 p/ @6 \0 q' i1 q
                    /* 复制需要发送的数据 */
    ! X& w5 R' O) `                mEMCpy(&msg.buf[i], buf, count);
    : |) S6 e0 I4 p* a( z9 n                msg.len = i + count;   /* 发送长度为数据长度加上地址长度*/
    5 `$ E& p8 u/ Y7 u/ P9 I5 _        }
    : i9 h0 M3 y5 r3 D1 t# ^
    * h* _6 p+ R+ Z& k1 [; [  e: A0 D8 O        /*/ @; E# S6 V, m
             * Writes fail if the previous one didn't complete yet. We may* E$ w3 y8 K/ [( L& M8 H
             * loop a few times until this one succeeds, waiting at least
    + d% m; i  p$ D+ @& J# ^+ c' a5 p8 H         * long enough for one entire page write to work.5 p% W+ X7 M0 U) ?# [* t6 {
             */
    " V- V' {% Z& q7 y: ~5 H: A        timeout = jiffies + msecs_to_jiffies(write_timeout);
    3 C. r/ D6 I. s! g: y2 |! K8 k% i        do {
    1 l. G* L4 O- y& Y                write_time = jiffies;
    4 |5 ^( b- E5 ^- j' E                if (at24->use_smbus) {& a$ R& S2 E9 F
                            /* 使用SMBus协议发送*/
    $ k3 X& s/ |! G                        status = i2c_smbus_write_i2c_block_data(client,3 H2 X* C# J0 Z
                                            offset, count, buf);7 P& w" L; }4 ~. _8 ^
                            if (status == 0)
    1 o- e3 I1 p" z7 ?9 w$ W                                status = count;1 k  G  G4 g, s! @+ O. e5 o
                    } else {
    - S' {+ j7 e- F( \% C% m                        /* 使用I2C协议发送*/
    # Q6 h; U# L7 S                        status = i2c_transfer(client->adapter, &msg, 1);9 P, |+ Y- @& I; A$ t7 W% m
                            if (status == 1); @/ S$ F1 G8 d* {0 U2 j' J* _5 H. `
                                    status = count;/ ^2 I9 l1 z/ s' V  W
                    }- c- N- C) G: q% t
                    dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",; g! W# D- H! R! A9 v
                                    count, offset, status, jiffies);
    - v5 {, D4 f: Z! b( x5 Q
    $ B, b8 u3 {0 l                if (status == count)( s" T- ~: G/ s4 T! F
                            return count;  /* 已全部写入,返回*/
      Q0 h8 y# Z3 |" T. ]" \5 _  E6 h' S% C1 M7 E" B$ y- v
                    /* REVISIT: at HZ=100, this is sloooow */! L6 N8 H" y% M) n
                    msleep(1);
    & k; [( X. z) F+ _, s, ~        } while (time_before(write_time, timeout));  /* 使用timeout */# y6 i3 [6 k7 q$ F$ e! e' |
    5 u) A6 b0 [# T2 Q* ]# m! F1 ]( h' K
            return -ETIMEDOUT;  /* 超时,返回错误*/
    # t3 B) Y( H5 V7 i+ K+ c2 g}
    : c2 A9 f1 [/ b' B
    / g& n2 |9 E& Q4 S5 q) F, e' _该函数首先调用了at24_translate_offset函数,来获取地址对应的client:% e% [6 E. H$ l% @  S7 o* g: Q
    /*9 a! X- l# h- u
    * This routine supports chips which consume multiple I2C addresses. It
    # x* R; M1 m+ `& }7 c$ j * computes the addressing information to be used for a given r/w request.
    . v# t& q1 |. o* N* B9 D& b5 r' G * Assumes that sanity checks for offset happened at sysfs-layer.
    . i  j- _; @: h( O1 C */4 H; l( S8 A' `  @3 D% x
    static struct i2c_client *at24_translate_offset(struct at24_data *at24,
    ; j& W0 l1 s; m- a% a                unsigned *offset)
    ; S6 r) k7 i7 r/ K{
    0 a- K  i. u2 w. h8 q+ K9 i* W        unsigned i;' D: [- w7 w3 G# u1 b% i0 h) _; P
    . A  b# W' Q" p) Z9 i/ @2 k
            /* 有多个I2C设备地址,根据offset获取该地址对应的client*/
    + i$ y6 g6 R2 K! A  H* L8 _9 T: ^        if (at24->chip.flags & AT24_FLAG_ADDR16) {
    5 K) U' n+ _0 c# s, [                i = *offset >> 16;2 l3 q  |5 p7 O' k! B
                    *offset &= 0xffff;
    2 [; Y( e; Y! P6 \3 t+ L        } else {" B# L# q  O. t/ k
                    i = *offset >> 8;
    # Q: S. n! Z3 E: W, i) ?! y                *offset &= 0xff;, d& Y' E" A& f; ]4 ]
            }1 k- Q0 x) |& L. o+ k$ A

    1 G, M: i9 @, P* J3 a        return at24->client[i];
    / j- F* X$ {# z( H& S}
    6 w" I/ X2 o/ {$ S7 D9 Z: T! ]7 Z, Y8 ~. P* c
    然后,对写入的字节数(count)进行了调整。
      Z" b' T6 {/ n! f+ Z2 o随后,如果使用I2C协议,则要组建msg用于发送。8 n/ A! x& u6 u6 ]! d

    1 F6 Q4 N9 g: t( z最后,根据使用I2C还是SMBus协议,调用相应的发送函数来发送数据。
    ' O, ~0 D6 f3 N/ M( ~5 w% m* m1 X4 d+ X6 z
    注意的是,这里使用了超时,超时时间write_timeout为驱动模块参数,可由用户设置,默认为25ms。如果发送超时了,while循环将终止。+ [5 @- R" l2 i  X- n+ s( X
    . B- `) H; z, c
    至此,at24c02的写入过程就结束了。
    4 l! u/ u5 a1 f8 \- d; d7 ^) g5 [7 e3 a9 X$ o* q  U
    4.2 读函数(at24_bin_read)
    8 C, H" A! D: x* M  写函数和读函数非常相似,只是在使用I2C协议时,组建的msg有所不同。同样读函数也使用了超时。
    6 _4 S. x" R& K. u0 {
    5 q0 k. O- y1 P4 t: ^  因此,这里仅仅给出代码:* }' \+ Y- c1 p% @  {2 Y
    /*
    ' j9 o) j: G* }0 Y7 [$ i+ _ * This routine supports chips which consume multiple I2C addresses. It" ~6 M/ }- H0 O7 K1 R/ b
    * computes the addressing information to be used for a given r/w request.
      e0 B2 Y$ S) i/ C. _$ @7 a * Assumes that sanity checks for offset happened at sysfs-layer.
    % }3 e; S1 T) F" \ */6 }+ X: V# W: P& n. `0 t- H: c
    static struct i2c_client *at24_translate_offset(struct at24_data *at24," M4 c! @8 O2 k0 y
                    unsigned *offset)
    ; C8 q& N" w- M4 u; A8 Z- g, R{4 {# c5 l* j5 V! q! _
            unsigned i;
    0 M, t8 u/ D& y. N0 A% H
    ! p( y" q# b( l3 W        /* 有多个I2C设备地址,根据offset获取该地址对应的client*// ]; w1 |0 g) V
            if (at24->chip.flags & AT24_FLAG_ADDR16) {
    * d: `1 V) C1 |- R' k                i = *offset >> 16;" ]% X" l# S" Q
                    *offset &= 0xffff;
    7 |. f, i8 I9 j2 L' o: i        } else {
    - M6 `2 Q% p  R. B2 s  @% d: F                i = *offset >> 8;
    / b) Y2 U+ f/ E                *offset &= 0xff;2 O1 X+ D( d; t+ V! u! {) @) M$ B7 |
            }
    ; k6 c% ~/ Y3 G) \
    6 l: {0 k$ l3 q6 `, B        return at24->client[i];
    3 ^# Z( U1 @8 Q2 z9 a' {}
    * m$ P& X6 A3 U$ j
    + o$ b9 g5 z  rstatic ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,4 l6 j4 s9 ^- B3 }  h9 `, d- X7 i
                    unsigned offset, size_t count)
    9 f2 G% h7 c, ^% |% K) t{  N  c8 b: J4 e' b- w
            struct i2c_msg msg[2];2 f6 |6 Z& r) f: w4 c. Q
            u8 msgbuf[2];
    , t6 I4 i9 E+ P9 A. h! w        struct i2c_client *client;9 F0 R+ \6 Q) n$ e( Q
            unsigned long timeout, read_time;
    / W2 R, `8 \# q: P3 Y( E        int status, i;, v0 B$ V1 s$ i/ }+ J

    / `) v+ u! X  l+ Z+ J        memset(msg, 0, sizeof(msg));
    $ D0 ]( C: F; S' U0 e# o: R, V8 s6 E
    - Q7 n! p6 }7 u. Q* O# h        /*6 @% a: q, l) C8 g5 n
             * REVISIT some multi-address chips don't rollover page reads to
    ( S: Y" x" P* n7 p) t         * the next slave address, so we may need to truncate the count.5 k2 A; f2 ~: R6 @$ W% B' i
             * Those chips might need another quirk flag.% F' B7 f2 ?( k- w1 @9 ^
             *
    * t6 i" [" l1 w* P. p3 }         * If the real hardware used four adjacent 24c02 chips and that
    $ y4 g( Z  E' ^3 }" g& K: A         * were misconfigured as one 24c08, that would be a similar effect:
      X7 V8 ~- F+ c% n/ Y         * one "eeprom" file not four, but larger reads would fail when
      T  R. k" M# h9 g- l         * they crossed certain pages.
    4 Q! I1 f; \! z% W8 [         */
    2 z3 G$ z* A! D2 m% k# |+ d; Q) X  ^) C$ t- m* V
            /*
    8 Y+ Z0 [( y0 b) b% ]& i+ T         * Slave address and byte offset derive from the offset. Always
    : C9 S. h5 Q6 R) D" C, t5 b. N8 |         * set the byte address; on a multi-master board, another master. s0 j4 A* ~4 m% s0 h
             * may have changed the chip's "current" address pointer.
    9 O4 N$ m8 {; F3 x6 s         */# D, _5 b, n1 z
            client = at24_translate_offset(at24, &offset);  o! @# [) l( t

      Y& ?. ~( \; d        if (count > io_limit)8 b/ s- k0 K; j1 C
                    count = io_limit;$ R6 c( W; P5 b) q! H0 ^+ k8 [

    1 u# w! X5 G5 J% A% |+ r" |        if (at24->use_smbus) {, e8 c0 B. L/ o
                    /* Smaller eeproms can work given some SMBus extension calls */$ n1 T0 I/ m' J( I( E
                    if (count > I2C_SMBUS_BLOCK_MAX); f1 o2 u  `) Y% B9 W
                            count = I2C_SMBUS_BLOCK_MAX;% O" x4 `  m: o  C) n6 n
            } else {
    * d0 w0 N- Z+ f, q  B8 E1 D3 q7 k                /* 使用I2C协议,需要填充msg*/
    6 n; F  K9 I% B                /*
    - c5 b$ C! X9 c6 F/ ]0 M/ T                 * When we have a better choice than SMBus calls, use a
    ! |- v/ j' E3 S( [0 }                 * combined I2C message. Write address; then read up to2 `6 r" L) Q+ [7 l
                     * io_limit data bytes. Note that read page rollover helps us6 |0 Q; F' G. F, s- R3 {. \: Q
                     * here (unlike writes). msgbuf is u8 and will cast to our
    & u# [) J1 I0 ]8 R4 N                 * needs.
    6 p7 g% Q, f; \1 m+ S( [9 q. w: B                 */
    + q+ Q9 \" j3 g/ m; u) R9 {                i = 0;
    + H7 e/ C* O' Q8 E8 e                if (at24->chip.flags & AT24_FLAG_ADDR16)- Z2 W' V6 r( D, r- z& l
                            msgbuf[i++] = offset >> 8;# m* S5 v$ {, T, F
                    msgbuf[i++] = offset;
    3 c* `% k& w( \" B5 V
    ! J" M' G  s+ Z9 l( h                msg[0].addr = client->addr;
    * I- T! v, {* Z3 U! l                msg[0].buf = msgbuf;
    ; N0 V' k7 v3 s7 t                msg[0].len = i;
    7 K/ x% z6 Q. J7 m" {- p; |
    : l4 c, W7 `3 W                msg[1].addr = client->addr;
    # v/ \: N  Y6 G( H" {/ n2 Y; B# \                msg[1].flags = I2C_M_RD;  /* 读模式*/9 W. F1 J0 t2 G) Y. D
                    msg[1].buf = buf;! N$ v2 q( v5 e- `0 A3 z
                    msg[1].len = count;* u. G, X4 C+ _: y5 T
            }+ o) c* ]9 r% B; q
    ; d5 v4 Q' j+ W. G( ~% F
            /*
    1 s# T  x. Y0 ~4 w& Q; |# T         * Reads fail if the previous write didn't complete yet. We may
    1 o- p1 m) j# ~, S$ g# F         * loop a few times until this one succeeds, waiting at least* n4 M% L6 {* J1 D9 H# v+ T
             * long enough for one entire page write to work.
    # q3 b  r  `% F         */
    , M# q  b4 Z" P$ }        timeout = jiffies + msecs_to_jiffies(write_timeout);2 ~. u  h  q5 d0 B2 e
            do {
    2 a1 I: b6 d5 Z) E  O                read_time = jiffies;1 Q( ^. P4 b- t8 \  S
                    if (at24->use_smbus) {1 H, g2 j8 T8 U2 A  _" p( R
                            status = i2c_smbus_read_i2c_block_data(client, offset,
    : u( N& ]6 I  l4 E% Q9 j4 D" K                                        count, buf);7 J( e2 S+ C- f% V# l
                    } else {& q8 q1 ?9 m- I' n4 N5 u+ D
                            status = i2c_transfer(client->adapter, msg, 2);+ X4 P( a* M9 B
                            if (status == 2)" \1 ^( M+ F& o+ ^3 Z7 g# z- U+ F
                                    status = count;
    % m- h/ D- w& F                }
    - u0 ~0 m, u' @7 k/ B0 G7 S                dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
    9 b. g5 b6 F$ Z. i                                count, offset, status, jiffies);, {( x2 k1 Q+ Y

    + @8 @  @8 h6 }                if (status == count)
    : O: w. i8 `) S% F* Z                        return count;
    + O4 D3 N: i: ^
    # @% M/ I" x; N6 Y) o( {1 s& O                /* REVISIT: at HZ=100, this is sloooow */4 a5 |+ g) j+ k! b8 _/ W. V* Q# I
                    msleep(1);9 K( }/ [8 n3 f* [6 q; c; O
            } while (time_before(read_time, timeout));  /* 使用timeout */
    ! g) a# `; m# g/ P; Q) j1 X
    5 E$ j8 X  Y, m  }        return -ETIMEDOUT;
    " @' Y1 A4 t# R) v3 R5 x' X; q}! D) S( L2 I! h" q( z# ?( V% j
    0 e$ L1 ~! Q1 Q. P& z1 W; j
    static ssize_t at24_read(struct at24_data *at24,
    - j7 _5 P& v, @4 C' [- L                char *buf, loff_t off, size_t count)
    5 [3 w, x7 X& G0 u% i{
    9 J+ i# q" ]: ~2 _/ i7 N        ssize_t retval = 0;; c3 k9 Y3 a# [: @3 `3 d

      }4 H# J1 j& }" Q        if (unlikely(!count))
    0 y1 J; O6 x4 h+ B( P6 u                return count;' v) C# d0 }+ s9 U6 E, C! O
    : F4 e) I$ a( r- b
            /*! E9 ?3 o8 Q# h9 {2 S8 T7 M9 c
             * Read data from chip, protecting against concurrent updates/ N8 D( M- \0 v) k( ?
             * from this host, but not from other I2C masters.  n* u% I% D  y0 P8 Y. Z
             */: t( d  E2 M: y! s5 Y+ t: @
             /* 访问设备前,加锁*/: ?9 s3 q- r. x* R% b, C
            mutex_lock(&at24->lock); - f0 E: U1 N0 P  D1 `0 E

    3 v+ I" f) l; c0 M$ n2 c        while (count) {8 ]( V- ~3 E+ L0 I
                    ssize_t        status;
    ! D: r  p4 s4 U% k- x  H+ F' P
    " x3 ^! p' y, l8 J. @                status = at24_eeprom_read(at24, buf, off, count);8 ?$ @0 ]- H1 t$ o3 y
                    if (status <= 0) {
      O; N* s: x' U2 `6 w) j                        if (retval == 0)
    ( x- f4 {9 e7 _" g; x                                retval = status;# d0 B1 E" S7 V% O3 z) O* z, Q
                            break;
    5 }' p- J9 h; H5 i1 ^                }4 C/ I9 n  X/ [( u) ~( Z, I
                    buf += status;
    0 H4 z, N7 _7 ?0 w# g                off += status;
    % Y, J5 v& i9 U5 b                count -= status;
    % M/ Z! B. V% N                retval += status;
    6 F  b1 a4 `; ^. @/ j        }; Q( u. F9 ]3 D" Q

    . V6 a. U% r5 Y7 m9 k        mutex_unlock(&at24->lock);
    8 i2 O9 E4 t$ z  Q+ O! p( V
    5 P6 E0 d! m& _; G" x        return retval;* g4 h  V3 a* x7 F) e' y  B" ?
    }
    8 x  @" ~3 ~! a: k1 K& Q; P* w
    , }$ C! e" v* r  C: fstatic ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr,
    3 b9 C% O9 d. u7 ~                char *buf, loff_t off, size_t count)1 e6 ]$ L! N* P3 P0 J9 Y+ A' x
    {: c& C& z/ {: E& f9 M+ |
            struct at24_data *at24;
    8 b4 q5 L; t2 q/ ?7 `. b
    3 I* G% p& j1 `: u2 z" s        /* 通过kobj获取device,再获取driver_data */! `. J6 n1 K  `
            at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
    $ W7 e5 _3 C# E4 W9 S5 G        return at24_read(at24, buf, off, count);, X, @! F8 r6 r" X# Y+ I, o% p
    }
    % @6 \8 ]) X( A' R6 S& g) s6 W. e' X0 g
    5. 总结& U; ^( [8 M) Z: G
        本文主要对at24c02的驱动架构进行了分析。该驱动基于i2c总线架构,提供了id表来帮助设备驱动的绑定,该驱动支持AT24CXX等多个系列,不仅仅是at24c02。+ V: M  d6 I. H

    , ]8 f' N2 h0 a4 [2 m8 g其次,该驱动并没有注册任何字符设备或者杂项设备,而是通过sys文件系统的二进制属性文件来对设备进行访问。此外,驱动同时支持I2C协议和SMBus协议来访问设备。5 `# v8 |1 K( f+ K
    0 X* Q4 m, O- [( a/ j5 ~

    4 `; B( }& t+ F7 A( M7 G4 Q, ^: K* {0 q' t/ \
    0 U* k$ Y: x4 A$ ]& G! S4 o

    该用户从未签到

    2#
    发表于 2020-5-29 13:19 | 只看该作者
    基于S3C2440的嵌入式Linux驱动
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-26 03:24 , Processed in 0.203125 second(s), 24 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表