diff options
Diffstat (limited to 'drivers/ide/pci/cs5530.c')
| -rw-r--r-- | drivers/ide/pci/cs5530.c | 160 |
1 files changed, 79 insertions, 81 deletions
diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c index b2d7c132ef4b..1eec1f308d16 100644 --- a/drivers/ide/pci/cs5530.c +++ b/drivers/ide/pci/cs5530.c | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * linux/drivers/ide/pci/cs5530.c Version 0.7 Sept 10, 2002 | 2 | * linux/drivers/ide/pci/cs5530.c Version 0.73 Mar 10 2007 |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org> | 4 | * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org> |
| 5 | * Ditto of GNU General Public License. | ||
| 6 | * | ||
| 7 | * Copyright (C) 2000 Mark Lord <mlord@pobox.com> | 5 | * Copyright (C) 2000 Mark Lord <mlord@pobox.com> |
| 6 | * Copyright (C) 2007 Bartlomiej Zolnierkiewicz | ||
| 7 | * | ||
| 8 | * May be copied or modified under the terms of the GNU General Public License | 8 | * May be copied or modified under the terms of the GNU General Public License |
| 9 | * | 9 | * |
| 10 | * Development of this chipset driver was funded | 10 | * Development of this chipset driver was funded |
| @@ -62,6 +62,14 @@ static unsigned int cs5530_pio_timings[2][5] = { | |||
| 62 | #define CS5530_BAD_PIO(timings) (((timings)&~0x80000000)==0x0000e132) | 62 | #define CS5530_BAD_PIO(timings) (((timings)&~0x80000000)==0x0000e132) |
| 63 | #define CS5530_BASEREG(hwif) (((hwif)->dma_base & ~0xf) + ((hwif)->channel ? 0x30 : 0x20)) | 63 | #define CS5530_BASEREG(hwif) (((hwif)->dma_base & ~0xf) + ((hwif)->channel ? 0x30 : 0x20)) |
| 64 | 64 | ||
| 65 | static void cs5530_tunepio(ide_drive_t *drive, u8 pio) | ||
| 66 | { | ||
| 67 | unsigned long basereg = CS5530_BASEREG(drive->hwif); | ||
| 68 | unsigned int format = (inl(basereg + 4) >> 31) & 1; | ||
| 69 | |||
| 70 | outl(cs5530_pio_timings[format][pio], basereg + ((drive->dn & 1)<<3)); | ||
| 71 | } | ||
| 72 | |||
| 65 | /** | 73 | /** |
| 66 | * cs5530_tuneproc - select/set PIO modes | 74 | * cs5530_tuneproc - select/set PIO modes |
| 67 | * | 75 | * |
| @@ -74,98 +82,78 @@ static unsigned int cs5530_pio_timings[2][5] = { | |||
| 74 | 82 | ||
| 75 | static void cs5530_tuneproc (ide_drive_t *drive, u8 pio) /* pio=255 means "autotune" */ | 83 | static void cs5530_tuneproc (ide_drive_t *drive, u8 pio) /* pio=255 means "autotune" */ |
| 76 | { | 84 | { |
| 77 | ide_hwif_t *hwif = HWIF(drive); | ||
| 78 | unsigned int format; | ||
| 79 | unsigned long basereg = CS5530_BASEREG(hwif); | ||
| 80 | static u8 modes[5] = { XFER_PIO_0, XFER_PIO_1, XFER_PIO_2, XFER_PIO_3, XFER_PIO_4}; | ||
| 81 | |||
| 82 | pio = ide_get_best_pio_mode(drive, pio, 4, NULL); | 85 | pio = ide_get_best_pio_mode(drive, pio, 4, NULL); |
| 83 | if (!cs5530_set_xfer_mode(drive, modes[pio])) { | 86 | |
| 84 | format = (inl(basereg + 4) >> 31) & 1; | 87 | if (cs5530_set_xfer_mode(drive, XFER_PIO_0 + pio) == 0) |
| 85 | outl(cs5530_pio_timings[format][pio], | 88 | cs5530_tunepio(drive, pio); |
| 86 | basereg+(drive->select.b.unit<<3)); | 89 | } |
| 90 | |||
| 91 | /** | ||
| 92 | * cs5530_udma_filter - UDMA filter | ||
| 93 | * @drive: drive | ||
| 94 | * | ||
| 95 | * cs5530_udma_filter() does UDMA mask filtering for the given drive | ||
| 96 | * taking into the consideration capabilities of the mate device. | ||
| 97 | * | ||
| 98 | * The CS5530 specifies that two drives sharing a cable cannot mix | ||
| 99 | * UDMA/MDMA. It has to be one or the other, for the pair, though | ||
| 100 | * different timings can still be chosen for each drive. We could | ||
| 101 | * set the appropriate timing bits on the fly, but that might be | ||
| 102 | * a bit confusing. So, for now we statically handle this requirement | ||
| 103 | * by looking at our mate drive to see what it is capable of, before | ||
| 104 | * choosing a mode for our own drive. | ||
| 105 | * | ||
| 106 | * Note: This relies on the fact we never fail from UDMA to MWDMA2 | ||
| 107 | * but instead drop to PIO. | ||
| 108 | */ | ||
| 109 | |||
| 110 | static u8 cs5530_udma_filter(ide_drive_t *drive) | ||
| 111 | { | ||
| 112 | ide_hwif_t *hwif = drive->hwif; | ||
| 113 | ide_drive_t *mate = &hwif->drives[(drive->dn & 1) ^ 1]; | ||
| 114 | struct hd_driveid *mateid = mate->id; | ||
| 115 | u8 mask = hwif->ultra_mask; | ||
| 116 | |||
| 117 | if (mate->present == 0) | ||
| 118 | goto out; | ||
| 119 | |||
| 120 | if ((mateid->capability & 1) && __ide_dma_bad_drive(mate) == 0) { | ||
| 121 | if ((mateid->field_valid & 4) && (mateid->dma_ultra & 7)) | ||
| 122 | goto out; | ||
| 123 | if ((mateid->field_valid & 2) && (mateid->dma_mword & 7)) | ||
| 124 | mask = 0; | ||
| 87 | } | 125 | } |
| 126 | out: | ||
| 127 | return mask; | ||
| 88 | } | 128 | } |
| 89 | 129 | ||
| 90 | /** | 130 | /** |
| 91 | * cs5530_config_dma - select/set DMA and UDMA modes | 131 | * cs5530_config_dma - set DMA/UDMA mode |
| 92 | * @drive: drive to tune | 132 | * @drive: drive to tune |
| 93 | * | 133 | * |
| 94 | * cs5530_config_dma() handles selection/setting of DMA/UDMA modes | 134 | * cs5530_config_dma() handles setting of DMA/UDMA mode |
| 95 | * for both the chipset and drive. The CS5530 has limitations about | 135 | * for both the chipset and drive. |
| 96 | * mixing DMA/UDMA on the same cable. | ||
| 97 | */ | 136 | */ |
| 98 | 137 | ||
| 99 | static int cs5530_config_dma (ide_drive_t *drive) | 138 | static int cs5530_config_dma(ide_drive_t *drive) |
| 100 | { | 139 | { |
| 101 | int udma_ok = 1, mode = 0; | 140 | if (ide_tune_dma(drive)) |
| 102 | ide_hwif_t *hwif = HWIF(drive); | 141 | return 0; |
| 103 | int unit = drive->select.b.unit; | ||
| 104 | ide_drive_t *mate = &hwif->drives[unit^1]; | ||
| 105 | struct hd_driveid *id = drive->id; | ||
| 106 | unsigned int reg, timings = 0; | ||
| 107 | unsigned long basereg; | ||
| 108 | 142 | ||
| 109 | /* | 143 | return 1; |
| 110 | * Default to DMA-off in case we run into trouble here. | 144 | } |
| 111 | */ | ||
| 112 | hwif->dma_off_quietly(drive); | ||
| 113 | 145 | ||
| 114 | /* | 146 | static int cs5530_tune_chipset(ide_drive_t *drive, u8 mode) |
| 115 | * The CS5530 specifies that two drives sharing a cable cannot | 147 | { |
| 116 | * mix UDMA/MDMA. It has to be one or the other, for the pair, | 148 | unsigned long basereg; |
| 117 | * though different timings can still be chosen for each drive. | 149 | unsigned int reg, timings = 0; |
| 118 | * We could set the appropriate timing bits on the fly, | ||
| 119 | * but that might be a bit confusing. So, for now we statically | ||
| 120 | * handle this requirement by looking at our mate drive to see | ||
| 121 | * what it is capable of, before choosing a mode for our own drive. | ||
| 122 | * | ||
| 123 | * Note: This relies on the fact we never fail from UDMA to MWDMA_2 | ||
| 124 | * but instead drop to PIO | ||
| 125 | */ | ||
| 126 | if (mate->present) { | ||
| 127 | struct hd_driveid *mateid = mate->id; | ||
| 128 | if (mateid && (mateid->capability & 1) && | ||
| 129 | !__ide_dma_bad_drive(mate)) { | ||
| 130 | if ((mateid->field_valid & 4) && | ||
| 131 | (mateid->dma_ultra & 7)) | ||
| 132 | udma_ok = 1; | ||
| 133 | else if ((mateid->field_valid & 2) && | ||
| 134 | (mateid->dma_mword & 7)) | ||
| 135 | udma_ok = 0; | ||
| 136 | else | ||
| 137 | udma_ok = 1; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | 150 | ||
| 141 | /* | 151 | mode = ide_rate_filter(drive, mode); |
| 142 | * Now see what the current drive is capable of, | ||
| 143 | * selecting UDMA only if the mate said it was ok. | ||
| 144 | */ | ||
| 145 | if (id && (id->capability & 1) && drive->autodma && | ||
| 146 | !__ide_dma_bad_drive(drive)) { | ||
| 147 | if (udma_ok && (id->field_valid & 4) && (id->dma_ultra & 7)) { | ||
| 148 | if (id->dma_ultra & 4) | ||
| 149 | mode = XFER_UDMA_2; | ||
| 150 | else if (id->dma_ultra & 2) | ||
| 151 | mode = XFER_UDMA_1; | ||
| 152 | else if (id->dma_ultra & 1) | ||
| 153 | mode = XFER_UDMA_0; | ||
| 154 | } | ||
| 155 | if (!mode && (id->field_valid & 2) && (id->dma_mword & 7)) { | ||
| 156 | if (id->dma_mword & 4) | ||
| 157 | mode = XFER_MW_DMA_2; | ||
| 158 | else if (id->dma_mword & 2) | ||
| 159 | mode = XFER_MW_DMA_1; | ||
| 160 | else if (id->dma_mword & 1) | ||
| 161 | mode = XFER_MW_DMA_0; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | 152 | ||
| 165 | /* | 153 | /* |
| 166 | * Tell the drive to switch to the new mode; abort on failure. | 154 | * Tell the drive to switch to the new mode; abort on failure. |
| 167 | */ | 155 | */ |
| 168 | if (!mode || cs5530_set_xfer_mode(drive, mode)) | 156 | if (cs5530_set_xfer_mode(drive, mode)) |
| 169 | return 1; /* failure */ | 157 | return 1; /* failure */ |
| 170 | 158 | ||
| 171 | /* | 159 | /* |
| @@ -178,14 +166,21 @@ static int cs5530_config_dma (ide_drive_t *drive) | |||
| 178 | case XFER_MW_DMA_0: timings = 0x00077771; break; | 166 | case XFER_MW_DMA_0: timings = 0x00077771; break; |
| 179 | case XFER_MW_DMA_1: timings = 0x00012121; break; | 167 | case XFER_MW_DMA_1: timings = 0x00012121; break; |
| 180 | case XFER_MW_DMA_2: timings = 0x00002020; break; | 168 | case XFER_MW_DMA_2: timings = 0x00002020; break; |
| 169 | case XFER_PIO_4: | ||
| 170 | case XFER_PIO_3: | ||
| 171 | case XFER_PIO_2: | ||
| 172 | case XFER_PIO_1: | ||
| 173 | case XFER_PIO_0: | ||
| 174 | cs5530_tunepio(drive, mode - XFER_PIO_0); | ||
| 175 | return 0; | ||
| 181 | default: | 176 | default: |
| 182 | BUG(); | 177 | BUG(); |
| 183 | break; | 178 | break; |
| 184 | } | 179 | } |
| 185 | basereg = CS5530_BASEREG(hwif); | 180 | basereg = CS5530_BASEREG(drive->hwif); |
| 186 | reg = inl(basereg + 4); /* get drive0 config register */ | 181 | reg = inl(basereg + 4); /* get drive0 config register */ |
| 187 | timings |= reg & 0x80000000; /* preserve PIO format bit */ | 182 | timings |= reg & 0x80000000; /* preserve PIO format bit */ |
| 188 | if (unit == 0) { /* are we configuring drive0? */ | 183 | if ((drive-> dn & 1) == 0) { /* are we configuring drive0? */ |
| 189 | outl(timings, basereg + 4); /* write drive0 config register */ | 184 | outl(timings, basereg + 4); /* write drive0 config register */ |
| 190 | } else { | 185 | } else { |
| 191 | if (timings & 0x00100000) | 186 | if (timings & 0x00100000) |
| @@ -311,6 +306,8 @@ static void __devinit init_hwif_cs5530 (ide_hwif_t *hwif) | |||
| 311 | hwif->serialized = hwif->mate->serialized = 1; | 306 | hwif->serialized = hwif->mate->serialized = 1; |
| 312 | 307 | ||
| 313 | hwif->tuneproc = &cs5530_tuneproc; | 308 | hwif->tuneproc = &cs5530_tuneproc; |
| 309 | hwif->speedproc = &cs5530_tune_chipset; | ||
| 310 | |||
| 314 | basereg = CS5530_BASEREG(hwif); | 311 | basereg = CS5530_BASEREG(hwif); |
| 315 | d0_timings = inl(basereg + 0); | 312 | d0_timings = inl(basereg + 0); |
| 316 | if (CS5530_BAD_PIO(d0_timings)) { | 313 | if (CS5530_BAD_PIO(d0_timings)) { |
| @@ -332,6 +329,7 @@ static void __devinit init_hwif_cs5530 (ide_hwif_t *hwif) | |||
| 332 | hwif->ultra_mask = 0x07; | 329 | hwif->ultra_mask = 0x07; |
| 333 | hwif->mwdma_mask = 0x07; | 330 | hwif->mwdma_mask = 0x07; |
| 334 | 331 | ||
| 332 | hwif->udma_filter = cs5530_udma_filter; | ||
| 335 | hwif->ide_dma_check = &cs5530_config_dma; | 333 | hwif->ide_dma_check = &cs5530_config_dma; |
| 336 | if (!noautodma) | 334 | if (!noautodma) |
| 337 | hwif->autodma = 1; | 335 | hwif->autodma = 1; |
