tiny4412 驱动 (10)设备树之beep

重要参考文档:

1.Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
2.Documentation/devicetree/bindings/gpio/gpio-samsung.txt
3.Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt

从原理图可以得知beep的GPIO是GPD0_0

设计到寄存器如下

1.GPD0CON      Base Address: 0x1140_0000  Address = Base Address + 0x00A0

2.GPD0DAT       Base Address: 0x1140_0000      Address = Base Address + 0x00A4


 3. GPD0PUD   Base Address: 0x1140_0000   Address = Base Address + 0x00A8
4.GPD0DRV   Base Address: 0x1140_0000        Address = Base Address + 0x00AC

 
 
设备树里面

pinctrl@11400000 {
    beep_gpio {
        samsung,pins = "gpd0-1";
        samsung,pin-function = <0x1>;   /* output  */
        samsung,pin-pud = <0x1>;        /* Enables Pull-down */
        samsung,pin-drv = <0x1>;        /* x4   */
        phandle = <0x51>;
    };
};


/{
    ...
    beep {
                compatible = "tiny4412,beep";
                reg = <0x114000A0 0x20>;
                tiny4412,gpio = <0x52 0x0 1>; // default low (1)
                pinctrl-names = "default";
                pinctrl-0 = <0x51>;
        };
    ...
};

 

这里设计到pinctrl的东西先掠过理论,只是把里面的内容搞明白。

参考Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt

1. samsung,pins

The child node should contain a list of pin(s) on which a particular pin
  function selection or pin configuration (or both) have to applied. This
  list of pins is specified using the property name "samsung,pins". There
  should be atleast one pin specfied for this property and there is no upper
  limit on the count of pins that can be specified. The pins are specified
  using pin names which are derived from the hardware manual of the SoC. As
  an example, the pins in GPA0 bank of the pin controller can be represented
  as "gpa0-0", "gpa0-1", "gpa0-2" and so on. The names should be in lower case.
  The format of the pin names should be (as per the hardware manual)
  "[pin bank name]-[pin number within the bank]".

2. samsung,pin-function

The pin function selection that should be applied on the pins listed in the
  child node is specified using the "samsung,pin-function" property. The value
  of this property that should be applied to each of the pins listed in the
  "samsung,pins" property should be picked from the hardware manual of the SoC
  for the specified pin group. This property is optional in the child node if
  no specific function selection is desired for the pins listed in the child
  node. The value of this property is used as-is to program the pin-controller
  function selector register of the pin-bank.

3. samsung,pin-pud: Pull up/down configuration.
4. samsung,pin-drv: Drive strength configuration.

以上两个要看具体的硬件

  The values specified by these config properties should be derived from the
  hardware manual and these values are programmed as-is into the pin
  pull up/down and driver strength register of the pin-controller.

文章后面有个例子

uart0_data: uart0-data {
                        samsung,pins = "gpa0-0", "gpa0-1";
                        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
                        samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
                        samsung,pin-drv = <EXYNOS4_PIN_DRV_LV1>;
                };

另外再参考Documentation/devicetree/bindings/gpio/gpio-samsung.txt

The syntax of the gpio specifier used by client nodes
  should be the following with values derived from the SoC user manual.
     <[phandle of the gpio controller node]
      [pin number within the gpio controller]
      [mux function]
      [flags and pull up/down]
      [drive strength]>

  Values for gpio specifier:
  - Pin number: is a value between 0 to 7.
  - Flags and Pull Up/Down: 0 - Pull Up/Down Disabled.
                            1 - Pull Down Enabled.
                            3 - Pull Up Enabled.
          Bit 16 (0x00010000) - Input is active low.
  - Drive Strength: 0 - 1x,
                    1 - 3x,
                    2 - 2x,
                    3 - 4x

另外节后数据手册,不难理解。

以上是pinctrl相关,那对于具体的器件呢,如上面的beep节点

beep {
                compatible = "tiny4412,beep";
                reg = <0x114000A0 0x20>;
                tiny4412,gpio = <0x52 0x0 1>; // default low (1)
                pinctrl-names = "default";
                pinctrl-0 = <0x51>;
        };

对于compatible主要是驱动程序匹配时使用,必须与驱动里面的相同

const struct of_device_id tiny4412_beep_match_tbl[] = 
{
	{.compatible = "tiny4412,beep"},
	{},
};

