📄 pata_hpt3x2n.c
字号:
/* * Libata driver for the highpoint 372N and 302N UDMA66 ATA controllers. * * This driver is heavily based upon: * * linux/drivers/ide/pci/hpt366.c Version 0.36 April 25, 2003 * * Copyright (C) 1999-2003 Andre Hedrick <andre@linux-ide.org> * Portions Copyright (C) 2001 Sun Microsystems, Inc. * Portions Copyright (C) 2003 Red Hat Inc * Portions Copyright (C) 2005-2007 MontaVista Software, Inc. * * * TODO * Work out best PLL policy */#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 <scsi/scsi_host.h>#include <linux/libata.h>#define DRV_NAME "pata_hpt3x2n"#define DRV_VERSION "0.3.4"enum { HPT_PCI_FAST = (1 << 31), PCI66 = (1 << 1), USE_DPLL = (1 << 0)};struct hpt_clock { u8 xfer_speed; u32 timing;};struct hpt_chip { const char *name; struct hpt_clock *clocks[3];};/* key for bus clock timings * bit * 0:3 data_high_time. inactive time of DIOW_/DIOR_ for PIO and MW * DMA. cycles = value + 1 * 4:8 data_low_time. active time of DIOW_/DIOR_ for PIO and MW * DMA. cycles = value + 1 * 9:12 cmd_high_time. inactive time of DIOW_/DIOR_ during task file * register access. * 13:17 cmd_low_time. active time of DIOW_/DIOR_ during task file * register access. * 18:21 udma_cycle_time. clock freq and clock cycles for UDMA xfer. * during task file register access. * 22:24 pre_high_time. time to initialize 1st cycle for PIO and MW DMA * xfer. * 25:27 cmd_pre_high_time. time to initialize 1st PIO cycle for task * register access. * 28 UDMA enable * 29 DMA enable * 30 PIO_MST enable. if set, the chip is in bus master mode during * PIO. * 31 FIFO enable. *//* 66MHz DPLL clocks */static struct hpt_clock hpt3x2n_clocks[] = { { XFER_UDMA_7, 0x1c869c62 }, { XFER_UDMA_6, 0x1c869c62 }, { XFER_UDMA_5, 0x1c8a9c62 }, { XFER_UDMA_4, 0x1c8a9c62 }, { XFER_UDMA_3, 0x1c8e9c62 }, { XFER_UDMA_2, 0x1c929c62 }, { XFER_UDMA_1, 0x1c9a9c62 }, { XFER_UDMA_0, 0x1c829c62 }, { XFER_MW_DMA_2, 0x2c829c62 }, { XFER_MW_DMA_1, 0x2c829c66 }, { XFER_MW_DMA_0, 0x2c829d2c }, { XFER_PIO_4, 0x0c829c62 }, { XFER_PIO_3, 0x0c829c84 }, { XFER_PIO_2, 0x0c829ca6 }, { XFER_PIO_1, 0x0d029d26 }, { XFER_PIO_0, 0x0d029d5e }, { 0, 0x0d029d5e }};/** * hpt3x2n_find_mode - reset the hpt3x2n bus * @ap: ATA port * @speed: transfer mode * * Return the 32bit register programming information for this channel * that matches the speed provided. For the moment the clocks table * is hard coded but easy to change. This will be needed if we use * different DPLLs */static u32 hpt3x2n_find_mode(struct ata_port *ap, int speed){ struct hpt_clock *clocks = hpt3x2n_clocks; while(clocks->xfer_speed) { if (clocks->xfer_speed == speed) return clocks->timing; clocks++; } BUG(); return 0xffffffffU; /* silence compiler warning */}/** * hpt3x2n_cable_detect - Detect the cable type * @ap: ATA port to detect on * * Return the cable type attached to this port */static int hpt3x2n_cable_detect(struct ata_port *ap){ u8 scr2, ata66; struct pci_dev *pdev = to_pci_dev(ap->host->dev); pci_read_config_byte(pdev, 0x5B, &scr2); pci_write_config_byte(pdev, 0x5B, scr2 & ~0x01); /* Cable register now active */ pci_read_config_byte(pdev, 0x5A, &ata66); /* Restore state */ pci_write_config_byte(pdev, 0x5B, scr2); if (ata66 & (1 << ap->port_no)) return ATA_CBL_PATA40; else return ATA_CBL_PATA80;}/** * hpt3x2n_pre_reset - reset the hpt3x2n bus * @link: ATA link to reset * @deadline: deadline jiffies for the operation * * Perform the initial reset handling for the 3x2n series controllers. * Reset the hardware and state machine, */static int hpt3xn_pre_reset(struct ata_link *link, unsigned long deadline){ struct ata_port *ap = link->ap; struct pci_dev *pdev = to_pci_dev(ap->host->dev); /* Reset the state machine */ pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37); udelay(100); return ata_std_prereset(link, deadline);}/** * hpt3x2n_error_handler - probe the hpt3x2n bus * @ap: ATA port to reset * * Perform the probe reset handling for the 3x2N */static void hpt3x2n_error_handler(struct ata_port *ap){ ata_bmdma_drive_eh(ap, hpt3xn_pre_reset, ata_std_softreset, NULL, ata_std_postreset);}/** * hpt3x2n_set_piomode - PIO setup * @ap: ATA interface * @adev: device on the interface * * Perform PIO mode setup. */static void hpt3x2n_set_piomode(struct ata_port *ap, struct ata_device *adev){ struct pci_dev *pdev = to_pci_dev(ap->host->dev); u32 addr1, addr2; u32 reg; u32 mode; u8 fast; addr1 = 0x40 + 4 * (adev->devno + 2 * ap->port_no); addr2 = 0x51 + 4 * ap->port_no; /* Fast interrupt prediction disable, hold off interrupt disable */ pci_read_config_byte(pdev, addr2, &fast); fast &= ~0x07; pci_write_config_byte(pdev, addr2, fast); pci_read_config_dword(pdev, addr1, ®); mode = hpt3x2n_find_mode(ap, adev->pio_mode); mode &= ~0x8000000; /* No FIFO in PIO */ mode &= ~0x30070000; /* Leave config bits alone */ reg &= 0x30070000; /* Strip timing bits */ pci_write_config_dword(pdev, addr1, reg | mode);}/** * hpt3x2n_set_dmamode - DMA timing setup * @ap: ATA interface * @adev: Device being configured * * Set up the channel for MWDMA or UDMA modes. Much the same as with * PIO, load the mode number and then set MWDMA or UDMA flag. */static void hpt3x2n_set_dmamode(struct ata_port *ap, struct ata_device *adev){ struct pci_dev *pdev = to_pci_dev(ap->host->dev); u32 addr1, addr2; u32 reg; u32 mode; u8 fast; addr1 = 0x40 + 4 * (adev->devno + 2 * ap->port_no); addr2 = 0x51 + 4 * ap->port_no; /* Fast interrupt prediction disable, hold off interrupt disable */ pci_read_config_byte(pdev, addr2, &fast); fast &= ~0x07; pci_write_config_byte(pdev, addr2, fast); pci_read_config_dword(pdev, addr1, ®); mode = hpt3x2n_find_mode(ap, adev->dma_mode); mode |= 0x8000000; /* FIFO in MWDMA or UDMA */ mode &= ~0xC0000000; /* Leave config bits alone */ reg &= 0xC0000000; /* Strip timing bits */ pci_write_config_dword(pdev, addr1, reg | mode);}/** * hpt3x2n_bmdma_end - DMA engine stop * @qc: ATA command * * Clean up after the HPT3x2n and later DMA engine */static void hpt3x2n_bmdma_stop(struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; struct pci_dev *pdev = to_pci_dev(ap->host->dev); int mscreg = 0x50 + 2 * ap->port_no; u8 bwsr_stat, msc_stat; pci_read_config_byte(pdev, 0x6A, &bwsr_stat); pci_read_config_byte(pdev, mscreg, &msc_stat); if (bwsr_stat & (1 << ap->port_no)) pci_write_config_byte(pdev, mscreg, msc_stat | 0x30); ata_bmdma_stop(qc);}/** * hpt3x2n_set_clock - clock control * @ap: ATA port * @source: 0x21 or 0x23 for PLL or PCI sourced clock * * Switch the ATA bus clock between the PLL and PCI clock sources * while correctly isolating the bus and resetting internal logic * * We must use the DPLL for * - writing * - second channel UDMA7 (SATA ports) or higher * - 66MHz PCI * * or we will underclock the device and get reduced performance. */static void hpt3x2n_set_clock(struct ata_port *ap, int source){ void __iomem *bmdma = ap->ioaddr.bmdma_addr; /* Tristate the bus */ iowrite8(0x80, bmdma+0x73); iowrite8(0x80, bmdma+0x77); /* Switch clock and reset channels */ iowrite8(source, bmdma+0x7B); iowrite8(0xC0, bmdma+0x79); /* Reset state machines */ iowrite8(0x37, bmdma+0x70); iowrite8(0x37, bmdma+0x74); /* Complete reset */ iowrite8(0x00, bmdma+0x79); /* Reconnect channels to bus */ iowrite8(0x00, bmdma+0x73); iowrite8(0x00, bmdma+0x77);}/* Check if our partner interface is busy */static int hpt3x2n_pair_idle(struct ata_port *ap){ struct ata_host *host = ap->host; struct ata_port *pair = host->ports[ap->port_no ^ 1]; if (pair->hsm_task_state == HSM_ST_IDLE) return 1; return 0;}static int hpt3x2n_use_dpll(struct ata_port *ap, int writing){ long flags = (long)ap->host->private_data; /* See if we should use the DPLL */ if (writing) return USE_DPLL; /* Needed for write */ if (flags & PCI66) return USE_DPLL; /* Needed at 66Mhz */ return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -