1. 程式人生 > >[嵌入式Linux驅動]S5PV210的步進電機Linux驅動程式

[嵌入式Linux驅動]S5PV210的步進電機Linux驅動程式

#include <asm/io.h>
#include <asm/uaccess.h>

#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/mutex.h>

#include <linux/platform_device.h>
#include <linux/miscdevice.h>

#define DEBUG 1
#define SINGLE_MODULE 1

#define READING_WAIT_TIME 300

#define MAGIC_WORD 'P'

#define STEPPER_MOTOR_DOWN_A_STEP	_IO(MAGIC_WORD,11)
#define STEPPER_MOTOR_UP_A_STEP		_IO(MAGIC_WORD,21)


#define GPH0_4_SET_LOWLEVEL(tmp) do{		\
	tmp =ioread32(GPH0_BASE+1);		\
	tmp &= ~(0x1<<4);			\
	iowrite32(tmp,GPH0_BASE+1);		\
}while(0)

#define GPH0_4_SET_HIGHLEVEL(tmp) do{	        \
	tmp =ioread32(GPH0_BASE+1);		\
	tmp |= (0x1<<4);			\
	iowrite32(tmp,GPH0_BASE+1);		\
}while(0)


#define GPH1_1_SET_LOWLEVEL(tmp) do{		\
	tmp =ioread32(GPH1_BASE+1);		\
	tmp  &= ~(0x1<<1);			\
	iowrite32(tmp,GPH1_BASE+1);		\
}while(0)

#define GPH1_1_SET_HIGHLEVEL(tmp) do{	        \
	tmp =ioread32(GPH1_BASE+1);		\
	tmp  |= (0x1<<1);			\
	iowrite32(tmp,GPH1_BASE+1);		\
}while(0)

#define GPH0_3_SET_LOWLEVEL(tmp) do{		\
	tmp =ioread32(GPH0_BASE+1);		\
	tmp &= ~(0x1<<3);			\
	iowrite32(tmp,GPH0_BASE+1);		\
}while(0)

#define GPH0_3_SET_HIGHLEVEL(tmp) do{	        \
	tmp =ioread32(GPH0_BASE+1);		\
	tmp |= (0x1<<3);			\
	iowrite32(tmp,GPH0_BASE+1);		\
}while(0)

#define GPH0_5_SET_LOWLEVEL(tmp) do{		\
	tmp =ioread32(GPH0_BASE+1);		\
	tmp  &= ~(0x1<<5);			\
	iowrite32(tmp,GPH0_BASE+1);		\
}while(0)

#define GPH0_5_SET_HIGHLEVEL(tmp) do{	        \
	tmp =ioread32(GPH0_BASE+1);		\
	tmp  |= (0x1<<5);			\
	iowrite32(tmp,GPH0_BASE+1);		\
}while(0)


#define OP_1(tmp)	do{			\
	GPH0_4_SET_HIGHLEVEL(tmp);		\
	mdelay(plat_data_p->pulse_period);	\
	GPH0_4_SET_LOWLEVEL(tmp);		\
}while(0)

#define OP_2(tmp)	do{			\
	GPH1_1_SET_HIGHLEVEL(tmp);		\
	mdelay(plat_data_p->pulse_period);	\
	GPH1_1_SET_LOWLEVEL(tmp);		\
}while(0)

#define OP_3(tmp)	do{			\
	GPH0_3_SET_HIGHLEVEL(tmp);		\
	mdelay(plat_data_p->pulse_period);	\
	GPH0_3_SET_LOWLEVEL(tmp);		\
}while(0)


#define OP_4(tmp)	do{			\
	GPH0_5_SET_HIGHLEVEL(tmp);		\
	mdelay(plat_data_p->pulse_period);	\
	GPH0_5_SET_LOWLEVEL(tmp);		\
}while(0)


struct StepperMotor_Plat_Data{
	unsigned short step_long;
	unsigned short one_round;
	unsigned short pulse_period;
	unsigned short max_position;
	unsigned short current_position;
	unsigned short min_position;
};

static struct StepperMotor_Plat_Data * plat_data_p;

struct resource * platform_resource;

static volatile unsigned long * GPH_BASE;


//Must caculate the offset carefully!!!

#define GPH0_BASE  (GPH_BASE + 0) 
#define GPH1_BASE  (GPH_BASE + 8)


/*** file_operation_function declare ****/

