找回密码
 注册
关于网站域名变更的通知
查看: 262|回复: 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接口来访问。
    - A1 f' s$ X. ]/ Q2 E
    , |; V7 b& t% X% h2 J3 t在开发板中,使用I2C控制器0和AT24C02连接,这里就不给出原理图了,如需要,可以搜索TQ2440开发板的原理图。6 }% \2 z0 m( m1 G

    ' Y9 K/ \' E! K8 v9 y9 A: j目标平台:TQ2440 + R/ K, B) T5 S& F/ w1 w4 k
    0 g! C& g1 [3 W! ^7 {+ I
    CPU:s3c2440
    / l6 `1 k9 k: J% r5 d- _
    + n8 u: U! m/ c# U1 e内核版本:2.6.32
    $ _& d+ G# O: n/ ]3 ]
    - v' t4 u- q$ o. z' W5 h" ?本文所有的代码均位于内核源码:linux/drivers/misc/eeprom/at24.c中。
    , [$ V0 _7 r8 c& l4 p. O8 u$ d! T0 ?* `4 q
    1. 模块注册和注销
    . y2 @: U; K1 V) Istatic int __init at24_init(void); J  K' R% e% F8 {4 Q* ^
    {6 a* u' T! L9 T0 ~6 Q+ U
            /* 将io_limit向下圆整到最近的2的幂*/
    % Z7 v8 x9 a' r; X( S        io_limit = rounddown_pow_of_two(io_limit);5 E& u9 j* ~; p$ E5 X7 r# r
            return i2c_add_driver(&at24_driver); /* i2c 驱动注册*/
    + m$ y2 t9 }1 ?6 N1 H}) D) o" I( G. Z- ?# j+ D  O
    module_init(at24_init);
    ( n' K4 H3 J. C& \  F: |/ x% s  u' F! p$ {
    static void __exit at24_exit(void)7 K3 B% M8 H6 @5 l  ~' Z
    {
    ' z6 u- M5 n, M: n        i2c_del_driver(&at24_driver);
    . }0 J8 f. X. I; e' N# ?}
    ) m3 U7 U* X: s) e/ Smodule_exit(at24_exit);6 ]# l) }( C6 x( L! ?
    ' b+ [' U/ [* Y2 @
    MODULE_DESCRIPTION("Driver for most I2C EEPROMs");0 {% ~, Y4 Z. H* w& I% X: t+ E6 c
    MODULE_AUTHOR("David Brownell and Wolfram Sang");
    % y6 ]+ B7 Z- eMODULE_LICENSE("GPL");
    ) r' }7 S  y% [5 z1 l: b
    + d* \/ t( v$ l
    ) m3 B* ]) h& {1 r6 N8 B; r% Y注册函数很简单。io_limit为写入时允许一次写入的最大字节,该参数为驱动模块参数,可由用户设置,默认值为128字节。4 Z) I9 q; e' y% h2 V
    # I- U/ [8 W/ ^, b0 ?' Y
    首先对io_limit向下圆整到最近的2的幂,接着直接调用了i2c_add_driver来注册一个i2c驱动。
    8 K0 j( Z3 y, N( [8 v
    8 |. A( p# I5 x6 A2 w- A5 V' o注销函数更简单。注销之前注册的i2c驱动。1 a5 O$ |' G7 a
    - P5 g  X) ?. w0 x. g5 G
    $ O* @3 X; m5 p/ Q, Q! S

    # Z# @& q. u' u% o2. 设备驱动绑定" D, X: B5 c7 j" C0 t/ S( ]: x$ d) s# G
    熟悉I2C驱动架构的可能会知道I2C驱动的match函数,该函数将使用id表(struct i2c_device_id)和i2c设备(struct i2c_client)进行匹配,判断是否有name字段相同,如果相同则匹配完成,即可完成设备和驱动的绑定,接着便会调用驱动提供的probe方法。我们来看下驱动提供的id表。
    $ T: N# |6 o2 v; f3 P7 o) J. h0 R8 c3 M& }3 o* C: u
    3 o* h2 G, y! o2 b7 F1 c
    static struct i2c_driver at24_driver = {
    : Y% i+ g, ?) C! t/ s6 z/ k        .driver = {
    + Y! m: F2 a4 O( r# X3 {2 D                .name = "at24",- o! Y8 T. P6 Q
                    .owner = THIS_MODULE,# Z5 Y0 @" w6 h6 ^8 O$ |
            },
    & J+ M6 w1 a/ V' z8 ^        .probe = at24_probe,# a/ T7 x0 {& e9 ~7 B
            .remove = __devexit_p(at24_remove),' Z& e1 h( J1 R: h" J  q2 @
            .id_table = at24_ids,
    ; R1 r: l! s$ _. d1 M" t* G};; H4 g$ D$ Q& Y) `% l3 s1 F( a% q
    驱动提供的id为at24_ids,如下:6 K( _; M* m; w% S$ u
    ! U  B( n& D9 ~; R) l

    6 y; ?! j: I; d% vstatic const struct i2c_device_id at24_ids[] = {
    8 {! y7 e, o2 D! L6 e, W3 R# Q0 G        /* needs 8 addresses as A0-A2 are ignored */1 N! Q" {2 a# ?) b% l& T6 s% o+ H
            { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },' _2 d8 Q% F1 @& ~6 A! c& [% q. s" U
            /* old variants can't be handled with this generic entry! */
    ( O& R; v: A4 q4 C0 r        { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },6 l) b/ _5 i3 C9 J* M2 ?
            { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
    $ b4 A6 G% D6 E3 C. |        /* spd is a 24c02 in memory DIMMs */% Q2 v3 ]; B$ J5 w, f( y- r
            { "spd", AT24_DEVICE_MAGIC(2048 / 8,
    ' E$ @8 ?; C) ]$ N) D                AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
    2 o  O# i5 ]! v  u6 r$ u! c3 V: W        { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
    . ~6 y, f  E7 R9 g6 S        /* 24RF08 quirk is handled at i2c-core */
    / Y$ I, w/ H2 p        { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },1 d* ?. C; m: e8 a" y. _; z
            { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },% y5 |9 J& N5 Y0 g; K
            { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
    ( v  b% c7 J& C        { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
    : T( H( H! H& K7 Y; x' U- k  a        { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
    / A: x8 y1 T% c8 p9 Y        { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
      {1 M* [0 E! @$ T8 |  a1 D2 J        { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },( s  G5 M* b+ Y( F5 |2 G( j2 [
            { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
    9 I$ [, E% z7 w1 |) u3 b& N        { "at24", 0 },
    ! V6 R; [8 o, L! I/ R" r2 T        { /* END OF LIST */ }9 G% f8 R  V- \! k5 F# u
    };$ i5 c  a2 u7 n% q" H9 |* |
    结构体成员的第一个参数即为name,表示设备的名字。第二个参数,在该驱动中,为一个幻术(magic),通过AT24_DEVICE_MAGIC宏计算。
    + l$ x8 H0 Q$ \3 J% s+ `5 S5 h1 i- a! P
    宏第一个参数为eeprom的大小,第二参数为一些标志位。我们看下这个宏:
    1 @9 B- v# k5 e% h' _8 G7 u7 q% n: ?! w/ p, h: t

    1 ]' A3 p" e4 j8 z+ ~5 a/ L#define AT24_SIZE_BYTELEN 5
    ) J6 m- ~* @3 G# x; o: }#define AT24_SIZE_FLAGS 8
    - a7 b. }& y% l- C5 ^* r
    " `" J& k2 d) V; p5 p/* create non-zero magic value for given eeprom parameters */' C( W. y4 O3 y, M! L% T
    #define AT24_DEVICE_MAGIC(_len, _flags)                 \+ V9 b8 h$ m2 w; l& t* ^9 ~/ j
            ((1 << AT24_SIZE_FLAGS | (_flags))                 \7 j  W+ ]% F  b3 k. b
                << AT24_SIZE_BYTELEN | ilog2(_len))5 g, d2 I0 e' e: Y+ \, }
    在这个表中,针对这里讲解的24c02,其大小为256字节,标志位为空。/ s4 ?5 D1 T1 k2 e7 W# u

    # ]! w. K" V6 \& r
    ' Y4 g5 \, E% v  i) O. S8 D" U" |! q/ X% e7 |, X
    3.probe函数
    : F- X. @% q! ]/ w" A$ v$ i    当i2c总线完成设备驱动绑定后,就会调用probe方法了。具体看下这个函数。
    # m  j1 V0 l) L$ V2 t+ s7 V1 g5 ?: G) \, l, X( Y

    1 v6 D: H5 D- e- \4 U, Y6 G- qstatic int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)  o1 r0 F+ [" n  x, d/ U
    {. W3 |# B6 m7 B0 a, \3 M
            struct at24_platform_data chip;9 D, r$ r  q4 M; o# n
            bool writable;
      e. j7 ^* R% R* R2 x$ t* C* P        bool use_smbus = false;- o) [: y8 k) ~! ~( T( b# `9 S/ `
            struct at24_data *at24;
    7 R2 H2 z5 D* I' i: S        int err;# z9 G% F8 h: Z( w6 a( R) h% J
            unsigned i, num_addresses;3 ]' [9 m" a- O$ h1 s
            kernel_ulong_t magic;
    - t6 Q) R7 j7 J/ [+ I/ ~4 ?$ J4 R" k/ {
            /* 获取板级设备信息*/  |  l2 G2 w1 @: L9 j- K
            if (client->dev.platform_data) {
    / x5 ^$ b+ g0 d! X* J9 ^) Q9 j                chip = *(struct at24_platform_data *)client->dev.platform_data;
    1 m7 M' Q- k) H) Y; |        } else {
    ) S7 n/ q2 e, O9 @$ u. T                /* 没有板级设备信息,也没有driver_data,直接出错*/
      K/ P  O$ B( `: x0 N" U6 f                if (!id->driver_data) {
    " R+ z* q( q; i9 v- F/ g                        err = -ENODEV;
    - Q! b1 e+ l" B9 `  t/ P- E' R                        goto err_out;
    / F/ V3 W5 L* \  q1 J! K$ t                }
    2 |( G1 T3 o* ~* i; Y4 g: P                magic = id->driver_data;! \4 H' V7 g( T( R6 ]+ t- B, x  i
                    chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
    # X( e' I9 e, e) b* V                magic >>= AT24_SIZE_BYTELEN;/ u8 c& R& s) ^1 ~9 ~; f
                    chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);# O6 M. k, U. G1 c9 |+ p
                    /*
    # B2 b; K9 W. L                 * This is slow, but we can't know all eeproms, so we better
    3 c' I7 o$ ~- w" n0 A4 [                 * play safe. Specifying custom eeprom-types via platform_data8 q! m: {6 S' f
                     * is recommended anyhow.
    / F! q! t* g1 t1 c6 P& H                 */
    8 [2 V, f2 U5 p$ F! ?                chip.page_size = 1;/ J: g( k, F% p5 V* k- o; X, W
    $ l/ F, G, @/ M6 ?3 H% q: c+ i
                    chip.setup = NULL;& S" O+ L+ g% G8 f' G8 B4 B( ~& R
                    chip.context = NULL;
    / K3 Z" Z( ]( w. h( I$ u2 ~6 Y- Z        }: O7 L7 X7 W8 U: q0 |+ T

    $ x6 i# [. F7 t        /* 检查参数,! R8 y  s( O& S8 E) F
                byte_len和page_size必须为2的幂,不是则打印警告*/( K" k) [/ o  V1 P8 B3 ^
            if (!is_power_of_2(chip.byte_len))
    * X% T4 N% A& _. g1 E                dev_warn(&client->dev,
    9 s/ ~" ?$ m. F# v) C3 y# ^! @! q1 o                        "byte_len looks suspicious (no power of 2)!\n");
    # k- S( S7 b6 p# w" H0 P: @        if (!is_power_of_2(chip.page_size))! G9 V" y( R& V" ~1 v- [" d
                    dev_warn(&client->dev,
    ; M! E5 A" y7 ?, p% b% ]                        "page_size looks suspicious (no power of 2)!\n");
    $ j2 s7 W0 d3 \2 `8 N$ X( z* Y+ `
            /* Use I2C operations unless we're stuck with SMBus extensions. */7 l( |$ e, a$ U0 a& Y; Z
            /* 检查是否支持I2C协议,7 b* e6 \7 v4 G5 N
                如果不支持,则检查是否使用SMBUS协议*/
    ! A1 P& q* x, |/ |        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
    , H+ w8 W+ N. F4 ?8 l5 w                /* 不支持I2C协议,但是使用16位地址,出错*/& S: k) |2 ^) g8 s
                    if (chip.flags & AT24_FLAG_ADDR16) {
    2 b' ?( {% Z& D  R1 d: T# ~                        err = -EPFNOSUPPORT;; l: K/ [0 ?6 s; Y) h/ j7 S' z
                            goto err_out;: R7 Q. L/ D' f+ c
                    }7 F0 t# R8 I6 w! V7 y3 j' U) Y: e- n0 C
                    /*  不支持I2C协议,使用8位地址,
    3 e  t% j3 u; x, g+ x/ J                   但是不支持I2C_FUNC_SMBUS_READ_I2C_BLOCK,出错*/$ o( h, d1 r4 F% X1 ^5 C; b' s
                    if (!i2c_check_functionality(client->adapter,6 |. J4 I" U( t) M7 e4 F* W) ?. G
                                    I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {/ }( H7 z5 z# X* w8 L7 g
                            err = -EPFNOSUPPORT;- P3 R8 e4 s1 `* E/ V4 d) ~$ H6 M
                            goto err_out;2 u+ K6 k) E4 }2 o
                    }1 f! z. h( n) k5 l% u" }# [1 I
                    use_smbus = true; /*使用 SMBUS协议*/  D! p: @/ f2 \9 u6 i! r8 q( o/ y4 D
            }
    # r' L' [" Y5 f4 K+ j5 @  S" k  i1 O" u
            /*是否使用8个地址,根据id表,
    3 `( p/ k$ q8 h$ N            目前只有AT24C00使用8个地址,其他都为1个*// e; _- {" H5 V9 w% q$ e3 ~
            if (chip.flags & AT24_FLAG_TAKE8ADDR)
    " S- v& u% G$ I/ N                num_addresses = 8;% z# o% N& [* q+ y
            else
    9 K# M! M- d, e4 n1 [                /* 24C02需要1个地址,24C04为2个,以此类推*/
    1 l  x8 I, L# y& }                num_addresses =        DIV_ROUND_UP(chip.byte_len,7 f" L8 i0 @5 Y
                            (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
    ! r5 l9 U8 q) r# f, y2 L$ }2 }. S! c  t, k4 T# I5 C  W
            /* 分配struct at24_data,同时根据地址个数分配struct i2c_client*/; h- z# {# i# ]! j1 n. x! f
            at24 = kzalloc(sizeof(struct at24_data) +; \. k1 j+ d3 E3 v' l
                    num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);. E7 S; O1 i& `- t8 ^
            if (!at24) {9 {' Z: @+ B- Q- t/ [
                    err = -ENOMEM;$ p4 v( J( O" ]& I1 ^' R) n  I& R
                    goto err_out;
    ; G2 H, c3 X: j5 X- R7 N        }# `8 Z" b) G( n+ B# D7 }

    : I+ K, T( v6 c; X& d5 c) s5 D        /* 初始化struct at24_data*/5 `5 P; l- M* ?& Q& z6 e( H
            mutex_init(&at24->lock);6 s/ E1 [7 _1 V+ i
            at24->use_smbus = use_smbus;
    8 n) R1 A$ K5 A5 r2 V) l' p4 ?        at24->chip = chip;
    8 t0 ~4 r6 n7 U! R; {' P7 N* N6 y        at24->num_addresses = num_addresses;) c* h' B7 p( k/ I, R* |* f8 N
    6 Q, s+ i6 ^6 M' N
            /*- _$ L/ M- \5 ]; Y, u! g
             * Export the EEPROM bytes through sysfs, since that's convenient.
    - M8 [: i, g+ ?# i+ r         * By default, only root should see the data (maybe passwords etc); r, v' p7 F2 a: P9 c
             */
    - R; S& o9 [& i& q3 `) ^5 _         /* 设置bin_attribute字段,二进制文件名为eeprom,
    * [2 e8 _1 O: {" c! E4 Q             通过它即可读写设备 */
    7 I4 M1 m4 Z# q# {        at24->bin.attr.name = "eeprom";$ C0 p( G: f2 f  M' ^1 o+ Q
            at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
    . K% a: P! k7 U! F+ K% I9 p- x" Y        at24->bin.read = at24_bin_read;
    $ _* V' }- k. Y6 I# v        at24->bin.size = chip.byte_len;, d: B6 S; x1 `" N9 k5 ?- A# P
    0 l+ g) c: x' _) J
            at24->macc.read = at24_macc_read;  /***  先忽略***/7 M& O1 a! N: t3 z

    8 F, }: V: I9 {7 e        /* 判断设备是否可写*/
    * p3 d) P3 W% `6 |/ n5 z        writable = !(chip.flags & AT24_FLAG_READONLY);/ b/ k$ p6 H9 B# n: Z. b! U
            if (writable) {
    2 A: e' q4 C' Q) h; N5 Q                if (!use_smbus || i2c_check_functionality(client->adapter,0 ~  q( o7 t0 ]7 y: J  t
                                    I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {$ e- y, `9 R% U3 v
    2 L4 |: e  v' ?# k+ l9 d) j5 f
                            unsigned write_max = chip.page_size;
    ( Q, z2 }+ a5 W5 L! ^1 {! |6 M- ?2 ?% \
                            at24->macc.write = at24_macc_write;  /***  先忽略***/
    2 K- z, u  m$ @6 @
    / k! q! f+ d4 [) c7 t+ A8 o                        at24->bin.write = at24_bin_write;    /* 写函数*/* S7 |* q+ D8 L. g5 ^
                            at24->bin.attr.mode |= S_IWUSR;    /* 文件拥有者可写*/
      X, q3 x2 A  z9 E( W
    ) q* [7 x7 q: Z' \2 ^$ T$ W                        if (write_max > io_limit)  /* 一次最多写io_limit个字节*/; V5 X; J$ e3 a: M9 J+ f& |0 d
                                    write_max = io_limit;) C- Q+ s  e/ F6 C
                            /* 如果使用smbus,对write_max检查*/
    $ B& s3 r# K$ {                        if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)/ D: R# p1 u$ d8 l; i
                                    write_max = I2C_SMBUS_BLOCK_MAX;
    & F8 r9 V$ k4 {) ]+ M$ i                        at24->write_max = write_max;  
    * L5 g5 j0 R1 d7 S% F* P
    7 U$ d& t+ }+ A                        /* buffer (data + address at the beginning) */& v; j4 J7 _  a2 n1 z/ W3 L
                            /* 分配写缓冲区,多余两个字节用于保存寄存器地址*/' Y0 u) F9 J1 w  c% N& g+ `
                            at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);* A2 _5 F0 X% Y+ f0 c8 @% {
                            if (!at24->writebuf) {# V) c0 A: ^1 e, ?
                                    err = -ENOMEM;
    % m- P( l; F5 G3 F/ m                                goto err_struct;' |: O: ]% f9 P7 U& M1 V: Q
                            }
    . g7 d! S! v2 {5 P' y, w% v                } else {6 S+ w; \2 y; ]5 q) T
                            dev_warn(&client->dev,
    2 k+ Y  V- `+ u# K                                "cannot write due to controller restrictions.");  I* q, A3 |* ~& O0 [; q5 I, |* Y
                    }
    ) o! p0 x9 W6 |' N. j- O0 r) E1 K        }7 d# n- T3 L! j& {4 k0 H  G* k

    . |1 u. y& j% Q4 M2 d        at24->client[0] = client;  /* 保存i2c设备client*/
    0 k' B* u$ W9 ~* H- ]
      a4 m* G1 w; `8 c" b4 R        /* use dummy devices for multiple-address chips */$ Y/ K" {( |% A  x$ ^4 G
            /* 为其余设备地址注册一个dummy设备*/
    + m2 |( U" ~9 A+ ^) m* h        for (i = 1; i < num_addresses; i++) {! C, n+ B$ @" c7 o$ w9 l* }
                    at24->client[i] = i2c_new_dummy(client->adapter,
    9 F. k3 L3 d% t- V* s) N' @8 k                                        client->addr + i);     /* 设备地址每次加1 */
    ( M# t, b6 o# S0 F( G8 d& H7 @  R                if (!at24->client[i]) {
    6 L( ?# N: ]  w# E% W/ h1 \1 C: n                        dev_err(&client->dev, "address 0x%02x unavailable\n",& z/ {. J& v, ^1 X% m6 E
                                            client->addr + i);
    ' y7 ?! j4 q0 _6 F                        err = -EADDRINUSE;2 V5 f6 R) ]& Y4 C. _
                            goto err_clients;+ N7 i+ I; I0 @# X
                    }  L; h$ A" H) U" Q4 Y: ^" M& R3 G
            }
    7 ?( ?) `; |: e: n+ R, j1 C& j) g. l6 h) d* f
            /* 创建二进制属性*/
    0 F5 U' c9 r+ I1 m3 q( {  s        err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);7 j5 V) n3 Q& Z6 |+ f- n9 I
            if (err)* l2 f: Q9 X2 t6 \2 E( `# s0 S2 i
                    goto err_clients;
    % e2 L! j4 y7 s: H6 o3 f8 Y+ W: c( S- j5 |
            i2c_set_clientdata(client, at24);  /* 保存驱动数据*/
    + O( r! B; W1 y7 O5 C# k1 {! F0 Q" H5 }* V. a4 A; I1 R
            /* 打印设备信息*/
    : a1 N8 V4 w' |, g& z+ `; o. Z+ {        dev_info(&client->dev, "%zu byte %s EEPROM %s\n",1 Y) g0 M6 M" C' m
                    at24->bin.size, client->name,3 z' T( H( h! F# [- R% l
                    writable ? "(writable)" : "(read-only)");9 x' f9 H8 l1 ?0 h: R* [/ O
            dev_dbg(&client->dev,
    " ]- Q- s+ j, ^; B1 N# i                "page_size %d, num_addresses %d, write_max %d%s\n",7 F0 ^" G& m2 s4 K" [! h2 B
                    chip.page_size, num_addresses,- g! X! F  J8 P
                    at24->write_max,
      H6 G+ q1 d, J                use_smbus ? ", use_smbus" : "");0 Q) j# i" ]# I6 h+ ?- j
    , H  c) p( y6 E
            /* export data to kernel code */
    # G) P/ D3 Z7 y4 y- q; R        if (chip.setup)
    , G) }1 l/ F% Y                chip.setup(&at24->macc, chip.context);
    $ z3 }6 z; ?- l; J. {7 A) c
    , ?  K) l5 U; K$ o- @8 U; b        return 0;
    7 a4 z6 r$ Q5 s' E& O! R/ D
    % U& K2 q8 u0 s( p/ X9 n2 Gerr_clients:
    2 v+ E' T3 j3 }1 i; N        for (i = 1; i < num_addresses; i++)
    # M6 o" H3 j2 _3 B% E; ^% O* j/ P6 S2 N                if (at24->client[i])
    : `- B0 r1 p. I$ Z0 x8 T                        i2c_unregister_device(at24->client[i]);+ ~. O6 h, a9 v4 J
    ) c% Q+ V/ x+ d& d% `
            kfree(at24->writebuf);
    # i( B$ Y  N) R& yerr_struct:1 Y2 u& g# V/ B2 v. n6 r
            kfree(at24);
    0 g' {: V" E7 s1 k+ z$ v4 j2 E}
    0 E0 Y0 Q  V' ]% r, a! ?- e. _驱动首先获取板级设备信息(client->dev.platform_data),我们假设驱动移植时,添加了该板级设备信息。
    5 P" U6 B! `7 |判断是使用I2C协议还是SMBus协议。在这里,I2C adpater使用I2C协议。
    8 w: x5 }5 D8 D# t  x
    1 R+ r; M: H. @2 S然后,判断设备需要多少个i2c设备地址。
    ; \! p9 ^# d+ \5 T1 q6 m# ^5 f
    8 I% L# c: A% |" Q这里补充下:根据at24c02的datasheet,设备地址的第1位到第3位,将根据不同的设备来进行设置。
    1 @1 ~% [& e1 P  E
    5 j9 h- X& s: T( b2 ]例如,如果是at24c04,则设备地址的第1位将用来表示寄存器地址,因为内存大小为512字节,而寄存器地址只有8位(256字节),* A0 ~; ]: ^* h  p: Q! |. N

    8 w4 w& {" P" |3 y需要额外的一位用来表示512字节,因此使用了设备地址当中的一位来实现此目的。具体的请看datasheet。5 f. T9 d/ F, B% S5 I) T% M

    # h$ C& [8 j6 t! n9 n这里使用at24c02,num_addresses将为1。$ s: G) c# ~  P3 n- F; p1 s" _
    ; x$ {9 D- M. d( U
    接着分配struct at24_data和struct i2c_client指针数组空间。
    % y% D/ c' _8 m5 _
    + q$ P% W  h% v) O' z然后对struct at24_data进行了初始化工作。
    ) }) J, D# T" g$ P$ j( \) ^- H% K  k3 q4 c
    接着,对二进制属性进行了配置。名字为eeprom,同时配置了其读方法(at24_bin_read),如果设备可写,还将配置其写方法(at24_bin_write)。. R/ {) M8 n, q

    ; s; Z) m* B5 `+ ~9 i7 e接下来很重要的一步,如果设备使用多个地址,则需要为所有地址(除了第一个地址)分配一个dummy device,这样这些地址就不会被其他的I2C设备占用了。
    / \4 X# N5 L6 t% k. F
    ) g6 F) R3 f) H$ G最后,向sys文件系统注册了二进制属性文件,通过该二进制文件,用户即可访问该设备。
    : g4 I- N/ O' H8 m! V6 U0 l. C
    5 s0 [3 g3 |2 y* s) S9 [, V9 }注意:驱动使用了struct memory_accessor的东东,对这个东东不是太了解,所以先忽略,这个东西不影响驱动整体的架构。- p) X# l2 q8 X
    5 T0 w: `9 c. M+ ^  B7 h

      k8 W! |" y% k8 k: ^0 D( I) X5 i+ A5 a1 P3 b! S! E
    4.设备访问方法
    " }2 [( C; D& F   从第3结的分析可知,驱动并没有注册任何字符设备或者杂项设备,只是向sys文件系统注册了一个二进制属性文件。因此要访问设备,必须通过该文件的读写函数来。  {+ d2 J- h' D! n* {4 P0 b% x
    # N# Z& S  f4 I6 z0 p0 ]/ }# k
    读写函数在probe函数中指定为at24_bin_write和at24_bin_read,我们来分别看下。
    9 N) e+ v2 _) s" p  V4 ^4 {! ~9 |) v: B/ a5 z5 r9 S( Q6 `$ l
    4.1 写函数(at24_bin_write)( ?( {) |' G8 m) x2 `+ e) i, t
    static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr,
    3 m- d6 A: _0 ^0 M                char *buf, loff_t off, size_t count)
    & S1 i, H. M+ W" n8 }: V5 g& o{
    # U- S1 Q' ]" P. A        struct at24_data *at24;
    : L! C* s( {2 z( X9 L
    3 N# _/ f1 G5 `# y( B/ u        /* 通过kobj获取device,再获取driver_data */4 u8 w& @" E4 Z. ^/ R+ l
            at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));$ w$ `) `% }+ G( J  W' o; m
            return at24_write(at24, buf, off, count);9 I1 L7 T" Q* v0 X
    }* r/ B& a, ?* P# C- A+ N. c% b
    该函数首先通过kobj获取了struct device的指针,再获取了at24。
    : f2 E+ l& `/ E8 ?2 l接着直接调用了at24_write。如下:
    6 ?6 p' x' L2 J  _& Z. w
      Z$ R" @; e! }* f9 itatic ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
      e4 A; a3 {) l" ]; ?. j9 ?% [+ I6 ?, @3 R                          size_t count)
    8 p: I% P) P6 o6 u2 m{
    / m4 j' T# b# _% R        ssize_t retval = 0;
    ) ?2 W1 j' B+ _$ B* l; Y( |
    6 [& `; Z2 ^4 K3 ]* m2 K        if (unlikely(!count))7 H' ?4 x8 R5 M- C
                    return count;% w* D, J( \  s+ ^! f

    6 U+ ~7 E( v1 n8 M" Z        /*1 U+ F5 e( j: `9 Z
             * Write data to chip, protecting against concurrent updates, h1 j" R; h6 F& t% n! k& o
             * from this host, but not from other I2C masters.
    % x: ^1 P& ?3 l9 C8 |         */
    : V2 _; H9 M/ g, L- C          /* 访问设备前,加锁*/5 K! g' k6 ~% @9 ^  H5 f& I" z8 z+ u
            mutex_lock(&at24->lock);
      X' |, _  X6 F: N( V6 ?. X
    : r' [. z9 S; d, r/ B7 M7 d        while (count) {
    5 C# E; x1 M% K$ ^, o+ n                ssize_t        status;' A4 p2 Z: O( J: E

    ; c; A9 G! w6 B                status = at24_eeprom_write(at24, buf, off, count);
    % Y( |- L. f6 M+ k                if (status <= 0) {' T; f+ Z' Z& O& ^# |9 k
                            if (retval == 0)! Y5 b5 Y9 P/ [6 B6 @% s
                                    retval = status;" f! O% m# b  G/ x4 D+ U/ `
                            break;
    ) B& c# B2 W) M" E; \! q1 Z3 f: F                }6 L. h( B$ G) B3 ]
                    buf += status;
    , U7 x- P  c' U& g) J: r$ h                off += status;( \! b8 I' g# y0 g% u/ h
                    count -= status;
    9 f* j6 C7 n& L" Q2 d3 V4 q                retval += status;
    $ {2 [+ b5 q/ j        }
    & B6 w, Z! [% }* n1 O% b6 u9 r
    " N0 G8 L' T9 W# l! Y        mutex_unlock(&at24->lock);
    " ~- K' h1 x# K& g$ X
    ; a; V: g1 n4 n! }" ^1 h- v% k        return retval;
    . y6 y/ p/ C  R; Y' l! P}$ n" M6 m  ]8 M3 v4 g; ]" [

    9 O+ [% C6 g2 n3 ~* S该函数不复杂。在访问设备前,首先加锁互斥体,以防止竞态。然后根据count来调用at24_eeprom_write函数将数据写入设备。
    8 d7 t8 }1 g  i6 H% G9 M; D写入成功后,更新偏移量等信息,如果还需要写入,则再次调用at24_eeprom_write函数。
    / `! Z& H0 a! H, a8 `4 u6 G: J/ Y0 G. c: Y0 K  [0 b. ?- [
    看下at24_eeprom_write函数:
      z, r4 H6 D& @/ H7 u$ L% c2 n+ S7 N' F3 p) [( @
    /*0 v. W3 T- Q6 A& u' b2 y) _
    * Note that if the hardware write-protect pin is pulled high, the whole  v% S& \/ O0 ?4 P) O5 @
    * chip is normally write protected. But there are plenty of product0 Q, u: y! s# j- f* F6 R: |6 k
    * variants here, including OTP fuses and partial chip protect., N: m9 K" Q8 S; y
    *7 y! b8 Y1 z$ g$ z$ ?7 N9 G8 b4 D
    * We only use page mode writes; the alternative is sloooow. This routine
    7 r1 B; }, r# D4 P1 b * writes at most one page.. H- \' {6 w( ~
    */; H; ]9 p6 U: {2 L# J9 b
    static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf," _. B, l& S# y5 V
                    unsigned offset, size_t count)
    / I. x2 m0 |1 I1 h& f{
    % X7 C1 p% j' m* m8 ]/ {        struct i2c_client *client;
    0 Y" |& D& P% K9 _: |        struct i2c_msg msg;
    6 j: @1 z6 u+ S% h6 F        ssize_t status;
    7 I( T, z3 L: m; k* p) s        unsigned long timeout, write_time;+ ]- v+ g8 m! Q( U' C1 z
            unsigned next_page;
    1 M0 T/ J7 b! Q; x
    " T6 |! W5 z9 p        /* Get corresponding I2C address and adjust offset */
    ' i2 J7 I8 f- i+ M! b7 T6 D        client = at24_translate_offset(at24, &offset);& O8 K/ g% A  ~: O) E) n

    5 \; f" E! }5 t0 u        /* write_max is at most a page */
    3 x) i( C! i: e2 U        /* 检查写入的字节数*/
    $ d5 l% k- F8 X* K* ^' ~# k+ ]        if (count > at24->write_max)& Z" S+ q% P+ D3 J
                    count = at24->write_max;: C+ R' D: d. F7 Q+ G% s" R

    5 T. Z0 Y" a0 z' K- r        /* Never roll over backwards, to the start of this page */. W) r( ^! z8 p& T1 Q! t2 F% N$ Z
            /* 写入不会超过下一页的边界*/: F  i4 h5 y& m/ J7 D
            next_page = roundup(offset + 1, at24->chip.page_size);' X( P  z' u" q: x
            /* 根据页大小调整count*// b1 A: z, j5 P/ H' F4 m& _
            if (offset + count > next_page)1 p, J6 r  P6 V' v2 C- Z
                    count = next_page - offset;
    9 e' m1 ?: `& a$ i& p; u# n# ?/ L  {% Q' W
            /* If we'll use I2C calls for I/O, set up the message */! J& ]' z& o. k1 t5 d
            /* 使用I2C协议,需要填充msg*/
      p% G) d1 E7 S  A) c        if (!at24->use_smbus) {
    ; e( J+ o0 [7 g" d9 X8 M7 a                int i = 0;
    6 _+ P8 B2 A0 ^2 j. t2 u6 E! D' i) ~9 G* D1 r, k- C+ V
                    msg.addr = client->addr;  /*设备地址*/
    ; E+ h+ H4 P4 y( ]/ _- c6 z9 f                msg.flags = 0;
    0 G" C0 Q* }* s$ b( g8 j# }' X
    8 S+ a6 D$ g" w4 k0 [                /* msg.buf is u8 and casts will mask the values */! j' ^' L8 ~5 _1 _% ~( V
                    /* 使用writebuf作为发送缓冲区 */5 a% @; V0 K  |) S+ r
                    msg.buf = at24->writebuf;; W5 a8 A8 \) N  `; l1 z
                    * G1 ]- f' d/ e" r0 B8 Z
                    /* 根据是8位还是16位地址,msg.buf的前一(两)个字节+ ~3 Q2 T5 i6 P! l4 _6 ?" z
                         为设备内部的寄存器地址*/. x7 W. u6 P4 {
                    if (at24->chip.flags & AT24_FLAG_ADDR16)
    " ~  C/ A9 U6 Y) f5 t. ?                        msg.buf[i++] = offset >> 8;  /* 16位地址,先写高位地址*/2 u% P! v' a& v  c

    : X+ B( m% R4 M( w; j. x" L                msg.buf[i++] = offset;- j/ e# ?# L1 e9 _4 z8 p
                    /* 复制需要发送的数据 */9 y0 j8 Y# U. N0 r* b) N
                    mEMCpy(&msg.buf[i], buf, count);& Q; W2 I0 c0 @! g2 Y1 J  C: }
                    msg.len = i + count;   /* 发送长度为数据长度加上地址长度*/
    7 \5 A0 x) H0 @/ p& |9 v5 Z; Z        }
    & ]! \8 l8 x, @7 K
    / M- M: ~; [  {* X: }( T        /*5 M4 q; ?" W6 _  M
             * Writes fail if the previous one didn't complete yet. We may
    6 D# n% j% j9 H         * loop a few times until this one succeeds, waiting at least
    * F3 I7 ]% y5 r: }# y         * long enough for one entire page write to work.  L) I: i! D' V; R! z" }
             */
    1 z( D" p% [4 l        timeout = jiffies + msecs_to_jiffies(write_timeout);7 g+ b% E+ ]. D. V" a! |7 t# V$ b
            do {
    8 z8 @4 m4 w4 w, R. y6 ~5 t                write_time = jiffies;- m2 O/ z2 q8 t7 g
                    if (at24->use_smbus) {1 Z- R' M& U4 w, R& O2 d9 C
                            /* 使用SMBus协议发送*/
    4 j' x. F4 [# `9 c9 x                        status = i2c_smbus_write_i2c_block_data(client,
    ' Q: V0 ]: y! X# V; q8 C- V- X5 z                                        offset, count, buf);3 w! {( |& b& s% c0 p8 f
                            if (status == 0)
    4 F% g+ ~& ~' ]3 u9 ^/ I2 e+ a                                status = count;
    ( u3 G7 R/ L/ y9 W2 u$ _$ I, ^+ }                } else {0 e9 u. r, h* P3 _) l4 ]
                            /* 使用I2C协议发送*/
    + j: H, u6 O( Y' W' D8 I                        status = i2c_transfer(client->adapter, &msg, 1);3 S' h" q+ U, @( p
                            if (status == 1)' u$ N2 ~! i% ?# L" l$ d
                                    status = count;
    # H) C% J# T# _, L4 q) R0 y                }) |& n$ a& s' S% x6 V2 W9 ~
                    dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
    ; K- y# J9 n8 J% g# e                                count, offset, status, jiffies);
    # m1 g: i) v- @/ G
    " c& x1 H( G' R( b, J                if (status == count)
    1 R, _5 x* @* P) o                        return count;  /* 已全部写入,返回*/
    " [1 O/ b4 z* E& |, K% D& j, O  }9 l
                    /* REVISIT: at HZ=100, this is sloooow */
    0 B* r8 T8 A  E                msleep(1);
    + c' B5 V/ L& |$ T6 {2 z        } while (time_before(write_time, timeout));  /* 使用timeout */# O  D" F% W( @* @3 z8 I

    6 H% r$ f" b% V% r  c        return -ETIMEDOUT;  /* 超时,返回错误*/
    6 V* H9 e" z- M' L# s% \. M) A' a+ ^}
    # R/ b% X6 K% Y( n% m- N) I  A/ \
    * k6 q. P  h  l' j4 P该函数首先调用了at24_translate_offset函数,来获取地址对应的client:
    9 n! L3 D4 g$ k& O- s" C3 @/*
    6 w% s7 A+ L: L$ ]  Z5 r * This routine supports chips which consume multiple I2C addresses. It
    9 o9 L* x* F- k! G * computes the addressing information to be used for a given r/w request.; Y9 J  e% |: M) u- z- T/ H
    * Assumes that sanity checks for offset happened at sysfs-layer.3 l( ?2 O( V8 N8 l  }' l
    */
    . k8 X! M  J0 _3 Astatic struct i2c_client *at24_translate_offset(struct at24_data *at24,: [9 n. f/ m4 ?) b7 }9 {
                    unsigned *offset)
    + O* H3 V7 K& ?{2 h! E* v2 u1 h0 S
            unsigned i;
    1 z. g; }2 R4 |, [3 N, Q( d! p! g  G1 f6 e/ l) Q5 N
            /* 有多个I2C设备地址,根据offset获取该地址对应的client*/9 H  a5 s' y* a+ q0 f, u8 k3 P
            if (at24->chip.flags & AT24_FLAG_ADDR16) {) h! w' X& g/ `% w+ @$ J  A8 w; a% L
                    i = *offset >> 16;) c- u1 C7 b- g
                    *offset &= 0xffff;( k7 O+ s3 G. @7 E& Z- ]$ w
            } else {9 z( q& O6 }& v+ U6 i  r3 ^7 r
                    i = *offset >> 8;) E5 ^# p/ C) @* M1 @2 B, m
                    *offset &= 0xff;
    : {* L  y1 P" C4 B- }+ C) @  S8 ]7 Q, r        }+ X$ \2 Y) s7 y) W* P" }  q7 m7 V$ K

    & n; h/ L. U3 l( ~  V% r  U        return at24->client[i];6 x$ J7 w6 P+ d% _, y3 [
    }7 d& ~% R" e6 |) p6 o" W

    " B7 v( J" K+ z# a然后,对写入的字节数(count)进行了调整。( h; N, T3 ]& y4 z! H/ ]
    随后,如果使用I2C协议,则要组建msg用于发送。
    4 c& |2 X1 y$ ^7 H) {: o) L2 p" v- |) ^" E! Q
    最后,根据使用I2C还是SMBus协议,调用相应的发送函数来发送数据。& i( [$ C' Z. V4 O( X" A* E7 H6 X  P4 B  [& X

    4 g9 z- _1 b& ]/ k. w注意的是,这里使用了超时,超时时间write_timeout为驱动模块参数,可由用户设置,默认为25ms。如果发送超时了,while循环将终止。
    1 g0 z* z) T1 r1 B& s; l3 Y# Y" T$ o+ t6 y0 [- r) D; O' d% T
    至此,at24c02的写入过程就结束了。
    7 X6 t) P( P( C. d1 t7 ?7 {, T. c, m% ~$ W( q+ f& S
    4.2 读函数(at24_bin_read)- G* U4 A* I) W" m$ N7 p$ E
      写函数和读函数非常相似,只是在使用I2C协议时,组建的msg有所不同。同样读函数也使用了超时。& H: I) g1 ^3 O1 w, Q# f9 m

    9 U2 C4 i5 \) m1 |8 n7 y  因此,这里仅仅给出代码:
    8 }6 m; D" d1 l3 f3 n* O/*) v) l8 ]# u; l+ g9 C  C
    * This routine supports chips which consume multiple I2C addresses. It
      Y- E- U- Z# q$ X) W6 u* H * computes the addressing information to be used for a given r/w request.. x; w% w/ A  V
    * Assumes that sanity checks for offset happened at sysfs-layer.
    9 ~, {1 c. z% o */8 ^. }) l1 U( \1 {/ E
    static struct i2c_client *at24_translate_offset(struct at24_data *at24,9 O- B: r+ v" `# U# C& g! K4 k, ?( I
                    unsigned *offset)% V; H% r5 _5 I2 F
    {5 G) S8 H# q  a/ F, I' `
            unsigned i;/ c3 ~8 d6 f, I/ j( }3 _
    1 x+ |8 W" R! @
            /* 有多个I2C设备地址,根据offset获取该地址对应的client*// P( r: e/ P9 t1 L) [
            if (at24->chip.flags & AT24_FLAG_ADDR16) {8 [+ u2 y8 G% f9 C/ z
                    i = *offset >> 16;4 |, Z6 g3 |9 `& Z& k5 k2 Y
                    *offset &= 0xffff;0 ]' O! E5 m3 \, C4 N9 _/ o
            } else {
    - _2 n' t& z" i6 o' k) {                i = *offset >> 8;1 R2 S# O1 G$ [. Z4 u7 O' h, d
                    *offset &= 0xff;4 @$ T8 k6 y: L, R6 ?
            }* M, v0 O: ]2 Y; `" ]) w. I& f

    0 w" l+ p+ \8 v# _& N' B+ Q% x8 y        return at24->client[i];' M- R. z* a9 j; }
    }
    ) M& ?+ u6 W7 \5 E' j% p& c" ?2 g" T: {8 Z$ z: z# v* W
    static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
    1 {( G, L) i8 P% r3 y/ D" ^                unsigned offset, size_t count)! N- n1 S, e, P" X
    {
    ; H! a% f9 ]+ O% l+ j& |; ?        struct i2c_msg msg[2];- B0 F: b# _2 E/ C* J  y, u) y( {
            u8 msgbuf[2];/ V5 [. H. |, O1 n  X- s* z# ]
            struct i2c_client *client;: ]  G1 p6 n+ ?( ^% V& J
            unsigned long timeout, read_time;% Q  K2 f! y0 i/ d) d8 {( N
            int status, i;
    & e6 h( E$ M' T1 g
    . h- m+ y0 E$ K+ a- U5 R& k        memset(msg, 0, sizeof(msg));
    3 q1 x2 l; {& I9 h  h8 I( u% T1 h6 d
            /*
    ' R: j9 I7 D6 p8 w" T' C7 Z( Y         * REVISIT some multi-address chips don't rollover page reads to9 ?. n  t+ L  x$ W5 P4 z/ K
             * the next slave address, so we may need to truncate the count.2 }7 R. d( h0 y8 C. M7 r, u' X/ F* j
             * Those chips might need another quirk flag.
    ( C; t: p2 C3 k, y' B0 }         *# n  J# v1 H1 q3 ~8 c  c2 U, J4 U
             * If the real hardware used four adjacent 24c02 chips and that
    8 J+ H+ N6 Y  ~4 b1 _. Y         * were misconfigured as one 24c08, that would be a similar effect:& Y! B( P3 A; D- l8 v( ]; {
             * one "eeprom" file not four, but larger reads would fail when
    3 I& z9 o! R; u& S" x         * they crossed certain pages.6 s) S  M0 I& L) t
             */
    . y( w8 N9 J$ ^  q0 N0 p7 v, J* n( |: D# e( u. B
            /*: c4 q& F( f/ p$ E7 R
             * Slave address and byte offset derive from the offset. Always* E. i% {& ^( ^& Q
             * set the byte address; on a multi-master board, another master0 f) H0 d" Z% A# ?% E& k
             * may have changed the chip's "current" address pointer.( i) w' i- T2 J/ a* \
             */, |& S' E- U; _3 v6 u
            client = at24_translate_offset(at24, &offset);( ^7 j9 y# F9 t! J% a: `
    4 l9 [  Y, S$ f, {) j$ Y" E
            if (count > io_limit)" M0 ^, p0 d/ z8 O
                    count = io_limit;+ f4 T% O% S" x2 q

    8 V7 q. a7 }# i, _5 X( X        if (at24->use_smbus) {
    ; l) @) v( u, i: J# C8 A1 c4 ^0 w                /* Smaller eeproms can work given some SMBus extension calls *// Q8 O" o4 n5 ~( y7 V  @4 g1 k9 l
                    if (count > I2C_SMBUS_BLOCK_MAX)" P4 ^' N! v& g
                            count = I2C_SMBUS_BLOCK_MAX;* f& C, n; ]5 {: e, Z6 ]$ ]/ r
            } else {
    & x3 V# S1 L: y                /* 使用I2C协议,需要填充msg*/. o. F. p: |- L( M2 q
                    /*
    $ s& x! r5 s7 H! K; d, j  I7 F                 * When we have a better choice than SMBus calls, use a
    2 h0 e8 [$ R: Q. g                 * combined I2C message. Write address; then read up to2 g" E5 y- M& S  b9 \
                     * io_limit data bytes. Note that read page rollover helps us
    3 C# x4 w3 [. X2 N" Q                 * here (unlike writes). msgbuf is u8 and will cast to our
    5 }% T+ ?3 N" g& t5 {                 * needs./ Z/ q% R: Z, ?3 Y+ {- W' Z
                     */
    ; @1 {: S- G+ z( w3 \6 I1 u                i = 0;* K( J3 N# n, |: |. z
                    if (at24->chip.flags & AT24_FLAG_ADDR16)
    ! y! b' w8 Q3 c- G) x% q9 J, m0 }                        msgbuf[i++] = offset >> 8;5 a; w) v9 g( D( o: A1 M; n# R8 O
                    msgbuf[i++] = offset;
    1 y% A2 m9 G% v
    6 \  O$ }6 G% n; ]: n' P8 h4 E+ m% k                msg[0].addr = client->addr;6 B% ^' Z/ x* ]
                    msg[0].buf = msgbuf;
    - ?: {4 ?# S2 q                msg[0].len = i;! L4 Q5 f. o  H$ V. d0 S
    + g7 e2 c) h0 ^4 g& e( B) }
                    msg[1].addr = client->addr;
    ; z  k, u5 @- l, b                msg[1].flags = I2C_M_RD;  /* 读模式*/
    : W* {- d, @' k/ c  [                msg[1].buf = buf;
    # H- u& z% M: h: i: `                msg[1].len = count;
    # j+ F$ q: D( O1 n; [# s2 G- r6 M        }
    & r% i, @1 F: T. n( b. p* R
    + X& |  E* h2 D  Q5 Z        /*
    7 ]  i3 _: o* z' w* l8 A" S' b         * Reads fail if the previous write didn't complete yet. We may
    " k1 f8 x) x2 ]! a! L         * loop a few times until this one succeeds, waiting at least
    4 X8 J2 H" T% t" F' N0 y0 T         * long enough for one entire page write to work.
    : j; [7 S4 N$ }" y$ o4 b2 i         */
    7 i/ y  m9 b: t0 U        timeout = jiffies + msecs_to_jiffies(write_timeout);7 o. F! @3 z1 X6 o2 x3 {9 l
            do {
    ! }3 z" n. E! p1 m                read_time = jiffies;) F( i/ F" x5 ~6 g$ S* |" {' [# J
                    if (at24->use_smbus) {
    4 b( @6 e2 o- J. c, s. ?                        status = i2c_smbus_read_i2c_block_data(client, offset,
      F: L) g( ^% P! {& ?                                        count, buf);
    ! j' N, {9 y4 U- L" z3 F                } else {
    + X# o3 c4 Y8 u. i1 |/ @                        status = i2c_transfer(client->adapter, msg, 2);
    ' U1 _. T( k, j+ q; f                        if (status == 2). `$ a4 i+ r% a7 ^# U
                                    status = count;: q7 W# n1 z# T1 ^
                    }, E2 W& I* }0 d8 p
                    dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
    1 {% @9 a% W0 `. d                                count, offset, status, jiffies);
    ( p  v5 S+ P! \
    * N, e8 E! p8 A* S8 H' \' Q/ k                if (status == count)
    0 C* D' c! z  T2 Y& C: }                        return count;
    0 k5 ~3 C$ ?. U3 A
    ; ^0 E, N0 l+ y                /* REVISIT: at HZ=100, this is sloooow */
    + m5 B9 I) p0 P3 L                msleep(1);: r7 ~' G) N% H, I6 f
            } while (time_before(read_time, timeout));  /* 使用timeout */
    # N  ^% s$ m1 o9 L+ p
    : A8 i( c/ G2 M" u, Q        return -ETIMEDOUT;
    8 T5 V' z7 o8 S}0 i  J, E# p8 [0 o1 L

    8 W% U. E0 M# y8 z  Jstatic ssize_t at24_read(struct at24_data *at24,, {; r- S9 w: n1 F# b( |
                    char *buf, loff_t off, size_t count)
    ( _1 l$ W& n! Y  ?$ U1 Y{
    ! P% b6 ^* C( _        ssize_t retval = 0;/ r& Y+ y8 N1 m) k# l% o

    1 v" Y9 W; B, A# M. t5 t        if (unlikely(!count))( n+ y' r1 W& @+ j
                    return count;
    ' f% p& u" ~9 w, Q' F
    1 D5 l9 j9 S" U% _        /*
    $ M; `! p7 J0 q7 s( O" M: H         * Read data from chip, protecting against concurrent updates
    8 [, C" _2 a" g# O' V8 R- W, z, E         * from this host, but not from other I2C masters.
    0 l+ T2 y7 Y+ w$ Z         */4 f( N) \* m) U  f$ L8 v
             /* 访问设备前,加锁*/0 ]' @4 I+ {6 ~% c- i
            mutex_lock(&at24->lock);
    $ [- R2 U1 B: a) i- U
    5 g( V4 X4 a6 D: h; e( h        while (count) {
    & i& s( x- x# u                ssize_t        status;; L$ w& `! D7 I

    ! e5 s( u8 ^& v( b* ]                status = at24_eeprom_read(at24, buf, off, count);3 q, |5 G, {8 H% B9 e) G
                    if (status <= 0) {
    ; C1 n, R5 @, [, {( @# M                        if (retval == 0)
    : S! T6 k% T  E% ~2 M                                retval = status;4 \# `  u$ k* x
                            break;2 u- P" k! X0 R2 A1 V+ D
                    }
    ; ]( y' d4 f6 z0 W                buf += status;
    4 q- g3 U/ D- ]- w; {8 M# Q                off += status;% w+ y" I! ~# _9 N" N
                    count -= status;
    9 ]0 `  C, E# |! \9 C) H                retval += status;& j. p  O  E- o$ `  z
            }7 V, Q+ [" _2 g$ V& e  b

    + u4 C  W$ W- o        mutex_unlock(&at24->lock);
    + }% u9 k& v, s$ `
      V. [8 \1 K4 {$ T) u  {        return retval;# |5 F* O- Z" v
    }
    6 Z4 H# m; ?- `. ~$ @4 O
    8 ]' ~  Y( {! K8 v  p  ]) jstatic ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr,! o$ k0 T: }5 j8 D
                    char *buf, loff_t off, size_t count)8 g' V1 i/ q, ^8 r
    {: k7 T( Y  H% `
            struct at24_data *at24;9 F7 [# }. [+ h8 m. [

    ' j$ g7 }  N) I/ i) M        /* 通过kobj获取device,再获取driver_data */: |7 l5 l! w- g
            at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
    6 ~) [. e9 A  E6 _        return at24_read(at24, buf, off, count);
    * z- a' V, i. N; t# F2 F3 H0 c( @! M2 l}
    3 `$ U& K) R: I5 T% }; M( w6 q% w3 z% t2 T/ z1 q. r% M
    5. 总结
    - g, w" K) T: r" S. d    本文主要对at24c02的驱动架构进行了分析。该驱动基于i2c总线架构,提供了id表来帮助设备驱动的绑定,该驱动支持AT24CXX等多个系列,不仅仅是at24c02。
    / ~7 D  A3 ]0 S5 V, n1 U4 D; V/ Q9 Y1 B; K
    其次,该驱动并没有注册任何字符设备或者杂项设备,而是通过sys文件系统的二进制属性文件来对设备进行访问。此外,驱动同时支持I2C协议和SMBus协议来访问设备。
    # S  e& X  ~. U; @
    5 P$ M' p8 O  q) }& [. \/ ~
    9 }: p8 ^- G2 {/ O- B
    + d. ]( }  V' b* H! k. }0 W' P5 y4 r& L- o$ H

    该用户从未签到

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

    本版积分规则

    关闭

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

    EDA365公众号

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

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

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

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

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