📄 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. * */#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 <linux/libata.h>#define DRV_NAME "sata_nv"#define DRV_VERSION "2.1-Driver Package V1.24"enum { 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,};//sgpio// Sgpio defines// SGPIO state defines#define NV_SGPIO_STATE_RESET 0#define NV_SGPIO_STATE_OPERATIONAL 1#define NV_SGPIO_STATE_ERROR 2// SGPIO command opcodes#define NV_SGPIO_CMD_RESET 0#define NV_SGPIO_CMD_READ_PARAMS 1#define NV_SGPIO_CMD_READ_DATA 2#define NV_SGPIO_CMD_WRITE_DATA 3// SGPIO command status defines#define NV_SGPIO_CMD_OK 0#define NV_SGPIO_CMD_ACTIVE 1#define NV_SGPIO_CMD_ERR 2#define NV_SGPIO_UPDATE_TICK 90#define NV_SGPIO_MIN_UPDATE_DELTA 33#define NV_CNTRLR_SHARE_INIT 2#define NV_SGPIO_MAX_ACTIVITY_ON 20#define NV_SGPIO_MIN_FORCE_OFF 5#define NV_SGPIO_PCI_CSR_OFFSET 0x58#define NV_SGPIO_PCI_CB_OFFSET 0x5C#define NV_SGPIO_DFLT_CB_SIZE 256#define NV_ON 1#define NV_OFF 0#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;};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 *regs);static irqreturn_t nv_nf2_interrupt(int irq, void *dev_instance, struct pt_regs *regs);static irqreturn_t nv_ck804_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 *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);enum nv_host_type{ GENERIC, NFORCE2, NFORCE3 = NFORCE2, /* NF2 == NF3 as far as sata_nv is concerned */ CK804};static const 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, CK804 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, { PCI_VENDOR_ID_NVIDIA, 0x045c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, { PCI_VENDOR_ID_NVIDIA, 0x045d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, { PCI_VENDOR_ID_NVIDIA, 0x045e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, { PCI_VENDOR_ID_NVIDIA, 0x045f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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 }, { 0, } /* 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 = { .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 const struct ata_port_operations nv_generic_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, .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 = nv_qc_issue, .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .data_xfer = ata_pio_data_xfer, .irq_handler = nv_generic_interrupt, .irq_clear = ata_bmdma_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 const struct ata_port_operations nv_nf2_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, .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 = nv_qc_issue, .freeze = nv_nf2_freeze, .thaw = nv_nf2_thaw, .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .data_xfer = ata_pio_data_xfer, .irq_handler = nv_nf2_interrupt, .irq_clear = ata_bmdma_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 const struct ata_port_operations nv_ck804_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, .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 = nv_qc_issue, .freeze = nv_ck804_freeze, .thaw = nv_ck804_thaw, .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .data_xfer = ata_pio_data_xfer, .irq_handler = nv_ck804_interrupt, .irq_clear = ata_bmdma_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_ck804_host_stop,};static struct ata_port_info nv_port_info[] = { /* generic */ { .sht = &nv_sht, .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, .pio_mask = NV_PIO_MASK, .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, .port_ops = &nv_generic_ops, }, /* nforce2/3 */ { .sht = &nv_sht, .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, .pio_mask = NV_PIO_MASK, .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, .port_ops = &nv_nf2_ops, }, /* ck804 */ { .sht = &nv_sht, .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, .pio_mask = NV_PIO_MASK, .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, .port_ops = &nv_ck804_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 irqreturn_t nv_generic_interrupt(int irq, void *dev_instance, struct pt_regs *regs){ struct ata_host *host = dev_instance; unsigned int i; unsigned int handled = 0; unsigned long flags; spin_lock_irqsave(&host->lock, flags); for (i = 0; i < host->n_ports; i++) { struct ata_port *ap; ap = host->ports[i]; if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) handled += ata_host_intr(ap, qc); else // No request pending? Clear interrupt status // anyway, in case there's one pending. ap->ops->check_status(ap); } } spin_unlock_irqrestore(&host->lock, flags); return IRQ_RETVAL(handled);}static int nv_host_intr(struct ata_port *ap, u8 irq_stat){ struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag); int handled; /* freeze if hotplugged */ if (unlikely(irq_stat & (NV_INT_ADDED | NV_INT_REMOVED))) { ata_port_freeze(ap); return 1; } /* bail out if not our interrupt */ if (!(irq_stat & NV_INT_DEV)) return 0; /* DEV interrupt w/ no active qc? */ if (unlikely(!qc || (qc->tf.flags & ATA_TFLAG_POLLING))) { ata_check_status(ap); return 1; } /* handle interrupt */ handled = ata_host_intr(ap, qc); if (unlikely(!handled)) { /* spurious, clear it */ ata_check_status(ap); } return 1;}static irqreturn_t nv_do_interrupt(struct ata_host *host, u8 irq_stat){ int i, handled = 0; for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -