/* * sata_nv.c - NVIDIA nForce SATA * * Copyright 2004 NVIDIA Corp. All rights reserved. * Copyright 2004 Andrew Chew * * * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * * libata documentation is available via 'make {ps|pdf}docs', * as Documentation/DocBook/libata.* * * No hardware documentation available outside of NVIDIA. * This driver programs the NVIDIA SATA controller in a similar * fashion as with other PCI IDE BMDMA controllers, with a few * NV-specific details such as register offsets, SATA phy location, * hotplug info, etc. * * * 0.08 * - Added support for MCP51 and MCP55. * * 0.07 * - Added support for RAID class code. * * 0.06 * - Added generic SATA support by using a pci_device_id that filters on * the IDE storage class code. * * 0.03 * - Fixed a bug where the hotplug handlers for non-CK804/MCP04 were using * mmio_base, which is only set for the CK804/MCP04 case. * * 0.02 * - Added support for CK804 SATA controller. * * 0.01 * - Initial revision. */ #include #include #include #include #include #include #include #include #include "scsi.h" #include #include //#define DEBUG #define DRV_NAME "sata_nv" #define DRV_VERSION "0.8" #define NV_PORTS 2 #define NV_PIO_MASK 0x1f #define NV_MWDMA_MASK 0x07 #define NV_UDMA_MASK 0x7f #define NV_PORT0_SCR_REG_OFFSET 0x00 #define NV_PORT1_SCR_REG_OFFSET 0x40 #define NV_INT_STATUS 0x10 #define NV_INT_STATUS_CK804 0x440 #define NV_INT_STATUS_PDEV_INT 0x01 #define NV_INT_STATUS_PDEV_PM 0x02 #define NV_INT_STATUS_PDEV_ADDED 0x04 #define NV_INT_STATUS_PDEV_REMOVED 0x08 #define NV_INT_STATUS_SDEV_INT 0x10 #define NV_INT_STATUS_SDEV_PM 0x20 #define NV_INT_STATUS_SDEV_ADDED 0x40 #define NV_INT_STATUS_SDEV_REMOVED 0x80 #define NV_INT_STATUS_PDEV_HOTPLUG (NV_INT_STATUS_PDEV_ADDED | \ NV_INT_STATUS_PDEV_REMOVED) #define NV_INT_STATUS_SDEV_HOTPLUG (NV_INT_STATUS_SDEV_ADDED | \ NV_INT_STATUS_SDEV_REMOVED) #define NV_INT_STATUS_HOTPLUG (NV_INT_STATUS_PDEV_HOTPLUG | \ NV_INT_STATUS_SDEV_HOTPLUG) #define NV_INT_ENABLE 0x11 #define NV_INT_ENABLE_CK804 0x441 #define NV_INT_ENABLE_PDEV_MASK 0x01 #define NV_INT_ENABLE_PDEV_PM 0x02 #define NV_INT_ENABLE_PDEV_ADDED 0x04 #define NV_INT_ENABLE_PDEV_REMOVED 0x08 #define NV_INT_ENABLE_SDEV_MASK 0x10 #define NV_INT_ENABLE_SDEV_PM 0x20 #define NV_INT_ENABLE_SDEV_ADDED 0x40 #define NV_INT_ENABLE_SDEV_REMOVED 0x80 #define NV_INT_ENABLE_PDEV_HOTPLUG (NV_INT_ENABLE_PDEV_ADDED | \ NV_INT_ENABLE_PDEV_REMOVED) #define NV_INT_ENABLE_SDEV_HOTPLUG (NV_INT_ENABLE_SDEV_ADDED | \ NV_INT_ENABLE_SDEV_REMOVED) #define NV_INT_ENABLE_HOTPLUG (NV_INT_ENABLE_PDEV_HOTPLUG | \ NV_INT_ENABLE_SDEV_HOTPLUG) #define NV_INT_CONFIG 0x12 #define NV_INT_CONFIG_METHD 0x01 // 0 = INT, 1 = SMI // For PCI config register 20 #define NV_MCP_SATA_CFG_20 0x50 #define NV_MCP_SATA_CFG_20_SATA_SPACE_EN 0x04 #define NV_MCP_SATA_CFG_20_PORT0_EN (1 << 17) #define NV_MCP_SATA_CFG_20_PORT1_EN (1 << 16) #define NV_MCP_SATA_CFG_20_PORT0_PWB_EN (1 << 14) #define NV_MCP_SATA_CFG_20_PORT1_PWB_EN (1 << 12) //#define NV_ADMA_NCQ #ifdef NV_ADMA_NCQ #define NV_ADMA_CAN_QUEUE ATA_MAX_QUEUE #else #define NV_ADMA_CAN_QUEUE ATA_DEF_QUEUE #endif #define NV_ADMA_CPB_SZ 128 #define NV_ADMA_APRD_SZ 16 #define NV_ADMA_SGTBL_LEN (1024 - NV_ADMA_CPB_SZ) / NV_ADMA_APRD_SZ #define NV_ADMA_SGTBL_SZ NV_ADMA_SGTBL_LEN * NV_ADMA_APRD_SZ #define NV_ADMA_PORT_PRIV_DMA_SZ NV_ADMA_CAN_QUEUE * (NV_ADMA_CPB_SZ + NV_ADMA_SGTBL_SZ) //#define NV_ADMA_MAX_CPBS 32 // BAR5 offset to ADMA general registers #define NV_ADMA_GEN 0x400 #define NV_ADMA_GEN_CTL 0x00 #define NV_ADMA_NOTIFIER_CLEAR 0x30 #define NV_ADMA_CHECK_INTR(GCTL, PORT) ((GCTL) & ( 1 << (19 + (12 * (PORT))))) // BAR5 offset to ADMA ports #define NV_ADMA_PORT 0x480 // size of ADMA port register space #define NV_ADMA_PORT_SIZE 0x100 // ADMA port registers #define NV_ADMA_CTL 0x40 #define NV_ADMA_CPB_COUNT 0x42 #define NV_ADMA_NEXT_CPB_IDX 0x43 #define NV_ADMA_STAT 0x44 #define NV_ADMA_CPB_BASE_LOW 0x48 #define NV_ADMA_CPB_BASE_HIGH 0x4C #define NV_ADMA_APPEND 0x50 #define NV_ADMA_NOTIFIER 0x68 #define NV_ADMA_NOTIFIER_ERROR 0x6C // NV_ADMA_CTL register bits #define NV_ADMA_CTL_HOTPLUG_IEN (1 << 0) #define NV_ADMA_CTL_CHANNEL_RESET (1 << 5) #define NV_ADMA_CTL_GO (1 << 7) #define NV_ADMA_CTL_AIEN (1 << 8) #define NV_ADMA_CTL_READ_NON_COHERENT (1 << 11) #define NV_ADMA_CTL_WRITE_NON_COHERENT (1 << 12) // CPB response flag bits #define NV_CPB_RESP_DONE (1 << 0) #define NV_CPB_RESP_ATA_ERR (1 << 3) #define NV_CPB_RESP_CMD_ERR (1 << 4) #define NV_CPB_RESP_CPB_ERR (1 << 7) // CPB control flag bits #define NV_CPB_CTL_CPB_VALID (1 << 0) #define NV_CPB_CTL_QUEUE (1 << 1) #define NV_CPB_CTL_APRD_VALID (1 << 2) #define NV_CPB_CTL_IEN (1 << 3) #define NV_CPB_CTL_FPDMA (1 << 4) // APRD flags #define NV_APRD_WRITE (1 << 1) #define NV_APRD_END (1 << 2) #define NV_APRD_CONT (1 << 3) // NV_ADMA_STAT flags #define NV_ADMA_STAT_TIMEOUT (1 << 0) #define NV_ADMA_STAT_HOTUNPLUG (1 << 1) #define NV_ADMA_STAT_HOTPLUG (1 << 2) #define NV_ADMA_STAT_CPBERR (1 << 4) #define NV_ADMA_STAT_SERROR (1 << 5) #define NV_ADMA_STAT_CMD_COMPLETE (1 << 6) #define NV_ADMA_STAT_IDLE (1 << 8) #define NV_ADMA_STAT_LEGACY (1 << 9) #define NV_ADMA_STAT_STOPPED (1 << 10) #define NV_ADMA_STAT_DONE (1 << 12) #define NV_ADMA_STAT_ERR (NV_ADMA_STAT_CPBERR | NV_ADMA_STAT_TIMEOUT) // port flags #define NV_ADMA_PORT_REGISTER_MODE (1 << 0) #ifndef min #define min(x,y) ((x) < (y) ? x : y) #endif struct nv_adma_prd { u64 addr; u32 len; u8 flags; u8 packet_len; u16 reserved; }; enum nv_adma_regbits { CMDEND = (1 << 15), /* end of command list */ WNB = (1 << 14), /* wait-not-BSY */ IGN = (1 << 13), /* ignore this entry */ CS1n = (1 << (4 + 8)), /* std. PATA signals follow... */ DA2 = (1 << (2 + 8)), DA1 = (1 << (1 + 8)), DA0 = (1 << (0 + 8)), }; struct nv_adma_cpb { u8 resp_flags; //0 u8 reserved1; //1 u8 ctl_flags; //2 // len is length of taskfile in 64 bit words u8 len; //3 u8 tag; //4 u8 next_cpb_idx; //5 u16 reserved2; //6-7 u16 tf[12]; //8-31 struct nv_adma_prd aprd[5]; //32-111 u64 next_aprd; //112-119 u64 reserved3; //120-127 }; struct nv_adma_port_priv { struct nv_adma_cpb *cpb; // u8 cpb_idx; u8 flags; u32 notifier; u32 notifier_error; dma_addr_t cpb_dma; struct nv_adma_prd *aprd; dma_addr_t aprd_dma; }; static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); static irqreturn_t nv_interrupt (int irq, void *dev_instance, struct pt_regs *regs); static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg); static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static void nv_host_stop (struct ata_host_set *host_set); static int nv_port_start(struct ata_port *ap); static void nv_port_stop(struct ata_port *ap); static int nv_adma_port_start(struct ata_port *ap); static void nv_adma_port_stop(struct ata_port *ap); static void nv_irq_clear(struct ata_port *ap); static void nv_adma_irq_clear(struct ata_port *ap); static void nv_enable_hotplug(struct ata_probe_ent *probe_ent); static void nv_disable_hotplug(struct ata_host_set *host_set); static void nv_check_hotplug(struct ata_host_set *host_set); static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent); static void nv_disable_hotplug_ck804(struct ata_host_set *host_set); static void nv_check_hotplug_ck804(struct ata_host_set *host_set); static void nv_enable_hotplug_adma(struct ata_probe_ent *probe_ent); static void nv_disable_hotplug_adma(struct ata_host_set *host_set); static void nv_check_hotplug_adma(struct ata_host_set *host_set); static void nv_qc_prep(struct ata_queued_cmd *qc); static int nv_qc_issue(struct ata_queued_cmd *qc); static int nv_adma_qc_issue(struct ata_queued_cmd *qc); static void nv_adma_qc_prep(struct ata_queued_cmd *qc); static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, u16 *cpb); static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb); static void nv_adma_fill_aprd(struct ata_queued_cmd *qc, int idx, struct nv_adma_prd *aprd); static void nv_adma_register_mode(struct ata_port *ap); static void nv_adma_mode(struct ata_port *ap); static u8 nv_bmdma_status(struct ata_port *ap); static u8 nv_adma_bmdma_status(struct ata_port *ap); static void nv_bmdma_stop(struct ata_queued_cmd *qc); static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc); static void nv_eng_timeout(struct ata_port *ap); static void nv_adma_eng_timeout(struct ata_port *ap); #ifdef DEBUG static void nv_adma_dump_cpb(struct nv_adma_cpb *cpb); static void nv_adma_dump_aprd(struct nv_adma_prd *aprd); static void nv_adma_dump_cpb_tf(u16 tf); static void nv_adma_dump_port(struct ata_port *ap); static void nv_adma_dump_iomem(void __iomem *m, int len); #endif enum nv_host_type { GENERIC, NFORCE2, NFORCE3, CK804, MCP51, MCP55, ADMA }; static struct pci_device_id nv_pci_tbl[] = { { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE2 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP51 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP51 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP55 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP55 }, { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE<<8, 0xffff00, GENERIC }, { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_RAID<<8, 0xffff00, GENERIC }, { 0, } /* terminate list */ }; #define NV_HOST_FLAGS_SCR_MMIO 0x00000001 struct nv_host_desc { enum nv_host_type host_type; void (*enable_hotplug)(struct ata_probe_ent *probe_ent); void (*disable_hotplug)(struct ata_host_set *host_set); void (*check_hotplug)(struct ata_host_set *host_set); }; static struct nv_host_desc nv_device_tbl[] = { { .host_type = GENERIC, .enable_hotplug = NULL, .disable_hotplug= NULL, .check_hotplug = NULL, }, { .host_type = NFORCE2, .enable_hotplug = nv_enable_hotplug, .disable_hotplug= nv_disable_hotplug, .check_hotplug = nv_check_hotplug, }, { .host_type = NFORCE3, .enable_hotplug = nv_enable_hotplug, .disable_hotplug= nv_disable_hotplug, .check_hotplug = nv_check_hotplug, }, { .host_type = CK804, .enable_hotplug = nv_enable_hotplug_ck804, .disable_hotplug= nv_disable_hotplug_ck804, .check_hotplug = nv_check_hotplug_ck804, }, { .host_type = MCP51, .enable_hotplug = nv_enable_hotplug, .disable_hotplug= nv_disable_hotplug, .check_hotplug = nv_check_hotplug, }, { .host_type = MCP55, .enable_hotplug = nv_enable_hotplug, .disable_hotplug= nv_disable_hotplug, .check_hotplug = nv_check_hotplug, }, { .host_type = ADMA, .enable_hotplug = nv_enable_hotplug_adma, .disable_hotplug= nv_disable_hotplug_adma, .check_hotplug = nv_check_hotplug_adma, }, }; struct nv_host { struct nv_host_desc *host_desc; unsigned long host_flags; }; static struct pci_driver nv_pci_driver = { .name = DRV_NAME, .id_table = nv_pci_tbl, .probe = nv_init_one, .remove = ata_pci_remove_one, }; static struct scsi_host_template nv_sht = { .module = THIS_MODULE, .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .queuecommand = ata_scsi_queuecmd, .eh_strategy_handler = ata_scsi_error, .can_queue = ATA_DEF_QUEUE, .this_id = ATA_SHT_THIS_ID, .sg_tablesize = LIBATA_MAX_PRD, .max_sectors = ATA_MAX_SECTORS, .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, .bios_param = ata_std_bios_param, .ordered_flush = 1, }; static struct ata_port_operations nv_ops = { .port_disable = ata_port_disable, .tf_load = ata_tf_load, .tf_read = ata_tf_read, .exec_command = ata_exec_command, .check_status = ata_check_status, .dev_select = ata_std_dev_select, .phy_reset = sata_phy_reset, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, .bmdma_stop = nv_bmdma_stop, .bmdma_status = nv_bmdma_status, .qc_prep = nv_qc_prep, .qc_issue = nv_qc_issue, .eng_timeout = nv_eng_timeout, .irq_handler = nv_interrupt, .irq_clear = nv_irq_clear, .scr_read = nv_scr_read, .scr_write = nv_scr_write, .port_start = nv_port_start, .port_stop = nv_port_stop, .host_stop = nv_host_stop, }; static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, u16 *cpb) { unsigned int idx = 0; cpb[idx++] = cpu_to_le16((ATA_REG_DEVICE << 8) | tf->device | WNB); if ((tf->flags & ATA_TFLAG_LBA48) == 0) { cpb[idx++] = cpu_to_le16(IGN); cpb[idx++] = cpu_to_le16(IGN); cpb[idx++] = cpu_to_le16(IGN); cpb[idx++] = cpu_to_le16(IGN); cpb[idx++] = cpu_to_le16(IGN); } else { cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->hob_feature); cpb[idx++] = cpu_to_le16((ATA_REG_NSECT << 8) | tf->hob_nsect); cpb[idx++] = cpu_to_le16((ATA_REG_LBAL << 8) | tf->hob_lbal); cpb[idx++] = cpu_to_le16((ATA_REG_LBAM << 8) | tf->hob_lbam); cpb[idx++] = cpu_to_le16((ATA_REG_LBAH << 8) | tf->hob_lbah); } cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->feature); cpb[idx++] = cpu_to_le16((ATA_REG_NSECT << 8) | tf->nsect); cpb[idx++] = cpu_to_le16((ATA_REG_LBAL << 8) | tf->lbal); cpb[idx++] = cpu_to_le16((ATA_REG_LBAM << 8) | tf->lbam); cpb[idx++] = cpu_to_le16((ATA_REG_LBAH << 8) | tf->lbah); cpb[idx++] = cpu_to_le16((ATA_REG_CMD << 8) | tf->command | CMDEND); return idx; } static inline void __iomem *__nv_adma_ctl_block(void __iomem *mmio, unsigned int port_no) { mmio += NV_ADMA_PORT + port_no * NV_ADMA_PORT_SIZE; return mmio; } static inline void __iomem *nv_adma_ctl_block(struct ata_port *ap) { return __nv_adma_ctl_block(ap->host_set->mmio_base, ap->port_no); } static inline void __iomem *nv_adma_gen_block(struct ata_port *ap) { return (ap->host_set->mmio_base + NV_ADMA_GEN); } static inline void __iomem *nv_adma_notifier_clear_block(struct ata_port *ap) { return (nv_adma_gen_block(ap) + NV_ADMA_NOTIFIER_CLEAR + (4 * ap->port_no)); } static inline void nv_adma_reset_channel(struct ata_port *ap) { void __iomem *mmio = nv_adma_ctl_block(ap); u16 tmp; // clear CPB fetch count writew(0, mmio + NV_ADMA_CPB_COUNT); // clear GO tmp = readw(mmio + NV_ADMA_CTL); writew(tmp & ~NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL); tmp = readw(mmio + NV_ADMA_CTL); writew(tmp | NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL); udelay(1); writew(tmp & ~NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL); } static inline int nv_adma_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc) { void __iomem *mmio = nv_adma_ctl_block(ap); struct nv_adma_port_priv *pp = ap->private_data; struct nv_adma_cpb *cpb = &pp->cpb[qc->tag]; u16 status; u32 gen_ctl; u16 flags; int have_err = 0; int handled = 0; status = readw(mmio + NV_ADMA_STAT); // if in ATA register mode, use standard ata interrupt handler if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) { VPRINTK("in ATA register mode\n"); return ata_host_intr(ap, qc); } gen_ctl = readl(nv_adma_gen_block(ap) + NV_ADMA_GEN_CTL); if (!NV_ADMA_CHECK_INTR(gen_ctl, ap->port_no)) { return 0; } if (!pp->notifier && !pp->notifier_error) { if (status) { VPRINTK("XXX no notifier, but status 0x%x\n", status); #ifdef DEBUG nv_adma_dump_port(ap); nv_adma_dump_cpb(cpb); #endif } else { return 0; } } if (pp->notifier_error) { have_err = 1; handled = 1; } if (status & NV_ADMA_STAT_TIMEOUT) { VPRINTK("timeout, stat = 0x%x\n", status); have_err = 1; handled = 1; } if (status & NV_ADMA_STAT_CPBERR) { VPRINTK("CPB error, stat = 0x%x\n", status); have_err = 1; handled = 1; } if (status & NV_ADMA_STAT_STOPPED) { VPRINTK("ADMA stopped, stat = 0x%x, resp_flags = 0x%x\n", status, cpb->resp_flags); if (!(status & NV_ADMA_STAT_DONE)) { have_err = 1; handled = 1; } } if (status & NV_ADMA_STAT_CMD_COMPLETE) { VPRINTK("ADMA command complete, stat = 0x%x\n", status); } if (status & NV_ADMA_STAT_DONE) { flags = cpb->resp_flags; VPRINTK("CPB done, stat = 0x%x, flags = 0x%x\n", status, flags); handled = 1; if (!(status & NV_ADMA_STAT_IDLE)) { VPRINTK("XXX CPB done, but not idle\n"); } if (flags & NV_CPB_RESP_DONE) { VPRINTK("CPB flags done, flags = 0x%x\n", flags); } if (flags & NV_CPB_RESP_ATA_ERR) { VPRINTK("CPB flags ATA err, flags = 0x%x\n", flags); have_err = 1; } if (flags & NV_CPB_RESP_CMD_ERR) { VPRINTK("CPB flags CMD err, flags = 0x%x\n", flags); have_err = 1; } if (flags & NV_CPB_RESP_CPB_ERR) { VPRINTK("CPB flags CPB err, flags = 0x%x\n", flags); have_err = 1; } } // clear status writew(status, mmio + NV_ADMA_STAT); if (handled) { u8 ata_status = readb(mmio + (ATA_REG_STATUS * 4)); ata_qc_complete(qc, have_err ? (ata_status | ATA_ERR) : ata_status); } return handled; /* irq handled */ } /* FIXME: The hardware provides the necessary SATA PHY controls * to support ATA_FLAG_SATA_RESET. However, it is currently * necessary to disable that flag, to solve misdetection problems. * See http://bugme.osdl.org/show_bug.cgi?id=3352 for more info. * * This problem really needs to be investigated further. But in the * meantime, we avoid ATA_FLAG_SATA_RESET to get people working. */ static struct ata_port_info nv_port_info = { .sht = &nv_sht, .host_flags = ATA_FLAG_SATA | /* ATA_FLAG_SATA_RESET | */ ATA_FLAG_SRST | ATA_FLAG_NO_LEGACY, .pio_mask = NV_PIO_MASK, .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, .port_ops = &nv_ops, }; MODULE_AUTHOR("NVIDIA"); MODULE_DESCRIPTION("low-level driver for NVIDIA nForce SATA controller"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, nv_pci_tbl); MODULE_VERSION(DRV_VERSION); static inline void nv_enable_adma_space (struct pci_dev *pdev) { u8 regval; VPRINTK("ENTER\n"); pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, ®val); regval |= NV_MCP_SATA_CFG_20_SATA_SPACE_EN; pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval); } static inline void nv_disable_adma_space (struct pci_dev *pdev) { u8 regval; VPRINTK("ENTER\n"); pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, ®val); regval &= ~NV_MCP_SATA_CFG_20_SATA_SPACE_EN; pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval); } static void nv_irq_clear(struct ata_port *ap) { struct ata_host_set *host_set = ap->host_set; struct nv_host *host = host_set->private_data; if (host->host_desc->host_type == ADMA) { nv_adma_irq_clear(ap); } else { ata_bmdma_irq_clear(ap); } } static void nv_adma_irq_clear(struct ata_port *ap) { /* TODO */ } static u8 nv_bmdma_status(struct ata_port *ap) { struct ata_host_set *host_set = ap->host_set; struct nv_host *host = host_set->private_data; if (host->host_desc->host_type == ADMA) { return nv_adma_bmdma_status(ap); } else { return ata_bmdma_status(ap); } } static u8 nv_adma_bmdma_status(struct ata_port *ap) { return inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); } static void nv_bmdma_stop(struct ata_queued_cmd *qc) { struct ata_host_set *host_set = qc->ap->host_set; struct nv_host *host = host_set->private_data; if (host->host_desc->host_type == ADMA) { nv_adma_bmdma_stop(qc); } else { ata_bmdma_stop(qc); } } static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc) { /* TODO */ } static irqreturn_t nv_interrupt (int irq, void *dev_instance, struct pt_regs *regs) { struct ata_host_set *host_set = dev_instance; struct nv_host *host = host_set->private_data; unsigned int i; unsigned int handled = 0; unsigned long flags; spin_lock_irqsave(&host_set->lock, flags); for (i = 0; i < host_set->n_ports; i++) { struct ata_port *ap = host_set->ports[i]; struct nv_adma_port_priv *pp = ap->private_data; if (ap && !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) { void __iomem *mmio = nv_adma_ctl_block(ap); struct ata_queued_cmd *qc; // read notifiers pp->notifier = readl(mmio + NV_ADMA_NOTIFIER); pp->notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR); qc = ata_qc_from_tag(ap, ap->active_tag); if (qc && (!(qc->tf.ctl & ATA_NIEN))) { if (host->host_desc->host_type == ADMA) { handled += nv_adma_host_intr(ap, qc); } else { handled += ata_host_intr(ap, qc); } } } } if (host->host_desc->check_hotplug) host->host_desc->check_hotplug(host_set); // clear notifier if (handled) { for (i = 0; i < host_set->n_ports; i++) { struct ata_port *ap = host_set->ports[i]; struct nv_adma_port_priv *pp = ap->private_data; writel(pp->notifier | pp->notifier_error, nv_adma_notifier_clear_block(ap)); } } spin_unlock_irqrestore(&host_set->lock, flags); return IRQ_RETVAL(handled); } static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg) { struct ata_host_set *host_set = ap->host_set; struct nv_host *host = host_set->private_data; u32 val = 0; VPRINTK("ENTER\n"); VPRINTK("reading SCR reg %d, got 0x%08x\n", sc_reg, val); if (sc_reg > SCR_CONTROL) return 0xffffffffU; if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO) val = readl((void*)ap->ioaddr.scr_addr + (sc_reg * 4)); else val = inl(ap->ioaddr.scr_addr + (sc_reg * 4)); VPRINTK("reading SCR reg %d, got 0x%08x\n", sc_reg, val); return val; } static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) { struct ata_host_set *host_set = ap->host_set; struct nv_host *host = host_set->private_data; VPRINTK("ENTER\n"); VPRINTK("writing SCR reg %d with 0x%08x\n", sc_reg, val); if (sc_reg > SCR_CONTROL) return; if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO) writel(val, (void*)ap->ioaddr.scr_addr + (sc_reg * 4)); else outl(val, ap->ioaddr.scr_addr + (sc_reg * 4)); } static void nv_host_stop (struct ata_host_set *host_set) { struct nv_host *host = host_set->private_data; struct pci_dev *pdev = to_pci_dev(host_set->dev); VPRINTK("ENTER\n"); // Disable hotplug event interrupts. if (host->host_desc->disable_hotplug) host->host_desc->disable_hotplug(host_set); kfree(host); if (host_set->mmio_base) pci_iounmap(pdev, host_set->mmio_base); } static int nv_port_start(struct ata_port *ap) { struct ata_host_set *host_set = ap->host_set; struct nv_host *host = host_set->private_data; if (host->host_desc->host_type == ADMA) { return nv_adma_port_start(ap); } else { return ata_port_start(ap); } } static void nv_port_stop(struct ata_port *ap) { struct ata_host_set *host_set = ap->host_set; struct nv_host *host = host_set->private_data; if (host->host_desc->host_type == ADMA) { nv_adma_port_stop(ap); } else { ata_port_stop(ap); } } static int nv_adma_port_start(struct ata_port *ap) { struct device *dev = ap->host_set->dev; struct nv_adma_port_priv *pp; int rc; void *mem; dma_addr_t mem_dma; void __iomem *mmio = nv_adma_ctl_block(ap); VPRINTK("ENTER\n"); nv_adma_reset_channel(ap); #ifdef DEBUG VPRINTK("after reset:\n"); nv_adma_dump_port(ap); #endif rc = ata_port_start(ap); if (rc) return rc; pp = kmalloc(sizeof(*pp), GFP_KERNEL); if (!pp) { rc = -ENOMEM; goto err_out; } memset(pp, 0, sizeof(*pp)); mem = dma_alloc_coherent(dev, NV_ADMA_PORT_PRIV_DMA_SZ, &mem_dma, GFP_KERNEL); VPRINTK("dma memory: vaddr = 0x%08x, paddr = 0x%08x\n", (u32)mem, (u32)mem_dma); if (!mem) { rc = -ENOMEM; goto err_out_kfree; } memset(mem, 0, NV_ADMA_PORT_PRIV_DMA_SZ); /* * First item in chunk of DMA memory: * 128-byte command parameter block (CPB) * one for each command tag */ pp->cpb = mem; pp->cpb_dma = mem_dma; VPRINTK("cpb = 0x%08x, cpb_dma = 0x%08x\n", (u32)pp->cpb, (u32)pp->cpb_dma); writel(mem_dma, mmio + NV_ADMA_CPB_BASE_LOW); writel(0, mmio + NV_ADMA_CPB_BASE_HIGH); mem += NV_ADMA_CAN_QUEUE * NV_ADMA_CPB_SZ; mem_dma += NV_ADMA_CAN_QUEUE * NV_ADMA_CPB_SZ; /* * Second item: block of ADMA_SGTBL_LEN s/g entries */ pp->aprd = mem; pp->aprd_dma = mem_dma; VPRINTK("aprd = 0x%08x, aprd_dma = 0x%08x\n", (u32)pp->aprd, (u32)pp->aprd_dma); ap->private_data = pp; // clear any outstanding interrupt conditions writew(0xffff, mmio + NV_ADMA_STAT); // initialize port variables // pp->cpb_idx = 0; pp->flags = NV_ADMA_PORT_REGISTER_MODE; // make sure controller is in ATA register mode nv_adma_register_mode(ap); return 0; err_out_kfree: kfree(pp); err_out: ata_port_stop(ap); return rc; } static void nv_adma_port_stop(struct ata_port *ap) { struct device *dev = ap->host_set->dev; struct nv_adma_port_priv *pp = ap->private_data; void __iomem *mmio = nv_adma_ctl_block(ap); VPRINTK("ENTER\n"); writew(0, mmio + NV_ADMA_CTL); ap->private_data = NULL; dma_free_coherent(dev, NV_ADMA_PORT_PRIV_DMA_SZ, pp->cpb, pp->cpb_dma); kfree(pp); ata_port_stop(ap); } static void nv_adma_setup_port(struct ata_probe_ent *probe_ent, unsigned int port) { void __iomem *mmio = probe_ent->mmio_base; struct ata_ioports *ioport = &probe_ent->port[port]; VPRINTK("ENTER\n"); mmio += NV_ADMA_PORT + port * NV_ADMA_PORT_SIZE; ioport->cmd_addr = (unsigned long) mmio; ioport->data_addr = (unsigned long) mmio + (ATA_REG_DATA * 4); ioport->error_addr = ioport->feature_addr = (unsigned long) mmio + (ATA_REG_ERR * 4); ioport->nsect_addr = (unsigned long) mmio + (ATA_REG_NSECT * 4); ioport->lbal_addr = (unsigned long) mmio + (ATA_REG_LBAL * 4); ioport->lbam_addr = (unsigned long) mmio + (ATA_REG_LBAM * 4); ioport->lbah_addr = (unsigned long) mmio + (ATA_REG_LBAH * 4); ioport->device_addr = (unsigned long) mmio + (ATA_REG_DEVICE * 4); ioport->status_addr = ioport->command_addr = (unsigned long) mmio + (ATA_REG_STATUS * 4); ioport->altstatus_addr = ioport->ctl_addr = (unsigned long) mmio + 0x20; } static int nv_adma_host_init(struct ata_probe_ent *probe_ent) { struct pci_dev *pdev = to_pci_dev(probe_ent->dev); unsigned int i; u32 tmp32; VPRINTK("ENTER\n"); probe_ent->n_ports = NV_PORTS; nv_enable_adma_space(pdev); // enable ADMA on the ports pci_read_config_dword(pdev, NV_MCP_SATA_CFG_20, &tmp32); tmp32 |= NV_MCP_SATA_CFG_20_PORT0_EN | NV_MCP_SATA_CFG_20_PORT0_PWB_EN | NV_MCP_SATA_CFG_20_PORT1_EN | NV_MCP_SATA_CFG_20_PORT1_PWB_EN; pci_write_config_dword(pdev, NV_MCP_SATA_CFG_20, tmp32); for (i = 0; i < probe_ent->n_ports; i++) nv_adma_setup_port(probe_ent, i); for (i = 0; i < probe_ent->n_ports; i++) { void __iomem *mmio = __nv_adma_ctl_block(probe_ent->mmio_base, i); u16 tmp; /* enable interrupt, clear reset if not already clear */ tmp = readw(mmio + NV_ADMA_CTL); writew(tmp | NV_ADMA_CTL_AIEN, mmio + NV_ADMA_CTL); } pci_set_master(pdev); return 0; } static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version = 0; struct nv_host *host; struct ata_port_info *ppi; struct ata_probe_ent *probe_ent; struct nv_host_desc *host_desc; int pci_dev_busy = 0; int rc; u32 bar; VPRINTK("ENTER\n"); // Make sure this is a SATA controller by counting the number of bars // (NVIDIA SATA controllers will always have six bars). Otherwise, // it's an IDE controller and we ignore it. for (bar=0; bar<6; bar++) if (pci_resource_start(pdev, bar) == 0) return -ENODEV; if (!printed_version++) printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); rc = pci_enable_device(pdev); if (rc) goto err_out; rc = pci_request_regions(pdev, DRV_NAME); if (rc) { pci_dev_busy = 1; goto err_out_disable; } rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) goto err_out_regions; rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); if (rc) goto err_out_regions; rc = -ENOMEM; ppi = &nv_port_info; host_desc = &nv_device_tbl[ent->driver_data]; if (host_desc->host_type == ADMA) { // ADMA overrides ppi->host_flags |= ATA_FLAG_MMIO | ATA_FLAG_SATA_RESET; #ifdef NV_ADMA_NCQ ppi->host_flags |= ATA_FLAG_NCQ; #endif ppi->sht->can_queue = NV_ADMA_CAN_QUEUE; ppi->sht->sg_tablesize = NV_ADMA_SGTBL_LEN; // ppi->port_ops->irq_handler = nv_adma_interrupt; } probe_ent = ata_pci_init_native_mode(pdev, &ppi); if (!probe_ent) goto err_out_regions; host = kmalloc(sizeof(struct nv_host), GFP_KERNEL); if (!host) goto err_out_free_ent; memset(host, 0, sizeof(struct nv_host)); host->host_desc = host_desc; probe_ent->private_data = host; if (pci_resource_flags(pdev, 5) & IORESOURCE_MEM) host->host_flags |= NV_HOST_FLAGS_SCR_MMIO; if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO) { unsigned long base; probe_ent->mmio_base = pci_iomap(pdev, 5, 0); if (probe_ent->mmio_base == NULL) { rc = -EIO; goto err_out_free_host; } base = (unsigned long)probe_ent->mmio_base; VPRINTK("BAR5 base is at 0x%x\n", (u32)base); probe_ent->port[0].scr_addr = base + NV_PORT0_SCR_REG_OFFSET; probe_ent->port[1].scr_addr = base + NV_PORT1_SCR_REG_OFFSET; } else { probe_ent->port[0].scr_addr = pci_resource_start(pdev, 5) | NV_PORT0_SCR_REG_OFFSET; probe_ent->port[1].scr_addr = pci_resource_start(pdev, 5) | NV_PORT1_SCR_REG_OFFSET; } pci_set_master(pdev); if (ent->driver_data == ADMA) { rc = nv_adma_host_init(probe_ent); if (rc) goto err_out_iounmap; } rc = ata_device_add(probe_ent); if (rc != NV_PORTS) goto err_out_iounmap; // Enable hotplug event interrupts. if (host->host_desc->enable_hotplug) host->host_desc->enable_hotplug(probe_ent); kfree(probe_ent); return 0; err_out_iounmap: if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO) pci_iounmap(pdev, probe_ent->mmio_base); err_out_free_host: kfree(host); err_out_free_ent: kfree(probe_ent); err_out_regions: pci_release_regions(pdev); err_out_disable: if (!pci_dev_busy) pci_disable_device(pdev); err_out: return rc; } static void nv_eng_timeout(struct ata_port *ap) { struct ata_host_set *host_set = ap->host_set; struct nv_host *host = host_set->private_data; if (host->host_desc->host_type == ADMA) { nv_adma_eng_timeout(ap); } else { return ata_eng_timeout(ap); } } static void nv_adma_eng_timeout(struct ata_port *ap) { struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag); struct nv_adma_port_priv *pp = ap->private_data; u8 drv_stat; VPRINTK("ENTER\n"); if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) { ata_eng_timeout(ap); goto out; } if (!qc) { printk(KERN_ERR "ata%u: BUG: timeout without command\n", ap->id); goto out; } // spin_lock_irqsave(&host_set->lock, flags); qc->scsidone = scsi_finish_command; drv_stat = ata_chk_status(ap); printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x\n", ap->id, qc->tf.command, drv_stat); // reset channel nv_adma_reset_channel(ap); /* complete taskfile transaction */ ata_qc_complete(qc, drv_stat); // spin_unlock_irqrestore(&host_set->lock, flags); out: DPRINTK("EXIT\n"); } static void nv_qc_prep(struct ata_queued_cmd *qc) { struct ata_host_set *host_set = qc->ap->host_set; struct nv_host *host = host_set->private_data; if (host->host_desc->host_type == ADMA) { nv_adma_qc_prep(qc); } else { ata_qc_prep(qc); } } static void nv_adma_qc_prep(struct ata_queued_cmd *qc) { struct nv_adma_port_priv *pp = qc->ap->private_data; struct nv_adma_cpb *cpb = &pp->cpb[qc->tag]; VPRINTK("ENTER\n"); VPRINTK("qc->flags = 0x%x\n", (u32)qc->flags); if (!(qc->flags & ATA_QCFLAG_DMAMAP)) { ata_qc_prep(qc); return; } memset(cpb, 0, sizeof(struct nv_adma_cpb)); cpb->ctl_flags = NV_CPB_CTL_CPB_VALID | NV_CPB_CTL_APRD_VALID | NV_CPB_CTL_IEN; cpb->len = 3; cpb->tag = qc->tag; cpb->next_cpb_idx = 0; #ifdef NV_ADMA_NCQ // turn on NCQ flags for NCQ commands if (qc->flags & ATA_QCFLAG_NCQ) cpb->ctl_flags |= NV_CPB_CTL_QUEUE | NV_CPB_CTL_FPDMA; #endif nv_adma_tf_to_cpb(&qc->tf, cpb->tf); nv_adma_fill_sg(qc, cpb); } static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb) { struct nv_adma_port_priv *pp = qc->ap->private_data; unsigned int idx; struct nv_adma_prd *aprd; VPRINTK("ENTER\n"); idx = 0; for (idx = 0; idx < qc->n_elem; idx++) { if (idx < 5) { aprd = &cpb->aprd[idx]; } else { aprd = &pp->aprd[idx-5]; } nv_adma_fill_aprd(qc, idx, aprd); } if (idx > 5) { cpb->next_aprd = (u64)(pp->aprd_dma + NV_ADMA_APRD_SZ * qc->tag); } } static void nv_adma_fill_aprd(struct ata_queued_cmd *qc, int idx, struct nv_adma_prd *aprd) { u32 sg_len, addr, flags; memset(aprd, 0, sizeof(struct nv_adma_prd)); addr = sg_dma_address(&qc->sg[idx]); sg_len = sg_dma_len(&qc->sg[idx]); flags = 0; if (qc->tf.flags & ATA_TFLAG_WRITE) flags |= NV_APRD_WRITE; if (idx == qc->n_elem - 1) { flags |= NV_APRD_END; } else if (idx != 4) { flags |= NV_APRD_CONT; } aprd->addr = cpu_to_le32(addr); aprd->len = cpu_to_le32(sg_len); /* len in bytes */ aprd->flags = cpu_to_le32(flags); } static void nv_adma_register_mode(struct ata_port *ap) { void __iomem *mmio = nv_adma_ctl_block(ap); struct nv_adma_port_priv *pp = ap->private_data; u16 tmp; tmp = readw(mmio + NV_ADMA_CTL); writew(tmp & ~NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL); pp->flags |= NV_ADMA_PORT_REGISTER_MODE; } static void nv_adma_mode(struct ata_port *ap) { void __iomem *mmio = nv_adma_ctl_block(ap); struct nv_adma_port_priv *pp = ap->private_data; u16 tmp; if(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)) { return; } #if 0 nv_adma_reset_channel(ap); #endif tmp = readw(mmio + NV_ADMA_CTL); writew(tmp | NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL); pp->flags &= ~NV_ADMA_PORT_REGISTER_MODE; } static int nv_qc_issue(struct ata_queued_cmd *qc) { struct ata_host_set *host_set = qc->ap->host_set; struct nv_host *host = host_set->private_data; if (host->host_desc->host_type == ADMA) { return nv_adma_qc_issue(qc); } else { return ata_qc_issue_prot(qc); } } static int nv_adma_qc_issue(struct ata_queued_cmd *qc) { #if 0 struct nv_adma_port_priv *pp = qc->ap->private_data; #endif void __iomem *mmio = nv_adma_ctl_block(qc->ap); VPRINTK("ENTER\n"); if (!(qc->flags & ATA_QCFLAG_DMAMAP)) { VPRINTK("no dmamap, using ATA register mode: 0x%x\n", (u32)qc->flags); // use ATA register mode nv_adma_register_mode(qc->ap); return ata_qc_issue_prot(qc); } else { nv_adma_mode(qc->ap); } #if 0 nv_adma_dump_port(qc->ap); nv_adma_dump_cpb(&pp->cpb[qc->tag]); if (qc->n_elem > 5) { int i; for (i = 0; i < qc->n_elem - 5; i++) { nv_adma_dump_aprd(&pp->aprd[i]); } } #endif // // write append register, command tag in lower 8 bits // and (number of cpbs to append -1) in top 8 bits // mb(); writew(qc->tag, mmio + NV_ADMA_APPEND); VPRINTK("EXIT\n"); return 0; } static void nv_enable_hotplug(struct ata_probe_ent *probe_ent) { u8 intr_mask; outb(NV_INT_STATUS_HOTPLUG, probe_ent->port[0].scr_addr + NV_INT_STATUS); intr_mask = inb(probe_ent->port[0].scr_addr + NV_INT_ENABLE); intr_mask |= NV_INT_ENABLE_HOTPLUG; outb(intr_mask, probe_ent->port[0].scr_addr + NV_INT_ENABLE); } static void nv_disable_hotplug(struct ata_host_set *host_set) { u8 intr_mask; intr_mask = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE); intr_mask &= ~(NV_INT_ENABLE_HOTPLUG); outb(intr_mask, host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE); } static void nv_check_hotplug(struct ata_host_set *host_set) { u8 intr_status; intr_status = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS); // Clear interrupt status. outb(0xff, host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS); if (intr_status & NV_INT_STATUS_HOTPLUG) { if (intr_status & NV_INT_STATUS_PDEV_ADDED) printk(KERN_WARNING "nv_sata: " "Primary device added\n"); if (intr_status & NV_INT_STATUS_PDEV_REMOVED) printk(KERN_WARNING "nv_sata: " "Primary device removed\n"); if (intr_status & NV_INT_STATUS_SDEV_ADDED) printk(KERN_WARNING "nv_sata: " "Secondary device added\n"); if (intr_status & NV_INT_STATUS_SDEV_REMOVED) printk(KERN_WARNING "nv_sata: " "Secondary device removed\n"); } } static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent) { struct pci_dev *pdev = to_pci_dev(probe_ent->dev); u8 intr_mask; nv_enable_adma_space(pdev); writeb(NV_INT_STATUS_HOTPLUG, probe_ent->mmio_base + NV_INT_STATUS_CK804); intr_mask = readb(probe_ent->mmio_base + NV_INT_ENABLE_CK804); intr_mask |= NV_INT_ENABLE_HOTPLUG; writeb(intr_mask, probe_ent->mmio_base + NV_INT_ENABLE_CK804); } static void nv_disable_hotplug_ck804(struct ata_host_set *host_set) { struct pci_dev *pdev = to_pci_dev(host_set->dev); u8 intr_mask; intr_mask = readb(host_set->mmio_base + NV_INT_ENABLE_CK804); intr_mask &= ~(NV_INT_ENABLE_HOTPLUG); writeb(intr_mask, host_set->mmio_base + NV_INT_ENABLE_CK804); nv_disable_adma_space(pdev); } static void nv_check_hotplug_ck804(struct ata_host_set *host_set) { u8 intr_status; intr_status = readb(host_set->mmio_base + NV_INT_STATUS_CK804); // Clear interrupt status. writeb(0xff, host_set->mmio_base + NV_INT_STATUS_CK804); if (intr_status & NV_INT_STATUS_HOTPLUG) { if (intr_status & NV_INT_STATUS_PDEV_ADDED) printk(KERN_WARNING "nv_sata: " "Primary device added\n"); if (intr_status & NV_INT_STATUS_PDEV_REMOVED) printk(KERN_WARNING "nv_sata: " "Primary device removed\n"); if (intr_status & NV_INT_STATUS_SDEV_ADDED) printk(KERN_WARNING "nv_sata: " "Secondary device added\n"); if (intr_status & NV_INT_STATUS_SDEV_REMOVED) printk(KERN_WARNING "nv_sata: " "Secondary device removed\n"); } } static void nv_enable_hotplug_adma(struct ata_probe_ent *probe_ent) { struct pci_dev *pdev = to_pci_dev(probe_ent->dev); unsigned int i; u16 tmp; nv_enable_adma_space(pdev); for (i = 0; i < probe_ent->n_ports; i++) { void __iomem *mmio = __nv_adma_ctl_block(probe_ent->mmio_base, i); writew(NV_ADMA_STAT_HOTPLUG | NV_ADMA_STAT_HOTUNPLUG, mmio + NV_ADMA_STAT); tmp = readw(mmio + NV_ADMA_CTL); writew(tmp | NV_ADMA_CTL_HOTPLUG_IEN, mmio + NV_ADMA_CTL); } } static void nv_disable_hotplug_adma(struct ata_host_set *host_set) { unsigned int i; u16 tmp; for (i = 0; i < host_set->n_ports; i++) { void __iomem *mmio = __nv_adma_ctl_block(host_set->mmio_base, i); tmp = readw(mmio + NV_ADMA_CTL); writew(tmp & ~NV_ADMA_CTL_HOTPLUG_IEN, mmio + NV_ADMA_CTL); } } static void nv_check_hotplug_adma(struct ata_host_set *host_set) { unsigned int i; u16 adma_status; for (i = 0; i < host_set->n_ports; i++) { void __iomem *mmio = __nv_adma_ctl_block(host_set->mmio_base, i); adma_status = readw(mmio + NV_ADMA_STAT); if (adma_status & NV_ADMA_STAT_HOTPLUG) { printk(KERN_WARNING "nv_sata: " "port %d device added\n", i); writew(NV_ADMA_STAT_HOTPLUG, mmio + NV_ADMA_STAT); } if (adma_status & NV_ADMA_STAT_HOTUNPLUG) { printk(KERN_WARNING "nv_sata: " "port %d device removed\n", i); writew(NV_ADMA_STAT_HOTUNPLUG, mmio + NV_ADMA_STAT); } } } static int __init nv_init(void) { return pci_module_init(&nv_pci_driver); } static void __exit nv_exit(void) { pci_unregister_driver(&nv_pci_driver); } module_init(nv_init); module_exit(nv_exit); #ifdef DEBUG static void nv_adma_dump_aprd(struct nv_adma_prd *aprd) { printk("%016llx %08x %02x %s %s %s\n", aprd->addr, aprd->len, aprd->flags, (aprd->flags & NV_APRD_WRITE) ? "WRITE" : " ", (aprd->flags & NV_APRD_END) ? "END" : " ", (aprd->flags & NV_APRD_CONT) ? "CONT" : " "); } static void nv_adma_dump_iomem(void __iomem *m, int len) { int i, j; for (i = 0; i < len/16; i++) { printk(KERN_WARNING "%02x: ", 16*i); for (j = 0; j < 16; j++) { printk("%02x%s", (u32)readb(m + 16*i + j), (j == 7) ? "-" : " "); } printk("\n"); } } static void nv_adma_dump_cpb_tf(u16 tf) { printk("0x%04x %s %s %s 0x%02x 0x%02x\n", tf, (tf & CMDEND) ? "END" : " ", (tf & WNB) ? "WNB" : " ", (tf & IGN) ? "IGN" : " ", ((tf >> 8) & 0x1f), (tf & 0xff)); } static void nv_adma_dump_port(struct ata_port *ap) { void __iomem *mmio = nv_adma_ctl_block(ap); nv_adma_dump_iomem(mmio, NV_ADMA_PORT_SIZE); } static void nv_adma_dump_cpb(struct nv_adma_cpb *cpb) { int i; printk("resp_flags: 0x%02x\n", cpb->resp_flags); printk("ctl_flags: 0x%02x\n", cpb->ctl_flags); printk("len: 0x%02x\n", cpb->len); printk("tag: 0x%02x\n", cpb->tag); printk("next_cpb_idx: 0x%02x\n", cpb->next_cpb_idx); printk("tf:\n"); for (i=0; i<12; i++) { nv_adma_dump_cpb_tf(cpb->tf[i]); } printk("aprd:\n"); for (i=0; i<5; i++) { nv_adma_dump_aprd(&cpb->aprd[i]); } printk("next_aprd: 0x%016llx\n", cpb->next_aprd); } #endif