dtsled.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #include <linux/types.h>
  2. #include <linux/kernel.h>
  3. #include <linux/delay.h>
  4. #include <linux/ide.h>
  5. #include <linux/init.h>
  6. #include <linux/module.h>
  7. #include <linux/errno.h>
  8. #include <linux/gpio.h>
  9. #include <linux/cdev.h>
  10. #include <linux/device.h>
  11. #include <linux/of.h>
  12. #include <linux/of_address.h>
  13. #include <asm/mach/map.h>
  14. #include <asm/uaccess.h>
  15. #include <asm/io.h>
  16. /***************************************************************
  17. Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
  18. 文件名 : dtsled.c
  19. 作者 : 左忠凯
  20. 版本 : V1.0
  21. 描述 : LED驱动文件。
  22. 其他 : 无
  23. 论坛 : www.openedv.com
  24. 日志 : 初版V1.0 2019/7/9 左忠凯创建
  25. ***************************************************************/
  26. #define DTSLED_CNT 1 /* 设备号个数 */
  27. #define DTSLED_NAME "dtsled" /* 名字 */
  28. #define LEDOFF 0 /* 关灯 */
  29. #define LEDON 1 /* 开灯 */
  30. /* 映射后的寄存器虚拟地址指针 */
  31. static void __iomem *IMX6U_CCM_CCGR1;
  32. static void __iomem *SW_MUX_GPIO1_IO03;
  33. static void __iomem *SW_PAD_GPIO1_IO03;
  34. static void __iomem *GPIO1_DR;
  35. static void __iomem *GPIO1_GDIR;
  36. /* dtsled设备结构体 */
  37. struct dtsled_dev{
  38. dev_t devid; /* 设备号 */
  39. struct cdev cdev; /* cdev */
  40. struct class *class; /* 类 */
  41. struct device *device; /* 设备 */
  42. int major; /* 主设备号 */
  43. int minor; /* 次设备号 */
  44. struct device_node *nd; /* 设备节点 */
  45. };
  46. struct dtsled_dev dtsled; /* led设备 */
  47. /*
  48. * @description : LED打开/关闭
  49. * @param - sta : LEDON(0) 打开LED,LEDOFF(1) 关闭LED
  50. * @return : 无
  51. */
  52. void led_switch(u8 sta)
  53. {
  54. u32 val = 0;
  55. if(sta == LEDON) {
  56. val = readl(GPIO1_DR);
  57. val &= ~(1 << 3);
  58. writel(val, GPIO1_DR);
  59. }else if(sta == LEDOFF) {
  60. val = readl(GPIO1_DR);
  61. val|= (1 << 3);
  62. writel(val, GPIO1_DR);
  63. }
  64. }
  65. /*
  66. * @description : 打开设备
  67. * @param - inode : 传递给驱动的inode
  68. * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
  69. * 一般在open的时候将private_data指向设备结构体。
  70. * @return : 0 成功;其他 失败
  71. */
  72. static int led_open(struct inode *inode, struct file *filp)
  73. {
  74. filp->private_data = &dtsled; /* 设置私有数据 */
  75. return 0;
  76. }
  77. /*
  78. * @description : 从设备读取数据
  79. * @param - filp : 要打开的设备文件(文件描述符)
  80. * @param - buf : 返回给用户空间的数据缓冲区
  81. * @param - cnt : 要读取的数据长度
  82. * @param - offt : 相对于文件首地址的偏移
  83. * @return : 读取的字节数,如果为负值,表示读取失败
  84. */
  85. static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
  86. {
  87. return 0;
  88. }
  89. /*
  90. * @description : 向设备写数据
  91. * @param - filp : 设备文件,表示打开的文件描述符
  92. * @param - buf : 要写给设备写入的数据
  93. * @param - cnt : 要写入的数据长度
  94. * @param - offt : 相对于文件首地址的偏移
  95. * @return : 写入的字节数,如果为负值,表示写入失败
  96. */
  97. static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
  98. {
  99. int retvalue;
  100. unsigned char databuf[1];
  101. unsigned char ledstat;
  102. retvalue = copy_from_user(databuf, buf, cnt);
  103. if(retvalue < 0) {
  104. printk("kernel write failed!\r\n");
  105. return -EFAULT;
  106. }
  107. ledstat = databuf[0]; /* 获取状态值 */
  108. if(ledstat == LEDON) {
  109. led_switch(LEDON); /* 打开LED灯 */
  110. } else if(ledstat == LEDOFF) {
  111. led_switch(LEDOFF); /* 关闭LED灯 */
  112. }
  113. return 0;
  114. }
  115. /*
  116. * @description : 关闭/释放设备
  117. * @param - filp : 要关闭的设备文件(文件描述符)
  118. * @return : 0 成功;其他 失败
  119. */
  120. static int led_release(struct inode *inode, struct file *filp)
  121. {
  122. return 0;
  123. }
  124. /* 设备操作函数 */
  125. static struct file_operations dtsled_fops = {
  126. .owner = THIS_MODULE,
  127. .open = led_open,
  128. .read = led_read,
  129. .write = led_write,
  130. .release = led_release,
  131. };
  132. /*
  133. * @description : 驱动出口函数
  134. * @param : 无
  135. * @return : 无
  136. */
  137. static int __init led_init(void)
  138. {
  139. u32 val = 0;
  140. int ret;
  141. u32 regdata[14];
  142. const char *str;
  143. struct property *proper;
  144. /* 获取设备树中的属性数据 */
  145. /* 1、获取设备节点:alphaled */
  146. dtsled.nd = of_find_node_by_name("my_backlight");
  147. if(dtsled.nd == NULL) {
  148. printk("alphaled node nost find!\r\n");
  149. return -EINVAL;
  150. } else {
  151. printk("alphaled node find!\r\n");
  152. }
  153. /* 2、获取compatible属性内容 */
  154. proper = of_find_property(dtsled.nd, "compatible", NULL);
  155. if(proper == NULL) {
  156. printk("compatible property find failed\r\n");
  157. } else {
  158. printk("compatible = %s\r\n", (char*)proper->value);
  159. }
  160. /* 3、获取status属性内容 */
  161. ret = of_property_read_string(dtsled.nd, "status", &str);
  162. if(ret < 0){
  163. printk("status read failed!\r\n");
  164. } else {
  165. printk("status = %s\r\n",str);
  166. }
  167. /* 4、获取reg属性内容 */
  168. ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
  169. if(ret < 0) {
  170. printk("reg property read failed!\r\n");
  171. } else {
  172. u8 i = 0;
  173. printk("reg data:\r\n");
  174. for(i = 0; i < 10; i++)
  175. printk("%#X ", regdata[i]);
  176. printk("\r\n");
  177. }
  178. /* 初始化LED */
  179. #if 0
  180. /* 1、寄存器地址映射 */
  181. IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
  182. SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]);
  183. SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]);
  184. GPIO1_DR = ioremap(regdata[6], regdata[7]);
  185. GPIO1_GDIR = ioremap(regdata[8], regdata[9]);
  186. #else
  187. IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);
  188. SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);
  189. SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);
  190. GPIO1_DR = of_iomap(dtsled.nd, 3);
  191. GPIO1_GDIR = of_iomap(dtsled.nd, 4);
  192. #endif
  193. /* 2、使能GPIO1时钟 */
  194. val = readl(IMX6U_CCM_CCGR1);
  195. val &= ~(3 << 26); /* 清楚以前的设置 */
  196. val |= (3 << 26); /* 设置新值 */
  197. writel(val, IMX6U_CCM_CCGR1);
  198. /* 3、设置GPIO1_IO03的复用功能,将其复用为
  199. * GPIO1_IO03,最后设置IO属性。
  200. */
  201. writel(5, SW_MUX_GPIO1_IO03);
  202. /*寄存器SW_PAD_GPIO1_IO03设置IO属性
  203. *bit 16:0 HYS关闭
  204. *bit [15:14]: 00 默认下拉
  205. *bit [13]: 0 kepper功能
  206. *bit [12]: 1 pull/keeper使能
  207. *bit [11]: 0 关闭开路输出
  208. *bit [7:6]: 10 速度100Mhz
  209. *bit [5:3]: 110 R0/6驱动能力
  210. *bit [0]: 0 低转换率
  211. */
  212. writel(0x10B0, SW_PAD_GPIO1_IO03);
  213. /* 4、设置GPIO1_IO03为输出功能 */
  214. val = readl(GPIO1_GDIR);
  215. val &= ~(1 << 3); /* 清除以前的设置 */
  216. val |= (1 << 3); /* 设置为输出 */
  217. writel(val, GPIO1_GDIR);
  218. /* 5、默认关闭LED */
  219. val = readl(GPIO1_DR);
  220. val |= (1 << 3);
  221. writel(val, GPIO1_DR);
  222. /* 注册字符设备驱动 */
  223. /* 1、创建设备号 */
  224. if (dtsled.major) { /* 定义了设备号 */
  225. dtsled.devid = MKDEV(dtsled.major, 0);
  226. register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
  227. } else { /* 没有定义设备号 */
  228. alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME); /* 申请设备号 */
  229. dtsled.major = MAJOR(dtsled.devid); /* 获取分配号的主设备号 */
  230. dtsled.minor = MINOR(dtsled.devid); /* 获取分配号的次设备号 */
  231. }
  232. printk("dtsled major=%d,minor=%d\r\n",dtsled.major, dtsled.minor);
  233. /* 2、初始化cdev */
  234. dtsled.cdev.owner = THIS_MODULE;
  235. cdev_init(&dtsled.cdev, &dtsled_fops);
  236. /* 3、添加一个cdev */
  237. cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);
  238. /* 4、创建类 */
  239. dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
  240. if (IS_ERR(dtsled.class)) {
  241. return PTR_ERR(dtsled.class);
  242. }
  243. /* 5、创建设备 */
  244. dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);
  245. if (IS_ERR(dtsled.device)) {
  246. return PTR_ERR(dtsled.device);
  247. }
  248. return 0;
  249. }
  250. /*
  251. * @description : 驱动出口函数
  252. * @param : 无
  253. * @return : 无
  254. */
  255. static void __exit led_exit(void)
  256. {
  257. /* 取消映射 */
  258. iounmap(IMX6U_CCM_CCGR1);
  259. iounmap(SW_MUX_GPIO1_IO03);
  260. iounmap(SW_PAD_GPIO1_IO03);
  261. iounmap(GPIO1_DR);
  262. iounmap(GPIO1_GDIR);
  263. /* 注销字符设备驱动 */
  264. cdev_del(&dtsled.cdev);/* 删除cdev */
  265. unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /* 注销设备号 */
  266. device_destroy(dtsled.class, dtsled.devid);
  267. class_destroy(dtsled.class);
  268. }
  269. module_init(led_init);
  270. module_exit(led_exit);
  271. MODULE_LICENSE("GPL");
  272. MODULE_AUTHOR("zuozhongkai");