mirror of https://github.com/OpenIPC/firmware.git
				
				
				
			
		
			
				
	
	
		
			256 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Diff
		
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Diff
		
	
	
| diff -drupN a/drivers/pwm/pwm-sunxi-g_dev.c b/drivers/pwm/pwm-sunxi-g_dev.c
 | ||
| --- a/drivers/pwm/pwm-sunxi-g_dev.c	1970-01-01 03:00:00.000000000 +0300
 | ||
| +++ b/drivers/pwm/pwm-sunxi-g_dev.c	2022-06-12 05:28:14.000000000 +0300
 | ||
| @@ -0,0 +1,251 @@
 | ||
| +/*
 | ||
| + * drivers/pwm/pwm-sunxi-dev.c
 | ||
| + *
 | ||
| + * Allwinnertech pulse-width-modulation controller driver
 | ||
| + *
 | ||
| + * Copyright (C) 2019 AllWinner
 | ||
| + *
 | ||
| + *
 | ||
| + * This file is licensed under the terms of the GNU General Public
 | ||
| + * License version 2. This program is licensed "as is" without any
 | ||
| + * warranty of any kind, whether express or implied.
 | ||
| + */
 | ||
| +#include <linux/module.h>
 | ||
| +#include <linux/init.h>
 | ||
| +#include <linux/kernel.h>
 | ||
| +#include <linux/fs.h>
 | ||
| +#include <linux/cdev.h>
 | ||
| +#include <linux/uaccess.h>
 | ||
| +#include <linux/slab.h>
 | ||
| +#include <linux/clk.h>
 | ||
| +#include <linux/gpio.h>
 | ||
| +#include <linux/device.h>
 | ||
| +#include <linux/pinctrl/consumer.h>
 | ||
| +#include <asm/io.h>
 | ||
| +#include <linux/pwm.h>
 | ||
| +
 | ||
| +#define PWM_ERR(fmt, arg...) pr_err("%s()%d - "fmt, __func__, __LINE__, ##arg)
 | ||
| +
 | ||
| +#define PWM_IOCTL_BASE 'P'
 | ||
| +#define GROUP_PWM_CONFIG	_IOW(PWM_IOCTL_BASE, 4, struct pwm_config_group)
 | ||
| +#define GROUP_PWM_DISABLE   _IOW(PWM_IOCTL_BASE, 5, struct pwm_config_group)
 | ||
| +
 | ||
| +struct pwm_config_group {
 | ||
| +	int group_channel;
 | ||
| +	int group_run_count;
 | ||
| +	int pwm_polarity;
 | ||
| +	int pwm_period;
 | ||
| +};
 | ||
| +
 | ||
| +struct sunxi_pwm_dev {
 | ||
| +	struct device *dev;
 | ||
| +	struct cdev cdev;
 | ||
| +	dev_t chrdev;
 | ||
| +};
 | ||
| +
 | ||
| +static struct sunxi_pwm_dev    *sunxi_pwm_dev;
 | ||
| +static struct class            *sunxi_pwm_class;
 | ||
| +
 | ||
| +static int sunxi_pwm_open(struct inode *inode, struct file *filp)
 | ||
| +{
 | ||
| +	filp->private_data = sunxi_pwm_dev;
 | ||
| +	return 0;
 | ||
| +}
 | ||
| +
 | ||
| +static int sunxi_pwm_release(struct inode *inode, struct file *filp)
 | ||
| +{
 | ||
| +	return 0;
 | ||
| +}
 | ||
| +
 | ||
| +static long  sunxi_pwm_unlocked_ioctl(struct file *filp, unsigned int cmd,
 | ||
| +		unsigned long arg)
 | ||
