agx pwm使用

devmem+pwmchip方式

devmem

使用devmem无需更改设备树与烧写新固件,可用于初步调试pwm功能。如果insmod pwm-user.ko后,会出现pwmchip*无法打开pwm0。

安装devmem

sudo apt-get install busybox

pwmchip 节点

/sys/class/pwm# ls -l
lrwxrwxrwx 1 root root 0 Apr 20 10:10 pwmchip0 -> ../../devices/3280000.pwm/pwm/pwmchip0          pwm1
lrwxrwxrwx 1 root root 0 Apr 20 10:10 pwmchip1 -> ../../devices/c340000.pwm/pwm/pwmchip1          pwm4
lrwxrwxrwx 1 root root 0 Apr 20 10:10 pwmchip2 -> ../../devices/32c0000.pwm/pwm/pwmchip2          pwm5
lrwxrwxrwx 1 root root 0 Apr 20 10:10 pwmchip3 -> ../../devices/32f0000.pwm/pwm/pwmchip3	  pwm8
lrwxrwxrwx 1 root root 0 Apr 20 10:10 pwmchip4 -> ../../devices/39c0000.tachometer/pwm/pwmchip4  

调试pwm

pwm5
busybox devmem 0x02434090 32 0x00000401
busybox devmem 0x02434090

echo 0 > /sys/class/pwm/pwmchip2/export
echo 20000000 > /sys/class/pwm/pwmchip2/pwm0/period
echo 1500000 > /sys/class/pwm/pwmchip2/pwm0/duty_cycle
echo 1 > /sys/class/pwm/pwmchip2/pwm0/enable

echo 15000000 > /sys/class/pwm/pwmchip2/pwm0/duty_cycle
echo 20000000 > /sys/class/pwm/pwmchip2/pwm0/duty_cycle
pwm1
busybox devmem 0x02440020 32 0x00000402
busybox devmem 0x02440020

echo 0 > /sys/class/pwm/pwmchip0/export
echo 20000000 > /sys/class/pwm/pwmchip0/pwm0/period
echo 1500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

echo 15000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
echo 20000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
pwm8
busybox devmem 0x02430040 32 0x00000401
busybox devmem 0x02430040

echo 0 > /sys/class/pwm/pwmchip3/export
echo 20000000 > /sys/class/pwm/pwmchip3/pwm0/period
echo 1500000 > /sys/class/pwm/pwmchip3/pwm0/duty_cycle
echo 1 > /sys/class/pwm/pwmchip3/pwm0/enable

echo 15000000 > /sys/class/pwm/pwmchip3/pwm0/duty_cycle
echo 20000000 > /sys/class/pwm/pwmchip3/pwm0/duty_cycle

pwm debug信息

root@localhost:/# cat /sys/kernel/debug/pwm
platform/39c0000.tachometer, 1 PWM device
 pwm-0   (generic_pwm_tachomet): requested period: 0 ns duty: 0 ns polarity: normal

platform/32f0000.pwm, 1 PWM device
 pwm-0   (user-pwm8           ): requested enabled period: 8000 ns duty: 0 ns polarity: normal

platform/32c0000.pwm, 1 PWM device
 pwm-0   (user-pwm5           ): requested enabled period: 5000 ns duty: 0 ns polarity: normal

platform/c340000.pwm, 1 PWM device
 pwm-0   (pwm-fan             ): requested enabled period: 45334 ns duty: 0 ns polarity: normal

platform/3280000.pwm, 1 PWM device
 pwm-0   (user-pwm1           ): requested enabled period: 10000 ns duty: 0 ns polarity: normal

cfg+设备树+驱动方式

cfg

cfg的配置过程

Jetson_AGX_Series_DevKit_Pinmux_Configuration_Template.xlsm 配置生成*.dtsi
–> Linux_for_Tegra/kernel/pinmux/t19x/pinmux-dts2cfg.py 生成 *.cfg配置文件
–> 复制配置文件到Linux_for_Tegra/bootloader/t186ref/BCT/tegra19x-mb1-pinmux-p2888-0000-a04-p2822-0000-b01.cfg

