找回密码
 注册
关于网站域名变更的通知
查看: 263|回复: 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接口来访问。
    : r  F$ b* Q0 \
    " S# |9 C- P% T  n7 e在开发板中,使用I2C控制器0和AT24C02连接,这里就不给出原理图了,如需要,可以搜索TQ2440开发板的原理图。
    7 v) L7 a+ F3 t2 C
    9 f* d6 a1 v7 f: d1 l4 Z: T& B目标平台:TQ2440 # A% N. {3 L; ?) T: I* d: I- e
    ! l7 J" v: G  ?
    CPU:s3c2440
    ; _7 k% i& `7 O: P( L
    . I  j  J" M- p7 N内核版本:2.6.32
    ) K; [, b3 p& b( Y5 O8 ~9 B8 G( P
    本文所有的代码均位于内核源码:linux/drivers/misc/eeprom/at24.c中。2 G2 q1 \. W7 ]1 C% D9 S# E8 X

    , v3 B9 N2 Q. y: x1. 模块注册和注销/ c4 x! }- y* G" `! ]
    static int __init at24_init(void)6 c; A4 {8 m# H3 H
    {
    8 u, N5 J/ X; |        /* 将io_limit向下圆整到最近的2的幂*/
      d* p3 S( n* e; Q) ^' \! p        io_limit = rounddown_pow_of_two(io_limit);
    8 ?+ w" W3 W+ @1 d0 J& H, @        return i2c_add_driver(&at24_driver); /* i2c 驱动注册*/( K. L0 M6 G7 ~( `* R5 e* j
    }
    ; L" I0 f+ Q8 n& ~, |module_init(at24_init);
    $ ]0 p. S) ?6 d3 r& k' V
    2 H+ f% h% t# ?static void __exit at24_exit(void)
    : I# o; r3 _2 k( O{
    2 g5 o; _% I" I$ f4 i1 W        i2c_del_driver(&at24_driver);
    / J0 s- W- F9 P0 h  b}" j3 R7 ]5 x9 C& k; f, m
    module_exit(at24_exit);! \) j9 A8 h) J# Q% d7 d7 Q
      G) H2 }" G: T
    MODULE_DESCRIPTION("Driver for most I2C EEPROMs");
    7 |3 v/ Z3 K3 \% M2 s) s7 G: UMODULE_AUTHOR("David Brownell and Wolfram Sang");8 [* C$ n9 ]7 g' M/ t' s- `
    MODULE_LICENSE("GPL");! d/ h8 F6 U$ e$ y. u. W
    ) A" y3 A/ I6 G8 f) v2 T
    $ A4 S0 F" U9 |6 J
    注册函数很简单。io_limit为写入时允许一次写入的最大字节,该参数为驱动模块参数,可由用户设置,默认值为128字节。# ]6 t! R7 O; t$ p0 ~
    1 L& X- t% V. q1 h' I
    首先对io_limit向下圆整到最近的2的幂,接着直接调用了i2c_add_driver来注册一个i2c驱动。
    % n6 K! `# j3 O) g+ S9 I
    $ b# E( Q4 ~* b7 u3 v注销函数更简单。注销之前注册的i2c驱动。1 e4 d0 `$ a% M. E

    8 b: T# ?3 W8 L4 [* G0 d) r5 f& e+ F' c  n
    8 c9 _. C2 p+ o- K7 x5 m
    2. 设备驱动绑定
    / B% E' `2 T* i3 G# D6 U; h+ q熟悉I2C驱动架构的可能会知道I2C驱动的match函数,该函数将使用id表(struct i2c_device_id)和i2c设备(struct i2c_client)进行匹配,判断是否有name字段相同,如果相同则匹配完成,即可完成设备和驱动的绑定,接着便会调用驱动提供的probe方法。我们来看下驱动提供的id表。
    / t5 J# m8 l& v8 K$ C" E; ]" O4 o4 b) e! ~$ a

    ; c# w/ V8 z% _+ vstatic struct i2c_driver at24_driver = {
    ( ]9 D) {1 f5 [. {2 y% S        .driver = {7 N, I5 ?' J& |2 N6 S
                    .name = "at24",4 c  C' {. s$ D/ e* `3 U: M( Q
                    .owner = THIS_MODULE,
      c8 Y  R, S# T1 R) I        },* }- U# v! Y4 S% j6 _; a
            .probe = at24_probe,0 e. T) {5 b' {. E. D* J
            .remove = __devexit_p(at24_remove)," N4 n; m' ?) ?6 h  T
            .id_table = at24_ids,
    6 k7 i: H% H9 u7 q0 o};
    7 Z3 {5 T( M# E3 h- D' K6 T  i驱动提供的id为at24_ids,如下:( o: S$ x, ^8 A% N" _
    ) Y- `0 R$ D" F7 K

    2 n: m. m+ q7 j" A# ystatic const struct i2c_device_id at24_ids[] = {% A- W! ~2 o' T2 d8 H. X7 n
            /* needs 8 addresses as A0-A2 are ignored */
    . M, E) |8 q( L8 h: \        { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },; S. |+ E2 @: Q! `
            /* old variants can't be handled with this generic entry! */. }6 S2 p0 C5 F/ N* t# |
            { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
    1 ^. G4 Q0 T" v, U2 ?' R+ L7 p2 \( C        { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) }," V$ m, _( C. k
            /* spd is a 24c02 in memory DIMMs */8 A0 o1 ~: w' ?2 a
            { "spd", AT24_DEVICE_MAGIC(2048 / 8,, F* q& b- Z( _* e
                    AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },& h3 c" r5 B) b
            { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
    * V: k2 }0 A% U1 Q        /* 24RF08 quirk is handled at i2c-core */
    # z3 S" V" `) G/ e" l; F        { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
    ) J) ~0 Z0 m/ \/ j1 c        { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
    " F( C* C9 @+ y* k" |" E3 |        { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
    % Y; l8 W( Z: f0 s$ ]0 _( ]0 ?        { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
    1 j' d& H9 l1 l1 E6 Q& v9 ?        { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
    : f  X- ]" R* Q- S; L" G) V        { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
    , g9 U; D1 ?1 A# r9 s+ s6 E6 F        { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },% W7 f- K# x+ Y2 G' L0 U1 q! T
            { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
    9 b. z9 U9 F/ q4 J        { "at24", 0 },
    / Y3 S1 |; |( m        { /* END OF LIST */ }
    : \; Q5 M  R3 Y- x8 F% o, z. m7 Z};
    - O& x( s6 e1 t! ?+ b/ N结构体成员的第一个参数即为name,表示设备的名字。第二个参数,在该驱动中,为一个幻术(magic),通过AT24_DEVICE_MAGIC宏计算。4 T# J; c0 X; n0 t, O1 z
    : T& {" H8 A" ^. A% p6 I) e% e
    宏第一个参数为eeprom的大小,第二参数为一些标志位。我们看下这个宏:$ C$ p* \+ D; r2 a$ b, {! f3 h
    7 Q; ^5 [, q; W) W1 L+ l

    9 y, A' X# _1 g% A9 m' j#define AT24_SIZE_BYTELEN 5' b/ i7 W; p4 g+ t; l& P1 H
    #define AT24_SIZE_FLAGS 8% u8 Y6 o: ^" Y# M% s2 K; y0 X& `- ~
    ! g7 [! ]1 L8 s! q
    /* create non-zero magic value for given eeprom parameters */
    / p- O3 s( H9 w) F( o5 t#define AT24_DEVICE_MAGIC(_len, _flags)                 \
    7 u: f3 A: g5 p6 V        ((1 << AT24_SIZE_FLAGS | (_flags))                 \9 [: m/ C, [' A) T( }) J
                << AT24_SIZE_BYTELEN | ilog2(_len))2 P; j6 ^! m8 w
    在这个表中,针对这里讲解的24c02,其大小为256字节,标志位为空。
    * Z- B6 z& t0 m9 w; F
    * S: Y! b; z" c! [1 P2 @4 @# N/ z2 A2 f+ O+ Z1 \  Y" P" L1 ?8 W. B
    " A- Z2 w0 r$ C
    3.probe函数
    # \5 W' J3 [8 g5 U( `, `    当i2c总线完成设备驱动绑定后,就会调用probe方法了。具体看下这个函数。* J* @3 f) e0 S8 y6 P3 U
    ) [8 {6 i; n- B& c6 k5 P1 {" h

    ' {' y0 o# @* p) ^' C* Z' ?7 Tstatic int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
    3 \, E# `9 @% E2 N{, ^# d2 H( z- k: b
            struct at24_platform_data chip;
    5 d/ }" R7 Y) V: M) g/ X6 r3 f- Y        bool writable;
    3 g! w1 j; n( T3 ]) x: y        bool use_smbus = false;4 A. r* X4 o# G) G" C
            struct at24_data *at24;: {6 a' P6 u# F9 H. Y! _
            int err;* `( N1 p. v# }. D8 J2 `& S  m& E
            unsigned i, num_addresses;; N; J, ^! c3 ]0 \0 K
            kernel_ulong_t magic;
    9 M& W0 f" R* o3 Y0 d6 Q1 g1 B/ x  b9 ?) p2 ~% w7 k
            /* 获取板级设备信息*/
    0 d3 w1 c2 @% @! ~' u- B* A9 ~, _' H        if (client->dev.platform_data) {
    - k; v* q7 w+ q! o" I3 k6 M6 C                chip = *(struct at24_platform_data *)client->dev.platform_data;
    1 @% n& {2 t" t7 z        } else {
    ' h$ p0 J6 b6 s* @  z                /* 没有板级设备信息,也没有driver_data,直接出错*/
    0 i( _( z( c9 X1 ~                if (!id->driver_data) {+ ^# n1 w0 [- p
                            err = -ENODEV;
      j! N4 O. O2 T                        goto err_out;
    5 O- J: V! @# y                }/ l. _- R& r! k& |5 q" [
                    magic = id->driver_data;( {' H& Y( K/ Y  y
                    chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));. a8 L$ a5 A3 S' o
                    magic >>= AT24_SIZE_BYTELEN;0 {/ i6 O$ M3 A# z) P; l
                    chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
    9 C. r- o7 m+ ?$ n+ t* e) W                /*! d; |9 z& c1 f
                     * This is slow, but we can't know all eeproms, so we better! W2 G+ v4 t: y4 c7 O8 L
                     * play safe. Specifying custom eeprom-types via platform_data
    # v; @" t% g2 d# P, H                 * is recommended anyhow.; |( |2 M1 I! [4 X' L& W$ Y+ w. R
                     */% U) o" J4 b- d) F( {% X! L; K3 z
                    chip.page_size = 1;
    & ?3 E! s/ Q0 J; ?6 O3 Z
    ; }& B9 X2 m# Y& J# }! Q                chip.setup = NULL;
    0 e. T; ]3 e' B" _                chip.context = NULL;; f5 J  D, C) q! c3 k$ L
            }
    8 p4 w' K( R' W" h4 \0 c
    & V3 Y8 A9 i( M" j        /* 检查参数,$ W4 Q4 ~3 |4 I, o. I
                byte_len和page_size必须为2的幂,不是则打印警告*/
    : S2 l, J0 _3 W' ?) t  H( z" U        if (!is_power_of_2(chip.byte_len))4 I& H3 L+ V+ s% i2 @
                    dev_warn(&client->dev,
    ) w' m4 L( `) P+ @# A7 `                        "byte_len looks suspicious (no power of 2)!\n");1 E( L0 e1 {0 u
            if (!is_power_of_2(chip.page_size))
    ' P3 X( v( Z# X6 o8 C1 B  S' e                dev_warn(&client->dev,
    ; W, h7 _7 V3 |. {1 c/ a( b. @, \' @                        "page_size looks suspicious (no power of 2)!\n");2 {3 K7 D: g2 ?; S. _: |

    7 E6 A7 `* P6 i/ Z7 `2 W! n4 C        /* Use I2C operations unless we're stuck with SMBus extensions. */
    ' |8 }; ~) u1 ~  m$ g1 N        /* 检查是否支持I2C协议,
    : m+ s: A9 M' z            如果不支持,则检查是否使用SMBUS协议*/
    0 f: u, ^1 a# N) F' ]6 n5 ~( n        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {  a# B1 K& W9 e8 X# v6 q8 r
                    /* 不支持I2C协议,但是使用16位地址,出错*/& Z# d  I" E- o% R6 v" ~- M
                    if (chip.flags & AT24_FLAG_ADDR16) {
    ' k. I# _) o; W                        err = -EPFNOSUPPORT;2 N; \  Z2 V& f7 |
                            goto err_out;3 S9 ?; A# s. ?! P
                    }
    5 u! K2 U2 O9 f; j2 X% j                /*  不支持I2C协议,使用8位地址,+ D$ b) i/ t9 }  M1 n5 J8 ^4 Z7 w) j/ U
                       但是不支持I2C_FUNC_SMBUS_READ_I2C_BLOCK,出错*/
    1 Z3 q  y6 q; h3 P4 y1 A                if (!i2c_check_functionality(client->adapter,
    " ^: ^- X. ]% M- L& G7 L) T# F                                I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
    - w! p5 Q5 o* e9 A7 U* j" f9 s* R                        err = -EPFNOSUPPORT;
    : A9 f8 [' z8 b& j                        goto err_out;1 F8 k5 d+ N5 g
                    }6 T$ r# y' N# }; G
                    use_smbus = true; /*使用 SMBUS协议*/
    0 ?5 o; L7 m$ B9 A* G# h# ~        }4 s% Q, w  t* B3 _: r5 \8 V

    & g( R: l. |* E) |0 O        /*是否使用8个地址,根据id表,2 T& k4 [: z/ \. `. M
                目前只有AT24C00使用8个地址,其他都为1个*/2 V$ M5 H' D* A3 Z
            if (chip.flags & AT24_FLAG_TAKE8ADDR)1 V- `) L" C% f
                    num_addresses = 8;5 T5 k, s- |! U; d, V
            else
    ) `, q# A9 m2 n3 \$ y8 J/ m4 M                /* 24C02需要1个地址,24C04为2个,以此类推*/$ O( n& _7 X( T# _9 [) u" u3 j
                    num_addresses =        DIV_ROUND_UP(chip.byte_len,; t( U3 q, r  N8 e1 s) _
                            (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);* o0 B3 ~4 W9 ]7 y: s8 d: [

    9 \/ U- ?" x* _; J' k        /* 分配struct at24_data,同时根据地址个数分配struct i2c_client*/
    2 b( C0 T$ S5 M9 _$ T/ o& G" H9 O- i        at24 = kzalloc(sizeof(struct at24_data) +
    ( n8 x8 T; o% N                num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
    " C( b% V5 O+ `) M2 K% O) W  j        if (!at24) {# E2 m; A3 W/ |6 ^6 h8 C8 Y
                    err = -ENOMEM;
    $ g/ o+ H+ v  k" `8 Z3 |9 [  l                goto err_out;9 C. J$ ]. g: v3 h- C, t
            }
    + i( g: ]4 A  n) j& a0 P& a9 R, `3 @
    0 t. G% g+ g3 w5 {/ X" f; M/ `        /* 初始化struct at24_data*/
    $ u. `" V; o/ J3 q" f" [1 N        mutex_init(&at24->lock);0 o9 D( |* i" r5 T( W
            at24->use_smbus = use_smbus;# A7 k1 P& ?! W& t) G& w$ u
            at24->chip = chip;2 L) D# y& X( |, o4 v/ x. ?, V4 N
            at24->num_addresses = num_addresses;4 g+ O# F% K6 [4 [

    6 R* n5 o, F. g$ L        /*: @, b9 }; C+ D0 Q) m  ^3 u& G. F
             * Export the EEPROM bytes through sysfs, since that's convenient.
    , Z; q0 S6 W* q9 }% q         * By default, only root should see the data (maybe passwords etc)- a6 K1 h. X# v9 o, R* C
             */
    2 k& q( w7 j; W         /* 设置bin_attribute字段,二进制文件名为eeprom,( f7 v, J% M, K8 k2 f
                 通过它即可读写设备 */$ M0 E+ \! e0 y  q/ {; a; a
            at24->bin.attr.name = "eeprom";3 q: v3 Z# w0 `2 j) X" Z5 k  D
            at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
    : |  V, o- u( F9 ^$ v. o        at24->bin.read = at24_bin_read;9 E; F7 b! b. n% v, C2 b
            at24->bin.size = chip.byte_len;7 w" k% y- u0 b* B- G+ u: e

    ! f: |' a9 r+ `# y9 B        at24->macc.read = at24_macc_read;  /***  先忽略***/! R3 B/ M' I: j0 R! s( X9 ?  X: Q
    ) e9 s4 B3 X9 B( X& {+ i5 s
            /* 判断设备是否可写*/6 I3 D0 Y7 V; R9 S" m
            writable = !(chip.flags & AT24_FLAG_READONLY);
    & \1 u( b6 l7 V2 z9 I% g        if (writable) {
    5 b3 H5 L, V5 X& y                if (!use_smbus || i2c_check_functionality(client->adapter,/ {. a/ f% w/ G
                                    I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {$ h( s4 V% }3 E/ l

    ; E" d( P% S" {' f% t( ~                        unsigned write_max = chip.page_size;1 r6 \5 n+ K7 S# m
    8 R* N4 ~, t: ^8 |  i
                            at24->macc.write = at24_macc_write;  /***  先忽略***/5 j$ l+ m: P' B& f$ d

    6 H' e( [7 {, E! R; C/ f                        at24->bin.write = at24_bin_write;    /* 写函数*/' i- l- j4 X: ]# H2 n6 G
                            at24->bin.attr.mode |= S_IWUSR;    /* 文件拥有者可写*/
    % w* w( p& q! X5 g4 t+ _+ N) j" F7 @+ ~: \  B
                            if (write_max > io_limit)  /* 一次最多写io_limit个字节*/
    , t0 b# z* x* l! ~& c                                write_max = io_limit;
    % [7 z+ U  |  D: k- v% {1 U                        /* 如果使用smbus,对write_max检查*/
    7 ~$ c6 g" a$ M% U- j3 l- g                        if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
      |0 f3 f5 E/ L2 X+ |                                write_max = I2C_SMBUS_BLOCK_MAX;
    & F! O3 p6 J2 h                        at24->write_max = write_max;    Q* I" X( d( Z  Z+ c8 B

    " ]4 M! \5 D1 K0 @& ?9 L                        /* buffer (data + address at the beginning) */
    . q- R9 w" _3 L0 W                        /* 分配写缓冲区,多余两个字节用于保存寄存器地址*/6 g! Q* O) ~* t, h8 t) B
                            at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);' M! b( P$ T  x( a1 l9 N! Y8 d! l8 z
                            if (!at24->writebuf) {( I3 ?- x/ n5 l* y  ]. P& I
                                    err = -ENOMEM;
    # b) p, R3 V2 F8 L( x                                goto err_struct;/ ?7 D) j" d, o: }3 s+ z
                            }, D% }: X- E4 Y( r( U. |
                    } else {
    1 J4 j  c  @2 Q, C                        dev_warn(&client->dev,. N" d, R' w. J3 T
                                    "cannot write due to controller restrictions.");
    0 i9 m/ W( y; G, P, {$ L$ ]3 ]& l- u                }
    8 ]1 q/ |& ~9 \! I- i. p        }/ r/ t. t+ E1 @' j7 L: `. m8 a

    % @% z+ A6 C; d- R        at24->client[0] = client;  /* 保存i2c设备client*/
    ' Z9 n1 h& M; S% L( W. M: f
    : j' ^. C- X: U8 N( N" z2 J        /* use dummy devices for multiple-address chips */) A6 s$ k9 C% u
            /* 为其余设备地址注册一个dummy设备*/& z7 ]+ b1 ~* d4 Y; [3 g) s9 C$ o
            for (i = 1; i < num_addresses; i++) {7 i/ f% c3 h# T* e8 Z  b
                    at24->client[i] = i2c_new_dummy(client->adapter,
    : ?/ P9 k* D7 c; @/ G% ]+ Q                                        client->addr + i);     /* 设备地址每次加1 */
    & y) F9 G  I& z: H* w4 Z% a                if (!at24->client[i]) {0 B, s& I$ m/ e. k6 W' l/ m  f
                            dev_err(&client->dev, "address 0x%02x unavailable\n",% I; ^* a( J, E; X
                                            client->addr + i);$ T/ c( y9 t1 Q% r& Q; p
                            err = -EADDRINUSE;$ w4 ]8 i& G2 ], X1 q9 z
                            goto err_clients;
    7 m- i# p# j* L5 B                }
    7 D1 w4 v, B3 X7 ?        }, [, Z9 u6 h. a, p% N, H
    . c4 \: l5 [, [
            /* 创建二进制属性*/
    % `3 _+ O) Q% H* D        err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);$ ?. A) L, ^. K$ ~6 W4 J
            if (err)8 z+ @. u0 g6 z( i, \
                    goto err_clients;
    9 g: M/ B* U, X5 d! V* D1 Y, m& u; s4 h: W) Q2 A
            i2c_set_clientdata(client, at24);  /* 保存驱动数据*/; J/ I4 ]$ t/ u( w9 z/ w- b

    ! L; ~1 I# g' H" I/ U9 `  y        /* 打印设备信息*/
    5 i( ~8 @4 m& O4 d        dev_info(&client->dev, "%zu byte %s EEPROM %s\n",
    , v& k+ q& z* a! C) x: B! O                at24->bin.size, client->name,+ b" _: J+ U; V* G( t: `
                    writable ? "(writable)" : "(read-only)");
    ! R3 Q$ f% ~8 p- m7 k% M        dev_dbg(&client->dev,, }5 j: k$ ]8 D" M; Z/ E7 j+ p
                    "page_size %d, num_addresses %d, write_max %d%s\n",+ P# a( L9 _: y8 O, B
                    chip.page_size, num_addresses,, K- ^3 e# `+ v! j" O& |
                    at24->write_max,
      ^& b# c6 f2 }) [                use_smbus ? ", use_smbus" : "");
    & I4 _4 o6 ?. _7 K, O) H, m- ~) I- J5 ?
            /* export data to kernel code */
    ; b& X7 n( V8 j5 i! F        if (chip.setup)
    * C  `0 \+ F$ R1 c/ i; v2 C: O4 ^                chip.setup(&at24->macc, chip.context);: u- T1 T. U) W' L# R' m5 S4 `' j
    - J% I) Q, A: R" p. L
            return 0;
    3 [5 e  q, g# W9 U* e, F3 S
    # v% @$ M' a$ O, J6 k0 ^err_clients:/ I7 G+ V) f9 B/ P
            for (i = 1; i < num_addresses; i++)( `/ H3 A* T& Y9 a& L/ }
                    if (at24->client[i]); Q; F3 L: G8 j+ n
                            i2c_unregister_device(at24->client[i]);9 W: U2 X7 ^" A& Q/ b* v; M

    2 N/ G! W/ G/ {. Y        kfree(at24->writebuf);' u8 o. F8 H4 P/ J$ W' f% O
    err_struct:6 @2 l  ?/ w' e
            kfree(at24);) i. L: X" V7 w7 o/ J) @' o
    }
    - E/ D4 n  z4 b- i* G& [驱动首先获取板级设备信息(client->dev.platform_data),我们假设驱动移植时,添加了该板级设备信息。$ n" z/ r( Y* i- C4 n9 l, R
    判断是使用I2C协议还是SMBus协议。在这里,I2C adpater使用I2C协议。
    ' R& n9 w% ~& T6 E8 z
    , t$ U% ?8 T6 G, U7 h然后,判断设备需要多少个i2c设备地址。. ?! }& C2 V( `# W7 Q+ B

    " ^) s/ X: p) @% r4 P$ W8 n这里补充下:根据at24c02的datasheet,设备地址的第1位到第3位,将根据不同的设备来进行设置。6 F7 {8 z/ L, B3 V- l

    . d# L  R3 r; j  f例如,如果是at24c04,则设备地址的第1位将用来表示寄存器地址,因为内存大小为512字节,而寄存器地址只有8位(256字节),, l0 d. ~! n8 h+ v4 U7 K" F
    $ \) x3 {( J. b/ m
    需要额外的一位用来表示512字节,因此使用了设备地址当中的一位来实现此目的。具体的请看datasheet。
    ' P" p5 G4 y1 K' B# E$ }( Y, t
    8 C6 h1 A4 z: x1 z这里使用at24c02,num_addresses将为1。
    1 t7 I/ [( r: s# q% r. P  @3 z- Q7 _: Y
    接着分配struct at24_data和struct i2c_client指针数组空间。
    9 n1 O3 p" w. t6 d: b2 X: U+ {: v7 t+ v& j4 e- Q( a5 r
    然后对struct at24_data进行了初始化工作。
    # e4 U+ N( a- G" v8 c6 O8 A; o
    ' X1 v, _; j1 O; b7 A接着,对二进制属性进行了配置。名字为eeprom,同时配置了其读方法(at24_bin_read),如果设备可写,还将配置其写方法(at24_bin_write)。# K3 S+ y$ y5 g, ]$ v7 c- Z3 v. s, n. x
    ) Q" N8 Z6 p9 ], _; M8 ^, D' x
    接下来很重要的一步,如果设备使用多个地址,则需要为所有地址(除了第一个地址)分配一个dummy device,这样这些地址就不会被其他的I2C设备占用了。: O$ d+ W/ g9 y* q1 Z. d9 a

    9 {4 f/ L  p* ^" T$ y最后,向sys文件系统注册了二进制属性文件,通过该二进制文件,用户即可访问该设备。( e+ Q( x+ Z) t- A0 Z
    % P* e' y$ m- [
    注意:驱动使用了struct memory_accessor的东东,对这个东东不是太了解,所以先忽略,这个东西不影响驱动整体的架构。6 C$ d' `" v; G. D6 i2 X

    4 S: V' T/ H& N9 N# z3 Q' t" q, A" C  R8 x+ O/ l$ i1 z: A! G. ?
    " o5 _( o. \) n0 U
    4.设备访问方法+ W% b% L  V; m- m5 ?; ?: h
       从第3结的分析可知,驱动并没有注册任何字符设备或者杂项设备,只是向sys文件系统注册了一个二进制属性文件。因此要访问设备,必须通过该文件的读写函数来。3 _7 c9 d5 o1 Y6 G: p
    8 V* k* C% C- P/ `% f; _* p
    读写函数在probe函数中指定为at24_bin_write和at24_bin_read,我们来分别看下。9 [, c5 [( O' L7 x5 |9 b- Z

    ; Q2 g7 h+ N: T7 s' m8 X4.1 写函数(at24_bin_write)7 Z7 s3 ~& v4 P- R$ s
    static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr,- I2 {, Y$ Y+ V5 u. H% E) q+ F
                    char *buf, loff_t off, size_t count)
      C. X# F7 J8 G6 l( y{5 H7 h4 b5 C$ G- [) o
            struct at24_data *at24;. }0 k8 c& F8 Y- ~' l7 h2 w

    0 l* Y! `$ ]+ |        /* 通过kobj获取device,再获取driver_data */8 d- Y% Z7 I( m# u: O' n
            at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
    . ^' [' B9 ~+ M; j% Z2 f8 ~        return at24_write(at24, buf, off, count);' R6 d8 a+ j7 D, w) W  ~- v: y6 p3 Z
    }
    1 S" u4 m+ \/ [. Z, a. O' t2 {该函数首先通过kobj获取了struct device的指针,再获取了at24。
    : _# B: L% Z& z0 S接着直接调用了at24_write。如下:; E! q& _# r8 S$ a9 b
    8 k/ c) w4 \6 |$ x1 ?
    tatic ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
    7 s; O. l% z; W* n                          size_t count)' k1 h4 z! u( h% v) ]
    {
    " a* Z2 b5 b6 m2 t+ b8 Z        ssize_t retval = 0;
    " j! q; A" B' g+ j4 L4 |9 K) U& }& ~4 o" X' E2 v9 E" R) R2 C3 j0 _/ J; v% c
            if (unlikely(!count))
    4 i. b: \# j8 T2 _% {                return count;6 A; z( ~- J9 Z" X: n5 H

    ' r2 l5 X* h$ q! t' ?' r        /*# f! D8 b8 _) m/ f, ]" [5 _6 @
             * Write data to chip, protecting against concurrent updates
    ) _" d0 w4 P* c         * from this host, but not from other I2C masters.
    , `/ X2 W1 D4 J/ _1 ?+ `         */
    9 v; |% l$ ^3 i8 ^( U          /* 访问设备前,加锁*/, Q: w! b* x  ]- d6 e" N
            mutex_lock(&at24->lock);" q2 I0 W! N+ U8 G/ p

    - h, x; D% ^6 S" M1 r, ~1 f        while (count) {% E! w1 f8 T0 e7 {3 K8 s( \
                    ssize_t        status;$ @. y# g  L2 e9 B. P
    5 O5 z8 l2 U4 C7 E4 ^1 x- H8 T
                    status = at24_eeprom_write(at24, buf, off, count);
    ' N# w! V0 R! M4 }0 i. C$ g                if (status <= 0) {
    ; l' t$ T: S2 x4 i                        if (retval == 0): T$ c9 B2 F" k& R5 a
                                    retval = status;
    ' q2 e: l; Z+ v4 t3 W' `0 y                        break;
    ! u1 R; O1 n- N5 P                }
    7 b! H5 ^8 h/ n! ~4 x# l                buf += status;2 y! e6 Y; @: F  P
                    off += status;+ i# T" ]. R1 n8 D* D  q8 k
                    count -= status;
    2 f7 _/ H8 s+ t                retval += status;# V. Q0 @$ I) E4 ^1 e' `# v) s
            }
    & A, M( [& d7 ~; s8 `/ W. h; G6 T( i5 k% A: R& P, U
            mutex_unlock(&at24->lock);4 z* e* u8 ^* U/ q0 Q9 T9 s2 h
    4 `2 c% K- B5 }8 @( U
            return retval;
    + F, }3 w* s' x7 O3 C6 K3 a  \" q: E' i}) w  B2 A3 G( j) D
    ) U3 H; o. w. O' w: Y* V! _; L. K& d
    该函数不复杂。在访问设备前,首先加锁互斥体,以防止竞态。然后根据count来调用at24_eeprom_write函数将数据写入设备。  [4 x6 L9 a" g5 y; H6 R8 E/ s4 V
    写入成功后,更新偏移量等信息,如果还需要写入,则再次调用at24_eeprom_write函数。
    5 t8 S6 `6 H* X2 ^4 u) x
    . Y3 [( q5 q# L; W6 O  [( C1 H看下at24_eeprom_write函数:7 L/ X% F9 R9 T+ r* t" L

    5 g9 N+ _3 ]2 t7 L* W; }/*
    . {8 T7 X. @' {& | * Note that if the hardware write-protect pin is pulled high, the whole
    # b2 {* f' s6 G/ O" m * chip is normally write protected. But there are plenty of product2 q; v9 ]" K+ [! A! W
    * variants here, including OTP fuses and partial chip protect.
    " G: F. T0 N7 _; c  }1 F2 T3 o2 i. i *
    ) b) V4 e. k3 i  p * We only use page mode writes; the alternative is sloooow. This routine" k! ?5 c, x3 F+ D6 M) x
    * writes at most one page.
    5 [: H8 C; P% S */
    $ N& J$ @7 [# a3 |5 astatic ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,; l7 ^# ?, o) k  d
                    unsigned offset, size_t count)' s7 r2 U/ o& g# `
    {
    - t7 ^8 ~8 C6 n" U" t        struct i2c_client *client;
    / v% K( S: j) z3 [( m0 O        struct i2c_msg msg;
    : J5 u$ i' C$ }4 w# V  h0 I        ssize_t status;
      @" |5 }1 o9 c- P0 J        unsigned long timeout, write_time;- Z& m# ~: w# Q/ r% g9 X
            unsigned next_page;
    2 d- x8 I/ M6 G2 V9 M% h# p2 n- s5 K3 Y$ Z
            /* Get corresponding I2C address and adjust offset */
    3 c7 L5 R9 @  ]9 y        client = at24_translate_offset(at24, &offset);. J$ z2 Q, k: K# [) I. Q

    % K4 l+ H& H5 d  J6 }9 s* P        /* write_max is at most a page */& a/ ^% ^8 ?2 T
            /* 检查写入的字节数*/
    * r- s  p8 t1 _; z        if (count > at24->write_max)' L) {) X$ @" ?# \7 R
                    count = at24->write_max;
    5 X3 w2 f+ I5 o8 X9 i3 x  @& G, S$ A3 ?+ }0 ^! b: N
            /* Never roll over backwards, to the start of this page */
    ' f! J$ \/ J4 q6 `" v$ F3 @0 k& b& h        /* 写入不会超过下一页的边界*/4 k  t7 v. H, z. d5 _) C' P* m
            next_page = roundup(offset + 1, at24->chip.page_size);7 k* [; q# ]6 ~! m) A- h
            /* 根据页大小调整count*/, b& l7 _  ]8 ]0 V
            if (offset + count > next_page)
    ( C9 Y! y" K' z: H                count = next_page - offset;/ X/ k# Q+ u$ |6 A% J! Z3 J. _
    & o7 l4 o% f* o6 D
            /* If we'll use I2C calls for I/O, set up the message */" j6 L, H6 d& y, w, D
            /* 使用I2C协议,需要填充msg*/) G, s) L- M8 B4 p
            if (!at24->use_smbus) {. e4 [9 R# t9 u, {2 O9 C3 M6 S/ z
                    int i = 0;
    , d  W! [* @1 W- g; T
    & Q& J4 F4 H: m" P  \/ P9 d0 f$ D                msg.addr = client->addr;  /*设备地址*/) L) z$ w+ L5 n3 B+ j( O
                    msg.flags = 0;6 c2 S' a% ^( D. a: {/ V* J
    , |4 q5 K, l9 ?
                    /* msg.buf is u8 and casts will mask the values */; |" j4 A5 t) u: _4 b
                    /* 使用writebuf作为发送缓冲区 *// E5 c* {" M0 m7 ]8 ~$ x3 h% Z
                    msg.buf = at24->writebuf;
    9 \! v& R2 L. N                ( m9 e6 `! y2 z; q* d
                    /* 根据是8位还是16位地址,msg.buf的前一(两)个字节/ Z- l1 w6 T, U
                         为设备内部的寄存器地址*/
    ( q$ Q# f" e4 o3 |' G( `                if (at24->chip.flags & AT24_FLAG_ADDR16)% K* p7 l0 k% L
                            msg.buf[i++] = offset >> 8;  /* 16位地址,先写高位地址*/
    2 A& n5 j  F; D( U, F  P" m  L) s' Y  E" F6 F1 @
                    msg.buf[i++] = offset;) x# Y+ L8 e( k9 N1 q' x+ W* V; h
                    /* 复制需要发送的数据 */
    2 O1 F8 Z: V4 b) b) ?  y                mEMCpy(&msg.buf[i], buf, count);1 P5 M- v* C( t2 ]' {
                    msg.len = i + count;   /* 发送长度为数据长度加上地址长度*/5 Z4 I( ]$ S. F
            }* |: t& r; K/ b$ x1 A5 y
    8 D& i- P/ _& m( v
            /*( W$ {3 j  j4 J& I5 o
             * Writes fail if the previous one didn't complete yet. We may/ j  X: H3 E2 P) Z( [6 I
             * loop a few times until this one succeeds, waiting at least
    - p4 f$ J: e5 s* B         * long enough for one entire page write to work.
    & T$ D5 D  a  W# G7 {! J         */7 c1 m" t5 j$ }1 o( o
            timeout = jiffies + msecs_to_jiffies(write_timeout);3 q, z+ r3 q8 t0 E+ {1 \
            do {5 |4 v5 |7 a! t; r# {6 z! N
                    write_time = jiffies;4 b( K2 z# b& o
                    if (at24->use_smbus) {3 L: b) ^* A% W& \/ S! c% _
                            /* 使用SMBus协议发送*/# ^: f' D' A6 |: W
                            status = i2c_smbus_write_i2c_block_data(client,
    " I2 {, c. r5 n% ^$ T' f) ^                                        offset, count, buf);
    ! |7 h5 D. k- h2 W3 |1 L                        if (status == 0); V5 }7 e2 O+ T
                                    status = count;+ t2 t2 z" N# {. Q
                    } else {
    8 i4 B  B' O% Y6 p                        /* 使用I2C协议发送*/; D8 T. x7 @9 N+ h% d) S/ B
                            status = i2c_transfer(client->adapter, &msg, 1);8 b" P2 N) d' S: I! V
                            if (status == 1)
      n+ e) \6 R' E9 |7 v                                status = count;
    : ~$ s" z/ z# i- N) |                }
    $ e' }: q- T) J2 _2 F2 R3 h* g7 O                dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
    4 q' i2 B# j6 N1 m                                count, offset, status, jiffies);
    7 P- ~0 c# t* b* o2 I+ p9 ~- s1 U- B1 @" _" I% V4 G. A
                    if (status == count)
    : I  i8 q% Y4 b* @4 i                        return count;  /* 已全部写入,返回*/1 n6 ]0 z0 }9 h9 A9 h

    ! g% _6 N4 N. W                /* REVISIT: at HZ=100, this is sloooow */+ a% c; j% y# k, S- I
                    msleep(1);/ B4 \& N* _; k5 v. A6 ?- ^
            } while (time_before(write_time, timeout));  /* 使用timeout */5 p1 e- @4 R2 x7 E' d) ^6 [( O; b. n

    6 L% n& l' x8 c' u# T5 {$ Z. f        return -ETIMEDOUT;  /* 超时,返回错误*/
    ; R3 t$ b7 Y  x" {5 d}
    % e  F. R0 g, x4 l5 x
    0 D2 T6 }- T1 G该函数首先调用了at24_translate_offset函数,来获取地址对应的client:6 D# z. L: U0 ]/ A# M+ }# U
    /*6 m- H& W) C6 d* v- d9 p
    * This routine supports chips which consume multiple I2C addresses. It
    8 g2 H9 l. P7 g. i% E5 g, g * computes the addressing information to be used for a given r/w request.
    ; g6 w& K2 j, |8 q * Assumes that sanity checks for offset happened at sysfs-layer.
    + b6 q# d1 |& E: A */1 ^' j* m3 ^/ L. j! f7 c, B0 t
    static struct i2c_client *at24_translate_offset(struct at24_data *at24,8 D. k& x# x' ^% ^* N* G( v- ~
                    unsigned *offset)
    : ?* d/ @7 s, y- M8 }+ a1 p{; r; Z" ~0 m8 P8 p5 t7 ^
            unsigned i;
    4 N2 Q0 u! Z3 X+ S9 V# S$ @
    ' w$ `1 [% C/ Y5 ^        /* 有多个I2C设备地址,根据offset获取该地址对应的client*/
    $ P9 ?$ d! \; [        if (at24->chip.flags & AT24_FLAG_ADDR16) {! v/ `$ L5 ~2 a; _( Z5 r3 P  ~2 L
                    i = *offset >> 16;
    % A7 X6 ?0 b% o9 a/ v0 c, o! f                *offset &= 0xffff;; I# o3 g. |: h4 H  M
            } else {
    8 h# h& E* P  O% P; S( A                i = *offset >> 8;; f; h4 o) n) z; I% y* E0 ?
                    *offset &= 0xff;
    0 U. P% Q" A( _* X( q+ R. E5 X        }3 f/ M4 d1 N- r3 v, \1 t; A2 `( S

    ; y) N1 A' d" L# `9 F5 [        return at24->client[i];- Q, h$ D" G9 J
    }
    0 Z0 `/ O" u+ E6 B  V) c/ F7 F6 }
    然后,对写入的字节数(count)进行了调整。: P2 g) z% U/ n- S# I
    随后,如果使用I2C协议,则要组建msg用于发送。
    " n' Z& C7 f4 s6 O: [" s* w0 I4 @4 P3 N' H
    最后,根据使用I2C还是SMBus协议,调用相应的发送函数来发送数据。3 K( T0 A  I2 c( d# n

    4 a! N: S% A. i) e& L注意的是,这里使用了超时,超时时间write_timeout为驱动模块参数,可由用户设置,默认为25ms。如果发送超时了,while循环将终止。
    % F6 j) g" E. u
    9 i$ |6 N3 `8 d, r6 X% U至此,at24c02的写入过程就结束了。
    4 x9 r+ y+ T, f7 o: {
    % q8 X7 I2 F: k4.2 读函数(at24_bin_read); w: y4 U( K/ p, k# v
      写函数和读函数非常相似,只是在使用I2C协议时,组建的msg有所不同。同样读函数也使用了超时。  ?! ~3 p2 o( B. M

    , k( }: Y* `0 j! d7 l/ K; H  因此,这里仅仅给出代码:& B3 [- c8 }/ D" F2 ^! Y
    /*% n' a: g6 E+ B! a3 ?; n" a
    * This routine supports chips which consume multiple I2C addresses. It) c6 R. B3 x/ @& P* u/ d2 {0 O
    * computes the addressing information to be used for a given r/w request.
    % c' l1 r/ H$ j8 n4 V * Assumes that sanity checks for offset happened at sysfs-layer.1 K5 n* @$ x$ p- a
    */
    # X' n3 h* l# e% u/ b* lstatic struct i2c_client *at24_translate_offset(struct at24_data *at24,
    5 o' t1 z  B: C                unsigned *offset)
    ; J% Y" P! o- s7 h; F: y* p{
    9 y$ c' M. I& i9 F        unsigned i;
    7 U+ q  I# g. F8 Y3 r1 g! j0 P/ E: |: l
            /* 有多个I2C设备地址,根据offset获取该地址对应的client*/) j3 a3 N9 y: p3 d4 e5 B+ s
            if (at24->chip.flags & AT24_FLAG_ADDR16) {
    * S6 }7 Y1 W9 j, Z                i = *offset >> 16;/ N3 I8 r$ ^/ j, D9 ~
                    *offset &= 0xffff;
      Z, Q# I- ~8 I$ Q        } else {
    : S' e2 T% Z8 S4 o) \' |                i = *offset >> 8;
    $ v" d( {9 Q& S2 h2 }% w                *offset &= 0xff;
    ' p& F% b7 |* [" d8 j- p        }, l4 K8 a$ @% P% S# \0 }! v3 }

    # h% [1 b$ e; C% N        return at24->client[i];; C+ _8 c1 d; u6 l
    }: ?  o- l) d0 k. l

    0 o* y/ v) w0 h9 G" E7 jstatic ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,( }7 p5 P& b0 H/ ?6 f' ?2 A
                    unsigned offset, size_t count)
    0 F+ V7 C1 R& I; W{
    ) }. U- N7 w) Q* m( [1 _! a        struct i2c_msg msg[2];
    / Z% ]* R# w" N$ l* T# s# ~; O3 S        u8 msgbuf[2];
    5 C: \) l8 B: [1 l; J" H% g; q. q! Z        struct i2c_client *client;
    ! n5 }) j/ Y  K7 E% Y9 U        unsigned long timeout, read_time;! ^, w3 Q$ j1 R2 F* Q
            int status, i;. b$ W' @# ~1 L0 i# f% b: I* @
    6 E. C0 p) ~5 G# o
            memset(msg, 0, sizeof(msg));
    ) f) G. Z8 M# i6 X6 D' ^: f/ N3 k. W
            /*
    ; F8 t1 n! h% c3 X0 `  b         * REVISIT some multi-address chips don't rollover page reads to
    5 u$ I& p& o! B- r         * the next slave address, so we may need to truncate the count.
      O& s  c! U' U$ H. @5 i2 n         * Those chips might need another quirk flag.
    ! M3 A+ H0 D7 ~; p! Y         *
    6 O/ M3 U. t, D4 S. |1 C         * If the real hardware used four adjacent 24c02 chips and that4 |- q" m* R9 K! e3 f7 {, J- ]1 w3 p  k
             * were misconfigured as one 24c08, that would be a similar effect:
    * O- J( u8 e& j# h4 @- M         * one "eeprom" file not four, but larger reads would fail when7 W, ]) M( T3 K- l* O. g5 n; @
             * they crossed certain pages.9 v$ f  H/ s, K0 W6 n+ j
             */
    : q, x4 o% k0 N2 q' _5 M0 V
    ( X" X: g/ e# h: V        /** v# x5 {+ U* Y, l  T/ M4 S
             * Slave address and byte offset derive from the offset. Always3 g4 |4 F7 Z  Z
             * set the byte address; on a multi-master board, another master4 ~2 X, }4 D/ i1 J; s. r
             * may have changed the chip's "current" address pointer.
    6 g+ k: A# `* `8 `/ C4 L         */6 ~+ `. ~/ a4 q* f! S
            client = at24_translate_offset(at24, &offset);
      @/ N$ G$ K8 J, N% `$ T
    * R; |* R' M& [1 J        if (count > io_limit)
    / R# A4 H2 Q- J* s, P4 z                count = io_limit;
    # B/ u) \. Y  u# l' D8 Z* u  V7 \4 r0 o8 s$ B- \
            if (at24->use_smbus) {
    : D; w) z1 A; f+ w1 A# M                /* Smaller eeproms can work given some SMBus extension calls */
    5 v2 n/ o) P  F6 P7 E1 ^  n0 b                if (count > I2C_SMBUS_BLOCK_MAX)
    % q; P: u+ B+ y  }. K, u3 j                        count = I2C_SMBUS_BLOCK_MAX;
    / G; @' j/ K& `- Q3 `        } else {4 m4 {6 H+ t) Y% ]: D: e
                    /* 使用I2C协议,需要填充msg*/- D! \$ o9 t5 ]2 G2 I
                    /*7 u  C# W6 W; }$ A7 E
                     * When we have a better choice than SMBus calls, use a
    $ d) M2 F" @& K5 I5 z+ F+ u                 * combined I2C message. Write address; then read up to
    9 w2 u( T3 |9 P% I5 I, y0 |: J8 s                 * io_limit data bytes. Note that read page rollover helps us% }5 j- T/ B2 E5 M+ z6 B7 M1 W- C
                     * here (unlike writes). msgbuf is u8 and will cast to our
    2 h( \8 k4 I) v3 q; s3 @                 * needs.
    / y: Z# b  l  A1 v4 b, n4 E8 ?                 */
    9 m6 ^7 f6 W4 m                i = 0;5 |8 B, e2 D" y) S, _1 i
                    if (at24->chip.flags & AT24_FLAG_ADDR16)5 Q) _0 c  ~9 E6 g! T6 z
                            msgbuf[i++] = offset >> 8;
    6 z  U" O: Y; i2 A* \                msgbuf[i++] = offset;
    5 T1 S. l8 I; F" f% S/ ^/ Y) ], j% ~8 g2 b  V
                    msg[0].addr = client->addr;6 ]3 D( u9 v6 s9 v9 f
                    msg[0].buf = msgbuf;
    9 O5 [9 X! ~4 O$ V0 P6 E! V                msg[0].len = i;
    ) `" i6 G, b( F" }6 A, e4 k; \" C' Y4 T
                    msg[1].addr = client->addr;
    " i6 u, ~5 |9 K" ~4 m. J                msg[1].flags = I2C_M_RD;  /* 读模式*/- R# u+ }& }0 }. {/ }
                    msg[1].buf = buf;2 k' q$ j. L! v1 \( y5 f" J
                    msg[1].len = count;
    % `2 a/ N$ C3 r/ S5 c        }
    $ i1 q+ A9 n! n/ t
    3 Z( H4 M6 z& y5 v) y/ |        /*3 w% H3 r4 _1 \) L' q, B+ N
             * Reads fail if the previous write didn't complete yet. We may) k/ L. A" ^8 ?
             * loop a few times until this one succeeds, waiting at least' k+ M) q! s' G7 Y# w
             * long enough for one entire page write to work.
    / s+ D, r4 E2 y6 }! t5 H1 Z# v         */
    # i# A' r( z6 {4 h+ Z        timeout = jiffies + msecs_to_jiffies(write_timeout);( g. _8 a, k; o1 r. y2 ^
            do {
    8 \! Q& J. y; V* q, v2 B4 v                read_time = jiffies;8 r- F9 S7 y* M' f
                    if (at24->use_smbus) {
    8 ~9 R, P6 O9 d2 q5 G: V                        status = i2c_smbus_read_i2c_block_data(client, offset,
    " F, I/ D- s- }* U0 {& E8 D                                        count, buf);
    9 i& y6 y. d/ Y' P* I( M                } else {
    " v. J5 R$ d# Q5 z$ u8 i8 Q0 B                        status = i2c_transfer(client->adapter, msg, 2);- ]* j7 i* l, A7 n; i0 P6 B; f
                            if (status == 2): f) I0 B5 a9 s& a
                                    status = count;6 \3 C7 ]0 }) _( ?: s. e
                    }! K- ~! N  V+ u( I$ M$ z7 Q. L
                    dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",4 k, m$ v& O8 @0 d/ v; O; g3 s, N7 R
                                    count, offset, status, jiffies);% v0 n* K" r0 z) c* ^& s. H

    ( _% O& b; U- T3 |, n                if (status == count)
    6 |* L% T3 r- G4 r; @                        return count;
    % K9 P0 a- B7 m. s
    + ?2 i- r) v$ S) @% j                /* REVISIT: at HZ=100, this is sloooow */
    9 u! m' Z* J( w2 S' ~6 i                msleep(1);, u( N& o' F. y6 `+ H
            } while (time_before(read_time, timeout));  /* 使用timeout */
    7 Z* @2 d3 B% }7 t
    ! [  c/ u+ k' K! ?& [8 H$ F        return -ETIMEDOUT;, X5 @# c. [: B  H( M- L" d8 j
    }
    2 Q7 X9 j* F+ B" X. |+ ]* u4 [6 u/ k6 @( c
    static ssize_t at24_read(struct at24_data *at24,1 t* o7 s8 d1 I3 R6 _7 N
                    char *buf, loff_t off, size_t count)  }: z$ _7 Z3 d8 v% m4 q
    {% Q- Q% Z  [, E: L
            ssize_t retval = 0;+ V0 o( Y) ?" [- L; c* {) {
    0 K, Q: }5 T2 o: H: J
            if (unlikely(!count)), f4 A# \3 M0 v0 b1 a6 O6 r- ]. `! U
                    return count;% i( h  L* R* M6 x% W+ p) M( U

    2 j8 R; I- L3 K1 A& I+ i        /*2 h5 e9 X: A; u/ W: v8 [; Y
             * Read data from chip, protecting against concurrent updates+ w# w0 g& z4 P' P! e
             * from this host, but not from other I2C masters.
    ; \; E) f0 L. }" E4 E1 d         */1 U2 W" P" |6 T6 N6 ?0 I$ Q
             /* 访问设备前,加锁*/
    ( v0 e& [! c1 Y* B        mutex_lock(&at24->lock); 4 H7 C4 [3 D0 u

    & b- _0 y% I% B        while (count) {
    4 ]& @  r% a- D1 l. _' X                ssize_t        status;
    6 T6 J+ S6 b% i9 E% h: H+ f2 r% K' d' m( i9 F
                    status = at24_eeprom_read(at24, buf, off, count);+ A1 Y6 K- i  a* O$ w* y0 z8 T, n
                    if (status <= 0) {; V# |6 t9 M9 p/ ~7 ?6 Y
                            if (retval == 0)2 @& E& n* j0 K8 t& L( K; u% I
                                    retval = status;
    6 @8 v1 p% V7 z, |' F% A4 v                        break;
    $ K, y8 T( k; J2 `9 Z/ w  x9 d                }7 D8 t1 S% K  d! L( r! W7 D% b
                    buf += status;
    ; c2 Y/ C6 R* a! Y                off += status;
    8 {( Y1 A, ]( m1 L4 k, D) \1 D: y. o                count -= status;
    0 i9 N, ~6 A9 P% C                retval += status;
    ) ?  i, B: e% o2 ^- ~        }6 m- D3 H! }3 @! S! g9 U3 u
    8 i4 x- @8 d" [0 Y. w3 I2 P
            mutex_unlock(&at24->lock);- P  a' M4 b8 l2 {9 M

    2 Q, u& K# q* w* _" D4 _        return retval;
    - f4 A0 [, l0 r9 @# V}
    # u) h  ]+ K: v0 z) L1 L
      _( g+ T) w2 h4 I6 Wstatic ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr,5 L7 Z* T# e: |, G; ^
                    char *buf, loff_t off, size_t count)
    % K' \" ~8 X" \9 c{
    ) e7 |. ?2 Q2 {: I, Y        struct at24_data *at24;
    - A0 O& e  ]; V# F: S8 Q/ |  w) C, W0 d" u6 m3 `5 o
            /* 通过kobj获取device,再获取driver_data */
    9 n2 F% ?- T9 O        at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));' R2 f9 N, x# E7 j) m$ d
            return at24_read(at24, buf, off, count);. o+ z* F2 V0 ?" o# |7 Z  N. B
    }/ Y8 Q3 o3 W7 |! o: k5 l

    2 e- j5 _. {2 w5 i4 B5. 总结! w. ^1 p% M9 {+ P6 D( m) l
        本文主要对at24c02的驱动架构进行了分析。该驱动基于i2c总线架构,提供了id表来帮助设备驱动的绑定,该驱动支持AT24CXX等多个系列,不仅仅是at24c02。# J; J) {- D$ N. t
    % }' M/ U: \: q% K  x1 a5 I# x
    其次,该驱动并没有注册任何字符设备或者杂项设备,而是通过sys文件系统的二进制属性文件来对设备进行访问。此外,驱动同时支持I2C协议和SMBus协议来访问设备。. P% N" }# A) Q3 P! U

    3 M% E( k6 R. z  R* @8 Q2 P
    6 n, W; H8 n  {, u' F3 t, H$ u: H: ]% z+ g( K& T& A9 `4 H

    7 I- ^: _! F# e$ I0 G, G

    该用户从未签到

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

    本版积分规则

    关闭

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

    EDA365公众号

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

    GMT+8, 2025-11-26 00:28 , Processed in 0.187500 second(s), 23 queries , Gzip On.

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

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

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