diff options
Diffstat (limited to 'drivers/ata/sata_promise.c')
-rw-r--r-- | drivers/ata/sata_promise.c | 155 |
1 files changed, 137 insertions, 18 deletions
diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index b1fd7d62071a..07d8d00b4d34 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -56,6 +56,7 @@ enum { /* host register offsets (from host->iomap[PDC_MMIO_BAR]) */ PDC_INT_SEQMASK = 0x40, /* Mask of asserted SEQ INTs */ PDC_FLASH_CTL = 0x44, /* Flash control register */ + PDC_PCI_CTL = 0x48, /* PCI control/status reg */ PDC_SATA_PLUG_CSR = 0x6C, /* SATA Plug control/status reg */ PDC2_SATA_PLUG_CSR = 0x60, /* SATAII Plug control/status reg */ PDC_TBG_MODE = 0x41C, /* TBG mode (not SATAII) */ @@ -75,7 +76,17 @@ enum { PDC_CTLSTAT = 0x60, /* IDE control and status (per port) */ /* per-port SATA register offsets (from ap->ioaddr.scr_addr) */ + PDC_SATA_ERROR = 0x04, PDC_PHYMODE4 = 0x14, + PDC_LINK_LAYER_ERRORS = 0x6C, + PDC_FPDMA_CTLSTAT = 0xD8, + PDC_INTERNAL_DEBUG_1 = 0xF8, /* also used for PATA */ + PDC_INTERNAL_DEBUG_2 = 0xFC, /* also used for PATA */ + + /* PDC_FPDMA_CTLSTAT bit definitions */ + PDC_FPDMA_CTLSTAT_RESET = 1 << 3, + PDC_FPDMA_CTLSTAT_DMASETUP_INT_FLAG = 1 << 10, + PDC_FPDMA_CTLSTAT_SETDB_INT_FLAG = 1 << 11, /* PDC_GLOBAL_CTL bit definitions */ PDC_PH_ERR = (1 << 8), /* PCI error while loading packet */ @@ -195,9 +206,12 @@ static struct ata_port_operations pdc_sata_ops = { .hardreset = pdc_sata_hardreset, }; -/* First-generation chips need a more restrictive ->check_atapi_dma op */ +/* First-generation chips need a more restrictive ->check_atapi_dma op, + and ->freeze/thaw that ignore the hotplug controls. */ static struct ata_port_operations pdc_old_sata_ops = { .inherits = &pdc_sata_ops, + .freeze = pdc_freeze, + .thaw = pdc_thaw, .check_atapi_dma = pdc_old_sata_check_atapi_dma, }; @@ -356,12 +370,76 @@ static int pdc_sata_port_start(struct ata_port *ap) return 0; } +static void pdc_fpdma_clear_interrupt_flag(struct ata_port *ap) +{ + void __iomem *sata_mmio = ap->ioaddr.scr_addr; + u32 tmp; + + tmp = readl(sata_mmio + PDC_FPDMA_CTLSTAT); + tmp |= PDC_FPDMA_CTLSTAT_DMASETUP_INT_FLAG; + tmp |= PDC_FPDMA_CTLSTAT_SETDB_INT_FLAG; + + /* It's not allowed to write to the entire FPDMA_CTLSTAT register + when NCQ is running. So do a byte-sized write to bits 10 and 11. */ + writeb(tmp >> 8, sata_mmio + PDC_FPDMA_CTLSTAT + 1); + readb(sata_mmio + PDC_FPDMA_CTLSTAT + 1); /* flush */ +} + +static void pdc_fpdma_reset(struct ata_port *ap) +{ + void __iomem *sata_mmio = ap->ioaddr.scr_addr; + u8 tmp; + + tmp = (u8)readl(sata_mmio + PDC_FPDMA_CTLSTAT); + tmp &= 0x7F; + tmp |= PDC_FPDMA_CTLSTAT_RESET; + writeb(tmp, sata_mmio + PDC_FPDMA_CTLSTAT); + readl(sata_mmio + PDC_FPDMA_CTLSTAT); /* flush */ + udelay(100); + tmp &= ~PDC_FPDMA_CTLSTAT_RESET; + writeb(tmp, sata_mmio + PDC_FPDMA_CTLSTAT); + readl(sata_mmio + PDC_FPDMA_CTLSTAT); /* flush */ + + pdc_fpdma_clear_interrupt_flag(ap); +} + +static void pdc_not_at_command_packet_phase(struct ata_port *ap) +{ + void __iomem *sata_mmio = ap->ioaddr.scr_addr; + unsigned int i; + u32 tmp; + + /* check not at ASIC packet command phase */ + for (i = 0; i < 100; ++i) { + writel(0, sata_mmio + PDC_INTERNAL_DEBUG_1); + tmp = readl(sata_mmio + PDC_INTERNAL_DEBUG_2); + if ((tmp & 0xF) != 1) + break; + udelay(100); + } +} + +static void pdc_clear_internal_debug_record_error_register(struct ata_port *ap) +{ + void __iomem *sata_mmio = ap->ioaddr.scr_addr; + + writel(0xffffffff, sata_mmio + PDC_SATA_ERROR); + writel(0xffff0000, sata_mmio + PDC_LINK_LAYER_ERRORS); +} + static void pdc_reset_port(struct ata_port *ap) { void __iomem *ata_ctlstat_mmio = ap->ioaddr.cmd_addr + PDC_CTLSTAT; unsigned int i; u32 tmp; + if (ap->flags & PDC_FLAG_GEN_II) + pdc_not_at_command_packet_phase(ap); + + tmp = readl(ata_ctlstat_mmio); + tmp |= PDC_RESET; + writel(tmp, ata_ctlstat_mmio); + for (i = 11; i > 0; i--) { tmp = readl(ata_ctlstat_mmio); if (tmp & PDC_RESET) @@ -376,6 +454,11 @@ static void pdc_reset_port(struct ata_port *ap) tmp &= ~PDC_RESET; writel(tmp, ata_ctlstat_mmio); readl(ata_ctlstat_mmio); /* flush */ + + if (sata_scr_valid(&ap->link) && (ap->flags & PDC_FLAG_GEN_II)) { + pdc_fpdma_reset(ap); + pdc_clear_internal_debug_record_error_register(ap); + } } static int pdc_pata_cable_detect(struct ata_port *ap) @@ -626,11 +709,6 @@ static unsigned int pdc_sata_ata_port_to_ata_no(const struct ata_port *ap) return pdc_port_no_to_ata_no(i, pdc_is_sataii_tx4(ap->flags)); } -static unsigned int pdc_sata_hotplug_offset(const struct ata_port *ap) -{ - return (ap->flags & PDC_FLAG_GEN_II) ? PDC2_SATA_PLUG_CSR : PDC_SATA_PLUG_CSR; -} - static void pdc_freeze(struct ata_port *ap) { void __iomem *ata_mmio = ap->ioaddr.cmd_addr; @@ -647,7 +725,7 @@ static void pdc_sata_freeze(struct ata_port *ap) { struct ata_host *host = ap->host; void __iomem *host_mmio = host->iomap[PDC_MMIO_BAR]; - unsigned int hotplug_offset = pdc_sata_hotplug_offset(ap); + unsigned int hotplug_offset = PDC2_SATA_PLUG_CSR; unsigned int ata_no = pdc_sata_ata_port_to_ata_no(ap); u32 hotplug_status; @@ -685,7 +763,7 @@ static void pdc_sata_thaw(struct ata_port *ap) { struct ata_host *host = ap->host; void __iomem *host_mmio = host->iomap[PDC_MMIO_BAR]; - unsigned int hotplug_offset = pdc_sata_hotplug_offset(ap); + unsigned int hotplug_offset = PDC2_SATA_PLUG_CSR; unsigned int ata_no = pdc_sata_ata_port_to_ata_no(ap); u32 hotplug_status; @@ -708,11 +786,50 @@ static int pdc_pata_softreset(struct ata_link *link, unsigned int *class, return ata_sff_softreset(link, class, deadline); } +static unsigned int pdc_ata_port_to_ata_no(const struct ata_port *ap) +{ + void __iomem *ata_mmio = ap->ioaddr.cmd_addr; + void __iomem *host_mmio = ap->host->iomap[PDC_MMIO_BAR]; + + /* ata_mmio == host_mmio + 0x200 + ata_no * 0x80 */ + return (ata_mmio - host_mmio - 0x200) / 0x80; +} + +static void pdc_hard_reset_port(struct ata_port *ap) +{ + void __iomem *host_mmio = ap->host->iomap[PDC_MMIO_BAR]; + void __iomem *pcictl_b1_mmio = host_mmio + PDC_PCI_CTL + 1; + unsigned int ata_no = pdc_ata_port_to_ata_no(ap); + u8 tmp; + + spin_lock(&ap->host->lock); + + tmp = readb(pcictl_b1_mmio); + tmp &= ~(0x10 << ata_no); + writeb(tmp, pcictl_b1_mmio); + readb(pcictl_b1_mmio); /* flush */ + udelay(100); + tmp |= (0x10 << ata_no); + writeb(tmp, pcictl_b1_mmio); + readb(pcictl_b1_mmio); /* flush */ + + spin_unlock(&ap->host->lock); +} + static int pdc_sata_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { + if (link->ap->flags & PDC_FLAG_GEN_II) + pdc_not_at_command_packet_phase(link->ap); + /* hotplug IRQs should have been masked by pdc_sata_freeze() */ + pdc_hard_reset_port(link->ap); pdc_reset_port(link->ap); - return sata_sff_hardreset(link, class, deadline); + + /* sata_promise can't reliably acquire the first D2H Reg FIS + * after hardreset. Do non-waiting hardreset and request + * follow-up SRST. + */ + return sata_std_hardreset(link, class, deadline); } static void pdc_error_handler(struct ata_port *ap) @@ -832,14 +949,14 @@ static irqreturn_t pdc_interrupt(int irq, void *dev_instance) spin_lock(&host->lock); /* read and clear hotplug flags for all ports */ - if (host->ports[0]->flags & PDC_FLAG_GEN_II) + if (host->ports[0]->flags & PDC_FLAG_GEN_II) { hotplug_offset = PDC2_SATA_PLUG_CSR; - else - hotplug_offset = PDC_SATA_PLUG_CSR; - hotplug_status = readl(host_mmio + hotplug_offset); - if (hotplug_status & 0xff) - writel(hotplug_status | 0xff, host_mmio + hotplug_offset); - hotplug_status &= 0xff; /* clear uninteresting bits */ + hotplug_status = readl(host_mmio + hotplug_offset); + if (hotplug_status & 0xff) + writel(hotplug_status | 0xff, host_mmio + hotplug_offset); + hotplug_status &= 0xff; /* clear uninteresting bits */ + } else + hotplug_status = 0; /* reading should also clear interrupts */ mask = readl(host_mmio + PDC_INT_SEQMASK); @@ -1034,9 +1151,11 @@ static void pdc_host_init(struct ata_host *host) tmp = readl(host_mmio + hotplug_offset); writel(tmp | 0xff, host_mmio + hotplug_offset); - /* unmask plug/unplug ints */ tmp = readl(host_mmio + hotplug_offset); - writel(tmp & ~0xff0000, host_mmio + hotplug_offset); + if (is_gen2) /* unmask plug/unplug ints */ + writel(tmp & ~0xff0000, host_mmio + hotplug_offset); + else /* mask plug/unplug ints */ + writel(tmp | 0xff0000, host_mmio + hotplug_offset); /* don't initialise TBG or SLEW on 2nd generation chips */ if (is_gen2) |