📄 pwm.c
字号:
#ifndef __KERNEL__# define __KERNEL__#endif#ifndef MODULE# define MODULE#endif#define THOMAS_NO_SLEEP#if 1#include <linux/module.h>#include <linux/version.h>#endif#include <linux/init.h>#include <linux/fs.h>#include <linux/delay.h>#include <linux/poll.h>#include <asm/uaccess.h> /* get_user,copy_to_user, copy_from_user */#include <linux/string.h>#include <linux/kernel.h>#include <linux/timer.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/in.h>//#include <linux/malloc.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/string.h>#include <linux/init.h>#include <asm/bitops.h>#include <asm/io.h>#include <linux/errno.h>#include <linux/tqueue.h>#include <linux/wait.h>#include <linux/pm.h> #include <asm/irq.h>#include <asm/arch/hardware.h>#include <asm/arch/irqs.h>#include "pwm.h"#define PWM_TEST#undef ZHIMING_VERSION// variablesint irq = 0;int gWriteOk;int gPwmMode;char *gpWriteBuf;U8 *gpWriteBufPtr8; //defined U8 as unsigned charU16 *gpWriteBufPtr16;int gWriteCnt;int gDataLen;U16 gSampleValue;MODULE_PARM(irq, "i");wait_queue_head_t ts_wait;struct fasync_struct *ts_fasync;int gMajor = 0; /* TODO dynamic major for now */struct file_operations ts_fops = { open: pwm_open, release: pwm_release, read: pwm_read, write: pwm_write, ioctl: pwm_ioctl, fasync: pwm_fasync };// -----------------------// FUNCTION ROUTINES// -----------------------//read register value for input addressU32 _register_read (U32 * addr){ return ( * ((U32 *) addr) );}// write register value into a specified addressvoid _register_write (U32 * addr, U32 value){ *((U32 *) addr) = (U32) value; }//YJ modifies the above function:/*void inline _register_write(U32 *addr, U32 value) { *((U32 volatile *) addr) = (U32) value; }*/// set a specified register bit value for a input addressvoid _set_register_bit (U32 * addr, U32 Nbit){ U32 temp; temp = (U32) * ((U32 *) addr); temp = temp | (0x00000001 << Nbit); _register_write ( (U32*) addr, (U32) temp);}// clear a specified register bit value for a input addressvoid _clear_register_bit (U32 * addr, U32 Nbit){ U32 temp; temp = * (U32 *) addr; temp = temp & (~((~temp) | (0x00000001 << Nbit))); _register_write ( (U32*) addr, (U32) temp);}//delay for a whilevoid delay (void){ U32 i=0; for (i=0; i<10000; i++);}//software resetvoid soft_reset (void){int i; //enable the PWM _set_register_bit ((U32*) PWMC1, 4); // set SWR bit to 1 _set_register_bit ((U32*) PWMC1, 16); //disable the PWM _clear_register_bit ((U32*) PWMC1, 4); //delay because PWM released after 5 system clock for(i=0;i<5;i++) delay ();}/****************************************************************************** * Function Name: pwm_release * * Input: inode : * filp : * Value Returned: int : Return status.If no error, return 0. * * Description: release resource when close the inode * * Modification History: * 10 DEC,2001, Chen Ning' *****************************************************************************/int pwm_release(struct inode * inode, struct file * filp){ PK("<1>in pwm_release ----\n"); // wait unit gWriteCnt == 0 interruptible_sleep_on(&ts_wait); MOD_DEC_USE_COUNT; return 0;}/****************************************************************************** * Function Name: pwm_open * * Input: inode : * filp : * Value Returned: int : Return status.If no error, return 0. * * Description: allocate resource when open the inode * * Modification History: * 10 DEC,2001, Chen Ning *****************************************************************************/int pwm_open(struct inode * inode, struct file * filp){ PK("<1>in pwm_open ----\n"); MOD_INC_USE_COUNT; // Init PWM hardware initpwm(); gWriteOk = 0; return 0;}/****************************************************************************** * Function Name: pwm_fasync * * Input: fd : * filp : * mode : * Value Returned: int : Return status.If no error, return 0. * * Description: provide fasync functionality for select system call * * Modification History: *****************************************************************************/static int pwm_fasync(int fd, struct file *filp, int mode){ PK("<1>in pwm_fasyn ----\n");#if 0 /* TODO TODO put this data into file private data */ int minor = checkDevice( filp->f_dentry->d_inode); if ( minor == - 1) { PK("<1>asp_fasyn:bad device minor\n"); return -ENODEV; } return( fasync_helper(fd, filp, mode, &ts_fasync) );#endif return 0;}/****************************************************************************** * Function Name: pwm_ioctl * * Input: inode : * filp : * cmd : command for ioctl * arg : parameter for command * Value Returned: int : Return status.If no error, return 0. * * Description: ioctl for this device driver * * Modification History: * 10 DEC,2001, Chen Ning *****************************************************************************/int pwm_ioctl(struct inode * inode, struct file *filp, unsigned int cmd , unsigned long arg){ char *str=NULL; int ret = 0; PK("<1>in pwm_ioctl ----\n"); switch(cmd) { case PWM_IOC_SMODE: gPwmMode = arg; if (gPwmMode == PWM_TONE_MODE) { // create periodic timer when tone mode PK("PWM Tone Mode.\n"); pwmTimer.timer_blk_ptr = &timer_blk; pwmTimer.timer_func = pwmIntFunc; CreateTimer(&pwmTimer); } else { PK("PWM Play Mode.\n"); } break; case PWM_IOC_SFREQ: _clear_register_bit ((U32*)PWMC1, 4); // Disable PWM _reg(PWMC1) &= 0xFFFF00F0; // sysclk // check sampling rate if (arg == PWM_SAMPLING_8KHZ) { // 96M / 23/2/256 = 8.152kHz _reg(PWMC1) |= (0 << 8); // pres = 22 (/23) str = "8Khz"; } else if (arg == PWM_SAMPLING_16KHZ) { _reg(PWMC1) |= (0 << 8); // pres = 11 (/12) str = "16Khz"; } _reg(PWMC1) &= ~0x3; // clksel = 0 (/2) PK("<2>Sample rate = %s\n", str); PK("<1>ioctl: PWMC = 0x%8x\n", (U32)_reg(PWMC1)); break; case PWM_IOC_SDATALEN: gDataLen = arg; if (arg == PWM_DATA_8BIT) { _reg(PWMP1) = 0xfe; // Period, 8bit str = "8bit"; } else // if(arg == PWM_DATA_16BIT) { _reg(PWMP1) = 0xfffe; // Period, 16bit str = "16bit"; } PK("<2>Data Length = %s\n", str); break; case PWM_IOC_SSAMPLE: gSampleValue = arg; break; case PWM_IOC_SPERIOD: if (gPwmMode == PWM_TONE_MODE) { PK("<1>PWM period = %d\n",(int)arg); pwmTimer.period = arg/12; } break; case PWM_IOC_STOP: StopPwm(); break;/*Jacky add--001begin*/ case PWM_IOC_SWAPDATA: if (arg&PWM_SWAP_HCTRL) //Halfword FIFO data swapping { _reg(PWMC1) |= PWM_SWAP_HCTRL; } else { _reg(PWMC1) &= ~PWM_SWAP_HCTRL; } if (arg&PWM_SWAP_BCTRL) //Byte FIFO data swapping { _reg(PWMC1) |= PWM_SWAP_BCTRL; } else { _reg(PWMC1) &= ~PWM_SWAP_BCTRL; } break;/*Jacky add--001end*/ default: break; } return ret;}/****************************************************************************** * Function Name: pwm_read * * Input: filp : the file * buf : data buffer * count : number of chars to be readed * l : offset of file * Value Returned: int : Return status.If no error, return 0. * * Description: read device driver * * Modification History: * 10 DEC,2001, Chen Ning *****************************************************************************/ssize_t pwm_read(struct file * filp, char * buf, size_t count, loff_t * l){ int ret; PK("<1>in pwm_read ----\n"); if(gWriteCnt == 0) ret = 1; // no data else ret = 0; // processing data return ret;}/****************************************************************************** * Function Name: pwm_interrupt * * * Description: interrupt handler * * Modification History: * 10 DEC,2001, Chen Ning *****************************************************************************/void pwm_interrupt(){U16 tmp16;U32 status; // clear status status = _reg(PWMC1); // check end if(gWriteCnt < 0) { StopPwm(); return; } if (gPwmMode == PWM_PLAY_MODE) { if(gDataLen == PWM_DATA_8BIT) { _reg(PWMS1) = (U32)(*gpWriteBufPtr8++); PK("<1>(%d) pwm_int: PWMS = 0x%8x\n", gWriteCnt, (U32)_reg(PWMS1)); gWriteCnt--; } else if(gDataLen == PWM_DATA_16BIT) { tmp16 = *gpWriteBufPtr8++; tmp16 |= (*gpWriteBufPtr8++ << 8); _reg(PWMS1) = (U32)tmp16; PK("<1>(%d) pwm_int: PWMS = 0x%8x\n", gWriteCnt, (U32)_reg(PWMS1)); gWriteCnt -= 2; } }}static ssize_t pwm_write(struct file *filp,const char *buf,size_t count,loff_t *f_pos){ int ret = 0; PK("<1>in pwm_write ----\n"); gWriteOk = 0; gpWriteBuf = kmalloc(count, GFP_KERNEL); if (!gpWriteBuf) goto out; gpWriteBufPtr8 = gpWriteBuf; if (copy_from_user(gpWriteBuf, buf, count)) { ret = -EFAULT; goto out; } //write data to PWM gWriteCnt = count; PK("<1>pwm_write: count = %d\n", gWriteCnt); //enable IRQEN, PW _reg(PWMC1) |= 0x00000010; if(gPwmMode == PWM_PLAY_MODE) { _reg(PWMC1) |= 0x00000040; } else { _reg(PWMS1) = gSampleValue; _reg(PWMC1) |= 0x7f00; // set prescaler to 128 gpWriteBufPtr16 = (U16*)gpWriteBuf; gWriteCnt /= 2; //input size is 8bit size, need /2 to get 16bit size StartTimer(&pwmTimer); } PK("<1>pwm_write: PWMC = 0x%8x\n", (U32)_reg(PWMC1));out: return ret;}/****************************************************************************** * Function Name: initpwm * * Input: : * Value Returned: int : Return status.If no error, return 0. * * Description: initialize PWM hardware * * Modification History: * 10 DEC,2001, Chen Ning *****************************************************************************/static int __init initpwm(){ PK("<1>in initpwm ----\n"); // Init Port PA2 : PWMO _set_register_bit ((U32*)PTA_DDIR, 2); _clear_register_bit((U32*)PTA_GIUS, 2); _clear_register_bit((U32*)PTA_GPR, 2); return 1;}/****************************************************************************** * Function Name: pwm_init * * Input: void : * Value Returned: int : Return status.If no error, return 0. * * Description: device driver initialization * * Modification History: * 10 DEC,2001, Chen Ning ******************************************************************************/#ifdef MODULEint init_module(void)#elseint __init pwm_init(void)#endif{ int result; printk("PWM driver version 0.2.0\n"); PK("<1>Init module\n"); /* register our character device */ result = register_chrdev(0, MOD_NAME, &ts_fops); if (result < 0) { PK("<1>ts_dev: can't get major number\n"); return result; } if( gMajor == 0 ) { gMajor = result; printk("PWM major number = %d\n",gMajor); } PK("Request IRQs for PWM_INT.\n"); //AITC_PWM_INT == 34 result = request_irq(AITC_PWM_INT, pwm_interrupt, SA_INTERRUPT, DEV_IRQ_NAME, DEV_IRQ_ID); if (result) { PK("<1>cannot init major= %d irq=%d\n", gMajor, irq); unregister_chrdev(gMajor, MOD_NAME); return -1; } // init wait queue init_waitqueue_head(&ts_wait); // register power manager pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, handle_pm_event); // init pwm initpwm(); return 0;}/****************************************************************************** * Function Name: pwm_cleanup * * Input: void : * Value Returned: void : * * Description: clean up and free all of resource for this MODULE * * Modification History: * 10 DEC,2001, Chen Ning *****************************************************************************/#ifdef MODULEvoid cleanup_module(void)#elsevoid __exit pwm_cleanup(void)#endif{ free_irq(AITC_PWM_INT, DEV_IRQ_ID); unregister_chrdev(gMajor, MOD_NAME); disable_irq(AITC_PWM_INT);}// --------------------------------------------------------------------// local rutines/****************************************************************************** * Function Name: checkDevice * * Input: inode : * Value Returned: int : Return status. If no error, return 0. * * Description: verify if the inode is a correct inode * * Modification History: * 10 DEC,2001, Chen Ning *****************************************************************************/static int checkDevice(struct inode *pInode){ int minor; kdev_t dev = pInode->i_rdev; if( MAJOR(dev) != gMajor) { PK("<1>checkDevice bad major = %d\n",MAJOR(dev) ); return -1; } minor = MINOR(dev); if ( minor < MAX_ID ) return minor; else { PK("<1>checkDevice bad minor = %d\n",minor ); return -1; }}/* timer */int CreateTimer(PWM_Timer_t *timer){ init_timer(timer->timer_blk_ptr); timer->timer_blk_ptr->function = timer->timer_func; return(0);}int StartTimer(PWM_Timer_t *timer){ timer->timer_blk_ptr->expires = jiffies + timer->period; timer->stop_flag = 0; add_timer((struct timer_list *)timer->timer_blk_ptr); return(0);}int StopTimer(PWM_Timer_t *timer){ timer->stop_flag = 1; del_timer_sync((struct timer_list *)timer->timer_blk_ptr); return(0);}void pwmIntFunc(){U32 period; PK("<1>pwm sam int\n"); if (gWriteCnt > 0) { PK("<1>PWMS = 0x%4x\n", (U16)_reg(PWMS1)); // 96M/128/2/period period = *gpWriteBufPtr16++; if(period > 0) _reg(PWMP1) = 96000000/128/12/2/period; //96000000/128/2*11 PK("<1>PWMP = 0x%4x\n", (U16)_reg(PWMP1)); StartTimer(&pwmTimer); gWriteCnt--; } else StopPwm();}void StopPwm(){ if(gpWriteBuf) kfree(gpWriteBuf); _clear_register_bit ((U32*)PWMC1, 6); // disable IRQEN _clear_register_bit ((U32*)PWMC1, 4); // disable EN gWriteOk = 1; gWriteCnt = 0; gDataLen = 0; gSampleValue = 0; if(gPwmMode == PWM_TONE_MODE) StopTimer(&pwmTimer); // can release PWM now wake_up_interruptible(&ts_wait); PK("<1>data completed.\n"); PK("<1>PWMC = 0x%8x\n", (U32)_reg(PWMC1));}static int handle_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data) //typedef int pm_request_t{ switch(rqst) { case PM_SUSPEND: PK("<1> PM suspend event received in PWM device.\n"); StopPwm(); break; case PM_RESUME: PK("<1> PM resume event received in PWM device.\n"); break; default: break; } return 0;} /* handle_pm_event */#ifndef MODULEmodule_init(pwm_init);module_exit(pwm_cleanup);#endif/* end of file */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -