Merge branch 'for-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata

Pull libata updates from Tejun Heo:
 "Most are ahci and other device specific additions.  Dan cleaned up
  ahci IRQ handling to prepare for future MSIX changes.  On the libata
  core side, Vinayak updated SG handling so that NCQ commands can be
  issued through SG_IO and Christoph cleaned up code a bit.  There's one
  merge from for-4.3-fixes to include a pata_macio commit that didn't
  get pushed out"

* 'for-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata:
  ahci: add new Intel device IDs
  ahci: Add Marvell 88se91a2 device id
  ahci: cleanup ahci_host_activate_multi_irqs
  ahci: ahci_host_activate: kill IRQF_SHARED
  devicetree: bindings: Fixed a few typos
  ahci: qoriq: Disable NCQ on ls2080a SoC
  ahci: qoriq: Rename LS2085A SoC support code to LS2080A
  libata: enable LBA flag in taskfile for ata_scsi_pass_thru()
  libata: add support for NCQ commands for SG interface
  ahci: qoriq: Fix a compiling warning
  pata_it821x: use "const char *" for string literals
  libata: only call ->done once all per-tag ressources are released
  libata: cleanup ata_scsi_qc_complete
  ata: ahci: find eSATA ports and flag them as removable
  libata: samsung_cf: fix handling platform_get_irq result
  ata: pata_macio: Fix module autoload for OF platform driver
  ata: pata_pxa: dmaengine conversion
  ahci: added a new driver for supporting Freescale AHCI sata
  devicetree:bindings: add devicetree bindings for Freescale AHCI
  Revert "ahci: added support for Freescale AHCI sata"
This commit is contained in:
Linus Torvalds 2015-11-05 14:32:54 -08:00
commit 11eaaadb3e
15 changed files with 433 additions and 164 deletions

View File

@ -0,0 +1,21 @@
Binding for Freescale QorIQ AHCI SATA Controller
Required properties:
- reg: Physical base address and size of the controller's register area.
- compatible: Compatibility string. Must be 'fsl,<chip>-ahci', where
chip could be ls1021a, ls2080a, ls1043a etc.
- clocks: Input clock specifier. Refer to common clock bindings.
- interrupts: Interrupt specifier. Refer to interrupt binding.
Optional properties:
- dma-coherent: Enable AHCI coherent DMA operation.
- reg-names: register area names when there are more than 1 register area.
Examples:
sata@3200000 {
compatible = "fsl,ls1021a-ahci";
reg = <0x0 0x3200000 0x0 0x10000>;
interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&platform_clk 1>;
dma-coherent;
};

View File

@ -16,8 +16,6 @@ Required properties:
- "snps,dwc-ahci"
- "snps,exynos5440-ahci"
- "snps,spear-ahci"
- "fsl,qoriq-ahci" : for qoriq series socs which include ls1021, ls2085, etc.
- "fsl,<chip>-ahci" : chip could be ls1021, ls2085 etc.
- "generic-ahci"
- interrupts : <interrupt mapping for SATA IRQ>
- reg : <registers mapping>

View File

@ -175,6 +175,15 @@ config AHCI_XGENE
help
This option enables support for APM X-Gene SoC SATA host controller.
config AHCI_QORIQ
tristate "Freescale QorIQ AHCI SATA support"
depends on OF
help
This option enables support for the Freescale QorIQ AHCI SoC's
onboard AHCI SATA.
If unsure, say N.
config SATA_FSL
tristate "Freescale 3.0Gbps SATA support"
depends on FSL_SOC

View File

@ -19,6 +19,7 @@ obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_ST) += ahci_st.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_TEGRA) += ahci_tegra.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_XGENE) += ahci_xgene.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_QORIQ) += ahci_qoriq.o libahci.o libahci_platform.o
# SFF w/ custom DMA
obj-$(CONFIG_PDC_ADMA) += pdc_adma.o

View File

@ -314,6 +314,16 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x1f37), board_ahci_avn }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f3e), board_ahci_avn }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f3f), board_ahci_avn }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0xa182), board_ahci }, /* Lewisburg AHCI*/
{ PCI_VDEVICE(INTEL, 0xa202), board_ahci }, /* Lewisburg AHCI*/
{ PCI_VDEVICE(INTEL, 0xa184), board_ahci }, /* Lewisburg RAID*/
{ PCI_VDEVICE(INTEL, 0xa204), board_ahci }, /* Lewisburg RAID*/
{ PCI_VDEVICE(INTEL, 0xa186), board_ahci }, /* Lewisburg RAID*/
{ PCI_VDEVICE(INTEL, 0xa206), board_ahci }, /* Lewisburg RAID*/
{ PCI_VDEVICE(INTEL, 0x2822), board_ahci }, /* Lewisburg RAID*/
{ PCI_VDEVICE(INTEL, 0x2826), board_ahci }, /* Lewisburg RAID*/
{ PCI_VDEVICE(INTEL, 0xa18e), board_ahci }, /* Lewisburg RAID*/
{ PCI_VDEVICE(INTEL, 0xa20e), board_ahci }, /* Lewisburg 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 */
@ -489,6 +499,8 @@ static const struct pci_device_id ahci_pci_tbl[] = {
.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, 0x91a2), /* 88se91a2 */
.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),

View File

@ -181,6 +181,8 @@ enum {
PORT_CMD_ALPE = (1 << 26), /* Aggressive Link PM enable */
PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */
PORT_CMD_FBSCP = (1 << 22), /* FBS Capable Port */
PORT_CMD_ESP = (1 << 21), /* External Sata Port */
PORT_CMD_HPCP = (1 << 18), /* HotPlug Capable Port */
PORT_CMD_PMP = (1 << 17), /* PMP attached */
PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */
PORT_CMD_FIS_ON = (1 << 14), /* FIS DMA engine running */

View File

@ -76,7 +76,6 @@ static const struct of_device_id ahci_of_match[] = {
{ .compatible = "ibm,476gtr-ahci", },
{ .compatible = "snps,dwc-ahci", },
{ .compatible = "hisilicon,hisi-ahci", },
{ .compatible = "fsl,qoriq-ahci", },
{},
};
MODULE_DEVICE_TABLE(of, ahci_of_match);

279
drivers/ata/ahci_qoriq.c Normal file
View File

@ -0,0 +1,279 @@
/*
* Freescale QorIQ AHCI SATA platform driver
*
* Copyright 2015 Freescale, Inc.
* Tang Yuantian <Yuantian.Tang@freescale.com>
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/ahci_platform.h>
#include <linux/device.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/libata.h>
#include "ahci.h"
#define DRV_NAME "ahci-qoriq"
/* port register definition */
#define PORT_PHY1 0xA8
#define PORT_PHY2 0xAC
#define PORT_PHY3 0xB0
#define PORT_PHY4 0xB4
#define PORT_PHY5 0xB8
#define PORT_TRANS 0xC8
/* port register default value */
#define AHCI_PORT_PHY_1_CFG 0xa003fffe
#define AHCI_PORT_PHY_2_CFG 0x28183411
#define AHCI_PORT_PHY_3_CFG 0x0e081004
#define AHCI_PORT_PHY_4_CFG 0x00480811
#define AHCI_PORT_PHY_5_CFG 0x192c96a4
#define AHCI_PORT_TRANS_CFG 0x08000025
#define SATA_ECC_DISABLE 0x00020000
enum ahci_qoriq_type {
AHCI_LS1021A,
AHCI_LS1043A,
AHCI_LS2080A,
};
struct ahci_qoriq_priv {
struct ccsr_ahci *reg_base;
enum ahci_qoriq_type type;
void __iomem *ecc_addr;
};
static const struct of_device_id ahci_qoriq_of_match[] = {
{ .compatible = "fsl,ls1021a-ahci", .data = (void *)AHCI_LS1021A},
{ .compatible = "fsl,ls1043a-ahci", .data = (void *)AHCI_LS1043A},
{ .compatible = "fsl,ls2080a-ahci", .data = (void *)AHCI_LS2080A},
{},
};
MODULE_DEVICE_TABLE(of, ahci_qoriq_of_match);
static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
void __iomem *port_mmio = ahci_port_base(link->ap);
u32 px_cmd, px_is, px_val;
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_qoriq_priv *qoriq_priv = hpriv->plat_data;
u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
struct ata_taskfile tf;
bool online;
int rc;
bool ls1021a_workaround = (qoriq_priv->type == AHCI_LS1021A);
DPRINTK("ENTER\n");
ahci_stop_engine(ap);
/*
* There is a errata on ls1021a Rev1.0 and Rev2.0 which is:
* A-009042: The device detection initialization sequence
* mistakenly resets some registers.
*
* Workaround for this is:
* The software should read and store PxCMD and PxIS values
* before issuing the device detection initialization sequence.
* After the sequence is complete, software should restore the
* PxCMD and PxIS with the stored values.
*/
if (ls1021a_workaround) {
px_cmd = readl(port_mmio + PORT_CMD);
px_is = readl(port_mmio + PORT_IRQ_STAT);
}
/* 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, timing, deadline, &online,
ahci_check_ready);
/* restore the PxCMD and PxIS on ls1021 */
if (ls1021a_workaround) {
px_val = readl(port_mmio + PORT_CMD);
if (px_val != px_cmd)
writel(px_cmd, port_mmio + PORT_CMD);
px_val = readl(port_mmio + PORT_IRQ_STAT);
if (px_val != px_is)
writel(px_is, port_mmio + PORT_IRQ_STAT);
}
hpriv->start_engine(ap);
if (online)
*class = ahci_dev_classify(ap);
DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
return rc;
}
static struct ata_port_operations ahci_qoriq_ops = {
.inherits = &ahci_ops,
.hardreset = ahci_qoriq_hardreset,
};
static struct ata_port_info ahci_qoriq_port_info = {
.flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_qoriq_ops,
};
static struct scsi_host_template ahci_qoriq_sht = {
AHCI_SHT(DRV_NAME),
};
static int ahci_qoriq_phy_init(struct ahci_host_priv *hpriv)
{
struct ahci_qoriq_priv *qpriv = hpriv->plat_data;
void __iomem *reg_base = hpriv->mmio;
switch (qpriv->type) {
case AHCI_LS1021A:
writel(SATA_ECC_DISABLE, qpriv->ecc_addr);
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
writel(AHCI_PORT_PHY_2_CFG, reg_base + PORT_PHY2);
writel(AHCI_PORT_PHY_3_CFG, reg_base + PORT_PHY3);
writel(AHCI_PORT_PHY_4_CFG, reg_base + PORT_PHY4);
writel(AHCI_PORT_PHY_5_CFG, reg_base + PORT_PHY5);
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
break;
case AHCI_LS1043A:
case AHCI_LS2080A:
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
break;
}
return 0;
}
static int ahci_qoriq_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct ahci_host_priv *hpriv;
struct ahci_qoriq_priv *qoriq_priv;
const struct of_device_id *of_id;
struct resource *res;
int rc;
hpriv = ahci_platform_get_resources(pdev);
if (IS_ERR(hpriv))
return PTR_ERR(hpriv);
of_id = of_match_node(ahci_qoriq_of_match, np);
if (!of_id)
return -ENODEV;
qoriq_priv = devm_kzalloc(dev, sizeof(*qoriq_priv), GFP_KERNEL);
if (!qoriq_priv)
return -ENOMEM;
qoriq_priv->type = (enum ahci_qoriq_type)of_id->data;
if (qoriq_priv->type == AHCI_LS1021A) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"sata-ecc");
qoriq_priv->ecc_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(qoriq_priv->ecc_addr))
return PTR_ERR(qoriq_priv->ecc_addr);
}
rc = ahci_platform_enable_resources(hpriv);
if (rc)
return rc;
hpriv->plat_data = qoriq_priv;
rc = ahci_qoriq_phy_init(hpriv);
if (rc)
goto disable_resources;
/* Workaround for ls2080a */
if (qoriq_priv->type == AHCI_LS2080A) {
hpriv->flags |= AHCI_HFLAG_NO_NCQ;
ahci_qoriq_port_info.flags &= ~ATA_FLAG_NCQ;
}
rc = ahci_platform_init_host(pdev, hpriv, &ahci_qoriq_port_info,
&ahci_qoriq_sht);
if (rc)
goto disable_resources;
return 0;
disable_resources:
ahci_platform_disable_resources(hpriv);
return rc;
}
#ifdef CONFIG_PM_SLEEP
static int ahci_qoriq_resume(struct device *dev)
{
struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data;
int rc;
rc = ahci_platform_enable_resources(hpriv);
if (rc)
return rc;
rc = ahci_qoriq_phy_init(hpriv);
if (rc)
goto disable_resources;
rc = ahci_platform_resume_host(dev);
if (rc)
goto disable_resources;
/* We resumed so update PM runtime state */
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
disable_resources:
ahci_platform_disable_resources(hpriv);
return rc;
}
#endif
static SIMPLE_DEV_PM_OPS(ahci_qoriq_pm_ops, ahci_platform_suspend,
ahci_qoriq_resume);
static struct platform_driver ahci_qoriq_driver = {
.probe = ahci_qoriq_probe,
.remove = ata_platform_remove_one,
.driver = {
.name = DRV_NAME,
.of_match_table = ahci_qoriq_of_match,
.pm = &ahci_qoriq_pm_ops,
},
};
module_platform_driver(ahci_qoriq_driver);
MODULE_DESCRIPTION("Freescale QorIQ AHCI SATA platform driver");
MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");
MODULE_LICENSE("GPL");

