Commit e01891ea authored by Mikhail Karpenko's avatar Mikhail Karpenko

Remove debug code from AHCI driver

parent 0a055f61
......@@ -2,10 +2,10 @@
obj-$(CONFIG_ATA) += libata.o
# non-SFF interface
obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o libahci_debug.o
obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o
obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o
obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o libahci_debug.o
obj-$(CONFIG_AHCI_ELPHEL) += ahci_elphel.o libahci.o libahci_platform.o libahci_debug.o
obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_ELPHEL) += ahci_elphel.o libahci.o libahci_platform.o
obj-$(CONFIG_SATA_FSL) += sata_fsl.o
obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
......
/*
* ahci.c - AHCI SATA support
*
* Maintained by: Tejun Heo <tj@kernel.org>
* 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/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/gfp.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
#include "ahci.h"
#define DRV_NAME "ahci"
#define DRV_VERSION "3.0"
enum {
AHCI_PCI_BAR_STA2X11 = 0,
AHCI_PCI_BAR_ENMOTUS = 2,
AHCI_PCI_BAR_STANDARD = 5,
};
enum board_ids {
/* board IDs by feature in alphabetical order */
board_ahci,
board_ahci_ign_iferr,
board_ahci_nomsi,
board_ahci_noncq,
board_ahci_nosntf,
board_ahci_yes_fbs,
/* board IDs for specific chipsets in alphabetical order */
board_ahci_mcp65,
board_ahci_mcp77,
board_ahci_mcp89,
board_ahci_mv,
board_ahci_sb600,
board_ahci_sb700, /* for SB700 and SB800 */
board_ahci_vt8251,
/* aliases */
board_ahci_mcp_linux = board_ahci_mcp65,
board_ahci_mcp67 = board_ahci_mcp65,
board_ahci_mcp73 = board_ahci_mcp65,
board_ahci_mcp79 = board_ahci_mcp77,
};
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static void ahci_mcp89_apple_enable(struct pci_dev *pdev);
static bool is_mcp89_apple(struct pci_dev *pdev);
static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
#ifdef CONFIG_PM
static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
static int ahci_pci_device_resume(struct pci_dev *pdev);
#endif
static struct scsi_host_template ahci_sht = {
AHCI_SHT("ahci"),
};
static struct ata_port_operations ahci_vt8251_ops = {
.inherits = &ahci_ops,
.hardreset = ahci_vt8251_hardreset,
};
static struct ata_port_operations ahci_p5wdh_ops = {
.inherits = &ahci_ops,
.hardreset = ahci_p5wdh_hardreset,
};
static const struct ata_port_info ahci_port_info[] = {
/* by features */
[board_ahci] = {
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_ign_iferr] = {
AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_nomsi] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_MSI),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_noncq] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_nosntf] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_SNTF),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_yes_fbs] = {
AHCI_HFLAGS (AHCI_HFLAG_YES_FBS),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
/* by chipsets */
[board_ahci_mcp65] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP |
AHCI_HFLAG_YES_NCQ),
.flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_mcp77] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_mcp89] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.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 | AHCI_HFLAG_NO_PMP),
.flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_sb600] = {
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL |
AHCI_HFLAG_NO_MSI | AHCI_HFLAG_SECT255 |
AHCI_HFLAG_32BIT_ONLY),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_pmp_retry_srst_ops,
},
[board_ahci_sb700] = { /* for SB700 and SB800 */
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_pmp_retry_srst_ops,
},
[board_ahci_vt8251] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_vt8251_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_nosntf }, /* ICH8 */
{ PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */
{ PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */
{ PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */
{ PCI_VDEVICE(INTEL, 0x2922), board_ahci }, /* ICH9 */
{ PCI_VDEVICE(INTEL, 0x2923), board_ahci }, /* ICH9 */
{ PCI_VDEVICE(INTEL, 0x2924), board_ahci }, /* ICH9 */
{ PCI_VDEVICE(INTEL, 0x2925), board_ahci }, /* ICH9 */
{ PCI_VDEVICE(INTEL, 0x2927), board_ahci }, /* ICH9 */
{ PCI_VDEVICE(INTEL, 0x2929), board_ahci }, /* ICH9M */
{ PCI_VDEVICE(INTEL, 0x292a), board_ahci }, /* ICH9M */
{ PCI_VDEVICE(INTEL, 0x292b), board_ahci }, /* ICH9M */
{ PCI_VDEVICE(INTEL, 0x292c), board_ahci }, /* ICH9M */
{ PCI_VDEVICE(INTEL, 0x292f), board_ahci }, /* ICH9M */
{ PCI_VDEVICE(INTEL, 0x294d), board_ahci }, /* ICH9 */
{ PCI_VDEVICE(INTEL, 0x294e), board_ahci }, /* ICH9M */
{ PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */
{ PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */
{ PCI_VDEVICE(INTEL, 0x3a05), board_ahci }, /* ICH10 */
{ PCI_VDEVICE(INTEL, 0x3a22), board_ahci }, /* ICH10 */
{ PCI_VDEVICE(INTEL, 0x3a25), board_ahci }, /* ICH10 */
{ PCI_VDEVICE(INTEL, 0x3b22), board_ahci }, /* PCH AHCI */
{ PCI_VDEVICE(INTEL, 0x3b23), board_ahci }, /* PCH AHCI */
{ PCI_VDEVICE(INTEL, 0x3b24), board_ahci }, /* PCH RAID */
{ PCI_VDEVICE(INTEL, 0x3b25), board_ahci }, /* PCH RAID */
{ PCI_VDEVICE(INTEL, 0x3b29), board_ahci }, /* PCH AHCI */
{ PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */
{ PCI_VDEVICE(INTEL, 0x3b2c), board_ahci }, /* PCH RAID */
{ PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */
{ PCI_VDEVICE(INTEL, 0x1c02), board_ahci }, /* CPT AHCI */
{ PCI_VDEVICE(INTEL, 0x1c03), board_ahci }, /* CPT AHCI */
{ PCI_VDEVICE(INTEL, 0x1c04), board_ahci }, /* CPT RAID */
{ PCI_VDEVICE(INTEL, 0x1c05), board_ahci }, /* CPT RAID */
{ PCI_VDEVICE(INTEL, 0x1c06), board_ahci }, /* CPT RAID */
{ PCI_VDEVICE(INTEL, 0x1c07), board_ahci }, /* CPT RAID */
{ PCI_VDEVICE(INTEL, 0x1d02), board_ahci }, /* PBG AHCI */
{ PCI_VDEVICE(INTEL, 0x1d04), board_ahci }, /* PBG RAID */
{ PCI_VDEVICE(INTEL, 0x1d06), board_ahci }, /* PBG RAID */
{ PCI_VDEVICE(INTEL, 0x2826), board_ahci }, /* PBG RAID */
{ PCI_VDEVICE(INTEL, 0x2323), board_ahci }, /* DH89xxCC AHCI */
{ PCI_VDEVICE(INTEL, 0x1e02), board_ahci }, /* Panther Point AHCI */
{ PCI_VDEVICE(INTEL, 0x1e03), board_ahci }, /* Panther Point AHCI */
{ PCI_VDEVICE(INTEL, 0x1e04), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x1e05), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x1e06), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x1e07), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x1e0e), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c02), board_ahci }, /* Lynx Point AHCI */
{ PCI_VDEVICE(INTEL, 0x8c03), board_ahci }, /* Lynx Point AHCI */
{ PCI_VDEVICE(INTEL, 0x8c04), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c05), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c06), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c07), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c0e), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c0f), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x9c02), board_ahci }, /* Lynx Point-LP AHCI */
{ PCI_VDEVICE(INTEL, 0x9c03), board_ahci }, /* Lynx Point-LP AHCI */
{ PCI_VDEVICE(INTEL, 0x9c04), board_ahci }, /* Lynx Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c05), board_ahci }, /* Lynx Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c06), board_ahci }, /* Lynx Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c07), board_ahci }, /* Lynx Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c0e), board_ahci }, /* Lynx Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c0f), board_ahci }, /* Lynx Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x1f22), board_ahci }, /* Avoton AHCI */
{ PCI_VDEVICE(INTEL, 0x1f23), board_ahci }, /* Avoton AHCI */
{ PCI_VDEVICE(INTEL, 0x1f24), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f25), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f26), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f32), board_ahci }, /* Avoton AHCI */
{ PCI_VDEVICE(INTEL, 0x1f33), board_ahci }, /* Avoton AHCI */
{ PCI_VDEVICE(INTEL, 0x1f34), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f35), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f36), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f37), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f3e), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f3f), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */
{ PCI_VDEVICE(INTEL, 0x8d04), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d06), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d0e), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d62), board_ahci }, /* Wellsburg AHCI */
{ PCI_VDEVICE(INTEL, 0x8d64), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d66), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d6e), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x23a3), board_ahci }, /* Coleto Creek AHCI */
{ PCI_VDEVICE(INTEL, 0x9c83), board_ahci }, /* Wildcat Point-LP AHCI */
{ PCI_VDEVICE(INTEL, 0x9c85), board_ahci }, /* Wildcat Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c87), board_ahci }, /* Wildcat Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c8f), board_ahci }, /* Wildcat Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x8c82), board_ahci }, /* 9 Series AHCI */
{ PCI_VDEVICE(INTEL, 0x8c83), board_ahci }, /* 9 Series AHCI */
{ PCI_VDEVICE(INTEL, 0x8c84), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c85), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c86), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c87), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c8e), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c8f), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x9d03), board_ahci }, /* Sunrise Point-LP AHCI */
{ PCI_VDEVICE(INTEL, 0x9d05), board_ahci }, /* Sunrise Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9d07), board_ahci }, /* Sunrise Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */
{ PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */
{ PCI_VDEVICE(INTEL, 0xa107), board_ahci }, /* Sunrise Point-H RAID */
{ PCI_VDEVICE(INTEL, 0xa10f), board_ahci }, /* Sunrise Point-H RAID */
/* JMicron 360/1/3/5/6, match class to avoid IDE function */
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci_ign_iferr },
/* JMicron 362B and 362C have an AHCI function with IDE class code */
{ PCI_VDEVICE(JMICRON, 0x2362), board_ahci_ign_iferr },
{ PCI_VDEVICE(JMICRON, 0x236f), board_ahci_ign_iferr },
/* ATI */
{ PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */
{ PCI_VDEVICE(ATI, 0x4390), board_ahci_sb700 }, /* ATI SB700/800 */
{ PCI_VDEVICE(ATI, 0x4391), board_ahci_sb700 }, /* ATI SB700/800 */
{ PCI_VDEVICE(ATI, 0x4392), board_ahci_sb700 }, /* ATI SB700/800 */
{ PCI_VDEVICE(ATI, 0x4393), board_ahci_sb700 }, /* ATI SB700/800 */
{ PCI_VDEVICE(ATI, 0x4394), board_ahci_sb700 }, /* ATI SB700/800 */
{ PCI_VDEVICE(ATI, 0x4395), board_ahci_sb700 }, /* ATI SB700/800 */
/* AMD */
{ PCI_VDEVICE(AMD, 0x7800), board_ahci }, /* AMD Hudson-2 */
{ PCI_VDEVICE(AMD, 0x7900), board_ahci }, /* AMD CZ */
/* AMD is using RAID class only for ahci controllers */
{ PCI_VENDOR_ID_AMD, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_RAID << 8, 0xffffff, board_ahci },
/* VIA */
{ PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */
{ PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */
/* NVIDIA */
{ PCI_VDEVICE(NVIDIA, 0x044c), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x044d), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x044e), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x044f), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x045c), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x045d), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x045e), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x045f), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x0550), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0551), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0552), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0553), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0554), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0555), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0556), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0557), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0558), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0559), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x055a), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x055b), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0580), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0581), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0582), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0583), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0584), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0585), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0586), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0587), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0588), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0589), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x058a), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x058b), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x058c), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x058d), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x058e), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x058f), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x0ad0), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad1), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad2), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad3), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad4), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad5), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad6), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad7), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad8), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad9), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ada), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0adb), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ab4), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0ab5), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0ab6), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0ab7), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0ab8), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0ab9), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0aba), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0abb), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0abc), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0abd), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0abe), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0abf), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0d84), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d85), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d86), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d87), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d88), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d89), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d8a), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d8b), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d8c), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d8d), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d8e), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d8f), board_ahci_mcp89 }, /* MCP89 */
/* SiS */
{ PCI_VDEVICE(SI, 0x1184), board_ahci }, /* SiS 966 */
{ PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 968 */
{ PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */
/* ST Microelectronics */
{ PCI_VDEVICE(STMICRO, 0xCC06), board_ahci }, /* ST ConneXt */
/* Marvell */
{ PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv }, /* 6145 */
{ PCI_VDEVICE(MARVELL, 0x6121), board_ahci_mv }, /* 6121 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9123),
.class = PCI_CLASS_STORAGE_SATA_AHCI,
.class_mask = 0xffffff,
.driver_data = board_ahci_yes_fbs }, /* 88se9128 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9125),
.driver_data = board_ahci_yes_fbs }, /* 88se9125 */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_MARVELL_EXT, 0x9178,
PCI_VENDOR_ID_MARVELL_EXT, 0x9170),
.driver_data = board_ahci_yes_fbs }, /* 88se9170 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x917a),
.driver_data = board_ahci_yes_fbs }, /* 88se9172 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9172),
.driver_data = board_ahci_yes_fbs }, /* 88se9182 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9182),
.driver_data = board_ahci_yes_fbs }, /* 88se9172 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9192),
.driver_data = board_ahci_yes_fbs }, /* 88se9172 on some Gigabyte */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0),
.driver_data = board_ahci_yes_fbs },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a3),
.driver_data = board_ahci_yes_fbs },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9230),
.driver_data = board_ahci_yes_fbs },
{ PCI_DEVICE(PCI_VENDOR_ID_TTI, 0x0642),
.driver_data = board_ahci_yes_fbs },
/* Promise */
{ PCI_VDEVICE(PROMISE, 0x3f20), board_ahci }, /* PDC42819 */
{ PCI_VDEVICE(PROMISE, 0x3781), board_ahci }, /* FastTrak TX8660 ahci-mode */
/* Asmedia */
{ PCI_VDEVICE(ASMEDIA, 0x0601), board_ahci }, /* ASM1060 */
{ PCI_VDEVICE(ASMEDIA, 0x0602), board_ahci }, /* ASM1060 */
{ PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci }, /* ASM1061 */
{ PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1062 */
/*
* Samsung SSDs found on some macbooks. NCQ times out if MSI is
* enabled. https://bugzilla.kernel.org/show_bug.cgi?id=60731
*/
{ PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_nomsi },
{ PCI_VDEVICE(SAMSUNG, 0xa800), board_ahci_nomsi },
/* Enmotus */
{ PCI_DEVICE(0x1c44, 0x8000), board_ahci },
/* Generic, PCI class code for AHCI */
{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci },
{ } /* terminate list */
};
static struct pci_driver ahci_pci_driver = {
.name = DRV_NAME,
.id_table = ahci_pci_tbl,
.probe = ahci_init_one,
.remove = ata_pci_remove_one,
#ifdef CONFIG_PM
.suspend = ahci_pci_device_suspend,
.resume = ahci_pci_device_resume,
#endif
};
#if defined(CONFIG_PATA_MARVELL) || defined(CONFIG_PATA_MARVELL_MODULE)
static int marvell_enable;
#else
static int marvell_enable = 1;
#endif
module_param(marvell_enable, int, 0644);
MODULE_PARM_DESC(marvell_enable, "Marvell SATA via AHCI (1 = enabled)");
static void ahci_pci_save_initial_config(struct pci_dev *pdev,
struct ahci_host_priv *hpriv)
{
if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361) {
dev_info(&pdev->dev, "JMB361 has only one port\n");
hpriv->force_port_map = 1;
}
/*
* Temporary Marvell 6145 hack: PATA port presence
* is asserted through the standard AHCI port
* presence register, as bit 4 (counting from 0)
*/
if (hpriv->flags & AHCI_HFLAG_MV_PATA) {
if (pdev->device == 0x6121)
hpriv->mask_port_map = 0x3;
else
hpriv->mask_port_map = 0xf;
dev_info(&pdev->dev,
"Disabling your PATA port. Use the boot option 'ahci.marvell_enable=0' to avoid this.\n");
}
ahci_save_initial_config(&pdev->dev, hpriv);
}
static int ahci_pci_reset_controller(struct ata_host *host)
{
struct pci_dev *pdev = to_pci_dev(host->dev);
ahci_reset_controller(host);
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
struct ahci_host_priv *hpriv = host->private_data;
u16 tmp16;
/* configure PCS */
pci_read_config_word(pdev, 0x92, &tmp16);
if ((tmp16 & hpriv->port_map) != hpriv->port_map) {
tmp16 |= hpriv->port_map;
pci_write_config_word(pdev, 0x92, tmp16);
}
}
return 0;
}
static void ahci_pci_init_controller(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
struct pci_dev *pdev = to_pci_dev(host->dev);
void __iomem *port_mmio;
u32 tmp;
int mv;
if (hpriv->flags & AHCI_HFLAG_MV_PATA) {
if (pdev->device == 0x6121)
mv = 2;
else
mv = 4;
port_mmio = __ahci_port_base(host, mv);
writel(0, port_mmio + PORT_IRQ_MASK);
/* clear port IRQ */
tmp = readl(port_mmio + PORT_IRQ_STAT);
VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
if (tmp)
writel(tmp, port_mmio + PORT_IRQ_STAT);
}
ahci_init_controller(host);
}
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
struct ata_port *ap = link->ap;
struct ahci_host_priv *hpriv = ap->host->private_data;
bool online;
int rc;
DPRINTK("ENTER\n");
ahci_stop_engine(ap);
rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),
deadline, &online, NULL);
dev_info(ap->host->dev, "ahci_vt8251_hardreset()\n");
hpriv->start_engine(ap);
DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
/* vt8251 doesn't clear BSY on signature FIS reception,
* request follow-up softreset.
*/
return online ? -EAGAIN : rc;
}
static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_host_priv *hpriv = ap->host->private_data;
u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
struct ata_taskfile tf;
bool online;
int rc;
dev_info(ap->host->dev, "ahci_p5wdh_hardreset()\n");
ahci_stop_engine(ap);
/* clear D2H reception area to properly wait for D2H FIS */
ata_tf_init(link->device, &tf);
tf.command = ATA_BUSY;
ata_tf_to_fis(&tf, 0, 0, d2h_fis);
rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),
deadline, &online, NULL);
hpriv->start_engine(ap);
/* The pseudo configuration device on SIMG4726 attached to
* ASUS P5W-DH Deluxe doesn't send signature FIS after
* hardreset if no device is attached to the first downstream
* port && the pseudo device locks up on SRST w/ PMP==0. To
* work around this, wait for !BSY only briefly. If BSY isn't
* cleared, perform CLO and proceed to IDENTIFY (achieved by
* ATA_LFLAG_NO_SRST and ATA_LFLAG_ASSUME_ATA).
*
* Wait for two seconds. Devices attached to downstream port
* which can't process the following IDENTIFY after this will
* have to be reset again. For most cases, this should
* suffice while making probing snappish enough.
*/
if (online) {
rc = ata_wait_after_reset(link, jiffies + 2 * HZ,
ahci_check_ready);
if (rc)
ahci_kick_engine(ap);
}
return rc;
}
#ifdef CONFIG_PM
static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
struct ata_host *host = pci_get_drvdata(pdev);
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
u32 ctl;
if (mesg.event & PM_EVENT_SUSPEND &&
hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
dev_err(&pdev->dev,
"BIOS update required for suspend/resume\n");
return -EIO;
}
if (mesg.event & PM_EVENT_SLEEP) {
/* AHCI spec rev1.1 section 8.3.3:
* Software must disable interrupts prior to requesting a
* transition of the HBA to D3 state.
*/
ctl = readl(mmio + HOST_CTL);
ctl &= ~HOST_IRQ_EN;
writel(ctl, mmio + HOST_CTL);
readl(mmio + HOST_CTL); /* flush */
}
return ata_pci_device_suspend(pdev, mesg);
}
static int ahci_pci_device_resume(struct pci_dev *pdev)
{
struct ata_host *host = pci_get_drvdata(pdev);
int rc;
rc = ata_pci_device_do_resume(pdev);
if (rc)
return rc;
/* Apple BIOS helpfully mangles the registers on resume */
if (is_mcp89_apple(pdev))
ahci_mcp89_apple_enable(pdev);
if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
rc = ahci_pci_reset_controller(host);
if (rc)
return rc;
ahci_pci_init_controller(host);
}
ata_host_resume(host);
return 0;
}
#endif
static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac)
{
int rc;
/*
* If the device fixup already set the dma_mask to some non-standard
* value, don't extend it here. This happens on STA2X11, for example.
*/
if (pdev->dma_mask && pdev->dma_mask < DMA_BIT_MASK(32))
return 0;
if (using_dac &&
!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
if (rc) {
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
dev_err(&pdev->dev,
"64-bit DMA enable failed\n");
return rc;
}
}
} else {
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
dev_err(&pdev->dev, "32-bit DMA enable failed\n");
return rc;
}
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
dev_err(&pdev->dev,
"32-bit consistent DMA enable failed\n");
return rc;
}
}
return 0;
}
static void ahci_pci_print_info(struct ata_host *host)
{
struct pci_dev *pdev = to_pci_dev(host->dev);
u16 cc;
const char *scc_s;
pci_read_config_word(pdev, 0x0a, &cc);
if (cc == PCI_CLASS_STORAGE_IDE)
scc_s = "IDE";
else if (cc == PCI_CLASS_STORAGE_SATA)
scc_s = "SATA";
else if (cc == PCI_CLASS_STORAGE_RAID)
scc_s = "RAID";
else
scc_s = "unknown";
ahci_print_info(host, scc_s);
}
/* On ASUS P5W DH Deluxe, the second port of PCI device 00:1f.2 is
* hardwired to on-board SIMG 4726. The chipset is ICH8 and doesn't
* support PMP and the 4726 either directly exports the device
* attached to the first downstream port or acts as a hardware storage
* controller and emulate a single ATA device (can be RAID 0/1 or some
* other configuration).
*
* When there's no device attached to the first downstream port of the
* 4726, "Config Disk" appears, which is a pseudo ATA device to
* configure the 4726. However, ATA emulation of the device is very
* lame. It doesn't send signature D2H Reg FIS after the initial
* hardreset, pukes on SRST w/ PMP==0 and has bunch of other issues.
*
* The following function works around the problem by always using
* hardreset on the port and not depending on receiving signature FIS
* afterward. If signature FIS isn't received soon, ATA class is
* assumed without follow-up softreset.
*/
static void ahci_p5wdh_workaround(struct ata_host *host)
{
static const struct dmi_system_id sysids[] = {
{
.ident = "P5W DH Deluxe",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
"ASUSTEK COMPUTER INC"),
DMI_MATCH(DMI_PRODUCT_NAME, "P5W DH Deluxe"),
},
},
{ }
};
struct pci_dev *pdev = to_pci_dev(host->dev);
if (pdev->bus->number == 0 && pdev->devfn == PCI_DEVFN(0x1f, 2) &&
dmi_check_system(sysids)) {
struct ata_port *ap = host->ports[1];
dev_info(&pdev->dev,
"enabling ASUS P5W DH Deluxe on-board SIMG4726 workaround\n");
ap->ops = &ahci_p5wdh_ops;
ap->link.flags |= ATA_LFLAG_NO_SRST | ATA_LFLAG_ASSUME_ATA;
}
}
/*
* Macbook7,1 firmware forcibly disables MCP89 AHCI and changes PCI ID when
* booting in BIOS compatibility mode. We restore the registers but not ID.
*/
static void ahci_mcp89_apple_enable(struct pci_dev *pdev)
{
u32 val;
printk(KERN_INFO "ahci: enabling MCP89 AHCI mode\n");
pci_read_config_dword(pdev, 0xf8, &val);
val |= 1 << 0x1b;
/* the following changes the device ID, but appears not to affect function */
/* val = (val & ~0xf0000000) | 0x80000000; */
pci_write_config_dword(pdev, 0xf8, val);
pci_read_config_dword(pdev, 0x54c, &val);
val |= 1 << 0xc;
pci_write_config_dword(pdev, 0x54c, val);
pci_read_config_dword(pdev, 0x4a4, &val);
val &= 0xff;
val |= 0x01060100;
pci_write_config_dword(pdev, 0x4a4, val);
pci_read_config_dword(pdev, 0x54c, &val);
val &= ~(1 << 0xc);
pci_write_config_dword(pdev, 0x54c, val);
pci_read_config_dword(pdev, 0xf8, &val);
val &= ~(1 << 0x1b);
pci_write_config_dword(pdev, 0xf8, val);
}
static bool is_mcp89_apple(struct pci_dev *pdev)
{
return pdev->vendor == PCI_VENDOR_ID_NVIDIA &&
pdev->device == PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA &&
pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE &&
pdev->subsystem_device == 0xcb89;
}
/* only some SB600 ahci controllers can do 64bit DMA */
static bool ahci_sb600_enable_64bit(struct pci_dev *pdev)
{
static const struct dmi_system_id sysids[] = {
/*
* The oldest version known to be broken is 0901 and
* working is 1501 which was released on 2007-10-26.
* Enable 64bit DMA on 1501 and anything newer.
*
* Please read bko#9412 for more info.
*/
{
.ident = "ASUS M2A-VM",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR,
"ASUSTeK Computer INC."),
DMI_MATCH(DMI_BOARD_NAME, "M2A-VM"),
},
.driver_data = "20071026", /* yyyymmdd */
},
/*
* All BIOS versions for the MSI K9A2 Platinum (MS-7376)
* support 64bit DMA.
*
* BIOS versions earlier than 1.5 had the Manufacturer DMI
* fields as "MICRO-STAR INTERANTIONAL CO.,LTD".
* This spelling mistake was fixed in BIOS version 1.5, so
* 1.5 and later have the Manufacturer as
* "MICRO-STAR INTERNATIONAL CO.,LTD".
* So try to match on DMI_BOARD_VENDOR of "MICRO-STAR INTER".
*
* BIOS versions earlier than 1.9 had a Board Product Name
* DMI field of "MS-7376". This was changed to be
* "K9A2 Platinum (MS-7376)" in version 1.9, but we can still
* match on DMI_BOARD_NAME of "MS-7376".
*/
{
.ident = "MSI K9A2 Platinum",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR,
"MICRO-STAR INTER"),
DMI_MATCH(DMI_BOARD_NAME, "MS-7376"),
},
},
/*
* All BIOS versions for the MSI K9AGM2 (MS-7327) support
* 64bit DMA.
*
* This board also had the typo mentioned above in the
* Manufacturer DMI field (fixed in BIOS version 1.5), so
* match on DMI_BOARD_VENDOR of "MICRO-STAR INTER" again.
*/
{
.ident = "MSI K9AGM2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR,
"MICRO-STAR INTER"),
DMI_MATCH(DMI_BOARD_NAME, "MS-7327"),
},
},
/*
* All BIOS versions for the Asus M3A support 64bit DMA.
* (all release versions from 0301 to 1206 were tested)
*/
{
.ident = "ASUS M3A",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR,
"ASUSTeK Computer INC."),
DMI_MATCH(DMI_BOARD_NAME, "M3A"),
},
},
{ }
};
const struct dmi_system_id *match;
int year, month, date;
char buf[9];
match = dmi_first_match(sysids);
if (pdev->bus->number != 0 || pdev->devfn != PCI_DEVFN(0x12, 0) ||
!match)
return false;
if (!match->driver_data)
goto enable_64bit;
dmi_get_date(DMI_BIOS_DATE, &year, &month, &date);
snprintf(buf, sizeof(buf), "%04d%02d%02d", year, month, date);
if (strcmp(buf, match->driver_data) >= 0)
goto enable_64bit;
else {
dev_warn(&pdev->dev,
"%s: BIOS too old, forcing 32bit DMA, update BIOS\n",
match->ident);
return false;
}
enable_64bit:
dev_warn(&pdev->dev, "%s: enabling 64bit DMA\n", match->ident);
return true;
}
static bool ahci_broken_system_poweroff(struct pci_dev *pdev)
{
static const struct dmi_system_id broken_systems[] = {
{
.ident = "HP Compaq nx6310",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx6310"),
},
/* PCI slot number of the controller */
.driver_data = (void *)0x1FUL,
},
{
.ident = "HP Compaq 6720s",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq 6720s"),
},
/* PCI slot number of the controller */
.driver_data = (void *)0x1FUL,
},
{ } /* terminate list */
};
const struct dmi_system_id *dmi = dmi_first_match(broken_systems);
if (dmi) {
unsigned long slot = (unsigned long)dmi->driver_data;
/* apply the quirk only to on-board controllers */
return slot == PCI_SLOT(pdev->devfn);
}
return false;
}
static bool ahci_broken_suspend(struct pci_dev *pdev)
{
static const struct dmi_system_id sysids[] = {
/*
* On HP dv[4-6] and HDX18 with earlier BIOSen, link
* to the harddisk doesn't become online after
* resuming from STR. Warn and fail suspend.
*
* http://bugzilla.kernel.org/show_bug.cgi?id=12276
*
* Use dates instead of versions to match as HP is
* apparently recycling both product and version
* strings.
*
* http://bugzilla.kernel.org/show_bug.cgi?id=15462
*/
{
.ident = "dv4",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME,
"HP Pavilion dv4 Notebook PC"),
},
.driver_data = "20090105", /* F.30 */
},
{
.ident = "dv5",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME,
"HP Pavilion dv5 Notebook PC"),
},
.driver_data = "20090506", /* F.16 */
},
{
.ident = "dv6",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME,
"HP Pavilion dv6 Notebook PC"),
},
.driver_data = "20090423", /* F.21 */
},
{
.ident = "HDX18",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME,
"HP HDX18 Notebook PC"),
},
.driver_data = "20090430", /* F.23 */
},
/*
* Acer eMachines G725 has the same problem. BIOS
* V1.03 is known to be broken. V3.04 is known to
* work. Between, there are V1.06, V2.06 and V3.03
* that we don't have much idea about. For now,
* blacklist anything older than V3.04.
*
* http://bugzilla.kernel.org/show_bug.cgi?id=15104
*/
{
.ident = "G725",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "eMachines"),
DMI_MATCH(DMI_PRODUCT_NAME, "eMachines G725"),
},
.driver_data = "20091216", /* V3.04 */
},
{ } /* terminate list */
};
const struct dmi_system_id *dmi = dmi_first_match(sysids);
int year, month, date;
char buf[9];
if (!dmi || pdev->bus->number || pdev->devfn != PCI_DEVFN(0x1f, 2))
return false;
dmi_get_date(DMI_BIOS_DATE, &year, &month, &date);
snprintf(buf, sizeof(buf), "%04d%02d%02d", year, month, date);
return strcmp(buf, dmi->driver_data) < 0;
}
static bool ahci_broken_online(struct pci_dev *pdev)
{
#define ENCODE_BUSDEVFN(bus, slot, func) \
(void *)(unsigned long)(((bus) << 8) | PCI_DEVFN((slot), (func)))
static const struct dmi_system_id sysids[] = {
/*
* There are several gigabyte boards which use
* SIMG5723s configured as hardware RAID. Certain
* 5723 firmware revisions shipped there keep the link
* online but fail to answer properly to SRST or
* IDENTIFY when no device is attached downstream
* causing libata to retry quite a few times leading
* to excessive detection delay.
*
* As these firmwares respond to the second reset try
* with invalid device signature, considering unknown
* sig as offline works around the problem acceptably.
*/
{
.ident = "EP45-DQ6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR,
"Gigabyte Technology Co., Ltd."),
DMI_MATCH(DMI_BOARD_NAME, "EP45-DQ6"),
},
.driver_data = ENCODE_BUSDEVFN(0x0a, 0x00, 0),
},
{
.ident = "EP45-DS5",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR,
"Gigabyte Technology Co., Ltd."),
DMI_MATCH(DMI_BOARD_NAME, "EP45-DS5"),
},
.driver_data = ENCODE_BUSDEVFN(0x03, 0x00, 0),
},
{ } /* terminate list */
};
#undef ENCODE_BUSDEVFN
const struct dmi_system_id *dmi = dmi_first_match(sysids);
unsigned int val;
if (!dmi)
return false;
val = (unsigned long)dmi->driver_data;
return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff);
}
static bool ahci_broken_devslp(struct pci_dev *pdev)
{
/* device with broken DEVSLP but still showing SDS capability */
static const struct pci_device_id ids[] = {
{ PCI_VDEVICE(INTEL, 0x0f23)}, /* Valleyview SoC */
{}
};
return pci_match_id(ids, pdev);
}
#ifdef CONFIG_ATA_ACPI
static void ahci_gtf_filter_workaround(struct ata_host *host)
{
static const struct dmi_system_id sysids[] = {
/*
* Aspire 3810T issues a bunch of SATA enable commands
* via _GTF including an invalid one and one which is
* rejected by the device. Among the successful ones
* is FPDMA non-zero offset enable which when enabled
* only on the drive side leads to NCQ command
* failures. Filter it out.
*/
{
.ident = "Aspire 3810T",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3810T"),
},
.driver_data = (void *)ATA_ACPI_FILTER_FPDMA_OFFSET,
},
{ }
};
const struct dmi_system_id *dmi = dmi_first_match(sysids);
unsigned int filter;
int i;
if (!dmi)
return;
filter = (unsigned long)dmi->driver_data;
dev_info(host->dev, "applying extra ACPI _GTF filter 0x%x for %s\n",
filter, dmi->ident);
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
struct ata_link *link;
struct ata_device *dev;
ata_for_each_link(link, ap, EDGE)
ata_for_each_dev(dev, link, ALL)
dev->gtf_filter |= filter;
}
}
#else
static inline void ahci_gtf_filter_workaround(struct ata_host *host)
{}
#endif
static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
struct ahci_host_priv *hpriv)
{
int rc, nvec;
if (hpriv->flags & AHCI_HFLAG_NO_MSI)
goto intx;
nvec = pci_msi_vec_count(pdev);
if (nvec < 0)
goto intx;
/*
* If number of MSIs is less than number of ports then Sharing Last
* Message mode could be enforced. In this case assume that advantage
* of multipe MSIs is negated and use single MSI mode instead.
*/
if (nvec < n_ports)
goto single_msi;
rc = pci_enable_msi_exact(pdev, nvec);
if (rc == -ENOSPC)
goto single_msi;
else if (rc < 0)
goto intx;
/* fallback to single MSI mode if the controller enforced MRSM mode */
if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) {
pci_disable_msi(pdev);
printk(KERN_INFO "ahci: MRSM is on, fallback to single MSI\n");
goto single_msi;
}
if (nvec > 1)
hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
return nvec;
single_msi:
if (pci_enable_msi(pdev))
goto intx;
return 1;
intx:
pci_intx(pdev, 1);
return 0;
}
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
unsigned int board_id = ent->driver_data;
struct ata_port_info pi = ahci_port_info[board_id];
const struct ata_port_info *ppi[] = { &pi, NULL };
struct device *dev = &pdev->dev;
struct ahci_host_priv *hpriv;
struct ata_host *host;
int n_ports, i, rc;
int ahci_pci_bar = AHCI_PCI_BAR_STANDARD;
VPRINTK("ENTER\n");
WARN_ON((int)ATA_MAX_QUEUE > AHCI_MAX_CMDS);
ata_print_version_once(&pdev->dev, DRV_VERSION);
/* The AHCI driver can only drive the SATA ports, the PATA driver
can drive them all so if both drivers are selected make sure
AHCI stays out of the way */
if (pdev->vendor == PCI_VENDOR_ID_MARVELL && !marvell_enable)
return -ENODEV;
/* Apple BIOS on MCP89 prevents us using AHCI */
if (is_mcp89_apple(pdev))
ahci_mcp89_apple_enable(pdev);
/* Promise's PDC42819 is a SAS/SATA controller that has an AHCI mode.
* At the moment, we can only use the AHCI mode. Let the users know
* that for SAS drives they're out of luck.
*/
if (pdev->vendor == PCI_VENDOR_ID_PROMISE)
dev_info(&pdev->dev,
"PDC42819 can only drive SATA devices with this driver\n");
/* Both Connext and Enmotus devices use non-standard BARs */
if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == 0xCC06)
ahci_pci_bar = AHCI_PCI_BAR_STA2X11;
else if (pdev->vendor == 0x1c44 && pdev->device == 0x8000)
ahci_pci_bar = AHCI_PCI_BAR_ENMOTUS;
/*
* The JMicron chip 361/363 contains one SATA controller and one
* PATA controller,for powering on these both controllers, we must
* follow the sequence one by one, otherwise one of them can not be
* powered on successfully, so here we disable the async suspend
* method for these chips.
*/
if (pdev->vendor == PCI_VENDOR_ID_JMICRON &&
(pdev->device == PCI_DEVICE_ID_JMICRON_JMB363 ||
pdev->device == PCI_DEVICE_ID_JMICRON_JMB361))
device_disable_async_suspend(&pdev->dev);
/* acquire resources */
rc = pcim_enable_device(pdev);
if (rc)
return rc;
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
(pdev->device == 0x2652 || pdev->device == 0x2653)) {
u8 map;
/* ICH6s share the same PCI ID for both piix and ahci
* modes. Enabling ahci mode while MAP indicates
* combined mode is a bad idea. Yield to ata_piix.
*/
pci_read_config_byte(pdev, ICH_MAP, &map);
if (map & 0x3) {
dev_info(&pdev->dev,
"controller is in combined mode, can't enable AHCI mode\n");
return -ENODEV;
}
}
/* AHCI controllers often implement SFF compatible interface.
* Grab all PCI BARs just in case.
*/
rc = pcim_iomap_regions_request_all(pdev, 1 << ahci_pci_bar, DRV_NAME);
if (rc == -EBUSY)
pcim_pin_device(pdev);
if (rc)
return rc;
hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
if (!hpriv)
return -ENOMEM;
hpriv->flags |= (unsigned long)pi.private_data;
/* MCP65 revision A1 and A2 can't do MSI */
if (board_id == board_ahci_mcp65 &&
(pdev->revision == 0xa1 || pdev->revision == 0xa2))
hpriv->flags |= AHCI_HFLAG_NO_MSI;
/* SB800 does NOT need the workaround to ignore SERR_INTERNAL */
if (board_id == board_ahci_sb700 && pdev->revision >= 0x40)
hpriv->flags &= ~AHCI_HFLAG_IGN_SERR_INTERNAL;
/* only some SB600s can do 64bit DMA */
if (ahci_sb600_enable_64bit(pdev))
hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY;
hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar];
/* must set flag prior to save config in order to take effect */
if (ahci_broken_devslp(pdev))
hpriv->flags |= AHCI_HFLAG_NO_DEVSLP;
/* save initial config */
ahci_pci_save_initial_config(pdev, hpriv);
/* prepare host */
if (hpriv->cap & HOST_CAP_NCQ) {
pi.flags |= ATA_FLAG_NCQ;
/*
* Auto-activate optimization is supposed to be
* supported on all AHCI controllers indicating NCQ
* capability, but it seems to be broken on some
* chipsets including NVIDIAs.
*/
if (!(hpriv->flags & AHCI_HFLAG_NO_FPDMA_AA))
pi.flags |= ATA_FLAG_FPDMA_AA;
/*
* All AHCI controllers should be forward-compatible
* with the new auxiliary field. This code should be
* conditionalized if any buggy AHCI controllers are
* encountered.
*/
pi.flags |= ATA_FLAG_FPDMA_AUX;
}
if (hpriv->cap & HOST_CAP_PMP)
pi.flags |= ATA_FLAG_PMP;
ahci_set_em_messages(hpriv, &pi);
if (ahci_broken_system_poweroff(pdev)) {
pi.flags |= ATA_FLAG_NO_POWEROFF_SPINDOWN;
dev_info(&pdev->dev,
"quirky BIOS, skipping spindown on poweroff\n");
}
if (ahci_broken_suspend(pdev)) {
hpriv->flags |= AHCI_HFLAG_NO_SUSPEND;
dev_warn(&pdev->dev,
"BIOS update required for suspend/resume\n");
}
if (ahci_broken_online(pdev)) {
hpriv->flags |= AHCI_HFLAG_SRST_TOUT_IS_OFFLINE;
dev_info(&pdev->dev,
"online status unreliable, applying workaround\n");
}
/* CAP.NP sometimes indicate the index of the last enabled
* port, at other times, that of the last possible port, so
* determining the maximum port number requires looking at
* both CAP.NP and port_map.
*/
n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
ahci_init_interrupts(pdev, n_ports, hpriv);
host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
if (!host)
return -ENOMEM;
host->private_data = hpriv;
if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
host->flags |= ATA_HOST_PARALLEL_SCAN;
else
dev_info(&pdev->dev, "SSS flag set, parallel bus scan disabled\n");
if (pi.flags & ATA_FLAG_EM)
ahci_reset_em(host);
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
ata_port_pbar_desc(ap, ahci_pci_bar, -1, "abar");
ata_port_pbar_desc(ap, ahci_pci_bar,
0x100 + ap->port_no * 0x80, "port");
/* set enclosure management message type */
if (ap->flags & ATA_FLAG_EM)
ap->em_message_type = hpriv->em_msg_type;
/* disabled/not-implemented port */
if (!(hpriv->port_map & (1 << i)))
ap->ops = &ata_dummy_port_ops;
}
/* apply workaround for ASUS P5W DH Deluxe mainboard */
ahci_p5wdh_workaround(host);
/* apply gtf filter quirk */
ahci_gtf_filter_workaround(host);
/* initialize adapter */
rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64);
if (rc)
return rc;
rc = ahci_pci_reset_controller(host);
if (rc)
return rc;
ahci_pci_init_controller(host);
ahci_pci_print_info(host);
pci_set_master(pdev);
return ahci_host_activate(host, pdev->irq, &ahci_sht);
}
module_pci_driver(ahci_pci_driver);
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("AHCI SATA low-level driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, ahci_pci_tbl);
MODULE_VERSION(DRV_VERSION);
......@@ -24,7 +24,6 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include "ahci.h"
#include "libahci_debug.h"
#define DRV_NAME "elphel-ahci"
......@@ -32,10 +31,6 @@
#define PROP_NAME_CLB_OFFS "clb_offs"
#define PROP_NAME_FB_OFFS "fb_offs"
bool crash_drv = false;
module_param(crash_drv, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(sh_drv, "Set or reset crash driver flag");
static struct ata_port_operations ahci_elphel_ops;
static const struct ata_port_info ahci_elphel_port_info;
static struct scsi_host_template ahci_platform_sht;
......@@ -58,13 +53,7 @@ static int elphel_port_start(struct ata_port *ap)
struct ahci_host_priv *hpriv = ap->host->private_data;
const struct elphel_ahci_priv *dpriv = hpriv->plat_data;
libahci_debug_init(ap->host);
libahci_debug_crash_set(crash_drv);
dev_dbg(dev, "starting port %d", ap->port_no);
#ifdef DEBUG_EVENT_ELPHEL
libahci_debug_wait_flag();
#endif
pp = devm_kzalloc(dev, sizeof(struct ahci_port_priv), GFP_KERNEL);
if (!pp)
return -ENOMEM;
......@@ -94,10 +83,6 @@ static int elphel_port_start(struct ata_port *ap)
ap->private_data = pp;
dev_dbg(dev, "flags (ATA_FLAG_xxx): %lu", ap->flags);
dev_dbg(dev, "pflags (ATA_PFLAG_xxx): %u", ap->pflags);
dev_dbg(dev, "ahci_elphel.c: Calling ahci_port_resume()");
return ahci_port_resume(ap);
}
......@@ -167,17 +152,6 @@ static int elphel_drv_probe(struct platform_device *pdev)
hpriv->plat_data = dpriv;
reg_val = readl(hpriv->mmio + HOST_CAP);
dev_dbg(dev, "HOST CAP register: 0x%08x", reg_val);
reg_val = readl(hpriv->mmio + HOST_CTL);
dev_dbg(dev, "HOST GHC register: 0x%08x", reg_val);
reg_val = readl(hpriv->mmio + HOST_IRQ_STAT);
dev_dbg(dev, "HOST IS register: 0x%08x", reg_val);
reg_val = readl(hpriv->mmio + HOST_PORTS_IMPL);
dev_dbg(dev, "HOST PI register: 0x%08x", reg_val);
reg_val = readl(hpriv->mmio + HOST_VERSION);
dev_dbg(dev, "HOST VS register: 0x%08x", reg_val);
ret = ahci_platform_init_host(pdev, hpriv, &ahci_elphel_port_info,
&ahci_platform_sht);
if (ret) {
......@@ -193,7 +167,6 @@ static int elphel_drv_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "removing Elphel AHCI driver");
ata_platform_remove_one(pdev);
libahci_debug_exit();
return 0;
}
......@@ -210,6 +183,9 @@ static void elphel_qc_prep(struct ata_queued_cmd *qc)
struct scatterlist *sg;
struct ahci_sg *ahci_sg;
/* There is only one slot in controller thus we need to change tag*/
qc->tag = 0;
/*
* Fill in command table information. First, the header,
* a SATA Register - Host to Device command FIS.
......
/*
* libahci.c - Common AHCI SATA low-level routines
*
* Maintained by: Tejun Heo <tj@kernel.org>
* 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/gfp.h>
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
#include "ahci.h"
#include "libahci_debug.h"
#include "libata.h"
static int ahci_skip_host_reset;
int ahci_ignore_sss;
EXPORT_SYMBOL_GPL(ahci_ignore_sss);
module_param_named(skip_host_reset, ahci_skip_host_reset, int, 0444);
MODULE_PARM_DESC(skip_host_reset, "skip global host reset (0=don't skip, 1=skip)");
module_param_named(ignore_sss, ahci_ignore_sss, int, 0444);
MODULE_PARM_DESC(ignore_sss, "Ignore staggered spinup flag (0=don't ignore, 1=ignore)");
static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
unsigned hints);
static ssize_t ahci_led_show(struct ata_port *ap, char *buf);
static ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
size_t size);
static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
ssize_t size);
static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
static int ahci_port_start(struct ata_port *ap);
static void ahci_port_stop(struct ata_port *ap);
static void ahci_qc_prep(struct ata_queued_cmd *qc);
static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc);
static void ahci_freeze(struct ata_port *ap);
static void ahci_thaw(struct ata_port *ap);
static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep);
static void ahci_enable_fbs(struct ata_port *ap);
static void ahci_disable_fbs(struct ata_port *ap);
static void ahci_pmp_attach(struct ata_port *ap);
static void ahci_pmp_detach(struct ata_port *ap);
static int ahci_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static int ahci_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static void ahci_postreset(struct ata_link *link, unsigned int *class);
static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
static void ahci_dev_config(struct ata_device *dev);
#ifdef CONFIG_PM
static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg);
#endif
static ssize_t ahci_activity_show(struct ata_device *dev, char *buf);
static ssize_t ahci_activity_store(struct ata_device *dev,
enum sw_activity val);
static void ahci_init_sw_activity(struct ata_link *link);
static ssize_t ahci_show_host_caps(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t ahci_show_host_cap2(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t ahci_show_host_version(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t ahci_show_port_cmd(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t ahci_read_em_buffer(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t ahci_store_em_buffer(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size);
static ssize_t ahci_show_em_supported(struct device *dev,
struct device_attribute *attr, char *buf);
static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL);
static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL);
static DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL);
static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL);
static DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO,
ahci_read_em_buffer, ahci_store_em_buffer);
static DEVICE_ATTR(em_message_supported, S_IRUGO, ahci_show_em_supported, NULL);
struct device_attribute *ahci_shost_attrs[] = {
&dev_attr_link_power_management_policy,
&dev_attr_em_message_type,
&dev_attr_em_message,
&dev_attr_ahci_host_caps,
&dev_attr_ahci_host_cap2,
&dev_attr_ahci_host_version,
&dev_attr_ahci_port_cmd,
&dev_attr_em_buffer,
&dev_attr_em_message_supported,
NULL
};
EXPORT_SYMBOL_GPL(ahci_shost_attrs);
struct device_attribute *ahci_sdev_attrs[] = {
&dev_attr_sw_activity,
&dev_attr_unload_heads,
NULL
};
EXPORT_SYMBOL_GPL(ahci_sdev_attrs);
struct ata_port_operations ahci_ops = {
.inherits = &sata_pmp_port_ops,
.qc_defer = ahci_pmp_qc_defer,
.qc_prep = ahci_qc_prep,
.qc_issue = ahci_qc_issue,
.qc_fill_rtf = ahci_qc_fill_rtf,
.freeze = ahci_freeze,
.thaw = ahci_thaw,
.softreset = ahci_softreset,
.hardreset = ahci_hardreset,
.postreset = ahci_postreset,
.pmp_softreset = ahci_softreset,
.error_handler = ahci_error_handler,
.post_internal_cmd = ahci_post_internal_cmd,
.dev_config = ahci_dev_config,
.scr_read = ahci_scr_read,
.scr_write = ahci_scr_write,
.pmp_attach = ahci_pmp_attach,
.pmp_detach = ahci_pmp_detach,
.set_lpm = ahci_set_lpm,
.em_show = ahci_led_show,
.em_store = ahci_led_store,
.sw_activity_show = ahci_activity_show,
.sw_activity_store = ahci_activity_store,
.transmit_led_message = ahci_transmit_led_message,
#ifdef CONFIG_PM
.port_suspend = ahci_port_suspend,
.port_resume = ahci_port_resume,
#endif
.port_start = ahci_port_start,
.port_stop = ahci_port_stop,
};
EXPORT_SYMBOL_GPL(ahci_ops);
struct ata_port_operations ahci_pmp_retry_srst_ops = {
.inherits = &ahci_ops,
.softreset = ahci_pmp_retry_softreset,
};
EXPORT_SYMBOL_GPL(ahci_pmp_retry_srst_ops);
static bool ahci_em_messages __read_mostly = true;
EXPORT_SYMBOL_GPL(ahci_em_messages);
module_param(ahci_em_messages, bool, 0444);
/* add other LED protocol types when they become supported */
MODULE_PARM_DESC(ahci_em_messages,
"AHCI Enclosure Management Message control (0 = off, 1 = on)");
/* device sleep idle timeout in ms */
static int devslp_idle_timeout __read_mostly = 1000;
module_param(devslp_idle_timeout, int, 0644);
MODULE_PARM_DESC(devslp_idle_timeout, "device sleep idle timeout");
static void ahci_enable_ahci(void __iomem *mmio)
{
int i;
u32 tmp;
#ifdef DEBUG_EVENT_ELPHEL
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
int len;
#endif
/* turn on AHCI_EN */
tmp = readl(mmio + HOST_CTL);
if (tmp & HOST_AHCI_EN) {// MSB (0x80000000) is already set (RO in x393_sata)
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_enable_ahci() - already enabled");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
msg_str = NULL;
}
#endif
return;
}
/* Some controllers need AHCI_EN to be written multiple times.
* Try a few times before giving up.
*/
for (i = 0; i < 5; i++) {
tmp |= HOST_AHCI_EN;
writel(tmp, mmio + HOST_CTL);
tmp = readl(mmio + HOST_CTL); /* flush && sanity check */
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "write HOST_AHCI_EN to HOST_CTL register");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
msg_str = NULL;
}
#endif
if (tmp & HOST_AHCI_EN)
return;
msleep(10);
}
WARN_ON(1);
}
static ssize_t ahci_show_host_caps(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
struct ahci_host_priv *hpriv = ap->host->private_data;
return sprintf(buf, "%x\n", hpriv->cap);
}
static ssize_t ahci_show_host_cap2(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
struct ahci_host_priv *hpriv = ap->host->private_data;
return sprintf(buf, "%x\n", hpriv->cap2);
}
static ssize_t ahci_show_host_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *mmio = hpriv->mmio;
return sprintf(buf, "%x\n", readl(mmio + HOST_VERSION));
}
static ssize_t ahci_show_port_cmd(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
void __iomem *port_mmio = ahci_port_base(ap);
return sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD));
}
static ssize_t ahci_read_em_buffer(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *mmio = hpriv->mmio;
void __iomem *em_mmio = mmio + hpriv->em_loc;
u32 em_ctl, msg;
unsigned long flags;
size_t count;
int i;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "read EM buffer");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
spin_lock_irqsave(ap->lock, flags);
em_ctl = readl(mmio + HOST_EM_CTL);
if (!(ap->flags & ATA_FLAG_EM) || em_ctl & EM_CTL_XMT ||
!(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO)) {
spin_unlock_irqrestore(ap->lock, flags);
return -EINVAL;
}
if (!(em_ctl & EM_CTL_MR)) {
spin_unlock_irqrestore(ap->lock, flags);
return -EAGAIN;
}
if (!(em_ctl & EM_CTL_SMB))
em_mmio += hpriv->em_buf_sz;
count = hpriv->em_buf_sz;
/* the count should not be larger than PAGE_SIZE */
if (count > PAGE_SIZE) {
if (printk_ratelimit())
ata_port_warn(ap,
"EM read buffer size too large: "
"buffer size %u, page size %lu\n",
hpriv->em_buf_sz, PAGE_SIZE);
count = PAGE_SIZE;
}
for (i = 0; i < count; i += 4) {
msg = readl(em_mmio + i);
buf[i] = msg & 0xff;
buf[i + 1] = (msg >> 8) & 0xff;
buf[i + 2] = (msg >> 16) & 0xff;
buf[i + 3] = (msg >> 24) & 0xff;
}
spin_unlock_irqrestore(ap->lock, flags);
return i;
}
static ssize_t ahci_store_em_buffer(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *mmio = hpriv->mmio;
void __iomem *em_mmio = mmio + hpriv->em_loc;
const unsigned char *msg_buf = buf;
u32 em_ctl, msg;
unsigned long flags;
int i;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "transmit message in EM buffer");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* check size validity */
if (!(ap->flags & ATA_FLAG_EM) ||
!(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO) ||
size % 4 || size > hpriv->em_buf_sz)
return -EINVAL;
spin_lock_irqsave(ap->lock, flags);
em_ctl = readl(mmio + HOST_EM_CTL);
if (em_ctl & EM_CTL_TM) {
spin_unlock_irqrestore(ap->lock, flags);
return -EBUSY;
}
for (i = 0; i < size; i += 4) {
msg = msg_buf[i] | msg_buf[i + 1] << 8 |
msg_buf[i + 2] << 16 | msg_buf[i + 3] << 24;
writel(msg, em_mmio + i);
}
writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL);
spin_unlock_irqrestore(ap->lock, flags);
return size;
}
static ssize_t ahci_show_em_supported(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *mmio = hpriv->mmio;
u32 em_ctl;
em_ctl = readl(mmio + HOST_EM_CTL);
return sprintf(buf, "%s%s%s%s\n",
em_ctl & EM_CTL_LED ? "led " : "",
em_ctl & EM_CTL_SAFTE ? "saf-te " : "",
em_ctl & EM_CTL_SES ? "ses-2 " : "",
em_ctl & EM_CTL_SGPIO ? "sgpio " : "");
}
/**
* ahci_save_initial_config - Save and fixup initial config values
* @dev: target AHCI device
* @hpriv: host private area to store config values
*
* Some registers containing configuration info might be setup by
* BIOS and might be cleared on reset. This function saves the
* initial values of those registers into @hpriv such that they
* can be restored after controller reset.
*
* If inconsistent, config values are fixed up by this function.
*
* If it is not set already this function sets hpriv->start_engine to
* ahci_start_engine.
*
* LOCKING:
* None.
*/
void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
{
void __iomem *mmio = hpriv->mmio;
u32 cap, cap2, vers, port_map;
int i;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "read and save host capabilities");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
#endif
/* make sure AHCI mode is enabled before accessing CAP */
ahci_enable_ahci(mmio);
/* Values prefixed with saved_ are written back to host after
* reset. Values without are used for driver operation.
*/
hpriv->saved_cap = cap = readl(mmio + HOST_CAP);
hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL);
/* CAP2 register is only defined for AHCI 1.2 and later */
vers = readl(mmio + HOST_VERSION);
if ((vers >> 16) > 1 ||
((vers >> 16) == 1 && (vers & 0xFFFF) >= 0x200))
hpriv->saved_cap2 = cap2 = readl(mmio + HOST_CAP2);
else
hpriv->saved_cap2 = cap2 = 0;
/* some chips have errata preventing 64bit use */
if ((cap & HOST_CAP_64) && (hpriv->flags & AHCI_HFLAG_32BIT_ONLY)) {
dev_info(dev, "controller can't do 64bit DMA, forcing 32bit\n");
cap &= ~HOST_CAP_64;
}
if ((cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_NO_NCQ)) {
dev_info(dev, "controller can't do NCQ, turning off CAP_NCQ\n");
cap &= ~HOST_CAP_NCQ;
}
if (!(cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_YES_NCQ)) {
dev_info(dev, "controller can do NCQ, turning on CAP_NCQ\n");
cap |= HOST_CAP_NCQ;
}
if ((cap & HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) {
dev_info(dev, "controller can't do PMP, turning off CAP_PMP\n");
cap &= ~HOST_CAP_PMP;
}
if ((cap & HOST_CAP_SNTF) && (hpriv->flags & AHCI_HFLAG_NO_SNTF)) {
dev_info(dev,
"controller can't do SNTF, turning off CAP_SNTF\n");
cap &= ~HOST_CAP_SNTF;
}
if ((cap2 & HOST_CAP2_SDS) && (hpriv->flags & AHCI_HFLAG_NO_DEVSLP)) {
dev_info(dev,
"controller can't do DEVSLP, turning off\n");
cap2 &= ~HOST_CAP2_SDS;
cap2 &= ~HOST_CAP2_SADM;
}
if (!(cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_YES_FBS)) {
dev_info(dev, "controller can do FBS, turning on CAP_FBS\n");
cap |= HOST_CAP_FBS;
}
if ((cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_NO_FBS)) {
dev_info(dev, "controller can't do FBS, turning off CAP_FBS\n");
cap &= ~HOST_CAP_FBS;
}
if (hpriv->force_port_map && port_map != hpriv->force_port_map) {
dev_info(dev, "forcing port_map 0x%x -> 0x%x\n",
port_map, hpriv->force_port_map);
port_map = hpriv->force_port_map;
}
if (hpriv->mask_port_map) {
dev_warn(dev, "masking port_map 0x%x -> 0x%x\n",
port_map,
port_map & hpriv->mask_port_map);
port_map &= hpriv->mask_port_map;
}
/* cross check port_map and cap.n_ports */
if (port_map) {
int map_ports = 0;
for (i = 0; i < AHCI_MAX_PORTS; i++)
if (port_map & (1 << i))
map_ports++;
/* If PI has more ports than n_ports, whine, clear
* port_map and let it be generated from n_ports.
*/
if (map_ports > ahci_nr_ports(cap)) {
dev_warn(dev,
"implemented port map (0x%x) contains more ports than nr_ports (%u), using nr_ports\n",
port_map, ahci_nr_ports(cap));
port_map = 0;
}
}
/* fabricate port_map from cap.nr_ports */
if (!port_map) {
port_map = (1 << ahci_nr_ports(cap)) - 1;
dev_warn(dev, "forcing PORTS_IMPL to 0x%x\n", port_map);
/* write the fixed up value to the PI register */
hpriv->saved_port_map = port_map;
}
/* record values to use during operation */
hpriv->cap = cap;
hpriv->cap2 = cap2;
hpriv->port_map = port_map;
if (!hpriv->start_engine)
hpriv->start_engine = ahci_start_engine;
dev_warn(dev, "ahci_save_initial_config() done\n");
//ahci_save_initial_config
}
EXPORT_SYMBOL_GPL(ahci_save_initial_config);
/**
* ahci_restore_initial_config - Restore initial config
* @host: target ATA host
*
* Restore initial config stored by ahci_save_initial_config().
*
* LOCKING:
* None.
*/
static void ahci_restore_initial_config(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "restore host capabilities");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
#endif
writel(hpriv->saved_cap, mmio + HOST_CAP);
if (hpriv->saved_cap2)
writel(hpriv->saved_cap2, mmio + HOST_CAP2);
writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL);
(void) readl(mmio + HOST_PORTS_IMPL); /* flush */
}
static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg)
{
static const int offset[] = {
[SCR_STATUS] = PORT_SCR_STAT, // 0 -> 0x28
[SCR_CONTROL] = PORT_SCR_CTL, // 2 -> 0x2c
[SCR_ERROR] = PORT_SCR_ERR, // 1 -> 0x30
[SCR_ACTIVE] = PORT_SCR_ACT, // 3 -> 0x34
[SCR_NOTIFICATION] = PORT_SCR_NTF, // 4 -> 0x3c
};
struct ahci_host_priv *hpriv = ap->host->private_data;
if (sc_reg < ARRAY_SIZE(offset) &&
(sc_reg != SCR_NOTIFICATION || (hpriv->cap & HOST_CAP_SNTF)))
return offset[sc_reg];
return 0;
}
static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val)
{
void __iomem *port_mmio = ahci_port_base(link->ap);
int offset = ahci_scr_offset(link->ap, sc_reg);
#ifdef DEBUG_EVENT_ELPHEL
int len;
u32 tmp;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_scr_read() port=%u SATA status and control registers %u", link->ap->port_no,sc_reg);
libahci_debug_event(link->ap, msg_str, len);
}
#endif
if (offset) {
*val = readl(port_mmio + offset);
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
tmp = readl(port_mmio + PORT_CMD);
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tport %u offset: 0x%x, value: 0x%x (current PxCMD=0x%08x)", link->ap->port_no, offset, *val, tmp);
libahci_debug_event(link->ap, msg_str, len);
kfree(msg_str);
#endif
return 0;
}
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL)
kfree(msg_str);
#endif
return -EINVAL;
}
static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
{
void __iomem *port_mmio = ahci_port_base(link->ap);
int offset = ahci_scr_offset(link->ap, sc_reg);
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_scr_write(): port %u SATA status and control registers", link->ap->port_no);
libahci_debug_event(link->ap, msg_str, len);
}
#endif
if (offset) {
writel(val, port_mmio + offset);
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tport %u offset: 0x%x, value: 0x%x", link->ap->port_no, offset, val);
libahci_debug_event(link->ap, msg_str, len);
kfree(msg_str);
#endif
return 0;
}
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL)
kfree(msg_str);
#endif
return -EINVAL;
}
void ahci_start_engine(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_start_engine() port %u set PxCMD.ST", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
#endif
dev_dbg(ap->host->dev, "ahci_start_engine()\n");
/* start DMA */
tmp = readl(port_mmio + PORT_CMD);
tmp |= PORT_CMD_START;
writel(tmp, port_mmio + PORT_CMD);
readl(port_mmio + PORT_CMD); /* flush */
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\twrite to port %u register PxCMD, value: 0x%08x", ap->port_no, tmp);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
}
EXPORT_SYMBOL_GPL(ahci_start_engine);
int ahci_stop_engine(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_stop_engine(): clear PxCMD.ST port %u command list DMA engine", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
#endif
tmp = readl(port_mmio + PORT_CMD);
/* check if the HBA is idle */
if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0){
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tDMA was not running, no need to stop: (PxCMD=0x%08x)", tmp);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
return 0;
}
/* setting HBA to idle */
tmp &= ~PORT_CMD_START;
writel(tmp, port_mmio + PORT_CMD);
/* wait for engine to stop. This could be as long as 500 msec */
tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
if (tmp & PORT_CMD_LIST_ON) {
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\t FAILED waiting PORT_CMD_LIST_ON to clear, PxCMD, value: 0x%08x", tmp);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
return -EIO;
}
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\t SUCCESS waiting PORT_CMD_LIST_ON to clear, PxCMD, value: 0x%08x", tmp);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
return 0;
}
EXPORT_SYMBOL_GPL(ahci_stop_engine);
void ahci_start_fis_rx(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
u32 tmp;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_start_fis_rx() port %u FIS RX reception (set PxCMD.FRE)", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* set FIS registers */
if (hpriv->cap & HOST_CAP_64)
writel((pp->cmd_slot_dma >> 16) >> 16,
port_mmio + PORT_LST_ADDR_HI);
writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
if (hpriv->cap & HOST_CAP_64)
writel((pp->rx_fis_dma >> 16) >> 16,
port_mmio + PORT_FIS_ADDR_HI);
writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
/* enable FIS reception */
tmp = readl(port_mmio + PORT_CMD);
tmp |= PORT_CMD_FIS_RX;
writel(tmp, port_mmio + PORT_CMD);
/* flush */
readl(port_mmio + PORT_CMD);
}
EXPORT_SYMBOL_GPL(ahci_start_fis_rx);
static int ahci_stop_fis_rx(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_stop_fis_rx port %u FIS RX reception (clear PxCMD.FRE)", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* disable FIS reception */
tmp = readl(port_mmio + PORT_CMD);
tmp &= ~PORT_CMD_FIS_RX;
writel(tmp, port_mmio + PORT_CMD);
/* wait for completion, spec says 500ms, give it 1000 */
tmp = ata_wait_register(ap, port_mmio + PORT_CMD, PORT_CMD_FIS_ON,
PORT_CMD_FIS_ON, 10, 1000);
// Currently x393_sata receives FISes to register memory, it may become RO, so ignore
#if 0
if (tmp & PORT_CMD_FIS_ON)
return -EBUSY;
#endif
return 0;
}
static void ahci_power_up(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 cmd;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_power_up() port %u wake up link", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
/* spin up device */
if (hpriv->cap & HOST_CAP_SSS) {
cmd |= PORT_CMD_SPIN_UP;
writel(cmd, port_mmio + PORT_CMD);
}
/* wake up link */
writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD);
}
static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
unsigned int hints)
{
struct ata_port *ap = link->ap;
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_set_lpm() set port %u link power management", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
if (policy != ATA_LPM_MAX_POWER) {
/*
* Disable interrupts on Phy Ready. This keeps us from
* getting woken up due to spurious phy ready
* interrupts.
*/
pp->intr_mask &= ~PORT_IRQ_PHYRDY;
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
sata_link_scr_lpm(link, policy, false);
}
if (hpriv->cap & HOST_CAP_ALPM) {
u32 cmd = readl(port_mmio + PORT_CMD);
if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) {
cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE);
cmd |= PORT_CMD_ICC_ACTIVE;
writel(cmd, port_mmio + PORT_CMD);
readl(port_mmio + PORT_CMD);
/* wait 10ms to be sure we've come out of LPM state */
ata_msleep(ap, 10);
} else {
cmd |= PORT_CMD_ALPE;
if (policy == ATA_LPM_MIN_POWER)
cmd |= PORT_CMD_ASP;
/* write out new cmd value */
writel(cmd, port_mmio + PORT_CMD);
}
}
/* set aggressive device sleep */
if ((hpriv->cap2 & HOST_CAP2_SDS) &&
(hpriv->cap2 & HOST_CAP2_SADM) &&
(link->device->flags & ATA_DFLAG_DEVSLP)) {
if (policy == ATA_LPM_MIN_POWER)
ahci_set_aggressive_devslp(ap, true);
else
ahci_set_aggressive_devslp(ap, false);
}
if (policy == ATA_LPM_MAX_POWER) {
sata_link_scr_lpm(link, policy, false);
/* turn PHYRDY IRQ back on */
pp->intr_mask |= PORT_IRQ_PHYRDY;
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}
return 0;
}
#ifdef CONFIG_PM
static void ahci_power_down(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 cmd, scontrol;
if (!(hpriv->cap & HOST_CAP_SSS))
return;
/* put device into listen mode, first set PxSCTL.DET to 0 */
scontrol = readl(port_mmio + PORT_SCR_CTL);
scontrol &= ~0xf;
writel(scontrol, port_mmio + PORT_SCR_CTL);
/* then set PxCMD.SUD to 0 */
cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
cmd &= ~PORT_CMD_SPIN_UP;
writel(cmd, port_mmio + PORT_CMD);
}
#endif
static void ahci_start_port(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
struct ata_link *link;
struct ahci_em_priv *emp;
ssize_t rc;
int i;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_start_port(): port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* enable FIS reception */
ahci_start_fis_rx(ap);
/* enable DMA */
if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE))
hpriv->start_engine(ap);
/* turn on LEDs */
if (ap->flags & ATA_FLAG_EM) {
ata_for_each_link(link, ap, EDGE) {
emp = &pp->em_priv[link->pmp];
/* EM Transmit bit maybe busy during init */
for (i = 0; i < EM_MAX_RETRY; i++) {
rc = ap->ops->transmit_led_message(ap,
emp->led_state,
4);
/*
* If busy, give a breather but do not
* release EH ownership by using msleep()
* instead of ata_msleep(). EM Transmit
* bit is busy for the whole host and
* releasing ownership will cause other
* ports to fail the same way.
*/
if (rc == -EBUSY)
msleep(1);
else
break;
}
}
}
if (ap->flags & ATA_FLAG_SW_ACTIVITY)
ata_for_each_link(link, ap, EDGE)
ahci_init_sw_activity(link);
}
static int ahci_deinit_port(struct ata_port *ap, const char **emsg)
{
int rc;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_deinit_port():port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* disable DMA */
rc = ahci_stop_engine(ap);
if (rc) {
*emsg = "failed to stop engine";
return rc;
}
/* disable FIS reception */
rc = ahci_stop_fis_rx(ap);
if (rc) {
*emsg = "failed stop FIS RX";
return rc;
}
return 0;
}
int ahci_reset_controller(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
u32 tmp;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_reset_controller()");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
#endif
/* we must be in AHCI mode, before using anything
* AHCI-specific, such as HOST_RESET.
*/
ahci_enable_ahci(mmio);
/* global controller reset */
if (!ahci_skip_host_reset) {
tmp = readl(mmio + HOST_CTL);
if ((tmp & HOST_RESET) == 0) {
writel(tmp | HOST_RESET, mmio + HOST_CTL);
readl(mmio + HOST_CTL); /* flush */
}
/*
* to perform host reset, OS should set HOST_RESET
* and poll until this bit is read to be "0".
* reset must complete within 1 second, or
* the hardware should be considered fried.
*/
tmp = ata_wait_register(NULL, mmio + HOST_CTL, HOST_RESET,
HOST_RESET, 10, 1000);
if (tmp & HOST_RESET) {
dev_err(host->dev, "controller reset failed (0x%x)\n",
tmp);
return -EIO;
}
/* turn on AHCI mode */
ahci_enable_ahci(mmio);
/* Some registers might be cleared on reset. Restore
* initial values.
*/
ahci_restore_initial_config(host);
} else
dev_info(host->dev, "skipping global host reset\n");
return 0;
}
EXPORT_SYMBOL_GPL(ahci_reset_controller);
static void ahci_sw_activity(struct ata_link *link)
{
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_sw_activity()");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
#endif
if (!(link->flags & ATA_LFLAG_SW_ACTIVITY))
return;
emp->activity++;
if (!timer_pending(&emp->timer))
mod_timer(&emp->timer, jiffies + msecs_to_jiffies(10));
}
static void ahci_sw_activity_blink(unsigned long arg)
{
struct ata_link *link = (struct ata_link *)arg;
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
unsigned long led_message = emp->led_state;
u32 activity_led_state;
unsigned long flags;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_sw_activity_blink()");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
#endif
led_message &= EM_MSG_LED_VALUE;
led_message |= ap->port_no | (link->pmp << 8);
/* check to see if we've had activity. If so,
* toggle state of LED and reset timer. If not,
* turn LED to desired idle state.
*/
spin_lock_irqsave(ap->lock, flags);
if (emp->saved_activity != emp->activity) {
emp->saved_activity = emp->activity;
/* get the current LED state */
activity_led_state = led_message & EM_MSG_LED_VALUE_ON;
if (activity_led_state)
activity_led_state = 0;
else
activity_led_state = 1;
/* clear old state */
led_message &= ~EM_MSG_LED_VALUE_ACTIVITY;
/* toggle state */
led_message |= (activity_led_state << 16);
mod_timer(&emp->timer, jiffies + msecs_to_jiffies(100));
} else {
/* switch to idle */
led_message &= ~EM_MSG_LED_VALUE_ACTIVITY;
if (emp->blink_policy == BLINK_OFF)
led_message |= (1 << 16);
}
spin_unlock_irqrestore(ap->lock, flags);
ap->ops->transmit_led_message(ap, led_message, 4);
}
static void ahci_init_sw_activity(struct ata_link *link)
{
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_init_sw_activity()");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
#endif
/* init activity stats, setup timer */
emp->saved_activity = emp->activity = 0;
setup_timer(&emp->timer, ahci_sw_activity_blink, (unsigned long)link);
/* check our blink policy and set flag for link if it's enabled */
if (emp->blink_policy)
link->flags |= ATA_LFLAG_SW_ACTIVITY;
}
int ahci_reset_em(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
u32 em_ctl;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_reset_em(): reset EM message logic");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
#endif
em_ctl = readl(mmio + HOST_EM_CTL);
if ((em_ctl & EM_CTL_TM) || (em_ctl & EM_CTL_RST))
return -EINVAL;
writel(em_ctl | EM_CTL_RST, mmio + HOST_EM_CTL);
return 0;
}
EXPORT_SYMBOL_GPL(ahci_reset_em);
static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
ssize_t size)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
void __iomem *mmio = hpriv->mmio;
u32 em_ctl;
u32 message[] = {0, 0};
unsigned long flags;
int pmp;
struct ahci_em_priv *emp;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_transmit_led_message() send port %u LED message", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* get the slot number from the message */
pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
if (pmp < EM_MAX_SLOTS)
emp = &pp->em_priv[pmp];
else
return -EINVAL;
spin_lock_irqsave(ap->lock, flags);
/*
* if we are still busy transmitting a previous message,
* do not allow
*/
em_ctl = readl(mmio + HOST_EM_CTL);
if (em_ctl & EM_CTL_TM) {
spin_unlock_irqrestore(ap->lock, flags);
return -EBUSY;
}
if (hpriv->em_msg_type & EM_MSG_TYPE_LED) {
/*
* create message header - this is all zero except for
* the message size, which is 4 bytes.
*/
message[0] |= (4 << 8);
/* ignore 0:4 of byte zero, fill in port info yourself */
message[1] = ((state & ~EM_MSG_LED_HBA_PORT) | ap->port_no);
/* write message to EM_LOC */
writel(message[0], mmio + hpriv->em_loc);
writel(message[1], mmio + hpriv->em_loc+4);
/*
* tell hardware to transmit the message
*/
writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL);
}
/* save off new led state for port/slot */
emp->led_state = state;
spin_unlock_irqrestore(ap->lock, flags);
return size;
}
static ssize_t ahci_led_show(struct ata_port *ap, char *buf)
{
struct ahci_port_priv *pp = ap->private_data;
struct ata_link *link;
struct ahci_em_priv *emp;
int rc = 0;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_led_show()");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
ata_for_each_link(link, ap, EDGE) {
emp = &pp->em_priv[link->pmp];
rc += sprintf(buf, "%lx\n", emp->led_state);
}
return rc;
}
static ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
size_t size)
{
unsigned int state;
int pmp;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_em_priv *emp;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_led_store()");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
if (kstrtouint(buf, 0, &state) < 0)
return -EINVAL;
/* get the slot number from the message */
pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
if (pmp < EM_MAX_SLOTS)
emp = &pp->em_priv[pmp];
else
return -EINVAL;
/* mask off the activity bits if we are in sw_activity
* mode, user should turn off sw_activity before setting
* activity led through em_message
*/
if (emp->blink_policy)
state &= ~EM_MSG_LED_VALUE_ACTIVITY;
return ap->ops->transmit_led_message(ap, state, size);
}
static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val)
{
struct ata_link *link = dev->link;
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
u32 port_led_state = emp->led_state;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_activity_store()");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* save the desired Activity LED behavior */
if (val == OFF) {
/* clear LFLAG */
link->flags &= ~(ATA_LFLAG_SW_ACTIVITY);
/* set the LED to OFF */
port_led_state &= EM_MSG_LED_VALUE_OFF;
port_led_state |= (ap->port_no | (link->pmp << 8));
ap->ops->transmit_led_message(ap, port_led_state, 4);
} else {
link->flags |= ATA_LFLAG_SW_ACTIVITY;
if (val == BLINK_OFF) {
/* set LED to ON for idle */
port_led_state &= EM_MSG_LED_VALUE_OFF;
port_led_state |= (ap->port_no | (link->pmp << 8));
port_led_state |= EM_MSG_LED_VALUE_ON; /* check this */
ap->ops->transmit_led_message(ap, port_led_state, 4);
}
}
emp->blink_policy = val;
return 0;
}
static ssize_t ahci_activity_show(struct ata_device *dev, char *buf)
{
struct ata_link *link = dev->link;
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_activity_show()");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* display the saved value of activity behavior for this
* disk.
*/
return sprintf(buf, "%d\n", emp->blink_policy);
}
static void ahci_port_init(struct device *dev, struct ata_port *ap,
int port_no, void __iomem *mmio,
void __iomem *port_mmio)
{
const char *emsg = NULL;
int rc;
u32 tmp;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_port_init() port %u init", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* make sure port is not active */
rc = ahci_deinit_port(ap, &emsg);
if (rc)
dev_warn(dev, "%s (%d)\n", emsg, rc);
/* clear SError */
tmp = readl(port_mmio + PORT_SCR_ERR);
VPRINTK("PORT_SCR_ERR 0x%x\n", tmp);
writel(tmp, port_mmio + PORT_SCR_ERR);
/* clear port IRQ */
tmp = readl(port_mmio + PORT_IRQ_STAT);
VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
if (tmp)
writel(tmp, port_mmio + PORT_IRQ_STAT);
writel(1 << port_no, mmio + HOST_IRQ_STAT);
}
void ahci_init_controller(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
int i;
void __iomem *port_mmio;
u32 tmp;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_init_controller() ... and all enable ports");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
#endif
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
port_mmio = ahci_port_base(ap);
if (ata_port_is_dummy(ap))
continue;
ahci_port_init(host->dev, ap, i, mmio, port_mmio);
}
tmp = readl(mmio + HOST_CTL);
VPRINTK("HOST_CTL 0x%x\n", tmp);
writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
tmp = readl(mmio + HOST_CTL);
VPRINTK("HOST_CTL 0x%x\n", tmp);
}
EXPORT_SYMBOL_GPL(ahci_init_controller);
static void ahci_dev_config(struct ata_device *dev)
{
struct ahci_host_priv *hpriv = dev->link->ap->host->private_data;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_dev_config()");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
#endif
if (hpriv->flags & AHCI_HFLAG_SECT255) {
dev->max_sectors = 255;
ata_dev_info(dev,
"SB600 AHCI: limiting to 255 sectors per cmd\n");
}
}
unsigned int ahci_dev_classify(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ata_taskfile tf;
u32 tmp;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
#endif
tmp = readl(port_mmio + PORT_SIG);
tf.lbah = (tmp >> 24) & 0xff;
tf.lbam = (tmp >> 16) & 0xff;
tf.lbal = (tmp >> 8) & 0xff;
tf.nsect = (tmp) & 0xff;
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_dev_classify(): reading port %u signature: PxSIG = 0x%08x", ap->port_no, tmp);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
return ata_dev_classify(&tf);
}
EXPORT_SYMBOL_GPL(ahci_dev_classify);
void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,
u32 opts)
{
dma_addr_t cmd_tbl_dma;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
#endif
cmd_tbl_dma = pp->cmd_tbl_dma + tag * AHCI_CMD_TBL_SZ;
pp->cmd_slot[tag].opts = cpu_to_le32(opts);
pp->cmd_slot[tag].status = 0;
pp->cmd_slot[tag].tbl_addr = cpu_to_le32((cmd_tbl_dma & 0xffffffff)); // All data correct
pp->cmd_slot[tag].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16);
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tfill command slot %u: DW0 = 0x%08x, DW1 = 0x%08x, DW2 = 0x%08x, DW3 = 0x%08x",
tag, pp->cmd_slot[tag].opts, pp->cmd_slot[tag].status, pp->cmd_slot[tag].tbl_addr, pp->cmd_slot[tag].tbl_addr_hi);
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
#endif
}
EXPORT_SYMBOL_GPL(ahci_fill_cmd_slot);
int ahci_kick_engine(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_host_priv *hpriv = ap->host->private_data;
u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
u32 tmp;
int busy, rc;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_kick_engine() kick port %u DMA engine", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
#endif
/* stop engine */
rc = ahci_stop_engine(ap);
if (rc)
goto out_restart;
/* need to do CLO?
* always do CLO if PMP is attached (AHCI-1.3 9.2)
*/
busy = status & (ATA_BUSY | ATA_DRQ);
if (!busy && !sata_pmp_attached(ap)) {
rc = 0;
goto out_restart;
}
if (!(hpriv->cap & HOST_CAP_CLO)) { // 0x1000000 supports command list override (x393_sata - not)
rc = -EOPNOTSUPP;
goto out_restart;
}
/* perform CLO */
tmp = readl(port_mmio + PORT_CMD);
tmp |= PORT_CMD_CLO;
writel(tmp, port_mmio + PORT_CMD);
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\twrite port %u register PxCMD, value: 0x%08x", ap->port_no, tmp);
libahci_debug_event(ap, msg_str, len);
}
#endif
rc = 0;
tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
PORT_CMD_CLO, PORT_CMD_CLO, 1, 500);
if (tmp & PORT_CMD_CLO)
rc = -EIO;
/* restart engine */
out_restart:
hpriv->start_engine(ap);
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tahci_kick_engine() will return %d, busy was 0x%08x", rc, busy);
libahci_debug_event(ap, msg_str, len);
}
kfree(msg_str);
#endif
return rc;
}
EXPORT_SYMBOL_GPL(ahci_kick_engine);
static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
struct ata_taskfile *tf, int is_cmd, u16 flags,
unsigned long timeout_msec)
{
const u32 cmd_fis_len = 5; /* five dwords */
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u8 *fis = pp->cmd_tbl;
u32 tmp;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_exec_polled_cmd(), port %u command issue", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* prep the command */
ata_tf_to_fis(tf, pmp, is_cmd, fis);
ahci_fill_cmd_slot(pp, 0, cmd_fis_len | flags | (pmp << 12));
libahci_debug_dump_region(ap, (const u32 *)fis, cmd_fis_len, "\twrite H2D register FIS; dump: ");
/* issue & wait */
writel(1, port_mmio + PORT_CMD_ISSUE);
if (timeout_msec) {
tmp = ata_wait_register(ap, port_mmio + PORT_CMD_ISSUE,
0x1, 0x1, 1, timeout_msec);
if (tmp & 0x1) {
ahci_kick_engine(ap);
return -EBUSY;
}
} else
readl(port_mmio + PORT_CMD_ISSUE); /* flush */
return 0;
}
int ahci_do_softreset(struct ata_link *link, unsigned int *class,
int pmp, unsigned long deadline,
int (*check_ready)(struct ata_link *link))
{
struct ata_port *ap = link->ap;
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
const char *reason = NULL;
unsigned long now, msecs;
struct ata_taskfile tf;
bool fbs_disabled = false;
int rc;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
dev_info(ap->host->dev, "ahci_do_softreset()");
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_do_softreset(): port %u softreset (complex actions follow)", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
#endif
DPRINTK("ENTER\n");
/* prepare for SRST (AHCI-1.1 10.4.1) */
rc = ahci_kick_engine(ap);
if (rc && rc != -EOPNOTSUPP)
ata_link_warn(link, "failed to reset engine (errno=%d)\n", rc);
/*
* According to AHCI-1.2 9.3.9: if FBS is enable, software shall
* clear PxFBS.EN to '0' prior to issuing software reset to devices
* that is attached to port multiplier.
*/
if (!ata_is_host_link(link) && pp->fbs_enabled) {
ahci_disable_fbs(ap);
fbs_disabled = true;
}
ata_tf_init(link->device, &tf);
/* issue the first D2H Register FIS */
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "issue the first H2D Register FIS; set ATA_SRST bit");
libahci_debug_event(ap, msg_str, len);
}
#endif
msecs = 0;
now = jiffies;
if (time_after(deadline, now))
msecs = jiffies_to_msecs(deadline - now);
tf.ctl |= ATA_SRST;
if (ahci_exec_polled_cmd(ap, pmp, &tf, 0,
AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY, msecs)) {
rc = -EIO;
reason = "1st FIS failed";
goto fail;
}
/* spec says at least 5us, but be generous and sleep for 1ms */
ata_msleep(ap, 1);
/* issue the second D2H Register FIS */
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "issue the second H2D Register FIS; reset ATA_SRST");
libahci_debug_event(ap, msg_str, len);
}
#endif
tf.ctl &= ~ATA_SRST;
ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0);
/* wait for link to become ready */
rc = ata_wait_after_reset(link, deadline, check_ready);
if (rc == -EBUSY && hpriv->flags & AHCI_HFLAG_SRST_TOUT_IS_OFFLINE) {
/*
* Workaround for cases where link online status can't
* be trusted. Treat device readiness timeout as link
* offline.
*/
ata_link_info(link, "device not ready, treating as offline\n");
*class = ATA_DEV_NONE;
} else if (rc) {
/* link occupied, -ENODEV too is an error */
reason = "device not ready";
goto fail;
} else
*class = ahci_dev_classify(ap);
/* re-enable FBS if disabled before */
if (fbs_disabled)
ahci_enable_fbs(ap);
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
kfree(msg_str);
}
#endif
DPRINTK("EXIT, class=%u\n", *class);
return 0;
fail:
ata_link_err(link, "softreset failed (%s)\n", reason);
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tsoftreset failed, reason code: %s", reason);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
return rc;
}
int ahci_check_ready(struct ata_link *link)
{
void __iomem *port_mmio = ahci_port_base(link->ap);
u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str;
msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_check_ready(): read port %u Task File Data: PxTFD = 0x%02x, ata_check_ready()->%d", link->ap->port_no, status,ata_check_ready(status));
libahci_debug_event(link->ap, msg_str, len);
kfree(msg_str);
}
#endif
return ata_check_ready(status);
}
EXPORT_SYMBOL_GPL(ahci_check_ready);
static int ahci_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
int pmp = sata_srst_pmp(link);
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str;
msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_softreset()");
libahci_debug_event(link->ap, msg_str, len);
kfree(msg_str);
}
#endif
DPRINTK("ENTER\n");
return ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready);
}
EXPORT_SYMBOL_GPL(ahci_do_softreset);
static int ahci_bad_pmp_check_ready(struct ata_link *link)
{
void __iomem *port_mmio = ahci_port_base(link->ap);
u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
u32 irq_status = readl(port_mmio + PORT_IRQ_STAT);
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str;
msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_bad_pmp_check_ready()");
libahci_debug_event(link->ap, msg_str, len);
kfree(msg_str);
}
#endif
/*
* There is no need to check TFDATA if BAD PMP is found due to HW bug,
* which can save timeout delay.
*/
if (irq_status & PORT_IRQ_BAD_PMP)
return -EIO;
return ata_check_ready(status);
}
static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
struct ata_port *ap = link->ap;
void __iomem *port_mmio = ahci_port_base(ap);
int pmp = sata_srst_pmp(link);
int rc;
u32 irq_sts;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str;
msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_pmp_retry_softreset()");
libahci_debug_event(link->ap, msg_str, len);
kfree(msg_str);
}
#endif
DPRINTK("ENTER\n");
rc = ahci_do_softreset(link, class, pmp, deadline,
ahci_bad_pmp_check_ready);
/*
* Soft reset fails with IPMS set when PMP is enabled but
* SATA HDD/ODD is connected to SATA port, do soft reset
* again to port 0.
*/
if (rc == -EIO) {
irq_sts = readl(port_mmio + PORT_IRQ_STAT);
if (irq_sts & PORT_IRQ_BAD_PMP) {
ata_link_warn(link,
"applying PMP SRST workaround "
"and retrying\n");
rc = ahci_do_softreset(link, class, 0, deadline,
ahci_check_ready);
}
}
return rc;
}
static int ahci_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_host_priv *hpriv = ap->host->private_data;
u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
struct ata_taskfile tf;
bool online;
int rc;
dev_dbg(ap->host->dev, "ahci_hardreset()");
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_hardreset(): port %u hardreset", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
#endif
DPRINTK("ENTER\n");
ahci_stop_engine(ap);
/* clear D2H reception area to properly wait for D2H FIS */
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tclear RX FIS area");
libahci_debug_event(ap, msg_str, len);
}
#endif
ata_tf_init(link->device, &tf);
tf.command = ATA_BUSY;
ata_tf_to_fis(&tf, 0, 0, d2h_fis);
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tproceed to sata_link_hardreset");
libahci_debug_event(ap, msg_str, len);
}
#endif
rc = sata_link_hardreset(link, timing, deadline, &online,
ahci_check_ready);
hpriv->start_engine(ap);
if (online) {
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tlink is online");
libahci_debug_event(ap, msg_str, len);
}
#endif
*class = ahci_dev_classify(ap);
}
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
kfree(msg_str);
}
#endif
DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
return rc;
}
static void ahci_postreset(struct ata_link *link, unsigned int *class)
{
struct ata_port *ap = link->ap;
void __iomem *port_mmio = ahci_port_base(ap);
u32 new_tmp, tmp;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_postreset(): port %u postreset actions", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
ata_std_postreset(link, class);
/* Make sure port's ATAPI bit is set appropriately */
new_tmp = tmp = readl(port_mmio + PORT_CMD);
if (*class == ATA_DEV_ATAPI)
new_tmp |= PORT_CMD_ATAPI;
else
new_tmp &= ~PORT_CMD_ATAPI;
if (new_tmp != tmp) {
writel(new_tmp, port_mmio + PORT_CMD);
readl(port_mmio + PORT_CMD); /* flush */
}
}
static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl)
{
struct scatterlist *sg;
struct ahci_sg *ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ;
unsigned int si;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
#endif
VPRINTK("ENTER\n");
/*
* Next, the S/G list.
*/
for_each_sg(qc->sg, sg, qc->n_elem, si) {
dma_addr_t addr = sg_dma_address(sg);
u32 sg_len = sg_dma_len(sg);
ahci_sg[si].addr = cpu_to_le32(addr & 0xffffffff);
ahci_sg[si].addr_hi = cpu_to_le32((addr >> 16) >> 16);
ahci_sg[si].flags_size = cpu_to_le32(sg_len - 1);
}
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tfill S/G list for port %u: %u PRD(s) written", qc->ap->port_no, si);
libahci_debug_event(qc->ap, msg_str, len);
//libahci_debug_dump_sg(qc, "reading data pointed by S/G list; dump: ");
kfree(msg_str);
}
#endif
return si;
}
static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct ahci_port_priv *pp = ap->private_data;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_pmp_qc_defer(): process qc_defer ata command for port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
if (!sata_pmp_attached(ap) || pp->fbs_enabled)
return ata_std_qc_defer(qc);
else
return sata_pmp_qc_defer_cmd_switch(qc);
}
static void ahci_qc_prep(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct ahci_port_priv *pp = ap->private_data;
int is_atapi = ata_is_atapi(qc->tf.protocol);
void *cmd_tbl;
u32 opts;
const u32 cmd_fis_len = 5; /* five dwords */
unsigned int n_elem;
u32 * elphel_dbg_ptr;
u32 elhel_dbg_buf[128];
int elphel_i;
#ifdef DEBUG_EVENT_ELPHEL
// elphel test: set tag = 0, change to qc->tag in functions below
//unsigned int tag = 0;
/*
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_qc_prep(): prepare command table information for port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
*/
#endif
/*
* Fill in command table information. First, the header,
* a SATA Register - Host to Device command FIS.
*/
// Elphel; Need to wrap with dma_sync_single_for_cpu / dma_sync_single_for_device, move to in ahci_elphel.c
dma_sync_single_for_cpu(&qc->dev->tdev, pp->cmd_tbl_dma, AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE);
cmd_tbl = pp->cmd_tbl + qc->tag * AHCI_CMD_TBL_SZ;
ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, cmd_tbl);
if (is_atapi) {
memset(cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32);
memcpy(cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, qc->dev->cdb_len);
// libahci_debug_dump_region(ap, (const u32 *)(cmd_tbl + AHCI_CMD_TBL_CDB), 4, "\tthis is ATAPI command, dump ACMD region: ");
}
// libahci_debug_dump_region(ap, (const u32 *)cmd_tbl, cmd_fis_len, "\twrite H2D register FIS; dump: ");
n_elem = 0;
if (qc->flags & ATA_QCFLAG_DMAMAP) {
n_elem = ahci_fill_sg(qc, cmd_tbl);
if (qc->dma_dir == DMA_TO_DEVICE) {
// dev_info(&qc->dev->tdev, "%s: dma_sync_sg_for_device, qc->dma_dir: %d", __func__, qc->dma_dir);
dma_sync_sg_for_device(&qc->dev->tdev, qc->sg, qc->n_elem, qc->dma_dir);
} else if (qc->dma_dir == DMA_FROM_DEVICE) {
// dev_info(&qc->dev->tdev, "%s: dma_sync_sg_for_cpu, qc->dma_dir: %d", __func__, qc->dma_dir);
dma_sync_sg_for_cpu(&qc->dev->tdev, qc->sg, qc->n_elem, qc->dma_dir);
}
}
/*
* Fill in command slot information.
*/
opts = cmd_fis_len | n_elem << 16 | (qc->dev->link->pmp << 12);
if (qc->tf.flags & ATA_TFLAG_WRITE)
opts |= AHCI_CMD_WRITE;
if (is_atapi)
opts |= AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH;
ahci_fill_cmd_slot(pp, qc->tag, opts);
//ahci_fill_cmd_slot(pp, tag, opts);
// Elphel move to ahci_elphel.c. See if the SG dma should also be handed to dma here
dma_sync_single_for_device(&qc->dev->tdev, pp->cmd_tbl_dma, AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE);
WARN_ON (qc->tag);
}
static void ahci_fbs_dec_intr(struct ata_port *ap)
{
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 fbs = readl(port_mmio + PORT_FBS);
int retries = 3;
DPRINTK("ENTER\n");
BUG_ON(!pp->fbs_enabled);
/* time to wait for DEC is not specified by AHCI spec,
* add a retry loop for safety.
*/
writel(fbs | PORT_FBS_DEC, port_mmio + PORT_FBS);
fbs = readl(port_mmio + PORT_FBS);
while ((fbs & PORT_FBS_DEC) && retries--) {
udelay(1);
fbs = readl(port_mmio + PORT_FBS);
}
if (fbs & PORT_FBS_DEC)
dev_err(ap->host->dev, "failed to clear device error\n");
}
static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
struct ata_eh_info *host_ehi = &ap->link.eh_info;
struct ata_link *link = NULL;
struct ata_queued_cmd *active_qc;
struct ata_eh_info *active_ehi;
bool fbs_need_dec = false;
u32 serror;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
#endif
/* determine active link with error */
if (pp->fbs_enabled) {
void __iomem *port_mmio = ahci_port_base(ap);
u32 fbs = readl(port_mmio + PORT_FBS);
int pmp = fbs >> PORT_FBS_DWE_OFFSET;
if ((fbs & PORT_FBS_SDE) && (pmp < ap->nr_pmp_links)) {
link = &ap->pmp_link[pmp];
fbs_need_dec = true;
}
} else
ata_for_each_link(link, ap, EDGE)
if (ata_link_active(link))
break;
if (!link)
link = &ap->link;
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_error_intr(): handle port %u error, irq_stat=0x%08x, SERR=0x%08x",
link->ap->port_no,irq_stat, ahci_scr_read(&ap->link, SCR_ERROR, &serror));
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
active_qc = ata_qc_from_tag(ap, link->active_tag);
active_ehi = &link->eh_info;
/* record irq stat */
ata_ehi_clear_desc(host_ehi);
ata_ehi_push_desc(host_ehi, "irq_stat 0x%08x", irq_stat);
/* AHCI needs SError cleared; otherwise, it might lock up */
ahci_scr_read(&ap->link, SCR_ERROR, &serror);
ahci_scr_write(&ap->link, SCR_ERROR, serror);
host_ehi->serror |= serror;
/* some controllers set IRQ_IF_ERR on device errors, ignore it */
if (hpriv->flags & AHCI_HFLAG_IGN_IRQ_IF_ERR)
irq_stat &= ~PORT_IRQ_IF_ERR;
if (irq_stat & PORT_IRQ_TF_ERR) {
/* If qc is active, charge it; otherwise, the active
* link. There's no active qc on NCQ errors. It will
* be determined by EH by reading log page 10h.
*/
if (active_qc)
active_qc->err_mask |= AC_ERR_DEV;
else
active_ehi->err_mask |= AC_ERR_DEV;
if (hpriv->flags & AHCI_HFLAG_IGN_SERR_INTERNAL)
host_ehi->serror &= ~SERR_INTERNAL;
}
if (irq_stat & PORT_IRQ_UNK_FIS) {
u32 *unk = pp->rx_fis + RX_FIS_UNK;
active_ehi->err_mask |= AC_ERR_HSM;
active_ehi->action |= ATA_EH_RESET;
ata_ehi_push_desc(active_ehi,
"unknown FIS %08x %08x %08x %08x" ,
unk[0], unk[1], unk[2], unk[3]);
}
if (sata_pmp_attached(ap) && (irq_stat & PORT_IRQ_BAD_PMP)) {
active_ehi->err_mask |= AC_ERR_HSM;
active_ehi->action |= ATA_EH_RESET;
ata_ehi_push_desc(active_ehi, "incorrect PMP");
}
if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) {
host_ehi->err_mask |= AC_ERR_HOST_BUS;
host_ehi->action |= ATA_EH_RESET;
ata_ehi_push_desc(host_ehi, "host bus error");
}
if (irq_stat & PORT_IRQ_IF_ERR) {
if (fbs_need_dec)
active_ehi->err_mask |= AC_ERR_DEV;
else {
host_ehi->err_mask |= AC_ERR_ATA_BUS;
host_ehi->action |= ATA_EH_RESET;
}
ata_ehi_push_desc(host_ehi, "interface fatal error");
}
if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) {
ata_ehi_hotplugged(host_ehi);
ata_ehi_push_desc(host_ehi, "%s",
irq_stat & PORT_IRQ_CONNECT ?
"connection status changed" : "PHY RDY changed");
}
/* okay, let's hand over to EH */
if (irq_stat & PORT_IRQ_FREEZE) //0x28c00050
ata_port_freeze(ap);
else if (fbs_need_dec) {
ata_link_abort(link);
ahci_fbs_dec_intr(ap);
} else
ata_port_abort(ap);
}
static void ahci_handle_port_interrupt(struct ata_port *ap,
void __iomem *port_mmio, u32 status)
{
struct ata_eh_info *ehi = &ap->link.eh_info;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_host_priv *hpriv = ap->host->private_data;
int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);
u32 qc_active = 0;
int rc;
/*
int len;
char *msg = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "ahci_handle_port_interrupt(): port %u interrupt handler, PxIS: 0x%x", ap->port_no, status);
libahci_debug_event(ap, msg, len);
libahci_debug_dump_irq(status);
libahci_debug_irq_notify(ap);
}
*/
/* ignore BAD_PMP while resetting */
if (unlikely(resetting))
status &= ~PORT_IRQ_BAD_PMP;
/* if LPM is enabled, PHYRDY doesn't mean anything */
if (ap->link.lpm_policy > ATA_LPM_MAX_POWER) {
status &= ~PORT_IRQ_PHYRDY;
ahci_scr_write(&ap->link, SCR_ERROR, SERR_PHYRDY_CHG);
/*
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "resetting PORT_IRQ_PHYRDY, new PxIS: 0x%x", status);
libahci_debug_event(ap, msg, len);
}
*/
}
if (unlikely(status & PORT_IRQ_ERROR)) {
/*
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "processing PORT_IRQ_ERROR");
libahci_debug_event(ap, msg, len);
}
*/
ahci_error_intr(ap, status);
return;
}
if (status & PORT_IRQ_SDB_FIS) {
/* If SNotification is available, leave notification
* handling to sata_async_notification(). If not,
* emulate it by snooping SDB FIS RX area.
*
* Snooping FIS RX area is probably cheaper than
* poking SNotification but some constrollers which
* implement SNotification, ICH9 for example, don't
* store AN SDB FIS into receive area.
*/
/*
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "processing PORT_IRQ_SDB_FIS, further processing will be done on sata layer");
libahci_debug_event(ap, msg, len);
}
*/
if (hpriv->cap & HOST_CAP_SNTF) {
/*
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "host supports SNotification register, proceed to 'sata_async_notification'");
libahci_debug_event(ap, msg, len);
}
*/
sata_async_notification(ap);
}
else {
/* If the 'N' bit in word 0 of the FIS is set,
* we just received asynchronous notification.
* Tell libata about it.
*
* Lack of SNotification should not appear in
* ahci 1.2, so the workaround is unnecessary
* when FBS is enabled.
*/
/*
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "host DOES NOT support SNotification register, snoop FIS RX area and proceed to 'sata_async_notification'");
libahci_debug_event(ap, msg, len);
}
*/
if (pp->fbs_enabled)
WARN_ON_ONCE(1);
else {
const __le32 *f = pp->rx_fis + RX_FIS_SDB;
u32 f0 = le32_to_cpu(f[0]);
if (f0 & (1 << 15))
sata_async_notification(ap);
}
}
}
/* pp->active_link is not reliable once FBS is enabled, both
* PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because
* NCQ and non-NCQ commands may be in flight at the same time.
*/
if (pp->fbs_enabled) {
if (ap->qc_active) {
qc_active = readl(port_mmio + PORT_SCR_ACT);
qc_active |= readl(port_mmio + PORT_CMD_ISSUE);
}
} else {
/* pp->active_link is valid iff any command is in flight */
if (ap->qc_active && pp->active_link->sactive)
qc_active = readl(port_mmio + PORT_SCR_ACT);
else
qc_active = readl(port_mmio + PORT_CMD_ISSUE);
}
/*
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "read port %u Serial ATA Active register, PxSACT: 0x%x", ap->port_no, qc_active);
libahci_debug_event(ap, msg, len);
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "proceed to 'ata_qc_complete_multiple'");
libahci_debug_event(ap, msg, len);
//libahci_debug_irq_notify(ap);
kfree(msg);
}
*/
rc = ata_qc_complete_multiple(ap, qc_active);
/* while resetting, invalid completions are expected */
if (unlikely(rc < 0 && !resetting)) {
ehi->err_mask |= AC_ERR_HSM;
ehi->action |= ATA_EH_RESET;
ata_port_freeze(ap);
}
}
static void ahci_port_intr(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
u32 status;
status = readl(port_mmio + PORT_IRQ_STAT);
writel(status, port_mmio + PORT_IRQ_STAT);
ahci_handle_port_interrupt(ap, port_mmio, status);
}
static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
{
struct ata_port *ap = dev_instance;
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 status;
status = atomic_xchg(&pp->intr_status, 0);
if (!status)
return IRQ_NONE;
spin_lock_bh(ap->lock);
ahci_handle_port_interrupt(ap, port_mmio, status);
spin_unlock_bh(ap->lock);
return IRQ_HANDLED;
}
static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
{
struct ata_port *ap = dev_instance;
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_port_priv *pp = ap->private_data;
u32 status;
VPRINTK("ENTER\n");
status = readl(port_mmio + PORT_IRQ_STAT);
writel(status, port_mmio + PORT_IRQ_STAT);
atomic_or(status, &pp->intr_status);
VPRINTK("EXIT\n");
return IRQ_WAKE_THREAD;
}
static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
{
struct ata_host *host = dev_instance;
struct ahci_host_priv *hpriv;
unsigned int i, handled = 0;
void __iomem *mmio;
u32 irq_stat, irq_masked;
VPRINTK("ENTER\n");
hpriv = host->private_data;
mmio = hpriv->mmio;
/* sigh. 0xffffffff is a valid return from h/w */
irq_stat = readl(mmio + HOST_IRQ_STAT);
if (!irq_stat)
return IRQ_NONE;
irq_masked = irq_stat & hpriv->port_map;
spin_lock(&host->lock);
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap;
if (!(irq_masked & (1 << i)))
continue;
ap = host->ports[i];
if (ap) {
ahci_port_intr(ap);
VPRINTK("port %u\n", i);
} else {
VPRINTK("port %u (no irq)\n", i);
if (ata_ratelimit())
dev_warn(host->dev,
"interrupt on disabled port %u\n", i);
}
handled = 1;
}
/* HOST_IRQ_STAT behaves as level triggered latch meaning that
* it should be cleared after all the port events are cleared;
* otherwise, it will raise a spurious interrupt after each
* valid one. Please read section 10.6.2 of ahci 1.1 for more
* information.
*
* Also, use the unmasked value to clear interrupt as spurious
* pending event on a dummy port might cause screaming IRQ.
*/
writel(irq_stat, mmio + HOST_IRQ_STAT);
spin_unlock(&host->lock);
VPRINTK("EXIT\n");
return IRQ_RETVAL(handled);
}
unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_port_priv *pp = ap->private_data;
// elphel test: set tag = 0
//unsigned int tag = 0;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_qc_issue(): port %u queued command issue", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
#endif
/* Keep track of the currently active link. It will be used
* in completion path to determine whether NCQ phase is in
* progress.
*/
pp->active_link = qc->dev->link;
if (qc->tf.protocol == ATA_PROT_NCQ) {
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\twrite port %u register PxSACT, value: 0x%08x", ap->port_no, (1 << qc->tag));
//len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\twrite port %u register PxSACT, value: 0x%08x", ap->port_no, (1 << tag));
libahci_debug_event(ap, msg_str, len);
}
#endif
writel(1 << qc->tag, port_mmio + PORT_SCR_ACT);
//writel(1 << tag, port_mmio + PORT_SCR_ACT);
}
if (pp->fbs_enabled && pp->fbs_last_dev != qc->dev->link->pmp) {
u32 fbs = readl(port_mmio + PORT_FBS);
fbs &= ~(PORT_FBS_DEV_MASK | PORT_FBS_DEC);
fbs |= qc->dev->link->pmp << PORT_FBS_DEV_OFFSET;
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\twrite port %u register PxFBS, value: 0x%08x", ap->port_no, fbs);
libahci_debug_event(ap, msg_str, len);
}
#endif
writel(fbs, port_mmio + PORT_FBS);
pp->fbs_last_dev = qc->dev->link->pmp;
}
writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE);
//writel(1 << tag, port_mmio + PORT_CMD_ISSUE);
ahci_sw_activity(qc->dev->link);
#ifdef DEBUG_EVENT_ELPHEL
kfree(msg_str);
#endif
return 0;
}
EXPORT_SYMBOL_GPL(ahci_qc_issue);
static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
{
struct ahci_port_priv *pp = qc->ap->private_data;
u8 *rx_fis = pp->rx_fis;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_qc_fill_rtf(): read port %u Received FIS and copy to Task File", qc->ap->port_no);
libahci_debug_event(qc->ap, msg_str, len);
kfree(msg_str);
}
#endif
if (pp->fbs_enabled)
rx_fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ;
/*
* After a successful execution of an ATA PIO data-in command,
* the device doesn't send D2H Reg FIS to update the TF and
* the host should take TF and E_Status from the preceding PIO
* Setup FIS.
*/
if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE &&
!(qc->flags & ATA_QCFLAG_FAILED)) {
ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf);
qc->result_tf.command = (rx_fis + RX_FIS_PIO_SETUP)[15];
#ifdef DEBUG_EVENT_ELPHEL
libahci_debug_dump_region(qc->ap, (const u32 *)(rx_fis + RX_FIS_PIO_SETUP), 5, "\tread PIO Setup FIS; dump: ");
#endif //libahci_debug_dump_sg(qc, "reading data pointed by S/G list; dump: ");
} else {
ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
#ifdef DEBUG_EVENT_ELPHEL
libahci_debug_dump_region(qc->ap, (const u32 *)(rx_fis + RX_FIS_D2H_REG), 5, "\tread D2H Register FIS; dump: ");
#endif
}
return true;
}
static void ahci_freeze(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_freeze(): freeze port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
#endif
dev_dbg(ap->host->dev, "ahci_freeze()\n");
/* turn IRQ off */
writel(0, port_mmio + PORT_IRQ_MASK);
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\twrite to port %u register PxIE, value: 0x0", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
}
static void ahci_thaw(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *mmio = hpriv->mmio;
void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
struct ahci_port_priv *pp = ap->private_data;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_thaw(): thaw port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* clear IRQ */
tmp = readl(port_mmio + PORT_IRQ_STAT);
writel(tmp, port_mmio + PORT_IRQ_STAT);
writel(1 << ap->port_no, mmio + HOST_IRQ_STAT);
/* turn IRQ back on */
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}
void ahci_error_handler(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_error_handler(): port %u error handler", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
/* restart engine */
ahci_stop_engine(ap);
hpriv->start_engine(ap);
}
sata_pmp_error_handler(ap);
if (!ata_dev_enabled(ap->link.device))
ahci_stop_engine(ap);
}
EXPORT_SYMBOL_GPL(ahci_error_handler);
static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_post_internal_cmd(): port %u post internal command", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* make DMA engine forget about the failed command */
if (qc->flags & ATA_QCFLAG_FAILED)
ahci_kick_engine(ap);
}
static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
struct ata_device *dev = ap->link.device;
u32 devslp, dm, dito, mdat, deto;
int rc;
unsigned int err_mask;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_set_aggressive_devslp(): set aggressive port %u device sleep", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
devslp = readl(port_mmio + PORT_DEVSLP);
if (!(devslp & PORT_DEVSLP_DSP)) {
dev_info(ap->host->dev, "port does not support device sleep\n");
return;
}
/* disable device sleep */
if (!sleep) {
if (devslp & PORT_DEVSLP_ADSE) {
writel(devslp & ~PORT_DEVSLP_ADSE,
port_mmio + PORT_DEVSLP);
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_DISABLE,
SATA_DEVSLP);
if (err_mask && err_mask != AC_ERR_DEV)
ata_dev_warn(dev, "failed to disable DEVSLP\n");
}
return;
}
/* device sleep was already enabled */
if (devslp & PORT_DEVSLP_ADSE)
return;
/* set DITO, MDAT, DETO and enable DevSlp, need to stop engine first */
rc = ahci_stop_engine(ap);
if (rc)
return;
dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET;
dito = devslp_idle_timeout / (dm + 1);
if (dito > 0x3ff)
dito = 0x3ff;
/* Use the nominal value 10 ms if the read MDAT is zero,
* the nominal value of DETO is 20 ms.
*/
if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] &
ATA_LOG_DEVSLP_VALID_MASK) {
mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] &
ATA_LOG_DEVSLP_MDAT_MASK;
if (!mdat)
mdat = 10;
deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO];
if (!deto)
deto = 20;
} else {
mdat = 10;
deto = 20;
}
devslp |= ((dito << PORT_DEVSLP_DITO_OFFSET) |
(mdat << PORT_DEVSLP_MDAT_OFFSET) |
(deto << PORT_DEVSLP_DETO_OFFSET) |
PORT_DEVSLP_ADSE);
writel(devslp, port_mmio + PORT_DEVSLP);
hpriv->start_engine(ap);
/* enable device sleep feature for the drive */
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_ENABLE,
SATA_DEVSLP);
if (err_mask && err_mask != AC_ERR_DEV)
ata_dev_warn(dev, "failed to enable DEVSLP\n");
}
static void ahci_enable_fbs(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 fbs;
int rc;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL && pp->fbs_supported) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_enable_fbs(): enable port %u FIS-based switching", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
if (!pp->fbs_supported)
return;
fbs = readl(port_mmio + PORT_FBS);
if (fbs & PORT_FBS_EN) {
pp->fbs_enabled = true;
pp->fbs_last_dev = -1; /* initialization */
return;
}
rc = ahci_stop_engine(ap);
if (rc)
return;
writel(fbs | PORT_FBS_EN, port_mmio + PORT_FBS);
fbs = readl(port_mmio + PORT_FBS);
if (fbs & PORT_FBS_EN) {
dev_info(ap->host->dev, "FBS is enabled\n");
pp->fbs_enabled = true;
pp->fbs_last_dev = -1; /* initialization */
} else
dev_err(ap->host->dev, "Failed to enable FBS\n");
hpriv->start_engine(ap);
}
static void ahci_disable_fbs(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 fbs;
int rc;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL && pp->fbs_supported) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_disable_fbs(): disable port %u FIS-based switching", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
if (!pp->fbs_supported)
return;
fbs = readl(port_mmio + PORT_FBS);
if ((fbs & PORT_FBS_EN) == 0) {
pp->fbs_enabled = false;
return;
}
rc = ahci_stop_engine(ap);
if (rc)
return;
writel(fbs & ~PORT_FBS_EN, port_mmio + PORT_FBS);
fbs = readl(port_mmio + PORT_FBS);
if (fbs & PORT_FBS_EN)
dev_err(ap->host->dev, "Failed to disable FBS\n");
else {
dev_info(ap->host->dev, "FBS is disabled\n");
pp->fbs_enabled = false;
}
hpriv->start_engine(ap);
}
static void ahci_pmp_attach(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_port_priv *pp = ap->private_data;
u32 cmd;
cmd = readl(port_mmio + PORT_CMD);
cmd |= PORT_CMD_PMP;
writel(cmd, port_mmio + PORT_CMD);
ahci_enable_fbs(ap);
pp->intr_mask |= PORT_IRQ_BAD_PMP;
/*
* We must not change the port interrupt mask register if the
* port is marked frozen, the value in pp->intr_mask will be
* restored later when the port is thawed.
*
* Note that during initialization, the port is marked as
* frozen since the irq handler is not yet registered.
*/
if (!(ap->pflags & ATA_PFLAG_FROZEN))
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}
static void ahci_pmp_detach(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_port_priv *pp = ap->private_data;
u32 cmd;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
#endif
ahci_disable_fbs(ap);
cmd = readl(port_mmio + PORT_CMD);
cmd &= ~PORT_CMD_PMP;
writel(cmd, port_mmio + PORT_CMD);
pp->intr_mask &= ~PORT_IRQ_BAD_PMP;
/* see comment above in ahci_pmp_attach() */
if (!(ap->pflags & ATA_PFLAG_FROZEN))
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
#ifdef DEBUG_EVENT_ELPHEL
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_pmp_detach(), ap->flags=0x%08x PxCMD <= 0x%08x, IE=0x%08x",ap->pflags, cmd, readl(port_mmio + PORT_IRQ_MASK) );
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
}
int ahci_port_resume(struct ata_port *ap)
{
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_port_resume(): first form ahci_elphel.c - never got here? -wrong, got");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
ahci_power_up(ap);
ahci_start_port(ap);
if (sata_pmp_attached(ap)) // false
ahci_pmp_attach(ap);
else
ahci_pmp_detach(ap);
return 0;
}
EXPORT_SYMBOL_GPL(ahci_port_resume);
#ifdef CONFIG_PM
static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
{
const char *emsg = NULL;
int rc;
rc = ahci_deinit_port(ap, &emsg);
if (rc == 0)
ahci_power_down(ap);
else {
ata_port_err(ap, "%s (%d)\n", emsg, rc);
ata_port_freeze(ap);
}
return rc;
}
#endif
static int ahci_port_start(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct device *dev = ap->host->dev;
struct ahci_port_priv *pp;
void *mem;
dma_addr_t mem_dma;
size_t dma_sz, rx_fis_sz;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_port_start(): starting port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
if (!pp)
return -ENOMEM;
if (ap->host->n_ports > 1) {
pp->irq_desc = devm_kzalloc(dev, 8, GFP_KERNEL);
if (!pp->irq_desc) {
devm_kfree(dev, pp);
return -ENOMEM;
}
snprintf(pp->irq_desc, 8,
"%s%d", dev_driver_string(dev), ap->port_no);
}
/* check FBS capability */
if ((hpriv->cap & HOST_CAP_FBS) && sata_pmp_supported(ap)) {
void __iomem *port_mmio = ahci_port_base(ap);
u32 cmd = readl(port_mmio + PORT_CMD);
if (cmd & PORT_CMD_FBSCP)
pp->fbs_supported = true;
else if (hpriv->flags & AHCI_HFLAG_YES_FBS) {
dev_info(dev, "port %d can do FBS, forcing FBSCP\n",
ap->port_no);
pp->fbs_supported = true;
} else
dev_warn(dev, "port %d is not capable of FBS\n",
ap->port_no);
}
if (pp->fbs_supported) {
dma_sz = AHCI_PORT_PRIV_FBS_DMA_SZ;
rx_fis_sz = AHCI_RX_FIS_SZ * 16;
} else {
dma_sz = AHCI_PORT_PRIV_DMA_SZ;
rx_fis_sz = AHCI_RX_FIS_SZ;
}
mem = dmam_alloc_coherent(dev, dma_sz, &mem_dma, GFP_KERNEL);
if (!mem)
return -ENOMEM;
memset(mem, 0, dma_sz);
/*
* First item in chunk of DMA memory: 32-slot command table,
* 32 bytes each in size
*/
pp->cmd_slot = mem;
pp->cmd_slot_dma = mem_dma;
mem += AHCI_CMD_SLOT_SZ;
mem_dma += AHCI_CMD_SLOT_SZ;
/*
* Second item: Received-FIS area
*/
pp->rx_fis = mem;
pp->rx_fis_dma = mem_dma;
mem += rx_fis_sz;
mem_dma += rx_fis_sz;
/*
* Third item: data area for storing a single command
* and its scatter-gather table
*/
pp->cmd_tbl = mem;
pp->cmd_tbl_dma = mem_dma;
/*
* Save off initial list of interrupts to be enabled.
* This could be changed later
*/
pp->intr_mask = DEF_PORT_IRQ;
/*
* Switch to per-port locking in case each port has its own MSI vector.
*/
if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) {
spin_lock_init(&pp->lock);
ap->lock = &pp->lock;
}
ap->private_data = pp;
/* engage engines, captain */
return ahci_port_resume(ap);
}
static void ahci_port_stop(struct ata_port *ap)
{
const char *emsg = NULL;
int rc;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_port_stop(): stopping port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
#endif
/* de-initialize port */
rc = ahci_deinit_port(ap, &emsg);
if (rc)
ata_port_warn(ap, "%s (%d)\n", emsg, rc);
}
void ahci_print_info(struct ata_host *host, const char *scc_s)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
u32 vers, cap, cap2, impl, speed;
const char *speed_s;
vers = readl(mmio + HOST_VERSION);
cap = hpriv->cap;
cap2 = hpriv->cap2;
impl = hpriv->port_map;
speed = (cap >> 20) & 0xf;
if (speed == 1)
speed_s = "1.5";
else if (speed == 2)
speed_s = "3";
else if (speed == 3)
speed_s = "6";
else
speed_s = "?";
dev_info(host->dev,
"AHCI %02x%02x.%02x%02x "
"%u slots %u ports %s Gbps 0x%x impl %s mode\n"
,
(vers >> 24) & 0xff,
(vers >> 16) & 0xff,
(vers >> 8) & 0xff,
vers & 0xff,
((cap >> 8) & 0x1f) + 1,
(cap & 0x1f) + 1,
speed_s,
impl,
scc_s);
dev_info(host->dev,
"flags: "
"%s%s%s%s%s%s%s"
"%s%s%s%s%s%s%s"
"%s%s%s%s%s%s%s"
"%s%s\n"
,
cap & HOST_CAP_64 ? "64bit " : "",
cap & HOST_CAP_NCQ ? "ncq " : "",
cap & HOST_CAP_SNTF ? "sntf " : "",
cap & HOST_CAP_MPS ? "ilck " : "",
cap & HOST_CAP_SSS ? "stag " : "",
cap & HOST_CAP_ALPM ? "pm " : "",
cap & HOST_CAP_LED ? "led " : "",
cap & HOST_CAP_CLO ? "clo " : "",
cap & HOST_CAP_ONLY ? "only " : "",
cap & HOST_CAP_PMP ? "pmp " : "",
cap & HOST_CAP_FBS ? "fbs " : "",
cap & HOST_CAP_PIO_MULTI ? "pio " : "",
cap & HOST_CAP_SSC ? "slum " : "",
cap & HOST_CAP_PART ? "part " : "",
cap & HOST_CAP_CCC ? "ccc " : "",
cap & HOST_CAP_EMS ? "ems " : "",
cap & HOST_CAP_SXS ? "sxs " : "",
cap2 & HOST_CAP2_DESO ? "deso " : "",
cap2 & HOST_CAP2_SADM ? "sadm " : "",
cap2 & HOST_CAP2_SDS ? "sds " : "",
cap2 & HOST_CAP2_APST ? "apst " : "",
cap2 & HOST_CAP2_NVMHCI ? "nvmp " : "",
cap2 & HOST_CAP2_BOH ? "boh " : ""
);
}
EXPORT_SYMBOL_GPL(ahci_print_info);
void ahci_set_em_messages(struct ahci_host_priv *hpriv,
struct ata_port_info *pi)
{
u8 messages;
void __iomem *mmio = hpriv->mmio;
u32 em_loc = readl(mmio + HOST_EM_LOC);
u32 em_ctl = readl(mmio + HOST_EM_CTL);
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_set_em_messages(): store EM registers");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
#endif
if (!ahci_em_messages || !(hpriv->cap & HOST_CAP_EMS))
return;
messages = (em_ctl & EM_CTRL_MSG_TYPE) >> 16;
if (messages) {
/* store em_loc */
hpriv->em_loc = ((em_loc >> 16) * 4);
hpriv->em_buf_sz = ((em_loc & 0xff) * 4);
hpriv->em_msg_type = messages;
pi->flags |= ATA_FLAG_EM;
if (!(em_ctl & EM_CTL_ALHD))
pi->flags |= ATA_FLAG_SW_ACTIVITY;
}
}
EXPORT_SYMBOL_GPL(ahci_set_em_messages);
static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
struct scsi_host_template *sht)
{
int i, rc;
rc = ata_host_start(host);
if (rc)
return rc;
for (i = 0; i < host->n_ports; i++) {
struct ahci_port_priv *pp = host->ports[i]->private_data;
/* Do not receive interrupts sent by dummy ports */
if (!pp) {
disable_irq(irq + i);
continue;
}
rc = devm_request_threaded_irq(host->dev, irq + i,
ahci_multi_irqs_intr,
ahci_port_thread_fn, IRQF_SHARED,
pp->irq_desc, host->ports[i]);
if (rc)
goto out_free_irqs;
}
for (i = 0; i < host->n_ports; i++)
ata_port_desc(host->ports[i], "irq %d", irq + i);
rc = ata_host_register(host, sht);
if (rc)
goto out_free_all_irqs;
return 0;
out_free_all_irqs:
i = host->n_ports;
out_free_irqs:
for (i--; i >= 0; i--)
devm_free_irq(host->dev, irq + i, host->ports[i]);
return rc;
}
/**
* ahci_host_activate - start AHCI host, request IRQs and register it
* @host: target ATA host
* @irq: base IRQ number to request
* @sht: scsi_host_template to use when registering the host
*
* Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
* when multiple MSIs were allocated. That is one MSI per port, starting
* from @irq.
*
* LOCKING:
* Inherited from calling layer (may sleep).
*
* RETURNS:
* 0 on success, -errno otherwise.
*/
int ahci_host_activate(struct ata_host *host, int irq,
struct scsi_host_template *sht)
{
struct ahci_host_priv *hpriv = host->private_data;
int rc;
#ifdef DEBUG_EVENT_ELPHEL
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_host_activate(): activate AHCI host");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
#endif
if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
rc = ahci_host_activate_multi_irqs(host, irq, sht);
else
rc = ata_host_activate(host, irq, ahci_single_irq_intr,
IRQF_SHARED, sht);
return rc;
}
EXPORT_SYMBOL_GPL(ahci_host_activate);
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("Common AHCI SATA low-level routines");
MODULE_LICENSE("GPL");
/*
* libahci_debug.c
*
* Created on: Jan 20, 2016
* Author: mk
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <asm/outercache.h>
#include <asm/cacheflush.h>
#include <elphel/elphel393-mem.h>
#include <linux/jiffies.h>
#include "libahci_debug.h"
#include <asm/io.h> // ioremap(), copy_fromio
/*
struct elphel_buf_t
{
void *vaddr;
dma_addr_t paddr;
ssize_t size;
memcpy(to,from,sizeof())
};
extern struct elphel_buf_t *pElphel_buf;
*/
static u32 page_cntr = 0;
static struct dentry *debug_root = NULL;
static struct libahci_debug_list debug_list = {.debug = 0};
static struct ahci_cmd cmd;
static bool load_flag = false;
static bool error_flag = false;
const size_t maxigp1_start = 0x80000000; // start of MAXIGP1 physical address range
const size_t maxigp1_size = 0x3000; // size of register memory to save
const size_t buffer_offset = 0x40000; // start of dumping area (0xxxx, 1xxxx and 2xxxx are used for dma buffers
const size_t counter_offset = 0x3fff8; // save page counter and page size with this offset in the buffer
const size_t fsm_state_offset = 0xffc;
static void * ioptr = 0; // keep iomemory mapped forever
char *early_buff;
static inline u32 libahci_debug_get_fsm_state(void);
//static struct mem_buffer mem_buff;
/*
* Print PxIS (0x10) analysis
*/
void libahci_debug_dump_irq(u32 status)
{
int len = 0;
int pos;
char *str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (!str)
return;
len = snprintf(str, LIBAHCI_DEBUG_BUFSZ, "\tinterrupt analysis: ");
pos = len;
if (status & PORT_IRQ_D2H_REG_FIS) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "D2H Register FIS * ");
pos += len;
}
if (status & PORT_IRQ_PIOS_FIS) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, " PIO Setup FIS * ");
pos += len;
}
if (status & PORT_IRQ_DMAS_FIS) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "DMA Setup FIS * ");
pos += len;
}
if (status & PORT_IRQ_SDB_FIS) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Set Device Bits FIS * ");
pos += len;
}
if (status & PORT_IRQ_UNK_FIS) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Unknown FIS * ");
pos += len;
}
if (status & PORT_IRQ_SG_DONE) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Descriptor processed * ");
pos += len;
}
if (status & PORT_IRQ_CONNECT) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Port connect change status * ");
pos += len;
}
if (status & PORT_IRQ_DEV_ILCK) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Device interlock * ");
pos += len;
}
if (status & PORT_IRQ_PHYRDY) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "PhyRdy change status * ");
pos += len;
}
if (status & PORT_IRQ_BAD_PMP) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Incorrect port multiplier * ");
pos += len;
}
if (status & PORT_IRQ_OVERFLOW) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Overflow * ");
pos += len;
}
if (status & PORT_IRQ_IF_NONFATAL) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Iface nonfatal error * ");
pos += len;
}
if (status & PORT_IRQ_IF_ERR) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Iface fatal error * ");
pos += len;
}
if (status & PORT_IRQ_HBUS_DATA_ERR) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Host bus data error * ");
pos += len;
}
if (status & PORT_IRQ_HBUS_ERR) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Host bus fatal error * ");
pos += len;
}
if (status & PORT_IRQ_TF_ERR) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Task file error * ");
pos += len;
}
if (status & PORT_IRQ_COLD_PRES) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Cold port detect * ");
pos += len;
}
libahci_debug_event(NULL, str, pos);
kfree(str);
}
EXPORT_SYMBOL_GPL(libahci_debug_dump_irq);
/*
* Read memory region pointed to by buff and dump its content
*/
void libahci_debug_dump_region(const struct ata_port *ap, const u32 *buff, size_t buff_sz, const char *prefix)
{
int i;
int len, sz;
char *str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (!str)
return;
len = strlen(prefix);
if (len < LIBAHCI_DEBUG_BUFSZ) {
strncpy(str, prefix, len);
} else {
len = 0;
}
for (i = 0; i < buff_sz; i++) {
sz = snprintf(&str[len], LIBAHCI_DEBUG_BUFSZ - len, "0x%08x ", buff[i]);
len += sz;
}
libahci_debug_event(ap, str, len);
kfree(str);
}
EXPORT_SYMBOL_GPL(libahci_debug_dump_region);
/*
* Copy data from S/G list to linear buffer and dump the data
*/
void libahci_debug_dump_sg(const struct ata_queued_cmd *qc, const char *prefix)
{
struct scatterlist *sg;
int si;
int i;
int len;
int sz;
int line_brk;
u32 buff_sz = 0;
u32 buff_ptr = 0;
char *buff;
char *str;
u32 *buff_map;
// Calculate the amount of memory needed
for_each_sg(qc->sg, sg, qc->n_elem, si) {
buff_sz += sg_dma_len(sg);
}
buff = kzalloc(buff_sz, GFP_KERNEL);
if (!buff) {
return;
}
str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (!str) {
kfree(buff);
return;
}
// Copy data from all DMA buffers
dma_sync_sg_for_cpu(&qc->dev->tdev, qc->sg, qc->n_elem, qc->dma_dir);
for_each_sg(qc->sg, sg, qc->n_elem, si) {
u32 sg_len = sg_dma_len(sg);
sz = sg_copy_to_buffer(sg, 1, buff + buff_ptr, sg_len);
buff_ptr += sz;
}
dma_sync_sg_for_device(&qc->dev->tdev, qc->sg, qc->n_elem, qc->dma_dir);
// Print the content of DMA buffers
buff_map = (u32 *)buff;
len = snprintf(str, LIBAHCI_DEBUG_BUFSZ, "\t%s\t%u bytes\n\t", prefix, buff_ptr);
for (i = 0, line_brk = 0; i < buff_ptr / 4; i++) {
sz = snprintf(&str[len], LIBAHCI_DEBUG_BUFSZ - len, "0x%08x ", buff_map[i]);
len += sz;
line_brk++;
if (line_brk >= 8) {
libahci_debug_event(qc->ap, str, len);
line_brk = 0;
len = snprintf(str, LIBAHCI_DEBUG_BUFSZ, "\t");
}
}
if (line_brk != 0) {
libahci_debug_event(qc->ap, str, len);
}
//printk(KERN_DEBUG "%s\tdump S/G list", MARKER);
kfree(buff);
kfree(str);
}
EXPORT_SYMBOL_GPL(libahci_debug_dump_sg);
static void libahci_debug_read_host_regs(struct ata_host *host, struct host_regs *regs)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *host_mmio = hpriv->mmio;
regs->CAP = readl(host_mmio + HOST_CAP);
regs->CAP2 = readl(host_mmio + HOST_CAP2);
regs->GHC = readl(host_mmio + HOST_CTL);
regs->IS = readl(host_mmio + HOST_IRQ_STAT);
regs->PI = readl(host_mmio + HOST_PORTS_IMPL);
regs->VS = readl(host_mmio + HOST_VERSION);
regs->CCC_CTL = readl(host_mmio + 0x14);
regs->CCC_PORTS = readl(host_mmio + 0x18);
regs->EM_CTL = readl(host_mmio + HOST_EM_CTL);
regs->EM_LOC = readl(host_mmio + HOST_EM_LOC);
regs->BOHC = readl(host_mmio + 0x28);
}
static void libahci_debug_read_port_regs(struct ata_port *ap, struct port_regs *pr)
{
void __iomem *port_mmio = ahci_port_base(ap);
int i;
pr->PxCLB = readl(port_mmio + PORT_LST_ADDR);
pr->PxCLBU = readl(port_mmio + PORT_LST_ADDR_HI);
pr->PxFB = readl(port_mmio + PORT_FIS_ADDR);
pr->PxFBU = readl(port_mmio + PORT_FIS_ADDR_HI);
pr->PxIS = readl(port_mmio + PORT_IRQ_STAT);
pr->PxIE = readl(port_mmio + PORT_IRQ_MASK);
pr->PxCMD = readl(port_mmio + PORT_CMD);
//pr->reserved_1 = readl(port_mmio + PORT_RESERVED_1);
pr->PxTFD = readl(port_mmio + PORT_TFDATA);
pr->PxSIG = readl(port_mmio + PORT_SIG);
pr->PxSSTS = readl(port_mmio + PORT_SCR_STAT);
pr->PxSCTL = readl(port_mmio + PORT_SCR_CTL);
pr->PxSERR = readl(port_mmio + PORT_SCR_ERR);
pr->PxSACT = readl(port_mmio + PORT_SCR_ACT);
pr->PxCI = readl(port_mmio + PORT_CMD_ISSUE);
pr->PxSNTF = readl(port_mmio + PORT_SCR_NTF);
pr->PxFBS = readl(port_mmio + PORT_FBS);
pr->PxDEVSLP = readl(port_mmio + PORT_DEVSLP);
for (i = 0; i < PORT_VENDOR_BYTES; i++) {
pr->reserved_2[i] = readb(port_mmio + 0x70 + i);
}
}
static int libahci_debug_host_show(struct seq_file *f, void *p)
{
struct ata_host *host = f->private;
struct host_regs hr = {0};
libahci_debug_read_host_regs(host, &hr);
seq_printf(f, "CAP:\t\t0x%08X\n", hr.CAP);
seq_printf(f, "CAP2:\t\t0x%08X\n", hr.CAP2);
seq_printf(f, "GHC:\t\t0x%08X\n", hr.GHC);
seq_printf(f, "IS:\t\t0x%08X\n", hr.IS);
seq_printf(f, "PI:\t\t0x%08X\n", hr.PI);
seq_printf(f, "VS:\t\t0x%08X\n", hr.VS);
seq_printf(f, "CCC_CTL:\t0x%08X\n", hr.CCC_CTL);
seq_printf(f, "CCC_PORTS:\t0x%08X\n", hr.CCC_PORTS);
seq_printf(f, "EM_LOC:\t\t0x%08X\n", hr.EM_LOC);
seq_printf(f, "EM_CTL:\t\t0x%08X\n", hr.EM_CTL);
seq_printf(f, "BOHC:\t\t0x%08X\n", hr.BOHC);
seq_printf(f, "\nbuffer location:\t\t0x%08X\n", pElphel_buf->paddr);
return 0;
}
static int libahci_debug_rdesc_show(struct seq_file *f, void *p)
{
struct ata_port *ap = f->private;
struct port_regs pr = {0};
int i;
libahci_debug_read_port_regs(ap, &pr);
seq_printf(f, "PxCLB:\t\t0x%08X\n", pr.PxCLB);
seq_printf(f, "PxCLBU:\t\t0x%08X\n", pr.PxCLBU);
seq_printf(f, "PxFB:\t\tx0%08X\n", pr.PxFB);
seq_printf(f, "PxFBU:\t\t0x%08X\n", pr.PxFBU);
seq_printf(f, "PxIS:\t\t0x%08X\n", pr.PxIS);
seq_printf(f, "PxIE:\t\t0x%08X\n", pr.PxIE);
seq_printf(f, "PxCMD:\t\t0x%08X\n", pr.PxCMD);
seq_printf(f, "reserved:\t0x%08X\n", pr.reserved_1);
seq_printf(f, "PxTFD:\t\t0x%08X\n", pr.PxTFD);
seq_printf(f, "PxSIG:\t\t0x%08X\n", pr.PxSIG);
seq_printf(f, "PxSSTS:\t\t0x%08X\n", pr.PxSSTS);
seq_printf(f, "PxSCTL:\t\t0x%08X\n", pr.PxSCTL);
seq_printf(f, "PxSERR:\t\t0x%08X\n", pr.PxSERR);
seq_printf(f, "PxSACT:\t\t0x%08X\n", pr.PxSACT);
seq_printf(f, "PxCI:\t\t0x%08X\n", pr.PxCI);
seq_printf(f, "PxSNTF:\t\t0x%08X\n", pr.PxSNTF);
seq_printf(f, "PxFBS:\t\t0x%08X\n", pr.PxFBS);
seq_printf(f, "PxDEVSLP:\t0x%08X\n", pr.PxDEVSLP);
seq_printf(f, "reserved area:\t");
for (i = 0; i < PORT_RESERVED_2; i++) {
seq_printf(f, "0x%02X ", pr.reserved_2[i]);
}
seq_printf(f, "\nVendor specific bytes:\t");
for (i = 0; i < PORT_VENDOR_BYTES; i++) {
seq_printf(f, "0x%02X ", pr.PxVS[i]);
}
seq_printf(f, "\n");
return 0;
}
static int libahci_debug_host_open(struct inode *i_node, struct file *f)
{
return single_open(f, libahci_debug_host_show, i_node->i_private);
}
static int libahci_debug_rdesc_open(struct inode *i_node, struct file *f)
{
return single_open(f, libahci_debug_rdesc_show, i_node->i_private);
}
static int libahci_debug_events_open(struct inode *i_node, struct file *f)
{
int err = 0;
unsigned long flags;
struct libahci_debug_list *list;
struct ata_port *port = i_node->i_private;
debug_list.debug = 1;
//Create event buffer for current port
list = kzalloc(sizeof(struct libahci_debug_list), GFP_KERNEL);
if (!list) {
err = -ENOMEM;
goto fail_handler;
}
if (!(list->libahci_debug_buf = kzalloc(sizeof(char) * LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL))) {
err = -ENOMEM;
kfree(list);
goto fail_handler;
}
list->port_n = port->port_no;
mutex_init(&list->read_mutex);
init_waitqueue_head(&list->debug_wait);
spin_lock_irqsave(&list->debug_list_lock, flags);
list_add_tail(&list->node, &debug_list.node);
spin_unlock_irqrestore(&list->debug_list_lock, flags);
// Associate debug list entry with corresponding file
f->private_data = list;
fail_handler:
return err;
}
static ssize_t libahci_debug_events_read(struct file *f, char __user *buff, size_t sz, loff_t *pos)
{
int ret = 0, len;
struct libahci_debug_list *list = f->private_data;
DECLARE_WAITQUEUE(wait, current);
mutex_lock(&list->read_mutex);
while (ret == 0) {
if (list->head == list->tail) {
// Buffer is empty, put the queue in sleep mode
add_wait_queue(&list->debug_wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
while (list->head == list->tail) {
if (f->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
mutex_unlock(&list->read_mutex);
schedule();
mutex_lock(&list->read_mutex);
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&list->debug_wait, &wait);
}
copy_rest:
//printk(KERN_DEBUG "%s Read event", MARKER);
if (list->head != list->tail && ret == 0) {
if (list->tail > list->head) {
len = list->tail - list->head;
if (copy_to_user(buff + ret, &list->libahci_debug_buf[list->head], len)) {
ret = -EFAULT;
} else {
ret += len;
list->head += len;
}
} else {
len = LIBAHCI_DEBUG_BUFSZ - list->head;
if (copy_to_user(buff, &list->libahci_debug_buf[list->head], len)) {
ret = -EFAULT;
} else {
ret += len;
list->head = 0;
goto copy_rest;
}
}
}
//printk(KERN_DEBUG "%s\thead now is %u", MARKER, list->head);
}
mutex_unlock(&list->read_mutex);
return ret;
}
static unsigned int libahci_debug_events_poll(struct file *f, struct poll_table_struct *wait)
{
struct libahci_debug_list *list = f->private_data;
poll_wait(f, &list->debug_wait, wait);
if (list->head != list->tail) {
return POLLIN | POLLRDNORM;
}
return 0;
}
static int libahci_debug_events_release(struct inode *i_node, struct file *f)
{
struct libahci_debug_list *list = f->private_data;
unsigned long flags;
debug_list.debug = 0;
spin_lock_irqsave(&list->debug_list_lock, flags);
list_del(&list->node);
spin_unlock_irqrestore(&list->debug_list_lock, flags);
kfree(list->libahci_debug_buf);
kfree(list);
return 0;
}
void libahci_debug_event(const struct ata_port *port, char *msg, size_t msg_sz)
{
int len;
int i;
u32 tmp;
char *format_msg = NULL;
unsigned long flags;
unsigned int port_index = (port == NULL) ? 0 : port->port_no;
struct libahci_debug_list *list = NULL, *pos = NULL;
//printk(KERN_DEBUG "%s Write event: %s", MARKER, msg);
if (debug_list.debug) {
format_msg = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (format_msg != NULL) {
// Find buffer which this event is addressed to
list_for_each_entry(list, &debug_list.node, node) {
if (list->port_n == port_index) {
pos = list;
}
}
if (pos != NULL) {
//i = libahci_debug_state_dump(port);
i = libahci_debug_saxigp1_save(port, 0x3000);
tmp = libahci_debug_get_fsm_state();
len = snprintf(format_msg, LIBAHCI_DEBUG_BUFSZ, "%s [%08u; fsm: 0x%08x @0x%08x] %s\n", EVT_MARKER, i, tmp, jiffies, msg);
spin_lock_irqsave(&pos->debug_list_lock, flags);
for (i = 0; i < len; i++) {
pos->libahci_debug_buf[(pos->tail+ i) % LIBAHCI_DEBUG_BUFSZ] = format_msg[i];
}
pos->tail = (pos->tail + len) % LIBAHCI_DEBUG_BUFSZ;
spin_unlock_irqrestore(&pos->debug_list_lock, flags);
//printk(KERN_DEBUG "%s\ttail is now %u", MARKER, pos->tail);
// Wake up the queue which should be sleeping
wake_up_interruptible(&pos->debug_wait);
}
kfree(format_msg);
}
} else {
if (early_buff) {
}
}
}
EXPORT_SYMBOL_GPL(libahci_debug_event);
static void libahci_debug_prep_cfis(struct ahci_cmd_fis *cmd, u32 *fis, u8 pmp)
{
fis[0] = cmd->dw0;
fis[1] = cmd->dw1;
fis[2] = cmd->dw2;
fis[3] = cmd->dw3;
fis[4] = cmd->dw4;
}
void libahci_debug_exec_cmd(struct ata_port *ap)
{
void *cmd_tbl;
dma_addr_t cmd_tbl_dma;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_sg *ahci_sg;
struct ahci_cmd_hdr *data;
void __iomem *port_mmio = ahci_port_base(ap);
unsigned int slot;
printk(KERN_DEBUG "%s Executing command for port %u", MARKER, ap->port_no);
slot = readl(port_mmio + PORT_CMD_ISSUE);
printk(KERN_DEBUG "%s PxCI: 0x%08x", MARKER, slot);
slot = ffz(slot);
printk(KERN_DEBUG "%s Preparing command; using slot %u", MARKER, slot);
cmd_tbl = pp->cmd_tbl + slot * AHCI_CMD_TBL_SZ;
libahci_debug_prep_cfis(&cmd.fis, cmd_tbl, ap->link.pmp);
libahci_debug_dump_region(ap, (const u32 *)cmd_tbl, 5, "\tcfis data dump: ");
/*printk(KERN_DEBUG "%s\tcfis data: DW0 = 0x%08x DW1 = 0x%08x DW2 = 0x%08x DW3 = 0x%08x DW4 = 0x%08x",
MARKER, cmd.fis.dw0, cmd.fis.dw1, cmd.fis.dw2, cmd.fis.dw3, cmd.fis.dw4);*/
printk(KERN_DEBUG "%s Preparing one S/G list", MARKER);
ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ;
ahci_sg->addr = cpu_to_le32(sg_dma_address(&cmd.sg) & 0xffffffff);
ahci_sg->addr_hi = cpu_to_le32((sg_dma_address(&cmd.sg) >> 16) >> 16);
ahci_sg->flags_size = cpu_to_le32(sg_dma_len(&cmd.sg));
printk(KERN_DEBUG "%s Preparing command header", MARKER);
cmd_tbl_dma = pp->cmd_tbl_dma + slot * AHCI_CMD_TBL_SZ;
pp->cmd_slot[slot].opts = cpu_to_le32(cmd.hdr.opts);
pp->cmd_slot[slot].status = 0;
pp->cmd_slot[slot].tbl_addr = cpu_to_le32(cmd_tbl_dma & 0xffffffff);
pp->cmd_slot[slot].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16);
data = &pp->cmd_slot[slot];
printk(KERN_DEBUG "%s\tchdr data: DW0 = 0x%08x DW1 = 0x%08x DW2 = 0x%08x DW3 = 0x%08x", MARKER, data->opts,
data->status, data->tbl_addr, data->tbl_addr_hi);
printk(KERN_DEBUG "%s Issuing command", MARKER);
writel(1 << slot, port_mmio + PORT_CMD_ISSUE);
cmd.cmd_sent = true;
}
EXPORT_SYMBOL_GPL(libahci_debug_exec_cmd);
void libahci_debug_irq_notify(const struct ata_port *ap)
{
int i, sz, line_brk, ptr;
size_t len;
char *buff = kzalloc(CMD_DMA_BUFSZ, GFP_KERNEL);
char *str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
u32 *buff_map;
struct ahci_port_priv *pp = ap->private_data;
u32 *rx_fis = pp->rx_fis;
void __iomem *port_mmio = ahci_port_base((struct ata_port *)ap);
u32 tmp;
if (!buff || !str)
return;
printk(KERN_DEBUG "%s IRQ notify event, flag: %d", MARKER, cmd.cmd_sent);
if (cmd.cmd_sent) {
tmp = readl(port_mmio + PORT_SCR_ERR);
printk(KERN_DEBUG "%s PxSERR: 0x%08x", MARKER, tmp);
// Read RX FIS memory
libahci_debug_dump_region(ap, rx_fis + RX_FIS_PIO_SETUP, 5, "\tread PIO SETUP FIS region; dump: ");
libahci_debug_dump_region(ap, rx_fis + RX_FIS_D2H_REG, 5, "\tread D2H Register FIS; dump: ");
dma_sync_sg_for_cpu(ap->dev, &cmd.sg, 1, DMA_BIDIRECTIONAL);
len = sg_copy_to_buffer(&cmd.sg, 1, buff, CMD_DMA_BUFSZ);
dma_sync_sg_for_device(ap->dev, &cmd.sg, 1, DMA_BIDIRECTIONAL);
printk(KERN_DEBUG "%s %u bytes copied from DMA buffer", MARKER, len);
// Print the content of DMA buffers
buff_map = (u32 *)buff;
for (i = 0, line_brk = 0, ptr = 0; i < len / 4; i++) {
sz = snprintf(&str[ptr], LIBAHCI_DEBUG_BUFSZ - ptr, "0x%08x ", buff_map[i]);
ptr += sz;
line_brk++;
if (line_brk >= 8) {
libahci_debug_event(ap, str, ptr);
line_brk = 0;
ptr = snprintf(str, LIBAHCI_DEBUG_BUFSZ, "\t");
}
}
if (line_brk != 0) {
libahci_debug_event(ap, str, ptr);
}
// Command processed, clear flag
cmd.cmd_sent = false;
}
}
EXPORT_SYMBOL_GPL(libahci_debug_irq_notify);
static int libahci_debug_fis_open(struct inode *i_node, struct file *f)
{
struct ata_port *ap = i_node->i_private;
// const char *name = f->f_path.dentry->d_name.name;
// void *buff = NULL;
/*if (strncmp(name, FILE_NAME_CFIS, 5) == 0) {
buff = kzalloc(sizeof(struct ahci_cmd_fis), GFP_KERNEL);
} else if (strncmp(name, FILE_NAME_CHDR, 5) == 0) {
buff = kzalloc(sizeof(struct ahci_cmd_hdr), GFP_KERNEL);
}
if (!buff)
return -ENOMEM;*/
f->private_data = ap;
return 0;
}
static ssize_t libahci_debug_cfis_write(struct file *f, const char __user *buff, size_t buff_sz, loff_t *ppos)
{
struct ahci_cmd_fis *data = &cmd.fis;
if (buff_sz != CMD_FIS_SZ) {
return -EINVAL;
}
data->dw0 = (((buff[3] & 0xff) << 24) | ((buff[2] & 0xff) << 16) | ((buff[1] & 0xff) << 8) | (buff[0] & 0xff));
data->dw1 = (((buff[7] & 0xff) << 24) | ((buff[6] & 0xff) << 16) | ((buff[5] & 0xff) << 8) | (buff[4] & 0xff));
data->dw2 = (((buff[11] & 0xff) << 24) | ((buff[10] & 0xff) << 16) | ((buff[9] & 0xff) << 8) | (buff[8] & 0xff));
data->dw3 = (((buff[15] & 0xff) << 24) | ((buff[14] & 0xff) << 16) | ((buff[13] & 0xff) << 8) | (buff[12] & 0xff));
data->dw4 = (((buff[19] & 0xff) << 24) | ((buff[18] & 0xff) << 16) | ((buff[17] & 0xff) << 8) | (buff[16] & 0xff));
//printk(KERN_DEBUG "%s cfis data: DW0 = 0x%08x DW1 = 0x%08x DW2 = 0x%08x DW3 = 0x%08x DW4 = 0x%08x, pos = %lld",
// MARKER, data->dw0, data->dw1, data->dw2, data->dw3, data->dw4, *ppos);
return buff_sz;
}
static ssize_t libahci_debug_chdr_write(struct file *f, const char __user *buff, size_t buff_sz, loff_t *ppos)
{
struct ahci_cmd_hdr *data = &cmd.hdr;
if (buff_sz != CMD_HDR_SZ) {
return -EINVAL;
}
data->opts = (((buff[3] & 0xff) << 24) | ((buff[2] & 0xff) << 16) | ((buff[1] & 0xff) << 8) | (buff[0] & 0xff));
data->status = (((buff[7] & 0xff) << 24) | ((buff[6] & 0xff) << 16) | ((buff[5] & 0xff) << 8) | (buff[4] & 0xff));
data->tbl_addr = (((buff[11] & 0xff) << 24) | ((buff[10] & 0xff) << 16) | ((buff[9] & 0xff) << 8) | (buff[8] & 0xff));
data->tbl_addr_hi = (((buff[15] & 0xff) << 24) | ((buff[14] & 0xff) << 16) | ((buff[13] & 0xff) << 8) | (buff[12] & 0xff));
//printk(KERN_DEBUG "%s chdr data: DW0 = 0x%08x DW1 = 0x%08x DW2 = 0x%08x DW3 = 0x%08x, pos = %lld", MARKER, data->opts,
// data->status, data->tbl_addr, data->tbl_addr_hi, *ppos);
libahci_debug_exec_cmd(f->private_data);
return buff_sz;
}
static int libahci_debug_fis_release(struct inode *i_node, struct file *f)
{
return 0;
}
static ssize_t libahci_debug_load(struct file *f, const char __user *buff, size_t buff_sz, loff_t *ppos)
{
load_flag = true;
return buff_sz;
}
static ssize_t libahci_debug_crash_write(struct file *f, const char __user *buff, size_t buff_sz, loff_t *ppos)
{
if (buff[0] == '1') {
error_flag = true;
} else {
error_flag = false;
}
return buff_sz;
}
static ssize_t libahci_debug_crash_read(struct file *f, char __user *buff, size_t buff_sz, loff_t *ppos)
{
char *tmp = error_flag ? "1" : "0";
copy_to_user(buff, tmp, 2);
return 2;
}
void libahci_debug_crash_here(void)
{
BUG_ON(error_flag == true);
}
EXPORT_SYMBOL_GPL(libahci_debug_crash_here);
void libahci_debug_crash_set(bool val)
{
error_flag = (val == 0) ? false : true;
}
EXPORT_SYMBOL_GPL(libahci_debug_crash_set);
/*
* This function waits until the loading flag is set through debugfs file.
* The state of the flag is checked every 100ms.
*/
void libahci_debug_wait_flag(void)
{
char *msg_str = "waiting for flag to be written to debugfs";
printk(KERN_DEBUG "%s Waiting for flag to be written to debugfs", MARKER);
libahci_debug_event(NULL, msg_str, sizeof(*msg_str));
while (load_flag == false) {
/*set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(msecs_to_jiffies(100));*/
msleep(500);
}
load_flag = false;
}
EXPORT_SYMBOL_GPL(libahci_debug_wait_flag);
static void libahci_debug_buff_line(void *mem, u32 cntr)
{
int i;
u32 *mem_ptr = mem;
mem_ptr[0] = cntr;
for (i = 1; i < MARKER_LEN; i++) {
mem_ptr[i] = 0xa5a5a5a5;
}
}
/*
* Copy controller registers to buffer memory and return the record number
*/
unsigned int libahci_debug_state_dump(struct ata_port *ap)
{
// static u32 page_cntr;
int i;
u32 tmp;
u32 ptr;
u32 * buf = (u32 *) (pElphel_buf->vaddr);
struct device *dev = ap->dev;
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *host_mmio = hpriv->mmio;
struct ahci_port_priv *ppriv = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
if (!ap)
return 0;
if (!pElphel_buf->vaddr) {
dev_err(dev, "dump buffer has not been allocated");
return 0;
}
dev_dbg(dev, "dump page num: %u", page_cntr);
ptr = page_cntr * DUMP_LEN;
if (ptr + DUMP_LEN > SEGMENT_SIZE)
ptr = 0;
dev_dbg(dev, "current ptr: %u", ptr);
for (i = 0; i < GHC_SZ; i++) {
tmp = ioread32(host_mmio + 4 * i);
buf[ptr++] = tmp;
}
for (i = 0; i < PORT_REG_SZ; i++) {
tmp = ioread32(port_mmio + 4 * i);
buf[ptr++] = tmp;
}
for (i = 0; i < CLB_SZ; i++) {
tmp = ioread32(ppriv->cmd_slot);
buf[ptr++] = tmp;
}
for (i = 0; i < FIS_SZ; i++) {
tmp = ioread32(ppriv->rx_fis);
buf[ptr++] = tmp;
}
libahci_debug_buff_line(pElphel_buf->vaddr +ptr + ALIGN_OFFSET, page_cntr);
//__cpuc_flush_kern_all();
//outer_flush_all();
page_cntr++;
return page_cntr;
}
EXPORT_SYMBOL_GPL(libahci_debug_state_dump);
unsigned int libahci_debug_saxigp1_save(struct ata_port *ap, size_t dump_size)
{
struct device *dev;
u32 * counter_save;
void *current_ptr;
static size_t bytes_copied;
const size_t end = 0x1000000;
u32 *start_ptr;
// ap pointer is not initialized on early loading stages, skip debug output
if (ap) {
dev = ap->dev;
if (!ioptr) {
dev_err(dev, "saxigp1 memory is not mapped");
return 0; // should be non-zero when error, 0 is OK usually
}
if (!pElphel_buf->vaddr) {
dev_err(dev, "elphel_buf has not been allocated");
return 0; // should be non-zero when error, 0 is OK usually
}
}
//if (bytes_copied < pElphel_buf->size * PAGE_SIZE - dump_size) {
if (bytes_copied < end) {
counter_save = (u32*) (pElphel_buf->vaddr + counter_offset);
start_ptr = (u32 *)(pElphel_buf->vaddr + buffer_offset + (page_cntr * dump_size));
//dev_err(dev, "Copying 0x%x bytes of data from saxigp1 to memory",dump_size);
memcpy_fromio(pElphel_buf->vaddr + buffer_offset + (page_cntr * dump_size), ioptr, dump_size);
counter_save[0] = page_cntr;
counter_save[1] = dump_size;
page_cntr++;
bytes_copied += dump_size;
} else {
page_cntr = 0;
//bytes_copied = 0;
//dev_dbg(dev, "memory pool has ended, writing from beginning");
}
return page_cntr;
}
EXPORT_SYMBOL_GPL(libahci_debug_saxigp1_save);
static void libahci_debug_buff_init(struct device *dev)
{
dev_dbg(dev, "Nothing to allocate - using elphel_buf allocated at startup");
/*
mem_buff.vaddr = dmam_alloc_coherent(dev, SEGMENT_SIZE, &mem_buff.paddr, GFP_KERNEL);
if (!mem_buff.vaddr)
dev_err(dev, "unable to allocate memory");
else
dev_dbg(dev, "dump buffer allocated");
*/
/*
const size_t maxigp1_start = 0x80000000; // start of MAXIGP1 physical address range
const size_t maxigp1_size = 0x3000; // size of register memory to save
*/
ioptr = ioremap_nocache(maxigp1_start, maxigp1_size);
dev_dbg(dev, "Mapped 0x%08x bytes from physical address 0x%08x to 0x%08x", maxigp1_size, maxigp1_start, (size_t) ioptr);
page_cntr = 0;
}
static inline u32 libahci_debug_get_fsm_state(void)
{
return ioread32(ioptr + fsm_state_offset);
}
static const struct file_operations libahci_debug_host_ops = {
.open = libahci_debug_host_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations libahci_debug_rdesc_ops = {
.open = libahci_debug_rdesc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations libahci_debug_events_fops = {
.owner = THIS_MODULE,
.open = libahci_debug_events_open,
.read = libahci_debug_events_read,
.poll = libahci_debug_events_poll,
.release = libahci_debug_events_release,
.llseek = noop_llseek,
};
static const struct file_operations libahci_debug_cfis_ops = {
.open = libahci_debug_fis_open,
.write = libahci_debug_cfis_write,
.release = libahci_debug_fis_release,
};
static const struct file_operations libahci_debug_chdr_ops = {
.open = libahci_debug_fis_open,
.write = libahci_debug_chdr_write,
.release = libahci_debug_fis_release,
};
static const struct file_operations libahci_debug_load_ops= {
.write = libahci_debug_load,
};
static const struct file_operations libahci_debug_error_ops= {
.write = libahci_debug_crash_write,
.read = libahci_debug_crash_read,
};
static int libahci_debug_init_sg(void)
{
cmd.sg_buff = kzalloc(CMD_DMA_BUFSZ, GFP_KERNEL);
// mark the area
memset(cmd.sg_buff, 0xa5, CMD_DMA_BUFSZ);
if (!cmd.sg_buff) {
return -ENOMEM;
} else {
sg_init_one(&cmd.sg, cmd.sg_buff, CMD_DMA_BUFSZ);
}
return 0;
}
int libahci_debug_init_early(struct device *dev)
{
early_buff = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (!early_buff) {
dev_err(dev, "unable to allocate mem for early buffer");
return -ENOMEM;
} else {
dev_dbg(dev, "early buffer allocated");
}
}
int libahci_debug_init(struct ata_host *host)
{
int i;
char port_n[] = "port00";
struct dentry *node;
INIT_LIST_HEAD(&debug_list.node);
printk(KERN_DEBUG "%s Loading debug AHCI driver", MARKER);
debug_root = debugfs_create_dir(ROOT_DIR_NAME, NULL);
if (!debug_root) {
goto fail_handler;
}
debugfs_create_file("rdesc_host", 0644,
debug_root, host, &libahci_debug_host_ops);
debugfs_create_file("loading", 0222,
debug_root, host, &libahci_debug_load_ops);
debugfs_create_file("crash_on_error", 0644,
debug_root, host, &libahci_debug_error_ops);
/* Create subdir for each port and add there several files:
* one for port registers, one for port events log and
* two files for working with FISes
*/
for (i = 0; i < host->n_ports; i++) {
snprintf(port_n, 7, "port%02d", i);
node = debugfs_create_dir(port_n, debug_root);
debugfs_create_file("rdesc_port", 0644,
node, host->ports[i], &libahci_debug_rdesc_ops);
debugfs_create_file("events", 0644,
node, host->ports[i], &libahci_debug_events_fops);
debugfs_create_file(FILE_NAME_CFIS, 0222,
node, host->ports[i], &libahci_debug_cfis_ops);
debugfs_create_file(FILE_NAME_CHDR, 0222,
node, host->ports[i], &libahci_debug_chdr_ops);
}
if (libahci_debug_init_sg() != 0) {
goto fail_handler;
}
libahci_debug_buff_init(host->dev);
return 0;
fail_handler:
debugfs_remove_recursive(debug_root);
printk(KERN_DEBUG "%s Unable to create debugfs file structure", MARKER);
return -ENOENT;
}
EXPORT_SYMBOL_GPL(libahci_debug_init);
void libahci_debug_exit(void)
{
kfree(cmd.sg_buff);
kfree(early_buff);
debugfs_remove_recursive(debug_root);
}
EXPORT_SYMBOL_GPL(libahci_debug_exit);
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("Debug AHCI SATA low-level routines");
MODULE_LICENSE("GPL");
/*
* libahci_debug.h
*
* Created on: Jan 20, 2016
* Author: mk
*/
#include "ahci.h"
#ifndef _LIBAHCI_DEBUG_H_
#define _LIBAHCI_DEBUG_H_
#define ROOT_DIR_NAME "ahci_exp"
#define FILE_NAME_CFIS "cfis"
#define FILE_NAME_CHDR "chdr"
#define MARKER "++"
#define EVT_MARKER ">"
#define CMD_FIS_SZ 20
#define CMD_HDR_SZ 16
#define CMD_DMA_BUFSZ 512
#define PORT_RESERVED_2 40
#define PORT_VENDOR_BYTES 16
#define LIBAHCI_DEBUG_BUFSZ 16384
/* Total size of dump buffer in bytes*/
#define SEGMENT_SIZE 0x10000
/* The sizes below are in DWORDs*/
#define GHC_SZ 0x0B
#define PORT_REG_SZ 0x20
#define CLB_SZ 0x08
#define FIS_SZ 0x40
#define DUMP_LEN 0x240
/* The length of delimiter line in memory */
#define MARKER_LEN 0x10
/* Offset to the end of nearest 16 DWORD string */
#define ALIGN_OFFSET 0x0d
struct libahci_debug_list {
unsigned int debug;
unsigned int port_n;
char *libahci_debug_buf;
int head;
int tail;
struct list_head node;
struct mutex read_mutex;
wait_queue_head_t debug_wait;
spinlock_t debug_list_lock;
};
struct ahci_cmd_fis {
__le32 dw0;
__le32 dw1;
__le32 dw2;
__le32 dw3;
__le32 dw4;
};
struct ahci_cmd {
struct ahci_cmd_hdr hdr;
struct ahci_cmd_fis fis;
struct scatterlist sg;
char *sg_buff;
int cmd_sent;
};
struct mem_buffer {
volatile u32 *vaddr;
dma_addr_t paddr;
ssize_t size;
};
struct dump_record {
u32 reg_ghc[GHC_SZ];
u32 reg_port[PORT_REG_SZ];
u32 reg_clb[CLB_SZ];
u32 reg_fis[FIS_SZ];
u32 cntr;
};
// AHCI Port registers
struct port_regs {
// Port command list base address
u32 PxCLB;
// Port command list based address upper 32-bits
u32 PxCLBU;
// Port FIS base address
u32 PxFB;
// Port FIS base address upper 32-bits
u32 PxFBU;
u32 PxIS;
u32 PxIE;
u32 PxCMD;
u32 reserved_1;
u32 PxTFD;
u32 PxSIG;
u32 PxSSTS;
u32 PxSCTL;
u32 PxSERR;
u32 PxSACT;
u32 PxCI;
u32 PxSNTF;
u32 PxFBS;
u32 PxDEVSLP;
char reserved_2[PORT_RESERVED_2];
char PxVS[PORT_VENDOR_BYTES];
};
struct host_regs {
u32 CAP;
u32 GHC;
u32 IS;
u32 PI;
u32 VS;
u32 CCC_CTL;
u32 CCC_PORTS;
u32 EM_LOC;
u32 EM_CTL;
u32 CAP2;
u32 BOHC;
};
int libahci_debug_init(struct ata_host *host);
void libahci_debug_exit(void);
void libahci_debug_event(const struct ata_port *port ,char *msg, size_t msg_sz);
void libahci_debug_dump_region(const struct ata_port *ap, const u32 *buf, size_t buff_sz, const char* prefix);
void libahci_debug_dump_irq(u32 status);
void libahci_debug_dump_sg(const struct ata_queued_cmd *qc, const char *prefix);
void libahci_debug_irq_notify(const struct ata_port *ap);
void libahci_debug_exec_cmd(struct ata_port *ap);
void libahci_debug_wait_flag(void);
unsigned int libahci_debug_state_dump(struct ata_port *ap);
unsigned int libahci_debug_saxigp1_save(struct ata_port *ap, size_t dump_size);
void libahci_debug_crash_here(void);
void libahci_debug_crash_set(bool val);
void libahci_debug_timestamp(cycles_t start_c, cycles_t end_c, unsigned long start_j, unsigned long end_j, u32 id);
#endif /* _LIBAHCI_DEBUG_H_ */
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment