第五十六讲 pinctrl子系统
文章目录
一、前言
Linux 是一个庞大而完善的系统。我们在控制硬件外设的时候,需要先去获取控制硬件外设相关的引脚,然后通过数据手册查看gpio的操作方法。这样开发效率比较低,对于我们写程序也比较麻烦。所以引入 pinctrl
来简化我们的开发步骤。
pinctrl 子系统主要用于管理芯片的引脚。imx6ull 芯片拥有众多的片上外设,大多数外设需要通过芯片的引脚与外部设备(器件)相连实现相对应的控制。在驱动程序中我们需要手动设置每个引脚的复用功能,不仅增加了工作量,编写的驱动程序不方便移植,可重用性差等。pinctrl 子系统是由芯片厂商来实现的, 简单来说用于帮助我们管理芯片引脚并自动完成引脚的初始化,而我们要做的只是在设备树中按照规定的格式写出想要的配置参数即可。
二、iomuxc 节点
1、作用
- 汇总引脚配置信息
- 预存 iomux 节点信息
2、节点信息
iomuxc: iomuxc@20e0000 {
compatible = "fsl,imx6ul-iomuxc";/*这里会跟pinctrl匹配*/
reg = <0x20e0000 0x4000>;/*iomuxc基地址0x20e0000, 长度0x4000*/
};
- compatible:修饰的是与平台驱动做匹配的名字, 这里则是与 pinctrl 子系统的平台驱动做匹
配。 - reg:表示的是引脚配置寄存器的基地址。
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */
MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059 /* SD1 RESET */
>;
};
...
};
-
pinctrl-names = “default”;
表示硬件状态为默认状态,(pinctrl-0状态)
-
pinctrl-0 = <&pinctrl_hog_1>;
定义第0状态需要使用的引脚
-
pinctrl-1 =
定义第1状态需要使用的引脚
-
…
3、节点格式
pinctrl_<自定义名字>:<自定义名字>{
fsl.pins=<
引脚宏定义 (PAD)引脚属性
>;
};
示例:
pinctrl_enet1: enet1grp {
fsl,pins = <
MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0
MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0
MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0
MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031
>;
};
4、引脚配置信息
引脚的配置信息一眼看去由两部分组成,一个宏定义和一个 16 进制数组成
在文件 arch/arm/boot/dts/imx6ul-pinfunc.h 中定义了许多引脚的宏
#define MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x00cc 0x0358 0x0000 0 0
每个宏定义后面有 5 个参数,名字依次为 mux_reg、conf_reg、input_reg、mux_mode、input_val。
如果将宏定义展开则在设备树中每条配置信息实际是 6 个参数,由于第 6 个参数设置较为复杂需要根据实际需要设置因此并没有把它放到宏定义里面。
- mux_reg 和 mux_mode :mux_reg 是引脚复用选择寄存器偏移地址,mux_mode 是引脚复用选择寄存器模式选择位的值。
- conf_reg ,引脚(PAD)属性控制寄存器偏移地址。与引脚复用选择寄存器不同,引脚属性寄
存器应当根据实际需要灵活的配置,所以它的值并不包含在宏定义中,它的值是我们上面所说的
“第六个”参数。 - input_reg 和 input_val ,input_reg 暂且称为输入选择寄存器偏移地址。input_val 是输入选择寄
存器的值。这个寄存器只有某些用作输入的引脚才有。 - 在 pinctrl 子系统中一条配置信息由一个宏定义和一个参数组成,将宏定义展开就是六个参数。pad 属性配置选项非常多,配置灵活。在 pinctrl 子系统中添加的 PAD 属性值就是引脚(PAD)属性设置寄存器的值(16 进制)。
含义
mux_reg | conf_reg | input_reg | mux_mode | input_val | 0x17059 |
---|---|---|---|---|---|
基地址偏移 0x0080 | 基地址偏移 0x030c | 0x0000 如果需要输入功能才使用 | 5 表示mux_reg设置的具体的值 | 0 设置input_reg的值 | 设置conf_reg的值 |
三、imx_pinctrl和pinctrl_dev
pinctrl子系统会预先确定引脚的数量和名字,并为每个引脚的信息分配内存,管理每个引脚的使用状态。
1、流程分析
-
imx6ul的pinctrl驱动位于文件
ebf_linux_kernel/drivers/pinctrl/freescale/pinctrl-imx6ul.c
-
看一个驱动函数首先看啥,当然是初始化函数呀,pinctrl的初始化函数为
static int __init imx6ul_pinctrl_init(void) { return platform_driver_register(&imx6ul_pinctrl_driver); }
可以看到,这里注册了一个平台设备
static struct platform_driver imx6ul_pinctrl_driver = { .driver = { .name = "imx6ul-pinctrl", .of_match_table = of_match_ptr(imx6ul_pinctrl_of_match), }, .probe = imx6ul_pinctrl_probe, };
这里有一个imx6ul_pinctrl_of_match
static const struct of_device_id imx6ul_pinctrl_of_match[] = { { .compatible = "fsl,imx6ul-iomuxc", .data = &imx6ul_pinctrl_info, }, { .compatible = "fsl,imx6ull-iomuxc-snvs", .data = &imx6ull_snvs_pinctrl_info, }, { /* sentinel */ } };
'fsl,imx6ul-iomuxc’是不是很眼熟,这就是
iomuxc
的compatible
属性值 -
imx6ul_pinctrl_probe
static int imx6ul_pinctrl_probe(struct platform_device *pdev) { const struct imx_pinctrl_soc_info *pinctrl_info; const struct of_device_id *match; pinctrl_info = of_device_get_match_data(&pdev->dev); if (!pinctrl_info) return -ENODEV; match = of_match_device(imx6ul_pinctrl_of_match, &pdev->dev); if (!match) return -ENODEV; pinctrl_info = (struct imx_pinctrl_soc_info *) match->data; return imx_pinctrl_probe(pdev, pinctrl_info); }
of_device_get_match_data
const void *of_device_get_match_data(const struct device *dev) { const struct of_device_id *match; match = of_match_device(dev->driver->of_match_table, dev); if (!match) return NULL; return match->data; }
of_match_device
/** * of_match_device - Tell if a struct device matches an of_device_id list * @ids: array of of device match structures to search in * @dev: the of device structure to match against * * Used by a driver to check whether an platform_device present in the * system is in its list of supported devices. */ const struct of_device_id *of_match_device(const struct of_device_id *matches, const struct device *dev) { if ((!matches) || (!dev->of_node)) return NULL; return of_match_node(matches, dev->of_node); }
这样
of_device_get_match_data
的返回就不为空,就匹配上了(匹配规则) -
当
of_device_get_match_data
成功后,会执行of_match_device
细心的朋友可能会发现,在of_device_get_match_data
里面已经调用过一次of_match_device
了。of_match_node
/** * of_match_node - Tell if a device_node has a matching of_match structure * @matches: array of of device match structures to search in * @node: the of device structure to match against * * Low level utility function used by device matching. */ const struct of_device_id *of_match_node(const struct of_device_id *matches, const struct device_node *node) { const struct of_device_id *match; unsigned long flags; raw_spin_lock_irqsave(&devtree_lock, flags); match = __of_match_node(matches, node); raw_spin_unlock_irqrestore(&devtree_lock, flags); return match; }
__of_match_node
const struct of_device_id *__of_match_node(const struct of_device_id *matches, const struct device_node *node) { const struct of_device_id *best_match = NULL; int score, best_score = 0; if (!matches) return NULL; for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) { score = __of_device_is_compatible(node, matches->compatible, matches->type, matches->name); if (score > best_score) { best_match = matches; best_score = score; } } return best_match; }
这里其实如果匹配的话就是返回
imx6ul_pinctrl_of_match
-
pinctrl_info = (struct imx_pinctrl_soc_info *) match->data;
这里其实就是pinctrl_info = imx6ul_pinctrl_of_match[0].data
也就是 &imx6ul_pinctrl_info
static const struct imx_pinctrl_soc_info imx6ul_pinctrl_info = { .pins = imx6ul_pinctrl_pads, .npins = ARRAY_SIZE(imx6ul_pinctrl_pads), .gpr_compatible = "fsl,imx6ul-iomuxc-gpr", };
-
imx6ul_pinctrl_pads
/** * struct pinctrl_pin_desc - boards/machines provide information on their * pins, pads or other muxable units in this struct * @number: unique pin number from the global pin number space * @name: a name for this pin * @drv_data: driver-defined per-pin data. pinctrl core does not touch this */ struct pinctrl_pin_desc { unsigned number; const char *name; void *drv_data; }; /* Pad names for the pinmux subsystem */ static const struct pinctrl_pin_desc imx6ul_pinctrl_pads[] = { IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE0), IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE1), IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE2), IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE3), IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE4), ... };
drivers\pinctrl\freescale\pinctrl-imx.h
#define IMX_PINCTRL_PIN(pin) PINCTRL_PIN(pin, #pin)
ebf_linux_kernel\build_image\build\debian\hdrtmp\usr\src\linux-headers-4.19.35-carp-imx6\include\linux\pinctrl\pinctrl.h
#define PINCTRL_PIN(a, b) { .number = a, .name = b }
-
到这里,我们可以知道有硬件的引脚信息存放在imx6ul_pinctrl_pads这个结构体数组里面了
例如MX6UL_PAD_RESERVE0,第0号引脚,他们跟 mux_reg 的关系就是
MX6UL_PAD_RESERVE0 * 4 = mux_reg
2、imx_pinctrl_probe分析
源码
int imx_pinctrl_probe(struct platform_device *pdev,
const struct imx_pinctrl_soc_info *info)
{
struct regmap_config config = { .name = "gpr" };
struct device_node *dev_np = pdev->dev.of_node;
struct pinctrl_desc *imx_pinctrl_desc;
struct device_node *np;
struct imx_pinctrl *ipctl;
struct resource *res;
struct regmap *gpr;
int ret, i;
if (!info || !info->pins || !info->npins) {
dev_err(&pdev->dev, "wrong pinctrl info\n");
return -EINVAL;
}
if (info->gpr_compatible) {
gpr = syscon_regmap_lookup_by_compatible(info->gpr_compatible);
if (!IS_ERR(gpr))
regmap_attach_dev(&pdev->dev, gpr, &config);
}
/* Create state holders etc for this driver */
ipctl = devm_kzalloc(&pdev->dev, sizeof(*ipctl), GFP_KERNEL);
if (!ipctl)
return -ENOMEM;
if (!(info->flags & IMX8_USE_SCU)) {
ipctl->pin_regs = devm_kmalloc(&pdev->dev, sizeof(*ipctl->pin_regs) *
info->npins, GFP_KERNEL);
if (!ipctl->pin_regs)
return -ENOMEM;
for (i = 0; i < info->npins; i++) {
ipctl->pin_regs[i].mux_reg = -1;
ipctl->pin_regs[i].conf_reg = -1;
}
/*获取resource*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/*映射resource*/
ipctl->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ipctl->base))
return PTR_ERR(ipctl->base);
/**
* of_property_read_bool - Findfrom a property
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
*
* Search for a property in a device node.
* Returns true if the property exists false otherwise.
*/
if (of_property_read_bool(dev_np, "fsl,input-sel")) {
np = of_parse_phandle(dev_np, "fsl,input-sel", 0);
if (!np) {
dev_err(&pdev->dev, "iomuxc fsl,input-sel property not found\n");
return -EINVAL;
}
/*映射*/
ipctl->input_sel_base = of_iomap(np, 0);
of_node_put(np);
if (!ipctl->input_sel_base) {
dev_err(&pdev->dev,
"iomuxc input select base address not found\n");
return -ENOMEM;
}
}
}
imx_pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*imx_pinctrl_desc),
GFP_KERNEL);
if (!imx_pinctrl_desc)
return -ENOMEM;
imx_pinctrl_desc->name = dev_name(&pdev->dev);
imx_pinctrl_desc->pins = info->pins;
imx_pinctrl_desc->npins = info->npins;
imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
imx_pinctrl_desc->pmxops = &imx_pmx_ops;
imx_pinctrl_desc->confops = &imx_pinconf_ops;
imx_pinctrl_desc->owner = THIS_MODULE;
/* for generic pinconf */
imx_pinctrl_desc->custom_params = info->custom_params;
imx_pinctrl_desc->num_custom_params = info->num_custom_params;
/* platform specific callback */
imx_pmx_ops.gpio_set_direction = info->gpio_set_direction;
mutex_init(&ipctl->mutex);
ipctl->info = info;
ipctl->dev = &pdev->dev;
platform_set_drvdata(pdev, ipctl);
ret = devm_pinctrl_register_and_init(&pdev->dev,
imx_pinctrl_desc, ipctl,
&ipctl->pctl);
if (ret) {
dev_err(&pdev->dev, "could not register IMX pinctrl driver\n");
goto free;
}
ret = imx_pinctrl_probe_dt(pdev, ipctl);
if (ret) {
dev_err(&pdev->dev, "fail to probe dt properties\n");
goto free;
}
dev_info(&pdev->dev, "initialized IMX pinctrl driver\n");
return pinctrl_enable(ipctl->pctl);
free:
imx_free_resources(ipctl);
return ret;
}
这个函数主要就是实现以下功能(这里就不展开了,感兴趣可以看视频分析),这里我没进行分析并不代表不重要呀,因为视频的分析已经做的很到位了,我这里再写的话可能表达的不全面,也怕误人子弟。所以一定要去看呀!!!
- 获取resource
- /映射resource/
- 注册pinctrl驱动
- 使能pinctrl
四、pinctl rgb 实验
1、设备树添加 penctl_reg_led
节点
新增的节点名为“rgb_led”,名字任意选取,长度不要超过32个字符,最好能表达出节点的信息。 “pinctrl_rgb_led”节点标签,“pinctrl_”是固定的格式,后面的内容可以自定义,我们将通过这个标签引用这个节点。 在添加完pinctrl子节点后,系统会根据我们添加的配置信息将引脚初始化为GPIO功能。
pinctrl_rgb_led:rgb_led{
fsl,pins=<
MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1
MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 0x000010B1
MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 0x000010B1
>;
};
2、编译设备树
更改设备树之后,在内核源码下执行以下命令:
make ARCH=arm -j1 CROSS_COMPILE=arm-linux-gnueabihf- dtbs
CALL scripts/checksyscalls.sh
DTC arch/arm/boot/dts/imx6ull-mmc-npi.dtb
3、将设备树移到开发板上
(这里就不做说明啦,方法有很多,比如 nfs、ftp…)
4、覆盖开发板上的 dtbo 文件
mv /home/debian/imx6ull-mmc-npi.dtb /usr/lib/linux-image-4.19.35-imx6/
5、编写测试代码
pinctl_led.h
#include <linux/of.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/of_gpio.h>
#include <linux/io.h>
#include <linux/of_address.h>
#define DEVICE_NAME "shadowrain_led"
typedef struct
{
struct device_node *device_node;
void __iomem *virtual_CCM_CCGR;
void __iomem *virtual_IOMUXC_SW_MUX_CTL_PAD;
void __iomem *virtual_IOMUXC_SW_PAD_CTL_PAD;
void __iomem *virtual_DR;
void __iomem *virtual_GDIR;
} rgbLedStr;
dev_t rgbDev;
struct class *rgbClass;
rgbLedStr gRGBLedStr[3];
pinctl_led.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include "pinctl_led.h"
int rgbLedOpen (struct inode *node, struct file *file)
{
printk("\n open form driver \n");
return 0;
}
ssize_t rgbLedWrite (struct file *file, const char __user *usr, size_t size, loff_t *loff)
{
char lRecvBuf[10] = {0};
int lRegData = 0;
printk("<1> Led write");
if(size >= 10)
{
printk("<1> size error!");
return -1;
}
if(copy_from_user(lRecvBuf, usr, size) < 0)
{
printk("<1> copy error!");
return -1;
}
printk("<1> recv is %s", lRecvBuf);
if(strstr(lRecvBuf, "red on") != NULL)
{
printk("red on");
lRegData = ioread32(gRGBLedStr[0].virtual_DR);
lRegData &= ~(0x01 << 4);
iowrite32(lRegData, gRGBLedStr[0].virtual_DR);
}
else if(strstr(lRecvBuf, "red off") != NULL)
{
lRegData = ioread32(gRGBLedStr[0].virtual_DR);
lRegData |= (0x01 << 4);
iowrite32(lRegData, gRGBLedStr[0].virtual_DR);
}
else if(strstr(lRecvBuf, "green on") != NULL)
{
lRegData = ioread32(gRGBLedStr[1].virtual_DR);
lRegData &= ~(0x01 << 20);
iowrite32(lRegData, gRGBLedStr[1].virtual_DR);
}
else if(strstr(lRecvBuf, "green off") != NULL)
{
lRegData = ioread32(gRGBLedStr[1].virtual_DR);
lRegData |= (0x01 << 20);
iowrite32(lRegData, gRGBLedStr[1].virtual_DR);
}
else if(strstr(lRecvBuf, "blue on") != NULL)
{
lRegData = ioread32(gRGBLedStr[2].virtual_DR);
lRegData &= ~(0x01 << 19);
iowrite32(lRegData, gRGBLedStr[2].virtual_DR);
}
else if(strstr(lRecvBuf, "blue off") != NULL)
{
lRegData = ioread32(gRGBLedStr[2].virtual_DR);
lRegData |= (0x01 << 19);
iowrite32(lRegData, gRGBLedStr[2].virtual_DR);
}
else
{
printk("error data %s", lRecvBuf);
lRegData = ioread32(gRGBLedStr[0].virtual_DR);
lRegData |= (0x01 << 4);
iowrite32(lRegData, gRGBLedStr[0].virtual_DR);
lRegData = ioread32(gRGBLedStr[1].virtual_DR);
lRegData |= (0x01 << 20);
iowrite32(lRegData, gRGBLedStr[1].virtual_DR);
lRegData = ioread32(gRGBLedStr[2].virtual_DR);
lRegData |= (0x01 << 19);
iowrite32(lRegData, gRGBLedStr[2].virtual_DR);
}
return size;
}
struct cdev gRGBLedCdev =
{
.owner = THIS_MODULE,
};
struct file_operations gRGBLedFp =
{
.open = rgbLedOpen,
.write = rgbLedWrite,
};
static int rgbProbe(struct platform_device *pdv)
{
struct device_node *lRGBLedDeviceNode;
int lRegData = 0;
/*查找设备树节点*/
printk("<1> path is "DEVICE_NAME"");
lRGBLedDeviceNode = of_find_node_by_path("/"DEVICE_NAME"");
if(lRGBLedDeviceNode == NULL)
{
printk("<1> Find node by path failed! \n");
return -1;
}
/*获取rgb_led节点的灯子节点*/
gRGBLedStr[0].device_node = of_find_node_by_name(lRGBLedDeviceNode, "rgb_led_red");
if(gRGBLedStr[0].device_node == NULL)
{
return -1;
}
/*获取 reg 属性并转化为虚拟地址*/
gRGBLedStr[0].virtual_CCM_CCGR = of_iomap(gRGBLedStr[0].device_node, 0);
gRGBLedStr[0].virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(gRGBLedStr[0].device_node, 1);
gRGBLedStr[0].virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(gRGBLedStr[0].device_node, 2);
gRGBLedStr[0].virtual_DR = of_iomap(gRGBLedStr[0].device_node, 3);
gRGBLedStr[0].virtual_GDIR = of_iomap(gRGBLedStr[0].device_node, 4);
/*---------------------------------------------------------------------------------*/
gRGBLedStr[1].device_node = of_find_node_by_name(lRGBLedDeviceNode, "rgb_led_green");
if(gRGBLedStr[1].device_node == NULL)
{
return -1;
}
/*获取 reg 属性并转化为虚拟地址*/
gRGBLedStr[1].virtual_CCM_CCGR = of_iomap(gRGBLedStr[1].device_node, 0);
gRGBLedStr[1].virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(gRGBLedStr[1].device_node, 1);
gRGBLedStr[1].virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(gRGBLedStr[1].device_node, 2);
gRGBLedStr[1].virtual_DR = of_iomap(gRGBLedStr[1].device_node, 3);
gRGBLedStr[1].virtual_GDIR = of_iomap(gRGBLedStr[1].device_node, 4);
/*---------------------------------------------------------------------------------*/
gRGBLedStr[2].device_node = of_find_node_by_name(lRGBLedDeviceNode,"rgb_led_blue");
if (gRGBLedStr[2].device_node == NULL)
{
printk(KERN_ERR "\n get rgb_led_blue_device_node failed ! \n");
return -1;
}
/*获取 reg 属性并转化为虚拟地址*/
gRGBLedStr[2].virtual_CCM_CCGR = of_iomap(gRGBLedStr[2].device_node, 0);
gRGBLedStr[2].virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(gRGBLedStr[2].device_node, 1);
gRGBLedStr[2].virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(gRGBLedStr[2].device_node, 2);
gRGBLedStr[2].virtual_DR = of_iomap(gRGBLedStr[2].device_node, 3);
gRGBLedStr[2].virtual_GDIR = of_iomap(gRGBLedStr[2].device_node, 4);
if( alloc_chrdev_region(&rgbDev, 0, 1, DEVICE_NAME) < 0)
{
printk("<1> alloc_chrdev_region failed \n");
return -1;
}
cdev_init(&gRGBLedCdev, &gRGBLedFp);
cdev_add(&gRGBLedCdev, rgbDev, 1);
rgbClass = class_create(THIS_MODULE, DEVICE_NAME);
device_create(rgbClass, NULL, rgbDev, NULL, DEVICE_NAME);
return 0;
}
static const struct of_device_id rgbLed[] =
{
{.compatible = "fire,rgb_led"},
{/* sentinel */}
};
struct platform_driver rgbPlatformDriver =
{
.probe = rgbProbe,
.driver =
{
.name = "rgb_led_platform",
.owner = THIS_MODULE,
.of_match_table = rgbLed,
},
};
static __init int RGBInit(void)
{
int driverState;
driverState = platform_driver_register(&rgbPlatformDriver);
printk(KERN_ALERT "\tDriverState is %d\n", driverState);
return 0;
}
static __exit void RGBExit(void)
{
}
module_init(RGBInit);
module_exit(RGBExit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yeyu");
MODULE_DESCRIPTION("RGBModule");
MODULE_ALIAS("RGBModule");
Makefile
KERNEL_DIR=../../ebf_linux_kernel/build_image/build/
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH CROSS_COMPILE
obj-m := pinctl_led.o
all:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
.PHONE:clean copy
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
copy:
sudo cp *.ko /home/dragon/nfsshare
6、编译并将编译出来的ko文件放到开发板上
7、重启开发板
8、加载编译出来的模块
sudo insmod pinctl_led.ko
9、开始实验
sudo sh -c "echo 'red on'>/dev/shadowrain_led"
灯亮了,试验成功!!!