zynq linux 设备树,【正点原子FPGA连载】第二十五章设备树下的LED驱动实验-领航者ZYNQ之linux开发指南...

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");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值