Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a930e528 authored by Elen Song's avatar Elen Song Committed by Greg Kroah-Hartman
Browse files

serial: at91: use function pointer to choose pdc or pio



use function pointer can avoid define atmel_use_pdc_tx/rx everywhere.
(*prepare_rx/tx)() is in setup transfer stage.
(*schedule_rx/tx)() is in tasklet schedule stage.
(*release_rx/tx)() is used when shutdown the transfer.

Signed-off-by: default avatarElen Song <elen.song@atmel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 64e22ebe
Loading
Loading
Loading
Loading
+130 −73
Original line number Diff line number Diff line
@@ -154,6 +154,12 @@ struct atmel_uart_port {

	struct serial_rs485	rs485;		/* rs485 settings */
	unsigned int		tx_done_mask;
	int (*prepare_rx)(struct uart_port *port);
	int (*prepare_tx)(struct uart_port *port);
	void (*schedule_rx)(struct uart_port *port);
	void (*schedule_tx)(struct uart_port *port);
	void (*release_rx)(struct uart_port *port);
	void (*release_tx)(struct uart_port *port);
};

static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -657,6 +663,17 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
	return pass_counter ? IRQ_HANDLED : IRQ_NONE;
}

static void atmel_release_tx_pdc(struct uart_port *port)
{
	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
	struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;

	dma_unmap_single(port->dev,
			 pdc->dma_addr,
			 pdc->dma_size,
			 DMA_TO_DEVICE);
}

/*
 * Called from tasklet with ENDTX and TXBUFE interrupts disabled.
 */
@@ -709,6 +726,23 @@ static void atmel_tx_pdc(struct uart_port *port)
		uart_write_wakeup(port);
}

static int atmel_prepare_tx_pdc(struct uart_port *port)
{
	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
	struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
	struct circ_buf *xmit = &port->state->xmit;

	pdc->buf = xmit->buf;
	pdc->dma_addr = dma_map_single(port->dev,
					pdc->buf,
					UART_XMIT_SIZE,
					DMA_TO_DEVICE);
	pdc->dma_size = UART_XMIT_SIZE;
	pdc->ofs = 0;

	return 0;
}

static void atmel_rx_from_ring(struct uart_port *port)
{
	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
@@ -777,6 +811,22 @@ static void atmel_rx_from_ring(struct uart_port *port)
	spin_lock(&port->lock);
}

static void atmel_release_rx_pdc(struct uart_port *port)
{
	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
	int i;

	for (i = 0; i < 2; i++) {
		struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];

		dma_unmap_single(port->dev,
				 pdc->dma_addr,
				 pdc->dma_size,
				 DMA_FROM_DEVICE);
		kfree(pdc->buf);
	}
}

static void atmel_rx_from_pdc(struct uart_port *port)
{
	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
@@ -854,6 +904,45 @@ static void atmel_rx_from_pdc(struct uart_port *port)
	UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
}

static int atmel_prepare_rx_pdc(struct uart_port *port)
{
	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
	int i;

	for (i = 0; i < 2; i++) {
		struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];

		pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL);
		if (pdc->buf == NULL) {
			if (i != 0) {
				dma_unmap_single(port->dev,
					atmel_port->pdc_rx[0].dma_addr,
					PDC_BUFFER_SIZE,
					DMA_FROM_DEVICE);
				kfree(atmel_port->pdc_rx[0].buf);
			}
			atmel_port->use_pdc_rx = 0;
			return -ENOMEM;
		}
		pdc->dma_addr = dma_map_single(port->dev,
						pdc->buf,
						PDC_BUFFER_SIZE,
						DMA_FROM_DEVICE);
		pdc->dma_size = PDC_BUFFER_SIZE;
		pdc->ofs = 0;
	}

	atmel_port->pdc_rx_idx = 0;

	UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr);
	UART_PUT_RCR(port, PDC_BUFFER_SIZE);

	UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr);
	UART_PUT_RNCR(port, PDC_BUFFER_SIZE);

	return 0;
}

/*
 * tasklet handling tty stuff outside the interrupt handler.
 */
@@ -867,10 +956,7 @@ static void atmel_tasklet_func(unsigned long data)
	/* The interrupt handler does not take the lock */
	spin_lock(&port->lock);

	if (atmel_use_pdc_tx(port))
		atmel_tx_pdc(port);
	else
		atmel_tx_chars(port);
	atmel_port->schedule_tx(port);

	status = atmel_port->irq_status;
	status_change = status ^ atmel_port->irq_status_prev;
