예제 #1
0
/**
 * cdns_spi_irq - Interrupt service routine of the SPI controller
 * @irq:	IRQ number
 * @dev_id:	Pointer to the xspi structure
 *
 * This function handles TX empty and Mode Fault interrupts only.
 * On TX empty interrupt this function reads the received data from RX FIFO and
 * fills the TX FIFO if there is any data remaining to be transferred.
 * On Mode Fault interrupt this function indicates that transfer is completed,
 * the SPI subsystem will identify the error as the remaining bytes to be
 * transferred is non-zero.
 *
 * Return:	IRQ_HANDLED when handled; IRQ_NONE otherwise.
 */
static irqreturn_t cdns_spi_irq(int irq, void *dev_id)
{
	struct spi_master *master = dev_id;
	struct cdns_spi *xspi = spi_master_get_devdata(master);
	u32 intr_status, status;

	status = IRQ_NONE;
	intr_status = cdns_spi_read(xspi, CDNS_SPI_ISR_OFFSET);
	cdns_spi_write(xspi, CDNS_SPI_ISR_OFFSET, intr_status);

	if (intr_status & CDNS_SPI_IXR_MODF_MASK) {
		/* Indicate that transfer is completed, the SPI subsystem will
		 * identify the error as the remaining bytes to be
		 * transferred is non-zero
		 */
		cdns_spi_write(xspi, CDNS_SPI_IDR_OFFSET,
			       CDNS_SPI_IXR_DEFAULT_MASK);
		spi_finalize_current_transfer(master);
		status = IRQ_HANDLED;
	} else if (intr_status & CDNS_SPI_IXR_TXOW_MASK) {
		unsigned long trans_cnt;

		trans_cnt = xspi->rx_bytes - xspi->tx_bytes;

		/* Read out the data from the RX FIFO */
		while (trans_cnt) {
			u8 data;

			data = cdns_spi_read(xspi, CDNS_SPI_RXD_OFFSET);
			if (xspi->rxbuf)
				*xspi->rxbuf++ = data;

			xspi->rx_bytes--;
			trans_cnt--;
		}

		if (xspi->tx_bytes) {
			/* There is more data to send */
			cdns_spi_fill_tx_fifo(xspi);
		} else {
			/* Transfer is completed */
			cdns_spi_write(xspi, CDNS_SPI_IDR_OFFSET,
				       CDNS_SPI_IXR_DEFAULT_MASK);
			spi_finalize_current_transfer(master);
		}
		status = IRQ_HANDLED;
	}

	return status;
}
예제 #2
0
/**
 * cdns_spi_config_clock_freq - Sets clock frequency
 * @spi:	Pointer to the spi_device structure
 * @transfer:	Pointer to the spi_transfer structure which provides
 *		information about next transfer setup parameters
 *
 * Sets the requested clock frequency.
 * Note: If the requested frequency is not an exact match with what can be
 * obtained using the prescalar value the driver sets the clock frequency which
 * is lower than the requested frequency (maximum lower) for the transfer. If
 * the requested frequency is higher or lower than that is supported by the SPI
 * controller the driver will set the highest or lowest frequency supported by
 * controller.
 */
static void cdns_spi_config_clock_freq(struct spi_device *spi,
				       struct spi_transfer *transfer)
{
	struct cdns_spi *xspi = spi_master_get_devdata(spi->master);
	u32 ctrl_reg, baud_rate_val;
	unsigned long frequency;

	frequency = clk_get_rate(xspi->ref_clk);

	ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR);

	/* Set the clock frequency */
	if (xspi->speed_hz != transfer->speed_hz) {
		/* first valid value is 1 */
		baud_rate_val = CDNS_SPI_BAUD_DIV_MIN;
		while ((baud_rate_val < CDNS_SPI_BAUD_DIV_MAX) &&
		       (frequency / (2 << baud_rate_val)) > transfer->speed_hz)
			baud_rate_val++;

		ctrl_reg &= ~CDNS_SPI_CR_BAUD_DIV;
		ctrl_reg |= baud_rate_val << CDNS_SPI_BAUD_DIV_SHIFT;

		xspi->speed_hz = frequency / (2 << baud_rate_val);
	}
	cdns_spi_write(xspi, CDNS_SPI_CR, ctrl_reg);
}
예제 #3
0
/**
 * cdns_spi_config_clock_mode - Sets clock polarity and phase
 * @spi:	Pointer to the spi_device structure
 *
 * Sets the requested clock polarity and phase.
 */
static void cdns_spi_config_clock_mode(struct spi_device *spi)
{
	struct cdns_spi *xspi = spi_master_get_devdata(spi->master);
	u32 ctrl_reg, new_ctrl_reg;

	new_ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR);
	ctrl_reg = new_ctrl_reg;

	/* Set the SPI clock phase and clock polarity */
	new_ctrl_reg &= ~(CDNS_SPI_CR_CPHA | CDNS_SPI_CR_CPOL);
	if (spi->mode & SPI_CPHA)
		new_ctrl_reg |= CDNS_SPI_CR_CPHA;
	if (spi->mode & SPI_CPOL)
		new_ctrl_reg |= CDNS_SPI_CR_CPOL;

	if (new_ctrl_reg != ctrl_reg) {
		/*
		 * Just writing the CR register does not seem to apply the clock
		 * setting changes. This is problematic when changing the clock
		 * polarity as it will cause the SPI slave to see spurious clock
		 * transitions. To workaround the issue toggle the ER register.
		 */
		cdns_spi_write(xspi, CDNS_SPI_ER, CDNS_SPI_ER_DISABLE);
		cdns_spi_write(xspi, CDNS_SPI_CR, new_ctrl_reg);
		cdns_spi_write(xspi, CDNS_SPI_ER, CDNS_SPI_ER_ENABLE);
	}
}
예제 #4
0
/**
 * cdns_spi_init_hw - Initialize the hardware and configure the SPI controller
 * @xspi:	Pointer to the cdns_spi structure
 *
 * On reset the SPI controller is configured to be in master mode, baud rate
 * divisor is set to 4, threshold value for TX FIFO not full interrupt is set
 * to 1 and size of the word to be transferred as 8 bit.
 * This function initializes the SPI controller to disable and clear all the
 * interrupts, enable manual slave select and manual start, deselect all the
 * chip select lines, and enable the SPI controller.
 */
