⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ahci.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  ahci.c - AHCI SATA support * *  Maintained by:  Jeff Garzik <jgarzik@pobox.com> *    		    Please ALWAYS copy linux-ide@vger.kernel.org *		    on emails. * *  Copyright 2004-2005 Red Hat, Inc. * * *  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.* * * AHCI hardware documentation: * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf * */#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/dma-mapping.h>#include <linux/device.h>#include <linux/dmi.h>#include <scsi/scsi_host.h>#include <scsi/scsi_cmnd.h>#include <linux/libata.h>#define DRV_NAME	"ahci"#define DRV_VERSION	"3.0"static int ahci_enable_alpm(struct ata_port *ap,		enum link_pm policy);static void ahci_disable_alpm(struct ata_port *ap);enum {	AHCI_PCI_BAR		= 5,	AHCI_MAX_PORTS		= 32,	AHCI_MAX_SG		= 168, /* hardware max is 64K */	AHCI_DMA_BOUNDARY	= 0xffffffff,	AHCI_USE_CLUSTERING	= 1,	AHCI_MAX_CMDS		= 32,	AHCI_CMD_SZ		= 32,	AHCI_CMD_SLOT_SZ	= AHCI_MAX_CMDS * AHCI_CMD_SZ,	AHCI_RX_FIS_SZ		= 256,	AHCI_CMD_TBL_CDB	= 0x40,	AHCI_CMD_TBL_HDR_SZ	= 0x80,	AHCI_CMD_TBL_SZ		= AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16),	AHCI_CMD_TBL_AR_SZ	= AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS,	AHCI_PORT_PRIV_DMA_SZ	= AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ +				  AHCI_RX_FIS_SZ,	AHCI_IRQ_ON_SG		= (1 << 31),	AHCI_CMD_ATAPI		= (1 << 5),	AHCI_CMD_WRITE		= (1 << 6),	AHCI_CMD_PREFETCH	= (1 << 7),	AHCI_CMD_RESET		= (1 << 8),	AHCI_CMD_CLR_BUSY	= (1 << 10),	RX_FIS_D2H_REG		= 0x40,	/* offset of D2H Register FIS data */	RX_FIS_SDB		= 0x58, /* offset of SDB FIS data */	RX_FIS_UNK		= 0x60, /* offset of Unknown FIS data */	board_ahci		= 0,	board_ahci_vt8251	= 1,	board_ahci_ign_iferr	= 2,	board_ahci_sb600	= 3,	board_ahci_mv		= 4,	/* global controller registers */	HOST_CAP		= 0x00, /* host capabilities */	HOST_CTL		= 0x04, /* global host control */	HOST_IRQ_STAT		= 0x08, /* interrupt status */	HOST_PORTS_IMPL		= 0x0c, /* bitmap of implemented ports */	HOST_VERSION		= 0x10, /* AHCI spec. version compliancy */	/* HOST_CTL bits */	HOST_RESET		= (1 << 0),  /* reset controller; self-clear */	HOST_IRQ_EN		= (1 << 1),  /* global IRQ enable */	HOST_AHCI_EN		= (1 << 31), /* AHCI enabled */	/* HOST_CAP bits */	HOST_CAP_SSC		= (1 << 14), /* Slumber capable */	HOST_CAP_PMP		= (1 << 17), /* Port Multiplier support */	HOST_CAP_CLO		= (1 << 24), /* Command List Override support */	HOST_CAP_ALPM		= (1 << 26), /* Aggressive Link PM support */	HOST_CAP_SSS		= (1 << 27), /* Staggered Spin-up */	HOST_CAP_SNTF		= (1 << 29), /* SNotification register */	HOST_CAP_NCQ		= (1 << 30), /* Native Command Queueing */	HOST_CAP_64		= (1 << 31), /* PCI DAC (64-bit DMA) support */	/* registers for each SATA port */	PORT_LST_ADDR		= 0x00, /* command list DMA addr */	PORT_LST_ADDR_HI	= 0x04, /* command list DMA addr hi */	PORT_FIS_ADDR		= 0x08, /* FIS rx buf addr */	PORT_FIS_ADDR_HI	= 0x0c, /* FIS rx buf addr hi */	PORT_IRQ_STAT		= 0x10, /* interrupt status */	PORT_IRQ_MASK		= 0x14, /* interrupt enable/disable mask */	PORT_CMD		= 0x18, /* port command */	PORT_TFDATA		= 0x20,	/* taskfile data */	PORT_SIG		= 0x24,	/* device TF signature */	PORT_CMD_ISSUE		= 0x38, /* command issue */	PORT_SCR_STAT		= 0x28, /* SATA phy register: SStatus */	PORT_SCR_CTL		= 0x2c, /* SATA phy register: SControl */	PORT_SCR_ERR		= 0x30, /* SATA phy register: SError */	PORT_SCR_ACT		= 0x34, /* SATA phy register: SActive */	PORT_SCR_NTF		= 0x3c, /* SATA phy register: SNotification */	/* PORT_IRQ_{STAT,MASK} bits */	PORT_IRQ_COLD_PRES	= (1 << 31), /* cold presence detect */	PORT_IRQ_TF_ERR		= (1 << 30), /* task file error */	PORT_IRQ_HBUS_ERR	= (1 << 29), /* host bus fatal error */	PORT_IRQ_HBUS_DATA_ERR	= (1 << 28), /* host bus data error */	PORT_IRQ_IF_ERR		= (1 << 27), /* interface fatal error */	PORT_IRQ_IF_NONFATAL	= (1 << 26), /* interface non-fatal error */	PORT_IRQ_OVERFLOW	= (1 << 24), /* xfer exhausted available S/G */	PORT_IRQ_BAD_PMP	= (1 << 23), /* incorrect port multiplier */	PORT_IRQ_PHYRDY		= (1 << 22), /* PhyRdy changed */	PORT_IRQ_DEV_ILCK	= (1 << 7), /* device interlock */	PORT_IRQ_CONNECT	= (1 << 6), /* port connect change status */	PORT_IRQ_SG_DONE	= (1 << 5), /* descriptor processed */	PORT_IRQ_UNK_FIS	= (1 << 4), /* unknown FIS rx'd */	PORT_IRQ_SDB_FIS	= (1 << 3), /* Set Device Bits FIS rx'd */	PORT_IRQ_DMAS_FIS	= (1 << 2), /* DMA Setup FIS rx'd */	PORT_IRQ_PIOS_FIS	= (1 << 1), /* PIO Setup FIS rx'd */	PORT_IRQ_D2H_REG_FIS	= (1 << 0), /* D2H Register FIS rx'd */	PORT_IRQ_FREEZE		= PORT_IRQ_HBUS_ERR |				  PORT_IRQ_IF_ERR |				  PORT_IRQ_CONNECT |				  PORT_IRQ_PHYRDY |				  PORT_IRQ_UNK_FIS |				  PORT_IRQ_BAD_PMP,	PORT_IRQ_ERROR		= PORT_IRQ_FREEZE |				  PORT_IRQ_TF_ERR |				  PORT_IRQ_HBUS_DATA_ERR,	DEF_PORT_IRQ		= PORT_IRQ_ERROR | PORT_IRQ_SG_DONE |				  PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS |				  PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS,	/* PORT_CMD bits */	PORT_CMD_ASP		= (1 << 27), /* Aggressive Slumber/Partial */	PORT_CMD_ALPE		= (1 << 26), /* Aggressive Link PM enable */	PORT_CMD_ATAPI		= (1 << 24), /* Device is ATAPI */	PORT_CMD_PMP		= (1 << 17), /* PMP attached */	PORT_CMD_LIST_ON	= (1 << 15), /* cmd list DMA engine running */	PORT_CMD_FIS_ON		= (1 << 14), /* FIS DMA engine running */	PORT_CMD_FIS_RX		= (1 << 4), /* Enable FIS receive DMA engine */	PORT_CMD_CLO		= (1 << 3), /* Command list override */	PORT_CMD_POWER_ON	= (1 << 2), /* Power up device */	PORT_CMD_SPIN_UP	= (1 << 1), /* Spin up device */	PORT_CMD_START		= (1 << 0), /* Enable port DMA engine */	PORT_CMD_ICC_MASK	= (0xf << 28), /* i/f ICC state mask */	PORT_CMD_ICC_ACTIVE	= (0x1 << 28), /* Put i/f in active state */	PORT_CMD_ICC_PARTIAL	= (0x2 << 28), /* Put i/f in partial state */	PORT_CMD_ICC_SLUMBER	= (0x6 << 28), /* Put i/f in slumber state */	/* hpriv->flags bits */	AHCI_HFLAG_NO_NCQ		= (1 << 0),	AHCI_HFLAG_IGN_IRQ_IF_ERR	= (1 << 1), /* ignore IRQ_IF_ERR */	AHCI_HFLAG_IGN_SERR_INTERNAL	= (1 << 2), /* ignore SERR_INTERNAL */	AHCI_HFLAG_32BIT_ONLY		= (1 << 3), /* force 32bit */	AHCI_HFLAG_MV_PATA		= (1 << 4), /* PATA port */	AHCI_HFLAG_NO_MSI		= (1 << 5), /* no PCI MSI */	AHCI_HFLAG_NO_PMP		= (1 << 6), /* no PMP */	AHCI_HFLAG_NO_HOTPLUG		= (1 << 7), /* ignore PxSERR.DIAG.N */	/* ap->flags bits */	AHCI_FLAG_COMMON		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |					  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |					  ATA_FLAG_ACPI_SATA | ATA_FLAG_AN |					  ATA_FLAG_IPM,	AHCI_LFLAG_COMMON		= ATA_LFLAG_SKIP_D2H_BSY,	ICH_MAP				= 0x90, /* ICH MAP register */};struct ahci_cmd_hdr {	u32			opts;	u32			status;	u32			tbl_addr;	u32			tbl_addr_hi;	u32			reserved[4];};struct ahci_sg {	u32			addr;	u32			addr_hi;	u32			reserved;	u32			flags_size;};struct ahci_host_priv {	unsigned int		flags;		/* AHCI_HFLAG_* */	u32			cap;		/* cap to use */	u32			port_map;	/* port map to use */	u32			saved_cap;	/* saved initial cap */	u32			saved_port_map;	/* saved initial port_map */};struct ahci_port_priv {	struct ata_link		*active_link;	struct ahci_cmd_hdr	*cmd_slot;	dma_addr_t		cmd_slot_dma;	void			*cmd_tbl;	dma_addr_t		cmd_tbl_dma;	void			*rx_fis;	dma_addr_t		rx_fis_dma;	/* for NCQ spurious interrupt analysis */	unsigned int		ncq_saw_d2h:1;	unsigned int		ncq_saw_dmas:1;	unsigned int		ncq_saw_sdb:1;	u32 			intr_mask;	/* interrupts to enable */};static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val);static int ahci_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val);static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc);static void ahci_irq_clear(struct ata_port *ap);static int ahci_port_start(struct ata_port *ap);static void ahci_port_stop(struct ata_port *ap);static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf);static void ahci_qc_prep(struct ata_queued_cmd *qc);static u8 ahci_check_status(struct ata_port *ap);static void ahci_freeze(struct ata_port *ap);static void ahci_thaw(struct ata_port *ap);static void ahci_pmp_attach(struct ata_port *ap);static void ahci_pmp_detach(struct ata_port *ap);static void ahci_error_handler(struct ata_port *ap);static void ahci_vt8251_error_handler(struct ata_port *ap);static void ahci_p5wdh_error_handler(struct ata_port *ap);static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);static int ahci_port_resume(struct ata_port *ap);static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl);static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,			       u32 opts);#ifdef CONFIG_PMstatic int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg);static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);static int ahci_pci_device_resume(struct pci_dev *pdev);#endifstatic struct class_device_attribute *ahci_shost_attrs[] = {	&class_device_attr_link_power_management_policy,	NULL};static struct scsi_host_template ahci_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		= AHCI_MAX_CMDS - 1,	.this_id		= ATA_SHT_THIS_ID,	.sg_tablesize		= AHCI_MAX_SG,	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,	.emulated		= ATA_SHT_EMULATED,	.use_clustering		= AHCI_USE_CLUSTERING,	.proc_name		= DRV_NAME,	.dma_boundary		= AHCI_DMA_BOUNDARY,	.slave_configure	= ata_scsi_slave_config,	.slave_destroy		= ata_scsi_slave_destroy,	.bios_param		= ata_std_bios_param,	.shost_attrs		= ahci_shost_attrs,};static const struct ata_port_operations ahci_ops = {	.check_status		= ahci_check_status,	.check_altstatus	= ahci_check_status,	.dev_select		= ata_noop_dev_select,	.tf_read		= ahci_tf_read,	.qc_defer		= sata_pmp_qc_defer_cmd_switch,	.qc_prep		= ahci_qc_prep,	.qc_issue		= ahci_qc_issue,	.irq_clear		= ahci_irq_clear,	.scr_read		= ahci_scr_read,	.scr_write		= ahci_scr_write,	.freeze			= ahci_freeze,	.thaw			= ahci_thaw,	.error_handler		= ahci_error_handler,	.post_internal_cmd	= ahci_post_internal_cmd,	.pmp_attach		= ahci_pmp_attach,	.pmp_detach		= ahci_pmp_detach,#ifdef CONFIG_PM	.port_suspend		= ahci_port_suspend,	.port_resume		= ahci_port_resume,#endif	.enable_pm		= ahci_enable_alpm,	.disable_pm		= ahci_disable_alpm,	.port_start		= ahci_port_start,	.port_stop		= ahci_port_stop,};static const struct ata_port_operations ahci_vt8251_ops = {	.check_status		= ahci_check_status,	.check_altstatus	= ahci_check_status,	.dev_select		= ata_noop_dev_select,	.tf_read		= ahci_tf_read,	.qc_defer		= sata_pmp_qc_defer_cmd_switch,	.qc_prep		= ahci_qc_prep,	.qc_issue		= ahci_qc_issue,	.irq_clear		= ahci_irq_clear,	.scr_read		= ahci_scr_read,	.scr_write		= ahci_scr_write,	.freeze			= ahci_freeze,	.thaw			= ahci_thaw,	.error_handler		= ahci_vt8251_error_handler,	.post_internal_cmd	= ahci_post_internal_cmd,	.pmp_attach		= ahci_pmp_attach,	.pmp_detach		= ahci_pmp_detach,#ifdef CONFIG_PM	.port_suspend		= ahci_port_suspend,	.port_resume		= ahci_port_resume,#endif	.port_start		= ahci_port_start,	.port_stop		= ahci_port_stop,};static const struct ata_port_operations ahci_p5wdh_ops = {	.check_status		= ahci_check_status,	.check_altstatus	= ahci_check_status,	.dev_select		= ata_noop_dev_select,	.tf_read		= ahci_tf_read,	.qc_defer		= sata_pmp_qc_defer_cmd_switch,	.qc_prep		= ahci_qc_prep,	.qc_issue		= ahci_qc_issue,	.irq_clear		= ahci_irq_clear,	.scr_read		= ahci_scr_read,	.scr_write		= ahci_scr_write,	.freeze			= ahci_freeze,	.thaw			= ahci_thaw,	.error_handler		= ahci_p5wdh_error_handler,	.post_internal_cmd	= ahci_post_internal_cmd,	.pmp_attach		= ahci_pmp_attach,	.pmp_detach		= ahci_pmp_detach,#ifdef CONFIG_PM	.port_suspend		= ahci_port_suspend,	.port_resume		= ahci_port_resume,#endif	.port_start		= ahci_port_start,	.port_stop		= ahci_port_stop,};#define AHCI_HFLAGS(flags)	.private_data	= (void *)(flags)static const struct ata_port_info ahci_port_info[] = {	/* board_ahci */	{		.flags		= AHCI_FLAG_COMMON,		.link_flags	= AHCI_LFLAG_COMMON,		.pio_mask	= 0x1f, /* pio0-4 */		.udma_mask	= ATA_UDMA6,		.port_ops	= &ahci_ops,	},	/* board_ahci_vt8251 */	{		AHCI_HFLAGS	(AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP),		.flags		= AHCI_FLAG_COMMON,		.link_flags	= AHCI_LFLAG_COMMON | ATA_LFLAG_HRST_TO_RESUME,		.pio_mask	= 0x1f, /* pio0-4 */		.udma_mask	= ATA_UDMA6,		.port_ops	= &ahci_vt8251_ops,	},	/* board_ahci_ign_iferr */	{		AHCI_HFLAGS	(AHCI_HFLAG_IGN_IRQ_IF_ERR),		.flags		= AHCI_FLAG_COMMON,		.link_flags	= AHCI_LFLAG_COMMON,		.pio_mask	= 0x1f, /* pio0-4 */		.udma_mask	= ATA_UDMA6,		.port_ops	= &ahci_ops,	},	/* board_ahci_sb600 */	{		AHCI_HFLAGS	(AHCI_HFLAG_IGN_SERR_INTERNAL |				 AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_PMP),		.flags		= AHCI_FLAG_COMMON,		.link_flags	= AHCI_LFLAG_COMMON,		.pio_mask	= 0x1f, /* pio0-4 */		.udma_mask	= ATA_UDMA6,		.port_ops	= &ahci_ops,	},	/* board_ahci_mv */	{		AHCI_HFLAGS	(AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI |				 AHCI_HFLAG_MV_PATA),		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |				  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA,		.link_flags	= AHCI_LFLAG_COMMON,		.pio_mask	= 0x1f, /* pio0-4 */		.udma_mask	= ATA_UDMA6,		.port_ops	= &ahci_ops,	},};static const struct pci_device_id ahci_pci_tbl[] = {	/* Intel */	{ PCI_VDEVICE(INTEL, 0x2652), board_ahci }, /* ICH6 */	{ PCI_VDEVICE(INTEL, 0x2653), board_ahci }, /* ICH6M */	{ PCI_VDEVICE(INTEL, 0x27c1), board_ahci }, /* ICH7 */	{ PCI_VDEVICE(INTEL, 0x27c5), board_ahci }, /* ICH7M */	{ PCI_VDEVICE(INTEL, 0x27c3), board_ahci }, /* ICH7R */	{ PCI_VDEVICE(AL, 0x5288), board_ahci_ign_iferr }, /* ULi M5288 */	{ PCI_VDEVICE(INTEL, 0x2681), board_ahci }, /* ESB2 */	{ PCI_VDEVICE(INTEL, 0x2682), board_ahci }, /* ESB2 */	{ PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */	{ PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */	{ PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */	{ PCI_VDEVICE(INTEL, 0x2822), board_ahci }, /* ICH8 */	{ PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */	{ PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -