source: trunk/hal/x86_64/drivers/ioc_ata.c @ 293

Last change on this file since 293 was 280, checked in by max@…, 7 years ago

sync

File size: 5.8 KB
Line 
1/*
2 * ioc_ata.c - ATA driver implementation
3 *
4 * Copyright (c) 2017 Maxime Villard
5 *
6 * This file is part of ALMOS-MKH.
7 *
8 * ALMOS-MKH is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2.0 of the License.
11 *
12 * ALMOS-MKH is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <chdev.h>
23#include <dev_ioc.h>
24#include <hal_drivers.h>
25#include <hal_kentry.h>
26#include <thread.h>
27#include <spinlock.h>
28
29#include <hal_internal.h>
30
31#define PIO_ATA_CBR_BASE        0x1F0
32#       define ATA_DATA         0x000
33#       define ATA_ERRFEAT      0x001 /* two regs */
34#       define ATA_SCR          0x002
35#       define ATA_SNR          0x003 /* lba0 */
36#       define ATA_CLR          0x004 /* lba1 */
37#       define ATA_CHR          0x005 /* lba2 */
38#       define ATA_DHR          0x006 /* drive | lba3 */
39#       define ATA_SR           0x007
40#               define ATA_SR_ERR       0x01
41#               define ATA_SR_IDX       0x02
42#               define ATA_SR_CORR      0x04
43#               define ATA_SR_DRQ       0x08
44#               define ATA_SR_DSC       0x10
45#               define ATA_SR_DF        0x20
46#               define ATA_SR_DRDY      0x40
47#               define ATA_SR_BSY       0x80
48#       define ATA_CR           0x007 /* two regs */
49
50#define ATA_CMD_READ_SECTORS_RETRY      0x20
51#define ATA_CMD_READ_SECTORS_NORETRY    0x21
52#define ATA_CMD_WRITE_SECTORS_RETRY     0x30
53#define ATA_CMD_WRITE_SECTORS_NORETRY   0x31
54#define ATA_CMD_IDENTIFY                0xEC
55
56static inline uint16_t ata_data_read()
57{
58        return in16(PIO_ATA_CBR_BASE + ATA_DATA);
59}
60
61static inline void ata_data_write(uint16_t val)
62{
63        out16(PIO_ATA_CBR_BASE + ATA_DATA, val);
64}
65
66static inline uint8_t ata_cbr_read(uint32_t reg)
67{
68        return in8(PIO_ATA_CBR_BASE + reg);
69}
70
71static inline void ata_cbr_write(uint32_t reg, uint8_t val)
72{
73        out8(PIO_ATA_CBR_BASE + reg, val);
74}
75
76static inline int ata_wait()
77{
78        uint8_t status;
79
80        while (1) {
81                status = ata_cbr_read(ATA_SR);
82                if (status & ATA_SR_DRQ)
83                        break;
84        }
85
86        return ((status & ATA_SR_ERR) ? -1 : 0);
87}
88
89static void ata_prepare(uint8_t slave, uint32_t lba, uint8_t count)
90{
91        ata_cbr_write(ATA_ERRFEAT, 0x00); /* NULL byte to port 0x1F1 */
92        ata_cbr_write(ATA_SCR, count); /* sector count */
93
94        /* set the lba */
95        ata_cbr_write(ATA_SNR, (lba >> 0) & 0xFF);
96        ata_cbr_write(ATA_CLR, (lba >> 8) & 0xFF);
97        ata_cbr_write(ATA_CHR, (lba >> 16)& 0xFF);
98
99        /* set the drive and lba3 */
100        ata_cbr_write(ATA_DHR, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
101}
102
103static int ata_read(uint8_t count, char *buf)
104{
105        uint16_t tmpword;
106        size_t idx, n;
107
108        for (n = 0; n < count; n++) {
109                /* wait for the drive to signal that it's ready */
110                if (ata_wait() == -1)
111                        x86_panic("ata_wait");
112
113                /* read one block */
114                for (idx = 0; idx < 256; idx++) {
115                        tmpword = ata_data_read();
116                        buf[n * 512 + idx * 2] = (uint8_t)(tmpword & 0xFF);
117                        buf[n * 512 + idx * 2 + 1] = (uint8_t)(tmpword >> 8);
118                }
119        }
120
121        return 0;
122}
123
124static int ata_write(uint8_t count, char *buf)
125{
126        uint16_t tmpword;
127        size_t idx, n;
128
129        for (n = 0; n < count; n++) {
130                /* wait for the drive to signal that it's ready */
131                if (ata_wait() == -1)
132                        x86_panic("ata_wait");
133
134                /* write one block */
135                for (idx = 0; idx < 256; idx++) {
136                        tmpword = (buf[n * 512 + idx * 2 + 1] << 8) |
137                                   buf[n * 512 + idx * 2];
138                        ata_data_write(tmpword);
139                }
140        }
141
142        return 0;
143}
144
145static uint16_t bswap16(uint16_t x)
146{
147        return ((x << 8) & 0xFF00) | ((x >> 8) & 0x00FF);
148}
149
150static void ata_init()
151{
152        uint8_t id[512];
153        uint16_t tmpw, *p;
154        size_t idx;
155        char *model;
156        uint8_t ncyl;
157        int ret;
158
159        ata_prepare(0, 0, 0);
160        ata_cbr_write(ATA_CR, ATA_CMD_IDENTIFY);
161
162        /* wait for the drive to signal that it's ready */
163        ret = ata_wait();
164        if (ret == -1)
165                x86_panic("-> unable to identify ATA\n");
166
167        for (idx = 0; idx < 256; idx++) {
168                tmpw = ata_data_read();
169                id[idx * 2]     = (uint8_t)((tmpw >> 0) & 0xFF);
170                id[idx * 2 + 1] = (uint8_t)((tmpw >> 8) & 0xFF);
171        }
172
173        ncyl = id[0] & 0x1;
174        x86_printf("-> cyl: %z\n", (uint64_t)ncyl);
175
176        for (idx = 27*2; idx < 46*2; idx += 2) {
177                p = (uint16_t *)(&id[idx]);
178                *p = bswap16(*p);
179        }
180
181        model = &id[27*2];
182        id[46*2] = '\0';
183        x86_printf("-> ATA model: '%s'\n", model);
184}
185
186/* -------------------------------------------------------------------------- */
187
188static void ioc_ata_cmd(xptr_t th_xp);
189extern void x86_ioapic_ata0();
190
191void ioc_ata_init(chdev_t *chdev)
192{
193        chdev->cmd = &ioc_ata_cmd;
194        chdev->isr = &x86_ioapic_ata0;
195        ata_init();
196}
197
198static void ioc_ata_cmd(xptr_t th_xp)
199{
200        uint32_t   cmd_type;     // IOC_READ / IOC_WRITE / IOC_SYNC_READ
201        uint32_t   lba;          // command argument
202        uint32_t   count;        // command argument
203        xptr_t     buf_xp;       // command argument
204
205        // get client thread cluster and local pointer
206        cxy_t      th_cxy = GET_CXY( th_xp );
207        thread_t * th_ptr = (thread_t *)GET_PTR( th_xp );
208
209        // get command arguments and extended pointer on IOC device
210        cmd_type =         hal_remote_lw ( XPTR( th_cxy , &th_ptr->ioc_cmd.type   ) );
211        lba      =         hal_remote_lw ( XPTR( th_cxy , &th_ptr->ioc_cmd.lba    ) );
212        count    =         hal_remote_lw ( XPTR( th_cxy , &th_ptr->ioc_cmd.count  ) );
213        buf_xp   = (xptr_t)hal_remote_lwd( XPTR( th_cxy , &th_ptr->ioc_cmd.buf_xp ) );
214
215        /* execute operation */
216        ata_prepare(0, lba, count);
217        if (cmd_type == IOC_WRITE) {
218                ata_cbr_write(ATA_CR, ATA_CMD_WRITE_SECTORS_RETRY);
219        } else {
220                ata_cbr_write(ATA_CR, ATA_CMD_READ_SECTORS_RETRY);
221        }
222
223        /* waiting policy depends on the command type */
224        if (cmd_type == IOC_SYNC_READ) {
225                ata_read(count, (char *)buf_xp);
226        } else {
227                x86_panic("!IOC_SYNC_READ not supported");
228        }
229}
230
231void ioc_ata_isr(hal_cpu_context_t *ctx)
232{
233        x86_printf("rip = %Z\n", ctx->tf_rip);
234
235        x86_panic((char *)__func__);
236}
237
Note: See TracBrowser for help on using the repository browser.