/*
* 文件名 : leddevice.c
* 作者 : glen
* 描述 : leddevice驱动文件
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
* 寄存器地址定义
*/
#define CCM_CCGR1_BASE (0x020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0x020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0x020E02F4)
#define GPIO1_DR_BASE (0x0209C000)
#define GPIO1_GDIR_BASE (0x0209C004)
#define REGISTER_LENGTH 4
/**
* \brief 释放platform设备模块时此函数会执行
* \param dev 要释放的设备
* \retval 无
*/
static void led_release(struct device *dev)
{
printk("led device release!\r\n");
}
/*
* 设备资源信息, 也就是LED0所使用的所有寄存器
*/
static struct resource led_resources[] = {
[0] = {
.start = CCM_CCGR1_BASE,
.end = (CCM_CCGR1_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM,
},
[1] = {
.start = SW_MUX_GPIO1_IO03_BASE,
.end = (SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM,
},
[2] = {
.start = SW_PAD_GPIO1_IO03_BASE,
.end = (SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM,
},
[3] = {
.start = GPIO1_DR_BASE,
.end = (GPIO1_DR_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM,
},
[4] = {
.start = GPIO1_GDIR_BASE,
.end = (GPIO1_GDIR_BASE + REGISTER_LENGTH - 1),
.flags = IORESOURCE_MEM,
},
};
/*
* platform设备结构体
*/
static struct platform_device leddevice = {
.name = "glen-led",
.id = -1,
.dev = {
.release = &led_release,
},
.num_resources = ARRAY_SIZE(led_resources),
.resource = led_resources,
};
/**
* \brief 设备模块加载
* \param 无
* \retval 无
*/
static int __init leddevice_init(void)
{
return platform_device_register(&leddevice);
}
/**
* \brief 设备模块注销
* \param 无
* \retval 无
*/
static void __exit leddevice_exit(void)
{
platform_device_unregister(&leddevice);
}
/* 设备注册入口, 展开后
* static initcall_t \
* __initcall_leddevice_init6 \
* __used \
* __attribute__((__section__(".initcall6.init"))) \
* = leddevice_init;
*/
module_init(leddevice_init);
/* 设备注册出口, 展开后
* static exitcall_t \
* __exitcall_leddevice_exit \
* __exit_call \
* = leddevice_exit;
*/
module_exit(leddevice_exit);
/* 模块的许可证声明, 展开后
* static const char __UNIQUE_ID_license__COUNTER__[] \
* __used __attribute__((section(".modinfo"), unused, aligned(1))) \
* = "license=GPL";
*/
MODULE_LICENSE("GPL");
/* 模块的作者声明, 展开后
* static const char __UNIQUE_ID_author__COUNTER__[] \
* __used __attribute__((section(".modinfo"), unused, aligned(1))) \
* = "author=glen_cao"
*/
MODULE_AUTHOR("glen");
/*
* 文件名 : leddriver.c
* 作者 : glen
* 描述 : leddriver驱动文件
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LEDDEV_CNT 1 /* 设备号长度 */
#define LEDDEV_NAME "platled" /* 设备名字 */
#define LEDOFF "OFF"
#define LEDON "ON"
/*
* 寄存器名
*/
static void __iomem *imx6u_ccm_ccgr1;
static void __iomem *sw_mux_gpio1_io03;
static void __iomem *sw_pad_gpio1_io03;
static void __iomem *gpio1_dr;
static void __iomem *gpio1_gdir;
/* leddev设备结构体 */
struct leddev_dev {
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
};
struct leddev_dev leddev; /* led设备 */
/**
* \brief LED打开/关闭
* \param sta
* \retval 无
*/
void led0_switch(char *stat)
{
u32 val = 0;
if (strcmp(stat, LEDON) == 0) {
val = readl(gpio1_dr);
val &= ~(1 << 3);
writel(val, gpio1_dr);
} else if (strcmp(stat, LEDOFF) == 0) {
val = readl(gpio1_dr);
val |= (1 << 3);
writel(val, gpio1_dr);
}
}
/**
* \brief 打开设备
* \param inode 传递给驱动的inode
* filp 设备文件
* \retval 0 成功; 其它 失败
*/
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &leddev; /* 设置私有数据 */
return 0;
}
/**
* \brief 向设备写数据
* \param filp 设备文件
* buf 要写给设备写入的数据
* cnt 要写入的数据长度
* offt 相对于文件首地址的偏移
* \retval 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret;
char data_buf[8];
ret = copy_from_user(data_buf, buf, ((cnt < sizeof(data_buf) ? cnt : sizeof(data_buf))));
if (ret < 0) {
return -EFAULT;
}
led0_switch(data_buf);
return 0;
}
/*
* 设备操作函数
*/
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
};
/**
* \brief platform驱动的probe函数,当驱动与设备匹配以后此函数就会执行
* \param dev platform设备
* \retval 0,成功 负值,失败
*/
static int led_probe(struct platform_device *dev)
{
int i = 0;
u32 res_sz[5];
u32 val = 0;
struct resource *reg_led[5];
printk("led driver and device has matched!\r\n");
/* 获取资源 */
for (i = 0; i < 5; i++) {
reg_led[i] = platform_get_resource(dev, IORESOURCE_MEM, i);
if (!reg_led[i]) {
dev_err(&dev->dev, "No Memory resource for always on\n");
return -ENXIO;
}
res_sz[i] = resource_size(reg_led[i]);
}
/* 初始化LED */
/* 寄存器地址映射 */
imx6u_ccm_ccgr1 = ioremap(reg_led[0]->start, res_sz[0]);
sw_mux_gpio1_io03 = ioremap(reg_led[1]->start, res_sz[1]);
sw_pad_gpio1_io03 = ioremap(reg_led[2]->start, res_sz[2]);
gpio1_dr = ioremap(reg_led[3]->start, res_sz[3]);
gpio1_gdir = ioremap(reg_led[4]->start, res_sz[4]);
val = readl(imx6u_ccm_ccgr1);
val |= (3 << 26); /* 设置时钟 */
writel(val, imx6u_ccm_ccgr1);
/* 设置gpio1_io03复用功能, 将其复用为gpio1_io03 */
writel(5, sw_mux_gpio1_io03);
writel(0x10B0, sw_pad_gpio1_io03);
/* 设置gpio1_io03为输出功能 */
val = readl(gpio1_gdir);
val |= (1 << 3); /* 设置为输出 */
writel(val, gpio1_gdir);
/* 关闭LED */
val = readl(gpio1_dr);
val |= (1 << 3);
writel(val, gpio1_dr);
/* 注册字符设备驱动 */
/* 创建设备号 */
if (leddev.major) {
leddev.devid = MKDEV(leddev.major, 0);
register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);
} else {
alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);
leddev.major = MAJOR(leddev.devid);
}
/* 初始化cdev */
leddev.cdev.owner = THIS_MODULE;
cdev_init(&leddev.cdev, &led_fops);
/* 添加一个cdev */
cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);
/* 创建类 */
leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
if (IS_ERR(leddev.class)) {
return PTR_ERR(leddev.class);
}
/* 创建设备 */
leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);
if (IS_ERR(leddev.device)) {
return PTR_ERR(leddev.device);
}
return 0;
}
/**
* \brief 移除platform驱动的时候此函数会执行
* \param dev platform设备
* \retval 0,成功; 其他负值,失败
*/
static int led_remove(struct platform_device *dev)
{
iounmap(imx6u_ccm_ccgr1);
iounmap(sw_mux_gpio1_io03);
iounmap(sw_pad_gpio1_io03);
iounmap(gpio1_dr);
iounmap(gpio1_gdir);
cdev_del(&leddev.cdev);
unregister_chrdev_region(leddev.devid, LEDDEV_CNT);
device_destroy(leddev.class, leddev.devid);
class_destroy(leddev.class);
return 0;
}
/* platform驱动结构体 */
static struct platform_driver led_driver = {
.driver = {
.name = "glen-led",
},
.probe = led_probe,
.remove = led_remove,
};
/**
* \brief 驱动模块加载函数
* \param 无
* \retval 无
*/
static int __init leddriver_init(void)
{
return platform_driver_register(&led_driver);
}
/**
* \brief 驱动模块缷载函数
* \param 无
* \return 无
*/
static void __exit leddriver_exit(void)
{
platform_driver_unregister(&led_driver);
}
/* 设备注册入口, 展开后
* static initcall_t \
* __initcall_leddriver_init6 \
* __used \
* __attribute__((__section__(".initcall6.init"))) \
* = leddriver_init;
*/
module_init(leddriver_init);
/* 设备注册出口, 展开后
* static exitcall_t \
* __exitcall_leddriver_exit \
* __exit_call \
* = leddriver_exit;
*/
module_exit(leddriver_exit);
/* 模块的许可证声明, 展开后
* static const char __UNIQUE_ID_license__COUNTER__[] \
* __used __attribute__((section(".modinfo"), unused, aligned(1))) \
* = "license=GPL";
*/
MODULE_LICENSE("GPL");
/* 模块的作者声明, 展开后
* static const char __UNIQUE_ID_author__COUNTER__[] \
* __used __attribute__((section(".modinfo"), unused, aligned(1))) \
* = "author=glen_cao"
*/
MODULE_AUTHOR("glen");
/*
* 文件名 : platform_test.c
* 作者 : glen
* 描述 : platform测试程序
*/
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"
#include "linux/ioctl.h"
#include "signal.h"
/**
* @brief : main函数
* @par : argc argv数组元素的个数
* argv 参数数组
* @retval : 0 成功 其它 失败
*/
int main(int argc, char *argv[])
{
static int fd = 0; /* 文件描述符 */
int ret = 0;
char *filename;
if (argc != 3) {
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打开驱动文件 */
fd = open(filename, O_RDWR); /* 阻塞访问 */
if (fd < 0) {
printf("Can't open file %s\r\n", filename);
return -1;
}
ret = write(fd, argv[2], sizeof(argv[2]));
if (ret < 0) {
printf("LED control failure!\r\n");
close(fd);
return -1;
}
/* 关闭文件 */
ret = close(fd);
if (ret < 0) {
printf("file %s close failed!\r\n", argv[1]);
return -1;
}
return 0;
}
拷贝到NFS文件系统目录下
glen@ubuntu:~/linux/imx6ull/linux/driver/15_platform$ sudo cp leddevice.ko leddriver.ko platform_test ~/linux/nfs/rootfs/lib/modules/4.1.15 -rf
/lib/modules/4.1.15 # insmod leddevice.ko
/lib/modules/4.1.15 # insmod leddriver.ko
led driver and device has matched!
5.2 查看platform平台的设备和驱动信息
查看platform平台的设备信息
/lib/modules/4.1.15 # ls /sys/bus/platform/devices/
20a8000.gpio 2290000.iomuxc-snvs
20ac000.gpio 2294000.snvs-gpr
20b0000.snvs 900000.sram
20b4000.ethernet 904000.sram
20bc000.wdog 905000.sram
20c4000.ccm Vivante GCCore
20c8000.anatop a01000.interrupt-controller
20c8000.anatop:regulator-3p0@120 alarmtimer
20c8000.anatop:regulator-vddcore@140 backlight
20c8000.anatop:regulator-vddsoc@140 beep
20c9000.usbphy ci_hdrc.0
20ca000.usbphy ci_hdrc.1
20cc000.snvs glen-led
查看platform平台的驱动信息
/lib/modules/4.1.15 # ls /sys/bus/platform/drivers/
CCI-400 imx-gpc imx6sll-pinctrl of-flash
CCI-400 PMU imx-gpcv2 imx6sx-pinctrl pata_imx
ahci imx-i2c imx6ul-pinctrl platform-lcd
ahci-imx imx-ipuv3 imx6ul-tsc pwm-backlight
alarmtimer imx-keypad imx7d-pinctrl pxp-v4l2
anatop_regulator imx-mmdc imx_busfreq reg-dummy
arm-pmu imx-mu imx_mc13783 reg-fixed-voltage
chipidea-usb2 imx-pre imx_thermal sdhci-esdhc-imx
ci_hdrc imx-prg imx_usb smc911x
coda imx-pwm ldo2p5-dummy smc91x
da9052-regulator imx-pxp leds-gpio smsc911x
eukrea_tlv320 imx-rpmsg mc13783-regulator snd-soc-dummy
fec imx-sdma mc13892-regulator snvs_pwrkey
fsl-lpuart imx-sema4 msm_hsusb snvs_rtc
fsl-quadspi imx-sgtl5000 mx3-camera soc-audio
fsl-spdif-dai imx-spdif mx3_sdc_fb soc-camera-pdrv
fsl-ssi-dai imx-ssi mxc-ehci spi_imx
generic-bl imx-uart mxc_lcdif sram
glen-led imx-weim mxc_mlb150 stmpe-ts
gpio-keys imx-wm8962 mxc_nand syscon
gpio-mxc imx2-wdt mxc_rtc usb_phy_generic
gpio-rc-recv imx50-pinctrl mxc_sdc_fb usbmisc_imx
gpio-reset imx53-pinctrl mxc_vdoa vf610-pinctrl
gpio-vf610 imx6dl-pinctrl mxc_vpu zevio_usb
gpmi-nand imx6q-cpufreq mxs-dma
imx-audmux imx6q-pinctrl mxs_phy
imx-ddrc imx6sl-pinctrl mxsfb
5.3 测试
/lib/modules/4.1.15 # ./platform_test /dev/platled ON
/lib/modules/4.1.15 # ./platform_test /dev/platled OFF
/lib/modules/4.1.15 # ./platform_test /dev/platled ON
/lib/modules/4.1.15 # ./platform_test /dev/platled OFF
/lib/modules/4.1.15 # ./platform_test /dev/platled ON
/lib/modules/4.1.15 # ./platform_test /dev/platled OFF
/lib/modules/4.1.15 # ./platform_test /dev/platled ON
/lib/modules/4.1.15 # ./platform_test /dev/platled OFF