xsysace.c

来自「linux 内核源代码」· C语言 代码 · 共 1,288 行 · 第 1/3 页

C
1,288
字号
#define ACE_TASK_WRITE     3#define ACE_FSM_NUM_TASKS  4/* FSM state definitions */#define ACE_FSM_STATE_IDLE               0#define ACE_FSM_STATE_REQ_LOCK           1#define ACE_FSM_STATE_WAIT_LOCK          2#define ACE_FSM_STATE_WAIT_CFREADY       3#define ACE_FSM_STATE_IDENTIFY_PREPARE   4#define ACE_FSM_STATE_IDENTIFY_TRANSFER  5#define ACE_FSM_STATE_IDENTIFY_COMPLETE  6#define ACE_FSM_STATE_REQ_PREPARE        7#define ACE_FSM_STATE_REQ_TRANSFER       8#define ACE_FSM_STATE_REQ_COMPLETE       9#define ACE_FSM_STATE_ERROR             10#define ACE_FSM_NUM_STATES              11/* Set flag to exit FSM loop and reschedule tasklet */static inline void ace_fsm_yield(struct ace_device *ace){	dev_dbg(ace->dev, "ace_fsm_yield()\n");	tasklet_schedule(&ace->fsm_tasklet);	ace->fsm_continue_flag = 0;}/* Set flag to exit FSM loop and wait for IRQ to reschedule tasklet */static inline void ace_fsm_yieldirq(struct ace_device *ace){	dev_dbg(ace->dev, "ace_fsm_yieldirq()\n");	if (ace->irq == NO_IRQ)		/* No IRQ assigned, so need to poll */		tasklet_schedule(&ace->fsm_tasklet);	ace->fsm_continue_flag = 0;}/* Get the next read/write request; ending requests that we don't handle */struct request *ace_get_next_request(struct request_queue * q){	struct request *req;	while ((req = elv_next_request(q)) != NULL) {		if (blk_fs_request(req))			break;		end_request(req, 0);	}	return req;}static void ace_fsm_dostate(struct ace_device *ace){	struct request *req;	u32 status;	u16 val;	int count;	int i;#if defined(DEBUG)	dev_dbg(ace->dev, "fsm_state=%i, id_req_count=%i\n",		ace->fsm_state, ace->id_req_count);#endif	switch (ace->fsm_state) {	case ACE_FSM_STATE_IDLE:		/* See if there is anything to do */		if (ace->id_req_count || ace_get_next_request(ace->queue)) {			ace->fsm_iter_num++;			ace->fsm_state = ACE_FSM_STATE_REQ_LOCK;			mod_timer(&ace->stall_timer, jiffies + HZ);			if (!timer_pending(&ace->stall_timer))				add_timer(&ace->stall_timer);			break;		}		del_timer(&ace->stall_timer);		ace->fsm_continue_flag = 0;		break;	case ACE_FSM_STATE_REQ_LOCK:		if (ace_in(ace, ACE_STATUS) & ACE_STATUS_MPULOCK) {			/* Already have the lock, jump to next state */			ace->fsm_state = ACE_FSM_STATE_WAIT_CFREADY;			break;		}		/* Request the lock */		val = ace_in(ace, ACE_CTRL);		ace_out(ace, ACE_CTRL, val | ACE_CTRL_LOCKREQ);		ace->fsm_state = ACE_FSM_STATE_WAIT_LOCK;		break;	case ACE_FSM_STATE_WAIT_LOCK:		if (ace_in(ace, ACE_STATUS) & ACE_STATUS_MPULOCK) {			/* got the lock; move to next state */			ace->fsm_state = ACE_FSM_STATE_WAIT_CFREADY;			break;		}		/* wait a bit for the lock */		ace_fsm_yield(ace);		break;	case ACE_FSM_STATE_WAIT_CFREADY:		status = ace_in32(ace, ACE_STATUS);		if (!(status & ACE_STATUS_RDYFORCFCMD) ||		    (status & ACE_STATUS_CFBSY)) {			/* CF card isn't ready; it needs to be polled */			ace_fsm_yield(ace);			break;		}		/* Device is ready for command; determine what to do next */		if (ace->id_req_count)			ace->fsm_state = ACE_FSM_STATE_IDENTIFY_PREPARE;		else			ace->fsm_state = ACE_FSM_STATE_REQ_PREPARE;		break;	case ACE_FSM_STATE_IDENTIFY_PREPARE:		/* Send identify command */		ace->fsm_task = ACE_TASK_IDENTIFY;		ace->data_ptr = &ace->cf_id;		ace->data_count = ACE_BUF_PER_SECTOR;		ace_out(ace, ACE_SECCNTCMD, ACE_SECCNTCMD_IDENTIFY);		/* As per datasheet, put config controller in reset */		val = ace_in(ace, ACE_CTRL);		ace_out(ace, ACE_CTRL, val | ACE_CTRL_CFGRESET);		/* irq handler takes over from this point; wait for the		 * transfer to complete */		ace->fsm_state = ACE_FSM_STATE_IDENTIFY_TRANSFER;		ace_fsm_yieldirq(ace);		break;	case ACE_FSM_STATE_IDENTIFY_TRANSFER:		/* Check that the sysace is ready to receive data */		status = ace_in32(ace, ACE_STATUS);		if (status & ACE_STATUS_CFBSY) {			dev_dbg(ace->dev, "CFBSY set; t=%i iter=%i dc=%i\n",				ace->fsm_task, ace->fsm_iter_num,				ace->data_count);			ace_fsm_yield(ace);			break;		}		if (!(status & ACE_STATUS_DATABUFRDY)) {			ace_fsm_yield(ace);			break;		}		/* Transfer the next buffer */		ace->reg_ops->datain(ace);		ace->data_count--;		/* If there are still buffers to be transfers; jump out here */		if (ace->data_count != 0) {			ace_fsm_yieldirq(ace);			break;		}		/* transfer finished; kick state machine */		dev_dbg(ace->dev, "identify finished\n");		ace->fsm_state = ACE_FSM_STATE_IDENTIFY_COMPLETE;		break;	case ACE_FSM_STATE_IDENTIFY_COMPLETE:		ace_fix_driveid(&ace->cf_id);		ace_dump_mem(&ace->cf_id, 512);	/* Debug: Dump out disk ID */		if (ace->data_result) {			/* Error occured, disable the disk */			ace->media_change = 1;			set_capacity(ace->gd, 0);			dev_err(ace->dev, "error fetching CF id (%i)\n",				ace->data_result);		} else {			ace->media_change = 0;			/* Record disk parameters */			set_capacity(ace->gd, ace->cf_id.lba_capacity);			dev_info(ace->dev, "capacity: %i sectors\n",				 ace->cf_id.lba_capacity);		}		/* We're done, drop to IDLE state and notify waiters */		ace->fsm_state = ACE_FSM_STATE_IDLE;		ace->id_result = ace->data_result;		while (ace->id_req_count) {			complete(&ace->id_completion);			ace->id_req_count--;		}		break;	case ACE_FSM_STATE_REQ_PREPARE:		req = ace_get_next_request(ace->queue);		if (!req) {			ace->fsm_state = ACE_FSM_STATE_IDLE;			break;		}		/* Okay, it's a data request, set it up for transfer */		dev_dbg(ace->dev,			"request: sec=%lx hcnt=%lx, ccnt=%x, dir=%i\n",			req->sector, req->hard_nr_sectors,			req->current_nr_sectors, rq_data_dir(req));		ace->req = req;		ace->data_ptr = req->buffer;		ace->data_count = req->current_nr_sectors * ACE_BUF_PER_SECTOR;		ace_out32(ace, ACE_MPULBA, req->sector & 0x0FFFFFFF);		count = req->hard_nr_sectors;		if (rq_data_dir(req)) {			/* Kick off write request */			dev_dbg(ace->dev, "write data\n");			ace->fsm_task = ACE_TASK_WRITE;			ace_out(ace, ACE_SECCNTCMD,				count | ACE_SECCNTCMD_WRITE_DATA);		} else {			/* Kick off read request */			dev_dbg(ace->dev, "read data\n");			ace->fsm_task = ACE_TASK_READ;			ace_out(ace, ACE_SECCNTCMD,				count | ACE_SECCNTCMD_READ_DATA);		}		/* As per datasheet, put config controller in reset */		val = ace_in(ace, ACE_CTRL);		ace_out(ace, ACE_CTRL, val | ACE_CTRL_CFGRESET);		/* Move to the transfer state.  The systemace will raise		 * an interrupt once there is something to do		 */		ace->fsm_state = ACE_FSM_STATE_REQ_TRANSFER;		if (ace->fsm_task == ACE_TASK_READ)			ace_fsm_yieldirq(ace);	/* wait for data ready */		break;	case ACE_FSM_STATE_REQ_TRANSFER:		/* Check that the sysace is ready to receive data */		status = ace_in32(ace, ACE_STATUS);		if (status & ACE_STATUS_CFBSY) {			dev_dbg(ace->dev,				"CFBSY set; t=%i iter=%i c=%i dc=%i irq=%i\n",				ace->fsm_task, ace->fsm_iter_num,				ace->req->current_nr_sectors * 16,				ace->data_count, ace->in_irq);			ace_fsm_yield(ace);	/* need to poll CFBSY bit */			break;		}		if (!(status & ACE_STATUS_DATABUFRDY)) {			dev_dbg(ace->dev,				"DATABUF not set; t=%i iter=%i c=%i dc=%i irq=%i\n",				ace->fsm_task, ace->fsm_iter_num,				ace->req->current_nr_sectors * 16,				ace->data_count, ace->in_irq);			ace_fsm_yieldirq(ace);			break;		}		/* Transfer the next buffer */		i = 16;		if (ace->fsm_task == ACE_TASK_WRITE)			ace->reg_ops->dataout(ace);		else			ace->reg_ops->datain(ace);		ace->data_count--;		/* If there are still buffers to be transfers; jump out here */		if (ace->data_count != 0) {			ace_fsm_yieldirq(ace);			break;		}		/* bio finished; is there another one? */		i = ace->req->current_nr_sectors;		if (end_that_request_first(ace->req, 1, i)) {			/* dev_dbg(ace->dev, "next block; h=%li c=%i\n",			 *      ace->req->hard_nr_sectors,			 *      ace->req->current_nr_sectors);			 */			ace->data_ptr = ace->req->buffer;			ace->data_count = ace->req->current_nr_sectors * 16;			ace_fsm_yieldirq(ace);			break;		}		ace->fsm_state = ACE_FSM_STATE_REQ_COMPLETE;		break;	case ACE_FSM_STATE_REQ_COMPLETE:		/* Complete the block request */		blkdev_dequeue_request(ace->req);		end_that_request_last(ace->req, 1);		ace->req = NULL;		/* Finished request; go to idle state */		ace->fsm_state = ACE_FSM_STATE_IDLE;		break;	default:		ace->fsm_state = ACE_FSM_STATE_IDLE;		break;	}}static void ace_fsm_tasklet(unsigned long data){	struct ace_device *ace = (void *)data;	unsigned long flags;	spin_lock_irqsave(&ace->lock, flags);	/* Loop over state machine until told to stop */	ace->fsm_continue_flag = 1;	while (ace->fsm_continue_flag)		ace_fsm_dostate(ace);	spin_unlock_irqrestore(&ace->lock, flags);}static void ace_stall_timer(unsigned long data){	struct ace_device *ace = (void *)data;	unsigned long flags;	dev_warn(ace->dev,		 "kicking stalled fsm; state=%i task=%i iter=%i dc=%i\n",		 ace->fsm_state, ace->fsm_task, ace->fsm_iter_num,		 ace->data_count);	spin_lock_irqsave(&ace->lock, flags);	/* Rearm the stall timer *before* entering FSM (which may then	 * delete the timer) */	mod_timer(&ace->stall_timer, jiffies + HZ);	/* Loop over state machine until told to stop */	ace->fsm_continue_flag = 1;	while (ace->fsm_continue_flag)		ace_fsm_dostate(ace);	spin_unlock_irqrestore(&ace->lock, flags);}/* --------------------------------------------------------------------- * Interrupt handling routines */static int ace_interrupt_checkstate(struct ace_device *ace){	u32 sreg = ace_in32(ace, ACE_STATUS);	u16 creg = ace_in(ace, ACE_CTRL);	/* Check for error occurance */	if ((sreg & (ACE_STATUS_CFGERROR | ACE_STATUS_CFCERROR)) &&	    (creg & ACE_CTRL_ERRORIRQ)) {		dev_err(ace->dev, "transfer failure\n");		ace_dump_regs(ace);		return -EIO;	}	return 0;}static irqreturn_t ace_interrupt(int irq, void *dev_id){	u16 creg;	struct ace_device *ace = dev_id;	/* be safe and get the lock */	spin_lock(&ace->lock);	ace->in_irq = 1;	/* clear the interrupt */	creg = ace_in(ace, ACE_CTRL);	ace_out(ace, ACE_CTRL, creg | ACE_CTRL_RESETIRQ);	ace_out(ace, ACE_CTRL, creg);	/* check for IO failures */	if (ace_interrupt_checkstate(ace))		ace->data_result = -EIO;	if (ace->fsm_task == 0) {		dev_err(ace->dev,			"spurious irq; stat=%.8x ctrl=%.8x cmd=%.4x\n",			ace_in32(ace, ACE_STATUS), ace_in32(ace, ACE_CTRL),			ace_in(ace, ACE_SECCNTCMD));		dev_err(ace->dev, "fsm_task=%i fsm_state=%i data_count=%i\n",			ace->fsm_task, ace->fsm_state, ace->data_count);	}	/* Loop over state machine until told to stop */	ace->fsm_continue_flag = 1;	while (ace->fsm_continue_flag)		ace_fsm_dostate(ace);	/* done with interrupt; drop the lock */	ace->in_irq = 0;	spin_unlock(&ace->lock);	return IRQ_HANDLED;}/* --------------------------------------------------------------------- * Block ops */static void ace_request(struct request_queue * q){	struct request *req;	struct ace_device *ace;	req = ace_get_next_request(q);	if (req) {		ace = req->rq_disk->private_data;		tasklet_schedule(&ace->fsm_tasklet);	}}static int ace_media_changed(struct gendisk *gd){	struct ace_device *ace = gd->private_data;	dev_dbg(ace->dev, "ace_media_changed(): %i\n", ace->media_change);	return ace->media_change;}static int ace_revalidate_disk(struct gendisk *gd){	struct ace_device *ace = gd->private_data;	unsigned long flags;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?