summaryrefslogtreecommitdiffstats
path: root/firmware/target/arm/pp/ata-pp5020.c
blob: 396e684019015cd6fb7e53af84c81d3226fc1f00 (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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2006 by Barry Wardell
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

/* ATA stuff was taken from the iPod code */

#include <stdbool.h>
#include "system.h"
#include "kernel.h"
#include "ata-driver.h"
#include "ata.h"

void ata_reset()
{

}

void ata_enable(bool on)
{
    /* TODO: Implement ata_enable() */
    (void)on;
}

bool ata_is_coldstart()
{
    return false;
    /* TODO: Implement coldstart variable */
}

/* These are PIO timings for 80 Mhz.  At 24 Mhz, the first value is 0 but the
   rest are the same. They go in IDE0_PRI_TIMING0.

   Rockbox used to use 0x10, and test_disk shows that leads to faster PIO.
   However when used with mSATA and some SD adapters this causes corrupt data
   so we now unconditionally use these timings from the OF.
*/
static const unsigned long pio80mhz[] = {
    0xC293, 0x43A2, 0x11A1, 0x7232, 0x3131
};

void ata_device_init()
{
#ifdef SAMSUNG_YH920
    CPU_INT_DIS = (1<<IDE_IRQ);
#endif
#ifdef HAVE_ATA_DMA
    IDE_DMA_CONTROL |= 2;
    IDE_DMA_CONTROL &= ~1;
    IDE0_CFG &= ~0x8010;
    IDE0_CFG |= 0x20;
#else

    /* From ipod-ide.c:ipod_ide_register() */
    IDE0_CFG |= (1<<5);
#ifdef IPOD_NANO
    IDE0_CFG |= (0x10000000); /* cpu > 65MHz */
#else
    IDE0_CFG &=~(0x10000000); /* cpu < 65MHz */
#endif
#endif

    IDE0_PRI_TIMING0 = pio80mhz[0];
    IDE0_PRI_TIMING1 = 0x80002150;
}

/* Setup the timing for PIO mode */
void ata_set_pio_timings(int mode)
{
    IDE0_PRI_TIMING0 = pio80mhz[mode];
}

#ifdef HAVE_ATA_DMA
/* Timings for multi-word and ultra DMA modes.
   These go in IDE0_PRI_TIMING1
 */
static const unsigned long tm_mwdma[] = {
    0xF9F92, 0x56562, 0x45451
};

static const unsigned long tm_udma[] = {
    0x800037C1, 0x80003491, 0x80003371,
#if ATA_MAX_UDMA > 2
    0x80003271, 0x80003071
#endif
};

#if ATA_MAX_UDMA > 2
static bool dma_boosted = false;
static bool dma_needs_boost;
#endif

/* This function sets up registers for 80 Mhz.
   Ultra DMA mode 2 works at 30 Mhz.
 */
void ata_dma_set_mode(unsigned char mode) {
    int modeidx;

    (*(volatile unsigned long *)(0x600060C4)) = 0xC0000000; /* 80 Mhz */
#if !defined(IPOD_NANO)
    IDE0_CFG &= ~0x10000000;
#endif

    modeidx = mode & 7;
    mode &= 0xF8;
    if (mode == 0x40 && modeidx <= ATA_MAX_UDMA) {
        IDE0_PRI_TIMING1 = tm_udma[modeidx];
#if ATA_MAX_UDMA > 2
        if (modeidx > 2)
            dma_needs_boost = true;
        else
            dma_needs_boost = false;
#endif
    } else if (mode == 0x20 && modeidx <= ATA_MAX_MWDMA)
        IDE0_PRI_TIMING1 = tm_mwdma[modeidx];

#if !defined(IPOD_NANO)
    IDE0_CFG |= 0x20000000; /* >= 50 Mhz */
#endif
}

#define IDE_CFG_INTRQ           8
#define IDE_DMA_CONTROL_READ    8

/* This waits for an ATA interrupt using polling.
   In ATA_CONTROL, CONTROL_nIEN must be cleared.
 */
static ICODE_ATTR int ata_wait_intrq(void)
{
    long timeout = current_tick + HZ*10;

    do
    {
        if (IDE0_CFG & IDE_CFG_INTRQ)
            return 1;
        ata_keep_active();
        yield();
    } while (TIME_BEFORE(current_tick, timeout));

    return 0; /* timeout */
}

/* This function checks if parameters are appropriate for DMA,
   and if they are, it sets up for DMA.

   If return value is false, caller may use PIO for this transfer.

   If return value is true, caller must issue a DMA ATA command
   and then call ata_dma_finish().
 */
bool ata_dma_setup(void *addr, unsigned long bytes, bool write) {
    /* Require cacheline alignment for reads to prevent interference. */
    if (!write && ((unsigned long)addr & 15))
        return false;

    /* Writes only need to be word-aligned, but by default DMA
     * is not used for writing on non-SSDs as it appears to be slower.
     */
    if (write) {
        if ((unsigned long)addr & 3)
            return false;
        if (!ata_disk_isssd())
            return false;
    }

#if ATA_MAX_UDMA > 2
    if (dma_needs_boost && !dma_boosted) {
        cpu_boost(true);
        dma_boosted = true;
    }
#endif

    if (write) {
        /* If unflushed, old data may be written to disk */
        commit_dcache();
    }
    else {
        /* Invalidate cache because new data may be present in RAM */
        commit_discard_dcache();
    }

    /* Clear pending interrupts so ata_dma_finish() can wait for an
       interrupt from this transfer
     */
    IDE0_CFG |= IDE_CFG_INTRQ;

    IDE_DMA_CONTROL |= 2;
    IDE_DMA_LENGTH = bytes - 4;

#if !defined(BOOTLOADER) || defined (HAVE_BOOTLOADER_USB_MODE)
    if ((unsigned long)addr < DRAM_START)
        /* Rockbox remaps DRAM to start at 0 */
        IDE_DMA_ADDR = (unsigned long)addr + DRAM_START;
    else
#endif
        IDE_DMA_ADDR = (unsigned long)addr;

    if (write)
        IDE_DMA_CONTROL &= ~IDE_DMA_CONTROL_READ;
    else
        IDE_DMA_CONTROL |= IDE_DMA_CONTROL_READ;

    IDE0_CFG |= 0x8000;

    return true;
}

/* This function waits for a DMA transfer to end.
   It must be called to finish what ata_dma_setup started.

   Return value is true if DMA completed before the timeout, and false
   if a timeout happened.
 */
bool ata_dma_finish(void) {
    bool res;

    /* It may be okay to put this at the end of setup */
    IDE_DMA_CONTROL |= 1;

    /* Wait for end of transfer.
       Reading standard ATA status while DMA is in progress causes
       failures and hangs.  Because of that, another wait is used.
     */
    res = ata_wait_intrq();

    IDE0_CFG &= ~0x8000;
    IDE_DMA_CONTROL &= ~0x80000001;

#if ATA_MAX_UDMA > 2
    if (dma_boosted) {
        cpu_boost(false);
        dma_boosted = false;
    }
#endif

    return res;
}

#endif /* HAVE_ATA_DMA */