由于ZYNQ的引脚分配应该在VIVADO中硬件设计时完全确定,且在应用时往往不需要再对引脚进行分配,所以并不需要对pinctrl子系统所需要的设备树进行配置。
但是,对于某个功能来说,例如点亮LED,需要对LED所需要的GPIO进行相关的描述,可以在顶层.dts补充内容,例如:
#define GPIO_ACTIVE_HIGH 0
#define GPIO_ACTIVE_LOW 1
....
led {
compatible = "zynq,led";
status = "okay";
default-state = "on";
led-gpio = <&gpio0 7 GPIO_ACTIVE_HIGH>,
<&gpio0 8 GPIO_ACTIVE_HIGH>;
};
代码使用的是就是MIO7和MIO8。EMIO按照编号往后排。
可以利用GPIO子系统提供的API编写具体的驱动。常见的API及相关OF函数有:
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
gpioled.led_gpio[i] = of_get_named_gpio(gpioled.nd,"led-gpio",i);
np:设备节点。
propname:包含要获取 GPIO 信息的属性名。
index :GPIO 索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO 的编号。上面的设备树led-gpio属性有两个GPIO,索引分别0,1.
返回值:正值,获取到的 GPIO 编号,这个编号最好在用户设备结构体里定义;负值,失败。
int gpio_request(unsigned gpio, const char *label)
gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信息,此函数会返回这个 GPIO 的标号。
label:给 gpio 设置个名字,随便取,不要重复。
返回值:0,申请成功;其他值,申请失败。
调用 gpio_free 函数对申请的GPIO进行释放。函数原型如下:
void gpio_free(unsigned gpio)
设置某个 GPIO 为输入:
int gpio_direction_input(unsigned gpio)
设置某个 GPIO 为输出,带输出值的那种:
int gpio_direction_output(unsigned gpio, int value)
gpio:要设置为输出的 GPIO 标号。
value :GPIO 默认输出值。
返回值:0,设置成功;负值,设置失败。
获取某个 GPIO 的值(0 或 1),此函数是个宏,定义所示:
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
函数参数和返回值含义如下:
gpio:要获取的 GPIO 标号。
返回值:非负值,得到的 GPIO 值;负值,获取失败。
gpio_set_value 函数
此函数用于设置某个 GPIO 的值,此函数是个宏,定义如下
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
函数参数和返回值含义如下:
gpio:要设置的 GPIO 标号。
value :要设置的值。
返回值:无
具体的LED设备驱动代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define NEWCHRLED_CNT 1 //the number of the devids
#define NEWCHRLED_NAME "gpio-led"
#define LED_CNT 2
struct chrdevbase_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int led_gpio[LED_CNT]; //the numbers of each led
};
const static char led_name[2][10] = {"LED-GPIO-0","LED-GPIO-1"};
static struct chrdevbase_dev gpioled;
static int chrdev_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioled; /* 设置私有数据 */
printk("led-dev!\r\n");
return 0;
}
static ssize_t chrdev_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t chrdev_write(struct file *filp, const char __user *buf,
size_t cnt, loff_t *offt)
{
int ret;
char kern_buf[1];
ret = copy_from_user(kern_buf, buf, cnt); // 得到应用层传递过来的数据
if(0 > ret) {
printk(KERN_ERR "kernel write failed!\r\n");
return -EFAULT;
}
if (0 == kern_buf[0])
gpio_set_value(gpioled.led_gpio[0], 0); // 如果传递过来的数据是0则关闭led0
else if (1 == kern_buf[0])
gpio_set_value(gpioled.led_gpio[0], 1); // 如果传递过来的数据是1则点亮led0
else if (2 == kern_buf[0])
gpio_set_value(gpioled.led_gpio[1], 0); // 如果传递过来的数据是2则点亮led1
else if (3 == kern_buf[0])
gpio_set_value(gpioled.led_gpio[1], 1); // 如果传递过来的数据是3则点亮led1
return 0;
}
static int chrdev_release(struct inode *inode, struct file *filp)
{
printk("chrdevbase release!\r\n");
return 0;
}
static struct file_operations chrdev_fops = {
.owner = THIS_MODULE,
.open = chrdev_open,
.read = chrdev_read,
.write = chrdev_write,
.release = chrdev_release,
};
static int __init chrdevbase_init(void)
{
int ret;
const char *str;
u8 i=0;
gpioled.nd = of_find_node_by_path("/led");
if(NULL == gpioled.nd){
printk("cant find the led\r\n");
return -EINVAL;
}
for(i=0;i<LED_CNT;i++){
gpioled.led_gpio[i] = of_get_named_gpio(gpioled.nd,"led-gpio",i);
if(!gpio_is_valid(gpioled.led_gpio[i])) {
printk(KERN_ERR "gpioled: Failed to get led-gpio %d!\r\n",i);
return -EINVAL;
}
printk(KERN_INFO "gpioled: led-gpio num = %d\r\n", gpioled.led_gpio[i]);
ret = gpio_request(gpioled.led_gpio[i],led_name[i]);
if (ret){
printk("gpioled:Failed to request led-gpio- %d!\r\n",i);
return ret;
}
gpio_direction_output(gpioled.led_gpio[i],0);
ret = of_property_read_string(gpioled.nd, "default-state", &str);
if(!ret) {
if (!strcmp(str, "on"))
gpio_set_value(gpioled.led_gpio[i], 1);
else
gpio_set_value(gpioled.led_gpio[i], 0);
} else
gpio_set_value(gpioled.led_gpio[i], 0);
}
/* major minor and devid */
ret = alloc_chrdev_region(&gpioled.devid,0,NEWCHRLED_CNT,NEWCHRLED_NAME);
if(ret)
goto out1;
gpioled.major = MAJOR(gpioled.devid);
gpioled.major = MINOR(gpioled.devid);
printk("chrdevbase major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);
/*initailization of cdev*/
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev,&chrdev_fops);
ret = cdev_add(&gpioled.cdev, gpioled.devid,NEWCHRLED_CNT);
if(ret)
goto out2;
/* create a class*/
gpioled.class = class_create(THIS_MODULE,NEWCHRLED_NAME);
if (IS_ERR(gpioled.class)) {
ret = PTR_ERR(gpioled.class);
goto out3;
}
gpioled.device = device_create(gpioled.class, NULL,
gpioled.devid, NULL, NEWCHRLED_NAME);
if (IS_ERR(gpioled.device)) {
ret = PTR_ERR(gpioled.device);
goto out4;
}
return 0;
out4:
class_destroy(gpioled.class);
out3:
cdev_del(&gpioled.cdev);
out2:
unregister_chrdev_region(gpioled.devid,NEWCHRLED_CNT);
out1:
gpio_free(gpioled.led_gpio[0]);
gpio_free(gpioled.led_gpio[1]);
return ret;
}
static void __exit chrdevbase_exit(void)
{
printk("Closing the driver!");
device_destroy(gpioled.class, gpioled.devid);
class_destroy(gpioled.class);
cdev_del(&gpioled.cdev);
unregister_chrdev_region(gpioled.devid,NEWCHRLED_CNT);
gpio_free(gpioled.led_gpio[0]);
gpio_free(gpioled.led_gpio[1]);
}
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
MODULE_AUTHOR("zangyujiagood");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("gpio_test");