int     Stepper_Motor_driver_open (struct inode * inode_p, struct file *file_p);
long    Stepper_Motor_driver_ioctl (struct file *file_p, unsigned int cmd, unsigned long arg);
int	Stepper_Motor_driver_close (struct inode *inode_p, struct file *file_p);
ssize_t Stepper_Motor_driver_read (struct file *file_p, char __user *buff, size_t size, loff_t *offset);


static int __devexit Stepper_Motor_driver_remove(struct platform_device * pdev);
static int __devinit Stepper_Motor_dirver_probe(struct platform_device * pdev);


/*** Mutex ***/

struct mutex Stepper_Motor_mutex;

/*** Struct declare ****/

static struct platform_driver Stepper_Motor_driver={

	.probe=Stepper_Motor_dirver_probe,
	.remove = Stepper_Motor_driver_remove,

	.driver  = {
		.name = "stepper_motor_drv",	  
		.owner = THIS_MODULE,
	},
};

static struct file_operations Stepper_Motor_fop={
	.open=Stepper_Motor_driver_open,
	.unlocked_ioctl=Stepper_Motor_driver_ioctl,
	.release=Stepper_Motor_driver_close,
	.read=Stepper_Motor_driver_read,
};


static struct miscdevice Stepper_Motor_miscdev = {	
	.minor	= MISC_DYNAMIC_MINOR,				//dynamic
	.name	= "smarthome_steppermotor_control",
	.fops	= &Stepper_Motor_fop,
};


/*** file_operation_function implement ****/

int  Stepper_Motor_driver_open (struct inode * inode_p, struct file *file_p)
{
	printk("entering %s\n",__FUNCTION__);

	unsigned int tmp;
		
/*nLED_1=EINT4/GPH0_4*/
		
	//GPH0CON[4]	[19:16] = 0001
	tmp =ioread32(GPH0_BASE+0);
	tmp &= ~(0xf<<16);
	tmp |= (0x1<<16);
	iowrite32(tmp,GPH0_BASE+0);
		
	GPH0_4_SET_LOWLEVEL(tmp);
		
/*nLED_4=EINT9/GPH1_1*/
	
	//GPH1CON[1]	[7:4]=0001
	tmp =ioread32(GPH1_BASE+0);
	tmp &= ~(0xf<<4);		
	tmp |= (0x1<<4);
	iowrite32(tmp,GPH1_BASE+0); 
		
	GPH1_1_SET_LOWLEVEL(tmp);
		
/*nLED_3=EINT3/GPH0_3*/
	
	//GPH0CON[3]	[15:12] = 0001
	tmp =ioread32(GPH0_BASE+0);
	tmp &= ~(0xf<<12);
	tmp |= (0x1<<12);
	iowrite32(tmp,GPH0_BASE+0);
	
	GPH0_3_SET_LOWLEVEL(tmp);
	
/*nLED_4=EINT5/GPH0_5*/
			
	//GPH0CON[5]	[23:20]=0001
	tmp =ioread32(GPH0_BASE+0);
	tmp &= ~(0xf<<20);		
	tmp |= (0x1<<20);
	iowrite32(tmp,GPH0_BASE+0); 
				
	GPH0_5_SET_LOWLEVEL(tmp);
	
	plat_data_p->current_position=0;
		
	printk("%s: gpio init finished!!!\n",__FUNCTION__);
	
	return 0;
}


ssize_t Stepper_Motor_driver_read (struct file * file_p,char __user * buff, size_t size,loff_t *offset)
{
	printk("entering %s\n",__FUNCTION__);

	unsigned char i;
	i=0;

	while(copy_to_user(buff,(void *)plat_data_p,sizeof(*plat_data_p))){
		
		msleep(READING_WAIT_TIME);
		if(i++ >= 2){
			printk("%s: copy_to_user failed!!!\n",__FUNCTION__);
			return -EBUSY;
		}
	}

	return 0;
}


