📄 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.2-Driver Package V1.24"#define NV_ADMA_DMA_BOUNDARY 0xffffffffULenum { 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), //sgpio // Sgpio defines // SGPIO state defines NV_SGPIO_STATE_RESET = 0, NV_SGPIO_STATE_OPERATIONAL = 1, NV_SGPIO_STATE_ERROR = 2, // SGPIO command opcodes NV_SGPIO_CMD_RESET = 0, NV_SGPIO_CMD_READ_PARAMS = 1, NV_SGPIO_CMD_READ_DATA = 2, NV_SGPIO_CMD_WRITE_DATA = 3, // SGPIO command status defines NV_SGPIO_CMD_OK = 0, NV_SGPIO_CMD_ACTIVE = 1, NV_SGPIO_CMD_ERR = 2, NV_SGPIO_UPDATE_TICK = 90, NV_SGPIO_MIN_UPDATE_DELTA = 33, NV_CNTRLR_SHARE_INIT = 2, NV_SGPIO_MAX_ACTIVITY_ON = 20, NV_SGPIO_MIN_FORCE_OFF = 5, NV_SGPIO_PCI_CSR_OFFSET = 0x58, NV_SGPIO_PCI_CB_OFFSET = 0x5C, NV_SGPIO_DFLT_CB_SIZE = 256, NV_ON = 1, NV_OFF = 0,};/* 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; u8 flags;};#ifndef bool#define bool u8#endif#define BF_EXTRACT(v, off, bc) \ ((((u8)(v)) >> (off)) & ((1 << (bc)) - 1))#define BF_INS(v, ins, off, bc) \ (((v) & ~((((1 << (bc)) - 1)) << (off))) | \ (((u8)(ins)) << (off)))#define BF_EXTRACT_U32(v, off, bc) \ ((((u32)(v)) >> (off)) & ((1 << (bc)) - 1))#define BF_INS_U32(v, ins, off, bc) \ (((v) & ~((((1 << (bc)) - 1)) << (off))) | \ (((u32)(ins)) << (off)))#define GET_SGPIO_STATUS(v) BF_EXTRACT(v, 0, 2)#define GET_CMD_STATUS(v) BF_EXTRACT(v, 3, 2)#define GET_CMD(v) BF_EXTRACT(v, 5, 3)#define SET_CMD(v, cmd) BF_INS(v, cmd, 5, 3) #define GET_ENABLE(v) BF_EXTRACT_U32(v, 23, 1)#define SET_ENABLE(v) BF_INS_U32(v, 1, 23, 1)// Needs to have a u8 bit-field insert.#define GET_ACTIVITY(v) BF_EXTRACT(v, 5, 3)#define SET_ACTIVITY(v, on_off) BF_INS(v, on_off, 5, 3)union nv_sgpio_nvcr { struct { u8 init_cnt; u8 cb_size; u8 cbver; u8 rsvd; } bit; u32 all;};union nv_sgpio_tx { u8 tx_port[4]; u32 all;};struct nv_sgpio_cb { u64 scratch_space; union nv_sgpio_nvcr nvcr; u32 cr0; u32 rsvd[4]; union nv_sgpio_tx tx[2];};struct nv_sgpio_host_share{ spinlock_t *plock; unsigned long *ptstamp;};struct nv_sgpio_host_flags{ u8 sgpio_enabled:1; u8 need_update:1; u8 rsvd:6;}; struct nv_host_sgpio{ struct nv_sgpio_host_flags flags; u8 *pcsr; struct nv_sgpio_cb *pcb; struct nv_sgpio_host_share share; struct timer_list sgpio_timer;};struct nv_sgpio_port_flags{ u8 last_state:1; u8 recent_activity:1; u8 rsvd:6;};struct nv_sgpio_led { struct nv_sgpio_port_flags flags; u8 force_off; u8 last_cons_active;};struct nv_port_sgpio{ struct nv_sgpio_led activity;};#define NV_ADMA_CHECK_INTR(GCTL, PORT) ((GCTL) & ( 1 << (19 + (12 * (PORT)))))static spinlock_t nv_sgpio_lock;static unsigned long nv_sgpio_tstamp;static inline void nv_sgpio_set_csr(u8 csr, unsigned long pcsr){ outb(csr, pcsr);}static inline u8 nv_sgpio_get_csr(unsigned long pcsr){ return inb(pcsr);}static inline u8 nv_sgpio_get_func(struct ata_host *host){ u8 devfn = (to_pci_dev(host->dev))->devfn; return (PCI_FUNC(devfn));}static inline u8 nv_sgpio_tx_host_offset(struct ata_host *host){ return (nv_sgpio_get_func(host)/NV_CNTRLR_SHARE_INIT);}static inline u8 nv_sgpio_calc_tx_offset(u8 cntrlr, u8 channel){ return (sizeof(union nv_sgpio_tx) - (NV_CNTRLR_SHARE_INIT * (cntrlr % NV_CNTRLR_SHARE_INIT)) - channel - 1);}static inline u8 nv_sgpio_tx_port_offset(struct ata_port *ap){ u8 cntrlr = nv_sgpio_get_func(ap->host); return (nv_sgpio_calc_tx_offset(cntrlr, ap->port_no));}static inline bool nv_sgpio_capable(const struct pci_device_id *ent){ if (ent->device == PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2) return 1; else return 0;}static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);static void nv_ck804_host_stop(struct ata_host *host);static irqreturn_t nv_generic_interrupt(int irq, void *dev_instance, struct pt_regs *pt_regs);static irqreturn_t nv_nf2_interrupt(int irq, void *dev_instance, struct pt_regs *pt_regs);static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance, struct pt_regs *pt_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 *host);static int nv_port_start(struct ata_port *ap);static void nv_port_stop(struct ata_port *ap);static unsigned int nv_qc_issue(struct ata_queued_cmd *qc);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, struct pt_regs *pt_regs);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);static void nv_adma_error_handler(struct ata_port *ap);static void nv_adma_host_stop(struct ata_host *host);static void nv_adma_bmdma_setup(struct ata_queued_cmd *qc);static void nv_adma_bmdma_start(struct ata_queued_cmd *qc);static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc);static u8 nv_adma_bmdma_status(struct ata_port *ap);enum nv_host_type{ GENERIC, NFORCE2, NFORCE3 = NFORCE2, /* NF2 == NF3 as far as sata_nv is concerned */ CK804, ADMA};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), GENERIC }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2), GENERIC }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA), GENERIC }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2), GENERIC }, { 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 }, { PCI_VDEVICE(NVIDIA, 0x045c), GENERIC }, { PCI_VDEVICE(NVIDIA, 0x045d), GENERIC }, { PCI_VDEVICE(NVIDIA, 0x045e), GENERIC }, { PCI_VDEVICE(NVIDIA, 0x045f), GENERIC }, { 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 }, { } /* terminate list */};struct nv_host{ unsigned long host_flags; struct nv_host_sgpio host_sgpio;};struct nv_port{ struct nv_port_sgpio port_sgpio;};// SGPIO function prototypesstatic void nv_sgpio_init(struct pci_dev *pdev, struct nv_host *phost);static void nv_sgpio_reset(u8 *pcsr);static void nv_sgpio_set_timer(struct timer_list *ptimer, unsigned int timeout_msec);static void nv_sgpio_timer_handler(unsigned long ptr);static void nv_sgpio_host_cleanup(struct nv_host *host);static bool nv_sgpio_update_led(struct nv_sgpio_led *led, bool *on_off);static void nv_sgpio_clear_all_leds(struct ata_port *ap);static bool nv_sgpio_send_cmd(struct nv_host *host, u8 cmd);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 = { .eh_strategy_handler = ata_scsi_error, .eh_timed_out = ata_scsi_timed_out, .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, .dump_sanity_check = ata_scsi_dump_sanity_check, .dump_quiesce = ata_scsi_dump_quiesce, .dump_poll = ata_scsi_dump_poll,};static struct scsi_host_template nv_adma_sht = { .eh_strategy_handler = ata_scsi_error, .eh_timed_out = ata_scsi_timed_out, .module = THIS_MODULE, .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .queuecommand = ata_scsi_queuecmd, .can_queue = NV_ADMA_MAX_CPBS, .this_id = ATA_SHT_THIS_ID,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -