platform总线框架+PWM设备驱动框架模板

内核版本:4.14.0
基于设备树

#include <linux/module.h> 
#include <linux/kernel.h> 
#include <linux/platform_device.h> 
#include <linux/pwm.h> 
#include <linux/io.h> 
#include <linux/clk.h> 

#define DEVICE_NAME		"xxx"				/* Device name */
#define COMPAT_PROPT	"xxx,xxx"			/* Compatible property of the device matched with this driver */
#define CLK_NAME		"xxx"

#define S_2_NS			1000000000

/* PWM register define */
#define PWM_AXI_CTRL_REG_OFFSET 	0x00 
#define PWM_AXI_STATUS_REG_OFFSET 	0x04 
#define PWM_AXI_PERIOD_REG_OFFSET 	0x08 
#define PWM_AXI_DUTY_REG_OFFSET 	0x40

/* Device information structure. */
struct pwm_info { 	
	struct device *p_dev;		
	struct pwm_chip chip;				/* describe a PWM device */
	struct clk *p_pwmclk;				/* PWM input clock */
	void __iomem *baseaddr;				/* PWM	register base addr */
	unsigned int period_min_ns			/* PWM min period */
};

/* 
 * @description:		PWM period and duty cycle configuration function.
 * @param – chip:		The pointer to struct pwm_chip.
 * @param - pwm:		The pointer to struct pwm_device, means pwm channel(s).
 * @param - duty_ns:	PWM duty cycle in ns.
 * @param - period_ns:	PWM period in ns.
 * @return:				0: Successful; Negative: Failed.
 */
static int axi_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) 
{
	u32 duty_mult, period_mult; 
	struct pwm_info *p_pwminfo = container_of(chip, struct pwm_info, chip); 

	if (period_ns < p_pwminfo->period_min_ns) 
		period_ns = p_pwminfo->period_min_ns; 

	period_mult = period_ns / p_pwminfo->period_min_ns; 
	duty_mult = duty_ns / p_pwminfo->period_min_ns; 

	printk(KERN_INFO "period:%d*%dns, duty:%d*%dns\n", period_mult,
		p_pwminfo->period_min_ns, duty_mult, p_pwminfo->period_min_ns); 

	writel(period_mult, p_pwminfo->baseaddr+PWM_AXI_PERIOD_REG_OFFSET); 
	writel(duty_mult, p_pwminfo->baseaddr+PWM_AXI_DUTY_REG_OFFSET+4*pwm->hwpwm); 
	
	return 0; 
}

/* 
 * @description:		PWM enable function.
 * @param – chip:		The pointer to struct pwm_chip.
 * @param - pwm:		The pointer to struct pwm_device, means pwm channel(s).
 * @return:				0: Successful; Negative: Failed.
 */
static int axi_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 
{ 
	struct pwm_info *p_pwminfo = container_of(chip, struct pwm_info, chip); 
	
	writel(1, p_pwminfo->baseaddr+PWM_AXI_CTRL_REG_OFFSET); 
	
	return 0; 
}

/* 
 * @description:		PWM disable function.
 * @param – chip:		The pointer to struct pwm_chip.
 * @param - pwm:		The pointer to struct pwm_device, means pwm channel(s).
 * @return:				None.
 */
static void axi_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 
{ 
	struct pwm_info *p_pwminfo = container_of(chip, struct pwm_info, chip); 
	
	writel(0, p_pwminfo->baseaddr+PWM_AXI_CTRL_REG_OFFSET); 
}

/* The pwm operation function structure. */
static const struct pwm_ops pwm_fops = {
	.owner = THIS_MODULE,
	.config = axi_pwm_config,		/* pwm config function */
	.enable = axi_pwm_enable,		/* pwm enable function */
	.disable = axi_pwm_disable,		/* pwm disable function */
}; 

/* 
 * @description :	Initialize the pwm.
 * @param -pdev:	Pointer to platform device.
 * @return :		0: Successful; Others: Failed.
 */
static int pwm_init(struct platform_device *pdev) 
{ 
	int ret;
	const char *str;
	unsigned long clk_rate;
	struct resource *res;
	struct pwm_info *p_pwminfo  = platform_get_drvdata(pdev);
	
	/* Get the platform resources, get the register base address */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 
	/* Mapped to a virtual address */
	p_pwminfo->baseaddr = devm_ioremap_resource(&pdev->dev, res); 
	if (IS_ERR(p_pwminfo->baseaddr)) 
		return PTR_ERR(p_pwminfo->baseaddr); 
	
	/* Get the clock */ 
	p_pwminfo->p_pwmclk = devm_clk_get(&pdev->dev, CLK_NAME); 
	if (IS_ERR(p_pwminfo->p_pwmclk)) 
	{ 
		dev_err(&pdev->dev, "failed to get %s\n", CLK_NAME); 
		return PTR_ERR(p_pwminfo->p_pwmclk); 
	}
	
	clk_rate = clk_get_rate(p_pwminfo->p_pwmclk);
	
	/* Calculate the minimum period of the PWM */
	p_pwminfo->period_min_ns = S_2_NS/clk_rate;
	
	printk(KERN_INFO "pwm_clk: %ldHz, period_min: %dns\n", clk_rate, p_pwminfo->period_min_ns);
	
	/* Init struct pwm_chip */
	p_pwminfo->chip.dev = &pdev->dev;
	p_pwminfo->chip.ops = &pwm_fops;
	p_pwminfo->chip.base = 0; 			/* first number of the PWM's channel */
	ret = of_property_read_u32(pdev->dev.of_node, "npwm", &p_pwminfo->chip.npwm); 
	if (ret)
	{ 
		dev_err(&pdev->dev, "failed to read npwm\n"); 
		return ret; 
	}
	
	return 0;
}

/* 
 * @description :	Probe function of the platform, it will be executed when the 
 * 					platform driver and platform device matching successfully.
 * @param -pdev :	Pointer to platform device.
 * @return :		0: Successful; Others: Failed.
 */
static int pwm_probe(struct platform_device *pdev)
{
	int ret;
	struct pwm_info *p_pwminfo;
	
	dev_info(&pdev->dev, "Driver and device matched successfully!\n");
	
	/* Allocate memory for struct led_info */
	p_pwminfo = devm_kzalloc(&pdev->dev, sizeof(struct pwm_info), GFP_KERNEL);
	if (!p_pwminfo)
		return -ENOMEM;
	
	p_pwminfo->p_dev = &pdev->dev;
	
	/* Store the misc_info pointer in pdev->dev.driver_data for later use */
	platform_set_drvdata(pdev, p_pwminfo);
	
	/* pwm device init */
	ret = pwm_init(pdev);
	if (ret)
		return ret;
	
	/* Register pwm device */
	ret = pwmchip_add(&p_pwminfo->chip); 
	if (ret < 0)
	{
		dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret); 
		return ret; 
	}
	
	return 0 ;
} 

/* 
 * @description :	Release some resources. This function will be executed when the platform
 *					driver module is unloaded.
 * @param :			None.
 * @return :		0: Successful; Others: Failed.
 */
static int pwm_remove(struct platform_device *pdev) 
{ 
	unsigned int i;
	/* Get the struct misc_info pointer which is stored in pdev->dev.driver_data before */
	struct pwm_info *p_pwminfo = platform_get_drvdata(pdev);
	
	/* Some Reset code */
	/* disable pwm output */
	for (i=0; i<p_pwminfo->chip.npwm; i++)
		writel(0, p_pwminfo->baseaddr+PWM_AXI_CTRL_REG_OFFSET); 
	
	/* Unregister the pwm device */
	pwmchip_remove(&p_pwminfo->chip);
	
	dev_info(&pdev->dev, "Driver has been removed!\n"); 

	return 0;
} 

/* Match table */
static const struct of_device_id pwm_of_match[] = {
	{.compatible = COMPAT_PROPT},
	{/* Sentinel */}
};

/* 
 * Declare device matching table. Note that this macro is generally used to dynamically 
 * load and unload drivers for hot-pluggable devices such as USB devices.
 */
MODULE_DEVICE_TABLE(of, pwm_of_match); 

/* Platform driver struct */
static struct platform_driver pwm_driver = {
	.driver = {
		.name = DEVICE_NAME,				//Drive name, used to match device who has the same name.
		.of_match_table = pwm_of_match,	//Used to match the device tree who has the same compatible property.
	},
	.probe = pwm_probe,					//probe function
	.remove = pwm_remove,				//remove function
};

/*
 * Register or unregister platform driver,
 * and Register the entry and exit functions of the Module.
 */
module_platform_driver(pwm_driver);

/* 
 *  Author, driver information and LICENSE.
 */ 
MODULE_AUTHOR("蒋楼丶");
MODULE_DESCRIPTION(DEVICE_NAME" Driver Based on Platform"); 
MODULE_LICENSE("GPL");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值