Project

General

Profile

新建 #105 » rp_pwm.c

余 顺, 08/30/2022 06:14 AM

 
/*
* PWM driver for Rockchip SoCs
*/

//original include file
#include <linux/module.h>
#include <linux/pwm.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>

//#include <dt-bindings/pwm/pwm.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/init.h>

//creat proc directory and file
#include <linux/proc_fs.h>
#include <linux/kernel.h>

//print driver information
#define PWM_INFO(fmt,arg...) // printk("<<-PWM-INFO->> "fmt"\n",##arg)

static unsigned int gval = 0;
struct rp_pwm_data {
int pwm_id;
struct pwm_device *pwm;
unsigned int period;
unsigned int pwm_period_ns;
unsigned int max_period;
unsigned int min_period;
unsigned int duty_ns;
bool enabled;
};
struct rp_pwm_data g_rpdzkj_pdata;

static int pwm_open(struct inode *inode, struct file *file)
{
PWM_INFO("here is %s", __func__);
return 0;
}
static ssize_t pwm_write(struct file *file, const char *buffer,size_t count, loff_t *data)
{
char buf[7] = {0}; //maxmum is 10000, and the str include a single newline before its terminating null, so be sizeof(10000)+2(\n\0)
unsigned long state;
int tmp_duty,ret;

PWM_INFO("here is %s", __func__);
if(copy_from_user(buf,buffer,count)){
printk("failed to copy data to kernel space\n");
return -EFAULT;
}
PWM_INFO("buf is %s, size is %ld", buf, sizeof(buf));
ret = kstrtoul(buf, 10, &state);
if (ret){
PWM_INFO("str_to_ul_faild!");
return ret;
}
if (state > g_rpdzkj_pdata.max_period)
state = g_rpdzkj_pdata.max_period;
else if (state < g_rpdzkj_pdata.min_period)
state = g_rpdzkj_pdata.min_period;
tmp_duty = state;

g_rpdzkj_pdata.enabled = true;
pwm_config(g_rpdzkj_pdata.pwm, tmp_duty, g_rpdzkj_pdata.pwm_period_ns);
pwm_enable(g_rpdzkj_pdata.pwm);
gval = tmp_duty;

return count;
}
static ssize_t pwm_read(struct file *file, char __user * buffer, size_t count, loff_t *data)
{
PWM_INFO("here is %s", __func__);
PWM_INFO(":\n\
duty: %d\n\
period: %d\n", gval, g_rpdzkj_pdata.pwm_period_ns);
return 0;
}

static const struct file_operations pwm_ops = {
.owner = THIS_MODULE,
.open = pwm_open,
.write = pwm_write,
.read = pwm_read,
};

static int rp_pwm_status_update(struct rp_pwm_data *pdata)
{
if (pdata->enabled)
return 0;

pwm_enable(pdata->pwm);
pwm_config(pdata->pwm, pdata->duty_ns, g_rpdzkj_pdata.pwm_period_ns);
pdata->enabled = true;
gval = pdata->duty_ns;
return 0;
}

ssize_t rp_pwm_parse_dt(struct rp_pwm_data *rp_pdata, struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const __be32 *id, *min_period, *max_period, *duty_ns;
int len;

id = of_get_property(np, "pwm_id", &len);
if (id)
rp_pdata->pwm_id = be32_to_cpu(*id);

min_period = of_get_property(np, "min_period", &len);
if (min_period)
rp_pdata->min_period = be32_to_cpu(*min_period);

max_period = of_get_property(np, "max_period", &len);
if (max_period)
rp_pdata->max_period = be32_to_cpu(*max_period);

rp_pdata->pwm_period_ns = rp_pdata->max_period - rp_pdata->min_period;

duty_ns = of_get_property(np, "duty_ns", &len);
if (duty_ns)
rp_pdata->duty_ns = be32_to_cpu(*duty_ns);

return 0;
}









static int rp_pwm_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct rp_pwm_data *rp_pdata = pdev->dev.platform_data;
int ret;
static struct proc_dir_entry *root_entry_pwm;

if (!np) {
dev_err(&pdev->dev, "Device Tree node missing\n");
return -EINVAL;
}

rp_pdata = devm_kzalloc(&pdev->dev, sizeof(*rp_pdata), GFP_KERNEL);
if (!rp_pdata)
return -ENOMEM;

if (np)
ret = rp_pwm_parse_dt(rp_pdata, pdev);

rp_pdata->enabled = false;

rp_pdata->pwm = pwm_request(rp_pdata->pwm_id, "rp-pwm");
if (IS_ERR(rp_pdata->pwm)) {
dev_err(&pdev->dev, "unable to request legacy PWM\n");
ret = PTR_ERR(rp_pdata->pwm);
goto err;
}
if (rp_pdata->pwm_period_ns > 0)
pwm_set_period(rp_pdata->pwm, rp_pdata->pwm_period_ns);
rp_pdata->period = pwm_get_period(rp_pdata->pwm);

g_rpdzkj_pdata = *rp_pdata;
rp_pwm_status_update(rp_pdata);
dev_info(&pdev->dev, "%s: rp PWM Demo !\n", __func__);


/*creat folder for operat pwn Conveniently*/
root_entry_pwm = proc_mkdir("rp_pwm", NULL);
proc_create("rp_pwm1", 0666 , root_entry_pwm , &pwm_ops);

return 0;
err:
//pwm_free(rp_pdata->pwm);
devm_kfree(&pdev->dev, rp_pdata);
return ret;
}

static int rp_pwm_remove(struct platform_device *pdev)
{
struct rp_pwm_data *rp_pdata = pdev->dev.platform_data;
pwm_free(rp_pdata->pwm);
return 0;
}

static const struct of_device_id rp_pwm_dt_ids[] = {
{ .compatible = "rp_pwm"},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rp_pwm_dt_ids);

static struct platform_driver rp_pwm_driver = {
.driver = {
.name = "rp_pwm",
.of_match_table = rp_pwm_dt_ids,
},
.probe = rp_pwm_probe,
.remove = rp_pwm_remove,
};

module_platform_driver(rp_pwm_driver);

MODULE_AUTHOR("rpdzkj Jsomeone");
MODULE_DESCRIPTION("rpdzkj pwm control");
MODULE_LICENSE("GPL v2");
(3-3/6)