Linux学习笔记(15.4)——LED设备驱动之面向对象演进4(用设备树)

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内核?设备树便应运而生。
  1. 本文,我们将实际电路的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 
		>;
	};
  1. 稍微修改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");
  1. led_drv.c文件不作修改
  2. 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
  1. 测试程序不做更改,验证参考Linux学习笔记(15)——LED设备驱动
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux设备驱动程序是用于控制和管理硬件设备的软件模块。学习Linux设备驱动程序可以帮助开发人员理解和掌握Linux内核的工作原理,以及如何编写和调试设备驱动程序。 以下是一些学习Linux设备驱动程序的笔记和建议: 1. 理解Linux设备模型:Linux设备模型是一种用于管理设备的框架,它提供了一种统一的方式来表示和操作设备学习Linux设备模型可以帮助你理解设备的注册、初始化和销毁过程。 2. 学习字符设备驱动程序:字符设备是一种以字节为单位进行读写的设备,如串口、终端等。学习字符设备驱动程序可以帮助你了解字符设备的打开、关闭、读写等操作,并学习如何实现设备文件的注册和操作。 3. 学习设备驱动程序:块设备是一种以块为单位进行读写的设备,如硬盘、闪存等。学习设备驱动程序可以帮助你了解块设备的分区、缓存、IO调度等操作,并学习如何实现块设备的注册和操作。 4. 学习中断处理:中断是设备向处理器发送信号的一种机制,用于通知处理器设备的状态变化。学习中断处理可以帮助你了解中断的注册、处理和释放过程,并学习如何编写中断处理程序。 5. 学习设备驱动程序的调试技巧:设备驱动程序的调试是一个重要的技能,可以帮助你快速定位和解决问题。学习设备驱动程序的调试技巧可以帮助你理解和使用调试工具,如 printk、kprobe等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值