Merge branch 'for-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata
Pull libata updates from Tejun Heo: "Mostly low level driver specific changes. Two changes are somewhat noteworthy. First, Dan's patchset to support per-port msix interrupt handling for ahci, which was tried last cycle but had to be backed out due to a couple issues, is back and seems to be working fine. Second, libata exception handling now uses usleep_range() instead of msleep() for sleeps < 20ms which can make things snappier in some corner cases" * 'for-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata: libata: skip debounce delay on link resume ata: ahci_brcmstb: disable DIPM support ata: ahci_brcmstb: enable support for ALPM drivers: libata-core: Use usleep_range() instead of msleep() for short sleeps (<20 ms) sata_sx4: correctly handling failed allocation ata: ahci_brcmstb: add support for MIPS-based platforms ahci: qoriq: Adjust the default register values on ls1021a ahci: qoriq: Update the default Rx watermark value ahci: qoriq: Adjust the default register values on ls1043a ahci: compile out msi/msix infrastructure ata: core: fix irq description on AHCI single irq systems ata: ahci_brcmstb: remove unused definitions ata: ahci_brcmstb: add a quirk for MIPS-based platforms ata: ahci_brcmstb: disable NCQ for MIPS-based platforms ata: sata_rcar: Remove obsolete platform_device_id entries sata_rcar: Add compatible string for r8a7795 ahci: kill 'intr_status' ahci: switch from 'threaded' to 'hardirq' interrupt handling ahci: per-port msix support
This commit is contained in:
commit
8c930204ce
|
@ -4,7 +4,9 @@ SATA nodes are defined to describe on-chip Serial ATA controllers.
|
||||||
Each SATA controller should have its own node.
|
Each SATA controller should have its own node.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : compatible list, may contain "brcm,bcm7445-ahci" and/or
|
- compatible : should be one or more of
|
||||||
|
"brcm,bcm7425-ahci"
|
||||||
|
"brcm,bcm7445-ahci"
|
||||||
"brcm,sata3-ahci"
|
"brcm,sata3-ahci"
|
||||||
- reg : register mappings for AHCI and SATA_TOP_CTRL
|
- reg : register mappings for AHCI and SATA_TOP_CTRL
|
||||||
- reg-names : "ahci" and "top-ctrl"
|
- reg-names : "ahci" and "top-ctrl"
|
||||||
|
|
|
@ -8,6 +8,7 @@ Required properties:
|
||||||
- "renesas,sata-r8a7790" for R-Car H2 other than ES1
|
- "renesas,sata-r8a7790" for R-Car H2 other than ES1
|
||||||
- "renesas,sata-r8a7791" for R-Car M2-W
|
- "renesas,sata-r8a7791" for R-Car M2-W
|
||||||
- "renesas,sata-r8a7793" for R-Car M2-N
|
- "renesas,sata-r8a7793" for R-Car M2-N
|
||||||
|
- "renesas,sata-r8a7795" for R-Car H3
|
||||||
- reg : address and length of the SATA registers;
|
- reg : address and length of the SATA registers;
|
||||||
- interrupts : must consist of one interrupt specifier.
|
- interrupts : must consist of one interrupt specifier.
|
||||||
- clocks : must contain a reference to the functional clock.
|
- clocks : must contain a reference to the functional clock.
|
||||||
|
|
|
@ -100,7 +100,7 @@ config SATA_AHCI_PLATFORM
|
||||||
|
|
||||||
config AHCI_BRCMSTB
|
config AHCI_BRCMSTB
|
||||||
tristate "Broadcom STB AHCI SATA support"
|
tristate "Broadcom STB AHCI SATA support"
|
||||||
depends on ARCH_BRCMSTB
|
depends on ARCH_BRCMSTB || BMIPS_GENERIC
|
||||||
help
|
help
|
||||||
This option enables support for the AHCI SATA3 controller found on
|
This option enables support for the AHCI SATA3 controller found on
|
||||||
STB SoC's.
|
STB SoC's.
|
||||||
|
|
|
@ -1306,15 +1306,13 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ahci_init_msix() only implements single MSI-X support, not multiple
|
* ahci_init_msix() - optionally enable per-port MSI-X otherwise defer
|
||||||
* MSI-X per-port interrupts. This is needed for host controllers that only
|
* to single msi.
|
||||||
* have MSI-X support implemented, but no MSI or intx.
|
|
||||||
*/
|
*/
|
||||||
static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
|
static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
|
||||||
struct ahci_host_priv *hpriv)
|
struct ahci_host_priv *hpriv, unsigned long flags)
|
||||||
{
|
{
|
||||||
int rc, nvec;
|
int nvec, i, rc;
|
||||||
struct msix_entry entry = {};
|
|
||||||
|
|
||||||
/* Do not init MSI-X if MSI is disabled for the device */
|
/* Do not init MSI-X if MSI is disabled for the device */
|
||||||
if (hpriv->flags & AHCI_HFLAG_NO_MSI)
|
if (hpriv->flags & AHCI_HFLAG_NO_MSI)
|
||||||
|
@ -1324,22 +1322,39 @@ static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
|
||||||
if (nvec < 0)
|
if (nvec < 0)
|
||||||
return nvec;
|
return nvec;
|
||||||
|
|
||||||
if (!nvec) {
|
/*
|
||||||
|
* Proper MSI-X implementations will have a vector per-port.
|
||||||
|
* Barring that, we prefer single-MSI over single-MSIX. If this
|
||||||
|
* check fails (not enough MSI-X vectors for all ports) we will
|
||||||
|
* be called again with the flag clear iff ahci_init_msi()
|
||||||
|
* fails.
|
||||||
|
*/
|
||||||
|
if (flags & AHCI_HFLAG_MULTI_MSIX) {
|
||||||
|
if (nvec < n_ports)
|
||||||
|
return -ENODEV;
|
||||||
|
nvec = n_ports;
|
||||||
|
} else if (nvec) {
|
||||||
|
nvec = 1;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Emit dev_err() since this was the non-legacy irq
|
||||||
|
* method of last resort.
|
||||||
|
*/
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
for (i = 0; i < nvec; i++)
|
||||||
* There can be more than one vector (e.g. for error detection or
|
hpriv->msix[i].entry = i;
|
||||||
* hdd hotplug). Only the first vector (entry.entry = 0) is used.
|
rc = pci_enable_msix_exact(pdev, hpriv->msix, nvec);
|
||||||
*/
|
|
||||||
rc = pci_enable_msix_exact(pdev, &entry, 1);
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
hpriv->irq = entry.vector;
|
if (nvec > 1)
|
||||||
|
hpriv->flags |= AHCI_HFLAG_MULTI_MSIX;
|
||||||
|
hpriv->irq = hpriv->msix[0].vector; /* for single msi-x */
|
||||||
|
|
||||||
return 1;
|
return nvec;
|
||||||
fail:
|
fail:
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"failed to enable MSI-X with error %d, # of vectors: %d\n",
|
"failed to enable MSI-X with error %d, # of vectors: %d\n",
|
||||||
|
@ -1403,20 +1418,25 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
|
||||||
{
|
{
|
||||||
int nvec;
|
int nvec;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to enable per-port MSI-X. If the host is not capable
|
||||||
|
* fall back to single MSI before finally attempting single
|
||||||
|
* MSI-X.
|
||||||
|
*/
|
||||||
|
nvec = ahci_init_msix(pdev, n_ports, hpriv, AHCI_HFLAG_MULTI_MSIX);
|
||||||
|
if (nvec >= 0)
|
||||||
|
return nvec;
|
||||||
|
|
||||||
nvec = ahci_init_msi(pdev, n_ports, hpriv);
|
nvec = ahci_init_msi(pdev, n_ports, hpriv);
|
||||||
if (nvec >= 0)
|
if (nvec >= 0)
|
||||||
return nvec;
|
return nvec;
|
||||||
|
|
||||||
/*
|
/* try single-msix */
|
||||||
* Currently, MSI-X support only implements single IRQ mode and
|
nvec = ahci_init_msix(pdev, n_ports, hpriv, 0);
|
||||||
* exists for controllers which can't do other types of IRQ. Only
|
|
||||||
* set it up if MSI fails.
|
|
||||||
*/
|
|
||||||
nvec = ahci_init_msix(pdev, n_ports, hpriv);
|
|
||||||
if (nvec >= 0)
|
if (nvec >= 0)
|
||||||
return nvec;
|
return nvec;
|
||||||
|
|
||||||
/* lagacy intx interrupts */
|
/* legacy intx interrupts */
|
||||||
pci_intx(pdev, 1);
|
pci_intx(pdev, 1);
|
||||||
hpriv->irq = pdev->irq;
|
hpriv->irq = pdev->irq;
|
||||||
|
|
||||||
|
@ -1578,7 +1598,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||||
if (!host)
|
if (!host)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
host->private_data = hpriv;
|
host->private_data = hpriv;
|
||||||
|
hpriv->msix = devm_kzalloc(&pdev->dev,
|
||||||
|
sizeof(struct msix_entry) * n_ports, GFP_KERNEL);
|
||||||
|
if (!hpriv->msix)
|
||||||
|
return -ENOMEM;
|
||||||
ahci_init_interrupts(pdev, n_ports, hpriv);
|
ahci_init_interrupts(pdev, n_ports, hpriv);
|
||||||
|
|
||||||
if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
|
if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#ifndef _AHCI_H
|
#ifndef _AHCI_H
|
||||||
#define _AHCI_H
|
#define _AHCI_H
|
||||||
|
|
||||||
|
#include <linux/pci.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/libata.h>
|
#include <linux/libata.h>
|
||||||
#include <linux/phy/phy.h>
|
#include <linux/phy/phy.h>
|
||||||
|
@ -237,11 +238,18 @@ enum {
|
||||||
AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on
|
AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on
|
||||||
port start (wait until
|
port start (wait until
|
||||||
error-handling stage) */
|
error-handling stage) */
|
||||||
AHCI_HFLAG_MULTI_MSI = (1 << 16), /* multiple PCI MSIs */
|
|
||||||
AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */
|
AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */
|
||||||
AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */
|
AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */
|
||||||
AHCI_HFLAG_EDGE_IRQ = (1 << 19), /* HOST_IRQ_STAT behaves as
|
AHCI_HFLAG_EDGE_IRQ = (1 << 19), /* HOST_IRQ_STAT behaves as
|
||||||
Edge Triggered */
|
Edge Triggered */
|
||||||
|
#ifdef CONFIG_PCI_MSI
|
||||||
|
AHCI_HFLAG_MULTI_MSI = (1 << 20), /* multiple PCI MSIs */
|
||||||
|
AHCI_HFLAG_MULTI_MSIX = (1 << 21), /* per-port MSI-X */
|
||||||
|
#else
|
||||||
|
/* compile out MSI infrastructure */
|
||||||
|
AHCI_HFLAG_MULTI_MSI = 0,
|
||||||
|
AHCI_HFLAG_MULTI_MSIX = 0,
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ap->flags bits */
|
/* ap->flags bits */
|
||||||
|
|
||||||
|
@ -308,7 +316,6 @@ struct ahci_port_priv {
|
||||||
unsigned int ncq_saw_d2h:1;
|
unsigned int ncq_saw_d2h:1;
|
||||||
unsigned int ncq_saw_dmas:1;
|
unsigned int ncq_saw_dmas:1;
|
||||||
unsigned int ncq_saw_sdb:1;
|
unsigned int ncq_saw_sdb:1;
|
||||||
atomic_t intr_status; /* interrupts to handle */
|
|
||||||
spinlock_t lock; /* protects parent ata_port */
|
spinlock_t lock; /* protects parent ata_port */
|
||||||
u32 intr_mask; /* interrupts to enable */
|
u32 intr_mask; /* interrupts to enable */
|
||||||
bool fbs_supported; /* set iff FBS is supported */
|
bool fbs_supported; /* set iff FBS is supported */
|
||||||
|
@ -343,6 +350,7 @@ struct ahci_host_priv {
|
||||||
* the PHY position in this array.
|
* the PHY position in this array.
|
||||||
*/
|
*/
|
||||||
struct phy **phys;
|
struct phy **phys;
|
||||||
|
struct msix_entry *msix; /* Optional MSI-X support */
|
||||||
unsigned nports; /* Number of ports */
|
unsigned nports; /* Number of ports */
|
||||||
void *plat_data; /* Other platform data */
|
void *plat_data; /* Other platform data */
|
||||||
unsigned int irq; /* interrupt line */
|
unsigned int irq; /* interrupt line */
|
||||||
|
@ -354,6 +362,21 @@ struct ahci_host_priv {
|
||||||
void (*start_engine)(struct ata_port *ap);
|
void (*start_engine)(struct ata_port *ap);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_PCI_MSI
|
||||||
|
static inline int ahci_irq_vector(struct ahci_host_priv *hpriv, int port)
|
||||||
|
{
|
||||||
|
if (hpriv->flags & AHCI_HFLAG_MULTI_MSIX)
|
||||||
|
return hpriv->msix[port].vector;
|
||||||
|
else
|
||||||
|
return hpriv->irq + port;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline int ahci_irq_vector(struct ahci_host_priv *hpriv, int port)
|
||||||
|
{
|
||||||
|
return hpriv->irq;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
extern int ahci_ignore_sss;
|
extern int ahci_ignore_sss;
|
||||||
|
|
||||||
extern struct device_attribute *ahci_shost_attrs[];
|
extern struct device_attribute *ahci_shost_attrs[];
|
||||||
|
|
|
@ -52,8 +52,10 @@
|
||||||
#define SATA_TOP_CTRL_2_PHY_GLOBAL_RESET BIT(14)
|
#define SATA_TOP_CTRL_2_PHY_GLOBAL_RESET BIT(14)
|
||||||
#define SATA_TOP_CTRL_PHY_OFFS 0x8
|
#define SATA_TOP_CTRL_PHY_OFFS 0x8
|
||||||
#define SATA_TOP_MAX_PHYS 2
|
#define SATA_TOP_MAX_PHYS 2
|
||||||
#define SATA_TOP_CTRL_SATA_TP_OUT 0x1c
|
|
||||||
#define SATA_TOP_CTRL_CLIENT_INIT_CTRL 0x20
|
#define SATA_FIRST_PORT_CTRL 0x700
|
||||||
|
#define SATA_NEXT_PORT_CTRL_OFFSET 0x80
|
||||||
|
#define SATA_PORT_PCTRL6(reg_base) (reg_base + 0x18)
|
||||||
|
|
||||||
/* On big-endian MIPS, buses are reversed to big endian, so switch them back */
|
/* On big-endian MIPS, buses are reversed to big endian, so switch them back */
|
||||||
#if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN)
|
#if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN)
|
||||||
|
@ -69,14 +71,21 @@
|
||||||
(DATA_ENDIAN << DMADESC_ENDIAN_SHIFT) | \
|
(DATA_ENDIAN << DMADESC_ENDIAN_SHIFT) | \
|
||||||
(MMIO_ENDIAN << MMIO_ENDIAN_SHIFT))
|
(MMIO_ENDIAN << MMIO_ENDIAN_SHIFT))
|
||||||
|
|
||||||
|
enum brcm_ahci_quirks {
|
||||||
|
BRCM_AHCI_QUIRK_NO_NCQ = BIT(0),
|
||||||
|
BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE = BIT(1),
|
||||||
|
};
|
||||||
|
|
||||||
struct brcm_ahci_priv {
|
struct brcm_ahci_priv {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
void __iomem *top_ctrl;
|
void __iomem *top_ctrl;
|
||||||
u32 port_mask;
|
u32 port_mask;
|
||||||
|
u32 quirks;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct ata_port_info ahci_brcm_port_info = {
|
static const struct ata_port_info ahci_brcm_port_info = {
|
||||||
.flags = AHCI_FLAG_COMMON,
|
.flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM,
|
||||||
|
.link_flags = ATA_LFLAG_NO_DB_DELAY,
|
||||||
.pio_mask = ATA_PIO4,
|
.pio_mask = ATA_PIO4,
|
||||||
.udma_mask = ATA_UDMA6,
|
.udma_mask = ATA_UDMA6,
|
||||||
.port_ops = &ahci_platform_ops,
|
.port_ops = &ahci_platform_ops,
|
||||||
|
@ -107,6 +116,34 @@ static inline void brcm_sata_writereg(u32 val, void __iomem *addr)
|
||||||
writel_relaxed(val, addr);
|
writel_relaxed(val, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void brcm_sata_alpm_init(struct ahci_host_priv *hpriv)
|
||||||
|
{
|
||||||
|
struct brcm_ahci_priv *priv = hpriv->plat_data;
|
||||||
|
u32 bus_ctrl, port_ctrl, host_caps;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Enable support for ALPM */
|
||||||
|
bus_ctrl = brcm_sata_readreg(priv->top_ctrl +
|
||||||
|
SATA_TOP_CTRL_BUS_CTRL);
|
||||||
|
brcm_sata_writereg(bus_ctrl | OVERRIDE_HWINIT,
|
||||||
|
priv->top_ctrl + SATA_TOP_CTRL_BUS_CTRL);
|
||||||
|
host_caps = readl(hpriv->mmio + HOST_CAP);
|
||||||
|
writel(host_caps | HOST_CAP_ALPM, hpriv->mmio);
|
||||||
|
brcm_sata_writereg(bus_ctrl, priv->top_ctrl + SATA_TOP_CTRL_BUS_CTRL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adjust timeout to allow PLL sufficient time to lock while waking
|
||||||
|
* up from slumber mode.
|
||||||
|
*/
|
||||||
|
for (i = 0, port_ctrl = SATA_FIRST_PORT_CTRL;
|
||||||
|
i < SATA_TOP_MAX_PHYS;
|
||||||
|
i++, port_ctrl += SATA_NEXT_PORT_CTRL_OFFSET) {
|
||||||
|
if (priv->port_mask & BIT(i))
|
||||||
|
writel(0xff1003fc,
|
||||||
|
hpriv->mmio + SATA_PORT_PCTRL6(port_ctrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void brcm_sata_phy_enable(struct brcm_ahci_priv *priv, int port)
|
static void brcm_sata_phy_enable(struct brcm_ahci_priv *priv, int port)
|
||||||
{
|
{
|
||||||
void __iomem *phyctrl = priv->top_ctrl + SATA_TOP_CTRL_PHY_CTRL +
|
void __iomem *phyctrl = priv->top_ctrl + SATA_TOP_CTRL_PHY_CTRL +
|
||||||
|
@ -114,6 +151,9 @@ static void brcm_sata_phy_enable(struct brcm_ahci_priv *priv, int port)
|
||||||
void __iomem *p;
|
void __iomem *p;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
|
if (priv->quirks & BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE)
|
||||||
|
return;
|
||||||
|
|
||||||
/* clear PHY_DEFAULT_POWER_STATE */
|
/* clear PHY_DEFAULT_POWER_STATE */
|
||||||
p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_1;
|
p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_1;
|
||||||
reg = brcm_sata_readreg(p);
|
reg = brcm_sata_readreg(p);
|
||||||
|
@ -143,6 +183,9 @@ static void brcm_sata_phy_disable(struct brcm_ahci_priv *priv, int port)
|
||||||
void __iomem *p;
|
void __iomem *p;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
|
if (priv->quirks & BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE)
|
||||||
|
return;
|
||||||
|
|
||||||
/* power-off the PHY digital logic */
|
/* power-off the PHY digital logic */
|
||||||
p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_2;
|
p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_2;
|
||||||
reg = brcm_sata_readreg(p);
|
reg = brcm_sata_readreg(p);
|
||||||
|
@ -230,6 +273,7 @@ static int brcm_ahci_resume(struct device *dev)
|
||||||
|
|
||||||
brcm_sata_init(priv);
|
brcm_sata_init(priv);
|
||||||
brcm_sata_phys_enable(priv);
|
brcm_sata_phys_enable(priv);
|
||||||
|
brcm_sata_alpm_init(hpriv);
|
||||||
return ahci_platform_resume(dev);
|
return ahci_platform_resume(dev);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -256,6 +300,11 @@ static int brcm_ahci_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(priv->top_ctrl))
|
if (IS_ERR(priv->top_ctrl))
|
||||||
return PTR_ERR(priv->top_ctrl);
|
return PTR_ERR(priv->top_ctrl);
|
||||||
|
|
||||||
|
if (of_device_is_compatible(dev->of_node, "brcm,bcm7425-ahci")) {
|
||||||
|
priv->quirks |= BRCM_AHCI_QUIRK_NO_NCQ;
|
||||||
|
priv->quirks |= BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE;
|
||||||
|
}
|
||||||
|
|
||||||
brcm_sata_init(priv);
|
brcm_sata_init(priv);
|
||||||
|
|
||||||
priv->port_mask = brcm_ahci_get_portmask(pdev, priv);
|
priv->port_mask = brcm_ahci_get_portmask(pdev, priv);
|
||||||
|
@ -269,10 +318,15 @@ static int brcm_ahci_probe(struct platform_device *pdev)
|
||||||
return PTR_ERR(hpriv);
|
return PTR_ERR(hpriv);
|
||||||
hpriv->plat_data = priv;
|
hpriv->plat_data = priv;
|
||||||
|
|
||||||
|
brcm_sata_alpm_init(hpriv);
|
||||||
|
|
||||||
ret = ahci_platform_enable_resources(hpriv);
|
ret = ahci_platform_enable_resources(hpriv);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
if (priv->quirks & BRCM_AHCI_QUIRK_NO_NCQ)
|
||||||
|
hpriv->flags |= AHCI_HFLAG_NO_NCQ;
|
||||||
|
|
||||||
ret = ahci_platform_init_host(pdev, hpriv, &ahci_brcm_port_info,
|
ret = ahci_platform_init_host(pdev, hpriv, &ahci_brcm_port_info,
|
||||||
&ahci_platform_sht);
|
&ahci_platform_sht);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -300,6 +354,7 @@ static int brcm_ahci_remove(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id ahci_of_match[] = {
|
static const struct of_device_id ahci_of_match[] = {
|
||||||
|
{.compatible = "brcm,bcm7425-ahci"},
|
||||||
{.compatible = "brcm,bcm7445-ahci"},
|
{.compatible = "brcm,bcm7445-ahci"},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,14 +34,20 @@
|
||||||
|
|
||||||
/* port register default value */
|
/* port register default value */
|
||||||
#define AHCI_PORT_PHY_1_CFG 0xa003fffe
|
#define AHCI_PORT_PHY_1_CFG 0xa003fffe
|
||||||
#define AHCI_PORT_PHY_2_CFG 0x28183411
|
#define AHCI_PORT_TRANS_CFG 0x08000029
|
||||||
#define AHCI_PORT_PHY_3_CFG 0x0e081004
|
|
||||||
#define AHCI_PORT_PHY_4_CFG 0x00480811
|
/* for ls1021a */
|
||||||
#define AHCI_PORT_PHY_5_CFG 0x192c96a4
|
#define LS1021A_PORT_PHY2 0x28183414
|
||||||
#define AHCI_PORT_TRANS_CFG 0x08000025
|
#define LS1021A_PORT_PHY3 0x0e080e06
|
||||||
|
#define LS1021A_PORT_PHY4 0x064a080b
|
||||||
|
#define LS1021A_PORT_PHY5 0x2aa86470
|
||||||
|
|
||||||
#define SATA_ECC_DISABLE 0x00020000
|
#define SATA_ECC_DISABLE 0x00020000
|
||||||
|
|
||||||
|
/* for ls1043a */
|
||||||
|
#define LS1043A_PORT_PHY2 0x28184d1f
|
||||||
|
#define LS1043A_PORT_PHY3 0x0e081509
|
||||||
|
|
||||||
enum ahci_qoriq_type {
|
enum ahci_qoriq_type {
|
||||||
AHCI_LS1021A,
|
AHCI_LS1021A,
|
||||||
AHCI_LS1043A,
|
AHCI_LS1043A,
|
||||||
|
@ -151,16 +157,23 @@ static int ahci_qoriq_phy_init(struct ahci_host_priv *hpriv)
|
||||||
case AHCI_LS1021A:
|
case AHCI_LS1021A:
|
||||||
writel(SATA_ECC_DISABLE, qpriv->ecc_addr);
|
writel(SATA_ECC_DISABLE, qpriv->ecc_addr);
|
||||||
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
|
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
|
||||||
writel(AHCI_PORT_PHY_2_CFG, reg_base + PORT_PHY2);
|
writel(LS1021A_PORT_PHY2, reg_base + PORT_PHY2);
|
||||||
writel(AHCI_PORT_PHY_3_CFG, reg_base + PORT_PHY3);
|
writel(LS1021A_PORT_PHY3, reg_base + PORT_PHY3);
|
||||||
writel(AHCI_PORT_PHY_4_CFG, reg_base + PORT_PHY4);
|
writel(LS1021A_PORT_PHY4, reg_base + PORT_PHY4);
|
||||||
writel(AHCI_PORT_PHY_5_CFG, reg_base + PORT_PHY5);
|
writel(LS1021A_PORT_PHY5, reg_base + PORT_PHY5);
|
||||||
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
|
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AHCI_LS1043A:
|
case AHCI_LS1043A:
|
||||||
|
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
|
||||||
|
writel(LS1043A_PORT_PHY2, reg_base + PORT_PHY2);
|
||||||
|
writel(LS1043A_PORT_PHY3, reg_base + PORT_PHY3);
|
||||||
|
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
|
||||||
|
break;
|
||||||
|
|
||||||
case AHCI_LS2080A:
|
case AHCI_LS2080A:
|
||||||
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
|
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
|
||||||
|
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include <scsi/scsi_host.h>
|
#include <scsi/scsi_host.h>
|
||||||
#include <scsi/scsi_cmnd.h>
|
#include <scsi/scsi_cmnd.h>
|
||||||
#include <linux/libata.h>
|
#include <linux/libata.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
#include "ahci.h"
|
#include "ahci.h"
|
||||||
#include "libata.h"
|
#include "libata.h"
|
||||||
|
|
||||||
|
@ -1804,29 +1805,10 @@ static void ahci_port_intr(struct ata_port *ap)
|
||||||
ahci_handle_port_interrupt(ap, port_mmio, status);
|
ahci_handle_port_interrupt(ap, port_mmio, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
|
static irqreturn_t ahci_multi_irqs_intr_hard(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;
|
struct ata_port *ap = dev_instance;
|
||||||
void __iomem *port_mmio = ahci_port_base(ap);
|
void __iomem *port_mmio = ahci_port_base(ap);
|
||||||
struct ahci_port_priv *pp = ap->private_data;
|
|
||||||
u32 status;
|
u32 status;
|
||||||
|
|
||||||
VPRINTK("ENTER\n");
|
VPRINTK("ENTER\n");
|
||||||
|
@ -1834,11 +1816,13 @@ static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
|
||||||
status = readl(port_mmio + PORT_IRQ_STAT);
|
status = readl(port_mmio + PORT_IRQ_STAT);
|
||||||
writel(status, port_mmio + PORT_IRQ_STAT);
|
writel(status, port_mmio + PORT_IRQ_STAT);
|
||||||
|
|
||||||
atomic_or(status, &pp->intr_status);
|
spin_lock(ap->lock);
|
||||||
|
ahci_handle_port_interrupt(ap, port_mmio, status);
|
||||||
|
spin_unlock(ap->lock);
|
||||||
|
|
||||||
VPRINTK("EXIT\n");
|
VPRINTK("EXIT\n");
|
||||||
|
|
||||||
return IRQ_WAKE_THREAD;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)
|
static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)
|
||||||
|
@ -2479,9 +2463,10 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ahci_set_em_messages);
|
EXPORT_SYMBOL_GPL(ahci_set_em_messages);
|
||||||
|
|
||||||
static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
|
static int ahci_host_activate_multi_irqs(struct ata_host *host,
|
||||||
struct scsi_host_template *sht)
|
struct scsi_host_template *sht)
|
||||||
{
|
{
|
||||||
|
struct ahci_host_priv *hpriv = host->private_data;
|
||||||
int i, rc;
|
int i, rc;
|
||||||
|
|
||||||
rc = ata_host_start(host);
|
rc = ata_host_start(host);
|
||||||
|
@ -2493,6 +2478,7 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < host->n_ports; i++) {
|
for (i = 0; i < host->n_ports; i++) {
|
||||||
struct ahci_port_priv *pp = host->ports[i]->private_data;
|
struct ahci_port_priv *pp = host->ports[i]->private_data;
|
||||||
|
int irq = ahci_irq_vector(hpriv, i);
|
||||||
|
|
||||||
/* Do not receive interrupts sent by dummy ports */
|
/* Do not receive interrupts sent by dummy ports */
|
||||||
if (!pp) {
|
if (!pp) {
|
||||||
|
@ -2500,14 +2486,14 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = devm_request_threaded_irq(host->dev, irq + i,
|
rc = devm_request_irq(host->dev, irq, ahci_multi_irqs_intr_hard,
|
||||||
ahci_multi_irqs_intr,
|
0, pp->irq_desc, host->ports[i]);
|
||||||
ahci_port_thread_fn, 0,
|
|
||||||
pp->irq_desc, host->ports[i]);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
ata_port_desc(host->ports[i], "irq %d", irq + i);
|
ata_port_desc(host->ports[i], "irq %d", irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ata_host_register(host, sht);
|
return ata_host_register(host, sht);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2528,8 +2514,8 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)
|
||||||
int irq = hpriv->irq;
|
int irq = hpriv->irq;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
|
if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI | AHCI_HFLAG_MULTI_MSIX))
|
||||||
rc = ahci_host_activate_multi_irqs(host, irq, sht);
|
rc = ahci_host_activate_multi_irqs(host, sht);
|
||||||
else if (hpriv->flags & AHCI_HFLAG_EDGE_IRQ)
|
else if (hpriv->flags & AHCI_HFLAG_EDGE_IRQ)
|
||||||
rc = ata_host_activate(host, irq, ahci_single_edge_irq_intr,
|
rc = ata_host_activate(host, irq, ahci_single_edge_irq_intr,
|
||||||
IRQF_SHARED, sht);
|
IRQF_SHARED, sht);
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
|
#include <linux/time.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/suspend.h>
|
#include <linux/suspend.h>
|
||||||
|
@ -3597,7 +3598,8 @@ int sata_link_resume(struct ata_link *link, const unsigned long *params,
|
||||||
* immediately after resuming. Delay 200ms before
|
* immediately after resuming. Delay 200ms before
|
||||||
* debouncing.
|
* debouncing.
|
||||||
*/
|
*/
|
||||||
ata_msleep(link->ap, 200);
|
if (!(link->flags & ATA_LFLAG_NO_DB_DELAY))
|
||||||
|
ata_msleep(link->ap, 200);
|
||||||
|
|
||||||
/* is SControl restored correctly? */
|
/* is SControl restored correctly? */
|
||||||
if ((rc = sata_scr_read(link, SCR_CONTROL, &scontrol)))
|
if ((rc = sata_scr_read(link, SCR_CONTROL, &scontrol)))
|
||||||
|
@ -6223,6 +6225,7 @@ int ata_host_activate(struct ata_host *host, int irq,
|
||||||
struct scsi_host_template *sht)
|
struct scsi_host_template *sht)
|
||||||
{
|
{
|
||||||
int i, rc;
|
int i, rc;
|
||||||
|
char *irq_desc;
|
||||||
|
|
||||||
rc = ata_host_start(host);
|
rc = ata_host_start(host);
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -6234,8 +6237,14 @@ int ata_host_activate(struct ata_host *host, int irq,
|
||||||
return ata_host_register(host, sht);
|
return ata_host_register(host, sht);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irq_desc = devm_kasprintf(host->dev, GFP_KERNEL, "%s[%s]",
|
||||||
|
dev_driver_string(host->dev),
|
||||||
|
dev_name(host->dev));
|
||||||
|
if (!irq_desc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
rc = devm_request_irq(host->dev, irq, irq_handler, irq_flags,
|
rc = devm_request_irq(host->dev, irq, irq_handler, irq_flags,
|
||||||
dev_name(host->dev), host);
|
irq_desc, host);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
@ -6697,7 +6706,12 @@ void ata_msleep(struct ata_port *ap, unsigned int msecs)
|
||||||
if (owns_eh)
|
if (owns_eh)
|
||||||
ata_eh_release(ap);
|
ata_eh_release(ap);
|
||||||
|
|
||||||
msleep(msecs);
|
if (msecs < 20) {
|
||||||
|
unsigned long usecs = msecs * USEC_PER_MSEC;
|
||||||
|
usleep_range(usecs, usecs + 50);
|
||||||
|
} else {
|
||||||
|
msleep(msecs);
|
||||||
|
}
|
||||||
|
|
||||||
if (owns_eh)
|
if (owns_eh)
|
||||||
ata_eh_acquire(ap);
|
ata_eh_acquire(ap);
|
||||||
|
|
|
@ -854,17 +854,14 @@ static struct of_device_id sata_rcar_match[] = {
|
||||||
.compatible = "renesas,sata-r8a7793",
|
.compatible = "renesas,sata-r8a7793",
|
||||||
.data = (void *)RCAR_GEN2_SATA
|
.data = (void *)RCAR_GEN2_SATA
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "renesas,sata-r8a7795",
|
||||||
|
.data = (void *)RCAR_GEN2_SATA
|
||||||
|
},
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, sata_rcar_match);
|
MODULE_DEVICE_TABLE(of, sata_rcar_match);
|
||||||
|
|
||||||
static const struct platform_device_id sata_rcar_id_table[] = {
|
|
||||||
{ "sata_rcar", RCAR_GEN1_SATA }, /* Deprecated by "sata-r8a7779" */
|
|
||||||
{ "sata-r8a7779", RCAR_GEN1_SATA },
|
|
||||||
{ },
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(platform, sata_rcar_id_table);
|
|
||||||
|
|
||||||
static int sata_rcar_probe(struct platform_device *pdev)
|
static int sata_rcar_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
const struct of_device_id *of_id;
|
const struct of_device_id *of_id;
|
||||||
|
@ -884,11 +881,10 @@ static int sata_rcar_probe(struct platform_device *pdev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
of_id = of_match_device(sata_rcar_match, &pdev->dev);
|
of_id = of_match_device(sata_rcar_match, &pdev->dev);
|
||||||
if (of_id)
|
if (!of_id)
|
||||||
priv->type = (enum sata_rcar_type)of_id->data;
|
return -ENODEV;
|
||||||
else
|
|
||||||
priv->type = platform_get_device_id(pdev)->driver_data;
|
|
||||||
|
|
||||||
|
priv->type = (enum sata_rcar_type)of_id->data;
|
||||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(priv->clk)) {
|
if (IS_ERR(priv->clk)) {
|
||||||
dev_err(&pdev->dev, "failed to get access to sata clock\n");
|
dev_err(&pdev->dev, "failed to get access to sata clock\n");
|
||||||
|
@ -1018,7 +1014,6 @@ static const struct dev_pm_ops sata_rcar_pm_ops = {
|
||||||
static struct platform_driver sata_rcar_driver = {
|
static struct platform_driver sata_rcar_driver = {
|
||||||
.probe = sata_rcar_probe,
|
.probe = sata_rcar_probe,
|
||||||
.remove = sata_rcar_remove,
|
.remove = sata_rcar_remove,
|
||||||
.id_table = sata_rcar_id_table,
|
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRV_NAME,
|
.name = DRV_NAME,
|
||||||
.of_match_table = sata_rcar_match,
|
.of_match_table = sata_rcar_match,
|
||||||
|
|
|
@ -1396,6 +1396,8 @@ static unsigned int pdc20621_dimm_init(struct ata_host *host)
|
||||||
addr = 0;
|
addr = 0;
|
||||||
length = size * 1024 * 1024;
|
length = size * 1024 * 1024;
|
||||||
buf = kzalloc(ECC_ERASE_BUF_SZ, GFP_KERNEL);
|
buf = kzalloc(ECC_ERASE_BUF_SZ, GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
return 1;
|
||||||
while (addr < length) {
|
while (addr < length) {
|
||||||
pdc20621_put_to_dimm(host, buf, addr,
|
pdc20621_put_to_dimm(host, buf, addr,
|
||||||
ECC_ERASE_BUF_SZ);
|
ECC_ERASE_BUF_SZ);
|
||||||
|
|
|
@ -205,6 +205,7 @@ enum {
|
||||||
ATA_LFLAG_NO_LPM = (1 << 8), /* disable LPM on this link */
|
ATA_LFLAG_NO_LPM = (1 << 8), /* disable LPM on this link */
|
||||||
ATA_LFLAG_RST_ONCE = (1 << 9), /* limit recovery to one reset */
|
ATA_LFLAG_RST_ONCE = (1 << 9), /* limit recovery to one reset */
|
||||||
ATA_LFLAG_CHANGED = (1 << 10), /* LPM state changed on this link */
|
ATA_LFLAG_CHANGED = (1 << 10), /* LPM state changed on this link */
|
||||||
|
ATA_LFLAG_NO_DB_DELAY = (1 << 11), /* no debounce delay on link resume */
|
||||||
|
|
||||||
/* struct ata_port flags */
|
/* struct ata_port flags */
|
||||||
ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */
|
ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */
|
||||||
|
|
Loading…
Reference in New Issue