View File

@ -1117,6 +1117,7 @@ static void ahci_port_init(struct device *dev, struct ata_port *ap,
int port_no, void __iomem *mmio,
void __iomem *port_mmio)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
const char *emsg = NULL;
int rc;
u32 tmp;
@ -1138,6 +1139,12 @@ static void ahci_port_init(struct device *dev, struct ata_port *ap,
writel(tmp, port_mmio + PORT_IRQ_STAT);
writel(1 << port_no, mmio + HOST_IRQ_STAT);
/* mark esata ports */
tmp = readl(port_mmio + PORT_CMD);
if ((tmp & PORT_CMD_HPCP) ||
((tmp & PORT_CMD_ESP) && (hpriv->cap & HOST_CAP_SXS)))
ap->pflags |= ATA_PFLAG_EXTERNAL;
}
void ahci_init_controller(struct ata_host *host)
@ -2486,28 +2493,13 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
rc = devm_request_threaded_irq(host->dev, irq + i,
ahci_multi_irqs_intr,
ahci_port_thread_fn, IRQF_SHARED,
ahci_port_thread_fn, 0,
pp->irq_desc, host->ports[i]);
if (rc)
goto out_free_irqs;
}
for (i = 0; i < host->n_ports; i++)
return rc;
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;
}
return ata_host_register(host, sht);
}
/**

View File

@ -1757,6 +1757,15 @@ nothing_to_do:
return 1;
}
static void ata_qc_done(struct ata_queued_cmd *qc)
{
struct scsi_cmnd *cmd = qc->scsicmd;
void (*done)(struct scsi_cmnd *) = qc->scsidone;
ata_qc_free(qc);
done(cmd);
}
static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
@ -1774,28 +1783,17 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
* asc,ascq = ATA PASS-THROUGH INFORMATION AVAILABLE
*/
if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) &&
((cdb[2] & 0x20) || need_sense)) {
((cdb[2] & 0x20) || need_sense))
ata_gen_passthru_sense(qc);
} else {
if (!need_sense) {
cmd->result = SAM_STAT_GOOD;
} else {
/* TODO: decide which descriptor format to use
* for 48b LBA devices and call that here
* instead of the fixed desc, which is only
* good for smaller LBA (and maybe CHS?)
* devices.
*/
ata_gen_ata_sense(qc);
}
}
else if (need_sense)
ata_gen_ata_sense(qc);
else
cmd->result = SAM_STAT_GOOD;
if (need_sense && !ap->ops->error_handler)
ata_dump_status(ap->print_id, &qc->result_tf);
qc->scsidone(cmd);
ata_qc_free(qc);
ata_qc_done(qc);
}
/**
@ -2015,8 +2013,11 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf)
VPRINTK("ENTER\n");
/* set scsi removable (RMB) bit per ata bit */
if (ata_id_removable(args->id))
/* set scsi removable (RMB) bit per ata bit, or if the
* AHCI port says it's external (Hotplug-capable, eSATA).
*/
if (ata_id_removable(args->id) ||
(args->dev->link->ap->pflags & ATA_PFLAG_EXTERNAL))
hdr[1] |= (1 << 7);
if (args->dev->class == ATA_DEV_ZAC) {
@ -2594,8 +2595,7 @@ static void atapi_sense_complete(struct ata_queued_cmd *qc)
ata_gen_passthru_sense(qc);
}
qc->scsidone(qc->scsicmd);
ata_qc_free(qc);
ata_qc_done(qc);
}
/* is it pointless to prefer PIO for "safety reasons"? */
@ -2690,8 +2690,7 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc)
qc->dev->sdev->locked = 0;
qc->scsicmd->result = SAM_STAT_CHECK_CONDITION;
qc->scsidone(cmd);
ata_qc_free(qc);
ata_qc_done(qc);
return;
}
@ -2735,8 +2734,7 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc)
cmd->result = SAM_STAT_GOOD;
}
qc->scsidone(cmd);
ata_qc_free(qc);
ata_qc_done(qc);
}
/**
* atapi_xlat - Initialize PACKET taskfile
@ -2914,12 +2912,14 @@ ata_scsi_map_proto(u8 byte1)
case 5: /* PIO Data-out */
return ATA_PROT_PIO;
case 12: /* FPDMA */
return ATA_PROT_NCQ;
case 0: /* Hard Reset */
case 1: /* SRST */
case 8: /* Device Diagnostic */
case 9: /* Device Reset */
case 7: /* DMA Queued */
case 12: /* FPDMA */
case 15: /* Return Response Info */
default: /* Reserved */
break;
@ -2947,6 +2947,9 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
if ((tf->protocol = ata_scsi_map_proto(cdb[1])) == ATA_PROT_UNKNOWN)
goto invalid_fld;
/* enable LBA */
tf->flags |= ATA_TFLAG_LBA;
/*
* 12 and 16 byte CDBs use different offsets to
* provide the various register values.
@ -2992,6 +2995,10 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
tf->command = cdb[9];
}
/* For NCQ commands with FPDMA protocol, copy the tag value */
if (tf->protocol == ATA_PROT_NCQ)
tf->nsect = qc->tag << 3;
/* enforce correct master/slave bit */
tf->device = dev->devno ?
tf->device | ATA_DEV1 : tf->device & ~ATA_DEV1;

