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

Commit df1f3a23 authored by Mans Rullgard's avatar Mans Rullgard Committed by Vinod Koul
Browse files

dmaengine: dw: fix byte order of hw descriptor fields



If the DMA controller uses a different byte order than the host CPU,
the hardware linked list descriptor fields need to be byte-swapped.

This patch makes the driver write these fields using the same byte
order it uses for mmio accesses to the DMA engine. I do not know
if this is guaranteed to always be correct.

Signed-off-by: default avatarMans Rullgard <mans@mansr.com>
Acked-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarVinod Koul <vinod.koul@intel.com>
parent bb3450ad
Loading
Loading
Loading
Loading
+62 −61
Original line number Diff line number Diff line
@@ -201,12 +201,12 @@ static inline void dwc_do_single_block(struct dw_dma_chan *dwc,
	 * Software emulation of LLP mode relies on interrupts to continue
	 * multi block transfer.
	 */
	ctllo = desc->lli.ctllo | DWC_CTLL_INT_EN;
	ctllo = lli_read(desc, ctllo) | DWC_CTLL_INT_EN;

	channel_writel(dwc, SAR, desc->lli.sar);
	channel_writel(dwc, DAR, desc->lli.dar);
	channel_writel(dwc, SAR, lli_read(desc, sar));
	channel_writel(dwc, DAR, lli_read(desc, dar));
	channel_writel(dwc, CTL_LO, ctllo);
	channel_writel(dwc, CTL_HI, desc->lli.ctlhi);
	channel_writel(dwc, CTL_HI, lli_read(desc, ctlhi));
	channel_set_bit(dw, CH_EN, dwc->mask);

	/* Move pointer to next descriptor */
@@ -424,7 +424,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
		}

		/* Check first descriptors llp */
		if (desc->lli.llp == llp) {
		if (lli_read(desc, llp) == llp) {
			/* This one is currently in progress */
			dwc->residue -= dwc_get_sent(dwc);
			spin_unlock_irqrestore(&dwc->lock, flags);
@@ -433,7 +433,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)

		dwc->residue -= desc->len;
		list_for_each_entry(child, &desc->tx_list, desc_node) {
			if (child->lli.llp == llp) {
			if (lli_read(child, llp) == llp) {
				/* Currently in progress */
				dwc->residue -= dwc_get_sent(dwc);
				spin_unlock_irqrestore(&dwc->lock, flags);
@@ -461,10 +461,14 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
	spin_unlock_irqrestore(&dwc->lock, flags);
}

static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli)
static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_desc *desc)
{
	dev_crit(chan2dev(&dwc->chan), "  desc: s0x%x d0x%x l0x%x c0x%x:%x\n",
		 lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo);
		 lli_read(desc, sar),
		 lli_read(desc, dar),
		 lli_read(desc, llp),
		 lli_read(desc, ctlhi),
		 lli_read(desc, ctllo));
}

static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc)
@@ -500,9 +504,9 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc)
	 */
	dev_WARN(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n"
				       "  cookie: %d\n", bad_desc->txd.cookie);
	dwc_dump_lli(dwc, &bad_desc->lli);
	dwc_dump_lli(dwc, bad_desc);
	list_for_each_entry(child, &bad_desc->tx_list, desc_node)
		dwc_dump_lli(dwc, &child->lli);
		dwc_dump_lli(dwc, child);

	spin_unlock_irqrestore(&dwc->lock, flags);

@@ -575,7 +579,7 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc,
		dma_writel(dw, CLEAR.XFER, dwc->mask);

		for (i = 0; i < dwc->cdesc->periods; i++)
			dwc_dump_lli(dwc, &dwc->cdesc->desc[i]->lli);
			dwc_dump_lli(dwc, dwc->cdesc->desc[i]);

		spin_unlock_irqrestore(&dwc->lock, flags);
	}