实际只需确认以下内容即可:
Linux_for_Tegra/bootloader/t186ref/BCT/tegra19x-mb1-pinmux-p2888-0000-a04-p2822-0000-b01.cfg内容包含

pinmux.0x02440020 = 0x00000402; # soc_gpio54_pn1
pinmux.0x02434090 = 0x00000401; # soc_gpio12_ph0
pinmux.0x02430040 = 0x00000401; # soc_gpio44_pr0

设备树

Linux_for_Tegra/source/public/hardware/nvidia/platform/t19x/galen/kernel-dts/user/user-pwm.dtsi

/*
 * Copyright (c) 2021- zwq.  All rights reserved.
 *
 *
 */
#include <dt-bindings/gpio/tegra194-gpio.h>
#include <dt-bindings/pinctrl/pinctrl-tegra.h>

/ {
	user-pwm {
		compatible = "user,user-pwm";
		status = "okay";

		user-pwm1 {
			pwms = <&tegra_pwm1 0 45334 PWM_POLARITY_NORMAL>;
			pwm-id = <1>;
			duty = <10>;
			period = <10000>;
			polarity = <0>;
			enable = <1>;
		};

		user-pwm5 {
			pwms = <&tegra_pwm5 0 20000>;
			pwm-id = <5>;
			duty = <50>;
			period = <5000>;
			polarity = <1>;
			enable = <1>;
		};
				
		user-pwm8 {
			pwms = <&tegra_pwm8 0 45334 PWM_POLARITY_NORMAL>; 
			pwm-id = <8>;
			duty = <80>;
			period = <8000>;
			polarity = <1>;
			enable = <1>;
		};

	};

	tegra_pwm5: pwm@32c0000 {
		status = "okay";
	};

	tegra_pwm1: pwm@3280000 {
		status = "okay";
	};

	tegra_pwm8: pwm@32f0000 {
		status = "okay";
	};
};

Linux_for_Tegra/source/public/hardware/nvidia/platform/t19x/galen/kernel-dts/tegra194-p2888-0001-p2822-0000.dts

#include "common/tegra194-p2888-0001-p2822-0000-common.dtsi"
#include "common/tegra194-p2822-camera-modules.dtsi"
#include "t19x-common-modules/tegra194-camera-plugin-manager.dtsi"

#include "user/user-pwm.dtsi"

编译内核,烧写整个固件后引脚配置才能生效(cfg配置文件生效)。

驱动

Linux_for_Tegra/source/public/kernel/kernel-4.9/drivers/pwm/pwm-user.c

#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/kthread.h>
#include <linux/pwm.h>

#include <dt-bindings/gpio/tegra194-gpio.h>

#define PWMX_MAX_NUM 10

struct pwmx_device_t {
	int pwm_id;
	struct pwm_device *pwm;
	struct device *dev;
	
	int enable;
	enum pwm_polarity polarity;
	int duty;
	int period;
};

struct pwmx_class_t {
	struct class *pwm_class;
	struct pwmx_device_t pwm_devs[PWMX_MAX_NUM];
	int pwm_cnt;
};

static ssize_t pwmx_enable_show(struct device * dev, struct device_attribute * attr, char * buf)
{
	struct pwmx_device_t * xdev = dev_get_drvdata(dev);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "enable")){
		if(xdev->enable)
			return strlcpy(buf, "1\n", 3);
		else
			return strlcpy(buf, "0\n", 3);
	}
	return strlcpy(buf, "0\n", 3);
}

static ssize_t pwmx_enable_store(struct device * dev, struct device_attribute * attr, const char * buf, size_t count)
{
	struct pwmx_device_t * xdev = dev_get_drvdata(dev);
	unsigned long on = simple_strtoul(buf, NULL, 10);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "enable")){
		
		if(on){
			pwm_enable(xdev->pwm);
		}else{
			pwm_disable(xdev->pwm);
		}
	
		xdev->enable = on ? 1 : 0;
	}
	
	return count;
}

static ssize_t pwmx_polarity_show(struct device * dev, struct device_attribute * attr, char * buf)
{
	const struct pwmx_device_t * xdev = dev_get_drvdata(dev);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "polarity")){
		if(xdev->polarity == PWM_POLARITY_NORMAL)
			return sprintf(buf, "%s\n","normal");
		else
			return sprintf(buf, "%s\n", "inversed");
	}
	
	return sprintf(buf, "%s\n","normal");
}

