您的位置:首页 > 运维架构 > Linux

设备树学习之(八)eeprom

2017-06-10 07:18 183 查看
转载自:http://blog.csdn.net/lizuobin2/article/details/54565121

开发板:tiny4412SDK + S702 + 4GB Flash

要移植的内核版本:Linux-4.4.0 (支持device tree)

u-boot版本:友善之臂自带的 U-Boot 2010.12

busybox版本:busybox 1.25

目标:

驱动 tiny4412 底板上的 i2c eeprom ,使用字符设备进行读写。

原理图:



设备地址为 0x50

设备树:

&i2c_0{
eeprom@50 {//它对应于driverid_table中的name
compatible = "tiny4412,eeprom";
reg = <0x50>;
};
};
1
2
3
4
5
6


1
2
3
4
5
6
[/code]

代码:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/delay.h>

#define uchar unsigned char
#define mydebug() printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__)
/*
24AA025E48
2K bit 只有低 1K bit可用
因此可以使用的范围 0 - 128 byte
一页 16bytes
支持写单字节,整页写操作
支持单字节读,连续读取
*/

static int major;
static struct class *class;
static struct i2c_client *at24cxx_client;

/* 传入: buf[0] : addr
* 输出: buf[1] : len
*/
static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t count, loff_t *off)
{
int ret, i;
unsigned char addr, len, data[2];
unsigned char *readbuf;
struct i2c_msg msg[2];

if (count != 2)
{
printk("%s count invalid \n", __func__);
return -EINVAL;
}

ret = copy_from_user(data, buf, 2);

if (ret < 0)
{
printk("%s copy_from_user error\n", __func__);
}

addr = data[0];
len  = data[1];
readbuf = kzalloc(len, GFP_KERNEL);

if (addr + len - 1 >= 128)
{
printk("%s write addr len invalid \n", __func__);
return -EINVAL;
}

if (len == 0)
{
return 0;
}
else
{
readbuf[0] = i2c_smbus_read_byte_data(at24cxx_client, addr);
mdelay(20);
addr += 1;
/* 读AT24CXX时,要先把要读的存储空间的地址发给它 */
msg[0].addr  = at24cxx_client->addr;    /* 目的 */
msg[0].buf   = &addr;                   /* 源 */
msg[0].len   = 1;                       /* 地址=1 byte */
msg[0].flags = 0;                       /* 表示写 */
/* 然后启动读操作 */
msg[1].addr  = at24cxx_client->addr;    /* 源 */
msg[1].buf   = readbuf + 1;             /* 目的 */
msg[1].len   = len - 1;                 /* 数据=1 byte */
msg[1].flags = I2C_M_RD;                /* 表示读 */
ret = i2c_transfer(at24cxx_client->adapter, msg, 2);

if (ret != 2)
{
printk("%s i2c_transfer error \n", __func__);
return -EINVAL;
}
}

if (data < 0)
{
printk("%s data read  error\n", __func__);
}

ret = copy_to_user(buf + 2, readbuf, len);

if (ret < 0)
{
printk("%s copy_from_user error\n", __func__);
}

kfree(readbuf);
return count;
}

static void calHead(uchar align, uchar start, uchar len, uchar *hstart, uchar *hlen)
{
if (start % align + len <= align)    //长度很短,不跨段
{
*hlen   = len;
*hstart = start;
return;
}

if (start % align == 0) //没有零散头部
{
*hlen   = 0;
*hstart = start;
}
else                    //有零散头部
{
//(start % align)           范围 0 - align-1
//align - (start % align);  范围 1 - align,start:0 <-> align
*hlen   = align - (start % align);
*hstart = start;
}
}

static void calMiddle(uchar align, uchar start, uchar len, uchar *mstart, uchar *mlen, uchar *num)
{
uchar hstart, hlen;
calHead(align, start, len, &hstart, &hlen);
*mstart = hstart + hlen;
*num    = (len - hlen) / align;
*mlen   = ((len - hlen) / align) * align;
}

