Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/powerpc/powermac/ata_dbdma.c
39507 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright 2008 by Nathan Whitehorn. All rights reserved.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
* 3. The name of the author may not be used to endorse or promote products
15
* derived from this software without specific prior written permission.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
30
/*
31
* Common routines for the DMA engine on both the Apple Kauai and MacIO
32
* ATA controllers.
33
*/
34
35
#include <sys/param.h>
36
#include <sys/systm.h>
37
#include <sys/kernel.h>
38
#include <sys/module.h>
39
#include <sys/bus.h>
40
#include <sys/malloc.h>
41
#include <sys/sema.h>
42
#include <sys/stdarg.h>
43
#include <sys/taskqueue.h>
44
#include <vm/uma.h>
45
#include <machine/resource.h>
46
#include <machine/bus.h>
47
#include <sys/rman.h>
48
#include <sys/ata.h>
49
#include <dev/ata/ata-all.h>
50
#include <dev/ata/ata-pci.h>
51
#include <ata_if.h>
52
53
#include "ata_dbdma.h"
54
55
struct ata_dbdma_dmaload_args {
56
struct ata_dbdma_channel *sc;
57
58
int write;
59
int nsegs;
60
};
61
62
static void
63
ata_dbdma_setprd(void *xarg, bus_dma_segment_t *segs, int nsegs, int error)
64
{
65
struct ata_dbdma_dmaload_args *arg = xarg;
66
struct ata_dbdma_channel *sc = arg->sc;
67
int branch_type, command;
68
int prev_stop;
69
int i;
70
71
mtx_lock(&sc->dbdma_mtx);
72
73
prev_stop = sc->next_dma_slot-1;
74
if (prev_stop < 0)
75
prev_stop = 0xff;
76
77
for (i = 0; i < nsegs; i++) {
78
/* Loop back to the beginning if this is our last slot */
79
if (sc->next_dma_slot == 0xff)
80
branch_type = DBDMA_ALWAYS;
81
else
82
branch_type = DBDMA_NEVER;
83
84
if (arg->write) {
85
command = (i + 1 < nsegs) ? DBDMA_OUTPUT_MORE :
86
DBDMA_OUTPUT_LAST;
87
} else {
88
command = (i + 1 < nsegs) ? DBDMA_INPUT_MORE :
89
DBDMA_INPUT_LAST;
90
}
91
92
dbdma_insert_command(sc->dbdma, sc->next_dma_slot++,
93
command, 0, segs[i].ds_addr, segs[i].ds_len,
94
DBDMA_NEVER, branch_type, DBDMA_NEVER, 0);
95
96
if (branch_type == DBDMA_ALWAYS)
97
sc->next_dma_slot = 0;
98
}
99
100
/* We have a corner case where the STOP command is the last slot,
101
* but you can't branch in STOP commands. So add a NOP branch here
102
* and the STOP in slot 0. */
103
104
if (sc->next_dma_slot == 0xff) {
105
dbdma_insert_branch(sc->dbdma, sc->next_dma_slot, 0);
106
sc->next_dma_slot = 0;
107
}
108
109
#if 0
110
dbdma_insert_command(sc->dbdma, sc->next_dma_slot++,
111
DBDMA_NOP, 0, 0, 0, DBDMA_ALWAYS, DBDMA_NEVER, DBDMA_NEVER, 0);
112
#endif
113
dbdma_insert_stop(sc->dbdma, sc->next_dma_slot++);
114
dbdma_insert_nop(sc->dbdma, prev_stop);
115
116
dbdma_sync_commands(sc->dbdma, BUS_DMASYNC_PREWRITE);
117
118
mtx_unlock(&sc->dbdma_mtx);
119
120
arg->nsegs = nsegs;
121
}
122
123
static int
124
ata_dbdma_status(device_t dev)
125
{
126
struct ata_dbdma_channel *sc = device_get_softc(dev);
127
struct ata_channel *ch = device_get_softc(dev);
128
129
if (sc->sc_ch.dma.flags & ATA_DMA_ACTIVE) {
130
return (!(dbdma_get_chan_status(sc->dbdma) &
131
DBDMA_STATUS_ACTIVE));
132
}
133
134
if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
135
DELAY(100);
136
if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY)
137
return 0;
138
}
139
return 1;
140
}
141
142
static int
143
ata_dbdma_start(struct ata_request *request)
144
{
145
struct ata_dbdma_channel *sc = device_get_softc(request->parent);
146
147
sc->sc_ch.dma.flags |= ATA_DMA_ACTIVE;
148
dbdma_wake(sc->dbdma);
149
return 0;
150
}
151
152
static void
153
ata_dbdma_reset(device_t dev)
154
{
155
struct ata_dbdma_channel *sc = device_get_softc(dev);
156
157
mtx_lock(&sc->dbdma_mtx);
158
159
dbdma_stop(sc->dbdma);
160
dbdma_insert_stop(sc->dbdma, 0);
161
sc->next_dma_slot=1;
162
dbdma_set_current_cmd(sc->dbdma, 0);
163
164
sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE;
165
166
mtx_unlock(&sc->dbdma_mtx);
167
}
168
169
static int
170
ata_dbdma_stop(struct ata_request *request)
171
{
172
struct ata_dbdma_channel *sc = device_get_softc(request->parent);
173
174
uint16_t status;
175
176
status = dbdma_get_chan_status(sc->dbdma);
177
178
dbdma_pause(sc->dbdma);
179
sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE;
180
181
if (status & DBDMA_STATUS_DEAD) {
182
device_printf(request->parent,"DBDMA dead, resetting "
183
"channel...\n");
184
ata_dbdma_reset(request->parent);
185
return ATA_S_ERROR;
186
}
187
188
if (!(status & DBDMA_STATUS_RUN)) {
189
device_printf(request->parent,"DBDMA confused, stop called "
190
"when channel is not running!\n");
191
return ATA_S_ERROR;
192
}
193
194
if (status & DBDMA_STATUS_ACTIVE) {
195
device_printf(request->parent,"DBDMA channel stopped "
196
"prematurely\n");
197
return ATA_S_ERROR;
198
}
199
return 0;
200
}
201
202
static int
203
ata_dbdma_load(struct ata_request *request, void *addr, int *entries)
204
{
205
struct ata_channel *ch = device_get_softc(request->parent);
206
struct ata_dbdma_dmaload_args args;
207
208
int error;
209
210
args.sc = device_get_softc(request->parent);
211
args.write = !(request->flags & ATA_R_READ);
212
213
if (!request->bytecount) {
214
device_printf(request->dev,
215
"FAILURE - zero length DMA transfer attempted\n");
216
return EIO;
217
}
218
if (((uintptr_t)(request->data) & (ch->dma.alignment - 1)) ||
219
(request->bytecount & (ch->dma.alignment - 1))) {
220
device_printf(request->dev,
221
"FAILURE - non aligned DMA transfer attempted\n");
222
return EIO;
223
}
224
if (request->bytecount > ch->dma.max_iosize) {
225
device_printf(request->dev,
226
"FAILURE - oversized DMA transfer attempt %d > %d\n",
227
request->bytecount, ch->dma.max_iosize);
228
return EIO;
229
}
230
231
request->dma = &ch->dma.slot[0];
232
233
if ((error = bus_dmamap_load(request->dma->data_tag,
234
request->dma->data_map, request->data, request->bytecount,
235
&ata_dbdma_setprd, &args, BUS_DMA_NOWAIT))) {
236
device_printf(request->dev, "FAILURE - load data\n");
237
goto error;
238
}
239
240
if (entries)
241
*entries = args.nsegs;
242
243
bus_dmamap_sync(request->dma->sg_tag, request->dma->sg_map,
244
BUS_DMASYNC_PREWRITE);
245
bus_dmamap_sync(request->dma->data_tag, request->dma->data_map,
246
(request->flags & ATA_R_READ) ?
247
BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
248
249
return 0;
250
251
error:
252
ch->dma.unload(request);
253
return EIO;
254
}
255
256
void
257
ata_dbdma_dmainit(device_t dev)
258
{
259
struct ata_dbdma_channel *sc = device_get_softc(dev);
260
261
dbdma_allocate_channel(sc->dbdma_regs, sc->dbdma_offset,
262
bus_get_dma_tag(dev), 256, &sc->dbdma);
263
264
dbdma_set_wait_selector(sc->dbdma,1 << 7, 1 << 7);
265
266
dbdma_insert_stop(sc->dbdma,0);
267
sc->next_dma_slot=1;
268
269
sc->sc_ch.dma.start = ata_dbdma_start;
270
sc->sc_ch.dma.stop = ata_dbdma_stop;
271
sc->sc_ch.dma.load = ata_dbdma_load;
272
sc->sc_ch.dma.reset = ata_dbdma_reset;
273
274
/*
275
* DBDMA's field for transfer size is 16 bits. This will overflow
276
* if we try to do a 64K transfer, so stop short of 64K.
277
*/
278
sc->sc_ch.dma.segsize = 126 * DEV_BSIZE;
279
ata_dmainit(dev);
280
281
sc->sc_ch.hw.status = ata_dbdma_status;
282
283
mtx_init(&sc->dbdma_mtx, "ATA DBDMA", NULL, MTX_DEF);
284
}
285
286