/*
* drivers/pwm/pwm-sunxi-dev.c
*
* Allwinnertech pulse-width-modulation controller driver
*
* Copyright (C) 2018 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>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/time.h>
#include <linux/hrtimer.h>
static u32 debug_mask;
#define dprintk(level_mask, fmt, arg...) \
do { \
if (unlikely(debug_mask & level_mask)) \
pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg); \
} while (0)
#define IRTX_ERR(fmt, arg...) pr_err("%s()%d - "fmt, __func__, __LINE__, ##arg)
#define assert(expr) \
do { \
if (!(expr)) { \
printk(KERN_ERR "Assertion failed! %s, %s, %s, line %d\n", \
#expr, __FILE__, __func__, __LINE__); \
} \
} while (0)
#define INFRATYPE 'I'
#define INFRA_START _IOW(INFRATYPE, 0, int)
#define INFRA_READ_DATA _IOR(INFRATYPE, 1, int)
#define INFRA_WRITE_DATA _IOW(INFRATYPE, 2, int)
#define PS_SIZE 10
#define MAX_PLUSE 2048
#define IR_TX_EVENT_SIZE 4
#define IR_TX_BUFFER_SIZE 2048
#define PWM_CONFIG _IOW(PWM_IOCTL_BASE, 1 , struct pwm_config)
#define PWM_ENABLE _IOW(PWM_IOCTL_BASE, 2 , int)
#define PWM_DISABLE _IOW(PWM_IOCTL_BASE, 3 , int)
static struct pwm_device *pwm = NULL;
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;
}
struct timeval time1, time2;
static struct hrtimer hrtimer;
static int infra_w_complete_flag;
static struct mutex mutex;
static int data_count = 0;
static unsigned int times_w_state[MAX_PLUSE];
static unsigned int times_w_data[MAX_PLUSE];
static enum hrtimer_restart hrtimer_hander(struct hrtimer *timer)
{
do_gettimeofday(&time2);
printk(KERN_EMERG"hrtimer_hander\n");
double elapsed_time = (time2.tv_sec - time1.tv_sec) * 1000. +(time2.tv_usec - time1.tv_usec) / 1000.;
time1.tv_sec = time2.tv_sec;
time1.tv_usec = time2.tv_usec;
assert(timer);
if (0 == times_w_data[data_count]) {
infra_w_complete_flag = 1;
pwm_disable(pwm);
return HRTIMER_NORESTART;
}
if (times_w_state[data_count] == 'u') {
pwm_disable(pwm);
printk(KERN_EMERG"%d pwm close %d\n", data_count, times_w_data[data_count]);
} else {
pwm_config(pwm, 13158, 26316);
pwm_enable(pwm);
printk(KERN_EMERG"%d pwm run %d\n", data_count, times_w_data[data_count]);
}
hrtimer_forward_now(&hrtimer, ktime_set(0, times_w_data[data_count] * 1000));
data_count++;
return HRTIMER_RESTART;
}
static void timer_start(void)
{
int ret = 0;
printk(KERN_EMERG"timer_start\n");
infra_w_complete_flag = 0;
data_count = 0;
hrtimer_init(&hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ret = hrtimer_start(&hrtimer, ktime_set(0, 100000), HRTIMER_MODE_REL);
assert(ret >= 0);
hrtimer.function = hrtimer_hander;
do_gettimeofday(&time1);
}
static ssize_t irblaster_dev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
char data[MAX_PLUSE];
char tone[PS_SIZE];
int i = 0, j = 0, m = 0, ret = 0;
int val;
int status = 0;
printk(KERN_EMERG"%d\n",count);
if(copy_from_user(data, (void __user *)buf, count)){
printk(KERN_EMERG"copy err\n");
return 0;
}
printk(KERN_EMERG"copy ok\n");
mutex_init(&mutex);
mutex_lock(&mutex);
while (data[i] != '\0' && i < count) {
if (data[i] == 'u' || data[i] == 'U') {
tone[j] = '\0';
ret = kstrtoint(tone, 10, &val);
//ev->buffer[m] = val * 10;
times_w_data[m] = val;
times_w_state[m] = data[i];
printk(KERN_EMERG"data=%d,status=%d\n", val, data[i]);
j = 0;
i++;
m++;
if (m >= IR_TX_BUFFER_SIZE)
break;
continue;
}
tone[j] = buf[i];
i++;
j++;
if (j >= PS_SIZE) {
pr_err("send timing value is out of range,i=%d, j=%d, m=%d\n", i, j, m);
mutex_unlock(&mutex);
return -ENOMEM;
}
}
//ev->size = m;
status = count;
/*to send cycle to,*/
//kfifo_put(&rffifo, (const struct tx_event *)ev);
pr_info("m = %d\n", m);
for(i=0; i < m; i++) {
printk(KERN_EMERG"times_w_data[%d] = %d state=%c\n", i, times_w_data[i], times_w_state[i]);
}
timer_start();
while(!infra_w_complete_flag)
msleep(100);
hrtimer_cancel(&hrtimer);
mutex_unlock(&mutex);
return status;
}
static const struct file_operations sunxi_pwm_fops = {
.owner = THIS_MODULE,
.open = sunxi_pwm_open,
.write = irblaster_dev_write,
.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) {
IRTX_ERR("kzalloc failed!\n");
return -ENOMEM;
}
err = alloc_chrdev_region(&sunxi_pwm_dev->chrdev, 0, 1, "pwm-ir-tx");
if (err) {
IRTX_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) {
IRTX_ERR("cdev_add failed!\n");
goto cdev_add_err;
}
sunxi_pwm_class = class_create(THIS_MODULE, "pwm-ir-tx_class");
if (IS_ERR(sunxi_pwm_class)) {
err = PTR_ERR(sunxi_pwm_class);
IRTX_ERR("pwm-ir-tx_class failed!\n");
goto class_err;
}
dev = device_create(sunxi_pwm_class, NULL, sunxi_pwm_dev->chrdev, NULL,
"pwm-ir-tx%d", 16);
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
IRTX_ERR("device_create failed!\n");
goto device_err;
}
pwm = pwm_request(16, "spwm0");
if(IS_ERR(pwm)){
err = PTR_ERR(pwm);
IRTX_ERR("pwm request err\n");
goto pwm_err;
}else
IRTX_ERR("pwm request success\n");
return 0;
pwm_err:
pwm_free(pwm);
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)
{
pwm_free(pwm);
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_IR_TX");