ckfwq/linux-3.0.4/drivers/misc/gsc3280_pwm.c

656 lines
15 KiB
C
Raw Normal View History

2024-12-30 10:53:50 +08:00
/*
* gsc3280_pwm.c - driver for pwm in blx gsc3280 soc
*
* Copyright (C) 2012 BLX Corporation
*
* 2013-04-08
*/
#include <linux/miscdevice.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/ioctl.h>
#include <linux/platform_device.h>
#include <linux/timer.h>
#include <linux/clk.h>
#include <linux/delay.h>
#define PWMODE 0x0
#define PWMCTRL 0x4
#define MCPWMCTRL 0x8
#define PWMCAPCTRL 0xc
#define PWMCNTCTRL 0x10
#define PWMIR 0x14
#define PWMIER 0x18
#define PWMTC0 0x1c
#define PWMTC1 0x20
#define PWMTC2 0x24
#define PWMLIM0 0x28
#define PWMLIM1 0x2C
#define PWMLIM2 0x30
#define PWMMR0_1 0x34
#define PWMMR0_2 0x38
#define PWMMR1_1 0x3c
#define PWMMR1_2 0x40
#define PWMMR2_1 0x44
#define PWMMR2_2 0x48
#define PWMCAP0 0x4c
#define PWMCAP1 0x50
#define PWMCAP2 0x54
#define PWMMCR 0x58
#define PWMDT 0x5c
#define PWMCPR 0x60
#define PWMLER 0x64
#define LOAD_11 11
#define LOAD_10 10
#define LOAD_9 9
#define LOAD_8 8
#define LOAD_7 7
#define LOAD_6 6
#define LOAD_5 5
#define LOAD_4 4
#define LOAD_3 3
#define LOAD_2 2
#define LOAD_1 1
#define LOAD_0 0
#define SINGLE 15
#define POLA5 14
#define POLA4 13
#define POLA3 12
#define POLA2 11
#define POLA1 10
#define POLA0 9
#define PWMSEL2 8
#define PWMSEL1 7
#define PWMSEL0 6
#define PWMEN5 5
#define PWMEN4 4
#define PWMEN3 3
#define PWMEN2 2
#define PWMEN1 1
#define PWMEN0 0
#define CMD_PWM0_START 0x01
#define CMD_PWM0_STOP 0x02
#define CMD_PWM0_R0 0x03
#define CMD_PWM0_R1 0x04
#define CMD_PWM0_LIM 0x05
#define CMD_PWM1_START 0x11
#define CMD_PWM1_STOP 0x12
#define CMD_PWM1_R0 0x13
#define CMD_PWM1_R1 0x14
#define CMD_PWM1_LIM 0x15
#define CMD_PWM2_START 0x21
#define CMD_PWM2_STOP 0x22
#define CMD_PWM2_R0 0x23
#define CMD_PWM2_R1 0x24
#define CMD_PWM2_LIM 0x25
#define CMD_SET_PWM_FREQ 0x31
#define CMD_GET_PWM_FREQ 0x32
#define CMD_SET_RENC 0x40
#define CMD_GET_RENC 0x41
#define DEBUG_PWM 0
static unsigned char __iomem *pwm_base = NULL;
struct resource *res;
static int gsc3280_pwm_probe(struct platform_device *pdev);
struct platform_device *pwm_dev;
static struct clk *pwm0_clk = NULL;
static struct clk *pwm1_clk = NULL;
static struct clk *pwm2_clk = NULL;
static struct clk *pwmr_clk = NULL;
static struct platform_driver gsc3280_pwm_driver = {
.probe = gsc3280_pwm_probe,
.driver = {
.name = "gsc3280-pwm",
},
};
static inline void gsc3280_pwm_writel(
unsigned int val, unsigned int off)
{
__raw_writel(val, pwm_base + off);
#if DEBUG_PWM
printk(KERN_ALERT"in write pwm_base=%lx",pwm_base);
printk(KERN_ALERT"in write val=%d",val);
printk(KERN_ALERT"in write off=%x",off);
#endif
#if DEBUG_PWM
volatile unsigned int *p = pwm_base+off;
volatile unsigned int *sysctl = 0xbc04a008;
volatile unsigned int *iomux = 0xbc04a0b0;
volatile unsigned int *pwm_r2=0xbc105044;
printk(KERN_ALERT"in write sysctl=%x\n",*sysctl);
printk(KERN_ALERT"in write iomux=%x\n",*iomux);
printk(KERN_ALERT"in write iomux=%x\n",*iomux);
printk(KERN_ALERT"in write pwm_r2=%x\n",*pwm_r2);
#endif
/*
*iomux=(*iomux & ~(1<<10));
*pwm_r2=11111;
*(volatile unsigned int*)0xbc105064 = 0xfff;
printk(KERN_ALERT"in write pwm_r2=%x\n",*pwm_r2);
*(volatile unsigned int*)0xbc105000 = 1;
printk(KERN_ALERT"0xbc105000 is 0x%x\n", *(volatile unsigned int*)0xbc105000);
*p=val;
printk(KERN_ALERT"in write data=%d",*p);
*p=10;
printk(KERN_ALERT"in write data=%d",*p);*/
}
static inline unsigned int gsc3280_pwm_readl(
unsigned int off)
{
return __raw_readl(pwm_base + off);
}
static void pwm_set_ctl(int value)
{
gsc3280_pwm_writel(value,PWMCTRL);
}
static void pwm_set_LIM(int channel, int value){
switch(channel){
case 0:
gsc3280_pwm_writel(value,PWMLIM0);
break;
case 1:
gsc3280_pwm_writel(value,PWMLIM1);
break;
case 2:
gsc3280_pwm_writel(value,PWMLIM2);
#if DEBUG_PWM
unsigned long *p;
p=pwm_base+PWMLIM2;
printk(KERN_ALERT"value==%d\n",value);
printk(KERN_ALERT"PWMLIM2==%ld\n",*p);
printk(KERN_ALERT"PWMLIM2_ADDR==%x\n",p);
#endif
break;
default:
printk(KERN_ERR"no channel %d\n", channel);
}
}
static void pwm_set_mr(int channel, int regno, int value)
{
switch(channel){
case 0:
if(regno == 0){
gsc3280_pwm_writel(value,PWMMR0_1);
}else if(regno == 1){
gsc3280_pwm_writel(value,PWMMR0_2);
}else{
printk(KERN_ERR"no reg %d\n", regno);
}
break;
case 1:
if(regno == 0){
gsc3280_pwm_writel(value,PWMMR1_1);
}else if(regno == 1){
gsc3280_pwm_writel(value,PWMMR1_2);
}else{
printk(KERN_ERR"no reg %d\n", regno);
}
break;
case 2:
if(regno == 0){
gsc3280_pwm_writel(value,PWMMR2_1);
}else if(regno == 1){
gsc3280_pwm_writel(value,PWMMR2_2);
}else{
printk(KERN_ERR"no reg %d\n", regno);
}
break;
default:
printk(KERN_ERR"no channel %d\n", channel);
}
}
static int init_clk_output(int channel, int edge, int valid)
{
int value = gsc3280_pwm_readl(PWMCTRL);
switch(channel){
case 0:
pwm_set_ctl( value|1<<PWMEN0|1<<PWMEN1|edge<<PWMSEL0|valid<<POLA0|valid<<POLA1);
break;
case 1:
pwm_set_ctl( value|1<<PWMEN2|1<<PWMEN3|edge<<PWMSEL1|valid<<POLA2|valid<<POLA3);
break;
case 2:
pwm_set_ctl( value|1<<PWMEN4|1<<PWMEN5|edge<<PWMSEL2);
break;
default:
printk(KERN_ERR"no %x channel \n", channel);
}
return 0;
}
static int fini_clk_output(int channel, int edge, int valid)
{
int value = gsc3280_pwm_readl(PWMCTRL);
switch(channel){
case 0:
pwm_set_ctl(value & (~(1<<PWMEN0|1<<PWMEN1)));
break;
case 1:
pwm_set_ctl(value & (~(1<<PWMEN2|1<<PWMEN3)));
break;
case 2:
pwm_set_ctl(value & (~( 1<<PWMEN4|1<<PWMEN5)));
break;
default:
printk(KERN_ERR"no %x channel \n", channel);
}
return 0;
}
#if 0 //for test
static void set_wave(int channel, int mr1, int mr2, int lim)
{
switch(channel){
case 0:
pwm_set_LIM(0,lim);
pwm_set_mr(0,0,mr1);
pwm_set_mr(0,1,mr2);
gsc3280_pwm_writel( 1<<LOAD_6|1<<LOAD_1|1<<LOAD_0, PWMLER);
break;
case 1:
pwm_set_LIM(1,lim);
pwm_set_mr(1,0,mr1);
pwm_set_mr(1,1,mr2);
gsc3280_pwm_writel( 1<<LOAD_7|1<<LOAD_2|1<<LOAD_3, PWMLER);
break;
case 2:
pwm_set_LIM(2,lim);
pwm_set_mr(2,0,mr1);
pwm_set_mr(2,1,mr2);
gsc3280_pwm_writel( 1<<LOAD_8|1<<LOAD_4|1<<LOAD_5, PWMLER);
break;
default:
printk(KERN_ERR"ctl is 0x%x\n", gsc3280_pwm_readl( PWMCTRL));
}
}
#endif
static int gsc3280_pwm_open(struct inode *inode, struct file *file)
{
int err;
pwm0_clk = clk_get(&pwm_dev->dev, "pwm0");
if (IS_ERR(pwm0_clk)) {
err = PTR_ERR(pwm0_clk);
return err;
}
clk_enable(pwm0_clk);
pwm1_clk = clk_get(&pwm_dev->dev, "pwm1");
if (IS_ERR(pwm1_clk)) {
err = PTR_ERR(pwm1_clk);
return err;
}
clk_enable(pwm1_clk);
pwm2_clk = clk_get(&pwm_dev->dev, "pwm2");
if (IS_ERR(pwm2_clk)) {
err = PTR_ERR(pwm2_clk);
return err;
}
clk_enable(pwm2_clk);
pwmr_clk = clk_get(&pwm_dev->dev, "pwmr");
if (IS_ERR(pwmr_clk)) {
err = PTR_ERR(pwmr_clk);
return err;
}
//clk_set_rate(pwmr_clk,5555555);
clk_set_rate(pwmr_clk,20000000);
clk_enable(pwmr_clk);
return 0;
}
static int gsc3280_pwm_close(struct inode *inode, struct file *file)
{
clk_disable(pwm0_clk);
clk_disable(pwm1_clk);
clk_disable(pwm2_clk);
clk_disable(pwmr_clk);
clk_put(pwm0_clk);
clk_put(pwm1_clk);
clk_put(pwm2_clk);
clk_put(pwmr_clk);
return 0;
}
static ssize_t gsc3280_pwm_read(struct file *file, char __user *buf, size_t count, loff_t *ptr)
{
return 0;
}
static ssize_t gsc3280_pwm_write(struct file *file, const char __user *buf, size_t count, loff_t *ptr)
{
return 0;
}
static int pwm_start(int channel)
{
init_clk_output(channel,0,1);
switch(channel){
case 0:
gsc3280_pwm_writel( 1<<LOAD_6|1<<LOAD_1|1<<LOAD_0, PWMLER);
break;
case 1:
gsc3280_pwm_writel( 1<<LOAD_7|1<<LOAD_2|1<<LOAD_3, PWMLER);
break;
case 2:
gsc3280_pwm_writel( 1<<LOAD_8|1<<LOAD_4, PWMLER);
break;
default:
printk(KERN_ERR"ctl is 0x%x\n", gsc3280_pwm_readl( PWMCTRL));
}
return 0;
}
static int pwm_stop(int channel)
{
fini_clk_output(channel,0,0);
return 0;
}
static long gsc3280_pwm_ioctl( struct file *file, unsigned int cmd, unsigned long arg)
{
void __user *p = (void __user *)arg;
int renc_lim = 0;
#if DEBUG_PWM
printk(KERN_INFO"in intol before switch\n");
#endif
switch (cmd) {
case CMD_PWM0_START:
pwm_start(0);
break;
case CMD_PWM0_STOP:
pwm_stop(0);
break;
case CMD_PWM0_R0:
pwm_set_mr(0,0,arg);
break;
case CMD_PWM0_R1:
pwm_set_mr(0,1,arg);
break;
case CMD_PWM0_LIM:
pwm_set_LIM(0,arg);
break;
case CMD_PWM1_START:
pwm_start(1);
break;
case CMD_PWM1_STOP:
pwm_stop(1);
break;
case CMD_PWM1_R0:
pwm_set_mr(1,0,arg);
break;
case CMD_PWM1_R1:
pwm_set_mr(1,1,arg);
break;
case CMD_PWM1_LIM:
pwm_set_LIM(1,arg);
break;
case CMD_PWM2_START:
pwm_start(2);
#if DEBUG_PWM
printk(KERN_ALERT"0xbc105000 MODE=%x\n",*(volatile unsigned int*)0xbc105000);
printk(KERN_ALERT"0xbc105008 MCCTRL=%x\n",*(volatile unsigned int*)0xbc105008);
printk(KERN_ALERT"0xbc10500c CAPCTRL=%x\n",*(volatile unsigned int*)0xbc10500c);
printk(KERN_ALERT"0xbc105010 CNTCTRL=%x\n",*(volatile unsigned int*)0xbc105010);
printk(KERN_ALERT"0xbc105014 IR=%x\n",*(volatile unsigned int*)0xbc105014);
*(volatile unsigned int *)0xbc105014 = 0x1000;
//p = 0x1000;
printk(KERN_ALERT"0xbc105014 IR=%x\n",*(volatile unsigned int*)0xbc105014);
printk(KERN_ALERT"0xbc10505c DT=%x\n",*(volatile unsigned int*)0xbc10505c);
printk(KERN_ALERT"0xbc105068 RE_EN=%x\n",*(volatile unsigned int*)0xbc105068);
printk(KERN_ALERT"0xbc04a008 SYSCTL0=%x\n",*(volatile unsigned int*)0xbc04a008);
printk(KERN_ALERT"0xbc04a00c SYSCTL1=%x\n",*(volatile unsigned int*)0xbc04a00c);
printk(KERN_ALERT"0xbc04a0b0 IOMUX0=%x\n",*(volatile unsigned int*)0xbc04a0b0);
printk(KERN_ALERT"0xbc04a0b4 IOMUX1=%x\n",*(volatile unsigned int*)0xbc04a0b4);
printk(KERN_ALERT"0xbc105024 TC2=%x\n",*(volatile unsigned int*)0xbc105024);
printk(KERN_ALERT"0xbc105044 MR2_1=%x\n",*(volatile unsigned int*)0xbc105044);
printk(KERN_ALERT"0xbc105030 LIM=%x\n",*(volatile unsigned int*)0xbc105030);
printk(KERN_ALERT"0xbc105004 CTRL=%x\n",*(volatile unsigned int*)0xbc105004);
printk(KERN_ALERT"0xbc105064 LER=%x\n",*(volatile unsigned int*)0xbc105064);
printk(KERN_ALERT"0xbc105018 IER=%x\n",*(volatile unsigned int*)0xbc105018);
printk(KERN_ALERT"0xbc105000 MODE=%x\n",*(volatile unsigned int*)0xbc105000);
printk(KERN_ALERT"0xbc105010 CNTCTRL=%x\n",*(volatile unsigned int*)0xbc105010);
printk(KERN_ALERT"0xbc105058 MCR=%x\n",*(volatile unsigned int*)0xbc105058);
printk(KERN_ALERT"0xbc105024 TC2=%x\n",*(volatile unsigned int*)0xbc105024);
#endif
break;
case CMD_PWM2_STOP:
pwm_stop(2);
break;
case CMD_PWM2_R0:
#if DEBUG_PWM
printk(KERN_INFO"in CMD_PWM2_R0,value==%d\n",arg);
#endif
pwm_set_mr(2,0,arg);
break;
case CMD_PWM2_R1:
pwm_set_mr(2,1,arg);
break;
case CMD_PWM2_LIM:
#if DEBUG_PWM
printk(KERN_INFO"in CMD_PWM2_LIM,value==%d\n",arg);
#endif
pwm_set_LIM(2,arg);
break;
case CMD_SET_PWM_FREQ:
{
unsigned long rate;
copy_from_user(&rate, p, sizeof(rate));
if(pwm0_clk)
clk_set_rate(pwm0_clk, rate);
}
break;
case CMD_GET_PWM_FREQ:
{
unsigned long rate;
#if DEBUG_PWM
struct clk *pll_clk = (struct clk *)NULL;
struct clk *pclk_clk = NULL;
unsigned long pll_rate;
unsigned long pclk_rate;
int err;
pll_clk = clk_get(NULL, "pll");
if (IS_ERR(pll_clk)) {
err = PTR_ERR(pll_clk);
return err;
}
pll_rate = clk_get_rate(pll_clk);
printk(KERN_ALERT"pll rate is %ld\n", pll_rate);
pclk_clk = clk_get(NULL, "pclk");
if (IS_ERR(pclk_clk)) {
err = PTR_ERR(pclk_clk);
return err;
}
pclk_rate = clk_get_rate(pclk_clk);
printk(KERN_ALERT"pclk_rate is %ld\n", pclk_rate);
#endif
if(pwm0_clk)
{
rate = clk_get_rate(pwm0_clk);
#if DEBUG_PWM
printk(KERN_ALERT"pwm_rate=%ld\n",rate);
#endif
copy_to_user(p, &rate, sizeof(rate));
}
else
{
printk(KERN_ALERT"pwm_rate=NULL\n");
}
}
break;
case CMD_SET_RENC:
{
//copy_from_user(&renc_lim, p, sizeof(renc_lim));
renc_lim = p;
printk(KERN_ALERT"renc_lim is %d, p is %x\n",renc_lim, p);
break;
}
case CMD_GET_RENC:
{
#define RENC_EN 0x68
#define RENC_LIM 0x6c
#define RENC_M1 0x70
#define RENC_M2 0x74
#define RENC_M3 0x78
#define RENC_TC1 0x7C
#define RENC_TC2 0x80
#define RENC_TC3 0x84
#define RENC_STATE 0x88
struct rencs{
int direct;
int tc3;
int m1;
int m2;
int m3;
int fclk;
}renc;
gsc3280_pwm_writel(1,RENC_EN);
if(renc_lim)
gsc3280_pwm_writel(renc_lim, RENC_LIM);
else
gsc3280_pwm_writel(0xff00, RENC_LIM);
//gsc3280_pwm_writel(rarg[0], RENC_LIM);
while(!(gsc3280_pwm_readl(RENC_STATE) & 0x4)){
printk(KERN_ALERT"LIM is 0x%x\n",gsc3280_pwm_readl(RENC_LIM));
printk(KERN_ALERT"EN is 0x%x\n",gsc3280_pwm_readl(RENC_EN));
}
//udelay(200);
renc.direct =gsc3280_pwm_readl(RENC_STATE) & 0x3;
renc.tc3 = gsc3280_pwm_readl(RENC_TC3) & 0xffff;
renc.m1 = gsc3280_pwm_readl(RENC_M1) & 0xffff;
renc.m2 = gsc3280_pwm_readl(RENC_M2) & 0xffff;
renc.m3 = gsc3280_pwm_readl(RENC_M3) & 0xffff;
renc.fclk = clk_get_rate(pwmr_clk);
//renc[0] = 1; renc[1] = 2;
//copy_to_user(p, renc, sizeof(renc));
//printk(KERN_ALERT"before copy to user \n");
copy_to_user(p, &renc, sizeof(renc));
}
default:
break;
}
return 0;
}
static const struct file_operations gsc3280_pwm_ops = {
.owner = THIS_MODULE,
.open = gsc3280_pwm_open,
.release = gsc3280_pwm_close,
.read = gsc3280_pwm_read,
.write = gsc3280_pwm_write,
.unlocked_ioctl = gsc3280_pwm_ioctl
};
static struct miscdevice gsc3280_pwm_miscdev = {
MISC_DYNAMIC_MINOR,
"pwm",
&gsc3280_pwm_ops,
};
static int gsc3280_pwm_getResourse(struct platform_device *pdev, unsigned int index)
{
struct resource *res1 = NULL;
res = platform_get_resource(pdev, IORESOURCE_MEM, index);
if (res == NULL)
{
printk(KERN_ERR"Fail to get gsc3280_pwm_resource!\n");
return -ENOENT;
}
//printk("Resource start=0x%x, end = 0x%x\n", res->start, res->end);
if (res1 != NULL)
{
release_mem_region(res->start, 0x0f);
}
res1 = request_mem_region(res->start, resource_size(res), "gsc3280-pwm");
if (res1 == NULL)
{
printk(KERN_ERR"Fail to request gsc3280_pwm region!\n");
return -ENOENT;
}
pwm_base = ioremap(res->start, res->end - res->start + 1);
if (pwm_base == NULL)
{
printk(KERN_ERR"Fail to ioremap gsc3280_pwm resource!\n");
return -EINVAL;
}
return 0;
}
static int __devinit gsc3280_pwm_probe(struct platform_device *pdev)
{
pwm_dev = pdev;
return gsc3280_pwm_getResourse(pdev, 0);
}
static int __init gsc3280_pwm_init(void) {
if (misc_register(&gsc3280_pwm_miscdev))
{
printk(KERN_WARNING "pwm: Couldn't register device 0, %d.\n", 255);
return -EBUSY;
}
return platform_driver_register(&gsc3280_pwm_driver);
}
static void __exit gsc3280_pwm_exit(void)
{
misc_deregister(&gsc3280_pwm_miscdev);
release_mem_region(res->start, 0x20);
platform_driver_unregister(&gsc3280_pwm_driver);
}
module_init(gsc3280_pwm_init);
module_exit(gsc3280_pwm_exit);
MODULE_LICENSE("GPL");