brintos

brintos / linux-shallow public Read only

0
0
Text · 11.2 KiB · d39b875 Raw
425 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  sata_vsc.c - Vitesse VSC7174 4 port DPA SATA4 *5 *  Maintained by:  Jeremy Higdon @ SGI6 * 		    Please ALWAYS copy linux-ide@vger.kernel.org7 *		    on emails.8 *9 *  Copyright 2004 SGI10 *11 *  Bits from Jeff Garzik, Copyright RedHat, Inc.12 *13 *  libata documentation is available via 'make {ps|pdf}docs',14 *  as Documentation/driver-api/libata.rst15 *16 *  Vitesse hardware documentation presumably available under NDA.17 *  Intel 31244 (same hardware interface) documentation presumably18 *  available from http://developer.intel.com/19 */20 21#include <linux/kernel.h>22#include <linux/module.h>23#include <linux/pci.h>24#include <linux/blkdev.h>25#include <linux/delay.h>26#include <linux/interrupt.h>27#include <linux/dma-mapping.h>28#include <linux/device.h>29#include <scsi/scsi_host.h>30#include <linux/libata.h>31 32#define DRV_NAME	"sata_vsc"33#define DRV_VERSION	"2.3"34 35enum {36	VSC_MMIO_BAR			= 0,37 38	/* Interrupt register offsets (from chip base address) */39	VSC_SATA_INT_STAT_OFFSET	= 0x00,40	VSC_SATA_INT_MASK_OFFSET	= 0x04,41 42	/* Taskfile registers offsets */43	VSC_SATA_TF_CMD_OFFSET		= 0x00,44	VSC_SATA_TF_DATA_OFFSET		= 0x00,45	VSC_SATA_TF_ERROR_OFFSET	= 0x04,46	VSC_SATA_TF_FEATURE_OFFSET	= 0x06,47	VSC_SATA_TF_NSECT_OFFSET	= 0x08,48	VSC_SATA_TF_LBAL_OFFSET		= 0x0c,49	VSC_SATA_TF_LBAM_OFFSET		= 0x10,50	VSC_SATA_TF_LBAH_OFFSET		= 0x14,51	VSC_SATA_TF_DEVICE_OFFSET	= 0x18,52	VSC_SATA_TF_STATUS_OFFSET	= 0x1c,53	VSC_SATA_TF_COMMAND_OFFSET	= 0x1d,54	VSC_SATA_TF_ALTSTATUS_OFFSET	= 0x28,55	VSC_SATA_TF_CTL_OFFSET		= 0x29,56 57	/* DMA base */58	VSC_SATA_UP_DESCRIPTOR_OFFSET	= 0x64,59	VSC_SATA_UP_DATA_BUFFER_OFFSET	= 0x6C,60	VSC_SATA_DMA_CMD_OFFSET		= 0x70,61 62	/* SCRs base */63	VSC_SATA_SCR_STATUS_OFFSET	= 0x100,64	VSC_SATA_SCR_ERROR_OFFSET	= 0x104,65	VSC_SATA_SCR_CONTROL_OFFSET	= 0x108,66 67	/* Port stride */68	VSC_SATA_PORT_OFFSET		= 0x200,69 70	/* Error interrupt status bit offsets */71	VSC_SATA_INT_ERROR_CRC		= 0x40,72	VSC_SATA_INT_ERROR_T		= 0x20,73	VSC_SATA_INT_ERROR_P		= 0x10,74	VSC_SATA_INT_ERROR_R		= 0x8,75	VSC_SATA_INT_ERROR_E		= 0x4,76	VSC_SATA_INT_ERROR_M		= 0x2,77	VSC_SATA_INT_PHY_CHANGE		= 0x1,78	VSC_SATA_INT_ERROR = (VSC_SATA_INT_ERROR_CRC  | VSC_SATA_INT_ERROR_T | \79			      VSC_SATA_INT_ERROR_P    | VSC_SATA_INT_ERROR_R | \80			      VSC_SATA_INT_ERROR_E    | VSC_SATA_INT_ERROR_M | \81			      VSC_SATA_INT_PHY_CHANGE),82};83 84static int vsc_sata_scr_read(struct ata_link *link,85			     unsigned int sc_reg, u32 *val)86{87	if (sc_reg > SCR_CONTROL)88		return -EINVAL;89	*val = readl(link->ap->ioaddr.scr_addr + (sc_reg * 4));90	return 0;91}92 93 94static int vsc_sata_scr_write(struct ata_link *link,95			      unsigned int sc_reg, u32 val)96{97	if (sc_reg > SCR_CONTROL)98		return -EINVAL;99	writel(val, link->ap->ioaddr.scr_addr + (sc_reg * 4));100	return 0;101}102 103 104static void vsc_freeze(struct ata_port *ap)105{106	void __iomem *mask_addr;107 108	mask_addr = ap->host->iomap[VSC_MMIO_BAR] +109		VSC_SATA_INT_MASK_OFFSET + ap->port_no;110 111	writeb(0, mask_addr);112}113 114 115static void vsc_thaw(struct ata_port *ap)116{117	void __iomem *mask_addr;118 119	mask_addr = ap->host->iomap[VSC_MMIO_BAR] +120		VSC_SATA_INT_MASK_OFFSET + ap->port_no;121 122	writeb(0xff, mask_addr);123}124 125 126static void vsc_intr_mask_update(struct ata_port *ap, u8 ctl)127{128	void __iomem *mask_addr;129	u8 mask;130 131	mask_addr = ap->host->iomap[VSC_MMIO_BAR] +132		VSC_SATA_INT_MASK_OFFSET + ap->port_no;133	mask = readb(mask_addr);134	if (ctl & ATA_NIEN)135		mask |= 0x80;136	else137		mask &= 0x7F;138	writeb(mask, mask_addr);139}140 141 142static void vsc_sata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)143{144	struct ata_ioports *ioaddr = &ap->ioaddr;145	unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;146 147	/*148	 * The only thing the ctl register is used for is SRST.149	 * That is not enabled or disabled via tf_load.150	 * However, if ATA_NIEN is changed, then we need to change151	 * the interrupt register.152	 */153	if ((tf->ctl & ATA_NIEN) != (ap->last_ctl & ATA_NIEN)) {154		ap->last_ctl = tf->ctl;155		vsc_intr_mask_update(ap, tf->ctl & ATA_NIEN);156	}157	if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {158		writew(tf->feature | (((u16)tf->hob_feature) << 8),159		       ioaddr->feature_addr);160		writew(tf->nsect | (((u16)tf->hob_nsect) << 8),161		       ioaddr->nsect_addr);162		writew(tf->lbal | (((u16)tf->hob_lbal) << 8),163		       ioaddr->lbal_addr);164		writew(tf->lbam | (((u16)tf->hob_lbam) << 8),165		       ioaddr->lbam_addr);166		writew(tf->lbah | (((u16)tf->hob_lbah) << 8),167		       ioaddr->lbah_addr);168	} else if (is_addr) {169		writew(tf->feature, ioaddr->feature_addr);170		writew(tf->nsect, ioaddr->nsect_addr);171		writew(tf->lbal, ioaddr->lbal_addr);172		writew(tf->lbam, ioaddr->lbam_addr);173		writew(tf->lbah, ioaddr->lbah_addr);174	}175 176	if (tf->flags & ATA_TFLAG_DEVICE)177		writeb(tf->device, ioaddr->device_addr);178 179	ata_wait_idle(ap);180}181 182 183static void vsc_sata_tf_read(struct ata_port *ap, struct ata_taskfile *tf)184{185	struct ata_ioports *ioaddr = &ap->ioaddr;186	u16 nsect, lbal, lbam, lbah, error;187 188	tf->status = ata_sff_check_status(ap);189	tf->device = readw(ioaddr->device_addr);190	error = readw(ioaddr->error_addr);191	nsect = readw(ioaddr->nsect_addr);192	lbal = readw(ioaddr->lbal_addr);193	lbam = readw(ioaddr->lbam_addr);194	lbah = readw(ioaddr->lbah_addr);195 196	tf->error = error;197	tf->nsect = nsect;198	tf->lbal = lbal;199	tf->lbam = lbam;200	tf->lbah = lbah;201 202	if (tf->flags & ATA_TFLAG_LBA48) {203		tf->hob_feature = error >> 8;204		tf->hob_nsect = nsect >> 8;205		tf->hob_lbal = lbal >> 8;206		tf->hob_lbam = lbam >> 8;207		tf->hob_lbah = lbah >> 8;208	}209}210 211static inline void vsc_error_intr(u8 port_status, struct ata_port *ap)212{213	if (port_status & (VSC_SATA_INT_PHY_CHANGE | VSC_SATA_INT_ERROR_M))214		ata_port_freeze(ap);215	else216		ata_port_abort(ap);217}218 219static void vsc_port_intr(u8 port_status, struct ata_port *ap)220{221	struct ata_queued_cmd *qc;222	int handled = 0;223 224	if (unlikely(port_status & VSC_SATA_INT_ERROR)) {225		vsc_error_intr(port_status, ap);226		return;227	}228 229	qc = ata_qc_from_tag(ap, ap->link.active_tag);230	if (qc && likely(!(qc->tf.flags & ATA_TFLAG_POLLING)))231		handled = ata_bmdma_port_intr(ap, qc);232 233	/* We received an interrupt during a polled command,234	 * or some other spurious condition.  Interrupt reporting235	 * with this hardware is fairly reliable so it is safe to236	 * simply clear the interrupt237	 */238	if (unlikely(!handled))239		ap->ops->sff_check_status(ap);240}241 242/*243 * vsc_sata_interrupt244 *245 * Read the interrupt register and process for the devices that have246 * them pending.247 */248static irqreturn_t vsc_sata_interrupt(int irq, void *dev_instance)249{250	struct ata_host *host = dev_instance;251	unsigned int i;252	unsigned int handled = 0;253	u32 status;254 255	status = readl(host->iomap[VSC_MMIO_BAR] + VSC_SATA_INT_STAT_OFFSET);256 257	if (unlikely(status == 0xffffffff || status == 0)) {258		if (status)259			dev_err(host->dev,260				": IRQ status == 0xffffffff, PCI fault or device removal?\n");261		goto out;262	}263 264	spin_lock(&host->lock);265 266	for (i = 0; i < host->n_ports; i++) {267		u8 port_status = (status >> (8 * i)) & 0xff;268		if (port_status) {269			vsc_port_intr(port_status, host->ports[i]);270			handled++;271		}272	}273 274	spin_unlock(&host->lock);275out:276	return IRQ_RETVAL(handled);277}278 279 280static const struct scsi_host_template vsc_sata_sht = {281	ATA_BMDMA_SHT(DRV_NAME),282};283 284 285static struct ata_port_operations vsc_sata_ops = {286	.inherits		= &ata_bmdma_port_ops,287	/* The IRQ handling is not quite standard SFF behaviour so we288	   cannot use the default lost interrupt handler */289	.lost_interrupt		= ATA_OP_NULL,290	.sff_tf_load		= vsc_sata_tf_load,291	.sff_tf_read		= vsc_sata_tf_read,292	.freeze			= vsc_freeze,293	.thaw			= vsc_thaw,294	.scr_read		= vsc_sata_scr_read,295	.scr_write		= vsc_sata_scr_write,296};297 298static void vsc_sata_setup_port(struct ata_ioports *port, void __iomem *base)299{300	port->cmd_addr		= base + VSC_SATA_TF_CMD_OFFSET;301	port->data_addr		= base + VSC_SATA_TF_DATA_OFFSET;302	port->error_addr	= base + VSC_SATA_TF_ERROR_OFFSET;303	port->feature_addr	= base + VSC_SATA_TF_FEATURE_OFFSET;304	port->nsect_addr	= base + VSC_SATA_TF_NSECT_OFFSET;305	port->lbal_addr		= base + VSC_SATA_TF_LBAL_OFFSET;306	port->lbam_addr		= base + VSC_SATA_TF_LBAM_OFFSET;307	port->lbah_addr		= base + VSC_SATA_TF_LBAH_OFFSET;308	port->device_addr	= base + VSC_SATA_TF_DEVICE_OFFSET;309	port->status_addr	= base + VSC_SATA_TF_STATUS_OFFSET;310	port->command_addr	= base + VSC_SATA_TF_COMMAND_OFFSET;311	port->altstatus_addr	= base + VSC_SATA_TF_ALTSTATUS_OFFSET;312	port->ctl_addr		= base + VSC_SATA_TF_CTL_OFFSET;313	port->bmdma_addr	= base + VSC_SATA_DMA_CMD_OFFSET;314	port->scr_addr		= base + VSC_SATA_SCR_STATUS_OFFSET;315	writel(0, base + VSC_SATA_UP_DESCRIPTOR_OFFSET);316	writel(0, base + VSC_SATA_UP_DATA_BUFFER_OFFSET);317}318 319 320static int vsc_sata_init_one(struct pci_dev *pdev,321			     const struct pci_device_id *ent)322{323	static const struct ata_port_info pi = {324		.flags		= ATA_FLAG_SATA,325		.pio_mask	= ATA_PIO4,326		.mwdma_mask	= ATA_MWDMA2,327		.udma_mask	= ATA_UDMA6,328		.port_ops	= &vsc_sata_ops,329	};330	const struct ata_port_info *ppi[] = { &pi, NULL };331	struct ata_host *host;332	void __iomem *mmio_base;333	int i, rc;334	u8 cls;335 336	ata_print_version_once(&pdev->dev, DRV_VERSION);337 338	/* allocate host */339	host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4);340	if (!host)341		return -ENOMEM;342 343	rc = pcim_enable_device(pdev);344	if (rc)345		return rc;346 347	/* check if we have needed resource mapped */348	if (pci_resource_len(pdev, 0) == 0)349		return -ENODEV;350 351	/* map IO regions and initialize host accordingly */352	rc = pcim_iomap_regions(pdev, 1 << VSC_MMIO_BAR, DRV_NAME);353	if (rc == -EBUSY)354		pcim_pin_device(pdev);355	if (rc)356		return rc;357	host->iomap = pcim_iomap_table(pdev);358 359	mmio_base = host->iomap[VSC_MMIO_BAR];360 361	for (i = 0; i < host->n_ports; i++) {362		struct ata_port *ap = host->ports[i];363		unsigned int offset = (i + 1) * VSC_SATA_PORT_OFFSET;364 365		vsc_sata_setup_port(&ap->ioaddr, mmio_base + offset);366 367		ata_port_pbar_desc(ap, VSC_MMIO_BAR, -1, "mmio");368		ata_port_pbar_desc(ap, VSC_MMIO_BAR, offset, "port");369	}370 371	/*372	 * Use 32 bit DMA mask, because 64 bit address support is poor.373	 */374	rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));375	if (rc)376		return rc;377 378	/*379	 * Due to a bug in the chip, the default cache line size can't be380	 * used (unless the default is non-zero).381	 */382	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cls);383	if (cls == 0x00)384		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x80);385 386	if (pci_enable_msi(pdev) == 0)387		pci_intx(pdev, 0);388 389	/*390	 * Config offset 0x98 is "Extended Control and Status Register 0"391	 * Default value is (1 << 28).  All bits except bit 28 are reserved in392	 * DPA mode.  If bit 28 is set, LED 0 reflects all ports' activity.393	 * If bit 28 is clear, each port has its own LED.394	 */395	pci_write_config_dword(pdev, 0x98, 0);396 397	pci_set_master(pdev);398	return ata_host_activate(host, pdev->irq, vsc_sata_interrupt,399				 IRQF_SHARED, &vsc_sata_sht);400}401 402static const struct pci_device_id vsc_sata_pci_tbl[] = {403	{ PCI_VENDOR_ID_VITESSE, 0x7174,404	  PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 },405	{ PCI_VENDOR_ID_INTEL, 0x3200,406	  PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 },407 408	{ }	/* terminate list */409};410 411static struct pci_driver vsc_sata_pci_driver = {412	.name			= DRV_NAME,413	.id_table		= vsc_sata_pci_tbl,414	.probe			= vsc_sata_init_one,415	.remove			= ata_pci_remove_one,416};417 418module_pci_driver(vsc_sata_pci_driver);419 420MODULE_AUTHOR("Jeremy Higdon");421MODULE_DESCRIPTION("low-level driver for Vitesse VSC7174 SATA controller");422MODULE_LICENSE("GPL");423MODULE_DEVICE_TABLE(pci, vsc_sata_pci_tbl);424MODULE_VERSION(DRV_VERSION);425