| +{
 | ||
| +	unsigned int size;
 | ||
| +	struct pwm_config_group *code_group;
 | ||
| +	unsigned int ret, i, group;
 | ||
| +	unsigned char name[30];
 | ||
| +
 | ||
| +	static struct pwm_device *pwm[8] = {NULL};
 | ||
| +	switch (cmd) {
 | ||
| +	case GROUP_PWM_CONFIG:
 | ||
| +		size = _IOC_SIZE(cmd);
 | ||
| +
 | ||
| +		code_group = (struct pwm_config_group *)kzalloc(size, GFP_KERNEL);
 | ||
| +		if (IS_ERR_OR_NULL(code_group)) {
 | ||
| +			PWM_ERR("not enough memory\n");
 | ||
| +			return -ENOMEM;
 | ||
| +		}
 | ||
| +
 | ||
| +		if (copy_from_user(code_group, (void __user *)arg, size)) {
 | ||
| +			PWM_ERR("copy buffer err\n");
 | ||
| +			return -ENOMEM;
 | ||
| +		}
 | ||
| +
 | ||
| +		group = code_group->group_channel;
 | ||
| +
 | ||
| +		if (group < 1) {
 | ||
| +			return -EINVAL;
 | ||
| +		}
 | ||
| +
 | ||
| +		for (i = 4*(group-1); i < 4*group; i++) {
 | ||
| +			sprintf(name, "sunxi_pwm%d", i);
 | ||
| +			if (pwm[i] == NULL) {
 | ||
| +				pwm[i] = pwm_request(i, name);
 | ||
| +
 | ||
| +				if (IS_ERR_OR_NULL(pwm[i])) {
 | ||
| +					PWM_ERR("pwm err\n");
 | ||
| +					return -ENODEV;
 | ||
| +				}
 | ||
| +			}
 | ||
| +
 | ||
| +			pwm[i]->chip_data = code_group;
 | ||
| +
 | ||
| +//			pwm_disable(pwm[i]);  /* first disabled then enable */
 | ||
| +
 | ||
| +			ret = pwm_config(pwm[i], 0, 1); /* the argument can’t be same as the first */
 | ||
| +			if (ret < 0) {
 | ||
| +				PWM_ERR("pwm ioctl err0\n");
 | ||
| +				return -EINVAL;
 | ||
| +			}
 | ||
| +			ret = pwm_config(pwm[i], 0x2ee, 0x7cf);
 | ||
| +			if (ret < 0) {
 | ||
| +				PWM_ERR("pwm ioctl err\n");
 | ||
| +				return -EINVAL;
 | ||
| +			}
 | ||
| +
 | ||
| +			pwm_enable(pwm[i]);
 | ||
| +			/*pwm_free(pwm);*/
 | ||
| +		}
 | ||
| +
 | ||
| +		kfree(code_group);
 | ||
| +		break;
 | ||
| +	case GROUP_PWM_DISABLE:
 | ||
| +		size = _IOC_SIZE(cmd);
 | ||
| +
 | ||
| +		code_group = (struct pwm_config_group *)kzalloc(size, GFP_KERNEL);
 | ||
| +		if (IS_ERR_OR_NULL(code_group)) {
 | ||
| +			PWM_ERR("not enough memory\n");
 | ||
| +			return -ENOMEM;
 | ||
| +		}
 | ||
| +
 | ||
| +		if (copy_from_user(code_group, (void __user *)arg, size)) {
 | ||
| +			PWM_ERR("copy buffer err\n");
 | ||
| +			return -ENOMEM;
 | ||
| +		}
 | ||
| +
 | ||
| +		group = code_group->group_channel;
 | ||
| +
 | ||
| +		if (group < 1) {
 | ||
| +			PWM_ERR("group para err\n");
 | ||
| +			return -EINVAL;
 | ||
| +		}
 | ||
| +
 | ||
| +		for (i = 4*(group-1); i < 4*group; i++) {
 | ||
| +			pwm[i]->chip_data = code_group;
 | ||
| +
 | ||
| +			if (pwm[i]) {
 | ||
| +				pwm_disable(pwm[i]);
 | ||
| +				pwm_free(pwm[i]);
 | ||
| +				pwm[i] = NULL;
 | ||
| +			}
 | ||
| +		}
 | ||
| +
 | ||
| +
 | ||
| +		kfree(code_group);
 | ||
| +		break;
 | ||
| +	default:
 | ||
| +		PWM_ERR("a err cmd");
 | ||
| +		return -ENOTTY;
 | ||
| +	}
 | ||
| +	return 0;
 | ||
| +}
 | ||
| +
 | ||
| +#ifdef CONFIG_COMPAT
 | ||
| +static long  sunxi_pwm_compat_ioctl(struct file *filp, unsigned int cmd,
 | ||
| +		unsigned long arg)
 | ||
| +{
 | ||
| +	unsigned long translated_arg = (unsigned long)compat_ptr(arg);
 | ||
| +
 | ||
| +	return sunxi_pwm_unlocked_ioctl(filp, cmd, translated_arg);
 | ||
| +}
 | ||
| +#endif
 | ||
| +
 | ||
