static int stm32_mdma_set_xfer_param()

in stm32-mdma.c [466:643]


static int stm32_mdma_set_xfer_param(struct stm32_mdma_chan *chan,
				     enum dma_transfer_direction direction,
				     u32 *mdma_ccr, u32 *mdma_ctcr,
				     u32 *mdma_ctbr, dma_addr_t addr,
				     u32 buf_len)
{
	struct stm32_mdma_device *dmadev = stm32_mdma_get_dev(chan);
	struct stm32_mdma_chan_config *chan_config = &chan->chan_config;
	enum dma_slave_buswidth src_addr_width, dst_addr_width;
	phys_addr_t src_addr, dst_addr;
	int src_bus_width, dst_bus_width;
	u32 src_maxburst, dst_maxburst, src_best_burst, dst_best_burst;
	u32 ccr, ctcr, ctbr, tlen;

	src_addr_width = chan->dma_config.src_addr_width;
	dst_addr_width = chan->dma_config.dst_addr_width;
	src_maxburst = chan->dma_config.src_maxburst;
	dst_maxburst = chan->dma_config.dst_maxburst;

	ccr = stm32_mdma_read(dmadev, STM32_MDMA_CCR(chan->id));
	ctcr = stm32_mdma_read(dmadev, STM32_MDMA_CTCR(chan->id));
	ctbr = stm32_mdma_read(dmadev, STM32_MDMA_CTBR(chan->id));

	/* Enable HW request mode */
	ctcr &= ~STM32_MDMA_CTCR_SWRM;

	/* Set DINC, SINC, DINCOS, SINCOS, TRGM and TLEN retrieve from DT */
	ctcr &= ~STM32_MDMA_CTCR_CFG_MASK;
	ctcr |= chan_config->transfer_config & STM32_MDMA_CTCR_CFG_MASK;

	/*
	 * For buffer transfer length (TLEN) we have to set
	 * the number of bytes - 1 in CTCR register
	 */
	tlen = STM32_MDMA_CTCR_LEN2_GET(ctcr);
	ctcr &= ~STM32_MDMA_CTCR_LEN2_MSK;
	ctcr |= STM32_MDMA_CTCR_TLEN((tlen - 1));

	/* Disable Pack Enable */
	ctcr &= ~STM32_MDMA_CTCR_PKE;

	/* Check burst size constraints */
	if (src_maxburst * src_addr_width > STM32_MDMA_MAX_BURST ||
	    dst_maxburst * dst_addr_width > STM32_MDMA_MAX_BURST) {
		dev_err(chan2dev(chan),
			"burst size * bus width higher than %d bytes\n",
			STM32_MDMA_MAX_BURST);
		return -EINVAL;
	}

	if ((!is_power_of_2(src_maxburst) && src_maxburst > 0) ||
	    (!is_power_of_2(dst_maxburst) && dst_maxburst > 0)) {
		dev_err(chan2dev(chan), "burst size must be a power of 2\n");
		return -EINVAL;
	}

	/*
	 * Configure channel control:
	 * - Clear SW request as in this case this is a HW one
	 * - Clear WEX, HEX and BEX bits
	 * - Set priority level
	 */
	ccr &= ~(STM32_MDMA_CCR_SWRQ | STM32_MDMA_CCR_WEX | STM32_MDMA_CCR_HEX |
		 STM32_MDMA_CCR_BEX | STM32_MDMA_CCR_PL_MASK);
	ccr |= STM32_MDMA_CCR_PL(chan_config->priority_level);

	/* Configure Trigger selection */
	ctbr &= ~STM32_MDMA_CTBR_TSEL_MASK;
	ctbr |= STM32_MDMA_CTBR_TSEL(chan_config->request);

	switch (direction) {
	case DMA_MEM_TO_DEV:
		dst_addr = chan->dma_config.dst_addr;

		/* Set device data size */
		dst_bus_width = stm32_mdma_get_width(chan, dst_addr_width);
		if (dst_bus_width < 0)
			return dst_bus_width;
		ctcr &= ~STM32_MDMA_CTCR_DSIZE_MASK;
		ctcr |= STM32_MDMA_CTCR_DSIZE(dst_bus_width);

		/* Set device burst value */
		dst_best_burst = stm32_mdma_get_best_burst(buf_len, tlen,
							   dst_maxburst,
							   dst_addr_width);
		chan->mem_burst = dst_best_burst;
		ctcr &= ~STM32_MDMA_CTCR_DBURST_MASK;
		ctcr |= STM32_MDMA_CTCR_DBURST((ilog2(dst_best_burst)));

		/* Set memory data size */
		src_addr_width = stm32_mdma_get_max_width(addr, buf_len, tlen);
		chan->mem_width = src_addr_width;
		src_bus_width = stm32_mdma_get_width(chan, src_addr_width);
		if (src_bus_width < 0)
			return src_bus_width;
		ctcr &= ~STM32_MDMA_CTCR_SSIZE_MASK |
			STM32_MDMA_CTCR_SINCOS_MASK;
		ctcr |= STM32_MDMA_CTCR_SSIZE(src_bus_width) |
			STM32_MDMA_CTCR_SINCOS(src_bus_width);

		/* Set memory burst value */
		src_maxburst = STM32_MDMA_MAX_BUF_LEN / src_addr_width;
		src_best_burst = stm32_mdma_get_best_burst(buf_len, tlen,
							   src_maxburst,
							   src_addr_width);
		chan->mem_burst = src_best_burst;
		ctcr &= ~STM32_MDMA_CTCR_SBURST_MASK;
		ctcr |= STM32_MDMA_CTCR_SBURST((ilog2(src_best_burst)));

		/* Select bus */
		stm32_mdma_set_bus(dmadev, &ctbr, STM32_MDMA_CTBR_DBUS,
				   dst_addr);

		if (dst_bus_width != src_bus_width)
			ctcr |= STM32_MDMA_CTCR_PKE;

		/* Set destination address */
		stm32_mdma_write(dmadev, STM32_MDMA_CDAR(chan->id), dst_addr);
		break;

	case DMA_DEV_TO_MEM:
		src_addr = chan->dma_config.src_addr;

		/* Set device data size */
		src_bus_width = stm32_mdma_get_width(chan, src_addr_width);
		if (src_bus_width < 0)
			return src_bus_width;
		ctcr &= ~STM32_MDMA_CTCR_SSIZE_MASK;
		ctcr |= STM32_MDMA_CTCR_SSIZE(src_bus_width);

		/* Set device burst value */
		src_best_burst = stm32_mdma_get_best_burst(buf_len, tlen,
							   src_maxburst,
							   src_addr_width);
		ctcr &= ~STM32_MDMA_CTCR_SBURST_MASK;
		ctcr |= STM32_MDMA_CTCR_SBURST((ilog2(src_best_burst)));

		/* Set memory data size */
		dst_addr_width = stm32_mdma_get_max_width(addr, buf_len, tlen);
		chan->mem_width = dst_addr_width;
		dst_bus_width = stm32_mdma_get_width(chan, dst_addr_width);
		if (dst_bus_width < 0)
			return dst_bus_width;
		ctcr &= ~(STM32_MDMA_CTCR_DSIZE_MASK |
			STM32_MDMA_CTCR_DINCOS_MASK);
		ctcr |= STM32_MDMA_CTCR_DSIZE(dst_bus_width) |
			STM32_MDMA_CTCR_DINCOS(dst_bus_width);

		/* Set memory burst value */
		dst_maxburst = STM32_MDMA_MAX_BUF_LEN / dst_addr_width;
		dst_best_burst = stm32_mdma_get_best_burst(buf_len, tlen,
							   dst_maxburst,
							   dst_addr_width);
		ctcr &= ~STM32_MDMA_CTCR_DBURST_MASK;
		ctcr |= STM32_MDMA_CTCR_DBURST((ilog2(dst_best_burst)));

		/* Select bus */
		stm32_mdma_set_bus(dmadev, &ctbr, STM32_MDMA_CTBR_SBUS,
				   src_addr);

		if (dst_bus_width != src_bus_width)
			ctcr |= STM32_MDMA_CTCR_PKE;

		/* Set source address */
		stm32_mdma_write(dmadev, STM32_MDMA_CSAR(chan->id), src_addr);
		break;

	default:
		dev_err(chan2dev(chan), "Dma direction is not supported\n");
		return -EINVAL;
	}

	*mdma_ccr = ccr;
	*mdma_ctcr = ctcr;
	*mdma_ctbr = ctbr;

	return 0;
}