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$");
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
43227293Sedstatic MALLOC_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
96184382Snwhitehorn	if (newslots > (PAGE_SIZE / sizeof(struct dbdma_command)))
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;
162184382Snwhitehorn	control_reg <<= DBDMA_REG_MASK_SHIFT;
163184382Snwhitehorn
164179644Smarcel	control_reg |= DBDMA_STATUS_RUN;
165179644Smarcel	dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
166179644Smarcel}
167179644Smarcel
168179644Smarcelvoid
169179644Smarceldbdma_pause(dbdma_channel_t *chan)
170179644Smarcel{
171179644Smarcel	uint32_t control_reg;
172179644Smarcel
173179644Smarcel	control_reg = DBDMA_STATUS_PAUSE;
174184382Snwhitehorn	control_reg <<= DBDMA_REG_MASK_SHIFT;
175184382Snwhitehorn
176179644Smarcel	control_reg |= DBDMA_STATUS_PAUSE;
177179644Smarcel	dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
178179644Smarcel}
179179644Smarcel
180179644Smarcelvoid
181179644Smarceldbdma_wake(dbdma_channel_t *chan)
182179644Smarcel{
183179644Smarcel	uint32_t control_reg;
184179644Smarcel
185179644Smarcel	control_reg = DBDMA_STATUS_WAKE | DBDMA_STATUS_PAUSE |
186179644Smarcel	    DBDMA_STATUS_RUN | DBDMA_STATUS_DEAD;
187184382Snwhitehorn	control_reg <<= DBDMA_REG_MASK_SHIFT;
188184382Snwhitehorn
189179644Smarcel	control_reg |= DBDMA_STATUS_WAKE | DBDMA_STATUS_RUN;
190179644Smarcel	dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
191179644Smarcel}
192179644Smarcel
193179644Smarcelvoid
194179644Smarceldbdma_stop(dbdma_channel_t *chan)
195179644Smarcel{
196179644Smarcel	uint32_t control_reg;
197179644Smarcel
198179644Smarcel	control_reg = DBDMA_STATUS_RUN;
199184382Snwhitehorn	control_reg <<= DBDMA_REG_MASK_SHIFT;
200184382Snwhitehorn
201179644Smarcel	dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
202179644Smarcel
203179644Smarcel	while (dbdma_read_reg(chan, CHAN_STATUS_REG) & DBDMA_STATUS_ACTIVE)
204179644Smarcel		DELAY(5);
205179644Smarcel}
206179644Smarcel
207179644Smarcelvoid
208179644Smarceldbdma_set_current_cmd(dbdma_channel_t *chan, int slot)
209179644Smarcel{
210179644Smarcel	uint32_t cmd;
211179644Smarcel
212184382Snwhitehorn	cmd = chan->sc_slots_pa + slot * sizeof(struct dbdma_command);
213179644Smarcel	dbdma_write_reg(chan, CHAN_CMDPTR, cmd);
214179644Smarcel}
215179644Smarcel
216179644Smarceluint16_t
217179644Smarceldbdma_get_chan_status(dbdma_channel_t *chan)
218179644Smarcel{
219179644Smarcel	uint32_t status_reg;
220179644Smarcel
221179644Smarcel	status_reg = dbdma_read_reg(chan, CHAN_STATUS_REG);
222179644Smarcel	return (status_reg & 0x0000ffff);
223179644Smarcel}
224179644Smarcel
225179644Smarceluint8_t
226183411Snwhitehorndbdma_get_device_status(dbdma_channel_t *chan)
227179644Smarcel{
228179644Smarcel	return (dbdma_get_chan_status(chan) & 0x00ff);
229179644Smarcel}
230179644Smarcel
231179644Smarcelvoid
232183411Snwhitehorndbdma_set_device_status(dbdma_channel_t *chan, uint8_t mask, uint8_t value)
233183411Snwhitehorn{
234183411Snwhitehorn	uint32_t control_reg;
235183411Snwhitehorn
236183411Snwhitehorn	control_reg = mask;
237184382Snwhitehorn	control_reg <<= DBDMA_REG_MASK_SHIFT;
238183411Snwhitehorn	control_reg |= value;
239183411Snwhitehorn
240183411Snwhitehorn	dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
241183411Snwhitehorn}
242183411Snwhitehorn
243183411Snwhitehornvoid
244179644Smarceldbdma_set_interrupt_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val)
245179644Smarcel{
246179644Smarcel	uint32_t intr_select;
247179644Smarcel
248179644Smarcel	intr_select = mask;
249184382Snwhitehorn	intr_select <<= DBDMA_REG_MASK_SHIFT;
250184382Snwhitehorn
251179644Smarcel	intr_select |= val;
252179644Smarcel	dbdma_write_reg(chan, CHAN_INTR_SELECT, intr_select);
253179644Smarcel}
254179644Smarcel
255179644Smarcelvoid
256179644Smarceldbdma_set_branch_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val)
257179644Smarcel{
258179644Smarcel	uint32_t br_select;
259179644Smarcel
260179644Smarcel	br_select = mask;
261184382Snwhitehorn	br_select <<= DBDMA_REG_MASK_SHIFT;
262184382Snwhitehorn
263179644Smarcel	br_select |= val;
264179644Smarcel	dbdma_write_reg(chan, CHAN_BRANCH_SELECT, br_select);
265179644Smarcel}
266179644Smarcel
267179644Smarcelvoid
268179644Smarceldbdma_set_wait_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val)
269179644Smarcel{
270179644Smarcel	uint32_t wait_select;
271179644Smarcel
272179644Smarcel	wait_select = mask;
273184382Snwhitehorn	wait_select <<= DBDMA_REG_MASK_SHIFT;
274179644Smarcel	wait_select |= val;
275179644Smarcel	dbdma_write_reg(chan, CHAN_WAIT_SELECT, wait_select);
276179644Smarcel}
277179644Smarcel
278179644Smarcelvoid
279179644Smarceldbdma_insert_command(dbdma_channel_t *chan, int slot, int command, int stream,
280179644Smarcel    bus_addr_t data, size_t count, uint8_t interrupt, uint8_t branch,
281179644Smarcel    uint8_t wait, uint32_t branch_slot)
282179644Smarcel{
283179644Smarcel	struct dbdma_command cmd;
284179644Smarcel	uint32_t *flip;
285179644Smarcel
286179644Smarcel	cmd.cmd = command;
287179644Smarcel	cmd.key = stream;
288179644Smarcel	cmd.intr = interrupt;
289179644Smarcel	cmd.branch = branch;
290179644Smarcel	cmd.wait = wait;
291179644Smarcel
292179644Smarcel	cmd.reqCount = count;
293179644Smarcel	cmd.address = (uint32_t)(data);
294179644Smarcel	if (command != DBDMA_STORE_QUAD && command != DBDMA_LOAD_QUAD)
295184382Snwhitehorn		cmd.cmdDep = chan->sc_slots_pa +
296184382Snwhitehorn		    branch_slot * sizeof(struct dbdma_command);
297179644Smarcel	else
298179644Smarcel		cmd.cmdDep = branch_slot;
299179644Smarcel
300179644Smarcel	cmd.resCount = 0;
301179644Smarcel	cmd.xferStatus = 0;
302179644Smarcel
303179644Smarcel	/*
304179644Smarcel	 * Move quadwords to little-endian. God only knows why
305179644Smarcel	 * Apple thought this was a good idea.
306179644Smarcel	 */
307179644Smarcel	flip = (uint32_t *)(&cmd);
308179644Smarcel	flip[0] = htole32(flip[0]);
309179644Smarcel	flip[1] = htole32(flip[1]);
310179644Smarcel	flip[2] = htole32(flip[2]);
311179644Smarcel
312179644Smarcel	chan->sc_slots[slot] = cmd;
313179644Smarcel}
314179644Smarcel
315179644Smarcelvoid
316179644Smarceldbdma_insert_stop(dbdma_channel_t *chan, int slot)
317179644Smarcel{
318179644Smarcel
319179644Smarcel	dbdma_insert_command(chan, slot, DBDMA_STOP, 0, 0, 0, DBDMA_NEVER,
320179644Smarcel	    DBDMA_NEVER, DBDMA_NEVER, 0);
321179644Smarcel}
322179644Smarcel
323179644Smarcelvoid
324179644Smarceldbdma_insert_nop(dbdma_channel_t *chan, int slot)
325179644Smarcel{
326179644Smarcel
327179644Smarcel	dbdma_insert_command(chan, slot, DBDMA_NOP, 0, 0, 0, DBDMA_NEVER,
328179644Smarcel	    DBDMA_NEVER, DBDMA_NEVER, 0);
329179644Smarcel}
330179644Smarcel
331179644Smarcelvoid
332179644Smarceldbdma_insert_branch(dbdma_channel_t *chan, int slot, int to_slot)
333179644Smarcel{
334179644Smarcel
335179644Smarcel	dbdma_insert_command(chan, slot, DBDMA_NOP, 0, 0, 0, DBDMA_NEVER,
336179644Smarcel	    DBDMA_ALWAYS, DBDMA_NEVER, to_slot);
337179644Smarcel}
338179644Smarcel
339179644Smarcelvoid
340179644Smarceldbdma_sync_commands(dbdma_channel_t *chan, bus_dmasync_op_t op)
341179644Smarcel{
342179644Smarcel
343179644Smarcel	bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, op);
344179644Smarcel}
345179644Smarcel
346259671Sjhibbitsvoid
347259671Sjhibbitsdbdma_save_state(dbdma_channel_t *chan)
348259671Sjhibbits{
349259671Sjhibbits
350259671Sjhibbits	chan->sc_saved_regs[0] = dbdma_read_reg(chan, CHAN_CMDPTR);
351259671Sjhibbits	chan->sc_saved_regs[1] = dbdma_read_reg(chan, CHAN_CMDPTR_HI);
352259671Sjhibbits	chan->sc_saved_regs[2] = dbdma_read_reg(chan, CHAN_INTR_SELECT);
353259671Sjhibbits	chan->sc_saved_regs[3] = dbdma_read_reg(chan, CHAN_BRANCH_SELECT);
354259671Sjhibbits	chan->sc_saved_regs[4] = dbdma_read_reg(chan, CHAN_WAIT_SELECT);
355259671Sjhibbits
356259671Sjhibbits	dbdma_stop(chan);
357259671Sjhibbits}
358259671Sjhibbits
359259671Sjhibbitsvoid
360259671Sjhibbitsdbdma_restore_state(dbdma_channel_t *chan)
361259671Sjhibbits{
362259671Sjhibbits
363259671Sjhibbits	dbdma_wake(chan);
364259671Sjhibbits	dbdma_write_reg(chan, CHAN_CMDPTR, chan->sc_saved_regs[0]);
365259671Sjhibbits	dbdma_write_reg(chan, CHAN_CMDPTR_HI, chan->sc_saved_regs[1]);
366259671Sjhibbits	dbdma_write_reg(chan, CHAN_INTR_SELECT, chan->sc_saved_regs[2]);
367259671Sjhibbits	dbdma_write_reg(chan, CHAN_BRANCH_SELECT, chan->sc_saved_regs[3]);
368259671Sjhibbits	dbdma_write_reg(chan, CHAN_WAIT_SELECT, chan->sc_saved_regs[4]);
369259671Sjhibbits}
370259671Sjhibbits
371179644Smarcelstatic uint32_t
372179644Smarceldbdma_read_reg(dbdma_channel_t *chan, u_int offset)
373179644Smarcel{
374179644Smarcel
375183288Snwhitehorn	return (bus_read_4(chan->sc_regs, chan->sc_off + offset));
376179644Smarcel}
377179644Smarcel
378179644Smarcelstatic void
379179644Smarceldbdma_write_reg(dbdma_channel_t *chan, u_int offset, uint32_t val)
380179644Smarcel{
381179644Smarcel
382183288Snwhitehorn	bus_write_4(chan->sc_regs, chan->sc_off + offset, val);
383179644Smarcel}
384