Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/dma/dw/dw.c
26282 views
1
// SPDX-License-Identifier: GPL-2.0
2
// Copyright (C) 2007-2008 Atmel Corporation
3
// Copyright (C) 2010-2011 ST Microelectronics
4
// Copyright (C) 2013,2018 Intel Corporation
5
6
#include <linux/bitops.h>
7
#include <linux/dmaengine.h>
8
#include <linux/errno.h>
9
#include <linux/slab.h>
10
#include <linux/types.h>
11
12
#include "internal.h"
13
14
static void dw_dma_initialize_chan(struct dw_dma_chan *dwc)
15
{
16
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
17
u32 cfghi = is_slave_direction(dwc->direction) ? 0 : DWC_CFGH_FIFO_MODE;
18
u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority);
19
bool hs_polarity = dwc->dws.hs_polarity;
20
21
cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id);
22
cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id);
23
cfghi |= DWC_CFGH_PROTCTL(dw->pdata->protctl);
24
25
/* Set polarity of handshake interface */
26
cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0;
27
28
channel_writel(dwc, CFG_LO, cfglo);
29
channel_writel(dwc, CFG_HI, cfghi);
30
}
31
32
static void dw_dma_suspend_chan(struct dw_dma_chan *dwc, bool drain)
33
{
34
u32 cfglo = channel_readl(dwc, CFG_LO);
35
36
channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP);
37
}
38
39
static void dw_dma_resume_chan(struct dw_dma_chan *dwc, bool drain)
40
{
41
u32 cfglo = channel_readl(dwc, CFG_LO);
42
43
channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP);
44
}
45
46
static u32 dw_dma_bytes2block(struct dw_dma_chan *dwc,
47
size_t bytes, unsigned int width, size_t *len)
48
{
49
u32 block;
50
51
if ((bytes >> width) > dwc->block_size) {
52
block = dwc->block_size;
53
*len = dwc->block_size << width;
54
} else {
55
block = bytes >> width;
56
*len = bytes;
57
}
58
59
return block;
60
}
61
62
static size_t dw_dma_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width)
63
{
64
return DWC_CTLH_BLOCK_TS(block) << width;
65
}
66
67
static inline u8 dw_dma_encode_maxburst(u32 maxburst)
68
{
69
/*
70
* Fix burst size according to dw_dmac. We need to convert them as:
71
* 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.
72
*/
73
return maxburst > 1 ? fls(maxburst) - 2 : 0;
74
}
75
76
static u32 dw_dma_prepare_ctllo(struct dw_dma_chan *dwc)
77
{
78
struct dma_slave_config *sconfig = &dwc->dma_sconfig;
79
u8 smsize = 0, dmsize = 0;
80
u8 sms, dms;
81
82
if (dwc->direction == DMA_MEM_TO_DEV) {
83
sms = dwc->dws.m_master;
84
dms = dwc->dws.p_master;
85
dmsize = dw_dma_encode_maxburst(sconfig->dst_maxburst);
86
} else if (dwc->direction == DMA_DEV_TO_MEM) {
87
sms = dwc->dws.p_master;
88
dms = dwc->dws.m_master;
89
smsize = dw_dma_encode_maxburst(sconfig->src_maxburst);
90
} else /* DMA_MEM_TO_MEM */ {
91
sms = dwc->dws.m_master;
92
dms = dwc->dws.m_master;
93
}
94
95
return DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN |
96
DWC_CTLL_DST_MSIZE(dmsize) | DWC_CTLL_SRC_MSIZE(smsize) |
97
DWC_CTLL_DMS(dms) | DWC_CTLL_SMS(sms);
98
}
99
100
static void dw_dma_set_device_name(struct dw_dma *dw, int id)
101
{
102
snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", id);
103
}
104
105
static void dw_dma_disable(struct dw_dma *dw)
106
{
107
do_dw_dma_off(dw);
108
}
109
110
static void dw_dma_enable(struct dw_dma *dw)
111
{
112
do_dw_dma_on(dw);
113
}
114
115
int dw_dma_probe(struct dw_dma_chip *chip)
116
{
117
struct dw_dma *dw;
118
119
dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL);
120
if (!dw)
121
return -ENOMEM;
122
123
/* Channel operations */
124
dw->initialize_chan = dw_dma_initialize_chan;
125
dw->suspend_chan = dw_dma_suspend_chan;
126
dw->resume_chan = dw_dma_resume_chan;
127
dw->prepare_ctllo = dw_dma_prepare_ctllo;
128
dw->bytes2block = dw_dma_bytes2block;
129
dw->block2bytes = dw_dma_block2bytes;
130
131
/* Device operations */
132
dw->set_device_name = dw_dma_set_device_name;
133
dw->disable = dw_dma_disable;
134
dw->enable = dw_dma_enable;
135
136
chip->dw = dw;
137
return do_dma_probe(chip);
138
}
139
EXPORT_SYMBOL_GPL(dw_dma_probe);
140
141
int dw_dma_remove(struct dw_dma_chip *chip)
142
{
143
return do_dma_remove(chip);
144
}
145
EXPORT_SYMBOL_GPL(dw_dma_remove);
146
147