static ssize_t pwmx_polarity_store(struct device * dev, struct device_attribute * attr, const char * buf, size_t count)
{
	struct pwmx_device_t * xdev = dev_get_drvdata(dev);
	
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "polarity")){
		if( !strcmp(buf,"normal")){
			xdev->polarity = PWM_POLARITY_NORMAL;
		}
		else if( !strcmp(buf,"inversed")){
			xdev->polarity = PWM_POLARITY_INVERSED;
		}else{
			return 0;
		}
		
		pwm_set_polarity(xdev->pwm,xdev->polarity);
	}

	return 0;
}

static ssize_t pwmx_duty_show(struct device * dev, struct device_attribute * attr, char * buf)
{
	struct pwmx_device_t * xdev = dev_get_drvdata(dev);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "duty")){
		return sprintf(buf,"%u\n",xdev->duty);
	}
	return strlcpy(buf, "0\n", 3);
}

static ssize_t pwmx_duty_store(struct device * dev, struct device_attribute * attr, const char * buf, size_t count)
{
	struct pwmx_device_t * xdev = dev_get_drvdata(dev);
	unsigned long duty = simple_strtoul(buf, NULL, 10);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "duty")){
		xdev->duty = duty>100?100:duty;
		pwm_config(xdev->pwm,(xdev->duty*xdev->period/100),xdev->period);
	}
	
	return count;
}

static ssize_t pwmx_period_show(struct device * dev, struct device_attribute * attr, char * buf)
{
	struct pwmx_device_t * xdev = dev_get_drvdata(dev);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "period")){
		return sprintf(buf,"%u\n",xdev->period);
	}
	return strlcpy(buf, "0\n", 3);
}

static ssize_t pwmx_period_store(struct device * dev, struct device_attribute * attr, const char * buf, size_t count)
{
	struct pwmx_device_t * xdev = dev_get_drvdata(dev);

	unsigned long period = simple_strtoul(buf, NULL, 10);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "period")){
		xdev->period = period;
		pwm_config(xdev->pwm,xdev->duty,xdev->period);
	}
	
	return count;
}
	
static DEVICE_ATTR(enable, 0664, pwmx_enable_show, pwmx_enable_store);
static DEVICE_ATTR(polarity, 0664, pwmx_polarity_show, pwmx_polarity_store);
static DEVICE_ATTR(duty, 0664, pwmx_duty_show, pwmx_duty_store);
static DEVICE_ATTR(period, 0664, pwmx_period_show, pwmx_period_store);

static struct attribute * pwmx_attrs[] = {
	&dev_attr_enable.attr,
	&dev_attr_polarity.attr,
	&dev_attr_duty.attr,
	&dev_attr_period.attr,
	NULL
};

static const struct attribute_group pwmx_group = {
	.attrs = pwmx_attrs,
};

static int init_device_data(struct pwmx_device_t *dev,struct pwm_device *pwm,int pwm_id,int enable,int polarity,int duty,int period)
{
	if(dev == NULL)
		return -1;

	dev->pwm = pwm;
	dev->pwm_id = pwm_id;
	dev->enable = enable;
	dev->polarity = polarity;
	dev->duty = duty;
	dev->period = period;

	pwm_config(dev->pwm, (duty*period/100), period);
	
	if(dev->enable){
		pwm_enable(dev->pwm);
	}else{
		pwm_disable(dev->pwm);
	}

	return 0;
}

static int create_device_nodes(struct pwmx_class_t *cls,struct pwm_device *pwm,int pwm_id,int enable,int polarity,int duty,int period)
{
	int ret = 0;
	if(cls == NULL)
		return -1;
	
	if(cls->pwm_cnt>=PWMX_MAX_NUM)	
		return -2;
	
	cls->pwm_devs[cls->pwm_cnt].dev = device_create(cls->pwm_class, NULL,MKDEV(0, cls->pwm_cnt), NULL, "%s", name);
	ret = sysfs_create_group(&cls->pwm_devs[cls->pwm_cnt].dev->kobj,&pwmx_group);

	init_device_data(&cls->pwm_devs[cls->pwm_cnt],pwm,pwm_id,enable,polarity,duty,period);
	
	dev_set_drvdata(cls->pwm_devs[cls->pwm_cnt].dev, &cls->pwm_devs[cls->pwm_cnt]);

	cls->pwm_cnt++;

	return 0;
}

