1/*
2 * Copyright 2004-2007, Haiku, Inc. All RightsReserved.
3 * Copyright 2002/03, Thomas Kurschel. All rights reserved.
4 *
5 * Distributed under the terms of the MIT License.
6 */
7
8//!	DMA helper functions
9
10
11#include "ide_internal.h"
12
13#define CHECK_DEV_DMA_MODE(infoblock, elem, mode, this_mode, num_modes ) \
14	if( infoblock->elem ) { \
15		mode = this_mode; \
16		++num_modes; \
17	}
18
19
20int
21get_device_dma_mode(ide_device_info *device)
22{
23	ide_device_infoblock *infoblock = &device->infoblock;
24
25	int num_modes, mode;
26
27	mode = 0;
28	num_modes = 0;
29
30	if (!infoblock->DMA_supported)
31		return -1;
32
33	CHECK_DEV_DMA_MODE(infoblock, MDMA0_selected, mode, 0, num_modes);
34	CHECK_DEV_DMA_MODE(infoblock, MDMA1_selected, mode, 1, num_modes);
35	CHECK_DEV_DMA_MODE(infoblock, MDMA2_selected, mode, 2, num_modes);
36
37	if (infoblock->_88_valid) {
38		CHECK_DEV_DMA_MODE(infoblock, UDMA0_selected, mode, 0x10, num_modes);
39		CHECK_DEV_DMA_MODE(infoblock, UDMA1_selected, mode, 0x11, num_modes);
40		CHECK_DEV_DMA_MODE(infoblock, UDMA2_selected, mode, 0x12, num_modes);
41		CHECK_DEV_DMA_MODE(infoblock, UDMA3_selected, mode, 0x13, num_modes);
42		CHECK_DEV_DMA_MODE(infoblock, UDMA4_selected, mode, 0x14, num_modes);
43		CHECK_DEV_DMA_MODE(infoblock, UDMA5_selected, mode, 0x15, num_modes);
44		CHECK_DEV_DMA_MODE(infoblock, UDMA6_selected, mode, 0x16, num_modes);
45	}
46
47	if (num_modes != 1)
48		return -1;
49
50	SHOW_FLOW(3, "%x", mode);
51
52	return mode;
53}
54
55
56bool
57configure_dma(ide_device_info *device)
58{
59	if (get_device_dma_mode(device) != -1) {
60		device->DMA_enabled = device->DMA_supported = device->bus->can_DMA;
61		if (device->DMA_enabled) {
62			dprintf("IDE: enabling DMA\n");
63		} else {
64			dprintf("IDE: disabling DMA (failsafe option selected)\n");
65		}
66	} else {
67		device->DMA_enabled = false;
68		dprintf("IDE: DMA not possible, disabling\n");
69	}
70
71	return true;
72}
73
74
75/*!	Abort DMA transmission
76	must be called _before_ start_dma_wait
77*/
78void
79abort_dma(ide_device_info *device, ide_qrequest *qrequest)
80{
81	ide_bus_info *bus = device->bus;
82
83	SHOW_FLOW0(0, "");
84
85	bus->controller->finish_dma(bus->channel_cookie);
86}
87
88
89/*!	Prepare DMA transmission
90	on return, DMA engine waits for device to transmit data
91	warning: doesn't set sense data on error
92*/
93bool
94prepare_dma(ide_device_info *device, ide_qrequest *qrequest)
95{
96	ide_bus_info *bus = device->bus;
97	scsi_ccb *request = qrequest->request;
98	status_t res;
99
100	res = bus->controller->prepare_dma(bus->channel_cookie, request->sg_list,
101		request->sg_count, qrequest->is_write);
102	if (res != B_OK)
103		return false;
104
105	return true;
106}
107
108
109/*! Start waiting for DMA to be finished */
110void
111start_dma_wait(ide_device_info *device, ide_qrequest *qrequest)
112{
113	ide_bus_info *bus = device->bus;
114
115	bus->controller->start_dma(bus->channel_cookie);
116
117	start_waiting(bus, qrequest->request->timeout > 0 ?
118		qrequest->request->timeout : IDE_STD_TIMEOUT, ide_state_async_waiting);
119}
120
121
122/*! Start waiting for DMA to be finished with bus lock not hold */
123void
124start_dma_wait_no_lock(ide_device_info *device, ide_qrequest *qrequest)
125{
126	ide_bus_info *bus = device->bus;
127
128	IDE_LOCK(bus);
129	start_dma_wait(device, qrequest);
130}
131
132
133/*! Finish dma transmission after device has fired IRQ */
134bool
135finish_dma(ide_device_info *device)
136{
137	ide_bus_info *bus = device->bus;
138	status_t dma_res;
139
140	dma_res = bus->controller->finish_dma(bus->channel_cookie);
141
142	return dma_res == B_OK || dma_res == B_DEV_DATA_OVERRUN;
143}
144
145