spi_bfin5xx.c

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

C
1,489
字号
	write_TDBR(drv_data, 0xFFFF);	cs_active(drv_data, chip);	dummy_read(drv_data);	while (drv_data->rx < drv_data->rx_end - 2) {		cs_deactive(drv_data, chip);		while (!(read_STAT(drv_data) & BIT_STAT_RXS))			cpu_relax();		cs_active(drv_data, chip);		*(u16 *) (drv_data->rx) = read_RDBR(drv_data);		drv_data->rx += 2;	}	cs_deactive(drv_data, chip);	while (!(read_STAT(drv_data) & BIT_STAT_RXS))		cpu_relax();	*(u16 *) (drv_data->rx) = read_SHAW(drv_data);	drv_data->rx += 2;}static void u16_duplex(struct driver_data *drv_data){	/* poll for SPI completion before start */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF))		cpu_relax();	/* in duplex mode, clk is triggered by writing of TDBR */	while (drv_data->tx < drv_data->tx_end) {		write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));		while (read_STAT(drv_data) & BIT_STAT_TXS)			cpu_relax();		while (!(read_STAT(drv_data) & BIT_STAT_RXS))			cpu_relax();		*(u16 *) (drv_data->rx) = read_RDBR(drv_data);		drv_data->rx += 2;		drv_data->tx += 2;	}}static void u16_cs_chg_duplex(struct driver_data *drv_data){	struct chip_data *chip = drv_data->cur_chip;	/* poll for SPI completion before start */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF))		cpu_relax();	while (drv_data->tx < drv_data->tx_end) {		cs_active(drv_data, chip);		write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));		while (read_STAT(drv_data) & BIT_STAT_TXS)			cpu_relax();		while (!(read_STAT(drv_data) & BIT_STAT_RXS))			cpu_relax();		*(u16 *) (drv_data->rx) = read_RDBR(drv_data);		cs_deactive(drv_data, chip);		drv_data->rx += 2;		drv_data->tx += 2;	}}/* test if ther is more transfer to be done */static void *next_transfer(struct driver_data *drv_data){	struct spi_message *msg = drv_data->cur_msg;	struct spi_transfer *trans = drv_data->cur_transfer;	/* Move to next transfer */	if (trans->transfer_list.next != &msg->transfers) {		drv_data->cur_transfer =		    list_entry(trans->transfer_list.next,			       struct spi_transfer, transfer_list);		return RUNNING_STATE;	} else		return DONE_STATE;}/* * caller already set message->status; * dma and pio irqs are blocked give finished message back */static void giveback(struct driver_data *drv_data){	struct chip_data *chip = drv_data->cur_chip;	struct spi_transfer *last_transfer;	unsigned long flags;	struct spi_message *msg;	spin_lock_irqsave(&drv_data->lock, flags);	msg = drv_data->cur_msg;	drv_data->cur_msg = NULL;	drv_data->cur_transfer = NULL;	drv_data->cur_chip = NULL;	queue_work(drv_data->workqueue, &drv_data->pump_messages);	spin_unlock_irqrestore(&drv_data->lock, flags);	last_transfer = list_entry(msg->transfers.prev,				   struct spi_transfer, transfer_list);	msg->state = NULL;	/* disable chip select signal. And not stop spi in autobuffer mode */	if (drv_data->tx_dma != 0xFFFF) {		cs_deactive(drv_data, chip);		bfin_spi_disable(drv_data);	}	if (!drv_data->cs_change)		cs_deactive(drv_data, chip);	if (msg->complete)		msg->complete(msg->context);}static irqreturn_t dma_irq_handler(int irq, void *dev_id){	struct driver_data *drv_data = (struct driver_data *)dev_id;	struct chip_data *chip = drv_data->cur_chip;	struct spi_message *msg = drv_data->cur_msg;	dev_dbg(&drv_data->pdev->dev, "in dma_irq_handler\n");	clear_dma_irqstat(drv_data->dma_channel);	/* Wait for DMA to complete */	while (get_dma_curr_irqstat(drv_data->dma_channel) & DMA_RUN)		cpu_relax();	/*	 * wait for the last transaction shifted out.  HRM states:	 * at this point there may still be data in the SPI DMA FIFO waiting	 * to be transmitted ... software needs to poll TXS in the SPI_STAT	 * register until it goes low for 2 successive reads	 */	if (drv_data->tx != NULL) {		while ((read_STAT(drv_data) & TXS) ||		       (read_STAT(drv_data) & TXS))			cpu_relax();	}	while (!(read_STAT(drv_data) & SPIF))		cpu_relax();	msg->actual_length += drv_data->len_in_bytes;	if (drv_data->cs_change)		cs_deactive(drv_data, chip);	/* Move to next transfer */	msg->state = next_transfer(drv_data);	/* Schedule transfer tasklet */	tasklet_schedule(&drv_data->pump_transfers);	/* free the irq handler before next transfer */	dev_dbg(&drv_data->pdev->dev,		"disable dma channel irq%d\n",		drv_data->dma_channel);	dma_disable_irq(drv_data->dma_channel);	return IRQ_HANDLED;}static void pump_transfers(unsigned long data){	struct driver_data *drv_data = (struct driver_data *)data;	struct spi_message *message = NULL;	struct spi_transfer *transfer = NULL;	struct spi_transfer *previous = NULL;	struct chip_data *chip = NULL;	u8 width;	u16 cr, dma_width, dma_config;	u32 tranf_success = 1;	/* Get current state information */	message = drv_data->cur_msg;	transfer = drv_data->cur_transfer;	chip = drv_data->cur_chip;	/*	 * if msg is error or done, report it back using complete() callback	 */	 /* Handle for abort */	if (message->state == ERROR_STATE) {		message->status = -EIO;		giveback(drv_data);		return;	}	/* Handle end of message */	if (message->state == DONE_STATE) {		message->status = 0;		giveback(drv_data);		return;	}	/* Delay if requested at end of transfer */	if (message->state == RUNNING_STATE) {		previous = list_entry(transfer->transfer_list.prev,				      struct spi_transfer, transfer_list);		if (previous->delay_usecs)			udelay(previous->delay_usecs);	}	/* Setup the transfer state based on the type of transfer */	if (flush(drv_data) == 0) {		dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n");		message->status = -EIO;		giveback(drv_data);		return;	}	if (transfer->tx_buf != NULL) {		drv_data->tx = (void *)transfer->tx_buf;		drv_data->tx_end = drv_data->tx + transfer->len;		dev_dbg(&drv_data->pdev->dev, "tx_buf is %p, tx_end is %p\n",			transfer->tx_buf, drv_data->tx_end);	} else {		drv_data->tx = NULL;	}	if (transfer->rx_buf != NULL) {		drv_data->rx = transfer->rx_buf;		drv_data->rx_end = drv_data->rx + transfer->len;		dev_dbg(&drv_data->pdev->dev, "rx_buf is %p, rx_end is %p\n",			transfer->rx_buf, drv_data->rx_end);	} else {		drv_data->rx = NULL;	}	drv_data->rx_dma = transfer->rx_dma;	drv_data->tx_dma = transfer->tx_dma;	drv_data->len_in_bytes = transfer->len;	drv_data->cs_change = transfer->cs_change;	/* Bits per word setup */	switch (transfer->bits_per_word) {	case 8:		drv_data->n_bytes = 1;		width = CFG_SPI_WORDSIZE8;		drv_data->read = chip->cs_change_per_word ?			u8_cs_chg_reader : u8_reader;		drv_data->write = chip->cs_change_per_word ?			u8_cs_chg_writer : u8_writer;		drv_data->duplex = chip->cs_change_per_word ?			u8_cs_chg_duplex : u8_duplex;		break;	case 16:		drv_data->n_bytes = 2;		width = CFG_SPI_WORDSIZE16;		drv_data->read = chip->cs_change_per_word ?			u16_cs_chg_reader : u16_reader;		drv_data->write = chip->cs_change_per_word ?			u16_cs_chg_writer : u16_writer;		drv_data->duplex = chip->cs_change_per_word ?			u16_cs_chg_duplex : u16_duplex;		break;	default:		/* No change, the same as default setting */		drv_data->n_bytes = chip->n_bytes;		width = chip->width;		drv_data->write = drv_data->tx ? chip->write : null_writer;		drv_data->read = drv_data->rx ? chip->read : null_reader;		drv_data->duplex = chip->duplex ? chip->duplex : null_writer;		break;	}	cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD));	cr |= (width << 8);	write_CTRL(drv_data, cr);	if (width == CFG_SPI_WORDSIZE16) {		drv_data->len = (transfer->len) >> 1;	} else {		drv_data->len = transfer->len;	}	dev_dbg(&drv_data->pdev->dev, "transfer: ",		"drv_data->write is %p, chip->write is %p, null_wr is %p\n",		drv_data->write, chip->write, null_writer);	/* speed and width has been set on per message */	message->state = RUNNING_STATE;	dma_config = 0;	/* Speed setup (surely valid because already checked) */	if (transfer->speed_hz)		write_BAUD(drv_data, hz_to_spi_baud(transfer->speed_hz));	else		write_BAUD(drv_data, chip->baud);	write_STAT(drv_data, BIT_STAT_CLR);	cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD));	cs_active(drv_data, chip);	dev_dbg(&drv_data->pdev->dev,		"now pumping a transfer: width is %d, len is %d\n",		width, transfer->len);	/*	 * Try to map dma buffer and do a dma transfer if	 * successful use different way to r/w according to	 * drv_data->cur_chip->enable_dma	 */	if (drv_data->cur_chip->enable_dma && drv_data->len > 6) {		disable_dma(drv_data->dma_channel);		clear_dma_irqstat(drv_data->dma_channel);		bfin_spi_disable(drv_data);		/* config dma channel */		dev_dbg(&drv_data->pdev->dev, "doing dma transfer\n");		if (width == CFG_SPI_WORDSIZE16) {			set_dma_x_count(drv_data->dma_channel, drv_data->len);			set_dma_x_modify(drv_data->dma_channel, 2);			dma_width = WDSIZE_16;		} else {			set_dma_x_count(drv_data->dma_channel, drv_data->len);			set_dma_x_modify(drv_data->dma_channel, 1);			dma_width = WDSIZE_8;		}		/* poll for SPI completion before start */		while (!(read_STAT(drv_data) & BIT_STAT_SPIF))			cpu_relax();		/* dirty hack for autobuffer DMA mode */		if (drv_data->tx_dma == 0xFFFF) {			dev_dbg(&drv_data->pdev->dev,				"doing autobuffer DMA out.\n");			/* no irq in autobuffer mode */			dma_config =			    (DMAFLOW_AUTO | RESTART | dma_width | DI_EN);			set_dma_config(drv_data->dma_channel, dma_config);			set_dma_start_addr(drv_data->dma_channel,					(unsigned long)drv_data->tx);			enable_dma(drv_data->dma_channel);			/* start SPI transfer */			write_CTRL(drv_data,				(cr | CFG_SPI_DMAWRITE | BIT_CTL_ENABLE));			/* just return here, there can only be one transfer			 * in this mode			 */			message->status = 0;			giveback(drv_data);			return;		}		/* In dma mode, rx or tx must be NULL in one transfer */		if (drv_data->rx != NULL) {			/* set transfer mode, and enable SPI */			dev_dbg(&drv_data->pdev->dev, "doing DMA in.\n");			/* clear tx reg soformer data is not shifted out */			write_TDBR(drv_data, 0xFFFF);			set_dma_x_count(drv_data->dma_channel, drv_data->len);			/* start dma */			dma_enable_irq(drv_data->dma_channel);			dma_config = (WNR | RESTART | dma_width | DI_EN);			set_dma_config(drv_data->dma_channel, dma_config);			set_dma_start_addr(drv_data->dma_channel,					(unsigned long)drv_data->rx);			enable_dma(drv_data->dma_channel);			/* start SPI transfer */			write_CTRL(drv_data,				(cr | CFG_SPI_DMAREAD | BIT_CTL_ENABLE));		} else if (drv_data->tx != NULL) {			dev_dbg(&drv_data->pdev->dev, "doing DMA out.\n");			/* start dma */			dma_enable_irq(drv_data->dma_channel);			dma_config = (RESTART | dma_width | DI_EN);			set_dma_config(drv_data->dma_channel, dma_config);			set_dma_start_addr(drv_data->dma_channel,					(unsigned long)drv_data->tx);			enable_dma(drv_data->dma_channel);			/* start SPI transfer */			write_CTRL(drv_data,				(cr | CFG_SPI_DMAWRITE | BIT_CTL_ENABLE));		}	} else {		/* IO mode write then read */		dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n");		if (drv_data->tx != NULL && drv_data->rx != NULL) {			/* full duplex mode */			BUG_ON((drv_data->tx_end - drv_data->tx) !=			       (drv_data->rx_end - drv_data->rx));			dev_dbg(&drv_data->pdev->dev,				"IO duplex: cr is 0x%x\n", cr);			/* set SPI transfer mode */			write_CTRL(drv_data, (cr | CFG_SPI_WRITE));			drv_data->duplex(drv_data);			if (drv_data->tx != drv_data->tx_end)				tranf_success = 0;		} else if (drv_data->tx != NULL) {			/* write only half duplex */			dev_dbg(&drv_data->pdev->dev,				"IO write: cr is 0x%x\n", cr);			/* set SPI transfer mode */			write_CTRL(drv_data, (cr | CFG_SPI_WRITE));			drv_data->write(drv_data);			if (drv_data->tx != drv_data->tx_end)				tranf_success = 0;		} else if (drv_data->rx != NULL) {			/* read only half duplex */			dev_dbg(&drv_data->pdev->dev,				"IO read: cr is 0x%x\n", cr);			/* set SPI transfer mode */			write_CTRL(drv_data, (cr | CFG_SPI_READ));			drv_data->read(drv_data);			if (drv_data->rx != drv_data->rx_end)				tranf_success = 0;		}		if (!tranf_success) {			dev_dbg(&drv_data->pdev->dev,				"IO write error!\n");			message->state = ERROR_STATE;		} else {			/* Update total byte transfered */			message->actual_length += drv_data->len;			/* Move to next transfer of this msg */			message->state = next_transfer(drv_data);		}		/* Schedule next transfer tasklet */		tasklet_schedule(&drv_data->pump_transfers);	}}/* pop a msg from queue and kick off real transfer */static void pump_messages(struct work_struct *work){	struct driver_data *drv_data;	unsigned long flags;	drv_data = container_of(work, struct driver_data, pump_messages);	/* Lock queue and check for queue work */	spin_lock_irqsave(&drv_data->lock, flags);	if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) {		/* pumper kicked off but no work to do */		drv_data->busy = 0;		spin_unlock_irqrestore(&drv_data->lock, flags);		return;	}	/* Make sure we are not already running a message */	if (drv_data->cur_msg) {		spin_unlock_irqrestore(&drv_data->lock, flags);		return;	}	/* Extract head of queue */	drv_data->cur_msg = list_entry(drv_data->queue.next,				       struct spi_message, queue);	/* Setup the SSP using the per chip configuration */	drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);	if (restore_state(drv_data)) {		spin_unlock_irqrestore(&drv_data->lock, flags);		return;	};	list_del_init(&drv_data->cur_msg->queue);	/* Initial message state */	drv_data->cur_msg->state = START_STATE;	drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,					    struct spi_transfer, transfer_list);	dev_dbg(&drv_data->pdev->dev, "got a message to pump, "		"state is set to: baud %d, flag 0x%x, ctl 0x%x\n",

⌨️ 快捷键说明

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