static int user_pwm_probe(struct platform_device * pdev)
{
	struct pwm_device * pwm_dev = NULL;
	struct device_node * node = pdev->dev.of_node;
	struct pwmx_class_t * pwmx_class;
	struct device_node *child;

	int ret;
	int cnt = 0;
	int duty = 0;
	int enable = 0;
	int polarity = 0;
	int period = 0;
	int pwm_id = 0;
	char name[20];

	if(!node)
		return -ENODEV;

	pwmx_class = kzalloc(sizeof(struct pwmx_class_t), GFP_KERNEL);
	if (!pwmx_class){
		printk("%s -ENOMEM\n",__func__);
		return -ENOMEM;
	}
	pwmx_class->pwm_cnt = 0;
	pwmx_class->pwm_class = class_create(THIS_MODULE, "pwm_usr");

	for_each_child_of_node(node, child){
	//*********************************************************
	
	ret = of_property_read_u32(child, "duty",&cnt);
	if (ret || !cnt ) {
		pr_err("no duty\n");
		goto INIT_ERR_FREE;
	}
	duty = cnt;
	printk("get duty:%d\n",duty);
	
	ret = of_property_read_u32(child,"enable",&cnt);
	if (ret || !cnt ) {
		goto INIT_ERR_FREE;
	}
	enable = cnt;
	printk("get enable:%d\n",enable);

	ret = of_property_read_u32(child,"pwm-id",&cnt);
	if(ret || !cnt){
		goto INIT_ERR_FREE;
	}
	pwm_id = cnt;
	printk("get pwm_id %d\n",pwm_id);

	ret = of_property_read_u32(child,"period",&cnt);
        if (ret || !cnt ) {
                goto INIT_ERR_FREE;
        }
        period = cnt;
        printk("get period:%d\n",period);

	ret = of_property_read_u32(child,"polarity",&cnt);
        if (ret) {
                goto INIT_ERR_FREE;
        }
        polarity = cnt;
        printk("get polarity:%d\n",polarity);

	sprintf(name,"pwm%d",pwm_id);
	pwm_dev = of_pwm_get(child, NULL);
        if (IS_ERR(pwm_dev)) {
		dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
                pwm_dev = pwm_request(pwm_id, name);
        }
        if (IS_ERR(pwm_dev)) {
                ret = PTR_ERR(pwm_dev);
                if (ret != -EPROBE_DEFER)
                        dev_err(&pdev->dev, "unable to request PWM\n");
                goto INIT_ERR_FREE;
        }
	
	printk("pwm%d duty:%d%% period:%d polarity:%d enable:%d\n",pwm_id,duty,period,polarity,enable);
		
	create_device_nodes(pwmx_class,pwm_dev,pwm_id,enable,polarity,duty,period);

	//********************************************************
	}

	dev_set_drvdata(&pdev->dev, pwmx_class);
	pr_info("pwm_init finish\n");
	return 0;

INIT_ERR_FREE:
	pr_err("pwm_init err\n");
	kfree(pwmx_class);
	return -1;
}

static int user_pwm_remove(struct platform_device *pdev)
{
	int i = 0;
	struct pwmx_class_t * xdev = dev_get_drvdata(&pdev->dev);
	if(xdev != NULL){
		for(i=0;i<xdev->pwm_cnt;i++){
			pwm_disable(xdev->pwm_devs[i].pwm);
	 		pwm_free(xdev->pwm_devs[i].pwm);
			printk("pwm%d free\n",xdev->pwm_devs[i].pwm_id);
			
			sysfs_remove_group(&xdev->pwm_devs[i].dev->kobj, &pwmx_group);
			
			device_destroy(xdev->pwm_class,MKDEV(0, i));
		}
		class_destroy(xdev->pwm_class);
	}
	return 0;
}