@@ -734,25 +738,24 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
		if (!desc)
			goto err_desc_get;

		desc->lli.sar = src + offset;
		desc->lli.dar = dest + offset;
		desc->lli.ctllo = ctllo;
		desc->lli.ctlhi = xfer_count;
		lli_write(desc, sar, src + offset);
		lli_write(desc, dar, dest + offset);
		lli_write(desc, ctllo, ctllo);
		lli_write(desc, ctlhi, xfer_count);
		desc->len = xfer_count << src_width;

		if (!first) {
			first = desc;
		} else {
			prev->lli.llp = desc->txd.phys;
			list_add_tail(&desc->desc_node,
					&first->tx_list);
			lli_write(prev, llp, desc->txd.phys);
			list_add_tail(&desc->desc_node, &first->tx_list);
		}
		prev = desc;
	}

	if (flags & DMA_PREP_INTERRUPT)
		/* Trigger interrupt after last block */
		prev->lli.ctllo |= DWC_CTLL_INT_EN;
		lli_set(prev, ctllo, DWC_CTLL_INT_EN);

	prev->lli.llp = 0;
	first->txd.flags = flags;
@@ -822,9 +825,9 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
			if (!desc)
				goto err_desc_get;

			desc->lli.sar = mem;
			desc->lli.dar = reg;
			desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width);
			lli_write(desc, sar, mem);
			lli_write(desc, dar, reg);
			lli_write(desc, ctllo, ctllo | DWC_CTLL_SRC_WIDTH(mem_width));
			if ((len >> mem_width) > dwc->block_size) {
				dlen = dwc->block_size << mem_width;
				mem += dlen;
@@ -834,15 +837,14 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
				len = 0;
			}

			desc->lli.ctlhi = dlen >> mem_width;
			lli_write(desc, ctlhi, dlen >> mem_width);
			desc->len = dlen;

			if (!first) {
				first = desc;
			} else {
				prev->lli.llp = desc->txd.phys;
				list_add_tail(&desc->desc_node,
						&first->tx_list);
				lli_write(prev, llp, desc->txd.phys);
				list_add_tail(&desc->desc_node, &first->tx_list);
			}
			prev = desc;
			total_len += dlen;
@@ -879,9 +881,9 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
			if (!desc)
				goto err_desc_get;

			desc->lli.sar = reg;
			desc->lli.dar = mem;
			desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width);
			lli_write(desc, sar, reg);
			lli_write(desc, dar, mem);
			lli_write(desc, ctllo, ctllo | DWC_CTLL_DST_WIDTH(mem_width));
			if ((len >> reg_width) > dwc->block_size) {
				dlen = dwc->block_size << reg_width;
				mem += dlen;
@@ -890,15 +892,14 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
				dlen = len;
				len = 0;
			}
			desc->lli.ctlhi = dlen >> reg_width;
			lli_write(desc, ctlhi, dlen >> reg_width);
			desc->len = dlen;

			if (!first) {
				first = desc;
			} else {
				prev->lli.llp = desc->txd.phys;
				list_add_tail(&desc->desc_node,
						&first->tx_list);
				lli_write(prev, llp, desc->txd.phys);
				list_add_tail(&desc->desc_node, &first->tx_list);
			}
			prev = desc;
			total_len += dlen;
@@ -913,7 +914,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,

	if (flags & DMA_PREP_INTERRUPT)
		/* Trigger interrupt after last block */
		prev->lli.ctllo |= DWC_CTLL_INT_EN;
		lli_set(prev, ctllo, DWC_CTLL_INT_EN);

	prev->lli.llp = 0;
	first->total_len = total_len;
