/* * PWM driver for Rockchip SoCs */ //original include file #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include //creat proc directory and file #include #include //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");