@@ -892,14 +978,36 @@ static void atmel_tasklet_func(unsigned long data)
		atmel_port->irq_status_prev = status;
	}

	if (atmel_use_pdc_rx(port))
		atmel_rx_from_pdc(port);
	else
		atmel_rx_from_ring(port);
	atmel_port->schedule_rx(port);

	spin_unlock(&port->lock);
}

static void atmel_set_ops(struct uart_port *port)
{
	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);

	if (atmel_use_pdc_rx(port)) {
		atmel_port->prepare_rx = &atmel_prepare_rx_pdc;
		atmel_port->schedule_rx = &atmel_rx_from_pdc;
		atmel_port->release_rx = &atmel_release_rx_pdc;
	} else {
		atmel_port->prepare_rx = NULL;
		atmel_port->schedule_rx = &atmel_rx_from_ring;
		atmel_port->release_rx = NULL;
	}

	if (atmel_use_pdc_tx(port)) {
		atmel_port->prepare_tx = &atmel_prepare_tx_pdc;
		atmel_port->schedule_tx = &atmel_tx_pdc;
		atmel_port->release_tx = &atmel_release_tx_pdc;
	} else {
		atmel_port->prepare_tx = NULL;
		atmel_port->schedule_tx = &atmel_tx_chars;
		atmel_port->release_tx = NULL;
	}
}

/*
 * Perform initialization and enable port for reception
 */
@@ -929,53 +1037,17 @@ static int atmel_startup(struct uart_port *port)
	/*
	 * Initialize DMA (if necessary)
	 */
	if (atmel_use_pdc_rx(port)) {
		int i;

		for (i = 0; i < 2; i++) {
			struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];

			pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL);
			if (pdc->buf == NULL) {
				if (i != 0) {
					dma_unmap_single(port->dev,
						atmel_port->pdc_rx[0].dma_addr,
						PDC_BUFFER_SIZE,
						DMA_FROM_DEVICE);
					kfree(atmel_port->pdc_rx[0].buf);
				}
				free_irq(port->irq, port);
				return -ENOMEM;
			}
			pdc->dma_addr = dma_map_single(port->dev,
						       pdc->buf,
						       PDC_BUFFER_SIZE,
						       DMA_FROM_DEVICE);
			pdc->dma_size = PDC_BUFFER_SIZE;
			pdc->ofs = 0;
		}

		atmel_port->pdc_rx_idx = 0;

		UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr);
		UART_PUT_RCR(port, PDC_BUFFER_SIZE);

		UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr);
		UART_PUT_RNCR(port, PDC_BUFFER_SIZE);
	if (atmel_port->prepare_rx) {
		retval = atmel_port->prepare_rx(port);
		if (retval < 0)
			atmel_set_ops(port);
	}
	if (atmel_use_pdc_tx(port)) {
		struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
		struct circ_buf *xmit = &port->state->xmit;

		pdc->buf = xmit->buf;
		pdc->dma_addr = dma_map_single(port->dev,
					       pdc->buf,
					       UART_XMIT_SIZE,
					       DMA_TO_DEVICE);
		pdc->dma_size = UART_XMIT_SIZE;
		pdc->ofs = 0;
	if (atmel_port->prepare_tx) {
		retval = atmel_port->prepare_tx(port);
		if (retval < 0)
			atmel_set_ops(port);
	}

	/*
	 * If there is a specific "open" function (to register
	 * control line interrupts)
@@ -1030,27 +1102,10 @@ static void atmel_shutdown(struct uart_port *port)
	/*
	 * Shut-down the DMA.
	 */
	if (atmel_use_pdc_rx(port)) {
		int i;

		for (i = 0; i < 2; i++) {
			struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];

			dma_unmap_single(port->dev,
					 pdc->dma_addr,
					 pdc->dma_size,
					 DMA_FROM_DEVICE);
			kfree(pdc->buf);
		}
	}
	if (atmel_use_pdc_tx(port)) {
		struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;

		dma_unmap_single(port->dev,
				 pdc->dma_addr,
				 pdc->dma_size,
				 DMA_TO_DEVICE);
	}
	if (atmel_port->release_rx)
		atmel_port->release_rx(port);
	if (atmel_port->release_tx)
		atmel_port->release_tx(port);

	/*
	 * Disable all interrupts, port and break condition.
@@ -1473,6 +1528,8 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
		atmel_port->rs485	= pdata->rs485;
	}

	atmel_set_ops(port);

	port->iotype		= UPIO_MEM;
	port->flags		= UPF_BOOT_AUTOCONF;
	port->ops		= &atmel_pops;