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

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

Fix ISA IDE

  • Property mode set to 100644
File size: 12.8 KB
RevLine 
[f8ef660]1/*
[a796812c]2 * Copyright (c) 2025 Jiri Svoboda
[f8ef660]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
[59c0f478]29/** @addtogroup isa-ide
[f8ef660]30 * @{
31 */
32
33/**
34 * @file
[59c0f478]35 * @brief ISA IDE driver
[f8ef660]36 */
37
38#include <ddi.h>
[64cf7a3]39#include <ddf/interrupt.h>
[9f391e9]40#include <ddf/log.h>
[f8ef660]41#include <async.h>
42#include <as.h>
[1e4cada]43#include <fibril_synch.h>
[bb0eab1]44#include <stdint.h>
[8d2dd7f2]45#include <stdbool.h>
46#include <stdio.h>
47#include <stddef.h>
[19f857a]48#include <str.h>
[fb6f1a5]49#include <inttypes.h>
[f8ef660]50#include <errno.h>
51
[59c0f478]52#include "isa-ide.h"
[66cb7a2]53#include "main.h"
[f8ef660]54
[646849b3]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 *);
[60744cb]59static void isa_ide_irq_handler(ipc_call_t *, void *);
[59c0f478]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[] = {
[64cf7a3]77 {
78 .base = 0,
79 .size = sizeof(ata_cmd_t)
80 }
81};
82
[59c0f478]83/** IDE interrupt pseudo code. */
84static const irq_cmd_t isa_ide_irq_cmds[] = {
[64cf7a3]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
[646849b3]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)
[f8ef660]98{
[b7fd2a0]99 errno_t rc;
[64cf7a3]100 bool irq_inited = false;
[2791fbb7]101 ata_params_t params;
[f8ef660]102
[59c0f478]103 ddf_msg(LVL_DEBUG, "isa_ide_ctrl_init()");
[f8ef660]104
[a796812c]105 memset(&params, 0, sizeof(params));
106
[646849b3]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 }
[e092dc5]119
[646849b3]120 ddf_msg(LVL_NOTE, "I/O address %p/%p", (void *) chan->cmd_physical,
121 (void *) chan->ctl_physical);
[f8ef660]122
[2791fbb7]123 ddf_msg(LVL_DEBUG, "Init I/O");
[646849b3]124 rc = isa_ide_init_io(chan);
[66cb7a2]125 if (rc != EOK)
126 return rc;
[f8ef660]127
[2791fbb7]128 ddf_msg(LVL_DEBUG, "Init IRQ");
[646849b3]129 rc = isa_ide_init_irq(chan);
[2791fbb7]130 if (rc != EOK) {
131 ddf_msg(LVL_NOTE, "init IRQ failed");
[64cf7a3]132 return rc;
[31de325]133 }
[1806e5d]134
[2791fbb7]135 irq_inited = true;
[66cb7a2]136
[59c0f478]137 ddf_msg(LVL_DEBUG, "isa_ide_ctrl_init(): Initialize IDE channel");
[2791fbb7]138
[646849b3]139 params.arg = (void *)chan;
[a796812c]140 params.use_dma = false;
[646849b3]141 params.have_irq = (chan->irq >= 0) ? true : false;
[59c0f478]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;
[2791fbb7]156
[646849b3]157 rc = ata_channel_create(&params, &chan->channel);
[2791fbb7]158 if (rc != EOK)
159 goto error;
[1806e5d]160
[646849b3]161 rc = ata_channel_initialize(chan->channel);
[2791fbb7]162 if (rc != EOK)
[66cb7a2]163 goto error;
[1806e5d]164
[59c0f478]165 ddf_msg(LVL_DEBUG, "isa_ide_ctrl_init: DONE");
[66cb7a2]166 return EOK;
167error:
[145d4e2e]168 if (chan->channel != NULL) {
169 (void) ata_channel_destroy(chan->channel);
170 chan->channel = NULL;
171 }
[64cf7a3]172 if (irq_inited)
[646849b3]173 isa_ide_fini_irq(chan);
174 isa_ide_fini_io(chan);
[66cb7a2]175 return rc;
[1806e5d]176}
177
[646849b3]178/** Finalize ISA IDE channel. */
179errno_t isa_ide_channel_fini(isa_ide_channel_t *chan)
[66cb7a2]180{
[b7fd2a0]181 errno_t rc;
[66cb7a2]182
[59c0f478]183 ddf_msg(LVL_DEBUG, ": isa_ide_ctrl_remove()");
[e092dc5]184
[646849b3]185 fibril_mutex_lock(&chan->lock);
[66cb7a2]186
[646849b3]187 rc = ata_channel_destroy(chan->channel);
[2791fbb7]188 if (rc != EOK) {
[646849b3]189 fibril_mutex_unlock(&chan->lock);
[2791fbb7]190 return rc;
[66cb7a2]191 }
192
[646849b3]193 isa_ide_fini_irq(chan);
194 isa_ide_fini_io(chan);
195 fibril_mutex_unlock(&chan->lock);
[66cb7a2]196
197 return EOK;
[e092dc5]198}
199
[66cb7a2]200/** Enable device I/O. */
[646849b3]201static errno_t isa_ide_init_io(isa_ide_channel_t *chan)
[f8ef660]202{
[b7fd2a0]203 errno_t rc;
[b16e77d]204 void *vaddr;
[66cb7a2]205
[646849b3]206 rc = pio_enable((void *) chan->cmd_physical, sizeof(ata_cmd_t), &vaddr);
[f8ef660]207 if (rc != EOK) {
[9f391e9]208 ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
[f8ef660]209 return rc;
210 }
[66cb7a2]211
[646849b3]212 chan->cmd = vaddr;
[66cb7a2]213
[646849b3]214 rc = pio_enable((void *) chan->ctl_physical, sizeof(ata_ctl_t), &vaddr);
[f8ef660]215 if (rc != EOK) {
[9f391e9]216 ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
[f8ef660]217 return rc;
218 }
[66cb7a2]219
[646849b3]220 chan->ctl = vaddr;
[f8ef660]221 return EOK;
222}
223
[66cb7a2]224/** Clean up device I/O. */
[646849b3]225static void isa_ide_fini_io(isa_ide_channel_t *chan)
[f8ef660]226{
[646849b3]227 (void) chan;
[66cb7a2]228 /* XXX TODO */
[f8ef660]229}
230
[64cf7a3]231/** Initialize IRQ. */
[646849b3]232static errno_t isa_ide_init_irq(isa_ide_channel_t *chan)
[64cf7a3]233{
234 irq_code_t irq_code;
235 irq_pio_range_t *ranges;
236 irq_cmd_t *cmds;
237 errno_t rc;
238
[646849b3]239 if (chan->irq < 0)
[64cf7a3]240 return EOK;
241
[59c0f478]242 ranges = malloc(sizeof(isa_ide_irq_ranges));
[64cf7a3]243 if (ranges == NULL)
244 return ENOMEM;
245
[59c0f478]246 cmds = malloc(sizeof(isa_ide_irq_cmds));
[64cf7a3]247 if (cmds == NULL) {
248 free(cmds);
249 return ENOMEM;
250 }
251
[59c0f478]252 memcpy(ranges, &isa_ide_irq_ranges, sizeof(isa_ide_irq_ranges));
[646849b3]253 ranges[0].base = chan->cmd_physical;
[59c0f478]254 memcpy(cmds, &isa_ide_irq_cmds, sizeof(isa_ide_irq_cmds));
[646849b3]255 cmds[0].addr = &chan->cmd->status;
[64cf7a3]256
[59c0f478]257 irq_code.rangecount = sizeof(isa_ide_irq_ranges) / sizeof(irq_pio_range_t);
[64cf7a3]258 irq_code.ranges = ranges;
[59c0f478]259 irq_code.cmdcount = sizeof(isa_ide_irq_cmds) / sizeof(irq_cmd_t);
[64cf7a3]260 irq_code.cmds = cmds;
261
[646849b3]262 ddf_msg(LVL_NOTE, "IRQ %d", chan->irq);
[60744cb]263 rc = register_interrupt_handler(chan->ctrl->dev, chan->irq,
[646849b3]264 isa_ide_irq_handler, (void *)chan, &irq_code, &chan->ihandle);
[64cf7a3]265 if (rc != EOK) {
266 ddf_msg(LVL_ERROR, "Error registering IRQ.");
267 goto error;
268 }
269
[2791fbb7]270 ddf_msg(LVL_DEBUG, "Interrupt handler registered");
[64cf7a3]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. */
[646849b3]281static void isa_ide_fini_irq(isa_ide_channel_t *chan)
[64cf7a3]282{
283 errno_t rc;
284 async_sess_t *parent_sess;
285
[646849b3]286 parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
[64cf7a3]287
[646849b3]288 rc = hw_res_disable_interrupt(parent_sess, chan->irq);
[64cf7a3]289 if (rc != EOK)
290 ddf_msg(LVL_ERROR, "Error disabling IRQ.");
291
[646849b3]292 (void) unregister_interrupt_handler(chan->ctrl->dev, chan->ihandle);
[64cf7a3]293}
294
[2791fbb7]295/** Interrupt handler.
[7a56e33e]296 *
[2791fbb7]297 * @param call Call data
[60744cb]298 * @param arg Argument (isa_ide_channel_t *)
[54d0ddc]299 */
[60744cb]300static void isa_ide_irq_handler(ipc_call_t *call, void *arg)
[54d0ddc]301{
[60744cb]302 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
[54d0ddc]303 uint8_t status;
[2791fbb7]304 async_sess_t *parent_sess;
[7a56e33e]305
[2791fbb7]306 status = ipc_get_arg1(call);
[646849b3]307 ata_channel_irq(chan->channel, status);
[7a56e33e]308
[646849b3]309 parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
310 hw_res_clear_interrupt(parent_sess, chan->irq);
[54d0ddc]311}
312
[2791fbb7]313/** Write the data register callback handler.
[88743b5]314 *
[646849b3]315 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]316 * @param data Data
317 * @param nwords Number of words to write
[88743b5]318 */
[59c0f478]319static void isa_ide_write_data_16(void *arg, uint16_t *data, size_t nwords)
[88743b5]320{
[646849b3]321 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
[88743b5]322 size_t i;
323
[2791fbb7]324 for (i = 0; i < nwords; i++)
[646849b3]325 pio_write_16(&chan->cmd->data_port, data[i]);
[88743b5]326}
327
[2791fbb7]328/** Read the data register callback handler.
[88743b5]329 *
[646849b3]330 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]331 * @param buf Destination buffer
332 * @param nwords Number of words to read
[88743b5]333 */
[59c0f478]334static void isa_ide_read_data_16(void *arg, uint16_t *buf, size_t nwords)
[88743b5]335{
[646849b3]336 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
[2791fbb7]337 size_t i;
[c2844735]338
[2791fbb7]339 for (i = 0; i < nwords; i++)
[646849b3]340 buf[i] = pio_read_16(&chan->cmd->data_port);
[c2844735]341}
342
[2791fbb7]343/** Write command register callback handler.
[c2844735]344 *
[646849b3]345 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]346 * @param off Register offset
347 * @param value Value to write to command register
[c2844735]348 */
[59c0f478]349static void isa_ide_write_cmd_8(void *arg, uint16_t off, uint8_t value)
[c2844735]350{
[646849b3]351 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
[c2844735]352
[646849b3]353 pio_write_8(((ioport8_t *)chan->cmd) + off, value);
[88743b5]354}
355
[2791fbb7]356/** Read command register callback handler.
[aa893e0]357 *
[646849b3]358 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]359 * @param off Register offset
360 * @return value Value read from command register
[aa893e0]361 */
[59c0f478]362static uint8_t isa_ide_read_cmd_8(void *arg, uint16_t off)
[0d247f5]363{
[646849b3]364 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
[0d247f5]365
[646849b3]366 return pio_read_8(((ioport8_t *)chan->cmd) + off);
[0d247f5]367}
368
[2791fbb7]369/** Write control register callback handler.
[4046b2f4]370 *
[646849b3]371 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]372 * @param off Register offset
373 * @param value Value to write to control register
[4046b2f4]374 */
[59c0f478]375static void isa_ide_write_ctl_8(void *arg, uint16_t off, uint8_t value)
[4046b2f4]376{
[646849b3]377 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
[35b8bfe]378
[646849b3]379 pio_write_8(((ioport8_t *)chan->ctl) + off, value);
[4046b2f4]380}
381
[2791fbb7]382/** Read control register callback handler.
[54d0ddc]383 *
[646849b3]384 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]385 * @param off Register offset
386 * @return value Value read from control register
[54d0ddc]387 */
[59c0f478]388static uint8_t isa_ide_read_ctl_8(void *arg, uint16_t off)
[f8ef660]389{
[646849b3]390 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
[f8ef660]391
[646849b3]392 return pio_read_8(((ioport8_t *)chan->ctl) + off);
[f8ef660]393}
394
[2791fbb7]395/** Enable IRQ callback handler
[54d0ddc]396 *
[646849b3]397 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]398 * @return EOK on success or an error code
[54d0ddc]399 */
[59c0f478]400static errno_t isa_ide_irq_enable(void *arg)
[d9f4c76]401{
[646849b3]402 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
[2791fbb7]403 async_sess_t *parent_sess;
[b7fd2a0]404 errno_t rc;
[d9f4c76]405
[646849b3]406 ddf_msg(LVL_DEBUG, "Enable IRQ %d for channel %u",
407 chan->irq, chan->chan_id);
[d9f4c76]408
[646849b3]409 parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
[a7de7907]410
[646849b3]411 rc = hw_res_enable_interrupt(parent_sess, chan->irq);
[2791fbb7]412 if (rc != EOK) {
413 ddf_msg(LVL_ERROR, "Error enabling IRQ.");
414 return rc;
[31de325]415 }
416
[2791fbb7]417 return EOK;
[d9f4c76]418}
419
[2791fbb7]420/** Disable IRQ callback handler
[4abe919]421 *
[646849b3]422 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]423 * @return EOK on success or an error code
[4abe919]424 */
[59c0f478]425static errno_t isa_ide_irq_disable(void *arg)
[4abe919]426{
[646849b3]427 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
[2791fbb7]428 async_sess_t *parent_sess;
[b7fd2a0]429 errno_t rc;
[4abe919]430
[2791fbb7]431 ddf_msg(LVL_DEBUG, "Disable IRQ");
[4abe919]432
[646849b3]433 parent_sess = ddf_dev_parent_sess_get(chan->ctrl->dev);
[4abe919]434
[646849b3]435 rc = hw_res_disable_interrupt(parent_sess, chan->irq);
[2791fbb7]436 if (rc != EOK) {
[646849b3]437 ddf_msg(LVL_ERROR, "Error disabling IRQ.");
[2791fbb7]438 return rc;
[4abe919]439 }
440
[2791fbb7]441 return EOK;
[4abe919]442}
443
[2791fbb7]444/** Add ATA device callback handler.
[21989e5]445 *
[646849b3]446 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]447 * @param idx Device index
448 * $param charg Connection handler argument
449 * @return EOK on success or an error code
[21989e5]450 */
[59c0f478]451static errno_t isa_ide_add_device(void *arg, unsigned idx, void *charg)
[21989e5]452{
[646849b3]453 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
454 return isa_ide_fun_create(chan, idx, charg);
[21989e5]455}
456
[2791fbb7]457/** Remove ATA device callback handler.
[5048be7]458 *
[646849b3]459 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]460 * @param idx Device index
461 * @return EOK on success or an error code
[5048be7]462 */
[59c0f478]463static errno_t isa_ide_remove_device(void *arg, unsigned idx)
[5048be7]464{
[646849b3]465 isa_ide_channel_t *chan = (isa_ide_channel_t *)arg;
466 return isa_ide_fun_remove(chan, idx);
[5048be7]467}
468
[2791fbb7]469/** Debug message callback handler.
[96cbd18]470 *
[646849b3]471 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]472 * @param msg Message
[5048be7]473 */
[59c0f478]474static void isa_ide_msg_debug(void *arg, char *msg)
[5048be7]475{
[2791fbb7]476 (void)arg;
477 ddf_msg(LVL_DEBUG, "%s", msg);
[5048be7]478}
479
[2791fbb7]480/** Notice message callback handler.
[54d0ddc]481 *
[646849b3]482 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]483 * @param msg Message
[54d0ddc]484 */
[59c0f478]485static void isa_ide_msg_note(void *arg, char *msg)
[54d0ddc]486{
[2791fbb7]487 (void)arg;
488 ddf_msg(LVL_NOTE, "%s", msg);
[54d0ddc]489}
490
[2791fbb7]491/** Warning message callback handler.
[64cf7a3]492 *
[646849b3]493 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]494 * @param msg Message
[64cf7a3]495 */
[59c0f478]496static void isa_ide_msg_warn(void *arg, char *msg)
[64cf7a3]497{
[2791fbb7]498 (void)arg;
499 ddf_msg(LVL_WARN, "%s", msg);
[64cf7a3]500}
501
[2791fbb7]502/** Error message callback handler.
[64cf7a3]503 *
[646849b3]504 * @param arg Argument (isa_ide_channel_t *)
[2791fbb7]505 * @param msg Message
[64cf7a3]506 */
[59c0f478]507static void isa_ide_msg_error(void *arg, char *msg)
[64cf7a3]508{
[2791fbb7]509 (void)arg;
510 ddf_msg(LVL_ERROR, "%s", msg);
[64cf7a3]511}
512
[f8ef660]513/**
514 * @}
515 */
Note: See TracBrowser for help on using the repository browser.