对于reg指的是其gpio地址内存,即gpd0的地址,大小时0x20

对于tiny4412,gpio = <0x52 0x0 1>; 其中

·0x52    : [phandle of the gpio controller node], 这里无疑会指向pinctrl里面的gpd0

·0x0      : [pin number within the gpio controller]

·0x0      : [flags]  (0 - Active High ,  1 - Active Low)

对于pinctrl-names, 和pinctrl-0,这两个联系很紧密, 参考Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt

pinctrl-names里面定义多个字符串,表示多个状态,例如default, active, sleep..., 字符串时自定义的,而这些排列的字符串将会分别对应pinctrl-0, pinctrl-1, pinctrl-2, ...

驱动里面会使用这个names来区分不同的状态,从而调用pinctrl-x

Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt里面说

== Pinctrl client devices ==

For each client device individually, every pin state is assigned an integer
ID. These numbers start at 0, and are contiguous. For each state ID, a unique
property exists to define the pin configuration. Each state may also be
assigned a name. When names are used, another property exists to map from
those names to the integer IDs.

Each client device's own binding determines the set of states that must be
defined in its device tree node, and whether to define the set of state
IDs that must be provided, or whether to define the set of state names that
must be provided.

...

Required properties:
...
pinctrl-n:      List of phandles, each pointing at a pin configuration
                node within a pin controller.
pinctrl-names:  The list of names to assign states. List entry 0 defines the
                name for integer state ID 0, list entry 1 for state ID 1, and
                so on.

后面还有一个例子

/*
         * For an IP block whose binding supports pin configuration,
         * but in use on an SoC that doesn't have any pin control hardware
         */
        device {
                pinctrl-names = "active", "idle";
                pinctrl-0 = <>;
                pinctrl-1 = <>;
        };

 

驱动beep_dt.c

/*
 * beep_dt driver for tiny4412 on linux-4.19.27
*/
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>

#include <linux/platform_device.h>
#include <linux/slab.h>       //
#include <linux/io.h>

#include <linux/gfp.h>
#include <linux/cdev.h>

#include <linux/uaccess.h> /* copy_from_usr, */


struct beep_data
{
	char name[32];
	int gpio;
	int state;
	struct pinctrl *pctrl;
	struct pinctrl_state *def_state;
};

/*
* resource
* GPD0_0 for buzzer
*
* Note(s) : GPD0_1 as LCD
*/

struct _gpio
{
	u32 __iomem con;          /* GPM0 configuration register */
	u32 __iomem data;         /* GPM0 data register */
	u32 __iomem pud;          /* GPM0 pull-up/ pull-down register */
	u32 __iomem drv;          /* GPM0 drive strength control register */
	u32 __iomem conpdn;       /* GPM0 power down mode configuration register */
	u32 __iomem pudpdn;       /* GPM0 power down mode pull-up/ pull-down register 0x000 */
};

static struct _gpio *beep_gpio = NULL;


#define DEV_NAME		"beep"

struct beep_dev
{
	struct cdev *cdev;
	dev_t dev;
	int major;
	char name[32];
	struct class *class;
};


static struct beep_dev *beep_dev = NULL;


static int beep_open (struct inode *inode, struct file *file)
{
	printk(KERN_INFO "open \n");	
	
	return 0;
}

static int beep_close (struct inode *inode, struct file *file)
{
	printk(KERN_INFO "close \n");	
	
	return 0;
}

static ssize_t beep_write (struct file *file, const char __user *usrbuf, size_t len, loff_t *off)
{
	char cmd,opt;
	char rev_buf[8] = {0};
	
	printk(KERN_INFO "write \n");		

	if(copy_from_user(rev_buf,usrbuf,8))
		return -EFAULT;

	cmd = rev_buf[0];
	opt = rev_buf[1];

	printk(KERN_NOTICE "cmd : %d opt : %d \n", cmd, opt);

	if(cmd == 0)
	{
		// off
		beep_gpio->data &= ~(1<<0);		
	}
	else if(cmd == 1)
	{
		// on
		beep_gpio->data |= (1<<0);
	}
	else
	{
		// do nothing.
	}
	return 0;
}

static long beep_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
{
	printk(KERN_INFO "ioctl \n");	

	return 0;
}


