1 /***************************************************************
2 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
3 文件名 : dtsled.c
4 作者 : 邓涛
5 版本 : V1.0
6 描述 : ZYNQ LED驱动文件。
7 其他 : 无
8 论坛 : www.openedv.com
9 日志 : 初版V1.0 2019/1/30 邓涛创建
10 ***************************************************************/
11
12 #include
13 #include
14 #include
15 #include
16 #include
17 #include
18 #include
19 #include
20 #include
21 #include
22 #include
23 #include
24 #include
25 #include
26
27 #define DTSLED_CNT 1 /* 设备号个数 */
28 #define DTSLED_NAME "dtsled" /* 名字 */
29
30 /* 映射后的寄存器虚拟地址指针 */
31 static void __iomem *data_addr;
32 static void __iomem *dirm_addr;
33 static void __iomem *outen_addr;
34 static void __iomem *intdis_addr;
35 static void __iomem *aper_clk_ctrl_addr;
36
37 /* dtsled设备结构体 */
38 struct dtsled_dev {
39 dev_t devid; /* 设备号 */
40 struct cdev cdev; /* cdev */
41 struct class *class; /* 类 */
42 struct device *device; /* 设备 */
43 int major; /* 主设备号 */
44 int minor; /* 次设备号 */
45 struct device_node *nd; /* 设备节点 */
46 };
47
48 static struct dtsled_dev dtsled; /* led设备 */
49
50 /*
51 * @description : 打开设备
52 * @param – inode : 传递给驱动的inode
53 * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
54 * 一般在open的时候将private_data指向设备结构体。
55 * @Return : 0 成功;其他 失败
56 */
57 static int led_open(struct inode *inode, struct file *filp)
58 {
59 filp->private_data = &dtsled; /* 设置私有数据 */
60 return 0;
61 }
62
63 /*
64 * @description : 从设备读取数据
65 * @param - filp : 要打开的设备文件(文件描述符)
66 * @param - buf : 返回给用户空间的数据缓冲区
67 * @param - cnt : 要读取的数据长度
68 * @param - offt : 相对于文件首地址的偏移
69 * @return : 读取的字节数,如果为负值,表示读取失败
70 */
71 static ssize_t led_read(struct file *filp, char __user *buf,
72 size_t cnt, loff_t *offt)
73 {
74 return 0;
75 }
76
77 /*
78 * @description : 向设备写数据
79 * @param - filp : 设备文件,表示打开的文件描述符
80 * @param - buf : 要写给设备写入的数据
81 * @param - cnt : 要写入的数据长度
82 * @param - offt : 相对于文件首地址的偏移
83 * @return : 写入的字节数,如果为负值,表示写入失败
84 */
85 static ssize_t led_write(struct file *filp, const char __user *buf,
86 size_t cnt, loff_t *offt)
87 {
88 int ret;
89 int val;
90 char kern_buf[1];
91
92 ret = copy_from_user(kern_buf, buf, cnt); // 得到应用层传递过来的数据
93 if(0 > ret) {
94 printk(KERN_ERR "kernel write failed!\r\n");
95 return -EFAULT;
96 }
97
98 val = readl(data_addr);
99 if (0 == kern_buf[0])
100 val &= ~(0x1U << 7); // 如果传递过来的数据是0则关闭led
101 else if (1 == kern_buf[0])
102 val |= (0x1U << 7); // 如果传递过来的数据是1则点亮led
103
104 writel(val, data_addr);
105 return 0;
106 }
107
108 /*
109 * @description : 关闭/释放设备
110 * @param – filp : 要关闭的设备文件(文件描述符)
111 * @return : 0 成功;其他 失败
112 */
113 static int led_release(struct inode *inode, struct file *filp)
114 {
115 return 0;
116 }
117
118 static inline void led_ioremap(void)
119 {
120 data_addr = of_iomap(dtsled.nd, 0);
121 dirm_addr = of_iomap(dtsled.nd, 1);
122 outen_addr = of_iomap(dtsled.nd, 2);
123 intdis_addr = of_iomap(dtsled.nd, 3);
124 aper_clk_ctrl_addr = of_iomap(dtsled.nd, 4);
125 }
126
127 static inline void led_iounmap(void)
128 {
129 iounmap(data_addr);
130 iounmap(dirm_addr);
131 iounmap(outen_addr);
132 iounmap(intdis_addr);
133 iounmap(aper_clk_ctrl_addr);
134 }
135
136 /* 设备操作函数 */
137 static struct file_operations dtsled_fops = {
138 .owner = THIS_MODULE,
139 .open = led_open,
140 .read = led_read,
141 .write = led_write,
142 .release = led_release,
143 };
144
145 static int __init led_init(void)
146 {
147 const char *str;
148 u32 val;
149 int ret;
150
151 /* 1.获取led设备节点 */
152 dtsled.nd = of_find_node_by_path("/led");
153 if(NULL == dtsled.nd) {
154 printk(KERN_ERR "led node can not found!\r\n");
155 return -EINVAL;
156 }
157
158 /* 2.读取status属性 */
159 ret = of_property_read_string(dtsled.nd, "status", &str);
160 if(!ret) {
161 if (strcmp(str, "okay"))
162 return -EINVAL;
163 }
165 /* 2、获取compatible属性值并进行匹配 */
166 ret = of_property_read_string(dtsled.nd, "compatible", &str);
167 if(0 > ret)
168 return -EINVAL;
169
170 if (strcmp(str, "alientek,led"))
171 return -EINVAL;
172
173 printk(KERN_ERR "led device matching successful!\r\n");
174
175 /* 4.寄存器地址映射 */
176 led_ioremap();
177
178 /* 5.使能GPIO时钟 */
179 val = readl(aper_clk_ctrl_addr);
180 val |= (0x1U << 22);
181 writel(val, aper_clk_ctrl_addr);
182
183 /* 6.关闭中断功能 */
184 val |= (0x1U << 7);
185 writel(val, intdis_addr);
186
187 /* 7.设置GPIO为输出功能 */
188 val = readl(dirm_addr);
189 val |= (0x1U << 7);
190 writel(val, dirm_addr);
191
192 /* 8.使能GPIO输出功能 */
193 val = readl(outen_addr);
194 val |= (0x1U << 7);
195 writel(val, outen_addr);
196
197 /* 9.初始化LED的默认状态 */
198 val = readl(data_addr);
199
200 ret = of_property_read_string(dtsled.nd, "default-state", &str);
201 if(!ret) {
202 if (!strcmp(str, "on"))
203 val |= (0x1U << 7);
204 else
205 val &= ~(0x1U << 7);
206 } else
207 val &= ~(0x1U << 7);
208
209 writel(val, data_addr);
210
211 /* 10.注册字符设备驱动 */
212 /* 创建设备号 */
213 if (dtsled.major) {
214 dtsled.devid = MKDEV(dtsled.major, 0);
215 ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
216 if (ret)
217 goto out1;
218 } else {
219 ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);
220 if (ret)
221 goto out1;
222
223 dtsled.major = MAJOR(dtsled.devid);
224 dtsled.minor = MINOR(dtsled.devid);
225 }
226
227 printk("dtsled major=%d,minor=%d\r\n",dtsled.major, dtsled.minor);
228
229 /* 初始化cdev */
230 dtsled.cdev.owner = THIS_MODULE;
231 cdev_init(&dtsled.cdev, &dtsled_fops);
232
233 /* 添加一个cdev */
234 ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);
235 if (ret)
236 goto out2;
237
238 /* 创建类 */
239 dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
240 if (IS_ERR(dtsled.class)) {
241 ret = PTR_ERR(dtsled.class);
242 goto out3;
243 }
244
245 /* 创建设备 */
246 dtsled.device = device_create(dtsled.class, NULL,
247 dtsled.devid, NULL, DTSLED_NAME);
248 if (IS_ERR(dtsled.device)) {
249 ret = PTR_ERR(dtsled.device);
250 goto out4;
251 }
252
253 return 0;
254
255 out4:
256 class_destroy(dtsled.class);
257
258 out3:
259 cdev_del(&dtsled.cdev);
260
261 out2:
262 unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
263
264 out1:
265 led_iounmap();
266
267 return ret;
268 }
269
270 static void __exit led_exit(void)
271 {
272 /* 注销设备 */
273 device_destroy(dtsled.class, dtsled.devid);
274
275 /* 注销类 */
276 class_destroy(dtsled.class);
277
278 /* 删除cdev */
279 cdev_del(&dtsled.cdev);
280
281 /* 注销设备号 */
282 unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
283
284 /* 取消地址映射 */
285 led_iounmap();
286 }
287
288 /* 驱动模块入口和出口函数注册 */
289 module_init(led_init);
290 module_exit(led_exit);
291
292 MODULE_AUTHOR("DengTao <773904075@qq.com>");
293 MODULE_DESCRIPTION("Alientek ZYNQ GPIO LED Driver");
294 MODULE_LICENSE("GPL");