@@ -1400,50 +1401,50 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,

		switch (direction) {
		case DMA_MEM_TO_DEV:
			desc->lli.dar = sconfig->dst_addr;
			desc->lli.sar = buf_addr + (period_len * i);
			desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan)
			lli_write(desc, dar, sconfig->dst_addr);
			lli_write(desc, sar, buf_addr + period_len * i);
			lli_write(desc, ctllo, (DWC_DEFAULT_CTLLO(chan)
				| DWC_CTLL_DST_WIDTH(reg_width)
				| DWC_CTLL_SRC_WIDTH(reg_width)
				| DWC_CTLL_DST_FIX
				| DWC_CTLL_SRC_INC
					| DWC_CTLL_INT_EN);
				| DWC_CTLL_INT_EN));

			desc->lli.ctllo |= sconfig->device_fc ?
			lli_set(desc, ctllo, sconfig->device_fc ?
					DWC_CTLL_FC(DW_DMA_FC_P_M2P) :
				DWC_CTLL_FC(DW_DMA_FC_D_M2P);
					DWC_CTLL_FC(DW_DMA_FC_D_M2P));

			break;
		case DMA_DEV_TO_MEM:
			desc->lli.dar = buf_addr + (period_len * i);
			desc->lli.sar = sconfig->src_addr;
			desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan)
			lli_write(desc, dar, buf_addr + period_len * i);
			lli_write(desc, sar, sconfig->src_addr);
			lli_write(desc, ctllo, (DWC_DEFAULT_CTLLO(chan)
				| DWC_CTLL_SRC_WIDTH(reg_width)
				| DWC_CTLL_DST_WIDTH(reg_width)
				| DWC_CTLL_DST_INC
				| DWC_CTLL_SRC_FIX
					| DWC_CTLL_INT_EN);
				| DWC_CTLL_INT_EN));

			desc->lli.ctllo |= sconfig->device_fc ?
			lli_set(desc, ctllo, sconfig->device_fc ?
					DWC_CTLL_FC(DW_DMA_FC_P_P2M) :
				DWC_CTLL_FC(DW_DMA_FC_D_P2M);
					DWC_CTLL_FC(DW_DMA_FC_D_P2M));

			break;
		default:
			break;
		}

		desc->lli.ctlhi = (period_len >> reg_width);
		lli_write(desc, ctlhi, period_len >> reg_width);
		cdesc->desc[i] = desc;

		if (last)
			last->lli.llp = desc->txd.phys;
			lli_write(last, llp, desc->txd.phys);

		last = desc;
	}

	/* Let's make a cyclic list */
	last->lli.llp = cdesc->desc[0]->txd.phys;
	lli_write(last, llp, cdesc->desc[0]->txd.phys);

	dev_dbg(chan2dev(&dwc->chan),
			"cyclic prepared buf %pad len %zu period %zu periods %d\n",
+25 −7
Original line number Diff line number Diff line
@@ -308,26 +308,44 @@ static inline struct dw_dma *to_dw_dma(struct dma_device *ddev)
	return container_of(ddev, struct dw_dma, dma);
}

#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO
typedef __be32 __dw32;
#else
typedef __le32 __dw32;
#endif

/* LLI == Linked List Item; a.k.a. DMA block descriptor */
struct dw_lli {
	/* values that are not changed by hardware */
	u32		sar;
	u32		dar;
	u32		llp;		/* chain to next lli */
	u32		ctllo;
	__dw32		sar;
	__dw32		dar;
	__dw32		llp;		/* chain to next lli */
	__dw32		ctllo;
	/* values that may get written back: */
	u32		ctlhi;
	__dw32		ctlhi;
	/* sstat and dstat can snapshot peripheral register state.
	 * silicon config may discard either or both...
	 */
	u32		sstat;
	u32		dstat;
	__dw32		sstat;
	__dw32		dstat;
};

struct dw_desc {
	/* FIRST values the hardware uses */
	struct dw_lli			lli;

#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO
#define lli_set(d, reg, v)		((d)->lli.reg |= cpu_to_be32(v))
#define lli_clear(d, reg, v)		((d)->lli.reg &= ~cpu_to_be32(v))
#define lli_read(d, reg)		be32_to_cpu((d)->lli.reg)
#define lli_write(d, reg, v)		((d)->lli.reg = cpu_to_be32(v))
#else
#define lli_set(d, reg, v)		((d)->lli.reg |= cpu_to_le32(v))
#define lli_clear(d, reg, v)		((d)->lli.reg &= ~cpu_to_le32(v))
#define lli_read(d, reg)		le32_to_cpu((d)->lli.reg)
#define lli_write(d, reg, v)		((d)->lli.reg = cpu_to_le32(v))
#endif

	/* THEN values for driver housekeeping */
	struct list_head		desc_node;
	struct list_head		tx_list;