1/*
2 * Copyright 2002/03, Thomas Kurschel. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6/*
7	IDE ISA controller driver
8
9	This is a testing-only driver. In reality, you want to use
10	the IDE PCI controller driver, but at least under Bochs, there's not
11	much choice as PCI support is very limited there.
12*/
13
14
15#include <KernelExport.h>
16#include <stdlib.h>
17#include <string.h>
18
19#include <bus/ISA.h>
20#include <bus/IDE.h>
21#include <ide_types.h>
22#include <device_manager.h>
23
24
25//#define TRACE_IDE_ISA
26#ifdef TRACE_IDE_ISA
27#	define TRACE(x...) dprintf("ide_isa: " x)
28#else
29#	define TRACE(x...) ;
30#endif
31
32
33#define IDE_ISA_MODULE_NAME "busses/ide/ide_isa/driver_v1"
34
35// private node item:
36// io address of command block
37#define IDE_ISA_COMMAND_BLOCK_BASE "ide_isa/command_block_base"
38// io address of control block
39#define IDE_ISA_CONTROL_BLOCK_BASE "ide_isa/control_block_base"
40// interrupt number
41#define IDE_ISA_INTNUM "ide_isa/irq"
42
43
44ide_for_controller_interface *ide;
45device_manager_info *pnp;
46
47
48// info about one channel
49typedef struct channel_info {
50	isa2_module_info	*isa;
51	uint16	command_block_base;	// io address command block
52	uint16	control_block_base; // io address control block
53	int		intnum;			// interrupt number
54
55	uint32	lost;			// != 0 if device got removed, i.e. if it must not
56							// be accessed anymore
57
58	ide_channel ide_channel;
59	device_node *node;
60} channel_info;
61
62
63/*! publish node of an ide channel */
64static status_t
65publish_channel(device_node *parent, uint16 command_block_base,
66	uint16 control_block_base, uint8 intnum, const char *name)
67{
68	device_attr attrs[] = {
69		{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE, { string: IDE_FOR_CONTROLLER_MODULE_NAME }},
70
71		// properties of this controller for ide bus manager
72		{ IDE_CONTROLLER_MAX_DEVICES_ITEM, B_UINT8_TYPE, { ui8: 2 }},
73		{ IDE_CONTROLLER_CAN_DMA_ITEM, B_UINT8_TYPE, { ui8: 0 }},
74		{ IDE_CONTROLLER_CAN_CQ_ITEM, B_UINT8_TYPE, { ui8: 1 }},
75		{ IDE_CONTROLLER_CONTROLLER_NAME_ITEM, B_STRING_TYPE, { string: name }},
76
77		// DMA properties; the 16 bit alignment is not necessary as
78		// the ide bus manager handles that very efficiently, but why
79		// not use the block device manager for doing that?
80		{ B_DMA_ALIGNMENT, B_UINT32_TYPE, { ui32: 1 }},
81		{ B_DMA_HIGH_ADDRESS, B_UINT64_TYPE, { ui64: 0x100000000LL }},
82
83		// private data to identify device
84		{ IDE_ISA_COMMAND_BLOCK_BASE, B_UINT16_TYPE, { ui16: command_block_base }},
85		{ IDE_ISA_CONTROL_BLOCK_BASE, B_UINT16_TYPE, { ui16: control_block_base }},
86		{ IDE_ISA_INTNUM, B_UINT8_TYPE, { ui8: intnum }},
87		{ NULL }
88	};
89	io_resource resources[3] = {
90		{ B_IO_PORT, command_block_base, 8 },
91		{ B_IO_PORT, control_block_base, 1 },
92		{}
93	};
94
95	TRACE("publishing %s, resources %#x %#x %d\n",
96		  name, command_block_base, control_block_base, intnum);
97
98	return pnp->register_node(parent, IDE_ISA_MODULE_NAME, attrs, resources,
99		NULL);
100}
101
102
103//	#pragma mark -
104
105
106static void
107set_channel(void *cookie, ide_channel ideChannel)
108{
109	channel_info *channel = cookie;
110	channel->ide_channel = ideChannel;
111}
112
113
114static status_t
115write_command_block_regs(void *channel_cookie, ide_task_file *tf,
116	ide_reg_mask mask)
117{
118	channel_info *channel = channel_cookie;
119	uint16 ioaddr = channel->command_block_base;
120	int i;
121
122	if (channel->lost)
123		return B_ERROR;
124
125	for (i = 0; i < 7; i++) {
126		if (((1 << (i-7)) & mask) != 0) {
127			TRACE("write_command_block_regs(): %x->HI(%x)\n",
128				tf->raw.r[i + 7], i);
129			channel->isa->write_io_8(ioaddr + 1 + i, tf->raw.r[i + 7]);
130		}
131
132		if (((1 << i) & mask) != 0 ) {
133			TRACE("write_comamnd_block_regs(): %x->LO(%x)\n", tf->raw.r[i], i);
134			channel->isa->write_io_8(ioaddr + 1 + i, tf->raw.r[i]);
135		}
136	}
137
138	return B_OK;
139}
140
141
142static status_t
143read_command_block_regs(void *channel_cookie, ide_task_file *tf,
144	ide_reg_mask mask)
145{
146	channel_info *channel = channel_cookie;
147	uint16 ioaddr = channel->command_block_base;
148	int i;
149
150	if (channel->lost)
151		return B_ERROR;
152
153	for (i = 0; i < 7; i++) {
154		if (((1 << i) & mask) != 0) {
155			tf->raw.r[i] = channel->isa->read_io_8(ioaddr + 1 + i);
156			TRACE("read_command_block_regs(%x): %x\n", i, (int)tf->raw.r[i]);
157		}
158	}
159
160	return B_OK;
161}
162
163
164static uint8
165get_altstatus(void *channel_cookie)
166{
167	channel_info *channel = channel_cookie;
168	uint16 altstatusaddr = channel->control_block_base;
169
170	if (channel->lost)
171		return B_ERROR;
172
173	return channel->isa->read_io_8(altstatusaddr);
174}
175
176
177static status_t
178write_device_control(void *channel_cookie, uint8 val)
179{
180	channel_info *channel = channel_cookie;
181	uint16 device_control_addr = channel->control_block_base;
182
183	TRACE("write_device_control(%x)\n", (int)val);
184
185	if (channel->lost)
186		return B_ERROR;
187
188	channel->isa->write_io_8(device_control_addr, val);
189
190	return B_OK;
191}
192
193
194static status_t
195write_pio_16(void *channel_cookie, uint16 *data, int count, bool force_16bit)
196{
197	channel_info *channel = channel_cookie;
198	uint16 ioaddr = channel->command_block_base;
199
200	if (channel->lost)
201		return B_ERROR;
202
203	// Bochs doesn't support 32 bit accesses;
204	// no real performance impact as this driver is for Bochs only anyway
205	force_16bit = true;
206
207	if ((count & 1) != 0 || force_16bit) {
208		for (; count > 0; --count)
209			channel->isa->write_io_16(ioaddr, *(data++));
210	} else {
211		uint32 *cur_data = (uint32 *)data;
212
213		for (; count > 0; count -= 2)
214			channel->isa->write_io_32(ioaddr, *(cur_data++));
215	}
216
217	return B_OK;
218}
219
220
221static status_t
222read_pio_16(void *channel_cookie, uint16 *data, int count, bool force_16bit)
223{
224	channel_info *channel = channel_cookie;
225	uint16 ioaddr = channel->command_block_base;
226
227	if (channel->lost)
228		return B_ERROR;
229
230	force_16bit = true;
231
232	if ((count & 1) != 0 || force_16bit) {
233		for (; count > 0; --count)
234			*(data++) = channel->isa->read_io_16(ioaddr);
235	} else {
236		uint32 *cur_data = (uint32 *)data;
237
238		for (; count > 0; count -= 2)
239			*(cur_data++) = channel->isa->read_io_32(ioaddr);
240	}
241
242	return B_OK;
243}
244
245
246static int32
247inthand(void *arg)
248{
249	channel_info *channel = (channel_info *)arg;
250	uint8 status;
251
252	TRACE("interrupt handler()\n");
253
254	if (channel->lost)
255		return B_UNHANDLED_INTERRUPT;
256
257	// acknowledge IRQ
258	status = channel->isa->read_io_8(channel->command_block_base + 7);
259
260	return ide->irq_handler(channel->ide_channel, status);
261}
262
263
264static status_t
265prepare_dma(void *channel_cookie, const physical_entry *sg_list,
266	size_t sg_list_count, bool write)
267{
268	return B_NOT_ALLOWED;
269}
270
271
272static status_t
273start_dma(void *channel_cookie)
274{
275	return B_NOT_ALLOWED;
276}
277
278
279static status_t
280finish_dma(void *channel_cookie)
281{
282	return B_NOT_ALLOWED;
283}
284
285
286//	#pragma mark -
287
288
289static float
290supports_device(device_node *parent)
291{
292	const char *bus;
293
294	// make sure parent is really the ISA bus manager
295	if (pnp->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
296		return B_ERROR;
297
298	if (strcmp(bus, "isa"))
299		return 0.0;
300
301	// we assume that every modern PC has an IDE controller, so no
302	// further testing is done (well - I don't really know how to detect the
303	// controller, but who cares ;)
304
305	return 0.6;
306}
307
308
309static status_t
310init_channel(device_node *node, void **_cookie)
311{
312	channel_info *channel;
313	device_node *parent;
314	isa2_module_info *isa;
315	uint16 command_block_base, control_block_base;
316	uint8 irq;
317	status_t res;
318
319	TRACE("ISA-IDE: channel init\n");
320
321	// get device data
322	if (pnp->get_attr_uint16(node, IDE_ISA_COMMAND_BLOCK_BASE, &command_block_base, false) != B_OK
323		|| pnp->get_attr_uint16(node, IDE_ISA_CONTROL_BLOCK_BASE, &control_block_base, false) != B_OK
324		|| pnp->get_attr_uint8(node, IDE_ISA_INTNUM, &irq, false) != B_OK)
325		return B_ERROR;
326
327	parent = pnp->get_parent_node(node);
328	pnp->get_driver(parent, (driver_module_info **)&isa, NULL);
329	pnp->put_node(parent);
330
331	channel = (channel_info *)malloc(sizeof(channel_info));
332	if (channel == NULL)
333		return B_NO_MEMORY;
334
335	TRACE("ISA-IDE: channel init, resources %#x %#x %d\n",
336		  command_block_base, control_block_base, irq);
337
338	channel->isa = isa;
339	channel->node = node;
340	channel->lost = false;
341	channel->command_block_base = command_block_base;
342	channel->control_block_base = control_block_base;
343	channel->intnum = irq;
344	channel->ide_channel = NULL;
345
346	res = install_io_interrupt_handler(channel->intnum,
347		inthand, channel, 0);
348
349	if (res < 0) {
350		TRACE("ISA-IDE: couldn't install irq handler for int %d\n", irq);
351		goto err;
352	}
353
354	// enable interrupts so the channel is ready to run
355	write_device_control(channel, ide_devctrl_bit3);
356
357	*_cookie = channel;
358	return B_OK;
359
360err:
361	free(channel);
362	return res;
363}
364
365
366static void
367uninit_channel(void *channel_cookie)
368{
369	channel_info *channel = channel_cookie;
370
371	TRACE("ISA-IDE: channel uninit\n");
372
373	// disable IRQs
374	write_device_control(channel, ide_devctrl_bit3 | ide_devctrl_nien);
375
376	// catch spurious interrupt
377	// (some controllers generate an IRQ when you _disable_ interrupts,
378	//  they are delayed by less then 40 µs, so 1 ms is safe)
379	snooze(1000);
380
381	remove_io_interrupt_handler(channel->intnum, inthand, channel);
382
383	free(channel);
384}
385
386
387static status_t
388register_device(device_node *parent)
389{
390	status_t primaryStatus;
391	status_t secondaryStatus;
392	TRACE("register_device()\n");
393
394	// our parent device is the isa bus and all device drivers are Universal,
395	// so the pnp_manager tries each ISA driver in turn
396	primaryStatus = publish_channel(parent, 0x1f0, 0x3f6, 14,
397		"primary IDE channel");
398	secondaryStatus = publish_channel(parent, 0x170, 0x376, 15,
399		"secondary IDE channel");
400
401	if (primaryStatus == B_OK || secondaryStatus == B_OK)
402		return B_OK;
403
404	return primaryStatus;
405}
406
407
408static void
409channel_removed(void *cookie)
410{
411	channel_info *channel = cookie;
412	TRACE("channel_removed()\n");
413
414	// disable access instantly
415	atomic_or(&channel->lost, 1);
416}
417
418
419module_dependency module_dependencies[] = {
420	{ IDE_FOR_CONTROLLER_MODULE_NAME, (module_info **)&ide },
421	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&pnp },
422	{}
423};
424
425// exported interface
426static ide_controller_interface sISAControllerInterface = {
427	{
428		{
429			IDE_ISA_MODULE_NAME,
430			0,
431			NULL
432		},
433
434		supports_device,
435		register_device,
436		init_channel,
437		uninit_channel,
438		NULL,	// register child devices
439		NULL,	// rescan
440		channel_removed,
441	},
442
443	&set_channel,
444
445	&write_command_block_regs,
446	&read_command_block_regs,
447
448	&get_altstatus,
449	&write_device_control,
450
451	&write_pio_16,
452	&read_pio_16,
453
454	&prepare_dma,
455	&start_dma,
456	&finish_dma,
457};
458
459module_info *modules[] = {
460	(module_info *)&sISAControllerInterface,
461	NULL
462};
463