Linux学习笔记(15.3)采用platform_device指定LED资源,即:
- 首先,根据实际电路将LED的引脚信息(默认状态、打开时电平、GPIO时钟开关位偏移量、引脚序号、GPIO物理地址)初始化;
- 然后,定义struct resource类型的led_res资源,将.start成员指向上面定义的LED引脚信息;
- 接着,定义struct platform_device类型的led_dev资源,指定资源的名称.name、.num_resources(几个LED)、.resource(led_res)成员;
- 最后,在led_dev的初始化函数中将struct platform_device类型的led_dev资源注册到平台设备,即platform_device_register(&led_dev)。
那么,能否省去这个文件不要让具体板子资源挤占linux内核?设备树便应运而生。
- 本文,我们将实际电路的LED信息放在设备树的根节点(如下),内核启动时便将具有compatible属性的gled@0和gled@1节点都转换为平台设备platform_device,当平台驱动platform_driver伴随着设备驱动模块加载时与platform_device匹配。
#define OFF 'C'
#define ON 'O'
#define LOW 0
#define HIGH 1
gled@0 {
compatible = "glen,led_drv";
led_pin = <
OFF
LOW
26
3
0x020C406C
0x020E0068
0x020E02F4
0x0209C000
0x0209C004
>;
};
gled@1 {
compatible = "glen,led_drv";
led_pin = <
ON
LOW
30
1
0x020C406C
0x0229000C
0x02290050
0x020AC000
0x020AC004
>;
};
- 稍微修改imx6_gpio_drv.c文件
/**
* @file imx6_gpio_drv.c
* @author glen (glen_cao@126.com)
* @brief
* @version 0.1
* @date 2021-12-24
*
* @copyright Copyright (c) 2021
*
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "led_dev.h"
static struct gled_device gled[16];
static void imx6_gled_ctrl(u8 idx, u8 status);
static int imx6_gled_init(u8 idx);
static int imx6_gled_exit(u8 idx);
static struct led_ops imx6_gpio_led_ops = {
.num = 0,
.led_init = imx6_gled_init,
.led_exit = imx6_gled_exit,
.led_ctrl = imx6_gled_ctrl,
};
/**
* @brief : 打开/关闭LED
* @par : status LEDON('O') 打开LED, LEDOFF('C') 关闭LED
* @retval : 无
*/
static void imx6_gled_ctrl(u8 idx, u8 status)
{
u32 val = 0;
if (gled[idx].vir_io.ccgr == NULL)
return;
if (status == ON) {
val = readl(gled[idx].vir_io.dr);
if (gled[idx].on_level == HIGH) {
val |= (1 << (gled[idx].pin));
} else if (gled[idx].on_level == LOW) {
val &= ~(1 << gled[idx].pin);
}
writel(val, gled[idx].vir_io.dr);
printk("The led%d is openned!\r\n", idx);
} else if (status == OFF) {
val = readl(gled[idx].vir_io.dr);
if (gled[idx].on_level == HIGH) {
val &= ~(1 << gled[idx].pin);
} else if (gled[idx].on_level == LOW) {
val |= (1 << (gled[idx].pin));
}
writel(val, gled[idx].vir_io.dr);
printk("The led%d is closed!\r\n", idx);
} else {
printk("Recived parameter is error!\r\n");
}
}
/**
* @brief LED状态初始化
* @param idx LED
*/
static int imx6_gled_init(u8 idx)
{
u32 val;
if (idx >= imx6_gpio_led_ops.num) {
return -EIO;
}
/* 寄存器地址映射 */
gled[idx].vir_io.ccgr = ioremap(gled[idx].phy_io.ccgr , 4);
gled[idx].vir_io.sw_mux = ioremap(gled[idx].phy_io.sw_mux, 4);
gled[idx].vir_io.sw_pad = ioremap(gled[idx].phy_io.sw_pad, 4);
gled[idx].vir_io.dr = ioremap(gled[idx].phy_io.dr , 4);
gled[idx].vir_io.dir = ioremap(gled[idx].phy_io.dir , 4);
/* 使能GPIO时钟 */
val = readl(gled[idx].vir_io.ccgr);
val |= (3 << gled[idx].clk_shft_bits);
writel(val, gled[idx].vir_io.ccgr);
/* 设置gpio1_io03的复用功能 */
writel(5, gled[idx].vir_io.sw_mux);
/* 寄存器sw_mux1_io03设置IO属性 */
writel(0x10B0, gled[idx].vir_io.sw_pad);
/* 设置gpio1_gdir为输出功能 */
val = readl(gled[idx].vir_io.dir);
val |= (1 << gled[idx].pin);
writel(val, gled[idx].vir_io.dir);
/* 设置led为默认状态 */
val = readl(gled[idx].vir_io.dr);
if (gled[idx].dft_status == ON) {
if (gled[idx].on_level == HIGH) {
val |= (1 << gled[idx].pin);
} else if (gled[idx].on_level == LOW) {
val &= ~(1 << gled[idx].pin);
}
} else if (gled[idx].dft_status == OFF) {
if (gled[idx].on_level == HIGH) {
val &= ~(1 << gled[idx].pin);
} else if (gled[idx].on_level == LOW) {
val |= (1 << gled[idx].pin);
}
}
writel(val, gled[idx].vir_io.dr);
return 0;
}
/**
* @brief LED状态解初始化
* @param idx LED
*/
static int imx6_gled_exit(u8 idx)
{
if (idx >= imx6_gpio_led_ops.num) {
return -EIO;
}
iounmap(gled[idx].vir_io.ccgr);
iounmap(gled[idx].vir_io.sw_mux);
iounmap(gled[idx].vir_io.sw_pad);
iounmap(gled[idx].vir_io.dr);
iounmap(gled[idx].vir_io.dir);
return 0;
}
/**
* @brief
*/
static int imx6_gpio_probe(struct platform_device *pdev)
{
u8 i = 0;
int ret;
struct device_node *nd = pdev->dev.of_node;
if (!nd)
return -1;
i = imx6_gpio_led_ops.num;
ret = of_property_read_u32_array(nd, "led_pin", (u32 *)&gled[i], 9);
if (ret < 0)
printk("Read property \"led_pin\" failed!\r\n");
else {
printk("led_pin data:");
for (ret = 0; ret < 9; ret++) {
printk("%#X\t", ((u32 *)&(gled[i]))[ret]);
}
printk("\r\n");
}
/* 创建设备节点 */
led_device_create(i);
imx6_gpio_led_ops.num++;
return 0;
}
static int imx6_gpio_remove(struct platform_device *pdev)
{
u8 i;
/* 销毁设备节点 */
for (i = 0; i < imx6_gpio_led_ops.num; i++) {
led_device_destroy(i);
}
imx6_gpio_led_ops.num = 0;
return 0;
}
static const struct of_device_id gled_devs[] = {
{.compatible = "glen,led_drv"},
{ },
};
static struct platform_driver imx6_gpio_driver = {
.driver = {
.name = "gled",
.of_match_table = gled_devs,
},
.probe = imx6_gpio_probe,
.remove = imx6_gpio_remove,
};
static int __init imx6_gpio_drv_init(void)
{
int ret;
ret = platform_driver_register(&imx6_gpio_driver);
if (ret)
pr_err("Unable to initialize imx6 gpio driver\n");
else
pr_info("The imx6 gpio driver is registered.\n");
led_ops_register(&imx6_gpio_led_ops);
return ret;
}
static void __exit imx6_gpio_drv_exit(void)
{
platform_driver_unregister(&imx6_gpio_driver);
}
module_init(imx6_gpio_drv_init);
module_exit(imx6_gpio_drv_exit);
/* insert license for module */
MODULE_LICENSE("GPL");
/* insert author information for module */
MODULE_AUTHOR("glen");
- led_drv.c文件不作修改
- Makefile文件删除atk_board_led.o文件
KERNELDIR := /home/glen/linux/imx6ull/linux/glen_linux
CURRENT_PATH := $(shell pwd)
#led_driver-y := led_drv.o led_dev.o
obj-m += led_drv.o imx6_gpio_drv.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
- 测试程序不做更改,验证参考Linux学习笔记(15)——LED设备驱动