📄 sata_nv.c
字号:
/* * 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. * * CK804/MCP04 controllers support an alternate programming interface * similar to the ADMA specification (with some modifications). * This allows the use of NCQ. Non-DMA-mapped ATA commands are still * sent through the legacy interface. * */#include <linux/kernel.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_device.h>#include <linux/libata.h>#define DRV_NAME "sata_nv"#define DRV_VERSION "3.5"#define NV_ADMA_DMA_BOUNDARY 0xffffffffULenum { NV_MMIO_BAR = 5, NV_PORTS = 2, NV_PIO_MASK = 0x1f, NV_MWDMA_MASK = 0x07, NV_UDMA_MASK = 0x7f, NV_PORT0_SCR_REG_OFFSET = 0x00, NV_PORT1_SCR_REG_OFFSET = 0x40, /* INT_STATUS/ENABLE */ NV_INT_STATUS = 0x10, NV_INT_ENABLE = 0x11, NV_INT_STATUS_CK804 = 0x440, NV_INT_ENABLE_CK804 = 0x441, /* INT_STATUS/ENABLE bits */ NV_INT_DEV = 0x01, NV_INT_PM = 0x02, NV_INT_ADDED = 0x04, NV_INT_REMOVED = 0x08, NV_INT_PORT_SHIFT = 4, /* each port occupies 4 bits */ NV_INT_ALL = 0x0f, NV_INT_MASK = NV_INT_DEV | NV_INT_ADDED | NV_INT_REMOVED, /* INT_CONFIG */ NV_INT_CONFIG = 0x12, NV_INT_CONFIG_METHD = 0x01, // 0 = INT, 1 = SMI // For PCI config register 20 NV_MCP_SATA_CFG_20 = 0x50, NV_MCP_SATA_CFG_20_SATA_SPACE_EN = 0x04, NV_MCP_SATA_CFG_20_PORT0_EN = (1 << 17), NV_MCP_SATA_CFG_20_PORT1_EN = (1 << 16), NV_MCP_SATA_CFG_20_PORT0_PWB_EN = (1 << 14), NV_MCP_SATA_CFG_20_PORT1_PWB_EN = (1 << 12), NV_ADMA_MAX_CPBS = 32, NV_ADMA_CPB_SZ = 128, NV_ADMA_APRD_SZ = 16, NV_ADMA_SGTBL_LEN = (1024 - NV_ADMA_CPB_SZ) / NV_ADMA_APRD_SZ, NV_ADMA_SGTBL_TOTAL_LEN = NV_ADMA_SGTBL_LEN + 5, NV_ADMA_SGTBL_SZ = NV_ADMA_SGTBL_LEN * NV_ADMA_APRD_SZ, NV_ADMA_PORT_PRIV_DMA_SZ = NV_ADMA_MAX_CPBS * (NV_ADMA_CPB_SZ + NV_ADMA_SGTBL_SZ), /* BAR5 offset to ADMA general registers */ NV_ADMA_GEN = 0x400, NV_ADMA_GEN_CTL = 0x00, NV_ADMA_NOTIFIER_CLEAR = 0x30, /* BAR5 offset to ADMA ports */ NV_ADMA_PORT = 0x480, /* size of ADMA port register space */ NV_ADMA_PORT_SIZE = 0x100, /* ADMA port registers */ NV_ADMA_CTL = 0x40, NV_ADMA_CPB_COUNT = 0x42, NV_ADMA_NEXT_CPB_IDX = 0x43, NV_ADMA_STAT = 0x44, NV_ADMA_CPB_BASE_LOW = 0x48, NV_ADMA_CPB_BASE_HIGH = 0x4C, NV_ADMA_APPEND = 0x50, NV_ADMA_NOTIFIER = 0x68, NV_ADMA_NOTIFIER_ERROR = 0x6C, /* NV_ADMA_CTL register bits */ NV_ADMA_CTL_HOTPLUG_IEN = (1 << 0), NV_ADMA_CTL_CHANNEL_RESET = (1 << 5), NV_ADMA_CTL_GO = (1 << 7), NV_ADMA_CTL_AIEN = (1 << 8), NV_ADMA_CTL_READ_NON_COHERENT = (1 << 11), NV_ADMA_CTL_WRITE_NON_COHERENT = (1 << 12), /* CPB response flag bits */ NV_CPB_RESP_DONE = (1 << 0), NV_CPB_RESP_ATA_ERR = (1 << 3), NV_CPB_RESP_CMD_ERR = (1 << 4), NV_CPB_RESP_CPB_ERR = (1 << 7), /* CPB control flag bits */ NV_CPB_CTL_CPB_VALID = (1 << 0), NV_CPB_CTL_QUEUE = (1 << 1), NV_CPB_CTL_APRD_VALID = (1 << 2), NV_CPB_CTL_IEN = (1 << 3), NV_CPB_CTL_FPDMA = (1 << 4), /* APRD flags */ NV_APRD_WRITE = (1 << 1), NV_APRD_END = (1 << 2), NV_APRD_CONT = (1 << 3), /* NV_ADMA_STAT flags */ NV_ADMA_STAT_TIMEOUT = (1 << 0), NV_ADMA_STAT_HOTUNPLUG = (1 << 1), NV_ADMA_STAT_HOTPLUG = (1 << 2), NV_ADMA_STAT_CPBERR = (1 << 4), NV_ADMA_STAT_SERROR = (1 << 5), NV_ADMA_STAT_CMD_COMPLETE = (1 << 6), NV_ADMA_STAT_IDLE = (1 << 8), NV_ADMA_STAT_LEGACY = (1 << 9), NV_ADMA_STAT_STOPPED = (1 << 10), NV_ADMA_STAT_DONE = (1 << 12), NV_ADMA_STAT_ERR = NV_ADMA_STAT_CPBERR | NV_ADMA_STAT_TIMEOUT, /* port flags */ NV_ADMA_PORT_REGISTER_MODE = (1 << 0), NV_ADMA_ATAPI_SETUP_COMPLETE = (1 << 1), /* MCP55 reg offset */ NV_CTL_MCP55 = 0x400, NV_INT_STATUS_MCP55 = 0x440, NV_INT_ENABLE_MCP55 = 0x444, NV_NCQ_REG_MCP55 = 0x448, /* MCP55 */ NV_INT_ALL_MCP55 = 0xffff, NV_INT_PORT_SHIFT_MCP55 = 16, /* each port occupies 16 bits */ NV_INT_MASK_MCP55 = NV_INT_ALL_MCP55 & 0xfffd, /* SWNCQ ENABLE BITS*/ NV_CTL_PRI_SWNCQ = 0x02, NV_CTL_SEC_SWNCQ = 0x04, /* SW NCQ status bits*/ NV_SWNCQ_IRQ_DEV = (1 << 0), NV_SWNCQ_IRQ_PM = (1 << 1), NV_SWNCQ_IRQ_ADDED = (1 << 2), NV_SWNCQ_IRQ_REMOVED = (1 << 3), NV_SWNCQ_IRQ_BACKOUT = (1 << 4), NV_SWNCQ_IRQ_SDBFIS = (1 << 5), NV_SWNCQ_IRQ_DHREGFIS = (1 << 6), NV_SWNCQ_IRQ_DMASETUP = (1 << 7), NV_SWNCQ_IRQ_HOTPLUG = NV_SWNCQ_IRQ_ADDED | NV_SWNCQ_IRQ_REMOVED,};/* ADMA Physical Region Descriptor - one SG segment */struct nv_adma_prd { __le64 addr; __le32 len; u8 flags; u8 packet_len; __le16 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)),};/* ADMA Command Parameter Block The first 5 SG segments are stored inside the Command Parameter Block itself. If there are more than 5 segments the remainder are stored in a separate memory area indicated by next_aprd. */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 */ __le16 reserved2; /* 6-7 */ __le16 tf[12]; /* 8-31 */ struct nv_adma_prd aprd[5]; /* 32-111 */ __le64 next_aprd; /* 112-119 */ __le64 reserved3; /* 120-127 */};struct nv_adma_port_priv { struct nv_adma_cpb *cpb; dma_addr_t cpb_dma; struct nv_adma_prd *aprd; dma_addr_t aprd_dma; void __iomem *ctl_block; void __iomem *gen_block; void __iomem *notifier_clear_block; u8 flags; int last_issue_ncq;};struct nv_host_priv { unsigned long type;};struct defer_queue { u32 defer_bits; unsigned int head; unsigned int tail; unsigned int tag[ATA_MAX_QUEUE];};enum ncq_saw_flag_list { ncq_saw_d2h = (1U << 0), ncq_saw_dmas = (1U << 1), ncq_saw_sdb = (1U << 2), ncq_saw_backout = (1U << 3),};struct nv_swncq_port_priv { struct ata_prd *prd; /* our SG list */ dma_addr_t prd_dma; /* and its DMA mapping */ void __iomem *sactive_block; void __iomem *irq_block; void __iomem *tag_block; u32 qc_active; unsigned int last_issue_tag; /* fifo circular queue to store deferral command */ struct defer_queue defer_queue; /* for NCQ interrupt analysis */ u32 dhfis_bits; u32 dmafis_bits; u32 sdbfis_bits; unsigned int ncq_flags;};#define NV_ADMA_CHECK_INTR(GCTL, PORT) ((GCTL) & (1 << (19 + (12 * (PORT)))))static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);#ifdef CONFIG_PMstatic int nv_pci_device_resume(struct pci_dev *pdev);#endifstatic void nv_ck804_host_stop(struct ata_host *host);static irqreturn_t nv_generic_interrupt(int irq, void *dev_instance);static irqreturn_t nv_nf2_interrupt(int irq, void *dev_instance);static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance);static int nv_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val);static int nv_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val);static void nv_nf2_freeze(struct ata_port *ap);static void nv_nf2_thaw(struct ata_port *ap);static void nv_ck804_freeze(struct ata_port *ap);static void nv_ck804_thaw(struct ata_port *ap);static void nv_error_handler(struct ata_port *ap);static int nv_adma_slave_config(struct scsi_device *sdev);static int nv_adma_check_atapi_dma(struct ata_queued_cmd *qc);static void nv_adma_qc_prep(struct ata_queued_cmd *qc);static unsigned int nv_adma_qc_issue(struct ata_queued_cmd *qc);static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance);static void nv_adma_irq_clear(struct ata_port *ap);static int nv_adma_port_start(struct ata_port *ap);static void nv_adma_port_stop(struct ata_port *ap);#ifdef CONFIG_PMstatic int nv_adma_port_suspend(struct ata_port *ap, pm_message_t mesg);static int nv_adma_port_resume(struct ata_port *ap);#endifstatic void nv_adma_freeze(struct ata_port *ap);static void nv_adma_thaw(struct ata_port *ap);static void nv_adma_error_handler(struct ata_port *ap);static void nv_adma_host_stop(struct ata_host *host);static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc);static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf);static void nv_mcp55_thaw(struct ata_port *ap);static void nv_mcp55_freeze(struct ata_port *ap);static void nv_swncq_error_handler(struct ata_port *ap);static int nv_swncq_slave_config(struct scsi_device *sdev);static int nv_swncq_port_start(struct ata_port *ap);static void nv_swncq_qc_prep(struct ata_queued_cmd *qc);static void nv_swncq_fill_sg(struct ata_queued_cmd *qc);static unsigned int nv_swncq_qc_issue(struct ata_queued_cmd *qc);static void nv_swncq_irq_clear(struct ata_port *ap, u16 fis);static irqreturn_t nv_swncq_interrupt(int irq, void *dev_instance);#ifdef CONFIG_PMstatic int nv_swncq_port_suspend(struct ata_port *ap, pm_message_t mesg);static int nv_swncq_port_resume(struct ata_port *ap);#endifenum nv_host_type{ GENERIC, NFORCE2, NFORCE3 = NFORCE2, /* NF2 == NF3 as far as sata_nv is concerned */ CK804, ADMA, SWNCQ,};static const struct pci_device_id nv_pci_tbl[] = { { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA), NFORCE2 }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA), NFORCE3 }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2), NFORCE3 }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA), CK804 }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2), CK804 }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA), CK804 }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2), CK804 }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA), SWNCQ }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2), SWNCQ }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA), SWNCQ }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2), SWNCQ }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA), GENERIC }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2), GENERIC }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3), GENERIC }, { } /* terminate list */};static struct pci_driver nv_pci_driver = { .name = DRV_NAME, .id_table = nv_pci_tbl, .probe = nv_init_one,#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = nv_pci_device_resume,#endif .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, .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,};static struct scsi_host_template nv_adma_sht = { .module = THIS_MODULE, .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .queuecommand = ata_scsi_queuecmd, .change_queue_depth = ata_scsi_change_queue_depth, .can_queue = NV_ADMA_MAX_CPBS, .this_id = ATA_SHT_THIS_ID, .sg_tablesize = NV_ADMA_SGTBL_TOTAL_LEN, .cmd_per_lun = ATA_SHT_CMD_PER_LUN, .emulated = ATA_SHT_EMULATED, .use_clustering = ATA_SHT_USE_CLUSTERING, .proc_name = DRV_NAME, .dma_boundary = NV_ADMA_DMA_BOUNDARY, .slave_configure = nv_adma_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param,};static struct scsi_host_template nv_swncq_sht = { .module = THIS_MODULE, .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .queuecommand = ata_scsi_queuecmd, .change_queue_depth = ata_scsi_change_queue_depth, .can_queue = ATA_MAX_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 = nv_swncq_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param,};static const struct ata_port_operations nv_generic_ops = { .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, .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, .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .data_xfer = ata_data_xfer, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .scr_read = nv_scr_read, .scr_write = nv_scr_write, .port_start = ata_port_start,};static const struct ata_port_operations nv_nf2_ops = { .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, .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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -