summaryrefslogtreecommitdiffstats
path: root/drivers/dma/dw/dw.c
blob: 156088f768f24bf2d4506a6a54aaeb1b4546f29a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2007-2008 Atmel Corporation
// Copyright (C) 2010-2011 ST Microelectronics
// Copyright (C) 2013,2018 Intel Corporation

#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/types.h>

#include "internal.h"

static void dw_dma_initialize_chan(struct dw_dma_chan *dwc)
{
	struct dw_dma *dw = to_dw_dma(dwc->chan.device);
	u32 cfghi = DWC_CFGH_FIFO_MODE;
	u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority);
	bool hs_polarity = dwc->dws.hs_polarity;

	cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id);
	cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id);
	cfghi |= DWC_CFGH_PROTCTL(dw->pdata->protctl);

	/* Set polarity of handshake interface */
	cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0;

	channel_writel(dwc, CFG_LO, cfglo);
	channel_writel(dwc, CFG_HI, cfghi);
}

static void dw_dma_suspend_chan(struct dw_dma_chan *dwc, bool drain)
{
	u32 cfglo = channel_readl(dwc, CFG_LO);

	channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP);
}

static void dw_dma_resume_chan(struct dw_dma_chan *dwc, bool drain)
{
	u32 cfglo = channel_readl(dwc, CFG_LO);

	channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP);
}

static u32 dw_dma_bytes2block(struct dw_dma_chan *dwc,
			      size_t bytes, unsigned int width, size_t *len)
{
	u32 block;

	if ((bytes >> width) > dwc->block_size) {
		block = dwc->block_size;
		*len = dwc->block_size << width;
	} else {
		block = bytes >> width;
		*len = bytes;
	}

	return block;
}

static size_t dw_dma_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width)
{
	return DWC_CTLH_BLOCK_TS(block) << width;
}

static void dw_dma_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst)
{
	/*
	 * Fix burst size according to dw_dmac. We need to convert them as:
	 * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.
	 */
	*maxburst = *maxburst > 1 ? fls(*maxburst) - 2 : 0;
}

static void dw_dma_set_device_name(struct dw_dma *dw, int id)
{
	snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", id);
}

static void dw_dma_disable(struct dw_dma *dw)
{
	do_dw_dma_off(dw);
}

static void dw_dma_enable(struct dw_dma *dw)
{
	do_dw_dma_on(dw);
}

int dw_dma_probe(struct dw_dma_chip *chip)
{
	struct dw_dma *dw;

	dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL);
	if (!dw)
		return -ENOMEM;

	/* Channel operations */
	dw->initialize_chan = dw_dma_initialize_chan;
	dw->suspend_chan = dw_dma_suspend_chan;
	dw->resume_chan = dw_dma_resume_chan;
	dw->encode_maxburst = dw_dma_encode_maxburst;
	dw->bytes2block = dw_dma_bytes2block;
	dw->block2bytes = dw_dma_block2bytes;

	/* Device operations */
	dw->set_device_name = dw_dma_set_device_name;
	dw->disable = dw_dma_disable;
	dw->enable = dw_dma_enable;

	chip->dw = dw;
	return do_dma_probe(chip);
}
EXPORT_SYMBOL_GPL(dw_dma_probe);

int dw_dma_remove(struct dw_dma_chip *chip)
{
	return do_dma_remove(chip);
}
EXPORT_SYMBOL_GPL(dw_dma_remove);