long  Stepper_Motor_driver_ioctl (struct file *file_p, unsigned int cmd, unsigned long arg)
{
/* Locked */

	mutex_lock(&Stepper_Motor_mutex);

	printk("entering %s\n",__FUNCTION__);

	unsigned int tmp;
	int i;
		
	if(_IOC_TYPE(cmd) != MAGIC_WORD) 
		return -EINVAL;

	switch(cmd){
		
	case STEPPER_MOTOR_DOWN_A_STEP:
		
		if(plat_data_p->current_position < plat_data_p->max_position){				

			for(i=0;i<plat_data_p->one_round;i++){			
				OP_1(tmp);			
				OP_2(tmp);
				OP_3(tmp);
				OP_4(tmp);
			}

			plat_data_p->current_position++;
#if DEBUG
			printk("Kernel Debug: plat_data_p->current_position is %d\n",
					plat_data_p->current_position);
#endif			
		}
                break;

	case STEPPER_MOTOR_UP_A_STEP:
		
		if(plat_data_p->current_position > plat_data_p->min_position){

			for(i=0;i<plat_data_p->one_round;i++){
				
				OP_4(tmp);
				OP_3(tmp);
				OP_2(tmp);
				OP_1(tmp);
			}

			plat_data_p->current_position--;
#if DEBUG
			printk("Kernel Debug: plat_data_p->current_position is %d\n",
					plat_data_p->current_position);
#endif	
		}
                break;

	default:
		printk("%s: invalid command !!!\n",__FUNCTION__);
		break;	
	}

/* Unlocked */
	mutex_unlock(&Stepper_Motor_mutex);

	return 0;
}

int	Stepper_Motor_driver_close (struct inode *inode_p, struct file *file_p)
{
	printk("entering %s\n",__FUNCTION__);

	unsigned int tmp;

	GPH0_4_SET_LOWLEVEL(tmp);
	GPH1_1_SET_LOWLEVEL(tmp);
	GPH0_3_SET_LOWLEVEL(tmp);
	GPH0_5_SET_LOWLEVEL(tmp);

	return 0;
}

/*** driver_operation ****/

static int __devinit Stepper_Motor_dirver_probe(struct platform_device * pdev)
{
	printk("entering %s\n",__FUNCTION__);

	struct resource * pcheck;

	plat_data_p=(struct StepperMotor_Plat_Data *)(pdev->dev.platform_data);

	platform_resource=platform_get_resource(pdev,IORESOURCE_MEM,0);

	if(NULL == platform_resource){
		printk("%s: platform_get_resource failed!\n",__FUNCTION__);	
		goto err1;
	}

#if SINGLE_MODULE
	pcheck=request_mem_region(platform_resource->start,
				  platform_resource->end - platform_resource->start + 1,
				  platform_resource->name);

	if(NULL==pcheck){
		printk("%s: request_mem_region failed!\n",__FUNCTION__);	
		goto err1;						//return device busy!
	}
#endif

	GPH_BASE=(unsigned long *)ioremap(platform_resource->start,
					  platform_resource->end - platform_resource->start + 1);

#if DEBUG
	printk("%s: GPH_BASE is %p \n",__FUNCTION__,GPH_BASE);	
	printk("%s: GPH0_BASE is %p \n",__FUNCTION__,GPH0_BASE);	
	printk("%s: GPH1_BASE is %p \n",__FUNCTION__,GPH1_BASE);	
#endif

	if( misc_register(&Stepper_Motor_miscdev) ){
		printk("%s: misc_register failed!\n",__FUNCTION__);	
		goto err2;
	}
	
/* initing the mutex */
	
	mutex_init(&Stepper_Motor_mutex);

	return 0;

err2:
	iounmap(GPH_BASE);
	
#if SINGLE_MODULE
	release_mem_region(platform_resource->start,
			   platform_resource->end - platform_resource->start + 1);
#endif

err1:
	return -EBUSY;
	
}


static int __devexit  Stepper_Motor_driver_remove(struct platform_device * pdev)
{
	printk("entering %s\n",__FUNCTION__);

	mutex_destroy(&Stepper_Motor_mutex);

	iounmap(GPH_BASE);

#if SINGLE_MODULE
	release_mem_region(platform_resource->start,
						 platform_resource->end - platform_resource->start + 1);
#endif

	if( misc_deregister(&Stepper_Motor_miscdev) ){
		printk("%s:misc_deregister failed!\n",__FUNCTION__);
		return -EPERM;
	}

	return 0;
}


static int __init  Stepper_Motor_driver_init(void)
{
	printk("entering %s\n",__FUNCTION__);

	if( platform_driver_register(& Stepper_Motor_driver) ){
		printk("%s: driver_register failed!\n",__FUNCTION__);
		return -EBUSY;
	}

	return 0;
}


static void __exit  Stepper_Motor_driver_exit(void)
{
	printk("entering %s\n",__FUNCTION__);

	platform_driver_unregister(& Stepper_Motor_driver);
}

module_init(Stepper_Motor_driver_init);
module_exit(Stepper_Motor_driver_exit);

MODULE_AUTHOR("kinyanderson");
MODULE_DESCRIPTION("Stepper_Motor_driver,use for controlling the Stepper motor");
MODULE_LICENSE("GPL");