source: mainline/uspace/drv/block/isa-ide/isa-ide.c@ 12824b9

Last change on this file since 12824b9 was a796812c, checked in by Jiri Svoboda <jiri@…>, 7 months ago

Fix ISA IDE

  • Property mode set to 100644
File size: 12.8 KB
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - 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 * - 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, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup isa-ide
30 * @{
31 */
32
33/**
34 * @file
35 * @brief ISA IDE driver
36 */
37
38#include <ddi.h>
39#include <ddf/interrupt.h>
40#include <ddf/log.h>
41#include <async.h>
42#include <as.h>
43#include <fibril_synch.h>
44#include <stdint.h>
45#include <stdbool.h>
46#include <stdio.h>
47#include <stddef.h>
48#include <str.h>
49#include <inttypes.h>
50#include <errno.h>
51
52#include "isa-ide.h"
53#include "main.h"
54
55static errno_t isa_ide_init_io(isa_ide_channel_t *);
56static void isa_ide_fini_io(isa_ide_channel_t *);
57static errno_t isa_ide_init_irq(isa_ide_channel_t *);
58static void isa_ide_fini_irq(isa_ide_channel_t *);
59static void isa_ide_irq_handler(ipc_call_t *, void *);
60
61static void isa_ide_write_data_16(void *, uint16_t *, size_t);
62static void isa_ide_read_data_16(void *, uint16_t *, size_t);
63static void isa_ide_write_cmd_8(void *, uint16_t, uint8_t);
64static uint8_t isa_ide_read_cmd_8(void *, uint16_t);
65static void isa_ide_write_ctl_8(void *, uint16_t, uint8_t);
66static uint8_t isa_ide_read_ctl_8(void *, uint16_t);
67static errno_t isa_ide_irq_enable(void *);
68static errno_t isa_ide_irq_disable(void *);
69static errno_t isa_ide_add_device(void *, unsigned, void *);
70static errno_t isa_ide_remove_device(void *, unsigned);
71static void isa_ide_msg_debug(void *, char *);
72static void isa_ide_msg_note(void *, char *);
73static void isa_ide_msg_warn(void *, char *);
74static void isa_ide_msg_error(void *, char *);
75
76static const irq_pio_range_t isa_ide_irq_ranges[] = {
77 {
78 .base = 0,
79 .size = sizeof(ata_cmd_t)
80 }
81};
82
83/** IDE interrupt pseudo code. */
84static const irq_cmd_t isa_ide_irq_cmds[] = {
85 {
86 .cmd = CMD_PIO_READ_8,
87 .addr = NULL, /* will be patched in run-time */
88 .dstarg = 1
89 },
90 {
91 .cmd = CMD_ACCEPT
92 }
93};
94
95/** Initialize ISA IDE channel. */
96errno_t isa_ide_channel_init(isa_ide_ctrl_t *ctrl, isa_ide_channel_t *chan,
97 unsigned chan_id, isa_ide_hwres_t *res)
98{
99 errno_t rc;
100 bool irq_inited = false;
101 ata_params_t params;
102
103 ddf_msg(LVL_DEBUG, "isa_ide_ctrl_init()");
104
105 memset(&params, 0, sizeof(params));
106
107 chan->ctrl = ctrl;
108 chan->chan_id = chan_id;
109 fibril_mutex_initialize(&chan->lock);
110 if (chan_id == 0) {
111 chan->cmd_physical = res->cmd1;
112 chan->ctl_physical = res->ctl1;
113 chan->irq = res->irq1;
114 } else {
115 chan->cmd_physical = res->cmd2;
116 chan->ctl_physical = res->ctl2;
117 chan->irq = res->irq2;
118 }
119
120 ddf_msg(LVL_NOTE, "I/O address %p/%p", (void *) chan->cmd_physical,
121 (void *) chan->ctl_physical);
122
123 ddf_msg(LVL_DEBUG, "Init I/O");
124 rc = isa_ide_init_io(chan);
125 if (rc != EOK)
126 return rc;
127
128 ddf_msg(LVL_DEBUG, "Init IRQ");
129 rc = isa_ide_init_irq(chan);
130 if (rc != EOK) {
131 ddf_msg(LVL_NOTE, "init IRQ failed");
132 return rc;
133 }
134
135 irq_inited = true;
136
137 ddf_msg(LVL_DEBUG, "isa_ide_ctrl_init(): Initialize IDE channel");
138
139 params.arg = (void *)chan;
140 params.use_dma = false;
141 params.have_irq = (chan->irq >= 0) ? true : false;
142 params.write_data_16 = isa_ide_write_data_16;
143 params.read_data_16 = isa_ide_read_data_16;
144 params.write_cmd_8 = isa_ide_write_cmd_8;
145 params.read_cmd_8 = isa_ide_read_cmd_8;
146 params.write_ctl_8 = isa_ide_write_ctl_8;
147 params.read_ctl_8 = isa_ide_read_ctl_8;
148 params.irq_enable = isa_ide_irq_enable;
149 params.irq_disable = isa_ide_irq_disable;
150 params.add_device = isa_ide_add_device;
151 params.remove_device = isa_ide_remove_device;
152 params.msg_debug = isa_ide_msg_debug;
153 params.msg_note = isa_ide_msg_note;
154 params.msg_warn = isa_ide_msg_warn;
155 params.msg_error = isa_ide_msg_error;
156
157 rc = ata_channel_create(&params, &chan->channel);
158 if (rc != EOK)
159 goto error;
160
161 rc = ata_channel_initialize(chan->channel);
162 if (rc != EOK)
163 goto error;
164
165 ddf_msg(LVL_DEBUG, "isa_ide_ctrl_init: DONE");
166 return EOK;
167error:
168 if (chan->channel != NULL) {
169 (void) ata_channel_destroy(chan->channel);
170 chan->channel = NULL;
171 }
172 if (irq_inited)
173 isa_ide_fini_irq(chan);
174 isa_ide_fini_io(chan);
175 return rc;
176}
177
178/** Finalize ISA IDE channel. */
179errno_t isa_ide_channel_fini(isa_ide_channel_t *chan)
180{
181 errno_t rc;
182
183 ddf_msg(LVL_DEBUG, ": isa_ide_ctrl_remove()");
184
185 fibril_mutex_lock(&chan->lock);
186
187 rc = ata_channel_destroy(chan->channel);
188 if (rc != EOK) {
189 fibril_mutex_unlock(&chan->lock);
190 return rc;
191 }
192
193 isa_ide_fini_irq(chan);
194 isa_ide_fini_io(chan);
195 fibril_mutex_unlock(&chan->lock);
196
197 return EOK;
198}
199
200/** Enable device I/O. */
201static errno_t isa_ide_init_io(isa_ide_channel_t *chan)
202{
203 errno_t rc;
204 void *vaddr;
205
206 rc = pio_enable((void *) chan->cmd_physical, sizeof(ata_cmd_t), &vaddr);
207 if (rc != EOK) {
208 ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
209 return rc;
210 }
211
212 chan->cmd = vaddr;
213
214 rc = pio_enable((void *) chan->ctl_physical, sizeof(ata_ctl_t), &vaddr);
215 if (rc != EOK) {
216 ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
217 return rc;
218 }
219
220 chan->ctl = vaddr;
221 return EOK;
222}
223
224/** Clean up device I/O. */
225static void isa_ide_fini_io(isa_ide_channel_t *chan)
226{
227 (void) chan;
228 /* XXX TODO */
229}
230
231/** Initialize IRQ. */
232static errno_t isa_ide_init_irq(isa_ide_channel_t *chan)
233{
234 irq_code_t irq_code;
235 irq_pio_range_t *ranges;
236 irq_cmd_t *cmds;
237 errno_t rc;
238
239 if (chan->irq < 0)
240 return EOK;
241
242 ranges = malloc(sizeof(isa_ide_irq_ranges));
243 if (ranges == NULL)
244 return ENOMEM;
245
246 cmds = malloc(sizeof(isa_ide_irq_cmds));
247 if (cmds == NULL) {
248 free(cmds);
249 return ENOMEM;
250 }
251
252 memcpy(ranges, &isa_ide_irq_ranges, sizeof(isa_ide_irq_ranges));
253 ranges[0].base = chan->cmd_physical;
254 memcpy(cmds, &isa_ide_irq_cmds, sizeof(isa_ide_irq_cmds));
255 cmds[0].addr = &chan->cmd->status;
256
257 irq_code.rangecount = sizeof(isa_ide_irq_ranges) / sizeof(irq_pio_range_t);
258 irq_code.ranges = ranges;
259 irq_code.cmdcount = sizeof(isa_ide_irq_cmds) / sizeof(irq_cmd_t);
260 irq_code.cmds = cmds;
261
262 ddf_msg(LVL_NOTE, "IRQ %d", chan->irq);
263 rc = register_interrupt_handler(chan->ctrl->dev, chan->irq,
264 isa_ide_irq_handler, (void *)chan, &irq_code, &chan->ihandle);
265 if (rc != EOK) {
266 ddf_msg(LVL_ERROR, "Error registering IRQ.");
267 goto error;
268 }
269
270 ddf_msg(LVL_DEBUG, "Interrupt handler registered");
271 free(ranges);
272 free(cmds);
273 return EOK;
274error:
275 free(ranges);
276 free(cmds);
277 return rc;
278}
279
280/** Clean up IRQ. */
281static void isa_ide_fini_irq(isa_ide_channel_t *chan)
282{
283 errno_t rc;
284 async_sess_t *parent_sess;
285
286 parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
287
288 rc = hw_res_disable_interrupt(parent_sess, chan->irq);
289 if (rc != EOK)
290 ddf_msg(LVL_ERROR, "Error disabling IRQ.");
291
292 (void) unregister_interrupt_handler(chan->ctrl->dev, chan->ihandle);
293}
294
295/** Interrupt handler.
296 *
297 * @param call Call data
298 * @param arg Argument (isa_ide_channel_t *)
299 */
300static void isa_ide_irq_handler(ipc_call_t *call, void *arg)
301{
302 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
303 uint8_t status;
304 async_sess_t *parent_sess;
305
306 status = ipc_get_arg1(call);
307 ata_channel_irq(chan->channel, status);
308
309 parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
310 hw_res_clear_interrupt(parent_sess, chan->irq);
311}
312
313/** Write the data register callback handler.
314 *
315 * @param arg Argument (isa_ide_channel_t *)
316 * @param data Data
317 * @param nwords Number of words to write
318 */
319static void isa_ide_write_data_16(void *arg, uint16_t *data, size_t nwords)
320{
321 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
322 size_t i;
323
324 for (i = 0; i < nwords; i++)
325 pio_write_16(&chan->cmd->data_port, data[i]);
326}
327
328/** Read the data register callback handler.
329 *
330 * @param arg Argument (isa_ide_channel_t *)
331 * @param buf Destination buffer
332 * @param nwords Number of words to read
333 */
334static void isa_ide_read_data_16(void *arg, uint16_t *buf, size_t nwords)
335{
336 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
337 size_t i;
338
339 for (i = 0; i < nwords; i++)
340 buf[i] = pio_read_16(&chan->cmd->data_port);
341}
342
343/** Write command register callback handler.
344 *
345 * @param arg Argument (isa_ide_channel_t *)
346 * @param off Register offset
347 * @param value Value to write to command register
348 */
349static void isa_ide_write_cmd_8(void *arg, uint16_t off, uint8_t value)
350{
351 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
352
353 pio_write_8(((ioport8_t *)chan->cmd) + off, value);
354}
355
356/** Read command register callback handler.
357 *
358 * @param arg Argument (isa_ide_channel_t *)
359 * @param off Register offset
360 * @return value Value read from command register
361 */
362static uint8_t isa_ide_read_cmd_8(void *arg, uint16_t off)
363{
364 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
365
366 return pio_read_8(((ioport8_t *)chan->cmd) + off);
367}
368
369/** Write control register callback handler.
370 *
371 * @param arg Argument (isa_ide_channel_t *)
372 * @param off Register offset
373 * @param value Value to write to control register
374 */
375static void isa_ide_write_ctl_8(void *arg, uint16_t off, uint8_t value)
376{
377 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
378
379 pio_write_8(((ioport8_t *)chan->ctl) + off, value);
380}
381
382/** Read control register callback handler.
383 *
384 * @param arg Argument (isa_ide_channel_t *)
385 * @param off Register offset
386 * @return value Value read from control register
387 */
388static uint8_t isa_ide_read_ctl_8(void *arg, uint16_t off)
389{
390 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
391
392 return pio_read_8(((ioport8_t *)chan->ctl) + off);
393}
394
395/** Enable IRQ callback handler
396 *
397 * @param arg Argument (isa_ide_channel_t *)
398 * @return EOK on success or an error code
399 */
400static errno_t isa_ide_irq_enable(void *arg)
401{
402 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
403 async_sess_t *parent_sess;
404 errno_t rc;
405
406 ddf_msg(LVL_DEBUG, "Enable IRQ %d for channel %u",
407 chan->irq, chan->chan_id);
408
409 parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
410
411 rc = hw_res_enable_interrupt(parent_sess, chan->irq);
412 if (rc != EOK) {
413 ddf_msg(LVL_ERROR, "Error enabling IRQ.");
414 return rc;
415 }
416
417 return EOK;
418}
419
420/** Disable IRQ callback handler
421 *
422 * @param arg Argument (isa_ide_channel_t *)
423 * @return EOK on success or an error code
424 */
425static errno_t isa_ide_irq_disable(void *arg)
426{
427 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
428 async_sess_t *parent_sess;
429 errno_t rc;
430
431 ddf_msg(LVL_DEBUG, "Disable IRQ");
432
433 parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
434
435 rc = hw_res_disable_interrupt(parent_sess, chan->irq);
436 if (rc != EOK) {
437 ddf_msg(LVL_ERROR, "Error disabling IRQ.");
438 return rc;
439 }
440
441 return EOK;
442}
443
444/** Add ATA device callback handler.
445 *
446 * @param arg Argument (isa_ide_channel_t *)
447 * @param idx Device index
448 * $param charg Connection handler argument
449 * @return EOK on success or an error code
450 */
451static errno_t isa_ide_add_device(void *arg, unsigned idx, void *charg)
452{
453 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
454 return isa_ide_fun_create(chan, idx, charg);
455}
456
457/** Remove ATA device callback handler.
458 *
459 * @param arg Argument (isa_ide_channel_t *)
460 * @param idx Device index
461 * @return EOK on success or an error code
462 */
463static errno_t isa_ide_remove_device(void *arg, unsigned idx)
464{
465 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
466 return isa_ide_fun_remove(chan, idx);
467}
468
469/** Debug message callback handler.
470 *
471 * @param arg Argument (isa_ide_channel_t *)
472 * @param msg Message
473 */
474static void isa_ide_msg_debug(void *arg, char *msg)
475{
476 (void)arg;
477 ddf_msg(LVL_DEBUG, "%s", msg);
478}
479
480/** Notice message callback handler.
481 *
482 * @param arg Argument (isa_ide_channel_t *)
483 * @param msg Message
484 */
485static void isa_ide_msg_note(void *arg, char *msg)
486{
487 (void)arg;
488 ddf_msg(LVL_NOTE, "%s", msg);
489}
490
491/** Warning message callback handler.
492 *
493 * @param arg Argument (isa_ide_channel_t *)
494 * @param msg Message
495 */
496static void isa_ide_msg_warn(void *arg, char *msg)
497{
498 (void)arg;
499 ddf_msg(LVL_WARN, "%s", msg);
500}
501
502/** Error message callback handler.
503 *
504 * @param arg Argument (isa_ide_channel_t *)
505 * @param msg Message
506 */
507static void isa_ide_msg_error(void *arg, char *msg)
508{
509 (void)arg;
510 ddf_msg(LVL_ERROR, "%s", msg);
511}
512
513/**
514 * @}
515 */
Note: See TracBrowser for help on using the repository browser.