static void calEnd(uchar align, uchar start, uchar len, uchar *estart, uchar *elen)
{
uchar hstart, hlen, mstart, mlen, num;
calHead(align, start, len, &hstart, &hlen);
calMiddle(align, start, len, &mstart, &mlen, &num);
*estart = mstart + mlen;
*elen   = len - hlen - mlen;
}

/* buf[0] : addr_start
* buf[1] : len
* buf
: data
*/
static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
{
int ret, i;
unsigned char addr, len;
unsigned char pagebuf[17];
unsigned char hstart, hlen, mstart, mlen, num, estart, elen;
unsigned char *data = kzalloc(count, GFP_KERNEL);
struct i2c_msg msg;

if (count < 3)
{
printk("%s count invalid \n", __func__);
return -EINVAL;
}

ret = copy_from_user(data, buf, count);

if (ret < 0)
{
printk("%s copy_from_user error\n", __func__);
}

addr = data[0];
len  = data[1];
printk("addr %d len %d\n", addr, len);

if (addr + len - 1 >= 128)
{
printk("%s write addr len invalid \n", __func__);
return -EINVAL;
}

calHead(  16, addr, len, &hstart, &hlen);
calMiddle(16, addr, len, &mstart, &mlen, &num);
calEnd(   16, addr, len, &estart, &elen);

for (i = hstart; i < hstart + hlen; i++)
{
if (i2c_smbus_write_byte_data(at24cxx_client, i, data[2 + i - addr]) < 0)
{
printk("%s i2c_smbus_write_byte_data %d \n", __func__, i);
return -EINVAL;
}

mdelay(5);
}

for (i = mstart; i < mstart + mlen; i += 16)
{
memset(pagebuf, i, 1);  //第一个字节为要写入的地址
memcpy(pagebuf + 1, data + 2 + i - addr, 16);
msg.addr  = at24cxx_client->addr;       /* 设备地址 */
msg.buf   = pagebuf;                    /* 源 */
msg.len   = 17;                         /* 地址+数据=17 byte */
msg.flags = 0;                          /* 表示写 */
ret = i2c_transfer(at24cxx_client->adapter, &msg, 1);

if (ret != 1)
{
printk("%s i2c_transfer error \n", __func__);
return -EINVAL;
}

mdelay(5);
}

for (i = estart; i < estart + elen; i++)
{
if (i2c_smbus_write_byte_data(at24cxx_client, i, data[2 + i - addr]) < 0)
{
printk("%s i2c_smbus_write_byte_data %d \n", __func__, i);
return -EINVAL;
}

mdelay(5);
}

kfree(data);
return count;
}

static struct file_operations at24cxx_fops =
{
.owner = THIS_MODULE,
.read  = at24cxx_read,
.write = at24cxx_write,
};

static int at24cxx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
at24cxx_client = client;
//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
major = register_chrdev(0, "at24cxx", &at24cxx_fops);
class = class_create(THIS_MODULE, "at24cxx");
device_create(class, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */
return 0;
}

static int at24cxx_remove(struct i2c_client *client)
{
//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
device_destroy(class, MKDEV(major, 0));
class_destroy(class);
unregister_chrdev(major, "at24cxx");
return 0;
}

static const struct i2c_device_id at24cxx_id_table[] =
{
{ "eeprom", 0 },
{}
};

/* 1. 分配/设置i2c_driver */
static struct i2c_driver at24cxx_driver =
{
.driver = {
.name   = "eeprom",
.owner  = THIS_MODULE,
},
.probe      = at24cxx_probe,
.remove     = at24cxx_remove,
.id_table   = at24cxx_id_table,
};

static int at24cxx_drv_init(void)
{
/* 2. 注册i2c_driver */
i2c_add_driver(&at24cxx_driver);
return 0;
}

static void at24cxx_drv_exit(void)
{
i2c_del_driver(&at24cxx_driver);
}

module_init(at24cxx_drv_init);
module_exit(at24cxx_drv_exit);
MODULE_LICENSE("GPL");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
[/code]

/div>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  tiny4412 linux驱动