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

Last change on this file was 07039850, checked in by Jiri Svoboda <jiri@…>, 6 months ago

Implement quiesce in ISA and PCI IDE and in PC Floppy.

  • Property mode set to 100644
File size: 13.1 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_channel_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_channel_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_channel_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_channel_fini()");
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/** Quiesce ISA IDE channel. */
201void isa_ide_channel_quiesce(isa_ide_channel_t *chan)
202{
203 ddf_msg(LVL_DEBUG, ": isa_ide_channel_quiesce()");
204
205 fibril_mutex_lock(&chan->lock);
206 ata_channel_quiesce(chan->channel);
207 fibril_mutex_unlock(&chan->lock);
208}
209
210/** Enable device I/O. */
211static errno_t isa_ide_init_io(isa_ide_channel_t *chan)
212{
213 errno_t rc;
214 void *vaddr;
215
216 rc = pio_enable((void *) chan->cmd_physical, sizeof(ata_cmd_t), &vaddr);
217 if (rc != EOK) {
218 ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
219 return rc;
220 }
221
222 chan->cmd = vaddr;
223
224 rc = pio_enable((void *) chan->ctl_physical, sizeof(ata_ctl_t), &vaddr);
225 if (rc != EOK) {
226 ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
227 return rc;
228 }
229
230 chan->ctl = vaddr;
231 return EOK;
232}
233
234/** Clean up device I/O. */
235static void isa_ide_fini_io(isa_ide_channel_t *chan)
236{
237 (void) chan;
238 /* XXX TODO */
239}
240
241/** Initialize IRQ. */
242static errno_t isa_ide_init_irq(isa_ide_channel_t *chan)
243{
244 irq_code_t irq_code;
245 irq_pio_range_t *ranges;
246 irq_cmd_t *cmds;
247 errno_t rc;
248
249 if (chan->irq < 0)
250 return EOK;
251
252 ranges = malloc(sizeof(isa_ide_irq_ranges));
253 if (ranges == NULL)
254 return ENOMEM;
255
256 cmds = malloc(sizeof(isa_ide_irq_cmds));
257 if (cmds == NULL) {
258 free(cmds);
259 return ENOMEM;
260 }
261
262 memcpy(ranges, &isa_ide_irq_ranges, sizeof(isa_ide_irq_ranges));
263 ranges[0].base = chan->cmd_physical;
264 memcpy(cmds, &isa_ide_irq_cmds, sizeof(isa_ide_irq_cmds));
265 cmds[0].addr = &chan->cmd->status;
266
267 irq_code.rangecount = sizeof(isa_ide_irq_ranges) / sizeof(irq_pio_range_t);
268 irq_code.ranges = ranges;
269 irq_code.cmdcount = sizeof(isa_ide_irq_cmds) / sizeof(irq_cmd_t);
270 irq_code.cmds = cmds;
271
272 ddf_msg(LVL_NOTE, "IRQ %d", chan->irq);
273 rc = register_interrupt_handler(chan->ctrl->dev, chan->irq,
274 isa_ide_irq_handler, (void *)chan, &irq_code, &chan->ihandle);
275 if (rc != EOK) {
276 ddf_msg(LVL_ERROR, "Error registering IRQ.");
277 goto error;
278 }
279
280 ddf_msg(LVL_DEBUG, "Interrupt handler registered");
281 free(ranges);
282 free(cmds);
283 return EOK;
284error:
285 free(ranges);
286 free(cmds);
287 return rc;
288}
289
290/** Clean up IRQ. */
291static void isa_ide_fini_irq(isa_ide_channel_t *chan)
292{
293 errno_t rc;
294 async_sess_t *parent_sess;
295
296 parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
297
298 rc = hw_res_disable_interrupt(parent_sess, chan->irq);
299 if (rc != EOK)
300 ddf_msg(LVL_ERROR, "Error disabling IRQ.");
301
302 (void) unregister_interrupt_handler(chan->ctrl->dev, chan->ihandle);
303}
304
305/** Interrupt handler.
306 *
307 * @param call Call data
308 * @param arg Argument (isa_ide_channel_t *)
309 */
310static void isa_ide_irq_handler(ipc_call_t *call, void *arg)
311{
312 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
313 uint8_t status;
314 async_sess_t *parent_sess;
315
316 status = ipc_get_arg1(call);
317 ata_channel_irq(chan->channel, status);
318
319 parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
320 hw_res_clear_interrupt(parent_sess, chan->irq);
321}
322
323/** Write the data register callback handler.
324 *
325 * @param arg Argument (isa_ide_channel_t *)
326 * @param data Data
327 * @param nwords Number of words to write
328 */
329static void isa_ide_write_data_16(void *arg, uint16_t *data, size_t nwords)
330{
331 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
332 size_t i;
333
334 for (i = 0; i < nwords; i++)
335 pio_write_16(&chan->cmd->data_port, data[i]);
336}
337
338/** Read the data register callback handler.
339 *
340 * @param arg Argument (isa_ide_channel_t *)
341 * @param buf Destination buffer
342 * @param nwords Number of words to read
343 */
344static void isa_ide_read_data_16(void *arg, uint16_t *buf, size_t nwords)
345{
346 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
347 size_t i;
348
349 for (i = 0; i < nwords; i++)
350 buf[i] = pio_read_16(&chan->cmd->data_port);
351}
352
353/** Write command register callback handler.
354 *
355 * @param arg Argument (isa_ide_channel_t *)
356 * @param off Register offset
357 * @param value Value to write to command register
358 */
359static void isa_ide_write_cmd_8(void *arg, uint16_t off, uint8_t value)
360{
361 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
362
363 pio_write_8(((ioport8_t *)chan->cmd) + off, value);
364}
365
366/** Read command register callback handler.
367 *
368 * @param arg Argument (isa_ide_channel_t *)
369 * @param off Register offset
370 * @return value Value read from command register
371 */
372static uint8_t isa_ide_read_cmd_8(void *arg, uint16_t off)
373{
374 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
375
376 return pio_read_8(((ioport8_t *)chan->cmd) + off);
377}
378
379/** Write control register callback handler.
380 *
381 * @param arg Argument (isa_ide_channel_t *)
382 * @param off Register offset
383 * @param value Value to write to control register
384 */
385static void isa_ide_write_ctl_8(void *arg, uint16_t off, uint8_t value)
386{
387 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
388
389 pio_write_8(((ioport8_t *)chan->ctl) + off, value);
390}
391
392/** Read control register callback handler.
393 *
394 * @param arg Argument (isa_ide_channel_t *)
395 * @param off Register offset
396 * @return value Value read from control register
397 */
398static uint8_t isa_ide_read_ctl_8(void *arg, uint16_t off)
399{
400 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
401
402 return pio_read_8(((ioport8_t *)chan->ctl) + off);
403}
404
405/** Enable IRQ callback handler
406 *
407 * @param arg Argument (isa_ide_channel_t *)
408 * @return EOK on success or an error code
409 */
410static errno_t isa_ide_irq_enable(void *arg)
411{
412 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
413 async_sess_t *parent_sess;
414 errno_t rc;
415
416 ddf_msg(LVL_DEBUG, "Enable IRQ %d for channel %u",
417 chan->irq, chan->chan_id);
418
419 parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
420
421 rc = hw_res_enable_interrupt(parent_sess, chan->irq);
422 if (rc != EOK) {
423 ddf_msg(LVL_ERROR, "Error enabling IRQ.");
424 return rc;
425 }
426
427 return EOK;
428}
429
430/** Disable IRQ callback handler
431 *
432 * @param arg Argument (isa_ide_channel_t *)
433 * @return EOK on success or an error code
434 */
435static errno_t isa_ide_irq_disable(void *arg)
436{
437 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
438 async_sess_t *parent_sess;
439 errno_t rc;
440
441 ddf_msg(LVL_DEBUG, "Disable IRQ");
442
443 parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
444
445 rc = hw_res_disable_interrupt(parent_sess, chan->irq);
446 if (rc != EOK) {
447 ddf_msg(LVL_ERROR, "Error disabling IRQ.");
448 return rc;
449 }
450
451 return EOK;
452}
453
454/** Add ATA device callback handler.
455 *
456 * @param arg Argument (isa_ide_channel_t *)
457 * @param idx Device index
458 * $param charg Connection handler argument
459 * @return EOK on success or an error code
460 */
461static errno_t isa_ide_add_device(void *arg, unsigned idx, void *charg)
462{
463 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
464 return isa_ide_fun_create(chan, idx, charg);
465}
466
467/** Remove ATA device callback handler.
468 *
469 * @param arg Argument (isa_ide_channel_t *)
470 * @param idx Device index
471 * @return EOK on success or an error code
472 */
473static errno_t isa_ide_remove_device(void *arg, unsigned idx)
474{
475 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
476 return isa_ide_fun_remove(chan, idx);
477}
478
479/** Debug message callback handler.
480 *
481 * @param arg Argument (isa_ide_channel_t *)
482 * @param msg Message
483 */
484static void isa_ide_msg_debug(void *arg, char *msg)
485{
486 (void)arg;
487 ddf_msg(LVL_DEBUG, "%s", msg);
488}
489
490/** Notice message callback handler.
491 *
492 * @param arg Argument (isa_ide_channel_t *)
493 * @param msg Message
494 */
495static void isa_ide_msg_note(void *arg, char *msg)
496{
497 (void)arg;
498 ddf_msg(LVL_NOTE, "%s", msg);
499}
500
501/** Warning message callback handler.
502 *
503 * @param arg Argument (isa_ide_channel_t *)
504 * @param msg Message
505 */
506static void isa_ide_msg_warn(void *arg, char *msg)
507{
508 (void)arg;
509 ddf_msg(LVL_WARN, "%s", msg);
510}
511
512/** Error message callback handler.
513 *
514 * @param arg Argument (isa_ide_channel_t *)
515 * @param msg Message
516 */
517static void isa_ide_msg_error(void *arg, char *msg)
518{
519 (void)arg;
520 ddf_msg(LVL_ERROR, "%s", msg);
521}
522
523/**
524 * @}
525 */
Note: See TracBrowser for help on using the repository browser.