2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Support for IDE interfaces on PowerMacs.
|
2008-02-02 06:09:33 +08:00
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* These IDE interfaces are memory-mapped and have a DBDMA channel
|
|
|
|
* for doing DMA.
|
|
|
|
*
|
|
|
|
* Copyright (C) 1998-2003 Paul Mackerras & Ben. Herrenschmidt
|
2008-07-17 02:33:38 +08:00
|
|
|
* Copyright (C) 2007-2008 Bartlomiej Zolnierkiewicz
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* Some code taken from drivers/ide/ide-dma.c:
|
|
|
|
*
|
|
|
|
* Copyright (c) 1995-1998 Mark Lord
|
|
|
|
*
|
|
|
|
* TODO: - Use pre-calculated (kauai) timing tables all the time and
|
|
|
|
* get rid of the "rounded" tables used previously, so we have the
|
|
|
|
* same table format for all controllers and can then just have one
|
|
|
|
* big table
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/ide.h>
|
|
|
|
#include <linux/notifier.h>
|
|
|
|
#include <linux/reboot.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/adb.h>
|
|
|
|
#include <linux/pmu.h>
|
|
|
|
#include <linux/scatterlist.h>
|
|
|
|
|
|
|
|
#include <asm/prom.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/dbdma.h>
|
|
|
|
#include <asm/ide.h>
|
|
|
|
#include <asm/pci-bridge.h>
|
|
|
|
#include <asm/machdep.h>
|
|
|
|
#include <asm/pmac_feature.h>
|
|
|
|
#include <asm/sections.h>
|
|
|
|
#include <asm/irq.h>
|
|
|
|
|
|
|
|
#ifndef CONFIG_PPC64
|
|
|
|
#include <asm/mediabay.h>
|
|
|
|
#endif
|
|
|
|
|
2008-07-24 01:55:49 +08:00
|
|
|
#define DRV_NAME "ide-pmac"
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#undef IDE_PMAC_DEBUG
|
|
|
|
|
|
|
|
#define DMA_WAIT_TIMEOUT 50
|
|
|
|
|
|
|
|
typedef struct pmac_ide_hwif {
|
|
|
|
unsigned long regbase;
|
|
|
|
int irq;
|
|
|
|
int kind;
|
|
|
|
int aapl_bus_id;
|
|
|
|
unsigned mediabay : 1;
|
|
|
|
unsigned broken_dma : 1;
|
|
|
|
unsigned broken_dma_warn : 1;
|
|
|
|
struct device_node* node;
|
|
|
|
struct macio_dev *mdev;
|
|
|
|
u32 timings[4];
|
|
|
|
volatile u32 __iomem * *kauai_fcr;
|
|
|
|
/* Those fields are duplicating what is in hwif. We currently
|
|
|
|
* can't use the hwif ones because of some assumptions that are
|
|
|
|
* beeing done by the generic code about the kind of dma controller
|
|
|
|
* and format of the dma table. This will have to be fixed though.
|
|
|
|
*/
|
|
|
|
volatile struct dbdma_regs __iomem * dma_regs;
|
|
|
|
struct dbdma_cmd* dma_table_cpu;
|
|
|
|
} pmac_ide_hwif_t;
|
|
|
|
|
|
|
|
enum {
|
|
|
|
controller_ohare, /* OHare based */
|
|
|
|
controller_heathrow, /* Heathrow/Paddington */
|
|
|
|
controller_kl_ata3, /* KeyLargo ATA-3 */
|
|
|
|
controller_kl_ata4, /* KeyLargo ATA-4 */
|
|
|
|
controller_un_ata6, /* UniNorth2 ATA-6 */
|
|
|
|
controller_k2_ata6, /* K2 ATA-6 */
|
|
|
|
controller_sh_ata6, /* Shasta ATA-6 */
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char* model_name[] = {
|
|
|
|
"OHare ATA", /* OHare based */
|
|
|
|
"Heathrow ATA", /* Heathrow/Paddington */
|
|
|
|
"KeyLargo ATA-3", /* KeyLargo ATA-3 (MDMA only) */
|
|
|
|
"KeyLargo ATA-4", /* KeyLargo ATA-4 (UDMA/66) */
|
|
|
|
"UniNorth ATA-6", /* UniNorth2 ATA-6 (UDMA/100) */
|
|
|
|
"K2 ATA-6", /* K2 ATA-6 (UDMA/100) */
|
|
|
|
"Shasta ATA-6", /* Shasta ATA-6 (UDMA/133) */
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extra registers, both 32-bit little-endian
|
|
|
|
*/
|
|
|
|
#define IDE_TIMING_CONFIG 0x200
|
|
|
|
#define IDE_INTERRUPT 0x300
|
|
|
|
|
|
|
|
/* Kauai (U2) ATA has different register setup */
|
|
|
|
#define IDE_KAUAI_PIO_CONFIG 0x200
|
|
|
|
#define IDE_KAUAI_ULTRA_CONFIG 0x210
|
|
|
|
#define IDE_KAUAI_POLL_CONFIG 0x220
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Timing configuration register definitions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Number of IDE_SYSCLK_NS ticks, argument is in nanoseconds */
|
|
|
|
#define SYSCLK_TICKS(t) (((t) + IDE_SYSCLK_NS - 1) / IDE_SYSCLK_NS)
|
|
|
|
#define SYSCLK_TICKS_66(t) (((t) + IDE_SYSCLK_66_NS - 1) / IDE_SYSCLK_66_NS)
|
|
|
|
#define IDE_SYSCLK_NS 30 /* 33Mhz cell */
|
|
|
|
#define IDE_SYSCLK_66_NS 15 /* 66Mhz cell */
|
|
|
|
|
|
|
|
/* 133Mhz cell, found in shasta.
|
|
|
|
* See comments about 100 Mhz Uninorth 2...
|
|
|
|
* Note that PIO_MASK and MDMA_MASK seem to overlap
|
|
|
|
*/
|
|
|
|
#define TR_133_PIOREG_PIO_MASK 0xff000fff
|
|
|
|
#define TR_133_PIOREG_MDMA_MASK 0x00fff800
|
|
|
|
#define TR_133_UDMAREG_UDMA_MASK 0x0003ffff
|
|
|
|
#define TR_133_UDMAREG_UDMA_EN 0x00000001
|
|
|
|
|
|
|
|
/* 100Mhz cell, found in Uninorth 2. I don't have much infos about
|
|
|
|
* this one yet, it appears as a pci device (106b/0033) on uninorth
|
|
|
|
* internal PCI bus and it's clock is controlled like gem or fw. It
|
|
|
|
* appears to be an evolution of keylargo ATA4 with a timing register
|
|
|
|
* extended to 2 32bits registers and a similar DBDMA channel. Other
|
|
|
|
* registers seem to exist but I can't tell much about them.
|
|
|
|
*
|
|
|
|
* So far, I'm using pre-calculated tables for this extracted from
|
|
|
|
* the values used by the MacOS X driver.
|
|
|
|
*
|
|
|
|
* The "PIO" register controls PIO and MDMA timings, the "ULTRA"
|
|
|
|
* register controls the UDMA timings. At least, it seems bit 0
|
|
|
|
* of this one enables UDMA vs. MDMA, and bits 4..7 are the
|
|
|
|
* cycle time in units of 10ns. Bits 8..15 are used by I don't
|
|
|
|
* know their meaning yet
|
|
|
|
*/
|
|
|
|
#define TR_100_PIOREG_PIO_MASK 0xff000fff
|
|
|
|
#define TR_100_PIOREG_MDMA_MASK 0x00fff000
|
|
|
|
#define TR_100_UDMAREG_UDMA_MASK 0x0000ffff
|
|
|
|
#define TR_100_UDMAREG_UDMA_EN 0x00000001
|
|
|
|
|
|
|
|
|
|
|
|
/* 66Mhz cell, found in KeyLargo. Can do ultra mode 0 to 2 on
|
|
|
|
* 40 connector cable and to 4 on 80 connector one.
|
|
|
|
* Clock unit is 15ns (66Mhz)
|
|
|
|
*
|
|
|
|
* 3 Values can be programmed:
|
|
|
|
* - Write data setup, which appears to match the cycle time. They
|
|
|
|
* also call it DIOW setup.
|
|
|
|
* - Ready to pause time (from spec)
|
|
|
|
* - Address setup. That one is weird. I don't see where exactly
|
|
|
|
* it fits in UDMA cycles, I got it's name from an obscure piece
|
|
|
|
* of commented out code in Darwin. They leave it to 0, we do as
|
|
|
|
* well, despite a comment that would lead to think it has a
|
|
|
|
* min value of 45ns.
|
|
|
|
* Apple also add 60ns to the write data setup (or cycle time ?) on
|
|
|
|
* reads.
|
|
|
|
*/
|
|
|
|
#define TR_66_UDMA_MASK 0xfff00000
|
|
|
|
#define TR_66_UDMA_EN 0x00100000 /* Enable Ultra mode for DMA */
|
|
|
|
#define TR_66_UDMA_ADDRSETUP_MASK 0xe0000000 /* Address setup */
|
|
|
|
#define TR_66_UDMA_ADDRSETUP_SHIFT 29
|
|
|
|
#define TR_66_UDMA_RDY2PAUS_MASK 0x1e000000 /* Ready 2 pause time */
|
|
|
|
#define TR_66_UDMA_RDY2PAUS_SHIFT 25
|
|
|
|
#define TR_66_UDMA_WRDATASETUP_MASK 0x01e00000 /* Write data setup time */
|
|
|
|
#define TR_66_UDMA_WRDATASETUP_SHIFT 21
|
|
|
|
#define TR_66_MDMA_MASK 0x000ffc00
|
|
|
|
#define TR_66_MDMA_RECOVERY_MASK 0x000f8000
|
|
|
|
#define TR_66_MDMA_RECOVERY_SHIFT 15
|
|
|
|
#define TR_66_MDMA_ACCESS_MASK 0x00007c00
|
|
|
|
#define TR_66_MDMA_ACCESS_SHIFT 10
|
|
|
|
#define TR_66_PIO_MASK 0x000003ff
|
|
|
|
#define TR_66_PIO_RECOVERY_MASK 0x000003e0
|
|
|
|
#define TR_66_PIO_RECOVERY_SHIFT 5
|
|
|
|
#define TR_66_PIO_ACCESS_MASK 0x0000001f
|
|
|
|
#define TR_66_PIO_ACCESS_SHIFT 0
|
|
|
|
|
|
|
|
/* 33Mhz cell, found in OHare, Heathrow (& Paddington) and KeyLargo
|
|
|
|
* Can do pio & mdma modes, clock unit is 30ns (33Mhz)
|
|
|
|
*
|
|
|
|
* The access time and recovery time can be programmed. Some older
|
|
|
|
* Darwin code base limit OHare to 150ns cycle time. I decided to do
|
|
|
|
* the same here fore safety against broken old hardware ;)
|
|
|
|
* The HalfTick bit, when set, adds half a clock (15ns) to the access
|
|
|
|
* time and removes one from recovery. It's not supported on KeyLargo
|
|
|
|
* implementation afaik. The E bit appears to be set for PIO mode 0 and
|
|
|
|
* is used to reach long timings used in this mode.
|
|
|
|
*/
|
|
|
|
#define TR_33_MDMA_MASK 0x003ff800
|
|
|
|
#define TR_33_MDMA_RECOVERY_MASK 0x001f0000
|
|
|
|
#define TR_33_MDMA_RECOVERY_SHIFT 16
|
|
|
|
#define TR_33_MDMA_ACCESS_MASK 0x0000f800
|
|
|
|
#define TR_33_MDMA_ACCESS_SHIFT 11
|
|
|
|
#define TR_33_MDMA_HALFTICK 0x00200000
|
|
|
|
#define TR_33_PIO_MASK 0x000007ff
|
|
|
|
#define TR_33_PIO_E 0x00000400
|
|
|
|
#define TR_33_PIO_RECOVERY_MASK 0x000003e0
|
|
|
|
#define TR_33_PIO_RECOVERY_SHIFT 5
|
|
|
|
#define TR_33_PIO_ACCESS_MASK 0x0000001f
|
|
|
|
#define TR_33_PIO_ACCESS_SHIFT 0
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Interrupt register definitions
|
|
|
|
*/
|
|
|
|
#define IDE_INTR_DMA 0x80000000
|
|
|
|
#define IDE_INTR_DEVICE 0x40000000
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FCR Register on Kauai. Not sure what bit 0x4 is ...
|
|
|
|
*/
|
|
|
|
#define KAUAI_FCR_UATA_MAGIC 0x00000004
|
|
|
|
#define KAUAI_FCR_UATA_RESET_N 0x00000002
|
|
|
|
#define KAUAI_FCR_UATA_ENABLE 0x00000001
|
|
|
|
|
|
|
|
/* Rounded Multiword DMA timings
|
|
|
|
*
|
|
|
|
* I gave up finding a generic formula for all controller
|
|
|
|
* types and instead, built tables based on timing values
|
|
|
|
* used by Apple in Darwin's implementation.
|
|
|
|
*/
|
|
|
|
struct mdma_timings_t {
|
|
|
|
int accessTime;
|
|
|
|
int recoveryTime;
|
|
|
|
int cycleTime;
|
|
|
|
};
|
|
|
|
|
2005-09-17 23:36:54 +08:00
|
|
|
struct mdma_timings_t mdma_timings_33[] =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
{ 240, 240, 480 },
|
|
|
|
{ 180, 180, 360 },
|
|
|
|
{ 135, 135, 270 },
|
|
|
|
{ 120, 120, 240 },
|
|
|
|
{ 105, 105, 210 },
|
|
|
|
{ 90, 90, 180 },
|
|
|
|
{ 75, 75, 150 },
|
|
|
|
{ 75, 45, 120 },
|
|
|
|
{ 0, 0, 0 }
|
|
|
|
};
|
|
|
|
|
2005-09-17 23:36:54 +08:00
|
|
|
struct mdma_timings_t mdma_timings_33k[] =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
{ 240, 240, 480 },
|
|
|
|
{ 180, 180, 360 },
|
|
|
|
{ 150, 150, 300 },
|
|
|
|
{ 120, 120, 240 },
|
|
|
|
{ 90, 120, 210 },
|
|
|
|
{ 90, 90, 180 },
|
|
|
|
{ 90, 60, 150 },
|
|
|
|
{ 90, 30, 120 },
|
|
|
|
{ 0, 0, 0 }
|
|
|
|
};
|
|
|
|
|
2005-09-17 23:36:54 +08:00
|
|
|
struct mdma_timings_t mdma_timings_66[] =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
{ 240, 240, 480 },
|
|
|
|
{ 180, 180, 360 },
|
|
|
|
{ 135, 135, 270 },
|
|
|
|
{ 120, 120, 240 },
|
|
|
|
{ 105, 105, 210 },
|
|
|
|
{ 90, 90, 180 },
|
|
|
|
{ 90, 75, 165 },
|
|
|
|
{ 75, 45, 120 },
|
|
|
|
{ 0, 0, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* KeyLargo ATA-4 Ultra DMA timings (rounded) */
|
|
|
|
struct {
|
|
|
|
int addrSetup; /* ??? */
|
|
|
|
int rdy2pause;
|
|
|
|
int wrDataSetup;
|
2005-09-17 23:36:54 +08:00
|
|
|
} kl66_udma_timings[] =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
{ 0, 180, 120 }, /* Mode 0 */
|
|
|
|
{ 0, 150, 90 }, /* 1 */
|
|
|
|
{ 0, 120, 60 }, /* 2 */
|
|
|
|
{ 0, 90, 45 }, /* 3 */
|
|
|
|
{ 0, 90, 30 } /* 4 */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* UniNorth 2 ATA/100 timings */
|
|
|
|
struct kauai_timing {
|
|
|
|
int cycle_time;
|
|
|
|
u32 timing_reg;
|
|
|
|
};
|
|
|
|
|
2005-09-17 23:36:54 +08:00
|
|
|
static struct kauai_timing kauai_pio_timings[] =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
{ 930 , 0x08000fff },
|
|
|
|
{ 600 , 0x08000a92 },
|
|
|
|
{ 383 , 0x0800060f },
|
|
|
|
{ 360 , 0x08000492 },
|
|
|
|
{ 330 , 0x0800048f },
|
|
|
|
{ 300 , 0x080003cf },
|
|
|
|
{ 270 , 0x080003cc },
|
|
|
|
{ 240 , 0x0800038b },
|
|
|
|
{ 239 , 0x0800030c },
|
|
|
|
{ 180 , 0x05000249 },
|
2007-10-12 05:54:01 +08:00
|
|
|
{ 120 , 0x04000148 },
|
|
|
|
{ 0 , 0 },
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2005-09-17 23:36:54 +08:00
|
|
|
static struct kauai_timing kauai_mdma_timings[] =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
{ 1260 , 0x00fff000 },
|
|
|
|
{ 480 , 0x00618000 },
|
|
|
|
{ 360 , 0x00492000 },
|
|
|
|
{ 270 , 0x0038e000 },
|
|
|
|
{ 240 , 0x0030c000 },
|
|
|
|
{ 210 , 0x002cb000 },
|
|
|
|
{ 180 , 0x00249000 },
|
|
|
|
{ 150 , 0x00209000 },
|
|
|
|
{ 120 , 0x00148000 },
|
|
|
|
{ 0 , 0 },
|
|
|
|
};
|
|
|
|
|
2005-09-17 23:36:54 +08:00
|
|
|
static struct kauai_timing kauai_udma_timings[] =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
{ 120 , 0x000070c0 },
|
|
|
|
{ 90 , 0x00005d80 },
|
|
|
|
{ 60 , 0x00004a60 },
|
|
|
|
{ 45 , 0x00003a50 },
|
|
|
|
{ 30 , 0x00002a30 },
|
|
|
|
{ 20 , 0x00002921 },
|
|
|
|
{ 0 , 0 },
|
|
|
|
};
|
|
|
|
|
2005-09-17 23:36:54 +08:00
|
|
|
static struct kauai_timing shasta_pio_timings[] =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
{ 930 , 0x08000fff },
|
|
|
|
{ 600 , 0x0A000c97 },
|
|
|
|
{ 383 , 0x07000712 },
|
|
|
|
{ 360 , 0x040003cd },
|
|
|
|
{ 330 , 0x040003cd },
|
|
|
|
{ 300 , 0x040003cd },
|
|
|
|
{ 270 , 0x040003cd },
|
|
|
|
{ 240 , 0x040003cd },
|
|
|
|
{ 239 , 0x040003cd },
|
|
|
|
{ 180 , 0x0400028b },
|
2007-10-12 05:54:01 +08:00
|
|
|
{ 120 , 0x0400010a },
|
|
|
|
{ 0 , 0 },
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2005-09-17 23:36:54 +08:00
|
|
|
static struct kauai_timing shasta_mdma_timings[] =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
{ 1260 , 0x00fff000 },
|
|
|
|
{ 480 , 0x00820800 },
|
|
|
|
{ 360 , 0x00820800 },
|
|
|
|
{ 270 , 0x00820800 },
|
|
|
|
{ 240 , 0x00820800 },
|
|
|
|
{ 210 , 0x00820800 },
|
|
|
|
{ 180 , 0x00820800 },
|
|
|
|
{ 150 , 0x0028b000 },
|
|
|
|
{ 120 , 0x001ca000 },
|
|
|
|
{ 0 , 0 },
|
|
|
|
};
|
|
|
|
|
2005-09-17 23:36:54 +08:00
|
|
|
static struct kauai_timing shasta_udma133_timings[] =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
{ 120 , 0x00035901, },
|
|
|
|
{ 90 , 0x000348b1, },
|
|
|
|
{ 60 , 0x00033881, },
|
|
|
|
{ 45 , 0x00033861, },
|
|
|
|
{ 30 , 0x00033841, },
|
|
|
|
{ 20 , 0x00033031, },
|
|
|
|
{ 15 , 0x00033021, },
|
|
|
|
{ 0 , 0 },
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static inline u32
|
|
|
|
kauai_lookup_timing(struct kauai_timing* table, int cycle_time)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i=0; table[i].cycle_time; i++)
|
|
|
|
if (cycle_time > table[i+1].cycle_time)
|
|
|
|
return table[i].timing_reg;
|
2007-10-13 23:47:48 +08:00
|
|
|
BUG();
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allow up to 256 DBDMA commands per xfer */
|
|
|
|
#define MAX_DCMDS 256
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait 1s for disk to answer on IDE bus after a hard reset
|
|
|
|
* of the device (via GPIO/FCR).
|
|
|
|
*
|
|
|
|
* Some devices seem to "pollute" the bus even after dropping
|
|
|
|
* the BSY bit (typically some combo drives slave on the UDMA
|
|
|
|
* bus) after a hard reset. Since we hard reset all drives on
|
|
|
|
* KeyLargo ATA66, we have to keep that delay around. I may end
|
|
|
|
* up not hard resetting anymore on these and keep the delay only
|
|
|
|
* for older interfaces instead (we have to reset when coming
|
|
|
|
* from MacOS...) --BenH.
|
|
|
|
*/
|
|
|
|
#define IDE_WAKEUP_DELAY (1*HZ)
|
|
|
|
|
2008-04-27 04:25:22 +08:00
|
|
|
static int pmac_ide_init_dma(ide_hwif_t *, const struct ide_port_info *);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-18 06:46:26 +08:00
|
|
|
#define PMAC_IDE_REG(x) \
|
2008-04-27 21:38:32 +08:00
|
|
|
((void __iomem *)((drive)->hwif->io_ports.data_addr + (x)))
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Apply the timings of the proper unit (master/slave) to the shared
|
|
|
|
* timing register when selecting that unit. This version is for
|
|
|
|
* ASICs with a single timing register
|
|
|
|
*/
|
2009-04-01 02:15:32 +08:00
|
|
|
static void pmac_ide_apply_timings(ide_drive_t *drive)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-10-14 03:39:40 +08:00
|
|
|
if (drive->dn & 1)
|
2005-04-17 06:20:36 +08:00
|
|
|
writel(pmif->timings[1], PMAC_IDE_REG(IDE_TIMING_CONFIG));
|
|
|
|
else
|
|
|
|
writel(pmif->timings[0], PMAC_IDE_REG(IDE_TIMING_CONFIG));
|
|
|
|
(void)readl(PMAC_IDE_REG(IDE_TIMING_CONFIG));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Apply the timings of the proper unit (master/slave) to the shared
|
|
|
|
* timing register when selecting that unit. This version is for
|
|
|
|
* ASICs with a dual timing register (Kauai)
|
|
|
|
*/
|
2009-04-01 02:15:32 +08:00
|
|
|
static void pmac_ide_kauai_apply_timings(ide_drive_t *drive)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-10-14 03:39:40 +08:00
|
|
|
if (drive->dn & 1) {
|
2005-04-17 06:20:36 +08:00
|
|
|
writel(pmif->timings[1], PMAC_IDE_REG(IDE_KAUAI_PIO_CONFIG));
|
|
|
|
writel(pmif->timings[3], PMAC_IDE_REG(IDE_KAUAI_ULTRA_CONFIG));
|
|
|
|
} else {
|
|
|
|
writel(pmif->timings[0], PMAC_IDE_REG(IDE_KAUAI_PIO_CONFIG));
|
|
|
|
writel(pmif->timings[2], PMAC_IDE_REG(IDE_KAUAI_ULTRA_CONFIG));
|
|
|
|
}
|
|
|
|
(void)readl(PMAC_IDE_REG(IDE_KAUAI_PIO_CONFIG));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Force an update of controller timing values for a given drive
|
|
|
|
*/
|
2005-09-17 23:36:54 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
pmac_ide_do_update_timings(ide_drive_t *drive)
|
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (pmif->kind == controller_sh_ata6 ||
|
|
|
|
pmif->kind == controller_un_ata6 ||
|
|
|
|
pmif->kind == controller_k2_ata6)
|
2009-04-01 02:15:32 +08:00
|
|
|
pmac_ide_kauai_apply_timings(drive);
|
2005-04-17 06:20:36 +08:00
|
|
|
else
|
2009-04-01 02:15:32 +08:00
|
|
|
pmac_ide_apply_timings(drive);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pmac_dev_select(ide_drive_t *drive)
|
|
|
|
{
|
|
|
|
pmac_ide_apply_timings(drive);
|
|
|
|
|
|
|
|
writeb(drive->select | ATA_DEVICE_OBS,
|
|
|
|
(void __iomem *)drive->hwif->io_ports.device_addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pmac_kauai_dev_select(ide_drive_t *drive)
|
|
|
|
{
|
|
|
|
pmac_ide_kauai_apply_timings(drive);
|
|
|
|
|
|
|
|
writeb(drive->select | ATA_DEVICE_OBS,
|
|
|
|
(void __iomem *)drive->hwif->io_ports.device_addr);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-07-24 01:55:51 +08:00
|
|
|
static void pmac_exec_command(ide_hwif_t *hwif, u8 cmd)
|
|
|
|
{
|
|
|
|
writeb(cmd, (void __iomem *)hwif->io_ports.command_addr);
|
|
|
|
(void)readl((void __iomem *)(hwif->io_ports.data_addr
|
|
|
|
+ IDE_TIMING_CONFIG));
|
|
|
|
}
|
|
|
|
|
2009-04-01 02:15:30 +08:00
|
|
|
static void pmac_write_devctl(ide_hwif_t *hwif, u8 ctl)
|
2008-07-24 01:55:52 +08:00
|
|
|
{
|
|
|
|
writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr);
|
|
|
|
(void)readl((void __iomem *)(hwif->io_ports.data_addr
|
|
|
|
+ IDE_TIMING_CONFIG));
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Old tuning functions (called on hdparm -p), sets up drive PIO timings
|
|
|
|
*/
|
2005-09-17 23:36:54 +08:00
|
|
|
static void
|
2007-10-12 05:54:00 +08:00
|
|
|
pmac_ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2008-07-17 02:33:38 +08:00
|
|
|
struct ide_timing *tim = ide_timing_find_mode(XFER_PIO_0 + pio);
|
2007-10-13 23:47:50 +08:00
|
|
|
u32 *timings, t;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned accessTicks, recTicks;
|
|
|
|
unsigned accessTime, recTime;
|
2007-07-20 07:11:56 +08:00
|
|
|
unsigned int cycle_time;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* which drive is it ? */
|
2008-10-14 03:39:40 +08:00
|
|
|
timings = &pmif->timings[drive->dn & 1];
|
2007-10-13 23:47:50 +08:00
|
|
|
t = *timings;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-07-20 07:11:56 +08:00
|
|
|
cycle_time = ide_pio_cycle_time(drive, pio);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
switch (pmif->kind) {
|
|
|
|
case controller_sh_ata6: {
|
|
|
|
/* 133Mhz cell */
|
2007-07-20 07:11:56 +08:00
|
|
|
u32 tr = kauai_lookup_timing(shasta_pio_timings, cycle_time);
|
2007-10-13 23:47:50 +08:00
|
|
|
t = (t & ~TR_133_PIOREG_PIO_MASK) | tr;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case controller_un_ata6:
|
|
|
|
case controller_k2_ata6: {
|
|
|
|
/* 100Mhz cell */
|
2007-07-20 07:11:56 +08:00
|
|
|
u32 tr = kauai_lookup_timing(kauai_pio_timings, cycle_time);
|
2007-10-13 23:47:50 +08:00
|
|
|
t = (t & ~TR_100_PIOREG_PIO_MASK) | tr;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case controller_kl_ata4:
|
|
|
|
/* 66Mhz cell */
|
2008-07-17 02:33:38 +08:00
|
|
|
recTime = cycle_time - tim->active - tim->setup;
|
2005-04-17 06:20:36 +08:00
|
|
|
recTime = max(recTime, 150U);
|
2008-07-17 02:33:38 +08:00
|
|
|
accessTime = tim->active;
|
2005-04-17 06:20:36 +08:00
|
|
|
accessTime = max(accessTime, 150U);
|
|
|
|
accessTicks = SYSCLK_TICKS_66(accessTime);
|
|
|
|
accessTicks = min(accessTicks, 0x1fU);
|
|
|
|
recTicks = SYSCLK_TICKS_66(recTime);
|
|
|
|
recTicks = min(recTicks, 0x1fU);
|
2007-10-13 23:47:50 +08:00
|
|
|
t = (t & ~TR_66_PIO_MASK) |
|
|
|
|
(accessTicks << TR_66_PIO_ACCESS_SHIFT) |
|
|
|
|
(recTicks << TR_66_PIO_RECOVERY_SHIFT);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
default: {
|
|
|
|
/* 33Mhz cell */
|
|
|
|
int ebit = 0;
|
2008-07-17 02:33:38 +08:00
|
|
|
recTime = cycle_time - tim->active - tim->setup;
|
2005-04-17 06:20:36 +08:00
|
|
|
recTime = max(recTime, 150U);
|
2008-07-17 02:33:38 +08:00
|
|
|
accessTime = tim->active;
|
2005-04-17 06:20:36 +08:00
|
|
|
accessTime = max(accessTime, 150U);
|
|
|
|
accessTicks = SYSCLK_TICKS(accessTime);
|
|
|
|
accessTicks = min(accessTicks, 0x1fU);
|
|
|
|
accessTicks = max(accessTicks, 4U);
|
|
|
|
recTicks = SYSCLK_TICKS(recTime);
|
|
|
|
recTicks = min(recTicks, 0x1fU);
|
|
|
|
recTicks = max(recTicks, 5U) - 4;
|
|
|
|
if (recTicks > 9) {
|
|
|
|
recTicks--; /* guess, but it's only for PIO0, so... */
|
|
|
|
ebit = 1;
|
|
|
|
}
|
2007-10-13 23:47:50 +08:00
|
|
|
t = (t & ~TR_33_PIO_MASK) |
|
2005-04-17 06:20:36 +08:00
|
|
|
(accessTicks << TR_33_PIO_ACCESS_SHIFT) |
|
|
|
|
(recTicks << TR_33_PIO_RECOVERY_SHIFT);
|
|
|
|
if (ebit)
|
2007-10-13 23:47:50 +08:00
|
|
|
t |= TR_33_PIO_E;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef IDE_PMAC_DEBUG
|
|
|
|
printk(KERN_ERR "%s: Set PIO timing for mode %d, reg: 0x%08x\n",
|
|
|
|
drive->name, pio, *timings);
|
|
|
|
#endif
|
|
|
|
|
2007-10-13 23:47:50 +08:00
|
|
|
*timings = t;
|
2007-10-12 05:54:01 +08:00
|
|
|
pmac_ide_do_update_timings(drive);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate KeyLargo ATA/66 UDMA timings
|
|
|
|
*/
|
2005-09-17 23:36:54 +08:00
|
|
|
static int
|
2005-04-17 06:20:36 +08:00
|
|
|
set_timings_udma_ata4(u32 *timings, u8 speed)
|
|
|
|
{
|
|
|
|
unsigned rdyToPauseTicks, wrDataSetupTicks, addrTicks;
|
|
|
|
|
|
|
|
if (speed > XFER_UDMA_4)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
rdyToPauseTicks = SYSCLK_TICKS_66(kl66_udma_timings[speed & 0xf].rdy2pause);
|
|
|
|
wrDataSetupTicks = SYSCLK_TICKS_66(kl66_udma_timings[speed & 0xf].wrDataSetup);
|
|
|
|
addrTicks = SYSCLK_TICKS_66(kl66_udma_timings[speed & 0xf].addrSetup);
|
|
|
|
|
|
|
|
*timings = ((*timings) & ~(TR_66_UDMA_MASK | TR_66_MDMA_MASK)) |
|
|
|
|
(wrDataSetupTicks << TR_66_UDMA_WRDATASETUP_SHIFT) |
|
|
|
|
(rdyToPauseTicks << TR_66_UDMA_RDY2PAUS_SHIFT) |
|
|
|
|
(addrTicks <<TR_66_UDMA_ADDRSETUP_SHIFT) |
|
|
|
|
TR_66_UDMA_EN;
|
|
|
|
#ifdef IDE_PMAC_DEBUG
|
|
|
|
printk(KERN_ERR "ide_pmac: Set UDMA timing for mode %d, reg: 0x%08x\n",
|
|
|
|
speed & 0xf, *timings);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate Kauai ATA/100 UDMA timings
|
|
|
|
*/
|
2005-09-17 23:36:54 +08:00
|
|
|
static int
|
2005-04-17 06:20:36 +08:00
|
|
|
set_timings_udma_ata6(u32 *pio_timings, u32 *ultra_timings, u8 speed)
|
|
|
|
{
|
|
|
|
struct ide_timing *t = ide_timing_find_mode(speed);
|
|
|
|
u32 tr;
|
|
|
|
|
|
|
|
if (speed > XFER_UDMA_5 || t == NULL)
|
|
|
|
return 1;
|
|
|
|
tr = kauai_lookup_timing(kauai_udma_timings, (int)t->udma);
|
|
|
|
*ultra_timings = ((*ultra_timings) & ~TR_100_UDMAREG_UDMA_MASK) | tr;
|
|
|
|
*ultra_timings = (*ultra_timings) | TR_100_UDMAREG_UDMA_EN;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate Shasta ATA/133 UDMA timings
|
|
|
|
*/
|
2005-09-17 23:36:54 +08:00
|
|
|
static int
|
2005-04-17 06:20:36 +08:00
|
|
|
set_timings_udma_shasta(u32 *pio_timings, u32 *ultra_timings, u8 speed)
|
|
|
|
{
|
|
|
|
struct ide_timing *t = ide_timing_find_mode(speed);
|
|
|
|
u32 tr;
|
|
|
|
|
|
|
|
if (speed > XFER_UDMA_6 || t == NULL)
|
|
|
|
return 1;
|
|
|
|
tr = kauai_lookup_timing(shasta_udma133_timings, (int)t->udma);
|
|
|
|
*ultra_timings = ((*ultra_timings) & ~TR_133_UDMAREG_UDMA_MASK) | tr;
|
|
|
|
*ultra_timings = (*ultra_timings) | TR_133_UDMAREG_UDMA_EN;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate MDMA timings for all cells
|
|
|
|
*/
|
2007-10-13 23:47:48 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2,
|
2007-10-13 23:47:48 +08:00
|
|
|
u8 speed)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-10-11 04:39:19 +08:00
|
|
|
u16 *id = drive->id;
|
2005-04-17 06:20:36 +08:00
|
|
|
int cycleTime, accessTime = 0, recTime = 0;
|
|
|
|
unsigned accessTicks, recTicks;
|
|
|
|
struct mdma_timings_t* tm = NULL;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Get default cycle time for mode */
|
|
|
|
switch(speed & 0xf) {
|
|
|
|
case 0: cycleTime = 480; break;
|
|
|
|
case 1: cycleTime = 150; break;
|
|
|
|
case 2: cycleTime = 120; break;
|
|
|
|
default:
|
2007-10-13 23:47:48 +08:00
|
|
|
BUG();
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-10-13 23:47:48 +08:00
|
|
|
|
|
|
|
/* Check if drive provides explicit DMA cycle time */
|
2008-10-11 04:39:19 +08:00
|
|
|
if ((id[ATA_ID_FIELD_VALID] & 2) && id[ATA_ID_EIDE_DMA_TIME])
|
|
|
|
cycleTime = max_t(int, id[ATA_ID_EIDE_DMA_TIME], cycleTime);
|
2007-10-13 23:47:48 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* OHare limits according to some old Apple sources */
|
|
|
|
if ((intf_type == controller_ohare) && (cycleTime < 150))
|
|
|
|
cycleTime = 150;
|
|
|
|
/* Get the proper timing array for this controller */
|
|
|
|
switch(intf_type) {
|
|
|
|
case controller_sh_ata6:
|
|
|
|
case controller_un_ata6:
|
|
|
|
case controller_k2_ata6:
|
|
|
|
break;
|
|
|
|
case controller_kl_ata4:
|
|
|
|
tm = mdma_timings_66;
|
|
|
|
break;
|
|
|
|
case controller_kl_ata3:
|
|
|
|
tm = mdma_timings_33k;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
tm = mdma_timings_33;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (tm != NULL) {
|
|
|
|
/* Lookup matching access & recovery times */
|
|
|
|
i = -1;
|
|
|
|
for (;;) {
|
|
|
|
if (tm[i+1].cycleTime < cycleTime)
|
|
|
|
break;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
cycleTime = tm[i].cycleTime;
|
|
|
|
accessTime = tm[i].accessTime;
|
|
|
|
recTime = tm[i].recoveryTime;
|
|
|
|
|
|
|
|
#ifdef IDE_PMAC_DEBUG
|
|
|
|
printk(KERN_ERR "%s: MDMA, cycleTime: %d, accessTime: %d, recTime: %d\n",
|
|
|
|
drive->name, cycleTime, accessTime, recTime);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
switch(intf_type) {
|
|
|
|
case controller_sh_ata6: {
|
|
|
|
/* 133Mhz cell */
|
|
|
|
u32 tr = kauai_lookup_timing(shasta_mdma_timings, cycleTime);
|
|
|
|
*timings = ((*timings) & ~TR_133_PIOREG_MDMA_MASK) | tr;
|
|
|
|
*timings2 = (*timings2) & ~TR_133_UDMAREG_UDMA_EN;
|
|
|
|
}
|
|
|
|
case controller_un_ata6:
|
|
|
|
case controller_k2_ata6: {
|
|
|
|
/* 100Mhz cell */
|
|
|
|
u32 tr = kauai_lookup_timing(kauai_mdma_timings, cycleTime);
|
|
|
|
*timings = ((*timings) & ~TR_100_PIOREG_MDMA_MASK) | tr;
|
|
|
|
*timings2 = (*timings2) & ~TR_100_UDMAREG_UDMA_EN;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case controller_kl_ata4:
|
|
|
|
/* 66Mhz cell */
|
|
|
|
accessTicks = SYSCLK_TICKS_66(accessTime);
|
|
|
|
accessTicks = min(accessTicks, 0x1fU);
|
|
|
|
accessTicks = max(accessTicks, 0x1U);
|
|
|
|
recTicks = SYSCLK_TICKS_66(recTime);
|
|
|
|
recTicks = min(recTicks, 0x1fU);
|
|
|
|
recTicks = max(recTicks, 0x3U);
|
|
|
|
/* Clear out mdma bits and disable udma */
|
|
|
|
*timings = ((*timings) & ~(TR_66_MDMA_MASK | TR_66_UDMA_MASK)) |
|
|
|
|
(accessTicks << TR_66_MDMA_ACCESS_SHIFT) |
|
|
|
|
(recTicks << TR_66_MDMA_RECOVERY_SHIFT);
|
|
|
|
break;
|
|
|
|
case controller_kl_ata3:
|
|
|
|
/* 33Mhz cell on KeyLargo */
|
|
|
|
accessTicks = SYSCLK_TICKS(accessTime);
|
|
|
|
accessTicks = max(accessTicks, 1U);
|
|
|
|
accessTicks = min(accessTicks, 0x1fU);
|
|
|
|
accessTime = accessTicks * IDE_SYSCLK_NS;
|
|
|
|
recTicks = SYSCLK_TICKS(recTime);
|
|
|
|
recTicks = max(recTicks, 1U);
|
|
|
|
recTicks = min(recTicks, 0x1fU);
|
|
|
|
*timings = ((*timings) & ~TR_33_MDMA_MASK) |
|
|
|
|
(accessTicks << TR_33_MDMA_ACCESS_SHIFT) |
|
|
|
|
(recTicks << TR_33_MDMA_RECOVERY_SHIFT);
|
|
|
|
break;
|
|
|
|
default: {
|
|
|
|
/* 33Mhz cell on others */
|
|
|
|
int halfTick = 0;
|
|
|
|
int origAccessTime = accessTime;
|
|
|
|
int origRecTime = recTime;
|
|
|
|
|
|
|
|
accessTicks = SYSCLK_TICKS(accessTime);
|
|
|
|
accessTicks = max(accessTicks, 1U);
|
|
|
|
accessTicks = min(accessTicks, 0x1fU);
|
|
|
|
accessTime = accessTicks * IDE_SYSCLK_NS;
|
|
|
|
recTicks = SYSCLK_TICKS(recTime);
|
|
|
|
recTicks = max(recTicks, 2U) - 1;
|
|
|
|
recTicks = min(recTicks, 0x1fU);
|
|
|
|
recTime = (recTicks + 1) * IDE_SYSCLK_NS;
|
|
|
|
if ((accessTicks > 1) &&
|
|
|
|
((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) &&
|
|
|
|
((recTime - IDE_SYSCLK_NS/2) >= origRecTime)) {
|
|
|
|
halfTick = 1;
|
|
|
|
accessTicks--;
|
|
|
|
}
|
|
|
|
*timings = ((*timings) & ~TR_33_MDMA_MASK) |
|
|
|
|
(accessTicks << TR_33_MDMA_ACCESS_SHIFT) |
|
|
|
|
(recTicks << TR_33_MDMA_RECOVERY_SHIFT);
|
|
|
|
if (halfTick)
|
|
|
|
*timings |= TR_33_MDMA_HALFTICK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef IDE_PMAC_DEBUG
|
|
|
|
printk(KERN_ERR "%s: Set MDMA timing for mode %d, reg: 0x%08x\n",
|
|
|
|
drive->name, speed & 0xf, *timings);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
ide: move ide_config_drive_speed() calls to upper layers (take 2)
* Convert {ide_hwif_t,ide_pci_device_t}->host_flag to be u16.
* Add IDE_HFLAG_POST_SET_MODE host flag to indicate the need to program
the host for the transfer mode after programming the device. Set it
in au1xxx-ide, amd74xx, cs5530, cs5535, pdc202xx_new, sc1200, pmac
and via82cxxx host drivers.
* Add IDE_HFLAG_NO_SET_MODE host flag to indicate the need to completely
skip programming of host/device for the transfer mode ("smart" hosts).
Set it in it821x host driver and check it in ide_tune_dma().
* Add ide_set_pio_mode()/ide_set_dma_mode() helpers and convert all
direct ->set_pio_mode/->speedproc users to use these helpers.
* Move ide_config_drive_speed() calls from ->set_pio_mode/->speedproc
methods to callers.
* Rename ->speedproc method to ->set_dma_mode, make it void and update
all implementations accordingly.
* Update ide_set_xfer_rate() comments.
* Unexport ide_config_drive_speed().
v2:
* Fix issues noticed by Sergei:
- export ide_set_dma_mode() instead of moving ->set_pio_mode abuse wrt
to setting DMA modes from sc1200_set_pio_mode() to do_special()
- check IDE_HFLAG_NO_SET_MODE in ide_tune_dma()
- check for (hwif->set_pio_mode) == NULL in ide_set_pio_mode()
- check for (hwif->set_dma_mode) == NULL in ide_set_dma_mode()
- return -1 from ide_set_{pio,dma}_mode() if ->set_{pio,dma}_mode == NULL
- don't set ->set_{pio,dma}_mode on it821x in "smart" mode
- fix build problem in pmac.c
- minor fixes in au1xxx-ide.c/cs5530.c/siimage.c
- improve patch description
Changes in behavior caused by this patch:
- HDIO_SET_PIO_MODE ioctl would now return -ENOSYS for attempts to change
PIO mode if it821x controller is in "smart" mode
- removal of two debugging printk-s (from cs5530.c and sc1200.c)
- transfer modes 0x00-0x07 passed from user space may be programmed twice on
the device (not really an issue since 0x00 is not supported correctly by
any host driver ATM, 0x01 is not supported at all and 0x02-0x07 are invalid)
Acked-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
2007-10-13 23:47:51 +08:00
|
|
|
static void pmac_ide_set_dma_mode(ide_drive_t *drive, const u8 speed)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2005-04-17 06:20:36 +08:00
|
|
|
int ret = 0;
|
2007-10-13 23:47:48 +08:00
|
|
|
u32 *timings, *timings2, tl[2];
|
2008-10-14 03:39:40 +08:00
|
|
|
u8 unit = drive->dn & 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
timings = &pmif->timings[unit];
|
|
|
|
timings2 = &pmif->timings[unit+2];
|
2007-10-13 23:47:48 +08:00
|
|
|
|
|
|
|
/* Copy timings to local image */
|
|
|
|
tl[0] = *timings;
|
|
|
|
tl[1] = *timings2;
|
|
|
|
|
2008-01-26 05:17:18 +08:00
|
|
|
if (speed >= XFER_UDMA_0) {
|
|
|
|
if (pmif->kind == controller_kl_ata4)
|
|
|
|
ret = set_timings_udma_ata4(&tl[0], speed);
|
|
|
|
else if (pmif->kind == controller_un_ata6
|
|
|
|
|| pmif->kind == controller_k2_ata6)
|
|
|
|
ret = set_timings_udma_ata6(&tl[0], &tl[1], speed);
|
|
|
|
else if (pmif->kind == controller_sh_ata6)
|
|
|
|
ret = set_timings_udma_shasta(&tl[0], &tl[1], speed);
|
|
|
|
else
|
|
|
|
ret = -1;
|
|
|
|
} else
|
|
|
|
set_timings_mdma(drive, pmif->kind, &tl[0], &tl[1], speed);
|
2008-12-09 00:52:05 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ret)
|
ide: move ide_config_drive_speed() calls to upper layers (take 2)
* Convert {ide_hwif_t,ide_pci_device_t}->host_flag to be u16.
* Add IDE_HFLAG_POST_SET_MODE host flag to indicate the need to program
the host for the transfer mode after programming the device. Set it
in au1xxx-ide, amd74xx, cs5530, cs5535, pdc202xx_new, sc1200, pmac
and via82cxxx host drivers.
* Add IDE_HFLAG_NO_SET_MODE host flag to indicate the need to completely
skip programming of host/device for the transfer mode ("smart" hosts).
Set it in it821x host driver and check it in ide_tune_dma().
* Add ide_set_pio_mode()/ide_set_dma_mode() helpers and convert all
direct ->set_pio_mode/->speedproc users to use these helpers.
* Move ide_config_drive_speed() calls from ->set_pio_mode/->speedproc
methods to callers.
* Rename ->speedproc method to ->set_dma_mode, make it void and update
all implementations accordingly.
* Update ide_set_xfer_rate() comments.
* Unexport ide_config_drive_speed().
v2:
* Fix issues noticed by Sergei:
- export ide_set_dma_mode() instead of moving ->set_pio_mode abuse wrt
to setting DMA modes from sc1200_set_pio_mode() to do_special()
- check IDE_HFLAG_NO_SET_MODE in ide_tune_dma()
- check for (hwif->set_pio_mode) == NULL in ide_set_pio_mode()
- check for (hwif->set_dma_mode) == NULL in ide_set_dma_mode()
- return -1 from ide_set_{pio,dma}_mode() if ->set_{pio,dma}_mode == NULL
- don't set ->set_{pio,dma}_mode on it821x in "smart" mode
- fix build problem in pmac.c
- minor fixes in au1xxx-ide.c/cs5530.c/siimage.c
- improve patch description
Changes in behavior caused by this patch:
- HDIO_SET_PIO_MODE ioctl would now return -ENOSYS for attempts to change
PIO mode if it821x controller is in "smart" mode
- removal of two debugging printk-s (from cs5530.c and sc1200.c)
- transfer modes 0x00-0x07 passed from user space may be programmed twice on
the device (not really an issue since 0x00 is not supported correctly by
any host driver ATM, 0x01 is not supported at all and 0x02-0x07 are invalid)
Acked-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
2007-10-13 23:47:51 +08:00
|
|
|
return;
|
2007-10-13 23:47:48 +08:00
|
|
|
|
|
|
|
/* Apply timings to controller */
|
|
|
|
*timings = tl[0];
|
|
|
|
*timings2 = tl[1];
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
pmac_ide_do_update_timings(drive);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Blast some well known "safe" values to the timing registers at init or
|
|
|
|
* wakeup from sleep time, before we do real calculation
|
|
|
|
*/
|
2005-09-17 23:36:54 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
sanitize_timings(pmac_ide_hwif_t *pmif)
|
|
|
|
{
|
|
|
|
unsigned int value, value2 = 0;
|
|
|
|
|
|
|
|
switch(pmif->kind) {
|
|
|
|
case controller_sh_ata6:
|
|
|
|
value = 0x0a820c97;
|
|
|
|
value2 = 0x00033031;
|
|
|
|
break;
|
|
|
|
case controller_un_ata6:
|
|
|
|
case controller_k2_ata6:
|
|
|
|
value = 0x08618a92;
|
|
|
|
value2 = 0x00002921;
|
|
|
|
break;
|
|
|
|
case controller_kl_ata4:
|
|
|
|
value = 0x0008438c;
|
|
|
|
break;
|
|
|
|
case controller_kl_ata3:
|
|
|
|
value = 0x00084526;
|
|
|
|
break;
|
|
|
|
case controller_heathrow:
|
|
|
|
case controller_ohare:
|
|
|
|
default:
|
|
|
|
value = 0x00074526;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pmif->timings[0] = pmif->timings[1] = value;
|
|
|
|
pmif->timings[2] = pmif->timings[3] = value2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Suspend call back, should be called after the child devices
|
|
|
|
* have actually been suspended
|
|
|
|
*/
|
2008-07-24 01:55:48 +08:00
|
|
|
static int pmac_ide_do_suspend(pmac_ide_hwif_t *pmif)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
/* We clear the timings */
|
|
|
|
pmif->timings[0] = 0;
|
|
|
|
pmif->timings[1] = 0;
|
|
|
|
|
2005-05-01 23:58:41 +08:00
|
|
|
disable_irq(pmif->irq);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* The media bay will handle itself just fine */
|
|
|
|
if (pmif->mediabay)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Kauai has bus control FCRs directly here */
|
|
|
|
if (pmif->kauai_fcr) {
|
|
|
|
u32 fcr = readl(pmif->kauai_fcr);
|
|
|
|
fcr &= ~(KAUAI_FCR_UATA_RESET_N | KAUAI_FCR_UATA_ENABLE);
|
|
|
|
writel(fcr, pmif->kauai_fcr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable the bus on older machines and the cell on kauai */
|
|
|
|
ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, pmif->node, pmif->aapl_bus_id,
|
|
|
|
0);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Resume call back, should be called before the child devices
|
|
|
|
* are resumed
|
|
|
|
*/
|
2008-07-24 01:55:48 +08:00
|
|
|
static int pmac_ide_do_resume(pmac_ide_hwif_t *pmif)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
/* Hard reset & re-enable controller (do we really need to reset ? -BenH) */
|
|
|
|
if (!pmif->mediabay) {
|
|
|
|
ppc_md.feature_call(PMAC_FTR_IDE_RESET, pmif->node, pmif->aapl_bus_id, 1);
|
|
|
|
ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, pmif->node, pmif->aapl_bus_id, 1);
|
|
|
|
msleep(10);
|
|
|
|
ppc_md.feature_call(PMAC_FTR_IDE_RESET, pmif->node, pmif->aapl_bus_id, 0);
|
|
|
|
|
|
|
|
/* Kauai has it different */
|
|
|
|
if (pmif->kauai_fcr) {
|
|
|
|
u32 fcr = readl(pmif->kauai_fcr);
|
|
|
|
fcr |= KAUAI_FCR_UATA_RESET_N | KAUAI_FCR_UATA_ENABLE;
|
|
|
|
writel(fcr, pmif->kauai_fcr);
|
|
|
|
}
|
2005-05-01 23:58:41 +08:00
|
|
|
|
|
|
|
msleep(jiffies_to_msecs(IDE_WAKEUP_DELAY));
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Sanitize drive timings */
|
|
|
|
sanitize_timings(pmif);
|
|
|
|
|
2005-05-01 23:58:41 +08:00
|
|
|
enable_irq(pmif->irq);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-06-16 03:00:23 +08:00
|
|
|
static u8 pmac_ide_cable_detect(ide_hwif_t *hwif)
|
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2008-06-16 03:00:23 +08:00
|
|
|
struct device_node *np = pmif->node;
|
|
|
|
const char *cable = of_get_property(np, "cable-type", NULL);
|
2009-04-01 02:15:34 +08:00
|
|
|
struct device_node *root = of_find_node_by_path("/");
|
|
|
|
const char *model = of_get_property(root, "model", NULL);
|
2008-06-16 03:00:23 +08:00
|
|
|
|
|
|
|
/* Get cable type from device-tree. */
|
2009-04-01 02:15:34 +08:00
|
|
|
if (cable && !strncmp(cable, "80-", 3)) {
|
|
|
|
/* Some drives fail to detect 80c cable in PowerBook */
|
|
|
|
/* These machine use proprietary short IDE cable anyway */
|
|
|
|
if (!strncmp(model, "PowerBook", 9))
|
|
|
|
return ATA_CBL_PATA40_SHORT;
|
|
|
|
else
|
|
|
|
return ATA_CBL_PATA80;
|
|
|
|
}
|
2008-06-16 03:00:23 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* G5's seem to have incorrect cable type in device-tree.
|
|
|
|
* Let's assume they have a 80 conductor cable, this seem
|
|
|
|
* to be always the case unless the user mucked around.
|
|
|
|
*/
|
|
|
|
if (of_device_is_compatible(np, "K2-UATA") ||
|
|
|
|
of_device_is_compatible(np, "shasta-ata"))
|
|
|
|
return ATA_CBL_PATA80;
|
|
|
|
|
|
|
|
return ATA_CBL_PATA40;
|
|
|
|
}
|
|
|
|
|
2008-07-24 01:55:49 +08:00
|
|
|
static void pmac_ide_init_dev(ide_drive_t *drive)
|
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
|
|
|
|
|
|
|
if (pmif->mediabay) {
|
|
|
|
#ifdef CONFIG_PMAC_MEDIABAY
|
|
|
|
if (check_media_bay_by_base(pmif->regbase, MB_CD) == 0) {
|
2008-10-14 03:39:36 +08:00
|
|
|
drive->dev_flags &= ~IDE_DFLAG_NOPROBE;
|
2008-07-24 01:55:49 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
2008-10-14 03:39:36 +08:00
|
|
|
drive->dev_flags |= IDE_DFLAG_NOPROBE;
|
2008-07-24 01:55:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-24 01:55:56 +08:00
|
|
|
static const struct ide_tp_ops pmac_tp_ops = {
|
|
|
|
.exec_command = pmac_exec_command,
|
|
|
|
.read_status = ide_read_status,
|
|
|
|
.read_altstatus = ide_read_altstatus,
|
2009-04-01 02:15:30 +08:00
|
|
|
.write_devctl = pmac_write_devctl,
|
2008-07-24 01:55:56 +08:00
|
|
|
|
2009-04-01 02:15:32 +08:00
|
|
|
.dev_select = pmac_dev_select,
|
2008-07-24 01:55:56 +08:00
|
|
|
.tf_load = ide_tf_load,
|
|
|
|
.tf_read = ide_tf_read,
|
|
|
|
|
|
|
|
.input_data = ide_input_data,
|
|
|
|
.output_data = ide_output_data,
|
|
|
|
};
|
|
|
|
|
2009-04-01 02:15:32 +08:00
|
|
|
static const struct ide_tp_ops pmac_ata6_tp_ops = {
|
|
|
|
.exec_command = pmac_exec_command,
|
|
|
|
.read_status = ide_read_status,
|
|
|
|
.read_altstatus = ide_read_altstatus,
|
|
|
|
.write_devctl = pmac_write_devctl,
|
|
|
|
|
|
|
|
.dev_select = pmac_kauai_dev_select,
|
|
|
|
.tf_load = ide_tf_load,
|
|
|
|
.tf_read = ide_tf_read,
|
|
|
|
|
|
|
|
.input_data = ide_input_data,
|
|
|
|
.output_data = ide_output_data,
|
2008-06-16 03:00:23 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct ide_port_ops pmac_ide_ata4_port_ops = {
|
2008-07-24 01:55:49 +08:00
|
|
|
.init_dev = pmac_ide_init_dev,
|
2008-06-16 03:00:23 +08:00
|
|
|
.set_pio_mode = pmac_ide_set_pio_mode,
|
|
|
|
.set_dma_mode = pmac_ide_set_dma_mode,
|
|
|
|
.cable_detect = pmac_ide_cable_detect,
|
2008-04-27 04:25:14 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct ide_port_ops pmac_ide_port_ops = {
|
2008-07-24 01:55:49 +08:00
|
|
|
.init_dev = pmac_ide_init_dev,
|
2008-04-27 04:25:14 +08:00
|
|
|
.set_pio_mode = pmac_ide_set_pio_mode,
|
|
|
|
.set_dma_mode = pmac_ide_set_dma_mode,
|
|
|
|
};
|
|
|
|
|
2008-04-27 04:25:24 +08:00
|
|
|
static const struct ide_dma_ops pmac_dma_ops;
|
2008-04-27 04:25:24 +08:00
|
|
|
|
2008-02-03 02:56:31 +08:00
|
|
|
static const struct ide_port_info pmac_port_info = {
|
2008-07-24 01:55:49 +08:00
|
|
|
.name = DRV_NAME,
|
2008-04-27 04:25:22 +08:00
|
|
|
.init_dma = pmac_ide_init_dma,
|
2008-02-03 02:56:31 +08:00
|
|
|
.chipset = ide_pmac,
|
2008-07-24 01:55:56 +08:00
|
|
|
.tp_ops = &pmac_tp_ops,
|
|
|
|
.port_ops = &pmac_ide_port_ops,
|
2008-04-27 04:25:24 +08:00
|
|
|
.dma_ops = &pmac_dma_ops,
|
2008-02-03 02:56:31 +08:00
|
|
|
.host_flags = IDE_HFLAG_SET_PIO_MODE_KEEP_DMA |
|
|
|
|
IDE_HFLAG_POST_SET_MODE |
|
2008-04-29 05:44:37 +08:00
|
|
|
IDE_HFLAG_MMIO |
|
2008-02-03 02:56:31 +08:00
|
|
|
IDE_HFLAG_UNMASK_IRQS,
|
|
|
|
.pio_mask = ATA_PIO4,
|
|
|
|
.mwdma_mask = ATA_MWDMA2,
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Setup, register & probe an IDE channel driven by this driver, this is
|
2008-06-16 03:00:23 +08:00
|
|
|
* called by one of the 2 probe functions (macio or PCI).
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2008-07-24 01:55:49 +08:00
|
|
|
static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct device_node *np = pmif->node;
|
2006-07-12 13:40:29 +08:00
|
|
|
const int *bidp;
|
2008-07-24 01:55:57 +08:00
|
|
|
struct ide_host *host;
|
2008-07-24 01:55:49 +08:00
|
|
|
ide_hwif_t *hwif;
|
2009-05-18 01:12:24 +08:00
|
|
|
hw_regs_t *hws[] = { hw };
|
2008-02-03 02:56:31 +08:00
|
|
|
struct ide_port_info d = pmac_port_info;
|
ide: add ide_host_add() helper
Add ide_host_add() helper which does ide_host_alloc()+ide_host_register(),
then convert ide_setup_pci_device[s](), ide_legacy_device_add() and some
host drivers to use it.
While at it:
* Fix ide_setup_pci_device[s](), ide_arm.c, gayle.c, ide-4drives.c,
macide.c, q40ide.c, cmd640.c and cs5520.c to return correct error value.
* -ENOENT -> -ENOMEM in rapide.c, ide-h8300.c, ide-generic.c, au1xxx-ide.c
and pmac.c
* -ENODEV -> -ENOMEM in palm_bk3710.c, ide_platform.c and delkin_cb.c
* -1 -> -ENOMEM in ide-pnp.c
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
2008-07-24 01:55:57 +08:00
|
|
|
int rc;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
pmif->broken_dma = pmif->broken_dma_warn = 0;
|
2008-02-03 02:56:31 +08:00
|
|
|
if (of_device_is_compatible(np, "shasta-ata")) {
|
2005-04-17 06:20:36 +08:00
|
|
|
pmif->kind = controller_sh_ata6;
|
2009-04-01 02:15:32 +08:00
|
|
|
d.tp_ops = &pmac_ata6_tp_ops;
|
|
|
|
d.port_ops = &pmac_ide_ata4_port_ops;
|
2008-02-03 02:56:31 +08:00
|
|
|
d.udma_mask = ATA_UDMA6;
|
|
|
|
} else if (of_device_is_compatible(np, "kauai-ata")) {
|
2005-04-17 06:20:36 +08:00
|
|
|
pmif->kind = controller_un_ata6;
|
2009-04-01 02:15:32 +08:00
|
|
|
d.tp_ops = &pmac_ata6_tp_ops;
|
|
|
|
d.port_ops = &pmac_ide_ata4_port_ops;
|
2008-02-03 02:56:31 +08:00
|
|
|
d.udma_mask = ATA_UDMA5;
|
|
|
|
} else if (of_device_is_compatible(np, "K2-UATA")) {
|
2005-04-17 06:20:36 +08:00
|
|
|
pmif->kind = controller_k2_ata6;
|
2009-04-01 02:15:32 +08:00
|
|
|
d.tp_ops = &pmac_ata6_tp_ops;
|
|
|
|
d.port_ops = &pmac_ide_ata4_port_ops;
|
2008-02-03 02:56:31 +08:00
|
|
|
d.udma_mask = ATA_UDMA5;
|
|
|
|
} else if (of_device_is_compatible(np, "keylargo-ata")) {
|
|
|
|
if (strcmp(np->name, "ata-4") == 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
pmif->kind = controller_kl_ata4;
|
2008-06-16 03:00:23 +08:00
|
|
|
d.port_ops = &pmac_ide_ata4_port_ops;
|
2008-02-03 02:56:31 +08:00
|
|
|
d.udma_mask = ATA_UDMA4;
|
|
|
|
} else
|
2005-04-17 06:20:36 +08:00
|
|
|
pmif->kind = controller_kl_ata3;
|
2008-02-03 02:56:31 +08:00
|
|
|
} else if (of_device_is_compatible(np, "heathrow-ata")) {
|
2005-04-17 06:20:36 +08:00
|
|
|
pmif->kind = controller_heathrow;
|
2008-02-03 02:56:31 +08:00
|
|
|
} else {
|
2005-04-17 06:20:36 +08:00
|
|
|
pmif->kind = controller_ohare;
|
|
|
|
pmif->broken_dma = 1;
|
|
|
|
}
|
|
|
|
|
2007-05-01 11:54:02 +08:00
|
|
|
bidp = of_get_property(np, "AAPL,bus-id", NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
pmif->aapl_bus_id = bidp ? *bidp : 0;
|
|
|
|
|
|
|
|
/* On Kauai-type controllers, we make sure the FCR is correct */
|
|
|
|
if (pmif->kauai_fcr)
|
|
|
|
writel(KAUAI_FCR_UATA_MAGIC |
|
|
|
|
KAUAI_FCR_UATA_RESET_N |
|
|
|
|
KAUAI_FCR_UATA_ENABLE, pmif->kauai_fcr);
|
|
|
|
|
|
|
|
pmif->mediabay = 0;
|
|
|
|
|
|
|
|
/* Make sure we have sane timings */
|
|
|
|
sanitize_timings(pmif);
|
|
|
|
|
2009-05-18 01:12:24 +08:00
|
|
|
host = ide_host_alloc(&d, hws, 1);
|
2008-07-28 09:29:56 +08:00
|
|
|
if (host == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
hwif = host->ports[0];
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifndef CONFIG_PPC64
|
|
|
|
/* XXX FIXME: Media bay stuff need re-organizing */
|
|
|
|
if (np->parent && np->parent->name
|
|
|
|
&& strcasecmp(np->parent->name, "media-bay") == 0) {
|
2005-06-28 05:36:34 +08:00
|
|
|
#ifdef CONFIG_PMAC_MEDIABAY
|
2008-04-18 06:46:23 +08:00
|
|
|
media_bay_set_ide_infos(np->parent, pmif->regbase, pmif->irq,
|
|
|
|
hwif);
|
2005-06-28 05:36:34 +08:00
|
|
|
#endif /* CONFIG_PMAC_MEDIABAY */
|
2005-04-17 06:20:36 +08:00
|
|
|
pmif->mediabay = 1;
|
|
|
|
if (!bidp)
|
|
|
|
pmif->aapl_bus_id = 1;
|
|
|
|
} else if (pmif->kind == controller_ohare) {
|
|
|
|
/* The code below is having trouble on some ohare machines
|
|
|
|
* (timing related ?). Until I can put my hand on one of these
|
|
|
|
* units, I keep the old way
|
|
|
|
*/
|
|
|
|
ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, 0, 1);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
/* This is necessary to enable IDE when net-booting */
|
|
|
|
ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 1);
|
|
|
|
ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmif->aapl_bus_id, 1);
|
|
|
|
msleep(10);
|
|
|
|
ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 0);
|
|
|
|
msleep(jiffies_to_msecs(IDE_WAKEUP_DELAY));
|
|
|
|
}
|
|
|
|
|
2008-07-24 01:55:49 +08:00
|
|
|
printk(KERN_INFO DRV_NAME ": Found Apple %s controller (%s), "
|
|
|
|
"bus ID %d%s, irq %d\n", model_name[pmif->kind],
|
|
|
|
pmif->mdev ? "macio" : "PCI", pmif->aapl_bus_id,
|
|
|
|
pmif->mediabay ? " (mediabay)" : "", hw->irq);
|
|
|
|
|
2008-07-28 09:29:56 +08:00
|
|
|
rc = ide_host_register(host, &d, hws);
|
|
|
|
if (rc) {
|
|
|
|
ide_host_free(host);
|
ide: add ide_host_add() helper
Add ide_host_add() helper which does ide_host_alloc()+ide_host_register(),
then convert ide_setup_pci_device[s](), ide_legacy_device_add() and some
host drivers to use it.
While at it:
* Fix ide_setup_pci_device[s](), ide_arm.c, gayle.c, ide-4drives.c,
macide.c, q40ide.c, cmd640.c and cs5520.c to return correct error value.
* -ENOENT -> -ENOMEM in rapide.c, ide-h8300.c, ide-generic.c, au1xxx-ide.c
and pmac.c
* -ENODEV -> -ENOMEM in palm_bk3710.c, ide_platform.c and delkin_cb.c
* -1 -> -ENOMEM in ide-pnp.c
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
2008-07-24 01:55:57 +08:00
|
|
|
return rc;
|
2008-07-28 09:29:56 +08:00
|
|
|
}
|
2007-05-10 06:01:11 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-04-18 06:46:29 +08:00
|
|
|
static void __devinit pmac_ide_init_ports(hw_regs_t *hw, unsigned long base)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 8; ++i)
|
2008-04-27 21:38:32 +08:00
|
|
|
hw->io_ports_array[i] = base + i * 0x10;
|
|
|
|
|
|
|
|
hw->io_ports.ctl_addr = base + 0x160;
|
2008-04-18 06:46:29 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Attach to a macio probed interface
|
|
|
|
*/
|
|
|
|
static int __devinit
|
2005-07-07 03:44:41 +08:00
|
|
|
pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
void __iomem *base;
|
|
|
|
unsigned long regbase;
|
|
|
|
pmac_ide_hwif_t *pmif;
|
2008-04-26 23:36:33 +08:00
|
|
|
int irq, rc;
|
2008-01-27 03:13:05 +08:00
|
|
|
hw_regs_t hw;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-26 23:36:32 +08:00
|
|
|
pmif = kzalloc(sizeof(*pmif), GFP_KERNEL);
|
|
|
|
if (pmif == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2005-12-13 15:01:21 +08:00
|
|
|
if (macio_resource_count(mdev) == 0) {
|
2008-04-26 23:36:33 +08:00
|
|
|
printk(KERN_WARNING "ide-pmac: no address for %s\n",
|
|
|
|
mdev->ofdev.node->full_name);
|
2008-04-26 23:36:32 +08:00
|
|
|
rc = -ENXIO;
|
|
|
|
goto out_free_pmif;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Request memory resource for IO ports */
|
|
|
|
if (macio_request_resource(mdev, 0, "ide-pmac (ports)")) {
|
2008-04-26 23:36:33 +08:00
|
|
|
printk(KERN_ERR "ide-pmac: can't request MMIO resource for "
|
|
|
|
"%s!\n", mdev->ofdev.node->full_name);
|
2008-04-26 23:36:32 +08:00
|
|
|
rc = -EBUSY;
|
|
|
|
goto out_free_pmif;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX This is bogus. Should be fixed in the registry by checking
|
|
|
|
* the kind of host interrupt controller, a bit like gatwick
|
|
|
|
* fixes in irq.c. That works well enough for the single case
|
|
|
|
* where that happens though...
|
|
|
|
*/
|
|
|
|
if (macio_irq_count(mdev) == 0) {
|
2008-04-26 23:36:33 +08:00
|
|
|
printk(KERN_WARNING "ide-pmac: no intrs for device %s, using "
|
|
|
|
"13\n", mdev->ofdev.node->full_name);
|
2006-09-22 10:56:30 +08:00
|
|
|
irq = irq_create_mapping(NULL, 13);
|
2005-04-17 06:20:36 +08:00
|
|
|
} else
|
|
|
|
irq = macio_irq(mdev, 0);
|
|
|
|
|
|
|
|
base = ioremap(macio_resource_start(mdev, 0), 0x400);
|
|
|
|
regbase = (unsigned long) base;
|
|
|
|
|
|
|
|
pmif->mdev = mdev;
|
|
|
|
pmif->node = mdev->ofdev.node;
|
|
|
|
pmif->regbase = regbase;
|
|
|
|
pmif->irq = irq;
|
|
|
|
pmif->kauai_fcr = NULL;
|
2008-12-09 00:52:05 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (macio_resource_count(mdev) >= 2) {
|
|
|
|
if (macio_request_resource(mdev, 1, "ide-pmac (dma)"))
|
2008-04-26 23:36:33 +08:00
|
|
|
printk(KERN_WARNING "ide-pmac: can't request DMA "
|
|
|
|
"resource for %s!\n",
|
|
|
|
mdev->ofdev.node->full_name);
|
2005-04-17 06:20:36 +08:00
|
|
|
else
|
|
|
|
pmif->dma_regs = ioremap(macio_resource_start(mdev, 1), 0x1000);
|
|
|
|
} else
|
|
|
|
pmif->dma_regs = NULL;
|
2008-12-09 00:52:05 +08:00
|
|
|
|
2008-07-24 01:55:48 +08:00
|
|
|
dev_set_drvdata(&mdev->ofdev.dev, pmif);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-01-27 03:13:05 +08:00
|
|
|
memset(&hw, 0, sizeof(hw));
|
2008-04-18 06:46:29 +08:00
|
|
|
pmac_ide_init_ports(&hw, pmif->regbase);
|
2008-01-27 03:13:05 +08:00
|
|
|
hw.irq = irq;
|
2008-07-17 02:33:40 +08:00
|
|
|
hw.dev = &mdev->bus->pdev->dev;
|
|
|
|
hw.parent = &mdev->ofdev.dev;
|
2008-01-27 03:13:05 +08:00
|
|
|
|
2008-07-24 01:55:49 +08:00
|
|
|
rc = pmac_ide_setup_device(pmif, &hw);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (rc != 0) {
|
|
|
|
/* The inteface is released to the common IDE layer */
|
|
|
|
dev_set_drvdata(&mdev->ofdev.dev, NULL);
|
|
|
|
iounmap(base);
|
2008-02-02 06:09:32 +08:00
|
|
|
if (pmif->dma_regs) {
|
2005-04-17 06:20:36 +08:00
|
|
|
iounmap(pmif->dma_regs);
|
2008-02-02 06:09:32 +08:00
|
|
|
macio_release_resource(mdev, 1);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
macio_release_resource(mdev, 0);
|
2008-04-26 23:36:32 +08:00
|
|
|
kfree(pmif);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
2008-04-26 23:36:32 +08:00
|
|
|
|
|
|
|
out_free_pmif:
|
|
|
|
kfree(pmif);
|
|
|
|
return rc;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2006-08-15 14:11:03 +08:00
|
|
|
pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
|
|
|
|
int rc = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-08-15 14:11:03 +08:00
|
|
|
if (mesg.event != mdev->ofdev.dev.power.power_state.event
|
2008-02-24 02:13:25 +08:00
|
|
|
&& (mesg.event & PM_EVENT_SLEEP)) {
|
2008-07-24 01:55:48 +08:00
|
|
|
rc = pmac_ide_do_suspend(pmif);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (rc == 0)
|
2006-08-15 14:11:03 +08:00
|
|
|
mdev->ofdev.dev.power.power_state = mesg;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pmac_ide_macio_resume(struct macio_dev *mdev)
|
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
|
|
|
|
int rc = 0;
|
|
|
|
|
2005-09-04 06:56:57 +08:00
|
|
|
if (mdev->ofdev.dev.power.power_state.event != PM_EVENT_ON) {
|
2008-07-24 01:55:48 +08:00
|
|
|
rc = pmac_ide_do_resume(pmif);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (rc == 0)
|
2005-09-04 06:56:56 +08:00
|
|
|
mdev->ofdev.dev.power.power_state = PMSG_ON;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attach to a PCI probed interface
|
|
|
|
*/
|
|
|
|
static int __devinit
|
|
|
|
pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
|
{
|
|
|
|
struct device_node *np;
|
|
|
|
pmac_ide_hwif_t *pmif;
|
|
|
|
void __iomem *base;
|
|
|
|
unsigned long rbase, rlen;
|
2008-04-26 23:36:33 +08:00
|
|
|
int rc;
|
2008-01-27 03:13:05 +08:00
|
|
|
hw_regs_t hw;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
np = pci_device_to_OF_node(pdev);
|
|
|
|
if (np == NULL) {
|
|
|
|
printk(KERN_ERR "ide-pmac: cannot find MacIO node for Kauai ATA interface\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2008-04-26 23:36:32 +08:00
|
|
|
|
|
|
|
pmif = kzalloc(sizeof(*pmif), GFP_KERNEL);
|
|
|
|
if (pmif == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (pci_enable_device(pdev)) {
|
2008-04-26 23:36:33 +08:00
|
|
|
printk(KERN_WARNING "ide-pmac: Can't enable PCI device for "
|
|
|
|
"%s\n", np->full_name);
|
2008-04-26 23:36:32 +08:00
|
|
|
rc = -ENXIO;
|
|
|
|
goto out_free_pmif;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
pci_set_master(pdev);
|
|
|
|
|
|
|
|
if (pci_request_regions(pdev, "Kauai ATA")) {
|
2008-04-26 23:36:33 +08:00
|
|
|
printk(KERN_ERR "ide-pmac: Cannot obtain PCI resources for "
|
|
|
|
"%s\n", np->full_name);
|
2008-04-26 23:36:32 +08:00
|
|
|
rc = -ENXIO;
|
|
|
|
goto out_free_pmif;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pmif->mdev = NULL;
|
|
|
|
pmif->node = np;
|
|
|
|
|
|
|
|
rbase = pci_resource_start(pdev, 0);
|
|
|
|
rlen = pci_resource_len(pdev, 0);
|
|
|
|
|
|
|
|
base = ioremap(rbase, rlen);
|
|
|
|
pmif->regbase = (unsigned long) base + 0x2000;
|
|
|
|
pmif->dma_regs = base + 0x1000;
|
|
|
|
pmif->kauai_fcr = base;
|
|
|
|
pmif->irq = pdev->irq;
|
|
|
|
|
2008-07-24 01:55:48 +08:00
|
|
|
pci_set_drvdata(pdev, pmif);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-01-27 03:13:05 +08:00
|
|
|
memset(&hw, 0, sizeof(hw));
|
2008-04-18 06:46:29 +08:00
|
|
|
pmac_ide_init_ports(&hw, pmif->regbase);
|
2008-01-27 03:13:05 +08:00
|
|
|
hw.irq = pdev->irq;
|
|
|
|
hw.dev = &pdev->dev;
|
|
|
|
|
2008-07-24 01:55:49 +08:00
|
|
|
rc = pmac_ide_setup_device(pmif, &hw);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (rc != 0) {
|
|
|
|
/* The inteface is released to the common IDE layer */
|
|
|
|
pci_set_drvdata(pdev, NULL);
|
|
|
|
iounmap(base);
|
|
|
|
pci_release_regions(pdev);
|
2008-04-26 23:36:32 +08:00
|
|
|
kfree(pmif);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
2008-04-26 23:36:32 +08:00
|
|
|
|
|
|
|
out_free_pmif:
|
|
|
|
kfree(pmif);
|
|
|
|
return rc;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2006-08-15 14:11:03 +08:00
|
|
|
pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)pci_get_drvdata(pdev);
|
|
|
|
int rc = 0;
|
|
|
|
|
2006-08-15 14:11:03 +08:00
|
|
|
if (mesg.event != pdev->dev.power.power_state.event
|
2008-02-24 02:13:25 +08:00
|
|
|
&& (mesg.event & PM_EVENT_SLEEP)) {
|
2008-07-24 01:55:48 +08:00
|
|
|
rc = pmac_ide_do_suspend(pmif);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (rc == 0)
|
2006-08-15 14:11:03 +08:00
|
|
|
pdev->dev.power.power_state = mesg;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pmac_ide_pci_resume(struct pci_dev *pdev)
|
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)pci_get_drvdata(pdev);
|
|
|
|
int rc = 0;
|
|
|
|
|
2005-09-04 06:56:57 +08:00
|
|
|
if (pdev->dev.power.power_state.event != PM_EVENT_ON) {
|
2008-07-24 01:55:48 +08:00
|
|
|
rc = pmac_ide_do_resume(pmif);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (rc == 0)
|
2005-09-04 06:56:56 +08:00
|
|
|
pdev->dev.power.power_state = PMSG_ON;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2005-07-07 03:44:41 +08:00
|
|
|
static struct of_device_id pmac_ide_macio_match[] =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
{
|
|
|
|
.name = "IDE",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "ATA",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.type = "ide",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.type = "ata",
|
|
|
|
},
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct macio_driver pmac_ide_macio_driver =
|
|
|
|
{
|
|
|
|
.name = "ide-pmac",
|
|
|
|
.match_table = pmac_ide_macio_match,
|
|
|
|
.probe = pmac_ide_macio_attach,
|
|
|
|
.suspend = pmac_ide_macio_suspend,
|
|
|
|
.resume = pmac_ide_macio_resume,
|
|
|
|
};
|
|
|
|
|
2007-10-17 04:29:56 +08:00
|
|
|
static const struct pci_device_id pmac_ide_pci_match[] = {
|
|
|
|
{ PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_UNI_N_ATA), 0 },
|
|
|
|
{ PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_IPID_ATA100), 0 },
|
|
|
|
{ PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_K2_ATA100), 0 },
|
|
|
|
{ PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_SH_ATA), 0 },
|
|
|
|
{ PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_IPID2_ATA), 0 },
|
2007-10-06 16:52:27 +08:00
|
|
|
{},
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct pci_driver pmac_ide_pci_driver = {
|
|
|
|
.name = "ide-pmac",
|
|
|
|
.id_table = pmac_ide_pci_match,
|
|
|
|
.probe = pmac_ide_pci_attach,
|
|
|
|
.suspend = pmac_ide_pci_suspend,
|
|
|
|
.resume = pmac_ide_pci_resume,
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, pmac_ide_pci_match);
|
|
|
|
|
2007-03-04 00:48:54 +08:00
|
|
|
int __init pmac_ide_probe(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-03-04 00:48:54 +08:00
|
|
|
int error;
|
|
|
|
|
2006-03-28 20:15:54 +08:00
|
|
|
if (!machine_is(powermac))
|
2007-03-04 00:48:54 +08:00
|
|
|
return -ENODEV;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST
|
2007-03-04 00:48:54 +08:00
|
|
|
error = pci_register_driver(&pmac_ide_pci_driver);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
error = macio_register_driver(&pmac_ide_macio_driver);
|
|
|
|
if (error) {
|
|
|
|
pci_unregister_driver(&pmac_ide_pci_driver);
|
|
|
|
goto out;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
#else
|
2007-03-04 00:48:54 +08:00
|
|
|
error = macio_register_driver(&pmac_ide_macio_driver);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
error = pci_register_driver(&pmac_ide_pci_driver);
|
|
|
|
if (error) {
|
|
|
|
macio_unregister_driver(&pmac_ide_macio_driver);
|
|
|
|
goto out;
|
|
|
|
}
|
2005-12-14 10:10:10 +08:00
|
|
|
#endif
|
2007-03-04 00:48:54 +08:00
|
|
|
out:
|
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pmac_ide_build_dmatable builds the DBDMA command list
|
|
|
|
* for a transfer and sets the DBDMA channel to point to it.
|
|
|
|
*/
|
2009-03-27 19:46:46 +08:00
|
|
|
static int pmac_ide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dbdma_cmd *table;
|
|
|
|
volatile struct dbdma_regs __iomem *dma = pmif->dma_regs;
|
|
|
|
struct scatterlist *sg;
|
2009-03-27 19:46:46 +08:00
|
|
|
int wr = !!(cmd->tf_flags & IDE_TFLAG_WRITE);
|
|
|
|
int i = cmd->sg_nents, count = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* DMA table is already aligned */
|
|
|
|
table = (struct dbdma_cmd *) pmif->dma_table_cpu;
|
|
|
|
|
|
|
|
/* Make sure DMA controller is stopped (necessary ?) */
|
|
|
|
writel((RUN|PAUSE|FLUSH|WAKE|DEAD) << 16, &dma->control);
|
|
|
|
while (readl(&dma->status) & RUN)
|
|
|
|
udelay(1);
|
|
|
|
|
|
|
|
/* Build DBDMA commands list */
|
|
|
|
sg = hwif->sg_table;
|
|
|
|
while (i && sg_dma_len(sg)) {
|
|
|
|
u32 cur_addr;
|
|
|
|
u32 cur_len;
|
|
|
|
|
|
|
|
cur_addr = sg_dma_address(sg);
|
|
|
|
cur_len = sg_dma_len(sg);
|
|
|
|
|
|
|
|
if (pmif->broken_dma && cur_addr & (L1_CACHE_BYTES - 1)) {
|
|
|
|
if (pmif->broken_dma_warn == 0) {
|
2007-11-28 04:35:55 +08:00
|
|
|
printk(KERN_WARNING "%s: DMA on non aligned address, "
|
2005-04-17 06:20:36 +08:00
|
|
|
"switching to PIO on Ohare chipset\n", drive->name);
|
|
|
|
pmif->broken_dma_warn = 1;
|
|
|
|
}
|
2009-04-01 02:15:21 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
while (cur_len) {
|
|
|
|
unsigned int tc = (cur_len < 0xfe00)? cur_len: 0xfe00;
|
|
|
|
|
|
|
|
if (count++ >= MAX_DCMDS) {
|
|
|
|
printk(KERN_WARNING "%s: DMA table too small\n",
|
|
|
|
drive->name);
|
2009-04-01 02:15:21 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
st_le16(&table->command, wr? OUTPUT_MORE: INPUT_MORE);
|
|
|
|
st_le16(&table->req_count, tc);
|
|
|
|
st_le32(&table->phy_addr, cur_addr);
|
|
|
|
table->cmd_dep = 0;
|
|
|
|
table->xfer_status = 0;
|
|
|
|
table->res_count = 0;
|
|
|
|
cur_addr += tc;
|
|
|
|
cur_len -= tc;
|
|
|
|
++table;
|
|
|
|
}
|
2007-07-25 14:13:56 +08:00
|
|
|
sg = sg_next(sg);
|
2005-04-17 06:20:36 +08:00
|
|
|
i--;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* convert the last command to an input/output last command */
|
|
|
|
if (count) {
|
|
|
|
st_le16(&table[-1].command, wr? OUTPUT_LAST: INPUT_LAST);
|
|
|
|
/* add the stop command to the end of the list */
|
|
|
|
memset(table, 0, sizeof(struct dbdma_cmd));
|
|
|
|
st_le16(&table->command, DBDMA_STOP);
|
|
|
|
mb();
|
|
|
|
writel(hwif->dmatable_dma, &dma->cmdptr);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
printk(KERN_DEBUG "%s: empty DMA table?\n", drive->name);
|
2008-02-02 06:09:31 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0; /* revert to PIO for this request */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prepare a DMA transfer. We build the DMA table, adjust the timings for
|
|
|
|
* a read on KeyLargo ATA/66 and mark us as waiting for DMA completion
|
|
|
|
*/
|
2009-03-27 19:46:46 +08:00
|
|
|
static int pmac_ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-01-07 00:20:52 +08:00
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
2008-07-24 01:55:48 +08:00
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2008-10-14 03:39:45 +08:00
|
|
|
u8 unit = drive->dn & 1, ata4 = (pmif->kind == controller_kl_ata4);
|
2009-03-27 19:46:46 +08:00
|
|
|
u8 write = !!(cmd->tf_flags & IDE_TFLAG_WRITE);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-04-01 02:15:21 +08:00
|
|
|
if (pmac_ide_build_dmatable(drive, cmd) == 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Apple adds 60ns to wrDataSetup on reads */
|
|
|
|
if (ata4 && (pmif->timings[unit] & TR_66_UDMA_EN)) {
|
2009-03-27 19:46:46 +08:00
|
|
|
writel(pmif->timings[unit] + (write ? 0 : 0x00800000UL),
|
2005-04-17 06:20:36 +08:00
|
|
|
PMAC_IDE_REG(IDE_TIMING_CONFIG));
|
|
|
|
(void)readl(PMAC_IDE_REG(IDE_TIMING_CONFIG));
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Kick the DMA controller into life after the DMA command has been issued
|
|
|
|
* to the drive.
|
|
|
|
*/
|
2005-09-17 23:36:54 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
pmac_ide_dma_start(ide_drive_t *drive)
|
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2005-04-17 06:20:36 +08:00
|
|
|
volatile struct dbdma_regs __iomem *dma;
|
|
|
|
|
|
|
|
dma = pmif->dma_regs;
|
|
|
|
|
|
|
|
writel((RUN << 16) | RUN, &dma->control);
|
|
|
|
/* Make sure it gets to the controller right now */
|
|
|
|
(void)readl(&dma->control);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* After a DMA transfer, make sure the controller is stopped
|
|
|
|
*/
|
2005-09-17 23:36:54 +08:00
|
|
|
static int
|
2005-04-17 06:20:36 +08:00
|
|
|
pmac_ide_dma_end (ide_drive_t *drive)
|
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2008-10-14 03:39:45 +08:00
|
|
|
volatile struct dbdma_regs __iomem *dma = pmif->dma_regs;
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 dstat;
|
|
|
|
|
|
|
|
dstat = readl(&dma->status);
|
|
|
|
writel(((RUN|WAKE|DEAD) << 16), &dma->control);
|
2008-10-14 03:39:45 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* verify good dma status. we don't check for ACTIVE beeing 0. We should...
|
|
|
|
* in theory, but with ATAPI decices doing buffer underruns, that would
|
|
|
|
* cause us to disable DMA, which isn't what we want
|
|
|
|
*/
|
|
|
|
return (dstat & (RUN|DEAD)) != RUN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check out that the interrupt we got was for us. We can't always know this
|
|
|
|
* for sure with those Apple interfaces (well, we could on the recent ones but
|
|
|
|
* that's not implemented yet), on the other hand, we don't have shared interrupts
|
|
|
|
* so it's not really a problem
|
|
|
|
*/
|
2005-09-17 23:36:54 +08:00
|
|
|
static int
|
2005-04-17 06:20:36 +08:00
|
|
|
pmac_ide_dma_test_irq (ide_drive_t *drive)
|
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2008-10-14 03:39:45 +08:00
|
|
|
volatile struct dbdma_regs __iomem *dma = pmif->dma_regs;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long status, timeout;
|
|
|
|
|
|
|
|
/* We have to things to deal with here:
|
|
|
|
*
|
|
|
|
* - The dbdma won't stop if the command was started
|
|
|
|
* but completed with an error without transferring all
|
|
|
|
* datas. This happens when bad blocks are met during
|
|
|
|
* a multi-block transfer.
|
|
|
|
*
|
|
|
|
* - The dbdma fifo hasn't yet finished flushing to
|
|
|
|
* to system memory when the disk interrupt occurs.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* If ACTIVE is cleared, the STOP command have passed and
|
|
|
|
* transfer is complete.
|
|
|
|
*/
|
|
|
|
status = readl(&dma->status);
|
|
|
|
if (!(status & ACTIVE))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* If dbdma didn't execute the STOP command yet, the
|
|
|
|
* active bit is still set. We consider that we aren't
|
|
|
|
* sharing interrupts (which is hopefully the case with
|
|
|
|
* those controllers) and so we just try to flush the
|
|
|
|
* channel for pending data in the fifo
|
|
|
|
*/
|
|
|
|
udelay(1);
|
|
|
|
writel((FLUSH << 16) | FLUSH, &dma->control);
|
|
|
|
timeout = 0;
|
|
|
|
for (;;) {
|
|
|
|
udelay(1);
|
|
|
|
status = readl(&dma->status);
|
|
|
|
if ((status & FLUSH) == 0)
|
|
|
|
break;
|
|
|
|
if (++timeout > 100) {
|
|
|
|
printk(KERN_WARNING "ide%d, ide_dma_test_irq \
|
2009-01-07 00:20:52 +08:00
|
|
|
timeout flushing channel\n", hwif->index);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-01-27 03:13:03 +08:00
|
|
|
static void pmac_ide_dma_host_set(ide_drive_t *drive, int on)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-07-10 05:17:54 +08:00
|
|
|
static void
|
|
|
|
pmac_ide_dma_lost_irq (ide_drive_t *drive)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2008-10-14 03:39:45 +08:00
|
|
|
volatile struct dbdma_regs __iomem *dma = pmif->dma_regs;
|
|
|
|
unsigned long status = readl(&dma->status);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
printk(KERN_ERR "ide-pmac lost interrupt, dma status: %lx\n", status);
|
|
|
|
}
|
|
|
|
|
2008-04-27 04:25:24 +08:00
|
|
|
static const struct ide_dma_ops pmac_dma_ops = {
|
2008-04-27 04:25:24 +08:00
|
|
|
.dma_host_set = pmac_ide_dma_host_set,
|
|
|
|
.dma_setup = pmac_ide_dma_setup,
|
|
|
|
.dma_start = pmac_ide_dma_start,
|
|
|
|
.dma_end = pmac_ide_dma_end,
|
|
|
|
.dma_test_irq = pmac_ide_dma_test_irq,
|
|
|
|
.dma_lost_irq = pmac_ide_dma_lost_irq,
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Allocate the data structures needed for using DMA with an interface
|
|
|
|
* and fill the proper list of functions pointers
|
|
|
|
*/
|
2008-04-27 04:25:22 +08:00
|
|
|
static int __devinit pmac_ide_init_dma(ide_hwif_t *hwif,
|
|
|
|
const struct ide_port_info *d)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-24 01:55:48 +08:00
|
|
|
pmac_ide_hwif_t *pmif =
|
|
|
|
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
|
2008-02-02 06:09:31 +08:00
|
|
|
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* We won't need pci_dev if we switch to generic consistent
|
|
|
|
* DMA routines ...
|
|
|
|
*/
|
2008-04-27 04:25:22 +08:00
|
|
|
if (dev == NULL || pmif->dma_regs == 0)
|
2008-02-03 02:56:31 +08:00
|
|
|
return -ENODEV;
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Allocate space for the DBDMA commands.
|
|
|
|
* The +2 is +1 for the stop command and +1 to allow for
|
|
|
|
* aligning the start address to a multiple of 16 bytes.
|
|
|
|
*/
|
2009-04-18 23:42:19 +08:00
|
|
|
pmif->dma_table_cpu = pci_alloc_consistent(
|
2008-02-02 06:09:31 +08:00
|
|
|
dev,
|
2005-04-17 06:20:36 +08:00
|
|
|
(MAX_DCMDS + 2) * sizeof(struct dbdma_cmd),
|
|
|
|
&hwif->dmatable_dma);
|
|
|
|
if (pmif->dma_table_cpu == NULL) {
|
|
|
|
printk(KERN_ERR "%s: unable to allocate DMA command list\n",
|
|
|
|
hwif->name);
|
2008-02-03 02:56:31 +08:00
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-01-27 03:13:08 +08:00
|
|
|
hwif->sg_max_nents = MAX_DCMDS;
|
|
|
|
|
2008-02-03 02:56:31 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-01-27 03:13:07 +08:00
|
|
|
|
|
|
|
module_init(pmac_ide_probe);
|
2008-04-03 03:22:03 +08:00
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|