dbdma.c revision 183411
1179644Smarcel/*-
2179644Smarcel * Copyright (c) 2008 Nathan Whitehorn
3179644Smarcel * All rights reserved
4179644Smarcel *
5179644Smarcel * Redistribution and use in source and binary forms, with or without
6179644Smarcel * modification, are permitted provided that the following conditions
7179644Smarcel * are met:
8179644Smarcel * 1. Redistributions of source code must retain the above copyright
9179644Smarcel *    notice, this list of conditions and the following disclaimer.
10179644Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11179644Smarcel *    notice, this list of conditions and the following disclaimer in the
12179644Smarcel *    documentation and/or other materials provided with the distribution.
13179644Smarcel *
14179644Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15179644Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16179644Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17179644Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18179644Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19179644Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20179644Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21179644Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22179644Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23179644Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24179644Smarcel * SUCH DAMAGE.
25179644Smarcel */
26179644Smarcel
27179644Smarcel#include <sys/cdefs.h>
28179644Smarcel__FBSDID("$FreeBSD: head/sys/powerpc/powermac/dbdma.c 183411 2008-09-27 15:41:16Z nwhitehorn $");
29179644Smarcel
30179644Smarcel#include <sys/param.h>
31179644Smarcel#include <sys/systm.h>
32179644Smarcel#include <sys/kernel.h>
33179644Smarcel#include <sys/malloc.h>
34179644Smarcel#include <sys/module.h>
35179644Smarcel#include <sys/endian.h>
36179644Smarcel#include <sys/bus.h>
37179644Smarcel#include <machine/bus.h>
38179644Smarcel#include <machine/dbdma.h>
39179644Smarcel#include <sys/rman.h>
40179644Smarcel
41179644Smarcel#include "dbdmavar.h"
42179644Smarcel
43179644SmarcelMALLOC_DEFINE(M_DBDMA, "dbdma", "DBDMA Command List");
44179644Smarcel
45179644Smarcelstatic uint32_t dbdma_read_reg(dbdma_channel_t *, u_int);
46179644Smarcelstatic void dbdma_write_reg(dbdma_channel_t *, u_int, uint32_t);
47179644Smarcelstatic void dbdma_phys_callback(void *, bus_dma_segment_t *, int, int);
48179644Smarcel
49179644Smarcelstatic void
50179644Smarceldbdma_phys_callback(void *chan, bus_dma_segment_t *segs, int nsegs, int error)
51179644Smarcel{
52179644Smarcel	dbdma_channel_t *channel = (dbdma_channel_t *)(chan);
53179644Smarcel
54179644Smarcel	channel->sc_slots_pa = segs[0].ds_addr;
55179644Smarcel	dbdma_write_reg(channel, CHAN_CMDPTR, channel->sc_slots_pa);
56179644Smarcel}
57179644Smarcel
58179644Smarcelint
59183288Snwhitehorndbdma_allocate_channel(struct resource *dbdma_regs, u_int offset,
60183288Snwhitehorn    bus_dma_tag_t parent_dma, int slots, dbdma_channel_t **chan)
61179644Smarcel{
62179644Smarcel	int error = 0;
63179644Smarcel	dbdma_channel_t *channel;
64179644Smarcel
65179644Smarcel	channel = *chan = malloc(sizeof(struct dbdma_channel), M_DBDMA,
66179644Smarcel	    M_WAITOK | M_ZERO);
67179644Smarcel
68183288Snwhitehorn	channel->sc_regs = dbdma_regs;
69183288Snwhitehorn	channel->sc_off = offset;
70179644Smarcel	dbdma_stop(channel);
71179644Smarcel
72179644Smarcel	channel->sc_slots_pa = 0;
73179644Smarcel
74179644Smarcel	error = bus_dma_tag_create(parent_dma, 16, 0, BUS_SPACE_MAXADDR_32BIT,
75179644Smarcel	    BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL,
76179644Smarcel	    NULL, &(channel->sc_dmatag));
77179644Smarcel
78179644Smarcel	error = bus_dmamem_alloc(channel->sc_dmatag,
79179644Smarcel	    (void **)&channel->sc_slots, BUS_DMA_WAITOK | BUS_DMA_ZERO,
80179644Smarcel	    &channel->sc_dmamap);
81179644Smarcel
82179644Smarcel	error = bus_dmamap_load(channel->sc_dmatag, channel->sc_dmamap,
83179644Smarcel	    channel->sc_slots, PAGE_SIZE, dbdma_phys_callback, channel, 0);
84179644Smarcel
85183288Snwhitehorn	dbdma_write_reg(channel, CHAN_CMDPTR_HI, 0);
86183288Snwhitehorn
87179644Smarcel	channel->sc_nslots = slots;
88179644Smarcel
89179644Smarcel	return (error);
90179644Smarcel}
91179644Smarcel
92179644Smarcelint
93179644Smarceldbdma_resize_channel(dbdma_channel_t *chan, int newslots)
94179644Smarcel{
95179644Smarcel
96179644Smarcel	if (newslots > (PAGE_SIZE / 16))
97179644Smarcel		return (-1);
98179644Smarcel
99179644Smarcel	chan->sc_nslots = newslots;
100179644Smarcel	return (0);
101179644Smarcel}
102179644Smarcel
103179644Smarcelint
104179644Smarceldbdma_free_channel(dbdma_channel_t *chan)
105179644Smarcel{
106179644Smarcel
107179644Smarcel	dbdma_stop(chan);
108179644Smarcel
109179644Smarcel	bus_dmamem_free(chan->sc_dmatag, chan->sc_slots, chan->sc_dmamap);
110179644Smarcel	bus_dma_tag_destroy(chan->sc_dmatag);
111179644Smarcel
112179644Smarcel	free(chan, M_DBDMA);
113179644Smarcel
114179644Smarcel	return (0);
115179644Smarcel}
116179644Smarcel
117179644Smarceluint16_t
118179644Smarceldbdma_get_cmd_status(dbdma_channel_t *chan, int slot)
119179644Smarcel{
120179644Smarcel
121179644Smarcel	bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, BUS_DMASYNC_POSTREAD);
122179644Smarcel
123179644Smarcel	/*
124179644Smarcel	 * I really did mean to swap resCount and xferStatus here, to
125179644Smarcel	 * account for the quad-word little endian fields.
126179644Smarcel	 */
127179644Smarcel	return (le16toh(chan->sc_slots[slot].resCount));
128179644Smarcel}
129179644Smarcel
130183411Snwhitehornvoid
131183411Snwhitehorndbdma_clear_cmd_status(dbdma_channel_t *chan, int slot)
132183411Snwhitehorn{
133183411Snwhitehorn	/* See endian note above */
134183411Snwhitehorn	chan->sc_slots[slot].resCount = 0;
135183411Snwhitehorn}
136183411Snwhitehorn
137179644Smarceluint16_t
138179644Smarceldbdma_get_residuals(dbdma_channel_t *chan, int slot)
139179644Smarcel{
140179644Smarcel
141179644Smarcel	bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, BUS_DMASYNC_POSTREAD);
142179644Smarcel
143179644Smarcel	return (le16toh(chan->sc_slots[slot].xferStatus));
144179644Smarcel}
145179644Smarcel
146179644Smarcelvoid
147179644Smarceldbdma_reset(dbdma_channel_t *chan)
148179644Smarcel{
149179644Smarcel
150179644Smarcel	dbdma_stop(chan);
151179644Smarcel	dbdma_set_current_cmd(chan, 0);
152179644Smarcel	dbdma_run(chan);
153179644Smarcel}
154179644Smarcel
155179644Smarcelvoid
156179644Smarceldbdma_run(dbdma_channel_t *chan)
157179644Smarcel{
158179644Smarcel	uint32_t control_reg;
159179644Smarcel
160179644Smarcel	control_reg = DBDMA_STATUS_RUN | DBDMA_STATUS_PAUSE |
161179644Smarcel	    DBDMA_STATUS_WAKE | DBDMA_STATUS_DEAD;
162179644Smarcel	control_reg <<= 16;
163179644Smarcel	control_reg |= DBDMA_STATUS_RUN;
164179644Smarcel	dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
165179644Smarcel}
166179644Smarcel
167179644Smarcelvoid
168179644Smarceldbdma_pause(dbdma_channel_t *chan)
169179644Smarcel{
170179644Smarcel	uint32_t control_reg;
171179644Smarcel
172179644Smarcel	control_reg = DBDMA_STATUS_PAUSE;
173179644Smarcel	control_reg <<= 16;
174179644Smarcel	control_reg |= DBDMA_STATUS_PAUSE;
175179644Smarcel	dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
176179644Smarcel}
177179644Smarcel
178179644Smarcelvoid
179179644Smarceldbdma_wake(dbdma_channel_t *chan)
180179644Smarcel{
181179644Smarcel	uint32_t control_reg;
182179644Smarcel
183179644Smarcel	control_reg = DBDMA_STATUS_WAKE | DBDMA_STATUS_PAUSE |
184179644Smarcel	    DBDMA_STATUS_RUN | DBDMA_STATUS_DEAD;
185179644Smarcel	control_reg <<= 16;
186179644Smarcel	control_reg |= DBDMA_STATUS_WAKE | DBDMA_STATUS_RUN;
187179644Smarcel	dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
188179644Smarcel}
189179644Smarcel
190179644Smarcelvoid
191179644Smarceldbdma_stop(dbdma_channel_t *chan)
192179644Smarcel{
193179644Smarcel	uint32_t control_reg;
194179644Smarcel
195179644Smarcel	control_reg = DBDMA_STATUS_RUN;
196179644Smarcel	control_reg <<= 16;
197179644Smarcel	dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
198179644Smarcel
199179644Smarcel	while (dbdma_read_reg(chan, CHAN_STATUS_REG) & DBDMA_STATUS_ACTIVE)
200179644Smarcel		DELAY(5);
201179644Smarcel}
202179644Smarcel
203179644Smarcelvoid
204179644Smarceldbdma_set_current_cmd(dbdma_channel_t *chan, int slot)
205179644Smarcel{
206179644Smarcel	uint32_t cmd;
207179644Smarcel
208179644Smarcel	cmd = chan->sc_slots_pa + slot * 16;
209179644Smarcel	dbdma_write_reg(chan, CHAN_CMDPTR, cmd);
210179644Smarcel}
211179644Smarcel
212179644Smarceluint16_t
213179644Smarceldbdma_get_chan_status(dbdma_channel_t *chan)
214179644Smarcel{
215179644Smarcel	uint32_t status_reg;
216179644Smarcel
217179644Smarcel	status_reg = dbdma_read_reg(chan, CHAN_STATUS_REG);
218179644Smarcel	return (status_reg & 0x0000ffff);
219179644Smarcel}
220179644Smarcel
221179644Smarceluint8_t
222183411Snwhitehorndbdma_get_device_status(dbdma_channel_t *chan)
223179644Smarcel{
224179644Smarcel	return (dbdma_get_chan_status(chan) & 0x00ff);
225179644Smarcel}
226179644Smarcel
227179644Smarcelvoid
228183411Snwhitehorndbdma_set_device_status(dbdma_channel_t *chan, uint8_t mask, uint8_t value)
229183411Snwhitehorn{
230183411Snwhitehorn	uint32_t control_reg;
231183411Snwhitehorn
232183411Snwhitehorn	control_reg = mask;
233183411Snwhitehorn	control_reg <<= 16;
234183411Snwhitehorn	control_reg |= value;
235183411Snwhitehorn
236183411Snwhitehorn	dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
237183411Snwhitehorn}
238183411Snwhitehorn
239183411Snwhitehornvoid
240179644Smarceldbdma_set_interrupt_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val)
241179644Smarcel{
242179644Smarcel	uint32_t intr_select;
243179644Smarcel
244179644Smarcel	intr_select = mask;
245179644Smarcel	intr_select <<= 16;
246179644Smarcel	intr_select |= val;
247179644Smarcel	dbdma_write_reg(chan, CHAN_INTR_SELECT, intr_select);
248179644Smarcel}
249179644Smarcel
250179644Smarcelvoid
251179644Smarceldbdma_set_branch_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val)
252179644Smarcel{
253179644Smarcel	uint32_t br_select;
254179644Smarcel
255179644Smarcel	br_select = mask;
256179644Smarcel	br_select <<= 16;
257179644Smarcel	br_select |= val;
258179644Smarcel	dbdma_write_reg(chan, CHAN_BRANCH_SELECT, br_select);
259179644Smarcel}
260179644Smarcel
261179644Smarcelvoid
262179644Smarceldbdma_set_wait_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val)
263179644Smarcel{
264179644Smarcel	uint32_t wait_select;
265179644Smarcel
266179644Smarcel	wait_select = mask;
267179644Smarcel	wait_select <<= 16;
268179644Smarcel	wait_select |= val;
269179644Smarcel	dbdma_write_reg(chan, CHAN_WAIT_SELECT, wait_select);
270179644Smarcel}
271179644Smarcel
272179644Smarcelvoid
273179644Smarceldbdma_insert_command(dbdma_channel_t *chan, int slot, int command, int stream,
274179644Smarcel    bus_addr_t data, size_t count, uint8_t interrupt, uint8_t branch,
275179644Smarcel    uint8_t wait, uint32_t branch_slot)
276179644Smarcel{
277179644Smarcel	struct dbdma_command cmd;
278179644Smarcel	uint32_t *flip;
279179644Smarcel
280179644Smarcel	cmd.cmd = command;
281179644Smarcel	cmd.key = stream;
282179644Smarcel	cmd.intr = interrupt;
283179644Smarcel	cmd.branch = branch;
284179644Smarcel	cmd.wait = wait;
285179644Smarcel
286179644Smarcel	cmd.reqCount = count;
287179644Smarcel	cmd.address = (uint32_t)(data);
288179644Smarcel	if (command != DBDMA_STORE_QUAD && command != DBDMA_LOAD_QUAD)
289179644Smarcel		cmd.cmdDep = chan->sc_slots_pa + branch_slot * 16;
290179644Smarcel	else
291179644Smarcel		cmd.cmdDep = branch_slot;
292179644Smarcel
293179644Smarcel	cmd.resCount = 0;
294179644Smarcel	cmd.xferStatus = 0;
295179644Smarcel
296179644Smarcel	/*
297179644Smarcel	 * Move quadwords to little-endian. God only knows why
298179644Smarcel	 * Apple thought this was a good idea.
299179644Smarcel	 */
300179644Smarcel	flip = (uint32_t *)(&cmd);
301179644Smarcel	flip[0] = htole32(flip[0]);
302179644Smarcel	flip[1] = htole32(flip[1]);
303179644Smarcel	flip[2] = htole32(flip[2]);
304179644Smarcel
305179644Smarcel	chan->sc_slots[slot] = cmd;
306179644Smarcel}
307179644Smarcel
308179644Smarcelvoid
309179644Smarceldbdma_insert_stop(dbdma_channel_t *chan, int slot)
310179644Smarcel{
311179644Smarcel
312179644Smarcel	dbdma_insert_command(chan, slot, DBDMA_STOP, 0, 0, 0, DBDMA_NEVER,
313179644Smarcel	    DBDMA_NEVER, DBDMA_NEVER, 0);
314179644Smarcel}
315179644Smarcel
316179644Smarcelvoid
317179644Smarceldbdma_insert_nop(dbdma_channel_t *chan, int slot)
318179644Smarcel{
319179644Smarcel
320179644Smarcel	dbdma_insert_command(chan, slot, DBDMA_NOP, 0, 0, 0, DBDMA_NEVER,
321179644Smarcel	    DBDMA_NEVER, DBDMA_NEVER, 0);
322179644Smarcel}
323179644Smarcel
324179644Smarcelvoid
325179644Smarceldbdma_insert_branch(dbdma_channel_t *chan, int slot, int to_slot)
326179644Smarcel{
327179644Smarcel
328179644Smarcel	dbdma_insert_command(chan, slot, DBDMA_NOP, 0, 0, 0, DBDMA_NEVER,
329179644Smarcel	    DBDMA_ALWAYS, DBDMA_NEVER, to_slot);
330179644Smarcel}
331179644Smarcel
332179644Smarcelvoid
333179644Smarceldbdma_sync_commands(dbdma_channel_t *chan, bus_dmasync_op_t op)
334179644Smarcel{
335179644Smarcel
336179644Smarcel	bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, op);
337179644Smarcel}
338179644Smarcel
339179644Smarcelstatic uint32_t
340179644Smarceldbdma_read_reg(dbdma_channel_t *chan, u_int offset)
341179644Smarcel{
342179644Smarcel
343183288Snwhitehorn	return (bus_read_4(chan->sc_regs, chan->sc_off + offset));
344179644Smarcel}
345179644Smarcel
346179644Smarcelstatic void
347179644Smarceldbdma_write_reg(dbdma_channel_t *chan, u_int offset, uint32_t val)
348179644Smarcel{
349179644Smarcel
350183288Snwhitehorn	bus_write_4(chan->sc_regs, chan->sc_off + offset, val);
351179644Smarcel}
352