1#include <asm.h>
2#include <console/printf.h>
3#include <drivers/ahci.h>
4#include <drivers/ata.h>
5#include <drivers/pci.h>
6#include <mem/alloc.h>
7#include <registry.h>
8#include <stdbool.h>
9#include <stdint.h>
10#include <time/spin_sleep.h>
11
12void ata_select_drive(struct ata_drive *ata_drive) {
13 uint16_t base = ata_drive->io_base;
14
15 outb(REG_DRIVE_HEAD(base), value: 0xA0 | (ata_drive->slave ? 0x10 : 0x00));
16 io_wait();
17}
18
19void ata_soft_reset(struct ata_drive *ata_drive) {
20 uint16_t ctrl = ata_drive->ctrl_base;
21
22 outb(port: ctrl, value: 0x04); // nIEN=0, SRST=1
23 io_wait();
24
25 outb(port: ctrl, value: 0x00); // nIEN=0, SRST=0
26 io_wait();
27
28 uint16_t base = ata_drive->io_base;
29 uint64_t timeout = IDE_CMD_TIMEOUT_MS * 1000;
30
31 while (inb(REG_STATUS(base)) & STATUS_BSY) {
32 sleep_spin_us(us: 10);
33 timeout--;
34 if (timeout == 0)
35 return;
36 }
37}
38
39bool ata_identify(struct ata_drive *ata_drive) {
40 ata_select_drive(ata_drive);
41 io_wait();
42
43 outb(REG_COMMAND(ata_drive->io_base), AHCI_CMD_IDENTIFY);
44 uint8_t status = inb(REG_STATUS(ata_drive->io_base));
45
46 if (status == 0)
47 return false;
48
49 uint64_t timeout = IDE_IDENT_TIMEOUT_MS * 1000;
50 while ((status & STATUS_BSY)) {
51 status = inb(REG_STATUS(ata_drive->io_base));
52 sleep_spin_us(us: 1);
53 timeout--;
54 if (timeout == 0)
55 return false;
56 }
57
58 if (inb(REG_LBA_MID(ata_drive->io_base)) ||
59 inb(REG_LBA_HIGH(ata_drive->io_base)))
60 return false;
61
62 insw(port: ata_drive->io_base, addr: (uint16_t *) ata_drive->identify_data, count: 256);
63 return true;
64}
65
66bool ata_setup_drive(struct ata_drive *ide, struct pci_device *devices,
67 uint64_t count, int channel, bool is_slave) {
68
69 for (uint64_t i = 0; i < count; i++) {
70 struct pci_device *curr = &devices[i];
71
72 if (curr->class_code == 1 && curr->subclass == 1) {
73 uint32_t bar =
74 pci_read_bar(bus: curr->bus, device: curr->dev, function: curr->function, bar_index: channel * 2);
75
76 uint32_t ctrl_bar = pci_read_bar(bus: curr->bus, device: curr->dev,
77 function: curr->function, bar_index: channel * 2 + 1);
78 ide->io_base = (bar & 1) ? (bar & 0xFFFFFFFC)
79 : ((channel == 0) ? ATA_PRIMARY_IO
80 : ATA_SECONDARY_IO);
81
82 uint8_t prog_if = pci_read_config8(bus: curr->bus, device: curr->dev,
83 function: curr->function, PCI_PROG_IF);
84 bool primary_native = (prog_if & 0x01);
85 bool secondary_native = (prog_if & 0x04);
86
87 if ((channel == 0 && !primary_native) ||
88 (channel == 1 && !secondary_native)) {
89 ide->irq = (channel == 0) ? 14 : 15;
90 } else {
91 ide->irq = pci_read_config8(bus: curr->bus, device: curr->dev,
92 function: curr->function, PCI_INTERRUPT_LINE);
93 }
94
95 ide->ctrl_base =
96 (ctrl_bar & 1)
97 ? (ctrl_bar & 0xFFFFFFFC)
98 : ((channel == 0) ? ATA_PRIMARY_CTRL : ATA_SECONDARY_CTRL);
99
100 ide->slave = is_slave;
101 ide->identify_data = kmalloc(512);
102 if (!ide->identify_data)
103 panic("Could not allocate space for IDE Identify");
104
105 ata_select_drive(ata_drive: ide);
106 ata_soft_reset(ata_drive: ide);
107
108 if (ata_identify(ata_drive: ide)) {
109 ide->type = IDE_TYPE_ATA;
110 ide->sector_size = 512;
111 return true;
112 } else if (atapi_identify(ide)) {
113 ide->type = IDE_TYPE_ATAPI;
114 ide->sector_size = 2048;
115 return true;
116 }
117
118 return false;
119 }
120 }
121
122 ide->io_base = 0;
123 ide->ctrl_base = 0;
124 ide->slave = 0;
125 return false;
126}
127
128static uint64_t ide_cnt = 1, atapi_cnt = 1;
129
130void ata_init(struct pci_device *devices, uint64_t count) {
131 struct ata_drive *drives =
132 kmalloc(sizeof(struct ata_drive) * 4, ALLOC_FLAGS_ZERO);
133 if (!drives)
134 panic("Could not allocate space for devices");
135
136 for (int i = 0; i < 2; i++) {
137 for (int j = 0; j < 2; j++) {
138 int ind = i * 2 + j;
139 if (ata_setup_drive(ide: &drives[ind], devices, count, channel: i, is_slave: j)) {
140 struct block_device *d = NULL;
141
142 if (drives[ind].type == IDE_TYPE_ATA) {
143 d = ide_create_generic(ide: &drives[ind]);
144 registry_mkname(disk: d, prefix: "ata", counter: ide_cnt++);
145 } else if (drives[ind].type == IDE_TYPE_ATAPI) {
146 d = atapi_create_generic(d: &drives[ind]);
147 registry_mkname(disk: d, prefix: "cdrom", counter: atapi_cnt++);
148 }
149
150 if (!d)
151 continue;
152
153 k_print_register(d->name);
154 registry_register(disk: d);
155 }
156 }
157 }
158}
159