⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pwm.c

📁 MX1的Linux(2.6内核)下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 + -