sata_fsl: add support for interrupt coalsecing feature
Adds support for interrupt coalescing feature to reduce interrupt events. Provides a mechanism of adjusting coalescing count and timeout tick by sysfs at runtime, so that tradeoff of latency and CPU load can be made depending on different applications. Signed-off-by: Qiang Liu <qiang.liu@freescale.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
This commit is contained in:
parent
40679b3ce9
commit
6b4b8fc87d
|
@ -6,7 +6,7 @@
|
||||||
* Author: Ashish Kalra <ashish.kalra@freescale.com>
|
* Author: Ashish Kalra <ashish.kalra@freescale.com>
|
||||||
* Li Yang <leoli@freescale.com>
|
* Li Yang <leoli@freescale.com>
|
||||||
*
|
*
|
||||||
* Copyright (c) 2006-2007, 2011 Freescale Semiconductor, Inc.
|
* Copyright (c) 2006-2007, 2011-2012 Freescale Semiconductor, Inc.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* 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
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
@ -26,6 +26,15 @@
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
|
|
||||||
|
static unsigned int intr_coalescing_count;
|
||||||
|
module_param(intr_coalescing_count, int, S_IRUGO);
|
||||||
|
MODULE_PARM_DESC(intr_coalescing_count,
|
||||||
|
"INT coalescing count threshold (1..31)");
|
||||||
|
|
||||||
|
static unsigned int intr_coalescing_ticks;
|
||||||
|
module_param(intr_coalescing_ticks, int, S_IRUGO);
|
||||||
|
MODULE_PARM_DESC(intr_coalescing_ticks,
|
||||||
|
"INT coalescing timer threshold in AHB ticks");
|
||||||
/* Controller information */
|
/* Controller information */
|
||||||
enum {
|
enum {
|
||||||
SATA_FSL_QUEUE_DEPTH = 16,
|
SATA_FSL_QUEUE_DEPTH = 16,
|
||||||
|
@ -82,6 +91,16 @@ enum {
|
||||||
SATA_FSL_IRQ_FLAG = IRQF_SHARED,
|
SATA_FSL_IRQ_FLAG = IRQF_SHARED,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interrupt Coalescing Control Register bitdefs */
|
||||||
|
enum {
|
||||||
|
ICC_MIN_INT_COUNT_THRESHOLD = 1,
|
||||||
|
ICC_MAX_INT_COUNT_THRESHOLD = ((1 << 5) - 1),
|
||||||
|
ICC_MIN_INT_TICKS_THRESHOLD = 0,
|
||||||
|
ICC_MAX_INT_TICKS_THRESHOLD = ((1 << 19) - 1),
|
||||||
|
ICC_SAFE_INT_TICKS = 1,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Host Controller command register set - per port
|
* Host Controller command register set - per port
|
||||||
*/
|
*/
|
||||||
|
@ -263,8 +282,65 @@ struct sata_fsl_host_priv {
|
||||||
void __iomem *csr_base;
|
void __iomem *csr_base;
|
||||||
int irq;
|
int irq;
|
||||||
int data_snoop;
|
int data_snoop;
|
||||||
|
struct device_attribute intr_coalescing;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void fsl_sata_set_irq_coalescing(struct ata_host *host,
|
||||||
|
unsigned int count, unsigned int ticks)
|
||||||
|
{
|
||||||
|
struct sata_fsl_host_priv *host_priv = host->private_data;
|
||||||
|
void __iomem *hcr_base = host_priv->hcr_base;
|
||||||
|
|
||||||
|
if (count > ICC_MAX_INT_COUNT_THRESHOLD)
|
||||||
|
count = ICC_MAX_INT_COUNT_THRESHOLD;
|
||||||
|
else if (count < ICC_MIN_INT_COUNT_THRESHOLD)
|
||||||
|
count = ICC_MIN_INT_COUNT_THRESHOLD;
|
||||||
|
|
||||||
|
if (ticks > ICC_MAX_INT_TICKS_THRESHOLD)
|
||||||
|
ticks = ICC_MAX_INT_TICKS_THRESHOLD;
|
||||||
|
else if ((ICC_MIN_INT_TICKS_THRESHOLD == ticks) &&
|
||||||
|
(count > ICC_MIN_INT_COUNT_THRESHOLD))
|
||||||
|
ticks = ICC_SAFE_INT_TICKS;
|
||||||
|
|
||||||
|
spin_lock(&host->lock);
|
||||||
|
iowrite32((count << 24 | ticks), hcr_base + ICC);
|
||||||
|
|
||||||
|
intr_coalescing_count = count;
|
||||||
|
intr_coalescing_ticks = ticks;
|
||||||
|
spin_unlock(&host->lock);
|
||||||
|
|
||||||
|
DPRINTK("intrrupt coalescing, count = 0x%x, ticks = %x\n",
|
||||||
|
intr_coalescing_count, intr_coalescing_ticks);
|
||||||
|
DPRINTK("ICC register status: (hcr base: 0x%x) = 0x%x\n",
|
||||||
|
hcr_base, ioread32(hcr_base + ICC));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t fsl_sata_intr_coalescing_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
return sprintf(buf, "%d %d\n",
|
||||||
|
intr_coalescing_count, intr_coalescing_ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t fsl_sata_intr_coalescing_store(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
unsigned int coalescing_count, coalescing_ticks;
|
||||||
|
|
||||||
|
if (sscanf(buf, "%d%d",
|
||||||
|
&coalescing_count,
|
||||||
|
&coalescing_ticks) != 2) {
|
||||||
|
printk(KERN_ERR "fsl-sata: wrong parameter format.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fsl_sata_set_irq_coalescing(dev_get_drvdata(dev),
|
||||||
|
coalescing_count, coalescing_ticks);
|
||||||
|
|
||||||
|
return strlen(buf);
|
||||||
|
}
|
||||||
|
|
||||||
static inline unsigned int sata_fsl_tag(unsigned int tag,
|
static inline unsigned int sata_fsl_tag(unsigned int tag,
|
||||||
void __iomem *hcr_base)
|
void __iomem *hcr_base)
|
||||||
{
|
{
|
||||||
|
@ -346,10 +422,10 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc,
|
||||||
(unsigned long long)sg_addr, sg_len);
|
(unsigned long long)sg_addr, sg_len);
|
||||||
|
|
||||||
/* warn if each s/g element is not dword aligned */
|
/* warn if each s/g element is not dword aligned */
|
||||||
if (sg_addr & 0x03)
|
if (unlikely(sg_addr & 0x03))
|
||||||
ata_port_err(qc->ap, "s/g addr unaligned : 0x%llx\n",
|
ata_port_err(qc->ap, "s/g addr unaligned : 0x%llx\n",
|
||||||
(unsigned long long)sg_addr);
|
(unsigned long long)sg_addr);
|
||||||
if (sg_len & 0x03)
|
if (unlikely(sg_len & 0x03))
|
||||||
ata_port_err(qc->ap, "s/g len unaligned : 0x%x\n",
|
ata_port_err(qc->ap, "s/g len unaligned : 0x%x\n",
|
||||||
sg_len);
|
sg_len);
|
||||||
|
|
||||||
|
@ -1245,6 +1321,13 @@ static int sata_fsl_init_controller(struct ata_host *host)
|
||||||
iowrite32(0x00000FFFF, hcr_base + CE);
|
iowrite32(0x00000FFFF, hcr_base + CE);
|
||||||
iowrite32(0x00000FFFF, hcr_base + DE);
|
iowrite32(0x00000FFFF, hcr_base + DE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* reset the number of command complete bits which will cause the
|
||||||
|
* interrupt to be signaled
|
||||||
|
*/
|
||||||
|
fsl_sata_set_irq_coalescing(host, intr_coalescing_count,
|
||||||
|
intr_coalescing_ticks);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* host controller will be brought on-line, during xx_port_start()
|
* host controller will be brought on-line, during xx_port_start()
|
||||||
* callback, that should also initiate the OOB, COMINIT sequence
|
* callback, that should also initiate the OOB, COMINIT sequence
|
||||||
|
@ -1309,7 +1392,7 @@ static int sata_fsl_probe(struct platform_device *ofdev)
|
||||||
void __iomem *csr_base = NULL;
|
void __iomem *csr_base = NULL;
|
||||||
struct sata_fsl_host_priv *host_priv = NULL;
|
struct sata_fsl_host_priv *host_priv = NULL;
|
||||||
int irq;
|
int irq;
|
||||||
struct ata_host *host;
|
struct ata_host *host = NULL;
|
||||||
u32 temp;
|
u32 temp;
|
||||||
|
|
||||||
struct ata_port_info pi = sata_fsl_port_info[0];
|
struct ata_port_info pi = sata_fsl_port_info[0];
|
||||||
|
@ -1356,6 +1439,10 @@ static int sata_fsl_probe(struct platform_device *ofdev)
|
||||||
|
|
||||||
/* allocate host structure */
|
/* allocate host structure */
|
||||||
host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_FSL_MAX_PORTS);
|
host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_FSL_MAX_PORTS);
|
||||||
|
if (!host) {
|
||||||
|
retval = -ENOMEM;
|
||||||
|
goto error_exit_with_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
/* host->iomap is not used currently */
|
/* host->iomap is not used currently */
|
||||||
host->private_data = host_priv;
|
host->private_data = host_priv;
|
||||||
|
@ -1373,10 +1460,24 @@ static int sata_fsl_probe(struct platform_device *ofdev)
|
||||||
|
|
||||||
dev_set_drvdata(&ofdev->dev, host);
|
dev_set_drvdata(&ofdev->dev, host);
|
||||||
|
|
||||||
|
host_priv->intr_coalescing.show = fsl_sata_intr_coalescing_show;
|
||||||
|
host_priv->intr_coalescing.store = fsl_sata_intr_coalescing_store;
|
||||||
|
sysfs_attr_init(&host_priv->intr_coalescing.attr);
|
||||||
|
host_priv->intr_coalescing.attr.name = "intr_coalescing";
|
||||||
|
host_priv->intr_coalescing.attr.mode = S_IRUGO | S_IWUSR;
|
||||||
|
retval = device_create_file(host->dev, &host_priv->intr_coalescing);
|
||||||
|
if (retval)
|
||||||
|
goto error_exit_with_cleanup;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error_exit_with_cleanup:
|
error_exit_with_cleanup:
|
||||||
|
|
||||||
|
if (host) {
|
||||||
|
dev_set_drvdata(&ofdev->dev, NULL);
|
||||||
|
ata_host_detach(host);
|
||||||
|
}
|
||||||
|
|
||||||
if (hcr_base)
|
if (hcr_base)
|
||||||
iounmap(hcr_base);
|
iounmap(hcr_base);
|
||||||
if (host_priv)
|
if (host_priv)
|
||||||
|
@ -1390,6 +1491,8 @@ static int sata_fsl_remove(struct platform_device *ofdev)
|
||||||
struct ata_host *host = dev_get_drvdata(&ofdev->dev);
|
struct ata_host *host = dev_get_drvdata(&ofdev->dev);
|
||||||
struct sata_fsl_host_priv *host_priv = host->private_data;
|
struct sata_fsl_host_priv *host_priv = host->private_data;
|
||||||
|
|
||||||
|
device_remove_file(&ofdev->dev, &host_priv->intr_coalescing);
|
||||||
|
|
||||||
ata_host_detach(host);
|
ata_host_detach(host);
|
||||||
|
|
||||||
dev_set_drvdata(&ofdev->dev, NULL);
|
dev_set_drvdata(&ofdev->dev, NULL);
|
||||||
|
|
Loading…
Reference in a new issue