const struct file_operations beep_ops = 
{
	.owner = THIS_MODULE,
	.open = beep_open,
	.release = beep_close,
	.unlocked_ioctl = beep_ioctl,
	.write = beep_write,
};


static ssize_t beep_show(struct device *dev,
    struct device_attribute *attr, char *buf)
{
	return 0;	
}

static ssize_t beep_store(struct device *dev,
    struct device_attribute *attr, const char *buf, size_t count)
{
	struct beep_data *data = dev_get_drvdata(dev);
	char state[16];
	int value = 0;

	if (sscanf(buf, "%s", state) != 1)
		return -EINVAL;

	if (sscanf(buf, "%d", &value)) {
		pr_notice("enter %s  value : %d \n", __func__, value);	
		if (value & (1 << 0))
			gpio_set_value(data->gpio, 1);
		else
			gpio_set_value(data->gpio, 0);
	}
	
	return count;
}


static DEVICE_ATTR(beep, 0644, beep_show,
    beep_store);


static int chrdev_setup(void)
{
	int ret = 0;
	beep_dev = kmalloc(sizeof(struct beep_dev),GFP_KERNEL);
	if(!beep_dev)
		return -ENOMEM;

	strcpy(beep_dev->name, DEV_NAME);

	ret = alloc_chrdev_region(&beep_dev->dev, 0, 1, beep_dev->name);
	beep_dev->major = MAJOR(beep_dev->dev);
	
	if(ret < 0)
	{
		kfree(beep_dev);
		return ret;
	}

	beep_dev->cdev = cdev_alloc();
	if(!beep_dev->cdev)
	{
		unregister_chrdev_region(beep_dev->dev,1);
		kfree(beep_dev);
		return -EFAULT;
	}
	cdev_init(beep_dev->cdev,&beep_ops);
	beep_dev->cdev->owner = THIS_MODULE;
	beep_dev->cdev->ops = &beep_ops;
	cdev_add(beep_dev->cdev,beep_dev->dev,1);

	beep_dev->class = class_create(THIS_MODULE,beep_dev->name);
	ret = PTR_ERR(beep_dev->class);
	if (IS_ERR(beep_dev->class))
	{
		cdev_del(beep_dev->cdev);
		unregister_chrdev_region(beep_dev->dev,1);
		kfree(beep_dev);
		return -EFAULT;
	}

	device_create(beep_dev->class,NULL,beep_dev->dev,NULL,beep_dev->name,beep_dev);

	return 0;
}

static void chrdev_remove(void)
{
	device_destroy(beep_dev->class, beep_dev->dev);
	class_destroy(beep_dev->class);
	cdev_del(beep_dev->cdev);
	unregister_chrdev_region(beep_dev->dev,1);
	kfree(beep_dev);
}

