diff options
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/ahci.c | 46 | ||||
-rw-r--r-- | drivers/ata/ahci_qoriq.c | 16 | ||||
-rw-r--r-- | drivers/ata/libahci.c | 1 | ||||
-rw-r--r-- | drivers/ata/libata-core.c | 44 | ||||
-rw-r--r-- | drivers/ata/libata-scsi.c | 87 | ||||
-rw-r--r-- | drivers/ata/libata.h | 2 | ||||
-rw-r--r-- | drivers/ata/pata_imx.c | 82 | ||||
-rw-r--r-- | drivers/ata/sata_mv.c | 15 |
8 files changed, 252 insertions, 41 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 9669fc7c19df..2fc52407306c 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -46,6 +46,8 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_cmnd.h> #include <linux/libata.h> +#include <linux/ahci-remap.h> +#include <linux/io-64-nonatomic-lo-hi.h> #include "ahci.h" #define DRV_NAME "ahci" @@ -1400,6 +1402,40 @@ static irqreturn_t ahci_thunderx_irq_handler(int irq, void *dev_instance) } #endif +static void ahci_remap_check(struct pci_dev *pdev, int bar, + struct ahci_host_priv *hpriv) +{ + int i, count = 0; + u32 cap; + + /* + * Check if this device might have remapped nvme devices. + */ + if (pdev->vendor != PCI_VENDOR_ID_INTEL || + pci_resource_len(pdev, bar) < SZ_512K || + bar != AHCI_PCI_BAR_STANDARD || + !(readl(hpriv->mmio + AHCI_VSCAP) & 1)) + return; + + cap = readq(hpriv->mmio + AHCI_REMAP_CAP); + for (i = 0; i < AHCI_MAX_REMAP; i++) { + if ((cap & (1 << i)) == 0) + continue; + if (readl(hpriv->mmio + ahci_remap_dcc(i)) + != PCI_CLASS_STORAGE_EXPRESS) + continue; + + /* We've found a remapped device */ + count++; + } + + if (!count) + return; + + dev_warn(&pdev->dev, "Found %d remapped NVMe devices.\n", count); + dev_warn(&pdev->dev, "Switch your BIOS from RAID to AHCI mode to use them.\n"); +} + static int ahci_get_irq_vector(struct ata_host *host, int port) { return pci_irq_vector(to_pci_dev(host->dev), port); @@ -1436,13 +1472,6 @@ static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports, "ahci: MRSM is on, fallback to single MSI\n"); pci_free_irq_vectors(pdev); } - - /* - * -ENOSPC indicated we don't have enough vectors. Don't bother - * trying a single vectors for any other error: - */ - if (nvec < 0 && nvec != -ENOSPC) - return nvec; } /* @@ -1548,6 +1577,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; + /* detect remapped nvme devices */ + ahci_remap_check(pdev, ahci_pci_bar, hpriv); + /* must set flag prior to save config in order to take effect */ if (ahci_broken_devslp(pdev)) hpriv->flags |= AHCI_HFLAG_NO_DEVSLP; diff --git a/drivers/ata/ahci_qoriq.c b/drivers/ata/ahci_qoriq.c index 1eba8dff875e..9884c8c6e934 100644 --- a/drivers/ata/ahci_qoriq.c +++ b/drivers/ata/ahci_qoriq.c @@ -46,11 +46,13 @@ #define LS1021A_AXICC_ADDR 0xC0 #define SATA_ECC_DISABLE 0x00020000 +#define LS1046A_SATA_ECC_DIS 0x80000000 enum ahci_qoriq_type { AHCI_LS1021A, AHCI_LS1043A, AHCI_LS2080A, + AHCI_LS1046A, }; struct ahci_qoriq_priv { @@ -63,6 +65,7 @@ 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}, + { .compatible = "fsl,ls1046a-ahci", .data = (void *)AHCI_LS1046A}, {}, }; MODULE_DEVICE_TABLE(of, ahci_qoriq_of_match); @@ -175,6 +178,13 @@ static int ahci_qoriq_phy_init(struct ahci_host_priv *hpriv) writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC); break; + + case AHCI_LS1046A: + writel(LS1046A_SATA_ECC_DIS, qpriv->ecc_addr); + writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1); + writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); + writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC); + break; } return 0; @@ -204,9 +214,9 @@ static int ahci_qoriq_probe(struct platform_device *pdev) 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"); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "sata-ecc"); + if (res) { qoriq_priv->ecc_addr = devm_ioremap_resource(dev, res); if (IS_ERR(qoriq_priv->ecc_addr)) return PTR_ERR(qoriq_priv->ecc_addr); diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 0d028ead99e8..ee7db3119b18 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -140,6 +140,7 @@ EXPORT_SYMBOL_GPL(ahci_shost_attrs); struct device_attribute *ahci_sdev_attrs[] = { &dev_attr_sw_activity, &dev_attr_unload_heads, + &dev_attr_ncq_prio_enable, NULL }; EXPORT_SYMBOL_GPL(ahci_sdev_attrs); diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 223a770f78f3..9cd0a2d41816 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -129,7 +129,7 @@ static int ata_force_tbl_size; static char ata_force_param_buf[PAGE_SIZE] __initdata; /* param_buf is thrown away after initialization, disallow read */ module_param_string(force, ata_force_param_buf, sizeof(ata_force_param_buf), 0); -MODULE_PARM_DESC(force, "Force ATA configurations including cable type, link speed and transfer mode (see Documentation/kernel-parameters.txt for details)"); +MODULE_PARM_DESC(force, "Force ATA configurations including cable type, link speed and transfer mode (see Documentation/admin-guide/kernel-parameters.rst for details)"); static int atapi_enabled = 1; module_param(atapi_enabled, int, 0444); @@ -739,6 +739,7 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev) * @n_block: Number of blocks * @tf_flags: RW/FUA etc... * @tag: tag + * @class: IO priority class * * LOCKING: * None. @@ -753,7 +754,7 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev) */ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, u64 block, u32 n_block, unsigned int tf_flags, - unsigned int tag) + unsigned int tag, int class) { tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; tf->flags |= tf_flags; @@ -785,6 +786,12 @@ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, tf->device = ATA_LBA; if (tf->flags & ATA_TFLAG_FUA) tf->device |= 1 << 7; + + if (dev->flags & ATA_DFLAG_NCQ_PRIO) { + if (class == IOPRIO_CLASS_RT) + tf->hob_nsect |= ATA_PRIO_HIGH << + ATA_SHIFT_PRIO; + } } else if (dev->flags & ATA_DFLAG_LBA) { tf->flags |= ATA_TFLAG_LBA; @@ -2156,6 +2163,37 @@ static void ata_dev_config_ncq_non_data(struct ata_device *dev) } } +static void ata_dev_config_ncq_prio(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + unsigned int err_mask; + + if (!(dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLE)) { + dev->flags &= ~ATA_DFLAG_NCQ_PRIO; + return; + } + + err_mask = ata_read_log_page(dev, + ATA_LOG_SATA_ID_DEV_DATA, + ATA_LOG_SATA_SETTINGS, + ap->sector_buf, + 1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to get Identify Device data, Emask 0x%x\n", + err_mask); + return; + } + + if (ap->sector_buf[ATA_LOG_NCQ_PRIO_OFFSET] & BIT(3)) { + dev->flags |= ATA_DFLAG_NCQ_PRIO; + } else { + dev->flags &= ~ATA_DFLAG_NCQ_PRIO; + ata_dev_dbg(dev, "SATA page does not support priority\n"); + } + +} + static int ata_dev_config_ncq(struct ata_device *dev, char *desc, size_t desc_sz) { @@ -2205,6 +2243,8 @@ static int ata_dev_config_ncq(struct ata_device *dev, ata_dev_config_ncq_send_recv(dev); if (ata_id_has_ncq_non_data(dev->id)) ata_dev_config_ncq_non_data(dev); + if (ata_id_has_ncq_prio(dev->id)) + ata_dev_config_ncq_prio(dev); } return 0; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 9cceb4a875a5..1f863e757ee4 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -50,6 +50,7 @@ #include <linux/uaccess.h> #include <linux/suspend.h> #include <asm/unaligned.h> +#include <linux/ioprio.h> #include "libata.h" #include "libata-transport.h" @@ -270,6 +271,83 @@ DEVICE_ATTR(unload_heads, S_IRUGO | S_IWUSR, ata_scsi_park_show, ata_scsi_park_store); EXPORT_SYMBOL_GPL(dev_attr_unload_heads); +static ssize_t ata_ncq_prio_enable_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(device); + struct ata_port *ap; + struct ata_device *dev; + bool ncq_prio_enable; + int rc = 0; + + ap = ata_shost_to_port(sdev->host); + + spin_lock_irq(ap->lock); + dev = ata_scsi_find_dev(ap, sdev); + if (!dev) { + rc = -ENODEV; + goto unlock; + } + + ncq_prio_enable = dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLE; + +unlock: + spin_unlock_irq(ap->lock); + + return rc ? rc : snprintf(buf, 20, "%u\n", ncq_prio_enable); +} + +static ssize_t ata_ncq_prio_enable_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct scsi_device *sdev = to_scsi_device(device); + struct ata_port *ap; + struct ata_device *dev; + long int input; + int rc; + + rc = kstrtol(buf, 10, &input); + if (rc) + return rc; + if ((input < 0) || (input > 1)) + return -EINVAL; + + ap = ata_shost_to_port(sdev->host); + dev = ata_scsi_find_dev(ap, sdev); + if (unlikely(!dev)) + return -ENODEV; + + spin_lock_irq(ap->lock); + if (input) + dev->flags |= ATA_DFLAG_NCQ_PRIO_ENABLE; + else + dev->flags &= ~ATA_DFLAG_NCQ_PRIO_ENABLE; + + dev->link->eh_info.action |= ATA_EH_REVALIDATE; + dev->link->eh_info.flags |= ATA_EHI_QUIET; + ata_port_schedule_eh(ap); + spin_unlock_irq(ap->lock); + + ata_port_wait_eh(ap); + + if (input) { + spin_lock_irq(ap->lock); + if (!(dev->flags & ATA_DFLAG_NCQ_PRIO)) { + dev->flags &= ~ATA_DFLAG_NCQ_PRIO_ENABLE; + rc = -EIO; + } + spin_unlock_irq(ap->lock); + } + + return rc ? rc : len; +} + +DEVICE_ATTR(ncq_prio_enable, S_IRUGO | S_IWUSR, + ata_ncq_prio_enable_show, ata_ncq_prio_enable_store); +EXPORT_SYMBOL_GPL(dev_attr_ncq_prio_enable); + void ata_scsi_set_sense(struct ata_device *dev, struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) { @@ -401,6 +479,7 @@ EXPORT_SYMBOL_GPL(dev_attr_sw_activity); struct device_attribute *ata_common_sdev_attrs[] = { &dev_attr_unload_heads, + &dev_attr_ncq_prio_enable, NULL }; EXPORT_SYMBOL_GPL(ata_common_sdev_attrs); @@ -1088,7 +1167,7 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc) desc[1] = tf->command; /* status */ desc[2] = tf->device; desc[3] = tf->nsect; - desc[0] = 0; + desc[7] = 0; if (tf->flags & ATA_TFLAG_LBA48) { desc[8] |= 0x80; if (tf->hob_nsect) @@ -1159,6 +1238,7 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev) { sdev->use_10_for_rw = 1; sdev->use_10_for_ms = 1; + sdev->no_write_same = 1; /* Schedule policy is determined by ->qc_defer() callback and * it needs to see every deferred qc. Set dev_blocked to 1 to @@ -1755,6 +1835,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) { struct scsi_cmnd *scmd = qc->scsicmd; const u8 *cdb = scmd->cmnd; + struct request *rq = scmd->request; + int class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq)); unsigned int tf_flags = 0; u64 block; u32 n_block; @@ -1821,7 +1903,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) qc->nbytes = n_block * scmd->device->sector_size; rc = ata_build_rw_tf(&qc->tf, qc->dev, block, n_block, tf_flags, - qc->tag); + qc->tag, class); + if (likely(rc == 0)) return 0; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 3b301a48007c..8f3a5596dd67 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -66,7 +66,7 @@ extern u64 ata_tf_to_lba48(const struct ata_taskfile *tf); extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag); extern int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, u64 block, u32 n_block, unsigned int tf_flags, - unsigned int tag); + unsigned int tag, int class); extern u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev); extern unsigned ata_exec_internal(struct ata_device *dev, diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c index 139d20778b29..d4caa23f5a88 100644 --- a/drivers/ata/pata_imx.c +++ b/drivers/ata/pata_imx.c @@ -11,19 +11,26 @@ * * TODO: * - dmaengine support - * - check if timing stuff needed */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/blkdev.h> -#include <scsi/scsi_host.h> + #include <linux/ata.h> +#include <linux/clk.h> #include <linux/libata.h> +#include <linux/module.h> #include <linux/platform_device.h> -#include <linux/clk.h> #define DRV_NAME "pata_imx" +#define PATA_IMX_ATA_TIME_OFF 0x00 +#define PATA_IMX_ATA_TIME_ON 0x01 +#define PATA_IMX_ATA_TIME_1 0x02 +#define PATA_IMX_ATA_TIME_2W 0x03 +#define PATA_IMX_ATA_TIME_2R 0x04 +#define PATA_IMX_ATA_TIME_AX 0x05 +#define PATA_IMX_ATA_TIME_PIO_RDX 0x06 +#define PATA_IMX_ATA_TIME_4 0x07 +#define PATA_IMX_ATA_TIME_9 0x08 + #define PATA_IMX_ATA_CONTROL 0x24 #define PATA_IMX_ATA_CTRL_FIFO_RST_B (1<<7) #define PATA_IMX_ATA_CTRL_ATA_RST_B (1<<6) @@ -33,6 +40,10 @@ #define PATA_IMX_DRIVE_DATA 0xA0 #define PATA_IMX_DRIVE_CONTROL 0xD8 +static u32 pio_t4[] = { 30, 20, 15, 10, 10 }; +static u32 pio_t9[] = { 20, 15, 10, 10, 10 }; +static u32 pio_tA[] = { 35, 35, 35, 35, 35 }; + struct pata_imx_priv { struct clk *clk; /* timings/interrupt/control regs */ @@ -40,28 +51,49 @@ struct pata_imx_priv { u32 ata_ctl; }; -static int pata_imx_set_mode(struct ata_link *link, struct ata_device **unused) +static void pata_imx_set_timing(struct ata_device *adev, + struct pata_imx_priv *priv) +{ + struct ata_timing timing; + unsigned long clkrate; + u32 T, mode; + + clkrate = clk_get_rate(priv->clk); + + if (adev->pio_mode < XFER_PIO_0 || adev->pio_mode > XFER_PIO_4 || + !clkrate) + return; + + T = 1000000000 / clkrate; + ata_timing_compute(adev, adev->pio_mode, &timing, T * 1000, 0); + + mode = adev->pio_mode - XFER_PIO_0; + + writeb(3, priv->host_regs + PATA_IMX_ATA_TIME_OFF); + writeb(3, priv->host_regs + PATA_IMX_ATA_TIME_ON); + writeb(timing.setup, priv->host_regs + PATA_IMX_ATA_TIME_1); + writeb(timing.act8b, priv->host_regs + PATA_IMX_ATA_TIME_2W); + writeb(timing.act8b, priv->host_regs + PATA_IMX_ATA_TIME_2R); + writeb(1, priv->host_regs + PATA_IMX_ATA_TIME_PIO_RDX); + + writeb(pio_t4[mode] / T + 1, priv->host_regs + PATA_IMX_ATA_TIME_4); + writeb(pio_t9[mode] / T + 1, priv->host_regs + PATA_IMX_ATA_TIME_9); + writeb(pio_tA[mode] / T + 1, priv->host_regs + PATA_IMX_ATA_TIME_AX); +} + +static void pata_imx_set_piomode(struct ata_port *ap, struct ata_device *adev) { - struct ata_device *dev; - struct ata_port *ap = link->ap; struct pata_imx_priv *priv = ap->host->private_data; u32 val; - ata_for_each_dev(dev, link, ENABLED) { - dev->pio_mode = dev->xfer_mode = XFER_PIO_0; - dev->xfer_shift = ATA_SHIFT_PIO; - dev->flags |= ATA_DFLAG_PIO; - - val = __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL); - if (ata_pio_need_iordy(dev)) - val |= PATA_IMX_ATA_CTRL_IORDY_EN; - else - val &= ~PATA_IMX_ATA_CTRL_IORDY_EN; - __raw_writel(val, priv->host_regs + PATA_IMX_ATA_CONTROL); + pata_imx_set_timing(adev, priv); - ata_dev_info(dev, "configured for PIO\n"); - } - return 0; + val = __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL); + if (ata_pio_need_iordy(adev)) + val |= PATA_IMX_ATA_CTRL_IORDY_EN; + else + val &= ~PATA_IMX_ATA_CTRL_IORDY_EN; + __raw_writel(val, priv->host_regs + PATA_IMX_ATA_CONTROL); } static struct scsi_host_template pata_imx_sht = { @@ -72,7 +104,7 @@ static struct ata_port_operations pata_imx_port_ops = { .inherits = &ata_sff_port_ops, .sff_data_xfer = ata_sff_data_xfer_noirq, .cable_detect = ata_cable_unknown, - .set_mode = pata_imx_set_mode, + .set_piomode = pata_imx_set_piomode, }; static void pata_imx_setup_port(struct ata_ioports *ioaddr) @@ -128,7 +160,7 @@ static int pata_imx_probe(struct platform_device *pdev) ap = host->ports[0]; ap->ops = &pata_imx_port_ops; - ap->pio_mask = ATA_PIO0; + ap->pio_mask = ATA_PIO4; ap->flags |= ATA_FLAG_SLAVE_POSS; io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index efc48bf89d51..823e938c9a78 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -4090,7 +4090,20 @@ static int mv_platform_probe(struct platform_device *pdev) /* allocate host */ if (pdev->dev.of_node) { - of_property_read_u32(pdev->dev.of_node, "nr-ports", &n_ports); + rc = of_property_read_u32(pdev->dev.of_node, "nr-ports", + &n_ports); + if (rc) { + dev_err(&pdev->dev, + "error parsing nr-ports property: %d\n", rc); + return rc; + } + + if (n_ports <= 0) { + dev_err(&pdev->dev, "nr-ports must be positive: %d\n", + n_ports); + return -EINVAL; + } + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); } else { mv_platform_data = dev_get_platdata(&pdev->dev); |