View File

@ -604,9 +604,9 @@ static void it821x_display_disk(int n, u8 *buf)
{
unsigned char id[41];
int mode = 0;
char *mtype = "";
const char *mtype = "";
char mbuf[8];
char *cbl = "(40 wire cable)";
const char *cbl = "(40 wire cable)";
static const char *types[5] = {
"RAID0", "RAID1", "RAID 0+1", "JBOD", "DISK"
@ -903,7 +903,7 @@ static int it821x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
};
const struct ata_port_info *ppi[] = { NULL, NULL };
static char *mode[2] = { "pass through", "smart" };
static const char *mode[2] = { "pass through", "smart" };
int rc;
rc = pcim_enable_device(pdev);

View File

@ -1344,6 +1344,7 @@ static struct of_device_id pata_macio_match[] =
},
{},
};
MODULE_DEVICE_TABLE(of, pata_macio_match);
static struct macio_driver pata_macio_driver =
{

View File

@ -24,79 +24,36 @@
#include <linux/ata.h>
#include <linux/libata.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/dma/pxa-dma.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/completion.h>
#include <scsi/scsi_host.h>
#include <mach/pxa2xx-regs.h>
#include <linux/platform_data/ata-pxa.h>
#include <mach/dma.h>
#define DRV_NAME "pata_pxa"
#define DRV_VERSION "0.1"
struct pata_pxa_data {
uint32_t dma_channel;
struct pxa_dma_desc *dma_desc;
dma_addr_t dma_desc_addr;
uint32_t dma_desc_id;
/* DMA IO physical address */
uint32_t dma_io_addr;
/* PXA DREQ<0:2> pin selector */
uint32_t dma_dreq;
/* DMA DCSR register value */
uint32_t dma_dcsr;
struct dma_chan *dma_chan;
dma_cookie_t dma_cookie;
struct completion dma_done;
};
/*
* Setup the DMA descriptors. The size is transfer capped at 4k per descriptor,
* if the transfer is longer, it is split into multiple chained descriptors.
* DMA interrupt handler.
*/
static void pxa_load_dmac(struct scatterlist *sg, struct ata_queued_cmd *qc)
static void pxa_ata_dma_irq(void *d)
{
struct pata_pxa_data *pd = qc->ap->private_data;
struct pata_pxa_data *pd = d;
enum dma_status status;
uint32_t cpu_len, seg_len;
dma_addr_t cpu_addr;
cpu_addr = sg_dma_address(sg);
cpu_len = sg_dma_len(sg);
do {
seg_len = (cpu_len > 0x1000) ? 0x1000 : cpu_len;
pd->dma_desc[pd->dma_desc_id].ddadr = pd->dma_desc_addr +
((pd->dma_desc_id + 1) * sizeof(struct pxa_dma_desc));
pd->dma_desc[pd->dma_desc_id].dcmd = DCMD_BURST32 |
DCMD_WIDTH2 | (DCMD_LENGTH & seg_len);
if (qc->tf.flags & ATA_TFLAG_WRITE) {
pd->dma_desc[pd->dma_desc_id].dsadr = cpu_addr;
pd->dma_desc[pd->dma_desc_id].dtadr = pd->dma_io_addr;
pd->dma_desc[pd->dma_desc_id].dcmd |= DCMD_INCSRCADDR |
DCMD_FLOWTRG;
} else {
pd->dma_desc[pd->dma_desc_id].dsadr = pd->dma_io_addr;
pd->dma_desc[pd->dma_desc_id].dtadr = cpu_addr;
pd->dma_desc[pd->dma_desc_id].dcmd |= DCMD_INCTRGADDR |
DCMD_FLOWSRC;
}
cpu_len -= seg_len;
cpu_addr += seg_len;
pd->dma_desc_id++;
} while (cpu_len);
/* Should not happen */
if (seg_len & 0x1f)
DALGN |= (1 << pd->dma_dreq);
status = dmaengine_tx_status(pd->dma_chan, pd->dma_cookie, NULL);
if (status == DMA_ERROR || status == DMA_COMPLETE)
complete(&pd->dma_done);
}
/*
@ -105,28 +62,22 @@ static void pxa_load_dmac(struct scatterlist *sg, struct ata_queued_cmd *qc)
static void pxa_qc_prep(struct ata_queued_cmd *qc)
{
struct pata_pxa_data *pd = qc->ap->private_data;
int si = 0;
struct scatterlist *sg;
struct dma_async_tx_descriptor *tx;
enum dma_transfer_direction dir;
if (!(qc->flags & ATA_QCFLAG_DMAMAP))
return;
pd->dma_desc_id = 0;
DCSR(pd->dma_channel) = 0;
DALGN &= ~(1 << pd->dma_dreq);
for_each_sg(qc->sg, sg, qc->n_elem, si)
pxa_load_dmac(sg, qc);
pd->dma_desc[pd->dma_desc_id - 1].ddadr = DDADR_STOP;
/* Fire IRQ only at the end of last block */
pd->dma_desc[pd->dma_desc_id - 1].dcmd |= DCMD_ENDIRQEN;
DDADR(pd->dma_channel) = pd->dma_desc_addr;
DRCMR(pd->dma_dreq) = DRCMR_MAPVLD | pd->dma_channel;
dir = (qc->dma_dir == DMA_TO_DEVICE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM);
tx = dmaengine_prep_slave_sg(pd->dma_chan, qc->sg, qc->n_elem, dir,
DMA_PREP_INTERRUPT);
if (!tx) {
ata_dev_err(qc->dev, "prep_slave_sg() failed\n");
return;
}
tx->callback = pxa_ata_dma_irq;
tx->callback_param = pd;
pd->dma_cookie = dmaengine_submit(tx);
}
/*
@ -145,7 +96,7 @@ static void pxa_bmdma_start(struct ata_queued_cmd *qc)
{
struct pata_pxa_data *pd = qc->ap->private_data;
init_completion(&pd->dma_done);
DCSR(pd->dma_channel) = DCSR_RUN;
dma_async_issue_pending(pd->dma_chan);
}
/*
@ -154,12 +105,14 @@ static void pxa_bmdma_start(struct ata_queued_cmd *qc)
static void pxa_bmdma_stop(struct ata_queued_cmd *qc)
{
struct pata_pxa_data *pd = qc->ap->private_data;
enum dma_status status;
if ((DCSR(pd->dma_channel) & DCSR_RUN) &&
wait_for_completion_timeout(&pd->dma_done, HZ))
dev_err(qc->ap->dev, "Timeout waiting for DMA completion!");
status = dmaengine_tx_status(pd->dma_chan, pd->dma_cookie, NULL);
if (status != DMA_ERROR && status != DMA_COMPLETE &&
wait_for_completion_timeout(&pd->dma_done, HZ))
ata_dev_err(qc->dev, "Timeout waiting for DMA completion!");
DCSR(pd->dma_channel) = 0;
dmaengine_terminate_all(pd->dma_chan);
}
/*
@ -170,8 +123,11 @@ static unsigned char pxa_bmdma_status(struct ata_port *ap)
{
struct pata_pxa_data *pd = ap->private_data;
unsigned char ret = ATA_DMA_INTR;
struct dma_tx_state state;
enum dma_status status;
if (pd->dma_dcsr & DCSR_BUSERR)
status = dmaengine_tx_status(pd->dma_chan, pd->dma_cookie, &state);
if (status != DMA_COMPLETE)
ret |= ATA_DMA_ERR;
return ret;
@ -213,21 +169,6 @@ static struct ata_port_operations pxa_ata_port_ops = {
.qc_prep = pxa_qc_prep,
};
/*
* DMA interrupt handler.
*/
static void pxa_ata_dma_irq(int dma, void *port)
{
struct ata_port *ap = port;
struct pata_pxa_data *pd = ap->private_data;
pd->dma_dcsr = DCSR(dma);
DCSR(dma) = pd->dma_dcsr;
if (pd->dma_dcsr & DCSR_STOPSTATE)
complete(&pd->dma_done);
}
static int pxa_ata_probe(struct platform_device *pdev)
{
struct ata_host *host;
@ -238,6 +179,9 @@ static int pxa_ata_probe(struct platform_device *pdev)
struct resource *dma_res;
struct resource *irq_res;
struct pata_pxa_pdata *pdata = dev_get_platdata(&pdev->dev);
struct dma_slave_config config;
dma_cap_mask_t mask;
struct pxad_param param;
int ret = 0;
/*
@ -333,29 +277,32 @@ static int pxa_ata_probe(struct platform_device *pdev)
return -ENOMEM;
ap->private_data = data;
data->dma_dreq = pdata->dma_dreq;
data->dma_io_addr = dma_res->start;
/*
* Allocate space for the DMA descriptors
*/
data->dma_desc = dmam_alloc_coherent(&pdev->dev, PAGE_SIZE,
&data->dma_desc_addr, GFP_KERNEL);
if (!data->dma_desc)
return -EINVAL;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
param.prio = PXAD_PRIO_LOWEST;
param.drcmr = pdata->dma_dreq;
memset(&config, 0, sizeof(config));
config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
config.src_addr = dma_res->start;
config.dst_addr = dma_res->start;
config.src_maxburst = 32;
config.dst_maxburst = 32;
/*
* Request the DMA channel
*/
data->dma_channel = pxa_request_dma(DRV_NAME, DMA_PRIO_LOW,
pxa_ata_dma_irq, ap);
if (data->dma_channel < 0)
data->dma_chan =
dma_request_slave_channel_compat(mask, pxad_filter_fn,
&param, &pdev->dev, "data");
if (!data->dma_chan)
return -EBUSY;
/*
* Stop and clear the DMA channel
*/
DCSR(data->dma_channel) = 0;
ret = dmaengine_slave_config(data->dma_chan, &config);
if (ret < 0) {
dev_err(&pdev->dev, "dma configuration failed: %d\n", ret);
return ret;
}
/*
* Activate the ATA host
@ -363,7 +310,7 @@ static int pxa_ata_probe(struct platform_device *pdev)
ret = ata_host_activate(host, irq_res->start, ata_sff_interrupt,
pdata->irq_flags, &pxa_ata_sht);
if (ret)
pxa_free_dma(data->dma_channel);
dma_release_channel(data->dma_chan);
return ret;
}
@ -373,7 +320,7 @@ static int pxa_ata_remove(struct platform_device *pdev)
struct ata_host *host = platform_get_drvdata(pdev);
struct pata_pxa_data *data = host->ports[0]->private_data;
pxa_free_dma(data->dma_channel);
dma_release_channel(data->dma_chan);
ata_host_detach(host);

View File

@ -70,7 +70,7 @@ struct s3c_ide_info {
struct clk *clk;
void __iomem *ide_addr;
void __iomem *sfr_addr;
unsigned int irq;
int irq;
enum s3c_cpu_type cpu_type;
unsigned int fifo_status_reg;
};

View File

@ -254,6 +254,7 @@ enum {
ATA_PFLAG_PIO32 = (1 << 20), /* 32bit PIO */
ATA_PFLAG_PIO32CHANGE = (1 << 21), /* 32bit PIO can be turned on/off */
ATA_PFLAG_EXTERNAL = (1 << 22), /* eSATA/external port */
/* struct ata_queued_cmd flags */
ATA_QCFLAG_ACTIVE = (1 << 0), /* cmd not yet ack'd to scsi lyer */