/*
* FriendlyARM's Exynos4412 based TINY4412 board device tree source
*
* Copyright (c) 2013 Alex Ling
*
* Device tree source file for FriendlyARM's TINY4412 board which is based on
* Samsung's Exynos4412 SoC.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/dts-v1/;
#include "exynos4412.dtsi"
#include
#include
/ {
model = "FriendlyARM TINY4412 board based on Exynos4412";
compatible = "friendlyarm,tiny4412", "samsung,exynos4412", "samsung,exynos4";
chosen {
stdout-path = &serial_0;
bootargs = "root=/dev/ram0 rw rootfstype=ext4 console=ttySAC0,115200 ethmac=1C:6F:65:34:51:7E init=/linuxrc";
};
memory {
reg = <0x40000000 0x40000000>;
};
leds {
compatible = "gpio-leds";
status = "disabled";
led1 {
label = "led1";
gpios = <&gpm4 0 GPIO_ACTIVE_LOW>;
default-state = "off";
linux,default-trigger = "heartbeat";
};
led2 {
label = "led2";
gpios = <&gpm4 1 GPIO_ACTIVE_LOW>;
default-state = "off";
};
led3 {
label = "led3";
gpios = <&gpm4 2 GPIO_ACTIVE_LOW>;
default-state = "off";
};
led4 {
label = "led4";
gpios = <&gpm4 3 GPIO_ACTIVE_LOW>;
default-state = "off";
linux,default-trigger = "mmc0";
};
};
fixed-rate-clocks {
xxti {
compatible = "samsung,clock-xxti";
clock-frequency = <0>;
};
xusbxti {
compatible = "samsung,clock-xusbxti";
clock-frequency = <24000000>;
};
};
usb-hub {
compatible = "smsc,usb4640";
reset-gpios = <&gpm2 4 GPIO_ACTIVE_LOW>;
initial-mode = ;
};
interrupt_demo: interrupt_demo {
compatible = "tiny4412,interrupt_demo";
tiny4412,int_gpio1 = <&gpx3 2 GPIO_ACTIVE_HIGH>;
tiny4412,int_gpio2 = <&gpx3 3 GPIO_ACTIVE_HIGH>;
tiny4412,int_gpio3 = <&gpx3 4 GPIO_ACTIVE_HIGH>;
tiny4412,int_gpio4 = <&gpx3 5 GPIO_ACTIVE_HIGH>;
};
led_pin {
compatible = "tiny4412,led_demo";
pinctrl-names = "led_demo";
pinctrl-0 = <&led_demo>;
tiny4412,int_gpio1 = <&gpm4 0 GPIO_ACTIVE_HIGH>;
tiny4412,int_gpio2 = <&gpm4 1 GPIO_ACTIVE_HIGH>;
tiny4412,int_gpio3 = <&gpm4 2 GPIO_ACTIVE_HIGH>;
tiny4412,int_gpio4 = <&gpm4 3 GPIO_ACTIVE_HIGH>;
};
};
&pinctrl_1 {
led_demo: led{
samsung,pins = "gpm4-0", "gpm4-1" ,"gpm4-2", "gpm4-3";
samsung,pin-function = <0x1>; //1为输出
samsung,pin-pud = <0x0>; //没有上拉
samsung,pin-drv = <0x0>; //驱动强度?
};
};
&rtc {
status = "okay";
};
&sdhci_2 {
bus-width = <4>;
pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>;
pinctrl-names = "default";
#status = "okay";
status = "disabled";
};
&serial_0 {
status = "okay";
};
&serial_1 {
status = "okay";
};
&serial_2 {
status = "okay";
};
&serial_3 {
status = "okay";
};
&exynos_usbphy {
status = "okay";
};
&ehci {
status = "okay";
port@0 {
status = "okay";
};
port@1 {
status = "okay";
};
port@2 {
status = "okay";
};
};
&ohci {
status = "okay";
port@0 {
status = "okay";
};
};
&hsotg {
status = "okay";
};
2、驱动
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LED_CNT 4
static int major;
static struct cdev led_cdev; //内核中用cdev描述一个字符设备
static struct class *cls;
static int led1,led2,led3,led4;
static ssize_t led_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{
char buf;
int minor = iminor(file->f_inode);
printk("minor is %d\n",minor);
printk("%s\n",__func__);
if(count != 1){
printk("count != 1\n");
return 1;
}
if (copy_from_user(&buf, user_buf, count))
return -EFAULT;
printk("rcv %d\n",buf);
if(buf == 0x01)
{
switch(minor){
case 0:
gpio_set_value(led1, 0);
break;
case 1:
gpio_set_value(led2, 0);
break;
case 2:
gpio_set_value(led3, 0);
break;
case 3:
gpio_set_value(led4, 0);
break;
default:
printk("%s rcv minor error\n",__func__);
}
}
else if(buf == 0x0)
{
switch(minor){
case 0:
gpio_set_value(led1, 1);
break;
case 1:
gpio_set_value(led2, 1);
break;
case 2:
gpio_set_value(led3, 1);
break;
case 3:
gpio_set_value(led4, 1);
break;
default:
printk("%s rcv minor error\n",__func__);
}
}
return 1;
}
static int led_open(struct inode *inode, struct file *file)
{
printk("led_open\n");
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
};
static int led_probe(struct platform_device *pdev) {
struct device *dev = &pdev->dev;
dev_t devid;
struct pinctrl *pctrl;
struct pinctrl_state *pstate;
pctrl = devm_pinctrl_get(dev);
if(pctrl == NULL)
{
printk("devm_pinctrl_get error\n");
}
pstate = pinctrl_lookup_state(pctrl, "led_demo");
if(pstate == NULL)
{
printk("pinctrl_lookup_state error\n");
}
pinctrl_select_state(pctrl, pstate);//设置为输出模式
printk("enter %s\n",__func__);
led1 = of_get_named_gpio(dev->of_node, "tiny4412,int_gpio1", 0);;
led2 = of_get_named_gpio(dev->of_node, "tiny4412,int_gpio2", 0);;
led3 = of_get_named_gpio(dev->of_node, "tiny4412,int_gpio3", 0);;
led4 = of_get_named_gpio(dev->of_node, "tiny4412,int_gpio4", 0);;
if(led1 <= 0)
{
printk("%s error\n",__func__);
return -EINVAL;
}
else
{
printk("led1 %d\n",led1);
printk("led2 %d\n",led2);
printk("led3 %d\n",led3);
printk("led4 %d\n",led4);
devm_gpio_request_one(dev, led1, GPIOF_OUT_INIT_HIGH, "LED1");
devm_gpio_request_one(dev, led2, GPIOF_OUT_INIT_HIGH, "LED2");
devm_gpio_request_one(dev, led3, GPIOF_OUT_INIT_HIGH, "LED3");
devm_gpio_request_one(dev, led4, GPIOF_OUT_INIT_HIGH, "LED4");
}
if(alloc_chrdev_region(&devid, 0, LED_CNT, "led") < 0)/* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */
{
printk("%s ERROR\n",__func__);
goto error;
}
major = MAJOR(devid);
cdev_init(&led_cdev, &led_fops); //绑定文件操作函数
cdev_add(&led_cdev, devid, LED_CNT); //注册到内核
cls = class_create(THIS_MODULE, "led"); //创建led类,向类中添加设备,mdev会帮我们创建设备节点
device_create(cls, NULL, MKDEV(major, 0), NULL, "led0");
device_create(cls, NULL, MKDEV(major, 1), NULL, "led1");
device_create(cls, NULL, MKDEV(major, 2), NULL, "led2");
device_create(cls, NULL, MKDEV(major, 3), NULL, "led3");
//return 0;
error:
unregister_chrdev_region(MKDEV(major, 0), LED_CNT);
return 0;
}
static int led_remove(struct platform_device *pdev) {
printk("enter %s\n",__func__);
device_destroy(cls, MKDEV(major, 0));
device_destroy(cls, MKDEV(major, 1));
device_destroy(cls, MKDEV(major, 2));
device_destroy(cls, MKDEV(major, 3));
class_destroy(cls);
cdev_del(&led_cdev);
unregister_chrdev_region(MKDEV(major, 0), LED_CNT);
printk("%s enter.\n", __func__);
return 0;
}
static const struct of_device_id led_dt_ids[] = {
{ .compatible = "tiny4412,led_demo", },
{},
};
MODULE_DEVICE_TABLE(of, led_dt_ids);
static struct platform_driver led_driver = {
.driver = {
.name = "led_demo",
.of_match_table = of_match_ptr(led_dt_ids),
},
.probe = led_probe,
.remove = led_remove,
};
static int led_init(void){
int ret;
printk("enter %s\n",__func__);
ret = platform_driver_register(&led_driver);
if (ret)
printk(KERN_ERR "led demo: probe failed: %d\n", ret);
return ret;
}
static void led_exit(void)
{
printk("enter %s\n",__func__);
platform_driver_unregister(&led_driver);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
设计函数解析
1. 获取一个pinctrl句柄,参数是dev是包含这个pin的device结构体即xxx这个设备的device
获取设备操作句柄(设备模型中的struct device)的pin control state holder(struct pinctrl)
/**
* struct devm_pinctrl_get() - Resource managed pinctrl_get()
* @dev: the device to obtain the handle for
*
* If there is a need to explicitly destroy the returned struct pinctrl,
* devm_pinctrl_put() should be used, rather than plain pinctrl_put().
*/
struct pinctrl *devm_pinctrl_get(struct device *dev)
2. 获取这个pin对应pin_state(引脚状态-turnon_tes/turnoff_tes)
/**
* pinctrl_lookup_state() - retrieves a state handle from a pinctrl handle
* @p: the pinctrl handle to retrieve the state from
* @name: the state name to retrieve
*/
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name)
3. 设置引脚为为某个stata -- turnon_tes/turnoff_tes
/**
* pinctrl_select_state() - select/activate/program a pinctrl state to HW
* @p: the pinctrl handle for the device that requests configuration
* @state: the state handle to select/activate/program
*/
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
4.得到GPIO的编号
./**
* include/of_gpio.h
* of_get_named_gpio - 从设备树中提取gpio口
* @np - 设备节点指针
* @propname - 属性名
* @index - gpio口引脚标号
* 成功:得到GPIO口编号int型;失败:负数,绝对值是错误码
*/
int of_get_named_gpio(struct device_node *np, const char *propname, int index);
of_get_named_gpio:此函数是解析设备树的函数,我们通过这个函数去解析设备树,
tiny4412,int_gpio1 = <&gpx3 2 GPIO_ACTIVE_HIGH>;
跟踪下去会发现这个函数掉用了list = of_get_property(np, "tiny4412,int_gpio2", &size);设备树解析是创界了设备节点,现在通过这个函数去获取属性。
设备树思路是: