source: trunk/kernel/drivers/ibmpc/ata.c @ 17

Last change on this file since 17 was 4, checked in by alain, 7 years ago

Introduce the chdev_t structure in place of device_t.

File size: 5.8 KB
Line 
1/*
2 * ata.c - ATA hard disk driver
3 *
4 * Copyright (c) 2008,2009,2010,2011,2012 Ghassan Almaless
5 * Copyright (c) 2011,2012 UPMC Sorbonne Universites
6 *
7 * This file is part of ALMOS-kernel.
8 *
9 * ALMOS-kernel is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2.0 of the License.
12 *
13 * ALMOS-kernel is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with ALMOS-kernel; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#include <chdev.h>
24#include <driver.h>
25#include <cpu.h>
26#include <thread.h>
27#include <scheduler.h>
28#include <kmem.h>
29#include <sysio.h>
30#include <string.h>
31#include <event.h>
32#include <cpu-trace.h>
33#include <cpu-io.h>
34#include <ata.h>
35
36#define ATA_BASE(x)  ((x) & 0xFFFF)
37#define ATA_DRIVE(x) ((x) >> 16)
38
39static void ata_start_request(struct device_s *dev, dev_request_t *rq, uint32_t type)
40{
41        uint16_t base  = ATA_BASE((uint32_t)dev->base);
42        uint16_t drive = ATA_DRIVE((uint32_t)dev->base);
43        uint32_t lba   = (uint32_t)rq->src;
44
45        cpu_io_out8(base + ATA_ERROR_REG, 0x00);
46        cpu_io_out8(base + ATA_COUNT_REG, rq->count);
47        cpu_io_out8(base + ATA_LBA_LOW_REG, (uint8_t)lba); 
48        cpu_io_out8(base + ATA_LBA_MID_REG, (uint8_t)(lba >> 8));
49        cpu_io_out8(base + ATA_LBA_HIGH_REG,(uint8_t)(lba >> 16));
50        cpu_io_out8(base + ATA_DRIVE_REG, 0xE0 | (drive << 4) | ((lba >> 24) & 0x0F));
51        cpu_io_out8(base + ATA_CMD_REG, type);
52}
53
54error_t ata_busy_wait(uint_t base)
55{
56        uint_t val;
57        uint_t val2;
58
59        while((val=cpu_io_in8(base + ATA_STATUS_REG)) & ATA_BSY)
60                val ++;
61 
62        val2 = val;
63
64        return val2 - val;
65}
66
67static void ata_finalize_request(struct device_s *dev, dev_request_t *rq, uint32_t type)
68{
69        register uint16_t val;
70        register uint16_t base;
71        register uint_t index;
72        register uint8_t *buffer; 
73        error_t err;
74
75        base = ATA_BASE((uint32_t)dev->base);
76        buffer = (uint8_t*) rq->dst;
77
78        if((err=ata_busy_wait(base)))
79        {
80                printk(ERROR, "ERROR: ata_finalize_request: time out [dev %x, cmd %x, start lba %d, count %d]\n", 
81                       dev->base, 
82                       type, 
83                       (uint32_t)rq->src, 
84                       rq->count);
85
86                return;
87        }
88
89        if(type == ATA_READ_CMD)
90        {
91                for (index = 0; index < 256 * rq->count; index++) 
92                {
93                        val = cpu_io_in16(base + ATA_DATA_REG);
94                        buffer[index * 2    ] = (uint8_t) val;
95                        buffer[index * 2 + 1] = (uint8_t)(val >> 8);
96                }
97                return;
98        }
99 
100        for (index = 0; index < 256 * rq->count; index++) 
101        {
102                val = (buffer[index * 2 + 1] << 8) | buffer[index * 2];
103                cpu_io_out16(base + ATA_DATA_REG, val);
104        }
105}
106
107void __attribute__ ((noinline)) ata_irq_handler(struct irq_action_s *action)
108{
109        register struct device_s *hdd;
110        register struct ata_context_s *ctx;
111        uint16_t base;
112        uint8_t status;
113
114        cpu_trace_write(CURRENT_THREAD->local_cpu, ata_irq_handler);
115
116        hdd = action->dev;
117        base = ATA_BASE((uint32_t)hdd->base);
118        ctx = (struct ata_context_s*)hdd->data;
119
120        status = cpu_io_in8(base + ATA_STATUS_REG); /* IRQ ACK */
121
122        isr_dmsg(INFO, "INFO: Recived irq on DevBlk %x, Drive %d, status %x\n", 
123                 base, 
124                 ATA_DRIVE((uint32_t)hdd->base), 
125                 status);
126}
127
128
129int32_t __attribute__ ((noinline)) ata_request(struct device_s *blk_dev, dev_request_t *rq, uint32_t type)
130{
131        struct ata_context_s *ctx;
132
133        cpu_trace_write(CURRENT_THREAD->local_cpu, ata_request);
134 
135        ctx = (struct ata_context_s*)blk_dev->data;
136
137        if((rq->count + ((uint_t)rq->src)) > ctx->params.blk_count)
138                return -1;
139
140        ata_start_request(blk_dev, rq, type);
141        ata_finalize_request(blk_dev, rq, type);
142
143        return 0;
144}
145
146
147static sint_t ata_read(struct device_s *blk_dev, dev_request_t *rq)
148{
149        return ata_request(blk_dev, rq, ATA_READ_CMD);
150}
151
152
153static sint_t ata_write(struct device_s *blk_dev, dev_request_t *rq)
154{
155        return ata_request(blk_dev, rq, ATA_WRITE_CMD);
156}
157
158
159static sint_t ata_get_params(struct device_s *blk_dev, dev_params_t *params) 
160{
161        struct ata_context_s *ctx = (struct ata_context_s*) blk_dev->data;
162        params->blk.count = ctx->params.blk_count;
163        params->blk.size = ctx->params.blk_size;
164        return 0;
165}
166
167void ibmpc_ata_init(struct device_s *hdd, void *base, uint_t irq)
168{
169        struct ata_context_s *ctx;
170        error_t err;
171        uint_t index;
172        uint16_t *buffer;
173        uint16_t dev_base;
174        uint16_t drive;
175
176        spinlock_init(&hdd->lock);
177        hdd->base = base;
178        hdd->irq = irq;
179        hdd->type = DEV_BLK;
180
181        hdd->action.dev = hdd;
182        hdd->action.irq_handler = &ata_irq_handler;
183        hdd->action.data = NULL;
184
185        strcpy(hdd->name, "BLK-DEV");
186        metafs_init(&hdd->node, hdd->name);
187
188        hdd->op.dev.open = NULL;
189        hdd->op.dev.read = &ata_read;
190        hdd->op.dev.write = &ata_write;
191        hdd->op.dev.close = NULL;
192        hdd->op.dev.lseek = NULL;
193        hdd->op.dev.get_params = &ata_get_params;
194        hdd->op.dev.set_params = NULL;
195
196        ctx = kbootMem_calloc(sizeof(*ctx));
197        list_root_init(&ctx->request_queue);
198        wait_queue_init(&ctx->pending, hdd->name);
199 
200        dev_base = ATA_BASE((uint32_t)base);
201        drive = ATA_DRIVE((uint32_t)base);
202
203        cpu_io_out8(dev_base + ATA_DRIVE_REG, 0xA0 | (drive << 4));
204        cpu_io_out8(dev_base + ATA_CMD_REG, ATA_IDENTIFY_CMD);
205 
206        if((err=ata_busy_wait(dev_base)))
207        {
208                printk(ERROR, "ERROR: ibmpc_ata_init: dev %x, ATA_IDENTIFY_CMD time out\n", dev_base);
209                return;
210        }
211
212        buffer = (uint16_t *)&ctx->params.info;
213
214        for(index = 0; index < 256; index++)
215                buffer[index] = cpu_io_in16(dev_base + ATA_DATA_REG);
216 
217        assert((ctx->params.info.capabilities & ATA_LBA_CAP) && "LBA mode is mandatory");
218 
219        ctx->params.blk_size = 512;
220        ctx->params.blk_count = ctx->params.info.sectors;
221        hdd->data = (void *)ctx;
222       
223        printk(INFO, "INFO: ATA(%x) Drive(%d): (size,count) %d:%d\n", 
224               dev_base, 
225               drive, 
226               ctx->params.blk_size, 
227               ctx->params.blk_count);
228}
Note: See TracBrowser for help on using the repository browser.