static void cdns_spi_init_hw(struct cdns_spi *xspi)
{
	u32 ctrl_reg = CDNS_SPI_CR_DEFAULT;

	if (xspi->is_decoded_cs)
		ctrl_reg |= CDNS_SPI_CR_PERI_SEL;

	cdns_spi_write(xspi, CDNS_SPI_ER, CDNS_SPI_ER_DISABLE);
	cdns_spi_write(xspi, CDNS_SPI_IDR, CDNS_SPI_IXR_ALL);

	/* Clear the RX FIFO */
	while (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_RXNEMTY)
		cdns_spi_read(xspi, CDNS_SPI_RXD);

	cdns_spi_write(xspi, CDNS_SPI_ISR, CDNS_SPI_IXR_ALL);
	cdns_spi_write(xspi, CDNS_SPI_CR, ctrl_reg);
	cdns_spi_write(xspi, CDNS_SPI_ER, CDNS_SPI_ER_ENABLE);
}
예제 #5
0
/**
 * cdns_spi_init_hw - Initialize the hardware and configure the SPI controller
 * @xspi:	Pointer to the cdns_spi structure
 *
 * On reset the SPI controller is configured to be in master mode, baud rate
 * divisor is set to 4, threshold value for TX FIFO not full interrupt is set
 * to 1 and size of the word to be transferred as 8 bit.
 * This function initializes the SPI controller to disable and clear all the
 * interrupts, enable manual slave select and manual start, deselect all the
 * chip select lines, and enable the SPI controller.
 */
static void cdns_spi_init_hw(struct cdns_spi *xspi)
{
	cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET,
		       CDNS_SPI_ER_DISABLE_MASK);
	cdns_spi_write(xspi, CDNS_SPI_IDR_OFFSET,
		       CDNS_SPI_IXR_ALL_MASK);

	/* Clear the RX FIFO */
	while (cdns_spi_read(xspi, CDNS_SPI_ISR_OFFSET) &
	       CDNS_SPI_IXR_RXNEMTY_MASK)
		cdns_spi_read(xspi, CDNS_SPI_RXD_OFFSET);

	cdns_spi_write(xspi, CDNS_SPI_ISR_OFFSET,
		       CDNS_SPI_IXR_ALL_MASK);
	cdns_spi_write(xspi, CDNS_SPI_CR_OFFSET,
		       CDNS_SPI_CR_DEFAULT_MASK);
	cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET,
		       CDNS_SPI_ER_ENABLE_MASK);
}
예제 #6
0
/**
 * cdns_spi_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible
 * @xspi:	Pointer to the cdns_spi structure
 */
static void cdns_spi_fill_tx_fifo(struct cdns_spi *xspi)
{
	unsigned long trans_cnt = 0;

	while ((trans_cnt < CDNS_SPI_FIFO_DEPTH) &&
	       (xspi->tx_bytes > 0)) {

		/* When xspi in busy condition, bytes may send failed,
		 * then spi control did't work thoroughly, add one byte delay
		 */
		if (cdns_spi_read(xspi, CDNS_SPI_ISR) &
		    CDNS_SPI_IXR_TXFULL)
			udelay(10);

		if (xspi->txbuf)
			cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++);
		else
			cdns_spi_write(xspi, CDNS_SPI_TXD, 0);

		xspi->tx_bytes--;
		trans_cnt++;
	}
}
예제 #7
0
/**
 * cdns_spi_chipselect - Select or deselect the chip select line
 * @spi:	Pointer to the spi_device structure
 * @enable:	Select (1) or deselect (0) the chip select line
 */
static void cdns_spi_chipselect(struct spi_device *spi, bool enable)
{
	struct cdns_spi *xspi = spi_master_get_devdata(spi->master);
	u32 ctrl_reg;

	ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR);

	if (!enable) {
		/* Deselect the slave */
		ctrl_reg |= CDNS_SPI_CR_SSCTRL;
	} else {
		/* Select the slave */
		ctrl_reg &= ~CDNS_SPI_CR_SSCTRL;
		if (!(xspi->is_decoded_cs))
			ctrl_reg |= ((~(CDNS_SPI_SS0 << spi->chip_select)) <<
				     CDNS_SPI_SS_SHIFT) &
				     CDNS_SPI_CR_SSCTRL;
		else
			ctrl_reg |= (spi->chip_select << CDNS_SPI_SS_SHIFT) &
				     CDNS_SPI_CR_SSCTRL;
	}

	cdns_spi_write(xspi, CDNS_SPI_CR, ctrl_reg);
}