static int beep_probe(struct platform_device *pdev)
{
	int ret = 0;
	int gpio = -1;
	struct beep_data *data = NULL;
	struct resource *res = NULL;

	struct device *dev = &pdev->dev;
	
	pr_notice("enter %s\n", __func__);	

	if (!dev->of_node) {
        dev_err(dev, "no platform data.\n");
        return -EINVAL;
    }

	ret = chrdev_setup();	
	if(ret){
		dev_err(dev, "setup error .\n");
		return -EINVAL;
	}

	data = devm_kzalloc(dev, sizeof(struct beep_data), GFP_KERNEL);
	if(!data)
	{
		dev_err(dev, "no memory.\n");
		return -ENOMEM;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if(!res){
		dev_err(dev, "no resource.\n");
		devm_kfree(dev, data);
		return -ENOMEM;
	}

	
	beep_gpio = (struct _gpio *)devm_ioremap(dev, res->start, res->end - res->start);
	if(!beep_gpio){
		dev_err(dev, "ioremap error.\n");
		devm_kfree(dev, data);
		return -EINVAL;
	}

	gpio = of_get_named_gpio(dev->of_node, "tiny4412,gpio", 0); 
	if (gpio < 0) {
		dev_err(dev, "Looking up %s (%d) property in node %s failed %d\n",
               "tiny4412,gpio", 0, dev->of_node->full_name, gpio);
		goto err;
	}

	printk("%s, cfg: 0x%x,data: 0x%x, pud: 0x%x, drv: 0x%x\n", __func__, 
			readl(&beep_gpio->con),readl(&beep_gpio->data), readl(&beep_gpio->pud), readl(&beep_gpio->drv));

	data->gpio = gpio;
	if (gpio_is_valid(gpio)) {
		sprintf(data->name, "tiny4412.beep%d", 0);
		ret = devm_gpio_request_one(dev, gpio, GPIOF_INIT_LOW, data->name);
		if (ret < 0) {
               dev_err(dev, "request gpio %d failed.\n", gpio);
               goto err;
        }
	}
	
	printk("%s, cfg: 0x%x,data: 0x%x, pud: 0x%x, drv: 0x%x\n", __func__, 
		readl(&beep_gpio->con),readl(&beep_gpio->data), readl(&beep_gpio->pud), readl(&beep_gpio->drv));

	data->pctrl = devm_pinctrl_get(dev);  // get pinctrl
	if (IS_ERR(data->pctrl)) {
        dev_err(dev, "pinctrl get failed.\n");
        devm_gpio_free(dev, gpio);
        goto err;
    }

	data->def_state = pinctrl_lookup_state(data->pctrl, "default");
	if (IS_ERR(data->def_state)) {
        dev_err(dev, "look up sleep state failed.\n");
        devm_pinctrl_put(data->pctrl);
        devm_gpio_free(dev, gpio);
        goto err;
    }

	dev_set_drvdata(dev, data);

	// /sys/bus/platform/drivers/leds/110002e0.led 下面生成led节点
	// 可以使用echo 1 > ./led写指令控制led, 最终调用leds_store达到控制的目的
	device_create_file(dev, &dev_attr_beep);
		
	pinctrl_select_state(data->pctrl, data->def_state);
	data->state = 1;
	printk("%s, cfg: 0x%x,data: 0x%x, pud: 0x%x, drv: 0x%x\n", __func__, 
		readl(&beep_gpio->con),readl(&beep_gpio->data), readl(&beep_gpio->pud), readl(&beep_gpio->drv));
	
	return 0;

err:
	devm_iounmap(dev, beep_gpio);
	devm_kfree(dev, data);
	return -EINVAL;	
}

static int beep_remove(struct platform_device *pdev)
{
	pr_notice("enter %s\n", __func__);

	device_remove_file(&pdev->dev, &dev_attr_beep);
	
	chrdev_remove();
	
	return 0;
}



const struct of_device_id tiny4412_beep_match_tbl[] = 
{
	{.compatible = "tiny4412,beep"},
	{},
};


static struct platform_driver tiny4412_beep_dt_drv = 
{
	.probe = beep_probe,
	.remove = beep_remove,
	.driver = 
	{
		.owner = THIS_MODULE,
		.name = "beep",
		.of_match_table = tiny4412_beep_match_tbl,
	}
};


static int __init beep_dt_init(void)
{
	int ret = 0;

	ret = platform_driver_register(&tiny4412_beep_dt_drv);
	if(ret)
		printk(KERN_ERR "init demo: probe failed: %d\n", ret);

	return ret;
}

static void __exit beep_dt_exit(void)
{
	platform_driver_unregister(&tiny4412_beep_dt_drv);
}

module_init(beep_dt_init);
module_exit(beep_dt_exit);
MODULE_LICENSE("GPL");



 Makefile

TARGET  := beep_dt

obj-m   += $(TARGET).o

BIN_PATH = /home/flinn/bin/tiny4412/
ROOTFS = /home/flinn/tmp/rootfs
KERNEL = /home/flinn/tiny4412-SDK/linux-4.19.27
DT     = exynos4412-tiny4412

all:
        make -C $(KERNEL) M=`pwd` modules

clean:
        make -C $(KERNEL) M=`pwd` clean
        
install:
        #make -C $(KERNEL) M=`pwd` modules_install INSTALL_MOD_PATH=$(ROOTFS)
        sudo cp $(TARGET).ko $(ROOTFS)
        sudo cp $(DT).dtb $(BIN_PATH)

dtbs:
        #dtc -I dtb -O dts tiny4412.dts -o tiny4412.dtb
        dtc -O dtb -o $(DT).dtb $(DT).dts

 最终,进入文件系统后

在 /sys/bus/platform/drivers/beep/114000a0.beep目录下会生成beep节点。执行

echo 1 > beep  // 蜂鸣器会响

echo 0 > beep   // 蜂鸣器会停止响

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值