/*************************************************************************
> File Name: pwm_new.c
> Author: wangermao
> Mail: wangermao@gmail.com
> Created Time: 2012年05月27日 星期天 23时10分59秒
************************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <plat/regs-timer.h>
#include <mach/regs-irq.h>
#include <asm/mach/time.h>
#include <linux/clk.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-e.h>
#include <mach/gpio-bank-f.h>
#include <mach/gpio-bank-k.h>
//#define PWM_MAGIC 1991
#ifndef PWM_MAGIC
#define PWM_IOCTL_STOP 0
#define PWM_IOCTL_SETFREQ 1
#else
#define PWM_IOCTL_STOP _IO(PWM_MAGIC, 0)
#define PWM_IOCTL_SETFREQ _(PWM_MAGIC, 1)
#endif
static pwm_new_major = 0;
static struct pwm_new_dev {
struct cdev cdev;
struct semaphore lock;
}pwm_new_dev;
static void pwm_set_freq(unsigned long arg)
{
unsigned int tmp;
struct clk *clk_p;
unsigned long pclk, tcnt;
tmp = ioread32(S3C64XX_GPFCON);
tmp &= ~(0x03U<<30);
tmp |= (0x02U<<30);
iowrite32(tmp, S3C64XX_GPFCON);
tmp = ioread32(S3C_TCFG0);
tmp &= ~S3C_TCFG_PRESCALER0_MASK;
tmp |= (50-1);
iowrite32(tmp, S3C_TCFG0);
tmp = ioread32(S3C_TCFG1);
tmp &= ~S3C_TCFG1_MUX0_MASK;
tmp |= S3C_TCFG1_MUX0_DIV16;
iowrite32(tmp, S3C_TCFG1);
clk_p = clk_get(NULL, "pclk");
pclk = clk_get_rate(clk_p);
tcnt = pclk/50/16/arg;
iowrite32(tcnt, S3C_TCNTB(0));
iowrite32(tcnt/2, S3C_TCMPB(0));
tmp = ioread32(S3C_TCON);
tmp &= ~0x1f;
tmp |= (0x0b);
iowrite32(tmp, S3C_TCON);
tmp &= ~2;
iowrite32(tmp, S3C_TCON);
}
static void pwm_stop()
{
unsigned int tmp;
tmp = ioread32(S3C64XX_GPFCON);
tmp &= ~(0x03U<<30);
iowrite32(tmp, S3C64XX_GPFCON);
}
static long pwm_new_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case PWM_IOCTL_STOP:
pwm_stop();
break;
case PWM_IOCTL_SETFREQ:
pwm_set_freq(arg);
break;
default:
break;
}
}
static int pwm_new_open(struct inode *inode, struct file *filp)
{
struct pwm_new_dev *dev;
dev = container_of(inode->i_cdev, struct pwm_new_dev, cdev);
if (down_trylock(&dev->lock))
return -EBUSY;
filp->private_data = dev;
return 0;
}
static int pwm_new_release(struct inode *inode, struct file *filp)
{
struct pwm_new_dev *dev;
dev = filp->private_data;
up(&dev->lock);
return 0;
}
static struct file_operations pwm_new_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = pwm_new_ioctl,
.open = pwm_new_open,
.release = pwm_new_release,
};
static void pwm_setup_cdev(struct pwm_new_dev *dev, int minor)
{
int err, devno = MKDEV(pwm_new_major, minor);
cdev_init(&dev->cdev, &pwm_new_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &pwm_new_fops;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE "error:%d adding cdev \n", err);
}
static int __init pwm_new_init(void)
{
int ret;
dev_t devno = MKDEV(pwm_new_major, 0);
if (devno)
register_chrdev_region(devno, 1, "pwm_new");
else {
ret = alloc_chrdev_region(&devno, 0, 1, "pwm_new");
pwm_new_major = MAJOR(devno);
}
if (ret < 0)
return ret;
sema_init(&pwm_new_dev.lock, 1);
pwm_setup_cdev(&pwm_new_dev, 0);
return 0;
}
static void __exit pwm_new_exit(void)
{
cdev_del(&pwm_new_dev.cdev);
unregister_chrdev_region(MKDEV(pwm_new_major, 0), 1);
}
MODULE_AUTHOR("WANGERMAO");
MODULE_LICENSE("GPL");
module_init(pwm_new_init);
module_exit(pwm_new_exit);