summaryrefslogtreecommitdiff
path: root/plat/nvidia/tegra/common/drivers/gpcdma/gpcdma.c
blob: 64e84acc72d5f745625df133a5d0ed6d5b90098b (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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
 * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <arch_helpers.h>
#include <common/debug.h>
#include <delay_timer.h>
#include <errno.h>
#include <gpcdma.h>
#include <mmio.h>
#include <platform_def.h>
#include <stdbool.h>
#include <tegra_def.h>
#include <utils_def.h>

/* DMA channel registers */
#define DMA_CH_CSR				U(0x0)
#define DMA_CH_CSR_WEIGHT_SHIFT			U(10)
#define DMA_CH_CSR_XFER_MODE_SHIFT		U(21)
#define DMA_CH_CSR_DMA_MODE_MEM2MEM		U(4)
#define DMA_CH_CSR_DMA_MODE_FIXEDPATTERN	U(6)
#define DMA_CH_CSR_IRQ_MASK_ENABLE		(U(1) << 15)
#define DMA_CH_CSR_RUN_ONCE			(U(1) << 27)
#define DMA_CH_CSR_ENABLE			(U(1) << 31)

#define DMA_CH_STAT				U(0x4)
#define DMA_CH_STAT_BUSY			(U(1) << 31)

#define DMA_CH_SRC_PTR				U(0xC)

#define DMA_CH_DST_PTR				U(0x10)

#define DMA_CH_HI_ADR_PTR			U(0x14)
#define DMA_CH_HI_ADR_PTR_SRC_MASK		U(0xFF)
#define DMA_CH_HI_ADR_PTR_DST_SHIFT		U(16)
#define DMA_CH_HI_ADR_PTR_DST_MASK		U(0xFF)

#define DMA_CH_MC_SEQ				U(0x18)
#define DMA_CH_MC_SEQ_REQ_CNT_SHIFT		U(25)
#define DMA_CH_MC_SEQ_REQ_CNT_VAL		U(0x10)
#define DMA_CH_MC_SEQ_BURST_SHIFT		U(23)
#define DMA_CH_MC_SEQ_BURST_16_WORDS		U(0x3)

#define DMA_CH_WORD_COUNT			U(0x20)
#define DMA_CH_FIXED_PATTERN			U(0x34)
#define DMA_CH_TZ				U(0x38)
#define DMA_CH_TZ_ACCESS_ENABLE			U(0)
#define DMA_CH_TZ_ACCESS_DISABLE		U(3)

#define MAX_TRANSFER_SIZE			(1U*1024U*1024U*1024U)	/* 1GB */
#define GPCDMA_TIMEOUT_MS			U(100)
#define GPCDMA_RESET_BIT			(U(1) << 1)

static bool init_done;

static void tegra_gpcdma_write32(uint32_t offset, uint32_t val)
{
	mmio_write_32(TEGRA_GPCDMA_BASE + offset, val);
}

static uint32_t tegra_gpcdma_read32(uint32_t offset)
{
	return mmio_read_32(TEGRA_GPCDMA_BASE + offset);
}

static void tegra_gpcdma_init(void)
{
	/* assert reset for DMA engine */
	mmio_write_32(TEGRA_CAR_RESET_BASE + TEGRA_GPCDMA_RST_SET_REG_OFFSET,
		      GPCDMA_RESET_BIT);

	udelay(2);

	/* de-assert reset for DMA engine */
	mmio_write_32(TEGRA_CAR_RESET_BASE + TEGRA_GPCDMA_RST_CLR_REG_OFFSET,
		      GPCDMA_RESET_BIT);
}

static void tegra_gpcdma_memcpy_priv(uint64_t dst_addr, uint64_t src_addr,
				     uint32_t num_bytes, uint32_t mode)
{
	uint32_t val, timeout = 0;
	int32_t ret = 0;

	/* sanity check byte count */
	if ((num_bytes > MAX_TRANSFER_SIZE) || ((num_bytes & 0x3U) != U(0))) {
		ret = -EINVAL;
	}

	/* initialise GPCDMA block */
	if (!init_done) {
		tegra_gpcdma_init();
		init_done = true;
	}

	/* make sure channel isn't busy */
	val = tegra_gpcdma_read32(DMA_CH_STAT);
	if ((val & DMA_CH_STAT_BUSY) == DMA_CH_STAT_BUSY) {
		ERROR("DMA channel is busy\n");
		ret = -EBUSY;
	}

	if (ret == 0) {

		/* disable any DMA transfers */
		tegra_gpcdma_write32(DMA_CH_CSR, 0);

		/* enable DMA access to TZDRAM */
		tegra_gpcdma_write32(DMA_CH_TZ, DMA_CH_TZ_ACCESS_ENABLE);

		/* configure MC sequencer */
		val = (DMA_CH_MC_SEQ_REQ_CNT_VAL << DMA_CH_MC_SEQ_REQ_CNT_SHIFT) |
		      (DMA_CH_MC_SEQ_BURST_16_WORDS << DMA_CH_MC_SEQ_BURST_SHIFT);
		tegra_gpcdma_write32(DMA_CH_MC_SEQ, val);

		/* reset fixed pattern */
		tegra_gpcdma_write32(DMA_CH_FIXED_PATTERN, 0);

		/* populate src and dst address registers */
		tegra_gpcdma_write32(DMA_CH_SRC_PTR, (uint32_t)src_addr);
		tegra_gpcdma_write32(DMA_CH_DST_PTR, (uint32_t)dst_addr);

		val = (uint32_t)((src_addr >> 32) & DMA_CH_HI_ADR_PTR_SRC_MASK);
		val |= (uint32_t)(((dst_addr >> 32) & DMA_CH_HI_ADR_PTR_DST_MASK) <<
			DMA_CH_HI_ADR_PTR_DST_SHIFT);
		tegra_gpcdma_write32(DMA_CH_HI_ADR_PTR, val);

		/* transfer size (in words) */
		tegra_gpcdma_write32(DMA_CH_WORD_COUNT, ((num_bytes >> 2) - 1U));

		/* populate value for CSR */
		val = (mode << DMA_CH_CSR_XFER_MODE_SHIFT) |
		      DMA_CH_CSR_RUN_ONCE | (U(1) << DMA_CH_CSR_WEIGHT_SHIFT) |
		      DMA_CH_CSR_IRQ_MASK_ENABLE;
		tegra_gpcdma_write32(DMA_CH_CSR, val);

		/* enable transfer */
		val = tegra_gpcdma_read32(DMA_CH_CSR);
		val |= DMA_CH_CSR_ENABLE;
		tegra_gpcdma_write32(DMA_CH_CSR, val);

		/* wait till transfer completes */
		do {

			/* read the status */
			val = tegra_gpcdma_read32(DMA_CH_STAT);
			if ((val & DMA_CH_STAT_BUSY) != DMA_CH_STAT_BUSY) {
				break;
			}

			mdelay(1);
			timeout++;

		} while (timeout < GPCDMA_TIMEOUT_MS);

		/* flag timeout error */
		if (timeout == GPCDMA_TIMEOUT_MS) {
			ERROR("DMA transfer timed out\n");
		}

		dsbsy();

		/* disable DMA access to TZDRAM */
		tegra_gpcdma_write32(DMA_CH_TZ, DMA_CH_TZ_ACCESS_DISABLE);
		isb();
	}
}

/*******************************************************************************
 * Memcpy using GPCDMA block (Mem2Mem copy)
 ******************************************************************************/
void tegra_gpcdma_memcpy(uint64_t dst_addr, uint64_t src_addr,
			 uint32_t num_bytes)
{
	tegra_gpcdma_memcpy_priv(dst_addr, src_addr, num_bytes,
				 DMA_CH_CSR_DMA_MODE_MEM2MEM);
}

/*******************************************************************************
 * Memset using GPCDMA block (Fixed pattern write)
 ******************************************************************************/
void tegra_gpcdma_zeromem(uint64_t dst_addr, uint32_t num_bytes)
{
	tegra_gpcdma_memcpy_priv(dst_addr, 0, num_bytes,
				 DMA_CH_CSR_DMA_MODE_FIXEDPATTERN);
}