#ifdef CONFIG_PM
static int user_pwm_suspend(struct device *dev)
{
	return 0;
}

static int user_pwm_resume(struct device *dev)
{
	return 0;
}
#else
#define user_pwm_suspend NULL
#define user_pwm_resume NULL
#endif

static const struct dev_pm_ops user_pwm_pm_ops = {
	.suspend = user_pwm_suspend,
	.resume = user_pwm_resume,
};

static struct of_device_id user_pwm_of_match[] = {
	{ .compatible = "user,user-pwm" },
	{},
};
MODULE_DEVICE_TABLE(of, user_pwm_of_match);

static struct platform_driver user_pwm_driver = {
	.driver		= {
		.name	= "user-pwm",
		.owner	= THIS_MODULE,
		.pm	= &user_pwm_pm_ops,
		.of_match_table	= of_match_ptr(user_pwm_of_match),
	},
	.probe		= user_pwm_probe,
	.remove		= user_pwm_remove,
};
module_platform_driver(user_pwm_driver);

MODULE_DESCRIPTION("user pwm driver");
MODULE_AUTHOR("zhengweiqing, 1548889230@qq.com");
MODULE_LICENSE("GPL");

Linux_for_Tegra/source/public/kernel/kernel-4.9/drivers/pwm/Makefile

obj-m                   += pwm-user.o

编译烧写

编译烧写

Linux_for_Tegra/source/public/kernel/kernel-4.9下编译内核, Linux_for_Tegra/目录下使用flash.sh脚本烧写固件。

测试

驱动加载

sudo insmod pwm-user.ko

测试pwm

cat /sys/kernel/debug/pwm

root@localhost:/# cat /sys/kernel/debug/pwm 
platform/39c0000.tachometer, 1 PWM device
 pwm-0   (generic_pwm_tachomet): requested period: 0 ns duty: 0 ns polarity: normal

platform/32f0000.pwm, 1 PWM device
 pwm-0   (user-pwm8           ): requested enabled period: 8000 ns duty: 6400 ns polarity: normal

platform/32c0000.pwm, 1 PWM device
 pwm-0   (user-pwm5           ): requested enabled period: 5000 ns duty: 2500 ns polarity: normal

platform/c340000.pwm, 1 PWM device
 pwm-0   (pwm-fan             ): requested enabled period: 45334 ns duty: 0 ns polarity: normal

platform/3280000.pwm, 1 PWM device
 pwm-0   (user-pwm1           ): requested enabled period: 10000 ns duty: 1000 ns polarity: normal
root@localhost:/# 
root@localhost:/# cd /sys/class/pwm_usr/
root@localhost:/sys/class/pwm_usr# ls
pwm1  pwm5  pwm8
root@localhost:/sys/class/pwm_usr# cd pwm8/
root@localhost:/sys/class/pwm_usr/pwm8# ls
duty  enable  period  polarity  power  subsystem  uevent
root@localhost:/sys/class/pwm_usr/pwm8# echo 100 > duty 
root@localhost:/sys/class/pwm_usr/pwm8# cd ../
root@localhost:/sys/class/pwm_usr# echo 70 > pwm1/duty 
root@localhost:/sys/class/pwm_usr# echo 50 > pwm5/duty 

root@localhost:/sys/class/pwm_usr# cat /sys/kernel/debug/pwm
platform/39c0000.tachometer, 1 PWM device
 pwm-0   (generic_pwm_tachomet): requested period: 0 ns duty: 0 ns polarity: normal

platform/32f0000.pwm, 1 PWM device
 pwm-0   (user-pwm8           ): requested enabled period: 8000 ns duty: 8000 ns polarity: normal

platform/32c0000.pwm, 1 PWM device
 pwm-0   (user-pwm5           ): requested enabled period: 5000 ns duty: 2500 ns polarity: normal

platform/c340000.pwm, 1 PWM device
 pwm-0   (pwm-fan             ): requested enabled period: 45334 ns duty: 0 ns polarity: normal

platform/3280000.pwm, 1 PWM device
 pwm-0   (user-pwm1           ): requested enabled period: 10000 ns duty: 7000 ns polarity: normal
root@localhost:/sys/class/pwm_usr# 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值