*
* Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
* Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
- * Copyright (C) 2003 Red Hat Inc <alan@redhat.com>
+ * Copyright (C) 2003 Red Hat Inc
*
*
* This program is free software; you can redistribute it and/or modify
* ICH2 spec c #20 - IDE PRD must not cross a 64K boundary
* and must be dword aligned
* ICH2 spec c #24 - UDMA mode 4,5 t85/86 should be 6ns not 3.3
+ * ICH7 errata #16 - MWDMA1 timings are incorrect
*
* Should have been BIOS fixed:
* 450NX: errata #19 - DMA hangs on old 450NX
#include <linux/dmi.h>
#define DRV_NAME "ata_piix"
-#define DRV_VERSION "2.12"
+#define DRV_VERSION "2.13"
enum {
PIIX_IOCFG = 0x54, /* IDE I/O configuration register */
ICH5_PMR = 0x90, /* port mapping register */
ICH5_PCS = 0x92, /* port control and status */
- PIIX_SCC = 0x0A, /* sub-class code register */
PIIX_SIDPR_BAR = 5,
PIIX_SIDPR_LEN = 16,
PIIX_SIDPR_IDX = 0,
PIIX_SIDPR_DATA = 4,
- PIIX_FLAG_AHCI = (1 << 27), /* AHCI possible */
PIIX_FLAG_CHECKINTR = (1 << 28), /* make sure PCI INTx enabled */
PIIX_FLAG_SIDPR = (1 << 29), /* SATA idx/data pair regs */
ich_pata_33, /* ICH up to UDMA 33 only */
ich_pata_66, /* ICH up to 66 Mhz */
ich_pata_100, /* ICH up to UDMA 100 */
+ ich_pata_100_nomwdma1, /* ICH up to UDMA 100 but with no MWDMA1*/
ich5_sata,
ich6_sata,
- ich6_sata_ahci,
- ich6m_sata_ahci,
- ich8_sata_ahci,
+ ich6m_sata,
+ ich8_sata,
ich8_2port_sata,
- ich8m_apple_sata_ahci, /* locks up on second port enable */
- tolapai_sata_ahci,
+ ich8m_apple_sata, /* locks up on second port enable */
+ tolapai_sata,
piix_pata_vmw, /* PIIX4 for VMware, spurious DMA_ERR */
};
struct piix_host_priv {
const int *map;
+ u32 saved_iocfg;
void __iomem *sidpr;
};
static int piix_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent);
-static void piix_pata_error_handler(struct ata_port *ap);
+static void piix_remove_one(struct pci_dev *pdev);
+static int piix_pata_prereset(struct ata_link *link, unsigned long deadline);
static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev);
static void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev);
static void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev);
static int ich_pata_cable_detect(struct ata_port *ap);
static u8 piix_vmw_bmdma_status(struct ata_port *ap);
-static int piix_sidpr_scr_read(struct ata_port *ap, unsigned int reg, u32 *val);
-static int piix_sidpr_scr_write(struct ata_port *ap, unsigned int reg, u32 val);
-static void piix_sidpr_error_handler(struct ata_port *ap);
+static int piix_sidpr_scr_read(struct ata_link *link,
+ unsigned int reg, u32 *val);
+static int piix_sidpr_scr_write(struct ata_link *link,
+ unsigned int reg, u32 val);
#ifdef CONFIG_PM
static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
static int piix_pci_device_resume(struct pci_dev *pdev);
/* ICH6 (and 6) (i915) UDMA 100 */
{ 0x8086, 0x266F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100 },
/* ICH7/7-R (i945, i975) UDMA 100*/
- { 0x8086, 0x27DF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100 },
- { 0x8086, 0x269E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100 },
+ { 0x8086, 0x27DF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100_nomwdma1 },
+ { 0x8086, 0x269E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100_nomwdma1 },
/* ICH8 Mobile PATA Controller */
{ 0x8086, 0x2850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100 },
- /* NOTE: The following PCI ids must be kept in sync with the
- * list in drivers/pci/quirks.c.
- */
-
+ /* SATA ports */
+
/* 82801EB (ICH5) */
{ 0x8086, 0x24d1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_sata },
/* 82801EB (ICH5) */
/* 82801FB/FW (ICH6/ICH6W) */
{ 0x8086, 0x2651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata },
/* 82801FR/FRW (ICH6R/ICH6RW) */
- { 0x8086, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata_ahci },
- /* 82801FBM ICH6M (ICH6R with only port 0 and 2 implemented) */
- { 0x8086, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6m_sata_ahci },
+ { 0x8086, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata },
+ /* 82801FBM ICH6M (ICH6R with only port 0 and 2 implemented).
+ * Attach iff the controller is in IDE mode. */
+ { 0x8086, 0x2653, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_IDE << 8, 0xffff00, ich6m_sata },
/* 82801GB/GR/GH (ICH7, identical to ICH6) */
- { 0x8086, 0x27c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata_ahci },
+ { 0x8086, 0x27c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata },
/* 2801GBM/GHM (ICH7M, identical to ICH6M) */
- { 0x8086, 0x27c4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6m_sata_ahci },
+ { 0x8086, 0x27c4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6m_sata },
/* Enterprise Southbridge 2 (631xESB/632xESB) */
- { 0x8086, 0x2680, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata_ahci },
+ { 0x8086, 0x2680, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata },
/* SATA Controller 1 IDE (ICH8) */
- { 0x8086, 0x2820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
+ { 0x8086, 0x2820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
/* SATA Controller 2 IDE (ICH8) */
{ 0x8086, 0x2825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
- /* Mobile SATA Controller IDE (ICH8M) */
- { 0x8086, 0x2828, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
/* Mobile SATA Controller IDE (ICH8M), Apple */
- { 0x8086, 0x2828, 0x106b, 0x00a0, 0, 0, ich8m_apple_sata_ahci },
+ { 0x8086, 0x2828, 0x106b, 0x00a0, 0, 0, ich8m_apple_sata },
+ { 0x8086, 0x2828, 0x106b, 0x00a1, 0, 0, ich8m_apple_sata },
+ { 0x8086, 0x2828, 0x106b, 0x00a3, 0, 0, ich8m_apple_sata },
+ /* Mobile SATA Controller IDE (ICH8M) */
+ { 0x8086, 0x2828, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
/* SATA Controller IDE (ICH9) */
- { 0x8086, 0x2920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
+ { 0x8086, 0x2920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
/* SATA Controller IDE (ICH9) */
{ 0x8086, 0x2921, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
/* SATA Controller IDE (ICH9) */
/* SATA Controller IDE (ICH9M) */
{ 0x8086, 0x292d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
/* SATA Controller IDE (ICH9M) */
- { 0x8086, 0x292e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
+ { 0x8086, 0x292e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
/* SATA Controller IDE (Tolapai) */
- { 0x8086, 0x5028, PCI_ANY_ID, PCI_ANY_ID, 0, 0, tolapai_sata_ahci },
+ { 0x8086, 0x5028, PCI_ANY_ID, PCI_ANY_ID, 0, 0, tolapai_sata },
/* SATA Controller IDE (ICH10) */
- { 0x8086, 0x3a00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
+ { 0x8086, 0x3a00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
/* SATA Controller IDE (ICH10) */
{ 0x8086, 0x3a06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
/* SATA Controller IDE (ICH10) */
- { 0x8086, 0x3a20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
+ { 0x8086, 0x3a20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
/* SATA Controller IDE (ICH10) */
{ 0x8086, 0x3a26, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
-
+ /* SATA Controller IDE (PCH) */
+ { 0x8086, 0x3b20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
+ /* SATA Controller IDE (PCH) */
+ { 0x8086, 0x3b21, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
+ /* SATA Controller IDE (PCH) */
+ { 0x8086, 0x3b26, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
+ /* SATA Controller IDE (PCH) */
+ { 0x8086, 0x3b28, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
+ /* SATA Controller IDE (PCH) */
+ { 0x8086, 0x3b2d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
+ /* SATA Controller IDE (PCH) */
+ { 0x8086, 0x3b2e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
{ } /* terminate list */
};
.name = DRV_NAME,
.id_table = piix_pci_tbl,
.probe = piix_init_one,
- .remove = ata_pci_remove_one,
+ .remove = piix_remove_one,
#ifdef CONFIG_PM
.suspend = piix_pci_device_suspend,
.resume = piix_pci_device_resume,
};
static struct scsi_host_template piix_sht = {
- .module = THIS_MODULE,
- .name = DRV_NAME,
- .ioctl = ata_scsi_ioctl,
- .queuecommand = ata_scsi_queuecmd,
- .can_queue = ATA_DEF_QUEUE,
- .this_id = ATA_SHT_THIS_ID,
- .sg_tablesize = LIBATA_MAX_PRD,
- .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
- .emulated = ATA_SHT_EMULATED,
- .use_clustering = ATA_SHT_USE_CLUSTERING,
- .proc_name = DRV_NAME,
- .dma_boundary = ATA_DMA_BOUNDARY,
- .slave_configure = ata_scsi_slave_config,
- .slave_destroy = ata_scsi_slave_destroy,
- .bios_param = ata_std_bios_param,
+ ATA_BMDMA_SHT(DRV_NAME),
};
-static const struct ata_port_operations piix_pata_ops = {
+static struct ata_port_operations piix_pata_ops = {
+ .inherits = &ata_bmdma32_port_ops,
+ .cable_detect = ata_cable_40wire,
.set_piomode = piix_set_piomode,
.set_dmamode = piix_set_dmamode,
- .mode_filter = ata_pci_default_filter,
-
- .tf_load = ata_tf_load,
- .tf_read = ata_tf_read,
- .check_status = ata_check_status,
- .exec_command = ata_exec_command,
- .dev_select = ata_std_dev_select,
-
- .bmdma_setup = ata_bmdma_setup,
- .bmdma_start = ata_bmdma_start,
- .bmdma_stop = ata_bmdma_stop,
- .bmdma_status = ata_bmdma_status,
- .qc_prep = ata_qc_prep,
- .qc_issue = ata_qc_issue_prot,
- .data_xfer = ata_data_xfer,
-
- .freeze = ata_bmdma_freeze,
- .thaw = ata_bmdma_thaw,
- .error_handler = piix_pata_error_handler,
- .post_internal_cmd = ata_bmdma_post_internal_cmd,
- .cable_detect = ata_cable_40wire,
-
- .irq_clear = ata_bmdma_irq_clear,
- .irq_on = ata_irq_on,
+ .prereset = piix_pata_prereset,
+};
- .port_start = ata_port_start,
+static struct ata_port_operations piix_vmw_ops = {
+ .inherits = &piix_pata_ops,
+ .bmdma_status = piix_vmw_bmdma_status,
};
-static const struct ata_port_operations ich_pata_ops = {
- .set_piomode = piix_set_piomode,
- .set_dmamode = ich_set_dmamode,
- .mode_filter = ata_pci_default_filter,
-
- .tf_load = ata_tf_load,
- .tf_read = ata_tf_read,
- .check_status = ata_check_status,
- .exec_command = ata_exec_command,
- .dev_select = ata_std_dev_select,
-
- .bmdma_setup = ata_bmdma_setup,
- .bmdma_start = ata_bmdma_start,
- .bmdma_stop = ata_bmdma_stop,
- .bmdma_status = ata_bmdma_status,
- .qc_prep = ata_qc_prep,
- .qc_issue = ata_qc_issue_prot,
- .data_xfer = ata_data_xfer,
-
- .freeze = ata_bmdma_freeze,
- .thaw = ata_bmdma_thaw,
- .error_handler = piix_pata_error_handler,
- .post_internal_cmd = ata_bmdma_post_internal_cmd,
+static struct ata_port_operations ich_pata_ops = {
+ .inherits = &piix_pata_ops,
.cable_detect = ich_pata_cable_detect,
-
- .irq_clear = ata_bmdma_irq_clear,
- .irq_on = ata_irq_on,
-
- .port_start = ata_port_start,
+ .set_dmamode = ich_set_dmamode,
};
-static const struct ata_port_operations piix_sata_ops = {
- .tf_load = ata_tf_load,
- .tf_read = ata_tf_read,
- .check_status = ata_check_status,
- .exec_command = ata_exec_command,
- .dev_select = ata_std_dev_select,
-
- .bmdma_setup = ata_bmdma_setup,
- .bmdma_start = ata_bmdma_start,
- .bmdma_stop = ata_bmdma_stop,
- .bmdma_status = ata_bmdma_status,
- .qc_prep = ata_qc_prep,
- .qc_issue = ata_qc_issue_prot,
- .data_xfer = ata_data_xfer,
-
- .freeze = ata_bmdma_freeze,
- .thaw = ata_bmdma_thaw,
- .error_handler = ata_bmdma_error_handler,
- .post_internal_cmd = ata_bmdma_post_internal_cmd,
-
- .irq_clear = ata_bmdma_irq_clear,
- .irq_on = ata_irq_on,
-
- .port_start = ata_port_start,
+static struct ata_port_operations piix_sata_ops = {
+ .inherits = &ata_bmdma_port_ops,
};
-static const struct ata_port_operations piix_vmw_ops = {
- .set_piomode = piix_set_piomode,
- .set_dmamode = piix_set_dmamode,
- .mode_filter = ata_pci_default_filter,
-
- .tf_load = ata_tf_load,
- .tf_read = ata_tf_read,
- .check_status = ata_check_status,
- .exec_command = ata_exec_command,
- .dev_select = ata_std_dev_select,
-
- .bmdma_setup = ata_bmdma_setup,
- .bmdma_start = ata_bmdma_start,
- .bmdma_stop = ata_bmdma_stop,
- .bmdma_status = piix_vmw_bmdma_status,
- .qc_prep = ata_qc_prep,
- .qc_issue = ata_qc_issue_prot,
- .data_xfer = ata_data_xfer,
-
- .freeze = ata_bmdma_freeze,
- .thaw = ata_bmdma_thaw,
- .error_handler = piix_pata_error_handler,
- .post_internal_cmd = ata_bmdma_post_internal_cmd,
- .cable_detect = ata_cable_40wire,
-
- .irq_handler = ata_interrupt,
- .irq_clear = ata_bmdma_irq_clear,
- .irq_on = ata_irq_on,
-
- .port_start = ata_port_start,
-};
-
-static const struct ata_port_operations piix_sidpr_sata_ops = {
- .tf_load = ata_tf_load,
- .tf_read = ata_tf_read,
- .check_status = ata_check_status,
- .exec_command = ata_exec_command,
- .dev_select = ata_std_dev_select,
-
- .bmdma_setup = ata_bmdma_setup,
- .bmdma_start = ata_bmdma_start,
- .bmdma_stop = ata_bmdma_stop,
- .bmdma_status = ata_bmdma_status,
- .qc_prep = ata_qc_prep,
- .qc_issue = ata_qc_issue_prot,
- .data_xfer = ata_data_xfer,
-
+static struct ata_port_operations piix_sidpr_sata_ops = {
+ .inherits = &piix_sata_ops,
+ .hardreset = sata_std_hardreset,
.scr_read = piix_sidpr_scr_read,
.scr_write = piix_sidpr_scr_write,
-
- .freeze = ata_bmdma_freeze,
- .thaw = ata_bmdma_thaw,
- .error_handler = piix_sidpr_error_handler,
- .post_internal_cmd = ata_bmdma_post_internal_cmd,
-
- .irq_clear = ata_bmdma_irq_clear,
- .irq_on = ata_irq_on,
-
- .port_start = ata_port_start,
};
static const struct piix_map_db ich5_map_db = {
static const struct piix_map_db *piix_map_db_table[] = {
[ich5_sata] = &ich5_map_db,
[ich6_sata] = &ich6_map_db,
- [ich6_sata_ahci] = &ich6_map_db,
- [ich6m_sata_ahci] = &ich6m_map_db,
- [ich8_sata_ahci] = &ich8_map_db,
+ [ich6m_sata] = &ich6m_map_db,
+ [ich8_sata] = &ich8_map_db,
[ich8_2port_sata] = &ich8_2port_map_db,
- [ich8m_apple_sata_ahci] = &ich8m_apple_map_db,
- [tolapai_sata_ahci] = &tolapai_map_db,
+ [ich8m_apple_sata] = &ich8m_apple_map_db,
+ [tolapai_sata] = &tolapai_map_db,
};
static struct ata_port_info piix_port_info[] = {
[piix_pata_mwdma] = /* PIIX3 MWDMA only */
{
.flags = PIIX_PATA_FLAGS,
- .pio_mask = 0x1f, /* pio0-4 */
- .mwdma_mask = 0x06, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
.port_ops = &piix_pata_ops,
},
[piix_pata_33] = /* PIIX4 at 33MHz */
{
.flags = PIIX_PATA_FLAGS,
- .pio_mask = 0x1f, /* pio0-4 */
- .mwdma_mask = 0x06, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
- .udma_mask = ATA_UDMA_MASK_40C,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
+ .udma_mask = ATA_UDMA2,
.port_ops = &piix_pata_ops,
},
[ich_pata_33] = /* ICH0 - ICH at 33Mhz*/
{
.flags = PIIX_PATA_FLAGS,
- .pio_mask = 0x1f, /* pio 0-4 */
- .mwdma_mask = 0x06, /* Check: maybe 0x07 */
- .udma_mask = ATA_UDMA2, /* UDMA33 */
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA12_ONLY, /* Check: maybe MWDMA0 is ok */
+ .udma_mask = ATA_UDMA2,
.port_ops = &ich_pata_ops,
},
[ich_pata_66] = /* ICH controllers up to 66MHz */
{
.flags = PIIX_PATA_FLAGS,
- .pio_mask = 0x1f, /* pio 0-4 */
- .mwdma_mask = 0x06, /* MWDMA0 is broken on chip */
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA12_ONLY, /* MWDMA0 is broken on chip */
.udma_mask = ATA_UDMA4,
.port_ops = &ich_pata_ops,
},
[ich_pata_100] =
{
.flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR,
- .pio_mask = 0x1f, /* pio0-4 */
- .mwdma_mask = 0x06, /* mwdma1-2 */
- .udma_mask = ATA_UDMA5, /* udma0-5 */
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA12_ONLY,
+ .udma_mask = ATA_UDMA5,
.port_ops = &ich_pata_ops,
},
- [ich5_sata] =
+ [ich_pata_100_nomwdma1] =
{
- .flags = PIIX_SATA_FLAGS,
- .pio_mask = 0x1f, /* pio0-4 */
- .mwdma_mask = 0x07, /* mwdma0-2 */
- .udma_mask = ATA_UDMA6,
- .port_ops = &piix_sata_ops,
+ .flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2_ONLY,
+ .udma_mask = ATA_UDMA5,
+ .port_ops = &ich_pata_ops,
},
- [ich6_sata] =
+ [ich5_sata] =
{
.flags = PIIX_SATA_FLAGS,
- .pio_mask = 0x1f, /* pio0-4 */
- .mwdma_mask = 0x07, /* mwdma0-2 */
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
.port_ops = &piix_sata_ops,
},
- [ich6_sata_ahci] =
+ [ich6_sata] =
{
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI,
- .pio_mask = 0x1f, /* pio0-4 */
- .mwdma_mask = 0x07, /* mwdma0-2 */
+ .flags = PIIX_SATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
.port_ops = &piix_sata_ops,
},
- [ich6m_sata_ahci] =
+ [ich6m_sata] =
{
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI,
- .pio_mask = 0x1f, /* pio0-4 */
- .mwdma_mask = 0x07, /* mwdma0-2 */
+ .flags = PIIX_SATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
.port_ops = &piix_sata_ops,
},
- [ich8_sata_ahci] =
+ [ich8_sata] =
{
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI |
- PIIX_FLAG_SIDPR,
- .pio_mask = 0x1f, /* pio0-4 */
- .mwdma_mask = 0x07, /* mwdma0-2 */
+ .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
.port_ops = &piix_sata_ops,
},
[ich8_2port_sata] =
{
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI |
- PIIX_FLAG_SIDPR,
- .pio_mask = 0x1f, /* pio0-4 */
- .mwdma_mask = 0x07, /* mwdma0-2 */
+ .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
.port_ops = &piix_sata_ops,
},
- [tolapai_sata_ahci] =
+ [tolapai_sata] =
{
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI,
- .pio_mask = 0x1f, /* pio0-4 */
- .mwdma_mask = 0x07, /* mwdma0-2 */
+ .flags = PIIX_SATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
.port_ops = &piix_sata_ops,
},
- [ich8m_apple_sata_ahci] =
+ [ich8m_apple_sata] =
{
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_AHCI |
- PIIX_FLAG_SIDPR,
- .pio_mask = 0x1f, /* pio0-4 */
- .mwdma_mask = 0x07, /* mwdma0-2 */
+ .flags = PIIX_SATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
.port_ops = &piix_sata_ops,
},
[piix_pata_vmw] =
{
- .sht = &piix_sht,
.flags = PIIX_PATA_FLAGS,
- .pio_mask = 0x1f, /* pio0-4 */
- .mwdma_mask = 0x06, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
- .udma_mask = ATA_UDMA_MASK_40C,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
+ .udma_mask = ATA_UDMA2,
.port_ops = &piix_vmw_ops,
},
{ 0x27DF, 0x0005, 0x0280 }, /* ICH7 on Acer 5602WLMi */
{ 0x27DF, 0x1025, 0x0102 }, /* ICH7 on Acer 5602aWLMi */
{ 0x27DF, 0x1025, 0x0110 }, /* ICH7 on Acer 3682WLMi */
+ { 0x27DF, 0x1028, 0x02b0 }, /* ICH7 on unknown Dell */
{ 0x27DF, 0x1043, 0x1267 }, /* ICH7 on Asus W5F */
{ 0x27DF, 0x103C, 0x30A1 }, /* ICH7 on HP Compaq nc2400 */
+ { 0x27DF, 0x103C, 0x361a }, /* ICH7 on unkown HP */
+ { 0x27DF, 0x1071, 0xD221 }, /* ICH7 on Hercules EC-900 */
+ { 0x27DF, 0x152D, 0x0778 }, /* ICH7 on unknown Intel */
{ 0x24CA, 0x1025, 0x0061 }, /* ICH4 on ACER Aspire 2023WLMi */
+ { 0x24CA, 0x1025, 0x003d }, /* ICH4 on ACER TM290 */
+ { 0x266F, 0x1025, 0x0066 }, /* ICH6 on ACER Aspire 1694WLMi */
+ { 0x2653, 0x1043, 0x82D8 }, /* ICH6M on Asus Eee 701 */
+ { 0x27df, 0x104d, 0x900e }, /* ICH7 on Sony TZ-90 */
/* end marker */
{ 0, }
};
static int ich_pata_cable_detect(struct ata_port *ap)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ struct piix_host_priv *hpriv = ap->host->private_data;
const struct ich_laptop *lap = &ich_laptop[0];
- u8 tmp, mask;
+ u8 mask;
/* Check for specials - Acer Aspire 5602WLMi */
while (lap->device) {
/* check BIOS cable detect results */
mask = ap->port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC;
- pci_read_config_byte(pdev, PIIX_IOCFG, &tmp);
- if ((tmp & mask) == 0)
+ if ((hpriv->saved_iocfg & mask) == 0)
return ATA_CBL_PATA40;
return ATA_CBL_PATA80;
}
if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no]))
return -ENOENT;
- return ata_std_prereset(link, deadline);
-}
-
-static void piix_pata_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, piix_pata_prereset, ata_std_softreset, NULL,
- ata_std_postreset);
+ return ata_sff_prereset(link, deadline);
}
/**
* do_pata_set_dmamode - Initialize host controller PATA PIO timings
* @ap: Port whose timings we are configuring
* @adev: Drive in question
- * @udma: udma mode, 0 - 6
* @isich: set if the chip is an ICH device
*
* Set UDMA mode for device, in host controller PCI config space.
* Serial ATA Index/Data Pair Superset Registers access
*
* Beginning from ICH8, there's a sane way to access SCRs using index
- * and data register pair located at BAR5. This creates an
- * interesting problem of mapping two SCRs to one port.
- *
- * Although they have separate SCRs, the master and slave aren't
- * independent enough to be treated as separate links - e.g. softreset
- * resets both. Also, there's no protocol defined for hard resetting
- * singled device sharing the virtual port (no defined way to acquire
- * device signature). This is worked around by merging the SCR values
- * into one sensible value and requesting follow-up SRST after
- * hardreset.
- *
- * SCR merging is perfomed in nibbles which is the unit contents in
- * SCRs are organized. If two values are equal, the value is used.
- * When they differ, merge table which lists precedence of possible
- * values is consulted and the first match or the last entry when
- * nothing matches is used. When there's no merge table for the
- * specific nibble, value from the first port is used.
+ * and data register pair located at BAR5 which means that we have
+ * separate SCRs for master and slave. This is handled using libata
+ * slave_link facility.
*/
static const int piix_sidx_map[] = {
[SCR_STATUS] = 0,
[SCR_CONTROL] = 1,
};
-static void piix_sidpr_sel(struct ata_device *dev, unsigned int reg)
+static void piix_sidpr_sel(struct ata_link *link, unsigned int reg)
{
- struct ata_port *ap = dev->link->ap;
+ struct ata_port *ap = link->ap;
struct piix_host_priv *hpriv = ap->host->private_data;
- iowrite32(((ap->port_no * 2 + dev->devno) << 8) | piix_sidx_map[reg],
+ iowrite32(((ap->port_no * 2 + link->pmp) << 8) | piix_sidx_map[reg],
hpriv->sidpr + PIIX_SIDPR_IDX);
}
-static int piix_sidpr_read(struct ata_device *dev, unsigned int reg)
-{
- struct piix_host_priv *hpriv = dev->link->ap->host->private_data;
-
- piix_sidpr_sel(dev, reg);
- return ioread32(hpriv->sidpr + PIIX_SIDPR_DATA);
-}
-
-static void piix_sidpr_write(struct ata_device *dev, unsigned int reg, u32 val)
-{
- struct piix_host_priv *hpriv = dev->link->ap->host->private_data;
-
- piix_sidpr_sel(dev, reg);
- iowrite32(val, hpriv->sidpr + PIIX_SIDPR_DATA);
-}
-
-static u32 piix_merge_scr(u32 val0, u32 val1, const int * const *merge_tbl)
+static int piix_sidpr_scr_read(struct ata_link *link,
+ unsigned int reg, u32 *val)
{
- u32 val = 0;
- int i, mi;
-
- for (i = 0, mi = 0; i < 32 / 4; i++) {
- u8 c0 = (val0 >> (i * 4)) & 0xf;
- u8 c1 = (val1 >> (i * 4)) & 0xf;
- u8 merged = c0;
- const int *cur;
-
- /* if no merge preference, assume the first value */
- cur = merge_tbl[mi];
- if (!cur)
- goto done;
- mi++;
-
- /* if two values equal, use it */
- if (c0 == c1)
- goto done;
-
- /* choose the first match or the last from the merge table */
- while (*cur != -1) {
- if (c0 == *cur || c1 == *cur)
- break;
- cur++;
- }
- if (*cur == -1)
- cur--;
- merged = *cur;
- done:
- val |= merged << (i * 4);
- }
-
- return val;
-}
-
-static int piix_sidpr_scr_read(struct ata_port *ap, unsigned int reg, u32 *val)
-{
- const int * const sstatus_merge_tbl[] = {
- /* DET */ (const int []){ 1, 3, 0, 4, 3, -1 },
- /* SPD */ (const int []){ 2, 1, 0, -1 },
- /* IPM */ (const int []){ 6, 2, 1, 0, -1 },
- NULL,
- };
- const int * const scontrol_merge_tbl[] = {
- /* DET */ (const int []){ 1, 0, 4, 0, -1 },
- /* SPD */ (const int []){ 0, 2, 1, 0, -1 },
- /* IPM */ (const int []){ 0, 1, 2, 3, 0, -1 },
- NULL,
- };
- u32 v0, v1;
+ struct piix_host_priv *hpriv = link->ap->host->private_data;
if (reg >= ARRAY_SIZE(piix_sidx_map))
return -EINVAL;
- if (!(ap->flags & ATA_FLAG_SLAVE_POSS)) {
- *val = piix_sidpr_read(&ap->link.device[0], reg);
- return 0;
- }
-
- v0 = piix_sidpr_read(&ap->link.device[0], reg);
- v1 = piix_sidpr_read(&ap->link.device[1], reg);
-
- switch (reg) {
- case SCR_STATUS:
- *val = piix_merge_scr(v0, v1, sstatus_merge_tbl);
- break;
- case SCR_ERROR:
- *val = v0 | v1;
- break;
- case SCR_CONTROL:
- *val = piix_merge_scr(v0, v1, scontrol_merge_tbl);
- break;
- }
-
+ piix_sidpr_sel(link, reg);
+ *val = ioread32(hpriv->sidpr + PIIX_SIDPR_DATA);
return 0;
}
-static int piix_sidpr_scr_write(struct ata_port *ap, unsigned int reg, u32 val)
+static int piix_sidpr_scr_write(struct ata_link *link,
+ unsigned int reg, u32 val)
{
+ struct piix_host_priv *hpriv = link->ap->host->private_data;
+
if (reg >= ARRAY_SIZE(piix_sidx_map))
return -EINVAL;
- piix_sidpr_write(&ap->link.device[0], reg, val);
-
- if (ap->flags & ATA_FLAG_SLAVE_POSS)
- piix_sidpr_write(&ap->link.device[1], reg, val);
-
+ piix_sidpr_sel(link, reg);
+ iowrite32(val, hpriv->sidpr + PIIX_SIDPR_DATA);
return 0;
}
-static int piix_sidpr_hardreset(struct ata_link *link, unsigned int *class,
- unsigned long deadline)
-{
- const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
- int rc;
-
- /* do hardreset */
- rc = sata_link_hardreset(link, timing, deadline);
- if (rc) {
- ata_link_printk(link, KERN_ERR,
- "COMRESET failed (errno=%d)\n", rc);
- return rc;
- }
-
- /* TODO: phy layer with polling, timeouts, etc. */
- if (ata_link_offline(link)) {
- *class = ATA_DEV_NONE;
- return 0;
- }
-
- return -EAGAIN;
-}
-
-static void piix_sidpr_error_handler(struct ata_port *ap)
-{
- ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset,
- piix_sidpr_hardreset, ata_std_postreset);
-}
-
#ifdef CONFIG_PM
static int piix_broken_suspend(void)
{
},
},
{
+ .ident = "TECRA M4",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TECRA M4"),
+ },
+ },
+ {
.ident = "TECRA M5",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M500"),
},
},
+ {
+ .ident = "VGN-BX297XP",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-BX297XP"),
+ },
+ },
{ } /* terminate list */
};
if (dmi_find_device(DMI_DEV_TYPE_OEM_STRING, oemstrs[i], NULL))
return 1;
+ /* TECRA M4 sometimes forgets its identify and reports bogus
+ * DMI information. As the bogus information is a bit
+ * generic, match as many entries as possible. This manual
+ * matching is necessary because dmi_system_id.matches is
+ * limited to four entries.
+ */
+ if (dmi_match(DMI_SYS_VENDOR, "TOSHIBA") &&
+ dmi_match(DMI_PRODUCT_NAME, "000000") &&
+ dmi_match(DMI_PRODUCT_VERSION, "000000") &&
+ dmi_match(DMI_PRODUCT_SERIAL, "000000") &&
+ dmi_match(DMI_BOARD_VENDOR, "TOSHIBA") &&
+ dmi_match(DMI_BOARD_NAME, "Portable PC") &&
+ dmi_match(DMI_BOARD_VERSION, "Version A0"))
+ return 1;
+
return 0;
}
* cycles and power trying to do something to the sleeping
* beauty.
*/
- if (piix_broken_suspend() && mesg.event == PM_EVENT_SUSPEND) {
+ if (piix_broken_suspend() && (mesg.event & PM_EVENT_SLEEP)) {
pci_save_state(pdev);
/* mark its power state as "unknown", since we don't
return map;
}
-static void __devinit piix_init_sidpr(struct ata_host *host)
+static bool piix_no_sidpr(struct ata_host *host)
+{
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+
+ /*
+ * Samsung DB-P70 only has three ATA ports exposed and
+ * curiously the unconnected first port reports link online
+ * while not responding to SRST protocol causing excessive
+ * detection delay.
+ *
+ * Unfortunately, the system doesn't carry enough DMI
+ * information to identify the machine but does have subsystem
+ * vendor and device set. As it's unclear whether the
+ * subsystem vendor/device is used only for this specific
+ * board, the port can't be disabled solely with the
+ * information; however, turning off SIDPR access works around
+ * the problem. Turn it off.
+ *
+ * This problem is reported in bnc#441240.
+ *
+ * https://bugzilla.novell.com/show_bug.cgi?id=441420
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->device == 0x2920 &&
+ pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG &&
+ pdev->subsystem_device == 0xb049) {
+ dev_printk(KERN_WARNING, host->dev,
+ "Samsung DB-P70 detected, disabling SIDPR\n");
+ return true;
+ }
+
+ return false;
+}
+
+static int __devinit piix_init_sidpr(struct ata_host *host)
{
struct pci_dev *pdev = to_pci_dev(host->dev);
struct piix_host_priv *hpriv = host->private_data;
- int i;
+ struct ata_link *link0 = &host->ports[0]->link;
+ u32 scontrol;
+ int i, rc;
/* check for availability */
for (i = 0; i < 4; i++)
if (hpriv->map[i] == IDE)
- return;
+ return 0;
+
+ /* is it blacklisted? */
+ if (piix_no_sidpr(host))
+ return 0;
if (!(host->ports[0]->flags & PIIX_FLAG_SIDPR))
- return;
+ return 0;
if (pci_resource_start(pdev, PIIX_SIDPR_BAR) == 0 ||
pci_resource_len(pdev, PIIX_SIDPR_BAR) != PIIX_SIDPR_LEN)
- return;
+ return 0;
if (pcim_iomap_regions(pdev, 1 << PIIX_SIDPR_BAR, DRV_NAME))
- return;
+ return 0;
hpriv->sidpr = pcim_iomap_table(pdev)[PIIX_SIDPR_BAR];
- host->ports[0]->ops = &piix_sidpr_sata_ops;
- host->ports[1]->ops = &piix_sidpr_sata_ops;
+
+ /* SCR access via SIDPR doesn't work on some configurations.
+ * Give it a test drive by inhibiting power save modes which
+ * we'll do anyway.
+ */
+ piix_sidpr_scr_read(link0, SCR_CONTROL, &scontrol);
+
+ /* if IPM is already 3, SCR access is probably working. Don't
+ * un-inhibit power save modes as BIOS might have inhibited
+ * them for a reason.
+ */
+ if ((scontrol & 0xf00) != 0x300) {
+ scontrol |= 0x300;
+ piix_sidpr_scr_write(link0, SCR_CONTROL, scontrol);
+ piix_sidpr_scr_read(link0, SCR_CONTROL, &scontrol);
+
+ if ((scontrol & 0xf00) != 0x300) {
+ dev_printk(KERN_INFO, host->dev, "SCR access via "
+ "SIDPR is available but doesn't work\n");
+ return 0;
+ }
+ }
+
+ /* okay, SCRs available, set ops and ask libata for slave_link */
+ for (i = 0; i < 2; i++) {
+ struct ata_port *ap = host->ports[i];
+
+ ap->ops = &piix_sidpr_sata_ops;
+
+ if (ap->flags & ATA_FLAG_SLAVE_POSS) {
+ rc = ata_slave_link_init(ap);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return 0;
}
-static void piix_iocfg_bit18_quirk(struct pci_dev *pdev)
+static void piix_iocfg_bit18_quirk(struct ata_host *host)
{
static const struct dmi_system_id sysids[] = {
{
{ } /* terminate list */
};
- u32 iocfg;
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+ struct piix_host_priv *hpriv = host->private_data;
if (!dmi_check_system(sysids))
return;
* seem to use it to disable a channel. Clear the bit on the
* affected systems.
*/
- pci_read_config_dword(pdev, PIIX_IOCFG, &iocfg);
- if (iocfg & (1 << 18)) {
+ if (hpriv->saved_iocfg & (1 << 18)) {
dev_printk(KERN_INFO, &pdev->dev,
"applying IOCFG bit18 quirk\n");
- iocfg &= ~(1 << 18);
- pci_write_config_dword(pdev, PIIX_IOCFG, iocfg);
+ pci_write_config_dword(pdev, PIIX_IOCFG,
+ hpriv->saved_iocfg & ~(1 << 18));
+ }
+}
+
+static bool piix_broken_system_poweroff(struct pci_dev *pdev)
+{
+ static const struct dmi_system_id broken_systems[] = {
+ {
+ .ident = "HP Compaq 2510p",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq 2510p"),
+ },
+ /* PCI slot number of the controller */
+ .driver_data = (void *)0x1FUL,
+ },
+ {
+ .ident = "HP Compaq nc6000",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nc6000"),
+ },
+ /* PCI slot number of the controller */
+ .driver_data = (void *)0x1FUL,
+ },
+
+ { } /* terminate list */
+ };
+ const struct dmi_system_id *dmi = dmi_first_match(broken_systems);
+
+ if (dmi) {
+ unsigned long slot = (unsigned long)dmi->driver_data;
+ /* apply the quirk only to on-board controllers */
+ return slot == PCI_SLOT(pdev->devfn);
}
+
+ return false;
}
/**
* Zero on success, or -ERRNO value.
*/
-static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit piix_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
{
static int printed_version;
struct device *dev = &pdev->dev;
dev_printk(KERN_DEBUG, &pdev->dev,
"version " DRV_VERSION "\n");
- /* no hotplugging support (FIXME) */
- if (!in_module_init)
+ /* no hotplugging support for later devices (FIXME) */
+ if (!in_module_init && ent->driver_data >= ich5_sata)
return -ENODEV;
+ if (piix_broken_system_poweroff(pdev)) {
+ piix_port_info[ent->driver_data].flags |=
+ ATA_FLAG_NO_POWEROFF_SPINDOWN |
+ ATA_FLAG_NO_HIBERNATE_SPINDOWN;
+ dev_info(&pdev->dev, "quirky BIOS, skipping spindown "
+ "on poweroff and hibernation\n");
+ }
+
port_info[0] = piix_port_info[ent->driver_data];
port_info[1] = piix_port_info[ent->driver_data];
if (rc)
return rc;
- /* SATA map init can change port_info, do it before prepping host */
hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
if (!hpriv)
return -ENOMEM;
+ /* Save IOCFG, this will be used for cable detection, quirk
+ * detection and restoration on detach. This is necessary
+ * because some ACPI implementations mess up cable related
+ * bits on _STM. Reported on kernel bz#11879.
+ */
+ pci_read_config_dword(pdev, PIIX_IOCFG, &hpriv->saved_iocfg);
+
+ /* ICH6R may be driven by either ata_piix or ahci driver
+ * regardless of BIOS configuration. Make sure AHCI mode is
+ * off.
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->device == 0x2652) {
+ rc = piix_disable_ahci(pdev);
+ if (rc)
+ return rc;
+ }
+
+ /* SATA map init can change port_info, do it before prepping host */
if (port_flags & ATA_FLAG_SATA)
hpriv->map = piix_init_sata_map(pdev, port_info,
piix_map_db_table[ent->driver_data]);
- rc = ata_pci_prepare_sff_host(pdev, ppi, &host);
+ rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
if (rc)
return rc;
host->private_data = hpriv;
/* initialize controller */
- if (port_flags & PIIX_FLAG_AHCI) {
- u8 tmp;
- pci_read_config_byte(pdev, PIIX_SCC, &tmp);
- if (tmp == PIIX_AHCI_DEVICE) {
- int rc = piix_disable_ahci(pdev);
- if (rc)
- return rc;
- }
- }
-
if (port_flags & ATA_FLAG_SATA) {
piix_init_pcs(host, piix_map_db_table[ent->driver_data]);
- piix_init_sidpr(host);
+ rc = piix_init_sidpr(host);
+ if (rc)
+ return rc;
}
/* apply IOCFG bit18 quirk */
- piix_iocfg_bit18_quirk(pdev);
+ piix_iocfg_bit18_quirk(host);
/* On ICH5, some BIOSen disable the interrupt using the
* PCI_COMMAND_INTX_DISABLE bit added in PCI 2.3.
host->ports[1]->mwdma_mask = 0;
host->ports[1]->udma_mask = 0;
}
+ host->flags |= ATA_HOST_PARALLEL_SCAN;
pci_set_master(pdev);
- return ata_pci_activate_sff_host(host, ata_interrupt, &piix_sht);
+ return ata_pci_sff_activate_host(host, ata_sff_interrupt, &piix_sht);
+}
+
+static void piix_remove_one(struct pci_dev *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct piix_host_priv *hpriv = host->private_data;
+
+ pci_write_config_dword(pdev, PIIX_IOCFG, hpriv->saved_iocfg);
+
+ ata_pci_remove_one(pdev);
}
static int __init piix_init(void)