summaryrefslogtreecommitdiff
path: root/src/libultra/io/epirawdma.c
blob: 3f238dd3fcb456262a74bca0409315ca5690e588 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#include "ultra64.h"
#include "ultra64/bcp.h"

s32 __osEPiRawStartDma(OSPiHandle* handle, s32 direction, u32 cartAddr, void* dramAddr, size_t size) {
#ifdef BBPLAYER
    u64 dummybuf[2];
#endif
    u32 status;
    OSPiHandle* curHandle;
#ifdef BBPLAYER
    u32 buffer;
    u32 pgsize;
    u16* adr;
    u32 i;
#endif

    status = IO_READ(PI_STATUS_REG);
    while (status & (PI_STATUS_DMA_BUSY | PI_STATUS_IO_BUSY)) {
        status = IO_READ(PI_STATUS_REG);
    }

    if (__osCurrentHandle[handle->domain]->type != handle->type) {
        curHandle = __osCurrentHandle[handle->domain];

        if (handle->domain == 0) {
            if (curHandle->latency != handle->latency) {
                IO_WRITE(PI_BSD_DOM1_LAT_REG, handle->latency);
            }

            if (curHandle->pageSize != handle->pageSize) {
                IO_WRITE(PI_BSD_DOM1_PGS_REG, handle->pageSize);
            }

            if (curHandle->relDuration != handle->relDuration) {
                IO_WRITE(PI_BSD_DOM1_RLS_REG, handle->relDuration);
            }

            if (curHandle->pulse != handle->pulse) {
                IO_WRITE(PI_BSD_DOM1_PWD_REG, handle->pulse);
            }
        } else {
            if (curHandle->latency != handle->latency) {
                IO_WRITE(PI_BSD_DOM2_LAT_REG, handle->latency);
            }

            if (curHandle->pageSize != handle->pageSize) {
                IO_WRITE(PI_BSD_DOM2_PGS_REG, handle->pageSize);
            }

            if (curHandle->relDuration != handle->relDuration) {
                IO_WRITE(PI_BSD_DOM2_RLS_REG, handle->relDuration);
            }

            if (curHandle->pulse != handle->pulse) {
                IO_WRITE(PI_BSD_DOM2_PWD_REG, handle->pulse);
            }
        }

        curHandle->type = handle->type;
        curHandle->latency = handle->latency;
        curHandle->pageSize = handle->pageSize;
        curHandle->relDuration = handle->relDuration;
        curHandle->pulse = handle->pulse;
    }

#ifdef BBPLAYER
    if (direction == OS_READ) {
        // Device page size in bytes
        pgsize = 1;
        for (i = 1; i <= (u32)handle->pageSize + 2; i++) {
            pgsize *= 2;
        }

        // If the initial cart address mod pgsize is at the last u16 in the page,
        // need to do some manual DMAs?
        if ((cartAddr & (pgsize - 1)) == pgsize - sizeof(u16)) {
            // Read 32 bits starting 2 bytes before the target DMA address,
            // so that the lower 16 bits of the result are the first 2 bytes
            // of the requested data.
            __osEPiRawReadIo(handle, cartAddr - sizeof(u16), &buffer);

            // Poke the lower 16 bits into the destination address
            adr = (u16*)PHYS_TO_K1(dramAddr);
            *(adr++) = (u16)buffer;

            // Update DMA parameters
            cartAddr += sizeof(u16);
            dramAddr = adr;
            size -= sizeof(u16);

            // If the remaining size is >= 4
            if (size >= sizeof(u32)) {
                // Read another 32 bits at the cart addr
                __osEPiRawReadIo(handle, cartAddr, &buffer);

                // Store all 32 bits to RAM
                adr = (u16*)dramAddr;
                *(adr++) = buffer >> 16;
                *(adr++) = (u16)buffer;

                // Update DMA parameters again
                cartAddr += sizeof(u32);
                dramAddr = adr;
                size -= sizeof(u32);

                // If we're not at the end of the DMA
                if (size != 0) {
                    // Read 32 bits again
                    __osEPiRawReadIo(handle, cartAddr, &buffer);

                    // Store just the upper 16 bits
                    adr = (u16*)PHYS_TO_K1(dramAddr);
                    *(adr++) = buffer >> 16;

                    // Update DMA parameters once more
                    cartAddr += sizeof(u16);
                    dramAddr = adr;
                    size -= sizeof(u16);
                }
            }
        }

        // If the end cart address mod pgsize is just 2 bytes into a page or the remaining data size is just 1x u16
        if (((((cartAddr + size) & (pgsize - 1)) == sizeof(u16)) | (size == sizeof(u16))) != 0) {
            if ((cartAddr + size) & 2) {
                // Read 32 bits at end - 2, store the upper 16 bits
                __osEPiRawReadIo(handle, cartAddr + size - sizeof(u16), &buffer);
                adr = (u16*)PHYS_TO_K1(dramAddr) + (size - sizeof(u16)) / sizeof(u16);
                *adr = buffer >> 16;
            } else {
                // Read 32 bits at end - 4, store the lower 16 bits
                __osEPiRawReadIo(handle, cartAddr + size - sizeof(u32), &buffer);
                adr = (u16*)PHYS_TO_K1(dramAddr) + (size - sizeof(u16)) / sizeof(u16);
                *adr = (u16)buffer;
            }
            size -= sizeof(u16);
        }

        if (size == 0) {
            // If size ended up 0 following the adjustments, run an 8-byte dummy DMA anyway
            size = 8;
            dramAddr = (void*)dummybuf;
            cartAddr = 0;
        }
    }
#endif

    IO_WRITE(PI_DRAM_ADDR_REG, osVirtualToPhysical(dramAddr));
    IO_WRITE(PI_CART_ADDR_REG, K1_TO_PHYS(handle->baseAddress | cartAddr));

#ifdef BBPLAYER
    if (direction != OS_READ && direction != OS_WRITE) {
        return -1;
    }

    if ((handle->baseAddress | cartAddr) <= 0x400) {
        IO_WRITE((direction == OS_READ) ? PI_EX_WR_LEN_REG : PI_EX_RD_LEN_REG, size - 1);
    } else {
        IO_WRITE((direction == OS_READ) ? PI_WR_LEN_REG : PI_RD_LEN_REG, size - 1);
    }
#else
    switch (direction) {
        case OS_READ:
            IO_WRITE(PI_WR_LEN_REG, size - 1);
            break;
        case OS_WRITE:
            IO_WRITE(PI_RD_LEN_REG, size - 1);
            break;
        default:
            return -1;
    }
#endif
    return 0;
}