| +static const struct file_operations sunxi_pwm_fops = {
 | ||
| +	.owner		= THIS_MODULE,
 | ||
| +	.unlocked_ioctl	= sunxi_pwm_unlocked_ioctl,
 | ||
| +#ifdef CONFIG_COMPAT
 | ||
| +	.compat_ioctl   = sunxi_pwm_compat_ioctl,
 | ||
| +#endif
 | ||
| +	.open		= sunxi_pwm_open,
 | ||
| +	.release	= sunxi_pwm_release,
 | ||
| +};
 | ||
| +
 | ||
| +static int __init sunxi_pwm_init(void)
 | ||
| +{
 | ||
| +	int err = 0;
 | ||
| +	struct device *dev;
 | ||
| +
 | ||
| +	sunxi_pwm_dev = kzalloc(sizeof(struct sunxi_pwm_dev), GFP_KERNEL);
 | ||
| +	if (sunxi_pwm_dev == NULL) {
 | ||
| +		PWM_ERR("kzalloc failed!\n");
 | ||
| +		return -ENOMEM;
 | ||
| +	}
 | ||
| +
 | ||
| +	err = alloc_chrdev_region(&sunxi_pwm_dev->chrdev, 0, 1, "sunxi-pwm-dev");
 | ||
| +
 | ||
| +	if (err) {
 | ||
| +		PWM_ERR("alloc_chrdev_region failed!\n");
 | ||
| +		goto alloc_chrdev_err;
 | ||
| +	}
 | ||
| +
 | ||
| +	cdev_init(&(sunxi_pwm_dev->cdev), &sunxi_pwm_fops);
 | ||
| +	sunxi_pwm_dev->cdev.owner = THIS_MODULE;
 | ||
| +	err = cdev_add(&(sunxi_pwm_dev->cdev), sunxi_pwm_dev->chrdev, 1);
 | ||
| +	if (err) {
 | ||
| +		PWM_ERR("cdev_add failed!\n");
 | ||
| +		goto cdev_add_err;
 | ||
| +	}
 | ||
| +
 | ||
| +	sunxi_pwm_class = class_create(THIS_MODULE, "sunxi_pwm_char_class");
 | ||
| +	if (IS_ERR(sunxi_pwm_class)) {
 | ||
| +		err = PTR_ERR(sunxi_pwm_class);
 | ||
| +		PWM_ERR("class_create failed!\n");
 | ||
| +		goto class_err;
 | ||
| +	}
 | ||
| +
 | ||
| +	dev = device_create(sunxi_pwm_class, NULL, sunxi_pwm_dev->chrdev, NULL,
 | ||
| +			"sunxi_pwm%d", 0);
 | ||
| +	if (IS_ERR(dev)) {
 | ||
| +		err = PTR_ERR(dev);
 | ||
| +		PWM_ERR("device_create failed!\n");
 | ||
| +		goto device_err;
 | ||
| +	}
 | ||
| +
 | ||
| +	return 0;
 | ||
| +
 | ||
| +device_err:
 | ||
| +	device_destroy(sunxi_pwm_class, sunxi_pwm_dev->chrdev);
 | ||
| +class_err:
 | ||
| +	cdev_del(&(sunxi_pwm_dev->cdev));
 | ||
| +cdev_add_err:
 | ||
| +	unregister_chrdev_region(sunxi_pwm_dev->chrdev, 1);
 | ||
| +alloc_chrdev_err:
 | ||
| +	kfree(sunxi_pwm_dev);
 | ||
| +
 | ||
| +	return err;
 | ||
| +}
 | ||
| +
 | ||
| +static void __exit sunxi_pwm_exit(void)
 | ||
| +{
 | ||
| +	cdev_del(&(sunxi_pwm_dev->cdev));
 | ||
| +	unregister_chrdev_region(sunxi_pwm_dev->chrdev, 1);
 | ||
| +	device_destroy(sunxi_pwm_class, sunxi_pwm_dev->chrdev);
 | ||
| +	class_destroy(sunxi_pwm_class);
 | ||
| +	kfree(sunxi_pwm_dev);
 | ||
| +}
 | ||
| +
 | ||
| +module_init(sunxi_pwm_init);
 | ||
| +module_exit(sunxi_pwm_exit);
 | ||
| +MODULE_AUTHOR("Li huaxing");
 | ||
| +MODULE_LICENSE("GPL");
 | ||
| +MODULE_DESCRIPTION("SUNXI_PWM char");
 |