找回密码
 注册
关于网站域名变更的通知
查看: 265|回复: 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$ _2 [% h2 }: g! I; L" o# }

    / p2 S! G1 b' {' N在开发板中,使用I2C控制器0和AT24C02连接,这里就不给出原理图了,如需要,可以搜索TQ2440开发板的原理图。
    ! T5 A4 ]  Z& e, d8 g; ^
    $ q9 C! d# T" R) d目标平台:TQ2440
    6 T1 i& X$ O6 a5 @3 b6 F: o5 n3 F5 h
    CPU:s3c2440
    4 i1 x. w7 s; o# s: C5 w- Q
    0 x1 f# O! l8 @4 m. K内核版本:2.6.323 t8 R# Y1 K% w) a( Z) o
    7 V3 A3 y! a' W3 P& c) [  d# f
    本文所有的代码均位于内核源码:linux/drivers/misc/eeprom/at24.c中。
    . z3 g6 o# ?# a8 g* r: U( O! B! Q4 t. y' X% c& [# y
    1. 模块注册和注销
    4 I$ ]6 @" i; Y  vstatic int __init at24_init(void)
    * U1 f; y( v) [5 T8 N{  `9 {$ q3 z1 t4 M7 F
            /* 将io_limit向下圆整到最近的2的幂*/( i3 Q$ Q9 ~0 i0 x1 V) \- Y- C6 s
            io_limit = rounddown_pow_of_two(io_limit);8 l' F" V8 W! ], ]6 q$ J. i
            return i2c_add_driver(&at24_driver); /* i2c 驱动注册*/  Y: C& `. Z. q9 }$ R
    }
    3 E/ b: ]/ m4 ]8 Rmodule_init(at24_init);/ d6 H. A# T- r. \! s
    9 d4 \# C+ W( y
    static void __exit at24_exit(void)! N) [7 k4 Y* S0 O3 ]
    {5 i" N7 f0 `. U* A$ t
            i2c_del_driver(&at24_driver);
    3 p+ b/ f( U8 ~6 ~9 d$ n7 g% E: O}
    ( X6 i4 I( }* O# T' P9 hmodule_exit(at24_exit);9 w6 Q2 H! x* D$ {, [' g
    6 y# C: `2 j: W, ?: R
    MODULE_DESCRIPTION("Driver for most I2C EEPROMs");0 A" L5 l. u0 a
    MODULE_AUTHOR("David Brownell and Wolfram Sang");+ C9 c3 o/ O2 k. p# M0 @" ?$ X
    MODULE_LICENSE("GPL");
    - ~8 R: @0 k# n" A
    5 K# k- j& z/ M1 p% Y
    4 }" n( i2 B/ x- ]. l, [0 m注册函数很简单。io_limit为写入时允许一次写入的最大字节,该参数为驱动模块参数,可由用户设置,默认值为128字节。2 G- k, O3 |' r8 Y3 a& v; ]; b
    + a1 D7 O# L% n: L4 }, T
    首先对io_limit向下圆整到最近的2的幂,接着直接调用了i2c_add_driver来注册一个i2c驱动。1 {4 H6 ]$ q# N9 i

    ! P) j+ N8 a0 Z/ a注销函数更简单。注销之前注册的i2c驱动。
    ' ~' F  [+ D! p5 V' f" @
    3 I; d! Y3 J7 W# A6 ]7 ?6 n/ H0 Q5 _

    ! \( l# |/ x3 N2. 设备驱动绑定
    # |, U% ^8 t7 n" j  g4 s' S熟悉I2C驱动架构的可能会知道I2C驱动的match函数,该函数将使用id表(struct i2c_device_id)和i2c设备(struct i2c_client)进行匹配,判断是否有name字段相同,如果相同则匹配完成,即可完成设备和驱动的绑定,接着便会调用驱动提供的probe方法。我们来看下驱动提供的id表。
    # m5 V1 [  G4 Y8 \) X& l" }$ V3 c& J* _8 Y/ e
    9 ^" i3 ]' B1 R. e( s
    static struct i2c_driver at24_driver = {
    8 n- d; P* h. g9 f        .driver = {# H& T% U5 ]( ]  }$ O8 w7 N( Z5 t
                    .name = "at24",% S* V" c( q+ z3 m3 a( ^5 {
                    .owner = THIS_MODULE,
    1 j0 \' W( b0 g. p: X8 q" c        },: C5 Q* ?% s. E7 \* t& Z* q9 X
            .probe = at24_probe,; X0 J, n! h( v* c2 e2 Z/ I" J
            .remove = __devexit_p(at24_remove),
    1 X4 K% K5 r  s) w" d/ k! h        .id_table = at24_ids,
    . c9 _) {# i- i! V7 r* Z7 Z& A};- S' c' Z7 J: z2 x. R- J
    驱动提供的id为at24_ids,如下:& ^7 X& }3 l# v# F
    3 |' l6 A8 J# _- q3 |
    3 H' G% `9 r) z( b" ^
    static const struct i2c_device_id at24_ids[] = {
    1 q+ c2 {6 h: a$ M$ R- V& Z        /* needs 8 addresses as A0-A2 are ignored */" Z" i5 X! G, S
            { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
    + G& S8 O- o% |5 |: A4 E: v6 R        /* old variants can't be handled with this generic entry! */
    7 ~  Q4 ^  ^$ n  {9 y& T/ F: Q        { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },) i' \2 R$ u" K% t& L2 z, {
            { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
    , p3 v* \3 i; d: F        /* spd is a 24c02 in memory DIMMs */: @7 K. e, B! g( N! G/ b
            { "spd", AT24_DEVICE_MAGIC(2048 / 8,7 N. S& [5 U( |! _7 r, R, l
                    AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
    ( U( ]3 j$ f/ I+ E! i" y6 H        { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
    % f7 v5 `( F1 ~5 S* P5 N# F0 x' X        /* 24RF08 quirk is handled at i2c-core */
    4 x$ b4 k, ^9 m  Z3 u6 W6 [        { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },: s' w. _! S; u: h& i4 F* }
            { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },8 y+ A1 _2 R  m: D4 p0 B2 P5 p
            { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },* Y6 w% W) P% O  _6 L( C! {4 i
            { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
    ) `4 Z8 N; w" Q2 b, s( R6 O        { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },! d% @' \5 s1 [
            { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
    " r( i' Z; E% e' G. S$ j        { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
    3 F; K8 D( h1 E% N5 c3 L+ F6 G        { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
    % d- C* g+ q3 V7 }1 N* q. ?6 ^        { "at24", 0 },
    4 x+ _" p1 l1 X+ M        { /* END OF LIST */ }9 J) [+ ]) n* }& L
    };
    6 Z) Q/ f9 V; k& X( z4 y结构体成员的第一个参数即为name,表示设备的名字。第二个参数,在该驱动中,为一个幻术(magic),通过AT24_DEVICE_MAGIC宏计算。7 @) T% z) d% ]

    0 S  c4 w& M7 G: y  y; J7 r1 u宏第一个参数为eeprom的大小,第二参数为一些标志位。我们看下这个宏:
    2 s9 p7 K( q) }# s2 u8 \- ?6 t, P$ D2 ?$ F: I% v' A' S$ D. N  d
    8 J- n  U: F: Z: ~& O% c
    #define AT24_SIZE_BYTELEN 5, A% v1 D. U& r0 N7 Y9 A. D3 Y
    #define AT24_SIZE_FLAGS 8/ S- e7 Q! h$ H# t& f
    4 @: C' v9 S1 z! F2 e+ }- Z
    /* create non-zero magic value for given eeprom parameters */
    , A; g& O3 I, z7 w#define AT24_DEVICE_MAGIC(_len, _flags)                 \$ `; a1 K& Y9 s# U
            ((1 << AT24_SIZE_FLAGS | (_flags))                 \  T5 E1 G2 k2 N  a' l! H+ G7 L
                << AT24_SIZE_BYTELEN | ilog2(_len))$ q! s% P" f4 G) _2 E( m( d
    在这个表中,针对这里讲解的24c02,其大小为256字节,标志位为空。
    3 ]9 ~% c+ w( M4 q3 K  p& U1 _: Z- t: x( q) r, c1 {$ h! B3 P

    ' l9 Y* I6 v$ z0 Y3 n9 A1 w. z! b( s; f
    3.probe函数
    0 C2 L3 _/ f6 e" w  Q. v; K$ ?% m    当i2c总线完成设备驱动绑定后,就会调用probe方法了。具体看下这个函数。; y: s1 u& v+ _% o

    8 i. e' a: _* \- _8 x  S. r4 C, z; S1 {' k; }5 P
    static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)1 W2 H& F" [' G( g7 Y' r# c( e
    {
    : |% g- I  B# L" P. J  V. \        struct at24_platform_data chip;/ e2 F4 X* G1 F2 |$ X+ l
            bool writable;
    2 Z/ W- K) g& L5 q" s7 B! j' j        bool use_smbus = false;
    9 i0 Z% b7 n4 v' v" U+ [        struct at24_data *at24;
    : E1 ]: c" t+ Y" ^        int err;7 {) U; M4 |, K
            unsigned i, num_addresses;4 K" S0 K$ L7 }7 t) U4 D
            kernel_ulong_t magic;1 @% y( ~& H9 Z( l3 a+ }- ^9 I
    ! t6 R7 ~5 S& ]9 Q
            /* 获取板级设备信息*/
    9 z; x; C$ |7 a) ~% L$ q        if (client->dev.platform_data) {
    # Z/ p  S. O) a3 B) Z                chip = *(struct at24_platform_data *)client->dev.platform_data;& m+ X) E+ j) `2 B/ T, {1 }
            } else {
    5 ]* _0 U& Q# E8 x# }                /* 没有板级设备信息,也没有driver_data,直接出错*/
    ) r6 P7 u% q7 y" }2 {6 @                if (!id->driver_data) {
    2 s% I4 y) f) r                        err = -ENODEV;: k2 d& Q4 K9 T1 v* c
                            goto err_out;" d4 M" a/ I) \) r: x: Z# e6 ~1 L
                    }
    * S9 w. X0 y$ f2 _% B5 \+ }                magic = id->driver_data;
    0 b, T. k+ w, S1 I3 y                chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));' j& X! z& R8 x/ f% M% s/ R. Q
                    magic >>= AT24_SIZE_BYTELEN;
    ! U0 D; S- F, @3 T, H                chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);2 f) O1 [+ U  O0 X8 i& k+ Z
                    /*
    ! j' k5 R" q2 O6 z# d3 Y                 * This is slow, but we can't know all eeproms, so we better
    4 C& u! ]( f% O                 * play safe. Specifying custom eeprom-types via platform_data9 B" [- U; ?& z3 N1 I* R
                     * is recommended anyhow.
    ( U; s( j" \) V7 |, c, }) i                 */
    2 p: |" w. S+ L6 {+ C                chip.page_size = 1;
    0 d$ _, [( ?, L! f/ U6 P) z9 I/ U; Z9 a" i/ W2 A" r
                    chip.setup = NULL;: [7 {. r: g) N, A! b
                    chip.context = NULL;* n0 U$ K4 r- {/ q" i
            }! U- a+ K9 [9 ^5 p
    * Z% @1 P* T- K6 Y) E
            /* 检查参数," N  N  @) a; y/ h- D
                byte_len和page_size必须为2的幂,不是则打印警告*/
    ! D: f  ~6 Z; u" f5 T' }: w/ A; P        if (!is_power_of_2(chip.byte_len))+ s0 x& S: d. w4 _
                    dev_warn(&client->dev,
    ) u) I# \9 Z7 w  v- {                        "byte_len looks suspicious (no power of 2)!\n");
    # g) O) b0 ]  k& v        if (!is_power_of_2(chip.page_size)). K5 w2 e: r3 P5 n1 x' O: l
                    dev_warn(&client->dev,; u# K$ n$ b. z1 ?
                            "page_size looks suspicious (no power of 2)!\n");. o9 f# T( i7 E/ j) A& u6 J

    ) g+ |3 N% q/ a3 M# i5 C2 Q        /* Use I2C operations unless we're stuck with SMBus extensions. */
    . e: r: `( q3 j+ l4 N. `9 x/ @6 n3 m        /* 检查是否支持I2C协议,( g/ C: V& |" r
                如果不支持,则检查是否使用SMBUS协议*/
    0 S. q, g! T  y, ~( \/ g2 W        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
    ) F$ [: w- J8 L0 P( p% \                /* 不支持I2C协议,但是使用16位地址,出错*/
    & @) ]% m8 I2 S% R- h. E/ y9 T                if (chip.flags & AT24_FLAG_ADDR16) {6 A8 R# s- C8 Q/ s% w
                            err = -EPFNOSUPPORT;. Z0 @1 R/ t) {2 O6 P, O9 i. \
                            goto err_out;* |0 p5 a4 `5 b- f
                    }
    ; T; u: Y. G. V  R' J                /*  不支持I2C协议,使用8位地址,
    7 R# h9 c$ x, }" \; I- `* N, s                   但是不支持I2C_FUNC_SMBUS_READ_I2C_BLOCK,出错*/
    9 S" L* ^1 I; x4 H6 G' F6 H                if (!i2c_check_functionality(client->adapter,$ F6 c0 O9 ?$ T0 n- h, r
                                    I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
    9 p) W5 d! U- ]& K6 P9 ]$ V                        err = -EPFNOSUPPORT;) a# f% E/ h( B) {! \  w
                            goto err_out;
    : {& p  V1 ~: p2 A$ V                }( S! R: T# u7 y& U2 O
                    use_smbus = true; /*使用 SMBUS协议*/( [" Q8 o+ O+ \& K5 y
            }4 F% q/ o4 {1 _6 L) N4 L
    * P5 ~8 E& u  B* y% |5 z
            /*是否使用8个地址,根据id表,
    9 K$ o  l1 e! v: W' M  x% r8 @2 r            目前只有AT24C00使用8个地址,其他都为1个*/. _$ Q1 K- S4 u( n- u
            if (chip.flags & AT24_FLAG_TAKE8ADDR)( K" O; D% o2 H% [% L
                    num_addresses = 8;
    - z" Y  i, t0 [1 ]0 ^+ }        else
    $ A6 K. X7 l- D5 e. n                /* 24C02需要1个地址,24C04为2个,以此类推*/+ S& q8 ~# U& M8 C8 d
                    num_addresses =        DIV_ROUND_UP(chip.byte_len,& k; \: y9 l5 L  Z# I/ p
                            (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
    1 E/ V5 b. r8 v- ^) P+ k$ m" D) L7 r* n8 P% m
            /* 分配struct at24_data,同时根据地址个数分配struct i2c_client*/
    * I4 z2 g) d4 c        at24 = kzalloc(sizeof(struct at24_data) +
    7 V0 z! l( G" S                num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
      |9 o4 z9 |* N1 A5 d/ b        if (!at24) {% S0 r! _# C) z6 q
                    err = -ENOMEM;
    9 d- X) p( \. _: B5 z# G- H                goto err_out;3 `8 R5 d8 G' f% q+ {
            }
    ! @# k- t6 d3 |/ o6 t: U
    ' l$ C9 @# _& J; N1 S% `* z0 S        /* 初始化struct at24_data*/" T+ f1 v$ i8 i) Q) ~8 G+ a+ i% S; }
            mutex_init(&at24->lock);
    / U( W  W7 b9 t6 D$ A8 ?/ {        at24->use_smbus = use_smbus;
    ; m7 X8 y. c8 N* m* `- r" K2 @        at24->chip = chip;
    $ b4 Z; Q1 H' ~( }6 E0 I        at24->num_addresses = num_addresses;7 W# {4 P& X7 T% S% Y
    $ x! W5 P/ T! l; c0 g6 F9 m8 S& w" V- E2 e
            /*
    4 P* D' f( }% `% Z# r5 N- z         * Export the EEPROM bytes through sysfs, since that's convenient.
    # O0 Q, t% ^) {+ S5 s         * By default, only root should see the data (maybe passwords etc)- G7 G6 e/ ]2 ^9 B0 }& G3 ?
             */$ G9 a" X( c3 X, U1 E8 F, k0 }
             /* 设置bin_attribute字段,二进制文件名为eeprom,1 i0 z& L' ]" v" f
                 通过它即可读写设备 */
    + K+ o  a" ?2 c        at24->bin.attr.name = "eeprom";' R7 x1 l. M; z0 t4 Z
            at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;3 h! t" o, J; X/ K( A
            at24->bin.read = at24_bin_read;1 ?; L/ E( @3 q8 j
            at24->bin.size = chip.byte_len;8 o" a5 g6 J. X& s, g
    & \$ d" n  \2 I1 W* I
            at24->macc.read = at24_macc_read;  /***  先忽略***/* A2 T; z" I8 b& H& _

    ' I  f3 l  @" a        /* 判断设备是否可写*/
    # [3 n5 t. X$ z; P+ v: z, C! c        writable = !(chip.flags & AT24_FLAG_READONLY);
    : _" P5 ?2 x) ?. i  M, V6 C2 R        if (writable) {
    ' A* p7 M2 N7 `" U* Z                if (!use_smbus || i2c_check_functionality(client->adapter,& Z* q$ e  n! N, h* Y3 }# n
                                    I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {+ @, F8 V2 X+ I

    2 [! i1 R3 ]2 D                        unsigned write_max = chip.page_size;* B. h; u; K. N6 G! D9 x

    : k8 Q$ }( s4 I" D# x                        at24->macc.write = at24_macc_write;  /***  先忽略***/7 E! h' X5 I3 ~& ?

    ' Y! O: N2 [: D( A% o                        at24->bin.write = at24_bin_write;    /* 写函数*/
      H8 I. ~: |- `% a  w                        at24->bin.attr.mode |= S_IWUSR;    /* 文件拥有者可写*/
    5 }* q0 A/ K6 r' D  u) E' }/ l" ^; V
                            if (write_max > io_limit)  /* 一次最多写io_limit个字节*/
    / I3 W' L+ {/ T. y( b9 I4 Q                                write_max = io_limit;2 z* p! a+ }# |, Y6 D' Y8 _, _
                            /* 如果使用smbus,对write_max检查*/; {9 u. X9 }% [
                            if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)* u# d6 q  g* l: B+ v1 w
                                    write_max = I2C_SMBUS_BLOCK_MAX;
    ; Q, _" `# x) D                        at24->write_max = write_max;  8 V/ z) j& c' h: h
    % X5 N# U" D( v" U
                            /* buffer (data + address at the beginning) */& U3 a' h( ^- w8 C0 g" G# a
                            /* 分配写缓冲区,多余两个字节用于保存寄存器地址*/
    7 c& _8 D1 X8 ?& b3 ~) m                        at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
    9 J7 K4 Q& j9 T) K* r3 B+ L                        if (!at24->writebuf) {; n1 r) p# F- d& Z  l! `, R
                                    err = -ENOMEM;8 E4 S0 X( B5 {  ^9 M) f" {6 A
                                    goto err_struct;/ ~/ y' `- r1 E- R2 ?. y
                            }" O. B" D( F/ r& X- V1 O1 M
                    } else {1 o- f6 G5 K# H# {
                            dev_warn(&client->dev,/ a! A( p" @4 Q+ B
                                    "cannot write due to controller restrictions.");
    0 X: _; S! {% p, R$ U  Q$ t3 n                }5 k6 l8 ?" W/ ]1 q$ i
            }( p# ]7 p, b1 ?* l7 I# N( `( D
    6 A  O) a% r/ G: V9 h
            at24->client[0] = client;  /* 保存i2c设备client*/
    6 H/ @2 }9 ?% ^6 Q) e. C7 ^& T3 B$ d4 ^
            /* use dummy devices for multiple-address chips */
    3 W5 W: l/ u* ], W" @* d        /* 为其余设备地址注册一个dummy设备*/. ?1 z+ p; \" O# y
            for (i = 1; i < num_addresses; i++) {$ l. V0 A/ k) k% a  U- n1 H
                    at24->client[i] = i2c_new_dummy(client->adapter,. D/ V: w" p0 P4 m: R! k
                                            client->addr + i);     /* 设备地址每次加1 */7 Y) O7 ?5 \0 F" K
                    if (!at24->client[i]) {
    0 J6 _7 c9 x' Z, E                        dev_err(&client->dev, "address 0x%02x unavailable\n",
    ! Q- y) ^5 S8 t9 N4 S) ~% X6 d                                        client->addr + i);
    ; u* z  ]0 N+ j3 G% O+ c. h* d                        err = -EADDRINUSE;; ]+ h/ q1 n1 [) ~+ x; a, b
                            goto err_clients;6 e! v7 a: C( \. N0 Q
                    }  @+ e( S5 c7 s2 A, }
            }* C0 q7 Y, a6 [% o5 m5 {1 J3 t0 ~

    . {' _+ _# P" Y5 E" C        /* 创建二进制属性*/, b. v8 m+ f- w! r* Y. Y! S) }: x8 j
            err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
    / m/ V* i  C5 P& f! ]! b. E% V        if (err)
    2 f# w8 p3 }! Y                goto err_clients;) C# h6 a$ k5 \; H

    / ]/ e: ^# |4 h6 d9 a5 t$ I; y$ p        i2c_set_clientdata(client, at24);  /* 保存驱动数据*/
    * v, x' F  k. G" L6 g
    2 N3 i3 x+ z; j4 T        /* 打印设备信息*/
    ( r2 h# E* N0 M2 p6 {        dev_info(&client->dev, "%zu byte %s EEPROM %s\n",
    0 G; W) F( g1 y1 j3 n+ z7 J9 R                at24->bin.size, client->name,
    * }6 m( p2 W$ x" E# s* b% |                writable ? "(writable)" : "(read-only)");+ C. @) L* x* r  u5 `/ X4 ~9 Q* G! B
            dev_dbg(&client->dev,3 o, c' A  E+ C& P3 s/ L4 S
                    "page_size %d, num_addresses %d, write_max %d%s\n",0 h9 M" R, r$ ^
                    chip.page_size, num_addresses,
    ! H2 l+ h; |6 Z( ]                at24->write_max,
    2 P6 G( @- J+ _' m+ |) `: i4 r2 R$ C                use_smbus ? ", use_smbus" : "");9 D8 k+ \! c/ g# f! Z
    2 J8 w. h' |/ C9 i& `% V
            /* export data to kernel code */
    1 q4 h- ?1 X' p- `' x) C3 k+ G7 k        if (chip.setup)
    : K8 V; M) @1 @9 r! T7 h6 \3 g, ~: z                chip.setup(&at24->macc, chip.context);
    ( F' q& |& c& d" B8 F2 ~3 ?
    3 `4 Y/ I! \" b" R        return 0;
    1 S, ?6 _$ J; t$ {
    - l; J9 j9 P) {, z, _err_clients:, x% J' J, o3 I
            for (i = 1; i < num_addresses; i++)
    ( O4 h4 a( J3 ?$ R) j% [' I3 |                if (at24->client[i])  U2 z/ l* n) t0 l4 w
                            i2c_unregister_device(at24->client[i]);( C+ m4 l) _6 X) h

    * K, m2 o5 R+ `, {* f& L: _: x3 ^        kfree(at24->writebuf);
    " i" H; d6 {+ X3 L* Ierr_struct:
    . [+ r* b5 ^* W+ o        kfree(at24);
    3 ]1 n2 ?  a% Q" \# U}$ k8 l. q$ ?5 q, x) O
    驱动首先获取板级设备信息(client->dev.platform_data),我们假设驱动移植时,添加了该板级设备信息。$ E7 g3 @5 t0 J* k  v- T! ~; |. Y
    判断是使用I2C协议还是SMBus协议。在这里,I2C adpater使用I2C协议。
    + v' s- C7 _) }, f# a& W' [/ U& G, N+ t) P- F8 W6 B; w* Z* o$ E9 {
    然后,判断设备需要多少个i2c设备地址。
    4 K  W, J) m6 u8 @% ~4 a" p
    4 M5 y* e5 u' T; F2 m这里补充下:根据at24c02的datasheet,设备地址的第1位到第3位,将根据不同的设备来进行设置。
    . T: ^6 r% `4 @2 |) d4 J
    & ]/ D9 n8 h' M例如,如果是at24c04,则设备地址的第1位将用来表示寄存器地址,因为内存大小为512字节,而寄存器地址只有8位(256字节),
    ( u; h; H$ _( Z: I' K0 z5 ~8 w8 Y/ Z; O- x3 ~8 Q
    需要额外的一位用来表示512字节,因此使用了设备地址当中的一位来实现此目的。具体的请看datasheet。
    * G, F8 @9 P+ J
    $ F+ T' ?. v: V- t7 R1 h% n8 E9 o这里使用at24c02,num_addresses将为1。. G9 A/ Z3 ]: ~* Z( |
    9 ~. o3 N+ N; B) r9 _( t
    接着分配struct at24_data和struct i2c_client指针数组空间。8 c) s- S* X, |: z* Q
    2 P3 q" f+ U: p8 ]
    然后对struct at24_data进行了初始化工作。
    & |8 g' ?% H- w) o9 R
    2 W3 Z+ t; k' D/ g* S7 I) M# t4 n1 i接着,对二进制属性进行了配置。名字为eeprom,同时配置了其读方法(at24_bin_read),如果设备可写,还将配置其写方法(at24_bin_write)。' e3 O; N* E$ s

    ( _9 ^. Q! u; [2 B接下来很重要的一步,如果设备使用多个地址,则需要为所有地址(除了第一个地址)分配一个dummy device,这样这些地址就不会被其他的I2C设备占用了。( z" C# S  I3 X" M* R

    ; e6 c/ O$ X. B% d最后,向sys文件系统注册了二进制属性文件,通过该二进制文件,用户即可访问该设备。
    + n# [9 o6 J2 J
    3 e. V( }8 J/ k+ x! d$ a7 }3 q注意:驱动使用了struct memory_accessor的东东,对这个东东不是太了解,所以先忽略,这个东西不影响驱动整体的架构。
    / B- i0 q9 z0 a
    ; W2 }! m, W, ]" ^* W$ @& Z( r# R  e' {" C0 O$ u% _
      C* q, v% P/ O, J3 ?7 u1 T- l
    4.设备访问方法
    / c) P" d6 T/ y. f$ ]* D  Y   从第3结的分析可知,驱动并没有注册任何字符设备或者杂项设备,只是向sys文件系统注册了一个二进制属性文件。因此要访问设备,必须通过该文件的读写函数来。! n/ A4 I# ]) S: N( |% W& n/ U. |

    - Y, d7 W+ c$ P  b$ |! B- \8 `7 V0 E读写函数在probe函数中指定为at24_bin_write和at24_bin_read,我们来分别看下。
    7 ~2 i' [1 H' [( m& R/ @8 y2 K
    7 `* T5 E9 [' C2 h4.1 写函数(at24_bin_write)* b5 G1 g1 z8 c# ^( F0 w
    static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr,3 ~6 p7 X: V1 e2 Q6 `
                    char *buf, loff_t off, size_t count)2 @8 a0 j+ r& j$ p- Z( N
    {
    & ]8 l" k& j" a* B2 m        struct at24_data *at24;8 E# ~7 x7 P* ^$ I
    5 h7 y: m% D9 X+ ~1 r* }
            /* 通过kobj获取device,再获取driver_data */2 p; Q+ {) {( z: I
            at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
    ( h4 g, R5 i  S' B& R0 S' S2 [2 L        return at24_write(at24, buf, off, count);0 v2 F, y# w' ?9 J. s
    }
    / G* o- j: m  s该函数首先通过kobj获取了struct device的指针,再获取了at24。
    9 o* |% o6 k0 e) k9 I1 {接着直接调用了at24_write。如下:
    7 M8 @6 m, K3 y# x
    ! x" |) n8 ^7 ftatic ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,; K; j: ?" S% |8 U, C
                              size_t count)8 I4 y& z. X8 Y: L' m+ x, o
    {
    3 n& Z4 G" m' S$ q4 V* O        ssize_t retval = 0;6 W6 b' d. ^8 j& X' q( P: [( q  Z
    1 U( a# o! S7 G+ V7 a% X
            if (unlikely(!count))
    9 L6 m1 Y, E; W7 ?                return count;
    ! C3 h5 I! E0 m4 B
    ( S7 Z8 d: ]& D2 s* ]' r        /*6 h) v# t+ b% u$ n* v( @3 c0 X" R
             * Write data to chip, protecting against concurrent updates+ E9 h3 E: {/ q9 \; \. g
             * from this host, but not from other I2C masters.
    ' {. i% E- X+ P- D, B, ]8 `0 c         */( ^: e+ k$ p( y/ ]2 \& Z
              /* 访问设备前,加锁*/) w% `1 u4 j; ?8 l0 {- z
            mutex_lock(&at24->lock);
    2 j, j6 K0 g1 G  n/ b$ q
      H% _2 \  I" J- N% |3 m        while (count) {
    $ e" E) R# T& B. A; @9 Z                ssize_t        status;# M+ r3 q8 I1 Y+ G1 r9 t. u: g
    ! W: x, V- _% e0 E* R
                    status = at24_eeprom_write(at24, buf, off, count);# i) y, Z/ {; A7 f+ G
                    if (status <= 0) {1 U  L1 X2 |) _+ R% J6 D- U
                            if (retval == 0)# U8 o$ S/ V1 w
                                    retval = status;
    ) `2 d6 i) o( ^9 h' P0 [2 K                        break;
    6 ?2 b9 k7 ~8 T, v                }
    3 o, I% e) T1 m" o; G                buf += status;
      w! V' Y3 b% G6 J( H7 h" |# e5 W0 i                off += status;7 i0 f: E7 @- {9 S  S* B5 P
                    count -= status;
    1 I1 @- C  z$ [+ Q# r7 \; R8 N                retval += status;+ N5 F' d3 u2 z! n* H4 [: `1 R
            }
    5 L3 m- Z- @- P3 z# Q4 u% k& ~- t. Q% W6 Q( \* I& D2 F) T
            mutex_unlock(&at24->lock);4 H2 i9 l9 B0 `+ W

    + o- G* X/ p% b8 O' U# W% w9 c* D        return retval;  t* V8 L0 I& T" V9 S  ]& T1 w
    }
    - K& X7 a6 T0 A" C$ z5 }9 l0 N+ N& g& r8 j9 q
    该函数不复杂。在访问设备前,首先加锁互斥体,以防止竞态。然后根据count来调用at24_eeprom_write函数将数据写入设备。2 u; R8 j" k; h" S2 ^) }( {
    写入成功后,更新偏移量等信息,如果还需要写入,则再次调用at24_eeprom_write函数。
    , j1 u# G  ]: C4 n/ c2 L. y1 _. ]& r2 y% i0 v
    看下at24_eeprom_write函数:8 Q- c" n' u8 s7 F# U! G1 q7 H  `
    ' k+ Y, C  u% Z) Z! d7 W7 |) u
    /*
    7 `; M$ T5 B; |9 _6 u * Note that if the hardware write-protect pin is pulled high, the whole
    # W9 ^8 N, H6 C: L3 b; O5 B+ ?7 U * chip is normally write protected. But there are plenty of product
    1 ]" P9 Y0 F0 }) n% N * variants here, including OTP fuses and partial chip protect.9 H/ ~) T, b2 C( s$ U3 h
    *
    # Y& @1 A* T, d* s" }& i * We only use page mode writes; the alternative is sloooow. This routine0 m9 j5 x$ p* D5 ^1 ^: Q
    * writes at most one page.
    0 @+ |, B6 w& J; N) p */3 O# K) t7 P- J% c* J; X- O9 }
    static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
    9 s' _6 _' G4 X* P- t$ i                unsigned offset, size_t count)
    0 s& ^- M% |9 D) ?! k8 M{, |7 C  u1 ^7 ~4 I
            struct i2c_client *client;3 j4 Q- D( q; b8 x& b
            struct i2c_msg msg;
    0 E; [6 C5 b% ^        ssize_t status;
    : ?& o4 d# C4 n% f1 m. ]        unsigned long timeout, write_time;' l0 Y6 I. ?% q
            unsigned next_page;
    / v: @  D' W: J  `+ U. P& e$ h) P3 d) ^. P5 }
            /* Get corresponding I2C address and adjust offset */$ p7 B. r% {+ Z& m5 l" S
            client = at24_translate_offset(at24, &offset);- e. _2 r  `- x. B, I6 [: C

    . l1 _6 r6 v. o% ~; R. o: w        /* write_max is at most a page */4 t8 r$ Y0 ]# J
            /* 检查写入的字节数*/
    # i3 o2 C4 [3 h; F/ `        if (count > at24->write_max), \, t6 S$ Z9 g' k, Q* k, w
                    count = at24->write_max;9 j+ ]) \+ n4 r2 B% Z) @3 L( C) W: t
    5 ^) O. ~0 h/ J5 q8 q1 @
            /* Never roll over backwards, to the start of this page */- m% E" P  ^! c" y, ~5 a
            /* 写入不会超过下一页的边界*/$ r* u0 g/ ^8 r: c- G' ^( _+ o1 a
            next_page = roundup(offset + 1, at24->chip.page_size);
    0 O3 m# I! }5 E( \, Y" u        /* 根据页大小调整count*/
    ! C8 J8 S5 @7 d        if (offset + count > next_page)
    , T+ W4 j: S  }- x                count = next_page - offset;1 f7 p4 x% }" a, \
    * _) }: k/ N" _, @
            /* If we'll use I2C calls for I/O, set up the message */) \( K6 i  }6 @- t/ Z# v  O
            /* 使用I2C协议,需要填充msg*/' @  `: U& |3 y% m( ?& B
            if (!at24->use_smbus) {5 T7 K' |/ E& z/ ~+ h
                    int i = 0;
    8 o8 q; {( H. t+ e3 R; c  d- M- b; q+ M; l3 _  t. @6 `, ]0 H
                    msg.addr = client->addr;  /*设备地址*/
    ' T) d% [; I5 `. ^) o                msg.flags = 0;
    # x4 C2 d$ Y' W  `9 g
    9 G9 k0 B3 R8 U$ h) }                /* msg.buf is u8 and casts will mask the values */: b" R( K; h( m3 V# O' u2 F
                    /* 使用writebuf作为发送缓冲区 */5 q8 D  ?) }9 }3 r& w. g
                    msg.buf = at24->writebuf;
    % p8 j) e4 u9 U                # `9 v# n+ s% r8 _
                    /* 根据是8位还是16位地址,msg.buf的前一(两)个字节7 C2 e1 `1 F: F( d
                         为设备内部的寄存器地址*/0 |! l1 T) y7 y
                    if (at24->chip.flags & AT24_FLAG_ADDR16)
    3 M7 Z  L5 x; y; Z                        msg.buf[i++] = offset >> 8;  /* 16位地址,先写高位地址*/
    7 i7 E- d/ G2 I' n  F+ r( L# e& s' N$ }  C+ i
                    msg.buf[i++] = offset;+ s; {/ P5 Q5 I! c0 K0 \/ L
                    /* 复制需要发送的数据 */( }6 C9 m7 {% O0 L* r, w% R8 A
                    mEMCpy(&msg.buf[i], buf, count);
    9 m/ N2 e, h* N' {5 ?" [$ g                msg.len = i + count;   /* 发送长度为数据长度加上地址长度*/
    6 |  f7 U, Q" U. p* \7 e; j        }
    ( P4 Y% q6 w3 i& M8 r& n, i
    & O5 H9 M9 r1 }) ~$ w- Q        /*" A' A! u7 }8 H: x8 ^
             * Writes fail if the previous one didn't complete yet. We may; h* I' u' i* V; z3 T
             * loop a few times until this one succeeds, waiting at least* G1 I( f+ ]* H; u
             * long enough for one entire page write to work.' X2 G  ]0 A1 T; P1 s/ Y
             */3 @1 C! _( q8 m  v' g" G
            timeout = jiffies + msecs_to_jiffies(write_timeout);' M* X) B! V8 \$ i% o) o2 b
            do {5 M7 W0 Z+ |3 T. [' \0 u% ?
                    write_time = jiffies;7 I- {! i5 i- k# P0 F8 t( m/ j- P9 l
                    if (at24->use_smbus) {5 c' v/ d$ |% R% B5 F% I7 W) @
                            /* 使用SMBus协议发送*/
    ; I. s) Y, g  G$ K                        status = i2c_smbus_write_i2c_block_data(client,$ j4 M- @6 q4 p0 h$ u0 `/ ~
                                            offset, count, buf);
    8 E3 s% m, [. B                        if (status == 0)
    2 D* o& m# g# w; l- k) ^: T+ b                                status = count;" B2 A1 c4 |: `4 ~1 o; b* I+ H; K
                    } else {
    : M6 o4 h: p4 k6 U% l! o                        /* 使用I2C协议发送*/
    + W' S  W1 S( U  i+ {2 s+ P9 z9 f5 q7 y7 C                        status = i2c_transfer(client->adapter, &msg, 1);' ]: g; K+ T: v) I* i
                            if (status == 1)6 H3 q; w7 U4 O/ A" u
                                    status = count;
    ' o& D- g! O+ d$ |2 `' T; {! P                }
    $ [( u  i' n- H# Q$ ?                dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
    : |5 l% }' z( u' A% |                                count, offset, status, jiffies);
    2 ~# a6 p9 ?  j$ S3 `* q. R0 }) h* i6 c# b- c* H  K
                    if (status == count)3 O; F7 I# t* n- V9 G6 u( C% V' j
                            return count;  /* 已全部写入,返回*/
    $ Y6 c1 r9 |0 n$ z0 c6 h5 @- `. \- {8 o% k
                    /* REVISIT: at HZ=100, this is sloooow */
    1 X: l4 C4 v/ ^1 J& ~                msleep(1);% t* A, L+ f! b# y
            } while (time_before(write_time, timeout));  /* 使用timeout */
    5 k* e# t& f% V& G# A0 _- M" G. h9 ?  W( ~! ^) m' f7 s* D3 z% }+ I
            return -ETIMEDOUT;  /* 超时,返回错误*/- V# |2 j9 c# K# f4 c9 f6 O$ K
    }: p. H& h" B! i; {
    / T$ J: Y; D" R( J' V8 c' b! V
    该函数首先调用了at24_translate_offset函数,来获取地址对应的client:
    / n" q) R+ Y. y* U( s. h/*; u/ g5 c& i( w
    * This routine supports chips which consume multiple I2C addresses. It) d* w7 o& u& B0 D
    * computes the addressing information to be used for a given r/w request." g5 n) h7 H3 p" \! d
    * Assumes that sanity checks for offset happened at sysfs-layer.  c5 \7 g2 }9 \. T" g
    */
    ' {7 e7 m3 ^9 p- N7 |# ]static struct i2c_client *at24_translate_offset(struct at24_data *at24,( K3 I8 x" \: q: [' T" J
                    unsigned *offset)
    7 u" l% A8 f; @/ x' W( j6 u2 Z+ x6 H{
    ( S# _1 U5 b# B        unsigned i;) {: o% u* N, O. Z' r+ j
    - U9 l: e/ I" |2 r7 R
            /* 有多个I2C设备地址,根据offset获取该地址对应的client*/. n: n' x- U9 H; E9 m6 X; M
            if (at24->chip.flags & AT24_FLAG_ADDR16) {
    $ R# w1 b4 |  K# r9 n9 R                i = *offset >> 16;5 c. @0 ^4 u. n. T
                    *offset &= 0xffff;
    ) z1 t0 ~9 Y, t! X9 Y5 T: Y$ G        } else {
    $ Q9 ]3 F5 a+ T( b                i = *offset >> 8;
    # c$ Y; P; V/ ?* u                *offset &= 0xff;; a2 {" X& I( p
            }
    2 N! T# @# [% Z$ [4 D
    1 {9 H7 p+ l& R7 w5 _; I) O1 V        return at24->client[i];5 k" r& P- J) |& h
    }5 @" V) [' q; r. X% G/ X/ J9 s1 P

    / w, F* \9 \1 y4 n* T5 U然后,对写入的字节数(count)进行了调整。
    , h! {% X, M( U5 y& I; l0 a! F随后,如果使用I2C协议,则要组建msg用于发送。
    4 G7 \1 @& d0 d& w% E+ [+ I% C) N7 \
    " j! D/ a8 `# C4 d最后,根据使用I2C还是SMBus协议,调用相应的发送函数来发送数据。
    / f" @  z- }- n3 H6 N* j  S, h# @/ R* p  J: _% C
    注意的是,这里使用了超时,超时时间write_timeout为驱动模块参数,可由用户设置,默认为25ms。如果发送超时了,while循环将终止。* T4 x; ]8 b. y: }4 x& P
    ! n. Y6 ^# |  m
    至此,at24c02的写入过程就结束了。
    9 P/ b7 i& u/ R5 Y9 {6 e9 Q$ m: |0 Y# e# M
    4.2 读函数(at24_bin_read)3 q' C3 a+ u, I/ S' n1 h
      写函数和读函数非常相似,只是在使用I2C协议时,组建的msg有所不同。同样读函数也使用了超时。0 U" d  X7 J5 w( G" G4 t9 u$ S
    # z6 i' c% g+ R5 H
      因此,这里仅仅给出代码:2 V  m6 S2 g- _' F: Y
    /*) b, I! ~% Z+ n( \
    * This routine supports chips which consume multiple I2C addresses. It; Y+ k$ [9 }; q/ G& E
    * computes the addressing information to be used for a given r/w request.
    # M# n+ t* T5 t" ~- ]) V3 C * Assumes that sanity checks for offset happened at sysfs-layer.
      D: C' N6 Q7 B% ?  q3 ^7 {5 P2 Z */
    ' u$ ?# [/ g2 z3 n9 R9 mstatic struct i2c_client *at24_translate_offset(struct at24_data *at24,
    9 E3 q/ e( o+ j5 F* f( {3 h, J; [                unsigned *offset)7 ]4 i) `9 d0 Y9 E- Q# w
    {
    + F+ ]) H5 Z" B3 @" O        unsigned i;( d* ]$ N: o+ B' F6 i
    9 h  w/ H: ]8 T
            /* 有多个I2C设备地址,根据offset获取该地址对应的client*/* I3 U0 s/ l& X7 p! i
            if (at24->chip.flags & AT24_FLAG_ADDR16) {3 ^8 n" }8 ~: R% t" t$ J
                    i = *offset >> 16;+ k. u' t5 |- u3 _
                    *offset &= 0xffff;
    ) {7 A+ s" E; e3 A. t0 w        } else {( |( j% m1 u6 i4 E+ F* ~
                    i = *offset >> 8;( T' [& K! P) Q
                    *offset &= 0xff;
    0 @% }7 a2 N9 S4 S( T) m8 a- S        }* {) X' M4 h+ A& C

    ; \0 ^7 h* l, {" P1 W& \' n        return at24->client[i];
    ! H6 n7 E% Z; r- D+ A. j6 F( F! P}  Z3 m& f+ ?& \3 k$ a8 y+ G7 R
    4 v2 Z0 `! q& W2 u3 G+ y. ^
    static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
    ) `6 t6 ?' D" H9 O0 [                unsigned offset, size_t count)
    ; b5 z/ o& d7 x# J: H7 \) s6 b{1 J' C; h' R0 A6 {( s2 }* w
            struct i2c_msg msg[2];- Q. {! |* {0 r+ _+ p2 }# {
            u8 msgbuf[2];! r/ a5 M& r5 X- I) A9 }" b
            struct i2c_client *client;* T% z' N3 @6 _! i2 M) [, ]
            unsigned long timeout, read_time;0 x2 I) E- u: h7 L, [
            int status, i;. L6 P$ O+ s9 @6 |  i- N. t

    7 E. w: k2 u7 m) i- @4 J# s7 y: s. v* y        memset(msg, 0, sizeof(msg));2 A9 H# L4 r* D3 o, V8 j% N* |; W) x
    % p' U  c8 Y" ~3 S* d
            /*
    & Z  e2 X* E9 ?- D         * REVISIT some multi-address chips don't rollover page reads to
    2 _% r( n8 N! u$ q0 m5 B# B         * the next slave address, so we may need to truncate the count.
    ; L  M' @# @4 V" M         * Those chips might need another quirk flag.
    7 P9 o" M0 j  T5 r         *. s, M# e+ G! x, |0 Y$ J( d
             * If the real hardware used four adjacent 24c02 chips and that! f; F# ]# ~, H# _7 T" B% Y1 U' c
             * were misconfigured as one 24c08, that would be a similar effect:
    # q$ Z% x) r) r. `; I         * one "eeprom" file not four, but larger reads would fail when8 P. e7 l4 `4 n. {; Z
             * they crossed certain pages.
    0 d" ?) Q' O0 j/ Y8 O& d         */
      C% b! }+ X* S  x; }! D1 U
    / E/ o# ~- n3 d' i6 F5 ^        /*# J2 |$ V0 E: p: O0 U. _
             * Slave address and byte offset derive from the offset. Always
      N; T/ K6 N. K; w7 @3 E2 m         * set the byte address; on a multi-master board, another master4 I: u  x" p5 {$ S3 b
             * may have changed the chip's "current" address pointer.
    ' G5 g1 n9 ?1 R+ i- N4 A         */4 ?' f" J7 Q8 w
            client = at24_translate_offset(at24, &offset);
    ; u- J" H! r! Y# p6 a3 z5 F  ^( k* j* a
            if (count > io_limit); @( P% @, p9 d/ J5 e" G% r+ g
                    count = io_limit;
    + _- R; v" @" Z3 A. A( o7 k( k  _/ V6 m; G5 l! E  g* C) l1 m2 \7 B
            if (at24->use_smbus) {9 `8 ]  Y9 `: l& L
                    /* Smaller eeproms can work given some SMBus extension calls */4 n4 s8 m5 s' {) ]. I( {% y8 t6 b
                    if (count > I2C_SMBUS_BLOCK_MAX)
    , L( }1 M  w, ?                        count = I2C_SMBUS_BLOCK_MAX;: |& j& C( V& b+ i& a
            } else {
    8 o, y! S3 W8 C( t- u                /* 使用I2C协议,需要填充msg*/
    0 o$ u3 }+ }, s5 N6 A$ ~                /*
    / P2 T! H3 b. \' h9 j8 n5 ^* D                 * When we have a better choice than SMBus calls, use a. N6 \# ^1 l9 _2 ~4 m
                     * combined I2C message. Write address; then read up to
    & ^+ S7 h; ]  H1 i4 V. {                 * io_limit data bytes. Note that read page rollover helps us3 ]* J- K! ]( U% S9 X
                     * here (unlike writes). msgbuf is u8 and will cast to our
    ) T: C' k( W, l                 * needs.& z" N/ ~, [2 K: D3 E( _6 T
                     */
    % N6 o) O6 t) J9 d                i = 0;1 T$ t. {! v6 ~: V, N6 q
                    if (at24->chip.flags & AT24_FLAG_ADDR16)5 X/ R/ s7 |3 s9 `+ w* [
                            msgbuf[i++] = offset >> 8;* j& D% A. L& f2 x( E, v9 x
                    msgbuf[i++] = offset;, H8 T5 q5 j$ Z1 K9 d# ]0 e3 a, r

    ; _0 a! x/ r8 l: N/ {1 T/ E                msg[0].addr = client->addr;
    , v: n+ p6 L3 P" `6 G0 l5 Z                msg[0].buf = msgbuf;
    ) ^. X; A) u5 ]. V5 Z                msg[0].len = i;/ e) j5 H8 k6 N( \% n
    2 Q" D3 z0 n, `4 J3 [
                    msg[1].addr = client->addr;
    2 c7 I# H1 h$ ~" \                msg[1].flags = I2C_M_RD;  /* 读模式*/5 |7 y4 C! q- B/ G0 R! G: f
                    msg[1].buf = buf;' r2 G: \& I. m+ d1 F& Z- Q
                    msg[1].len = count;& ?% Q: H4 s  }3 v4 h" z
            }6 C# R+ X  _4 ^5 E  a
    7 d# F: X4 M! y3 E( l
            /*( K5 E: R/ g- Z# J% K& L7 C
             * Reads fail if the previous write didn't complete yet. We may0 ^. ^) y$ A+ p& n5 v: L
             * loop a few times until this one succeeds, waiting at least; T5 T7 M* e. ~( F0 ^& J% E
             * long enough for one entire page write to work.6 c, [0 A$ S8 o- I0 _, w0 j  ?
             */
    ; p# T+ V' C! Y, \+ Y3 ?" K( \        timeout = jiffies + msecs_to_jiffies(write_timeout);
    3 s! {8 W' u) P% n: T, q+ n        do {" e! L; }- F# b) E9 T
                    read_time = jiffies;1 v" c: {+ A% N; Y. i7 Y. f8 ?: k
                    if (at24->use_smbus) {
    - i* m" d4 `3 j; R                        status = i2c_smbus_read_i2c_block_data(client, offset,7 k2 w, P: w! [9 e% d* h# j2 H2 C% f
                                            count, buf);
    $ l, x0 o* j! m4 P, r5 {- ~' v                } else {
    9 x  J/ @) Q  J* i                        status = i2c_transfer(client->adapter, msg, 2);
    . R; z; o. w6 ]. A9 U  y' p, q                        if (status == 2)
    3 J4 ^+ c7 a5 e) |                                status = count;; C6 A% A' U" E
                    }3 W# |0 F# N+ s# p( l
                    dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
    . B$ E) u) W- r& f3 k6 K. S( _9 \. K                                count, offset, status, jiffies);0 r+ P6 X. `+ U' |7 W7 ^* H

    ( d9 }) w9 ?! x5 `9 c7 J                if (status == count)2 d/ w  D# `1 O; d
                            return count;3 Z0 i; `( f7 \5 S* o* [( b3 U

    9 A) E' h- x2 _5 d1 A# M  l$ X1 a                /* REVISIT: at HZ=100, this is sloooow *// a* ]& G( |2 c3 @+ _8 b
                    msleep(1);  s  a0 e  V1 f
            } while (time_before(read_time, timeout));  /* 使用timeout */
    % o) L6 h) @5 M" r  s! c" }8 g2 M/ m$ l
            return -ETIMEDOUT;6 u# M, m1 Z- U8 B. q. U
    }
    : E& j3 i5 f6 f9 p" M6 D+ i6 v0 z; P. _9 s! ^
    static ssize_t at24_read(struct at24_data *at24,
    % ]' r4 U, D8 i7 Q/ Q' v0 _                char *buf, loff_t off, size_t count), j/ f: V# @- c! j1 h( W* t
    {+ |3 d5 ?1 ?+ S0 P
            ssize_t retval = 0;- I$ d9 u4 j4 r( A% z# o
    ! Q- B# c- F3 q) D
            if (unlikely(!count))
    8 ^& {% t3 r3 n; o% w; D                return count;. y5 o! ^7 n% b9 @. g! s- I' y
    , [& s! i+ c. L* \
            /*
    5 h9 x- z# |& J) y: Z  I         * Read data from chip, protecting against concurrent updates
    % Q" t0 U; W1 f) {         * from this host, but not from other I2C masters.
    $ _( u( r6 q& E1 ~8 n0 R# z         */" v/ }8 Q4 r1 j% k0 l, S
             /* 访问设备前,加锁*/
    7 I9 z. X  {  o% i5 S        mutex_lock(&at24->lock);
    " n* g) E' \" W+ I
    ! n5 ?4 v6 ~& N1 @+ f) J- ~2 f$ k        while (count) {
    4 W2 \- D$ `$ m                ssize_t        status;
    + x( t5 R" a& o( Y- m4 |* F: [$ \. S, }5 m3 i1 L/ Q
                    status = at24_eeprom_read(at24, buf, off, count);
    8 @- t/ W7 {! D1 O8 I. j                if (status <= 0) {
    ) s9 W. L2 x" D                        if (retval == 0)+ `, g8 O! A. [; B# Z3 A5 J( ~; K& M
                                    retval = status;
    3 B  I# c7 d+ I3 D& Q                        break;. j8 p" O3 R1 y% I3 w0 A9 d
                    }) s% b% z' n" r: M
                    buf += status;
    : G2 V* {1 A% V8 ?  Z6 c8 |. |                off += status;7 {) Y4 T( P. n* |/ U, a8 A
                    count -= status;$ v# Q& d$ c1 h$ I1 q
                    retval += status;. R! L, e8 C9 |) K) f0 V6 @  D4 j  ~
            }3 T7 ^' U+ M9 T) R. s0 n

    & E5 q$ M, a2 z9 G4 j2 w        mutex_unlock(&at24->lock);
      E$ p  O1 [4 K0 G4 J. W/ g+ _8 s
    - u% V, J2 u6 @# x$ S, x/ m        return retval;
    6 c0 I! E2 a0 t( `}
    % j* w9 B3 j  n2 |2 ?6 h
    9 F1 v3 K: D# [. W, n: K% U: |static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr," G4 Y3 U2 l# u- b. V
                    char *buf, loff_t off, size_t count)
    . U% `4 ^' x: b- U$ V7 k0 ]5 R{
    ; H- h4 ~2 l. _% c- v+ F, b+ B        struct at24_data *at24;9 n- v" h1 s% R2 e7 W

    : X7 Q# ^/ W! b# m) V! p5 x        /* 通过kobj获取device,再获取driver_data */
    . f) V0 g: S: R        at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));' \1 X3 F0 o5 g* ], J1 A, ~. T! I  ?
            return at24_read(at24, buf, off, count);+ d1 f7 I4 u/ ?. T, x9 [2 Q
    }
      P8 a+ p. K, j1 A% @, |  Y& b5 k# d+ V: s
    5. 总结
    & c3 p! q3 l; ]2 F  Z1 Z    本文主要对at24c02的驱动架构进行了分析。该驱动基于i2c总线架构,提供了id表来帮助设备驱动的绑定,该驱动支持AT24CXX等多个系列,不仅仅是at24c02。
    9 K. I) Z" e; C9 B2 J
    $ I+ z/ a: n2 x5 d: A8 Y其次,该驱动并没有注册任何字符设备或者杂项设备,而是通过sys文件系统的二进制属性文件来对设备进行访问。此外,驱动同时支持I2C协议和SMBus协议来访问设备。1 q3 w; N% j1 ?. y" @6 E
    * F4 s1 T6 i! C' O$ R& s$ }
    8 ], |/ ~* `2 o& \3 q
    " q8 o4 q7 M7 ?3 U5 {. A7 m
    : F& `& e  K$ I: C" Q$ s9 r) L

    该用户从未签到

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

    本版积分规则

    关闭

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

    EDA365公众号

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

    GMT+8, 2025-11-26 01:59 , Processed in 0.156250 second(s), 23 queries , Gzip On.

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

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

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