diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-25 16:49:21 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-25 16:49:21 -0700 |
commit | 64e22b86854227b4d13a57fa02f26472bc426812 (patch) | |
tree | 2a23b94b3310592249c807c4235ff674b59d844a | |
parent | 6597ac8a514e2085cf19822a5783345c613312a5 (diff) | |
parent | 243918be6393f643e513a26e7882e6ae06ff7717 (diff) |
Merge branch 'for-4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata
Pull libata updates from Tejun Heo:
- a number of libata core changes to better support NCQ TRIM.
- ahci now supports MSI-X in single IRQ mode to support a new
controller which doesn't implement MSI or INTX.
- ahci now supports edge-triggered IRQ mode to support a new controller
which for some odd reason did edge-triggered IRQ.
- the usual controller support additions and changes.
* 'for-4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata: (27 commits)
libata: Do not blacklist Micron M500DC
ata: ahci_mvebu: add suspend/resume support
ahci, msix: Fix build error for !PCI_MSI
ahci: Add support for Cavium's ThunderX host controller
ahci: Add generic MSI-X support for single interrupts to SATA PCI driver
libata: finally use __initconst in ata_parse_force_one()
drivers: ata: add support for Ceva sata host controller
devicetree:bindings: add devicetree bindings for ceva ahci
ahci: added support for Freescale AHCI sata
ahci: Store irq number in struct ahci_host_priv
ahci: Move interrupt enablement code to a separate function
Doc: libata: Fix spelling typo found in libata.xml
ata:sata_nv - Change 1 to true for bool type variable.
ata: add Broadcom AHCI SATA3 driver for STB chips
Documentation: devicetree: add Broadcom SATA binding
libata: Fix regression when the NCQ Send and Receive log page is absent
ata: hpt366: fix constant cast warning
ata: ahci_xgene: potential NULL dereference in probe
ata: ahci_xgene: Add AHCI Support for 2nd HW version of APM X-Gene SoC AHCI SATA Host controller.
libahci: Add support to handle HOST_IRQ_STAT as edge trigger latch.
...
26 files changed, 988 insertions, 82 deletions
diff --git a/Documentation/ABI/testing/sysfs-ata b/Documentation/ABI/testing/sysfs-ata index 0a932155cbba..aa4296498859 100644 --- a/Documentation/ABI/testing/sysfs-ata +++ b/Documentation/ABI/testing/sysfs-ata @@ -90,6 +90,17 @@ gscr 130: SATA_PMP_GSCR_SII_GPIO Only valid if the device is a PM. +trim + + Shows the DSM TRIM mode currently used by the device. Valid + values are: + unsupported: Drive does not support DSM TRIM + unqueued: Drive supports unqueued DSM TRIM only + queued: Drive supports queued DSM TRIM + forced_unqueued: Drive's queued DSM support is known to be + buggy and only unqueued TRIM commands + are sent + spdn_cnt Number of time libata decided to lower the speed of link due to errors. diff --git a/Documentation/devicetree/bindings/ata/ahci-ceva.txt b/Documentation/devicetree/bindings/ata/ahci-ceva.txt new file mode 100644 index 000000000000..7ca8b976c13a --- /dev/null +++ b/Documentation/devicetree/bindings/ata/ahci-ceva.txt @@ -0,0 +1,20 @@ +Binding for CEVA AHCI SATA Controller + +Required properties: + - reg: Physical base address and size of the controller's register area. + - compatible: Compatibility string. Must be 'ceva,ahci-1v84'. + - clocks: Input clock specifier. Refer to common clock bindings. + - interrupts: Interrupt specifier. Refer to interrupt binding. + +Optional properties: + - ceva,broken-gen2: limit to gen1 speed instead of gen2. + +Examples: + ahci@fd0c0000 { + compatible = "ceva,ahci-1v84"; + reg = <0xfd0c0000 0x200>; + interrupt-parent = <&gic>; + interrupts = <0 133 4>; + clocks = <&clkc SATA_CLK_ID>; + ceva,broken-gen2; + }; diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt index c2340eeeb97f..a2321819e7f5 100644 --- a/Documentation/devicetree/bindings/ata/ahci-platform.txt +++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt @@ -16,6 +16,8 @@ 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> diff --git a/Documentation/devicetree/bindings/ata/brcm,sata-brcmstb.txt b/Documentation/devicetree/bindings/ata/brcm,sata-brcmstb.txt new file mode 100644 index 000000000000..20ac9bbfa1fd --- /dev/null +++ b/Documentation/devicetree/bindings/ata/brcm,sata-brcmstb.txt @@ -0,0 +1,34 @@ +* Broadcom SATA3 AHCI Controller for STB + +SATA nodes are defined to describe on-chip Serial ATA controllers. +Each SATA controller should have its own node. + +Required properties: +- compatible : compatible list, may contain "brcm,bcm7445-ahci" and/or + "brcm,sata3-ahci" +- reg : register mappings for AHCI and SATA_TOP_CTRL +- reg-names : "ahci" and "top-ctrl" +- interrupts : interrupt mapping for SATA IRQ + +Also see ahci-platform.txt. + +Example: + + sata@f045a000 { + compatible = "brcm,bcm7445-ahci", "brcm,sata3-ahci"; + reg = <0xf045a000 0xa9c>, <0xf0458040 0x24>; + reg-names = "ahci", "top-ctrl"; + interrupts = <0 30 0>; + #address-cells = <1>; + #size-cells = <0>; + + sata0: sata-port@0 { + reg = <0>; + phys = <&sata_phy 0>; + }; + + sata1: sata-port@1 { + reg = <1>; + phys = <&sata_phy 1>; + }; + }; diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index fd66d220c115..c84d078a6376 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1791,6 +1791,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. * [no]ncq: Turn on or off NCQ. + * [no]ncqtrim: Turn off queued DSM TRIM. + * nohrst, nosrst, norst: suppress hard, soft and both resets. diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 9dca4b995be0..b11470a7bd8f 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -98,6 +98,15 @@ config SATA_AHCI_PLATFORM If unsure, say N. +config AHCI_BRCMSTB + tristate "Broadcom STB AHCI SATA support" + depends on ARCH_BRCMSTB + help + This option enables support for the AHCI SATA3 controller found on + STB SoC's. + + If unsure, say N. + config AHCI_DA850 tristate "DaVinci DA850 AHCI SATA support" depends on ARCH_DAVINCI_DA850 @@ -124,6 +133,15 @@ config AHCI_IMX If unsure, say N. +config AHCI_CEVA + tristate "CEVA AHCI SATA support" + depends on OF + help + This option enables support for the CEVA AHCI SATA. + It can be found on the Xilinx Zynq UltraScale+ MPSoC. + + If unsure, say N. + config AHCI_MVEBU tristate "Marvell EBU AHCI SATA support" depends on ARCH_MVEBU diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 40f7865f20a1..af70919f7dde 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o obj-$(CONFIG_SATA_SIL24) += sata_sil24.o obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o +obj-$(CONFIG_AHCI_BRCMSTB) += ahci_brcmstb.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_CEVA) += ahci_ceva.o libahci.o libahci_platform.o obj-$(CONFIG_AHCI_DA850) += ahci_da850.o libahci.o libahci_platform.o obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o obj-$(CONFIG_AHCI_MVEBU) += ahci_mvebu.o libahci.o libahci_platform.o diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c index 12489ce863c4..ed6a30cd681a 100644 --- a/drivers/ata/acard-ahci.c +++ b/drivers/ata/acard-ahci.c @@ -433,6 +433,8 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; + + hpriv->irq = pdev->irq; hpriv->flags |= (unsigned long)pi.private_data; if (!(hpriv->flags & AHCI_HFLAG_NO_MSI)) @@ -498,7 +500,7 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id acard_ahci_pci_print_info(host); pci_set_master(pdev); - return ahci_host_activate(host, pdev->irq, &acard_ahci_sht); + return ahci_host_activate(host, &acard_ahci_sht); } module_pci_driver(acard_ahci_pci_driver); diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 65ee94454bbd..7e62751abfac 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -42,6 +42,7 @@ #include <linux/device.h> #include <linux/dmi.h> #include <linux/gfp.h> +#include <linux/msi.h> #include <scsi/scsi_host.h> #include <scsi/scsi_cmnd.h> #include <linux/libata.h> @@ -52,6 +53,7 @@ enum { AHCI_PCI_BAR_STA2X11 = 0, + AHCI_PCI_BAR_CAVIUM = 0, AHCI_PCI_BAR_ENMOTUS = 2, AHCI_PCI_BAR_STANDARD = 5, }; @@ -1288,17 +1290,60 @@ 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) +/* + * ahci_init_msix() only implements single MSI-X support, not multiple + * MSI-X per-port interrupts. This is needed for host controllers that only + * have MSI-X support implemented, but no MSI or intx. + */ +static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports, + struct ahci_host_priv *hpriv) { int rc, nvec; + struct msix_entry entry = {}; + /* Do not init MSI-X if MSI is disabled for the device */ if (hpriv->flags & AHCI_HFLAG_NO_MSI) - goto intx; + return -ENODEV; + + nvec = pci_msix_vec_count(pdev); + if (nvec < 0) + return nvec; + + if (!nvec) { + rc = -ENODEV; + goto fail; + } + + /* + * There can be more than one vector (e.g. for error detection or + * hdd hotplug). Only the first vector (entry.entry = 0) is used. + */ + rc = pci_enable_msix_exact(pdev, &entry, 1); + if (rc < 0) + goto fail; + + hpriv->irq = entry.vector; + + return 1; +fail: + dev_err(&pdev->dev, + "failed to enable MSI-X with error %d, # of vectors: %d\n", + rc, nvec); + + return rc; +} + +static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports, + struct ahci_host_priv *hpriv) +{ + int rc, nvec; + + if (hpriv->flags & AHCI_HFLAG_NO_MSI) + return -ENODEV; nvec = pci_msi_vec_count(pdev); if (nvec < 0) - goto intx; + return nvec; /* * If number of MSIs is less than number of ports then Sharing Last @@ -1311,8 +1356,8 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, rc = pci_enable_msi_exact(pdev, nvec); if (rc == -ENOSPC) goto single_msi; - else if (rc < 0) - goto intx; + if (rc < 0) + return rc; /* fallback to single MSI mode if the controller enforced MRSM mode */ if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) { @@ -1324,15 +1369,42 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, if (nvec > 1) hpriv->flags |= AHCI_HFLAG_MULTI_MSI; - return nvec; + goto out; single_msi: - if (pci_enable_msi(pdev)) - goto intx; - return 1; + nvec = 1; + + rc = pci_enable_msi(pdev); + if (rc < 0) + return rc; +out: + hpriv->irq = pdev->irq; + + return nvec; +} + +static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, + struct ahci_host_priv *hpriv) +{ + int nvec; + + nvec = ahci_init_msi(pdev, n_ports, hpriv); + if (nvec >= 0) + return nvec; + + /* + * Currently, MSI-X support only implements single IRQ mode and + * 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) + return nvec; -intx: + /* lagacy intx interrupts */ pci_intx(pdev, 1); + hpriv->irq = pdev->irq; + return 0; } @@ -1371,11 +1443,13 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev_info(&pdev->dev, "PDC42819 can only drive SATA devices with this driver\n"); - /* Both Connext and Enmotus devices use non-standard BARs */ + /* Some 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; + else if (pdev->vendor == 0x177d && pdev->device == 0xa01c) + ahci_pci_bar = AHCI_PCI_BAR_CAVIUM; /* * The JMicron chip 361/363 contains one SATA controller and one @@ -1497,13 +1571,13 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) */ 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; + ahci_init_interrupts(pdev, n_ports, hpriv); + if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) host->flags |= ATA_HOST_PARALLEL_SCAN; else @@ -1549,7 +1623,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_master(pdev); - return ahci_host_activate(host, pdev->irq, &ahci_sht); + return ahci_host_activate(host, &ahci_sht); } module_pci_driver(ahci_pci_driver); diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 71262e08648e..5b8e8a0fab48 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -238,6 +238,8 @@ enum { AHCI_HFLAG_MULTI_MSI = (1 << 16), /* multiple PCI MSIs */ AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */ AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */ + AHCI_HFLAG_EDGE_IRQ = (1 << 19), /* HOST_IRQ_STAT behaves as + Edge Triggered */ /* ap->flags bits */ @@ -341,6 +343,7 @@ struct ahci_host_priv { struct phy **phys; unsigned nports; /* Number of ports */ void *plat_data; /* Other platform data */ + unsigned int irq; /* interrupt line */ /* * Optional ahci_start_engine override, if not set this gets set to the * default ahci_start_engine during ahci_save_initial_config, this can @@ -393,8 +396,7 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv, struct ata_port_info *pi); int ahci_reset_em(struct ata_host *host); void ahci_print_info(struct ata_host *host, const char *scc_s); -int ahci_host_activate(struct ata_host *host, int irq, - struct scsi_host_template *sht); +int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht); void ahci_error_handler(struct ata_port *ap); static inline void __iomem *__ahci_port_base(struct ata_host *host, diff --git a/drivers/ata/ahci_brcmstb.c b/drivers/ata/ahci_brcmstb.c new file mode 100644 index 000000000000..ce1e3a885981 --- /dev/null +++ b/drivers/ata/ahci_brcmstb.c @@ -0,0 +1,322 @@ +/* + * Broadcom SATA3 AHCI Controller Driver + * + * Copyright © 2009-2015 Broadcom Corporation + * + * 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. + */ + +#include <linux/ahci_platform.h> +#include <linux/compiler.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/libata.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/string.h> + +#include "ahci.h" + +#define DRV_NAME "brcm-ahci" + +#define SATA_TOP_CTRL_VERSION 0x0 +#define SATA_TOP_CTRL_BUS_CTRL 0x4 + #define MMIO_ENDIAN_SHIFT 0 /* CPU->AHCI */ + #define DMADESC_ENDIAN_SHIFT 2 /* AHCI->DDR */ + #define DMADATA_ENDIAN_SHIFT 4 /* AHCI->DDR */ + #define PIODATA_ENDIAN_SHIFT 6 + #define ENDIAN_SWAP_NONE 0 + #define ENDIAN_SWAP_FULL 2 + #define OVERRIDE_HWINIT BIT(16) +#define SATA_TOP_CTRL_TP_CTRL 0x8 +#define SATA_TOP_CTRL_PHY_CTRL 0xc + #define SATA_TOP_CTRL_PHY_CTRL_1 0x0 + #define SATA_TOP_CTRL_1_PHY_DEFAULT_POWER_STATE BIT(14) + #define SATA_TOP_CTRL_PHY_CTRL_2 0x4 + #define SATA_TOP_CTRL_2_SW_RST_MDIOREG BIT(0) + #define SATA_TOP_CTRL_2_SW_RST_OOB BIT(1) + #define SATA_TOP_CTRL_2_SW_RST_RX BIT(2) + #define SATA_TOP_CTRL_2_SW_RST_TX BIT(3) + #define SATA_TOP_CTRL_2_PHY_GLOBAL_RESET BIT(14) + #define SATA_TOP_CTRL_PHY_OFFS 0x8 + #define SATA_TOP_MAX_PHYS 2 +#define SATA_TOP_CTRL_SATA_TP_OUT 0x1c +#define SATA_TOP_CTRL_CLIENT_INIT_CTRL 0x20 + +/* On big-endian MIPS, buses are reversed to big endian, so switch them back */ +#if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN) +#define DATA_ENDIAN 2 /* AHCI->DDR inbound accesses */ +#define MMIO_ENDIAN 2 /* CPU->AHCI outbound accesses */ +#else +#define DATA_ENDIAN 0 +#define MMIO_ENDIAN 0 +#endif + +#define BUS_CTRL_ENDIAN_CONF \ + ((DATA_ENDIAN << DMADATA_ENDIAN_SHIFT) | \ + (DATA_ENDIAN << DMADESC_ENDIAN_SHIFT) | \ + (MMIO_ENDIAN << MMIO_ENDIAN_SHIFT)) + +struct brcm_ahci_priv { + struct device *dev; + void __iomem *top_ctrl; + u32 port_mask; +}; + +static const struct ata_port_info ahci_brcm_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_platform_ops, +}; + +static inline u32 brcm_sata_readreg(void __iomem *addr) +{ + /* + * MIPS endianness is configured by boot strap, which also reverses all + * bus endianness (i.e., big-endian CPU + big endian bus ==> native + * endian I/O). + * + * Other architectures (e.g., ARM) either do not support big endian, or + * else leave I/O in little endian mode. + */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN)) + return __raw_readl(addr); + else + return readl_relaxed(addr); +} + +static inline void brcm_sata_writereg(u32 val, void __iomem *addr) +{ + /* See brcm_sata_readreg() comments */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN)) + __raw_writel(val, addr); + else + writel_relaxed(val, addr); +} + +static void brcm_sata_phy_enable(struct brcm_ahci_priv *priv, int port) +{ + void __iomem *phyctrl = priv->top_ctrl + SATA_TOP_CTRL_PHY_CTRL + + (port * SATA_TOP_CTRL_PHY_OFFS); + void __iomem *p; + u32 reg; + + /* clear PHY_DEFAULT_POWER_STATE */ + p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_1; + reg = brcm_sata_readreg(p); + reg &= ~SATA_TOP_CTRL_1_PHY_DEFAULT_POWER_STATE; + brcm_sata_writereg(reg, p); + + /* reset the PHY digital logic */ + p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_2; + reg = brcm_sata_readreg(p); + reg &= ~(SATA_TOP_CTRL_2_SW_RST_MDIOREG | SATA_TOP_CTRL_2_SW_RST_OOB | + SATA_TOP_CTRL_2_SW_RST_RX); + reg |= SATA_TOP_CTRL_2_SW_RST_TX; + brcm_sata_writereg(reg, p); + reg = brcm_sata_readreg(p); + reg |= SATA_TOP_CTRL_2_PHY_GLOBAL_RESET; + brcm_sata_writereg(reg, p); + reg = brcm_sata_readreg(p); + reg &= ~SATA_TOP_CTRL_2_PHY_GLOBAL_RESET; + brcm_sata_writereg(reg, p); + (void)brcm_sata_readreg(p); +} + +static void brcm_sata_phy_disable(struct brcm_ahci_priv *priv, int port) +{ + void __iomem *phyctrl = priv->top_ctrl + SATA_TOP_CTRL_PHY_CTRL + + (port * SATA_TOP_CTRL_PHY_OFFS); + void __iomem *p; + u32 reg; + + /* power-off the PHY digital logic */ + p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_2; + reg = brcm_sata_readreg(p); + reg |= (SATA_TOP_CTRL_2_SW_RST_MDIOREG | SATA_TOP_CTRL_2_SW_RST_OOB | + SATA_TOP_CTRL_2_SW_RST_RX | SATA_TOP_CTRL_2_SW_RST_TX | + SATA_TOP_CTRL_2_PHY_GLOBAL_RESET); + brcm_sata_writereg(reg, p); + + /* set PHY_DEFAULT_POWER_STATE */ + p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_1; + reg = brcm_sata_readreg(p); + reg |= SATA_TOP_CTRL_1_PHY_DEFAULT_POWER_STATE; + brcm_sata_writereg(reg, p); +} + +static void brcm_sata_phys_enable(struct brcm_ahci_priv *priv) +{ + int i; + + for (i = 0; i < SATA_TOP_MAX_PHYS; i++) + if (priv->port_mask & BIT(i)) + brcm_sata_phy_enable(priv, i); +} + +static void brcm_sata_phys_disable(struct brcm_ahci_priv *priv) +{ + int i; + + for (i = 0; i < SATA_TOP_MAX_PHYS; i++) + if (priv->port_mask & BIT(i)) + brcm_sata_phy_disable(priv, i); +} + +static u32 brcm_ahci_get_portmask(struct platform_device *pdev, + struct brcm_ahci_priv *priv) +{ + void __iomem *ahci; + struct resource *res; + u32 impl; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ahci"); + ahci = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ahci)) + return 0; + + impl = readl(ahci + HOST_PORTS_IMPL); + + if (fls(impl) > SATA_TOP_MAX_PHYS) + dev_warn(priv->dev, "warning: more ports than PHYs (%#x)\n", + impl); + else if (!impl) + dev_info(priv->dev, "no ports found\n"); + + devm_iounmap(&pdev->dev, ahci); + devm_release_mem_region(&pdev->dev, res->start, resource_size(res)); + + return impl; +} + +static void brcm_sata_init(struct brcm_ahci_priv *priv) +{ + /* Configure endianness */ + brcm_sata_writereg(BUS_CTRL_ENDIAN_CONF, + priv->top_ctrl + SATA_TOP_CTRL_BUS_CTRL); +} + +static int brcm_ahci_suspend(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + struct brcm_ahci_priv *priv = hpriv->plat_data; + int ret; + + ret = ahci_platform_suspend(dev); + brcm_sata_phys_disable(priv); + return ret; +} + +static int brcm_ahci_resume(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + struct brcm_ahci_priv *priv = hpriv->plat_data; + + brcm_sata_init(priv); + brcm_sata_phys_enable(priv); + return ahci_platform_resume(dev); +} + +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + +static int brcm_ahci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct brcm_ahci_priv *priv; + struct ahci_host_priv *hpriv; + struct resource *res; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "top-ctrl"); + priv->top_ctrl = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->top_ctrl)) + return PTR_ERR(priv->top_ctrl); + + brcm_sata_init(priv); + + priv->port_mask = brcm_ahci_get_portmask(pdev, priv); + if (!priv->port_mask) + return -ENODEV; + + brcm_sata_phys_enable(priv); + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + hpriv->plat_data = priv; + + ret = ahci_platform_enable_resources(hpriv); + if (ret) + return ret; + + ret = ahci_platform_init_host(pdev, hpriv, &ahci_brcm_port_info, + &ahci_platform_sht); + if (ret) + return ret; + + dev_info(dev, "Broadcom AHCI SATA3 registered\n"); + + return 0; +} + +static int brcm_ahci_remove(struct platform_device *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ahci_host_priv *hpriv = host->private_data; + struct brcm_ahci_priv *priv = hpriv->plat_data; + int ret; + + ret = ata_platform_remove_one(pdev); + if (ret) + return ret; + + brcm_sata_phys_disable(priv); + + return 0; +} + +static const struct of_device_id ahci_of_match[] = { + {.compatible = "brcm,bcm7445-ahci"}, + {}, +}; +MODULE_DEVICE_TABLE(of, ahci_of_match); + +static SIMPLE_DEV_PM_OPS(ahci_brcm_pm_ops, brcm_ahci_suspend, brcm_ahci_resume); + +static struct platform_driver brcm_ahci_driver = { + .probe = brcm_ahci_probe, + .remove = brcm_ahci_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = ahci_of_match, + .pm = &ahci_brcm_pm_ops, + }, +}; +module_platform_driver(brcm_ahci_driver); + +MODULE_DESCRIPTION("Broadcom SATA3 AHCI Controller Driver"); +MODULE_AUTHOR("Brian Norris"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sata-brcmstb"); diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c new file mode 100644 index 000000000000..207649d323c5 --- /dev/null +++ b/drivers/ata/ahci_ceva.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2015 Xilinx, Inc. + * CEVA AHCI SATA platform driver + * + * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/ahci_platform.h> +#include <linux/kernel.h> +#include <linux/libata.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include "ahci.h" + +/* Vendor Specific Register Offsets */ +#define AHCI_VEND_PCFG 0xA4 +#define AHCI_VEND_PPCFG 0xA8 +#define AHCI_VEND_PP2C 0xAC +#define AHCI_VEND_PP3C 0xB0 +#define AHCI_VEND_PP4C 0xB4 +#define AHCI_VEND_PP5C 0xB8 +#define AHCI_VEND_PAXIC 0xC0 +#define AHCI_VEND_PTC 0xC8 + +/* Vendor Specific Register bit definitions */ +#define PAXIC_ADBW_BW64 0x1 +#define PAXIC_MAWIDD (1 << 8) +#define PAXIC_MARIDD (1 << 16) +#define PAXIC_OTL (0x4 << 20) + +#define PCFG_TPSS_VAL (0x32 << 16) +#define PCFG_TPRS_VAL (0x2 << 12) +#define PCFG_PAD_VAL 0x2 + +#define PPCFG_TTA 0x1FFFE +#define PPCFG_PSSO_EN (1 << 28) +#define PPCFG_PSS_EN (1 << 29) +#define PPCFG_ESDF_EN (1 << 31) + +#define PP2C_CIBGMN 0x0F +#define PP2C_CIBGMX (0x25 << 8) +#define PP2C_CIBGN (0x18 << 16) +#define PP2C_CINMP (0x29 << 24) + +#define PP3C_CWBGMN 0x04 +#define PP3C_CWBGMX (0x0B << 8) +#define PP3C_CWBGN (0x08 << 16) +#define PP3C_CWNMP (0x0F << 24) + +#define PP4C_BMX 0x0a +#define PP4C_BNM (0x08 << 8) +#define PP4C_SFD (0x4a << 16) +#define PP4C_PTST (0x06 << 24) + +#define PP5C_RIT 0x60216 +#define PP5C_RCT (0x7f0 << 20) + +#define PTC_RX_WM_VAL 0x40 +#define PTC_RSVD (1 << 27) + +#define PORT0_BASE 0x100 +#define PORT1_BASE 0x180 + +/* Port Control Register Bit Definitions */ +#define PORT_SCTL_SPD_GEN2 (0x2 << 4) +#define PORT_SCTL_SPD_GEN1 (0x1 << 4) +#define PORT_SCTL_IPM (0x3 << 8) + +#define PORT_BASE 0x100 +#define PORT_OFFSET 0x80 +#define NR_PORTS 2 +#define DRV_NAME "ahci-ceva" +#define CEVA_FLAG_BROKEN_GEN2 1 + +struct ceva_ahci_priv { + struct platform_device *ahci_pdev; + int flags; +}; + +static struct ata_port_operations ahci_ceva_ops = { + .inherits = &ahci_platform_ops, +}; + +static const struct ata_port_info ahci_ceva_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ceva_ops, +}; + +static void ahci_ceva_setup(struct ahci_host_priv *hpriv) +{ + void __iomem *mmio = hpriv->mmio; + struct ceva_ahci_priv *cevapriv = hpriv->plat_data; + u32 tmp; + int i; + + /* + * AXI Data bus width to 64 + * Set Mem Addr Read, Write ID for data transfers + * Transfer limit to 72 DWord + */ + tmp = PAXIC_ADBW_BW64 | PAXIC_MAWIDD | PAXIC_MARIDD | PAXIC_OTL; + writel(tmp, mmio + AHCI_VEND_PAXIC); + + /* Set AHCI Enable */ + tmp = readl(mmio + HOST_CTL); + tmp |= HOST_AHCI_EN; + writel(tmp, mmio + HOST_CTL); + + for (i = 0; i < NR_PORTS; i++) { + /* TPSS TPRS scalars, CISE and Port Addr */ + tmp = PCFG_TPSS_VAL | PCFG_TPRS_VAL | (PCFG_PAD_VAL + i); + writel(tmp, mmio + AHCI_VEND_PCFG); + + /* Port Phy Cfg register enables */ + tmp = PPCFG_TTA | PPCFG_PSS_EN | PPCFG_ESDF_EN; + writel(tmp, mmio + AHCI_VEND_PPCFG); + + /* Phy Control OOB timing parameters COMINIT */ + tmp = PP2C_CIBGMN | PP2C_CIBGMX | PP2C_CIBGN | PP2C_CINMP; + writel(tmp, mmio + AHCI_VEND_PP2C); + + /* Phy Control OOB timing parameters COMWAKE */ + tmp = PP3C_CWBGMN | PP3C_CWBGMX | PP3C_CWBGN | PP3C_CWNMP; + writel(tmp, mmio + AHCI_VEND_PP3C); + + /* Phy Control Burst timing setting */ + tmp = PP4C_BMX | PP4C_BNM | PP4C_SFD | PP4C_PTST; + writel(tmp, mmio + AHCI_VEND_PP4C); + + /* Rate Change Timer and Retry Interval Timer setting */ + tmp = PP5C_RIT | PP5C_RCT; + writel(tmp, mmio + AHCI_VEND_PP5C); + + /* Rx Watermark setting */ + tmp = PTC_RX_WM_VAL | PTC_RSVD; + writel(tmp, mmio + AHCI_VEND_PTC); + + /* Default to Gen 2 Speed and Gen 1 if Gen2 is broken */ + tmp = PORT_SCTL_SPD_GEN2 | PORT_SCTL_IPM; + if (cevapriv->flags & CEVA_FLAG_BROKEN_GEN2) + tmp = PORT_SCTL_SPD_GEN1 | PORT_SCTL_IPM; + writel(tmp, mmio + PORT_SCR_CTL + PORT_BASE + PORT_OFFSET * i); + } +} + +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + +static int ceva_ahci_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; + struct ceva_ahci_priv *cevapriv; + int rc; + + cevapriv = devm_kzalloc(dev, sizeof(*cevapriv), GFP_KERNEL); + if (!cevapriv) + return -ENOMEM; + + cevapriv->ahci_pdev = pdev; + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + if (of_property_read_bool(np, "ceva,broken-gen2")) + cevapriv->flags = CEVA_FLAG_BROKEN_GEN2; + + hpriv->plat_data = cevapriv; + + /* CEVA specific initialization */ + ahci_ceva_setup(hpriv); + + rc = ahci_platform_init_host(pdev, hpriv, &ahci_ceva_port_info, + &ahci_platform_sht); + if (rc) + goto disable_resources; + + return 0; + +disable_resources: + ahci_platform_disable_resources(hpriv); + return rc; +} + +static int __maybe_unused ceva_ahci_suspend(struct device *dev) +{ + return ahci_platform_suspend_host(dev); +} + +static int __maybe_unused ceva_ahci_resume(struct device *dev) +{ + return ahci_platform_resume_host(dev); +} + +static SIMPLE_DEV_PM_OPS(ahci_ceva_pm_ops, ceva_ahci_suspend, ceva_ahci_resume); + +static const struct of_device_id ceva_ahci_of_match[] = { + { .compatible = "ceva,ahci-1v84" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ceva_ahci_of_match); + +static struct platform_driver ceva_ahci_driver = { + .probe = ceva_ahci_probe, + .remove = ata_platform_remove_one, + .driver = { + .name = DRV_NAME, + .of_match_table = ceva_ahci_of_match, + .pm = &ahci_ceva_pm_ops, + }, +}; +module_platform_driver(ceva_ahci_driver); + +MODULE_DESCRIPTION("CEVA AHCI SATA platform driver"); +MODULE_AUTHOR("Xilinx Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c index 5928d0746a27..8490d37aee2a 100644 --- a/drivers/ata/ahci_mvebu.c +++ b/drivers/ata/ahci_mvebu.c @@ -62,6 +62,26 @@ static void ahci_mvebu_regret_option(struct ahci_host_priv *hpriv) writel(0x80, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_DATA); } +static int ahci_mvebu_suspend(struct platform_device *pdev, pm_message_t state) +{ + return ahci_platform_suspend_host(&pdev->dev); +} + +static int ahci_mvebu_resume(struct platform_device *pdev) +{ + struct ata_host *host = platform_get_drvdata(pdev); + struct ahci_host_priv *hpriv = host->private_data; + const struct mbus_dram_target_info *dram; + + dram = mv_mbus_dram_info(); + if (dram) + ahci_mvebu_mbus_config(hpriv, dram); + + ahci_mvebu_regret_option(hpriv); + + return ahci_platform_resume_host(&pdev->dev); +} + static const struct ata_port_info ahci_mvebu_port_info = { .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, @@ -120,6 +140,8 @@ MODULE_DEVICE_TABLE(of, ahci_mvebu_of_match); static struct platform_driver ahci_mvebu_driver = { .probe = ahci_mvebu_probe, .remove = ata_platform_remove_one, + .suspend = ahci_mvebu_suspend, + .resume = ahci_mvebu_resume, .driver = { .name = DRV_NAME, .of_match_table = ahci_mvebu_of_match, diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 78d6ae0b90c4..614c78f510f0 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -74,6 +74,7 @@ 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); diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index 2b78510d94dd..e2c6d9e0c5ac 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -27,6 +27,7 @@ #include <linux/platform_device.h> #include <linux/ahci_platform.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/phy/phy.h> #include "ahci.h" @@ -84,6 +85,11 @@ /* Max retry for link down */ #define MAX_LINK_DOWN_RETRY 3 +enum xgene_ahci_version { + XGENE_AHCI_V1 = 1, + XGENE_AHCI_V2, +}; + struct xgene_ahci_context { struct ahci_host_priv *hpriv; struct device *dev; @@ -542,7 +548,7 @@ softreset_retry: return rc; } -static struct ata_port_operations xgene_ahci_ops = { +static struct ata_port_operations xgene_ahci_v1_ops = { .inherits = &ahci_ops, .host_stop = xgene_ahci_host_stop, .hardreset = xgene_ahci_hardreset, @@ -552,11 +558,25 @@ static struct ata_port_operations xgene_ahci_ops = { .pmp_softreset = xgene_ahci_pmp_softreset }; -static const struct ata_port_info xgene_ahci_port_info = { +static const struct ata_port_info xgene_ahci_v1_port_info = { + .flags = AHCI_FLAG_COMMON | ATA_FLAG_PMP, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &xgene_ahci_v1_ops, +}; + +static struct ata_port_operations xgene_ahci_v2_ops = { + .inherits = &ahci_ops, + .host_stop = xgene_ahci_host_stop, + .hardreset = xgene_ahci_hardreset, + .read_id = xgene_ahci_read_id, +}; + +static const struct ata_port_info xgene_ahci_v2_port_info = { .flags = AHCI_FLAG_COMMON | ATA_FLAG_PMP, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, - .port_ops = &xgene_ahci_ops, + .port_ops = &xgene_ahci_v2_ops, }; static int xgene_ahci_hw_init(struct ahci_host_priv *hpriv) @@ -629,12 +649,32 @@ static struct scsi_host_template ahci_platform_sht = { AHCI_SHT(DRV_NAME), }; +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_ahci_acpi_match[] = { + { "APMC0D0D", XGENE_AHCI_V1}, + { "APMC0D32", XGENE_AHCI_V2}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, xgene_ahci_acpi_match); +#endif + +static const struct of_device_id xgene_ahci_of_match[] = { + {.compatible = "apm,xgene-ahci", .data = (void *) XGENE_AHCI_V1}, + {.compatible = "apm,xgene-ahci-v2", .data = (void *) XGENE_AHCI_V2}, + {}, +}; +MODULE_DEVICE_TABLE(of, xgene_ahci_of_match); + static int xgene_ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; struct xgene_ahci_context *ctx; struct resource *res; + const struct of_device_id *of_devid; + enum xgene_ahci_version version = XGENE_AHCI_V1; + const struct ata_port_info *ppi[] = { &xgene_ahci_v1_port_info, + &xgene_ahci_v2_port_info }; int rc; hpriv = ahci_platform_get_resources(pdev); @@ -677,6 +717,35 @@ static int xgene_ahci_probe(struct platform_device *pdev) ctx->csr_mux = csr; } + of_devid = of_match_device(xgene_ahci_of_match, dev); + if (of_devid) { + if (of_devid->data) + version = (enum xgene_ahci_version) of_devid->data; + } +#ifdef CONFIG_ACPI + else { + const struct acpi_device_id *acpi_id; + struct acpi_device_info *info; + acpi_status status; + + acpi_id = acpi_match_device(xgene_ahci_acpi_match, &pdev->dev); + if (!acpi_id) { + dev_warn(&pdev->dev, "No node entry in ACPI table. Assume version1\n"); + version = XGENE_AHCI_V1; + } else if (acpi_id->driver_data) { + version = (enum xgene_ahci_version) acpi_id->driver_data; + status = acpi_get_object_info(ACPI_HANDLE(&pdev->dev), &info); + if (ACPI_FAILURE(status)) { + dev_warn(&pdev->dev, "%s: Error reading device info. Assume version1\n", + __func__); + version = XGENE_AHCI_V1; + } + if (info->valid & ACPI_VALID_CID) + version = XGENE_AHCI_V2; + } + } +#endif + dev_dbg(dev, "VAddr 0x%p Mmio VAddr 0x%p\n", ctx->csr_core, hpriv->mmio); @@ -704,9 +773,19 @@ static int xgene_ahci_probe(struct platform_device *pdev) /* Configure the host controller */ xgene_ahci_hw_init(hpriv); skip_clk_phy: - hpriv->flags = AHCI_HFLAG_NO_PMP | AHCI_HFLAG_NO_NCQ; - rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info, + switch (version) { + case XGENE_AHCI_V1: + hpriv->flags = AHCI_HFLAG_NO_NCQ; + break; + case XGENE_AHCI_V2: + hpriv->flags |= AHCI_HFLAG_YES_FBS | AHCI_HFLAG_EDGE_IRQ; + break; + default: + break; + } + + rc = ahci_platform_init_host(pdev, hpriv, ppi[version - 1], &ahci_platform_sht); if (rc) goto disable_resources; @@ -719,20 +798,6 @@ disable_resources: return rc; } -#ifdef CONFIG_ACPI -static const struct acpi_device_id xgene_ahci_acpi_match[] = { - { "APMC0D0D", }, - { } -}; -MODULE_DEVICE_TABLE(acpi, xgene_ahci_acpi_match); -#endif - -static const struct of_device_id xgene_ahci_of_match[] = { - {.compatible = "apm,xgene-ahci"}, - {}, -}; -MODULE_DEVICE_TABLE(of, xgene_ahci_of_match); - static struct platform_driver xgene_ahci_driver = { .probe = xgene_ahci_probe, .remove = ata_platform_remove_one, diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 287c4ba0219f..d256a66158be 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1825,11 +1825,38 @@ static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance) return IRQ_WAKE_THREAD; } -static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance) +static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked) +{ + unsigned int i, handled = 0; + + 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; + } + + return handled; +} + +static irqreturn_t ahci_single_edge_irq_intr(int irq, void *dev_instance) { struct ata_host *host = dev_instance; struct ahci_host_priv *hpriv; - unsigned int i, handled = 0; + unsigned int rc = 0; void __iomem *mmio; u32 irq_stat, irq_masked; @@ -1847,25 +1874,44 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance) spin_lock(&host->lock); - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap; + /* + * HOST_IRQ_STAT behaves as edge triggered latch meaning that + * it should be cleared before all the port events are cleared. + */ + writel(irq_stat, mmio + HOST_IRQ_STAT); - if (!(irq_masked & (1 << i))) - continue; + rc = ahci_handle_port_intr(host, irq_masked); - 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); - } + spin_unlock(&host->lock); - handled = 1; - } + VPRINTK("EXIT\n"); + + return IRQ_RETVAL(rc); +} + +static irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance) +{ + struct ata_host *host = dev_instance; + struct ahci_host_priv *hpriv; + unsigned int rc = 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); + + rc = ahci_handle_port_intr(host, irq_masked); /* HOST_IRQ_STAT behaves as level triggered latch meaning that * it should be cleared after all the port events are cleared; @@ -1882,7 +1928,7 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance) VPRINTK("EXIT\n"); - return IRQ_RETVAL(handled); + return IRQ_RETVAL(rc); } unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) @@ -2297,7 +2343,7 @@ static int ahci_port_start(struct ata_port *ap) /* * Switch to per-port locking in case each port has its own MSI vector. */ - if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) { + if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) { spin_lock_init(&pp->lock); ap->lock = &pp->lock; } @@ -2425,7 +2471,10 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq, rc = ata_host_start(host); if (rc) return rc; - + /* + * Requests IRQs according to AHCI-1.1 when multiple MSIs were + * allocated. That is one MSI per port, starting from @irq. + */ for (i = 0; i < host->n_ports; i++) { struct ahci_port_priv *pp = host->ports[i]->private_data; @@ -2464,29 +2513,27 @@ out_free_irqs: /** * 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) +int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht) { struct ahci_host_priv *hpriv = host->private_data; + int irq = hpriv->irq; int rc; if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) rc = ahci_host_activate_multi_irqs(host, irq, sht); + else if (hpriv->flags & AHCI_HFLAG_EDGE_IRQ) + rc = ata_host_activate(host, irq, ahci_single_edge_irq_intr, + IRQF_SHARED, sht); else - rc = ata_host_activate(host, irq, ahci_single_irq_intr, + rc = ata_host_activate(host, irq, ahci_single_level_irq_intr, IRQF_SHARED, sht); return rc; } diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index d89305d289f6..aaa761b9081c 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -518,6 +518,8 @@ int ahci_platform_init_host(struct platform_device *pdev, return -EINVAL; } + hpriv->irq = irq; + /* prepare host */ pi.private_data = (void *)(unsigned long)hpriv->flags; @@ -588,7 +590,7 @@ int ahci_platform_init_host(struct platform_device *pdev, ahci_init_controller(host); ahci_print_info(host, "platform"); - return ahci_host_activate(host, irq, sht); + return ahci_host_activate(host, sht); } EXPORT_SYMBOL_GPL(ahci_platform_init_host); diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 577849c6611a..e83fc3d0da9c 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3654,7 +3654,7 @@ int sata_link_resume(struct ata_link *link, const unsigned long *params, * EH context. * * RETURNS: - * 0 on succes, -errno otherwise. + * 0 on success, -errno otherwise. */ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, bool spm_wakeup) @@ -4225,7 +4225,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER }, /* devices that don't properly handle queued TRIM commands */ - { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | + { "Micron_M500_*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | ATA_HORKAGE_ZERO_AFTER_TRIM, }, { "Crucial_CT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | ATA_HORKAGE_ZERO_AFTER_TRIM, }, @@ -6456,12 +6456,7 @@ static int __init ata_parse_force_one(char **cur, struct ata_force_ent *force_ent, const char **reason) { - /* FIXME: Currently, there's no way to tag init const data and - * using __initdata causes build failure on some versions of - * gcc. Once __initdataconst is implemented, add const to the - * following structure. - */ - static struct ata_force_param force_tbl[] __initdata = { + static const struct ata_force_param force_tbl[] __initconst = { { "40c", .cbl = ATA_CBL_PATA40 }, { "80c", .cbl = ATA_CBL_PATA80 }, { "short40c", .cbl = ATA_CBL_PATA40_SHORT }, @@ -6472,6 +6467,8 @@ static int __init ata_parse_force_one(char **cur, { "3.0Gbps", .spd_limit = 2 }, { "noncq", .horkage_on = ATA_HORKAGE_NONCQ }, { "ncq", .horkage_off = ATA_HORKAGE_NONCQ }, + { "noncqtrim", .horkage_on = ATA_HORKAGE_NO_NCQ_TRIM }, + { "ncqtrim", .horkage_off = ATA_HORKAGE_NO_NCQ_TRIM }, { "dump_id", .horkage_on = ATA_HORKAGE_DUMP_ID }, { "pio0", .xfer_mask = 1 << (ATA_SHIFT_PIO + 0) }, { "pio1", .xfer_mask = 1 << (ATA_SHIFT_PIO + 1) }, diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index cf0022ec07f2..7465031a893c 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1507,16 +1507,21 @@ unsigned int ata_read_log_page(struct ata_device *dev, u8 log, { struct ata_taskfile tf; unsigned int err_mask; + bool dma = false; DPRINTK("read log page - log 0x%x, page 0x%x\n", log, page); +retry: ata_tf_init(dev, &tf); - if (dev->dma_mode && ata_id_has_read_log_dma_ext(dev->id)) { + if (dev->dma_mode && ata_id_has_read_log_dma_ext(dev->id) && + !(dev->horkage & ATA_HORKAGE_NO_NCQ_LOG)) { tf.command = ATA_CMD_READ_LOG_DMA_EXT; tf.protocol = ATA_PROT_DMA; + dma = true; } else { tf.command = ATA_CMD_READ_LOG_EXT; tf.protocol = ATA_PROT_PIO; + dma = false; } tf.lbal = log; tf.lbam = page; @@ -1527,6 +1532,12 @@ unsigned int ata_read_log_page(struct ata_device *dev, u8 log, err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE, buf, sectors * ATA_SECT_SIZE, 0); + if (err_mask && dma) { + dev->horkage |= ATA_HORKAGE_NO_NCQ_LOG; + ata_dev_warn(dev, "READ LOG DMA EXT failed, trying unqueued\n"); + goto retry; + } + DPRINTK("EXIT, err_mask=%x\n", err_mask); return err_mask; } diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 3227b7c8a05f..d6c37bcd416d 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -560,6 +560,27 @@ show_ata_dev_gscr(struct device *dev, static DEVICE_ATTR(gscr, S_IRUGO, show_ata_dev_gscr, NULL); +static ssize_t +show_ata_dev_trim(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ata_device *ata_dev = transport_class_to_dev(dev); + unsigned char *mode; + + if (!ata_id_has_trim(ata_dev->id)) + mode = "unsupported"; + else if (ata_dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM) + mode = "forced_unqueued"; + else if (ata_fpdma_dsm_supported(ata_dev)) + mode = "queued"; + else + mode = "unqueued"; + + return snprintf(buf, 20, "%s\n", mode); +} + +static DEVICE_ATTR(trim, S_IRUGO, show_ata_dev_trim, NULL); + static DECLARE_TRANSPORT_CLASS(ata_dev_class, "ata_device", NULL, NULL, NULL); @@ -733,6 +754,7 @@ struct scsi_transport_template *ata_attach_transport(void) SETUP_DEV_ATTRIBUTE(ering); SETUP_DEV_ATTRIBUTE(id); SETUP_DEV_ATTRIBUTE(gscr); + SETUP_DEV_ATTRIBUTE(trim); BUG_ON(count > ATA_DEV_ATTRS); i->dev_attrs[count] = NULL; diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c index cbc3de793d1d..0038dc4c06c7 100644 --- a/drivers/ata/pata_hpt366.c +++ b/drivers/ata/pata_hpt366.c @@ -352,7 +352,7 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id) }; const struct ata_port_info *ppi[] = { &info_hpt366, NULL }; - void *hpriv = NULL; + const void *hpriv = NULL; u32 reg1; int rc; @@ -383,7 +383,7 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id) break; } /* Now kick off ATA set up */ - return ata_pci_bmdma_init_one(dev, ppi, &hpt36x_sht, hpriv, 0); + return ata_pci_bmdma_init_one(dev, ppi, &hpt36x_sht, (void *)hpriv, 0); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c index fa44eb2872db..cbb5a471eb9d 100644 --- a/drivers/ata/pata_samsung_cf.c +++ b/drivers/ata/pata_samsung_cf.c @@ -638,7 +638,7 @@ static const struct dev_pm_ops pata_s3c_pm_ops = { #endif /* driver device registration */ -static struct platform_device_id pata_s3c_driver_ids[] = { +static const struct platform_device_id pata_s3c_driver_ids[] = { { .name = "s3c64xx-pata", .driver_data = TYPE_S3C64XX, diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c index 24e311fe2c1c..8638d575b2b9 100644 --- a/drivers/ata/sata_highbank.c +++ b/drivers/ata/sata_highbank.c @@ -499,6 +499,7 @@ static int ahci_highbank_probe(struct platform_device *pdev) return -ENOMEM; } + hpriv->irq = irq; hpriv->flags |= (unsigned long)pi.private_data; hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); @@ -568,7 +569,7 @@ static int ahci_highbank_probe(struct platform_device *pdev) ahci_init_controller(host); ahci_print_info(host, "platform"); - rc = ahci_host_activate(host, irq, &ahci_highbank_platform_sht); + rc = ahci_host_activate(host, &ahci_highbank_platform_sht); if (rc) goto err0; diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 7ece85f43020..734f563b8d37 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -599,7 +599,7 @@ MODULE_DEVICE_TABLE(pci, nv_pci_tbl); MODULE_VERSION(DRV_VERSION); static bool adma_enabled; -static bool swncq_enabled = 1; +static bool swncq_enabled = true; static bool msi_enabled; static void nv_adma_register_mode(struct ata_port *ap) diff --git a/include/linux/ata.h b/include/linux/ata.h index b666b773e111..fed36418dd1c 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -704,9 +704,19 @@ static inline bool ata_id_wcache_enabled(const u16 *id) static inline bool ata_id_has_read_log_dma_ext(const u16 *id) { + /* Word 86 must have bit 15 set */ if (!(id[ATA_ID_CFS_ENABLE_2] & (1 << 15))) return false; - return id[ATA_ID_COMMAND_SET_3] & (1 << 3); + + /* READ LOG DMA EXT support can be signaled either from word 119 + * or from word 120. The format is the same for both words: Bit + * 15 must be cleared, bit 14 set and bit 3 set. + */ + if ((id[ATA_ID_COMMAND_SET_3] & 0xC008) == 0x4008 || + (id[ATA_ID_COMMAND_SET_4] & 0xC008) == 0x4008) + return true; + + return false; } static inline bool ata_id_has_sense_reporting(const u16 *id) diff --git a/include/linux/libata.h b/include/linux/libata.h index 51cb312d9bb9..36ce37bcc963 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -430,6 +430,7 @@ enum { ATA_HORKAGE_NOLPM = (1 << 20), /* don't use LPM */ ATA_HORKAGE_WD_BROKEN_LPM = (1 << 21), /* some WDs have broken LPM */ ATA_HORKAGE_ZERO_AFTER_TRIM = (1 << 22),/* guarantees zero after trim */ + ATA_HORKAGE_NO_NCQ_LOG = (1 << 23), /* don't use NCQ for log read */ /* DMA mask for user DMA control: User visible values; DO NOT renumber */ |