From 16169fb781827b82a4c0261195184dcc97a57af7 Mon Sep 17 00:00:00 2001 From: Tomas Henzl Date: Wed, 10 Aug 2022 19:59:09 +0200 Subject: [PATCH 01/45] ata: libata-core: Print timeout value when internal command times Printing the timeout value may help in troubleshooting failures. Signed-off-by: David Milburn Signed-off-by: Tomas Henzl Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 826d41f341e4..9478194740e0 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1578,8 +1578,8 @@ static unsigned ata_exec_internal_sg(struct ata_device *dev, else ata_qc_complete(qc); - ata_dev_warn(dev, "qc timeout (cmd 0x%x)\n", - command); + ata_dev_warn(dev, "qc timeout after %u msecs (cmd 0x%x)\n", + timeout, command); } spin_unlock_irqrestore(ap->lock, flags); From 99ad3f9f829fafdcd606a8b1123e331b3b53eb04 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 16 Aug 2022 13:53:28 +0200 Subject: [PATCH 02/45] ata: libata-core: improve parameter names for ata_dev_set_feature() ata_dev_set_feature() is currently used for enabling/disabling any ATA feature, e.g. SETFEATURES_SPINUP and SETFEATURE_SENSE_DATA, i.e. it is not only used to enable/disable SATA specific features. For most features, the enable/disable bit is specified in the subcommand specific field "count". It is only for the specific subcommands "Enable SATA feature" (0x10) and "Disable SATA feature" (0x90) where the field "count" is used to specify a feature instead of enable/disable. The parameter names for this function are thus quite misleading. Rename the parameter names to be more generic and in line with ACS-5, and remove the references to "SATA FEATURES" in the kernel-doc. Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 19 +++++++++---------- drivers/ata/libata.h | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 9478194740e0..864b26009eae 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4324,13 +4324,12 @@ static unsigned int ata_dev_set_xfermode(struct ata_device *dev) } /** - * ata_dev_set_feature - Issue SET FEATURES - SATA FEATURES + * ata_dev_set_feature - Issue SET FEATURES * @dev: Device to which command will be sent - * @enable: Whether to enable or disable the feature - * @feature: The sector count represents the feature to set + * @subcmd: The SET FEATURES subcommand to be sent + * @action: The sector count represents a subcommand specific action * - * Issue SET FEATURES - SATA FEATURES command to device @dev - * on port @ap with sector count + * Issue SET FEATURES command to device @dev on port @ap with sector count * * LOCKING: * PCI/etc. bus probe sem. @@ -4338,23 +4337,23 @@ static unsigned int ata_dev_set_xfermode(struct ata_device *dev) * RETURNS: * 0 on success, AC_ERR_* mask otherwise. */ -unsigned int ata_dev_set_feature(struct ata_device *dev, u8 enable, u8 feature) +unsigned int ata_dev_set_feature(struct ata_device *dev, u8 subcmd, u8 action) { struct ata_taskfile tf; unsigned int err_mask; unsigned int timeout = 0; /* set up set-features taskfile */ - ata_dev_dbg(dev, "set features - SATA features\n"); + ata_dev_dbg(dev, "set features\n"); ata_tf_init(dev, &tf); tf.command = ATA_CMD_SET_FEATURES; - tf.feature = enable; + tf.feature = subcmd; tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; tf.protocol = ATA_PROT_NODATA; - tf.nsect = feature; + tf.nsect = action; - if (enable == SETFEATURES_SPINUP) + if (subcmd == SETFEATURES_SPINUP) timeout = ata_probe_timeout ? ata_probe_timeout * 1000 : SETFEATURES_SPINUP_TIMEOUT; err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, timeout); diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 98bc8649c63f..bc84fbb48c0a 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -64,7 +64,7 @@ extern int ata_dev_configure(struct ata_device *dev); extern int sata_down_spd_limit(struct ata_link *link, u32 spd_limit); extern int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel); extern unsigned int ata_dev_set_feature(struct ata_device *dev, - u8 enable, u8 feature); + u8 subcmd, u8 action); extern void ata_qc_free(struct ata_queued_cmd *qc); extern void ata_qc_issue(struct ata_queued_cmd *qc); extern void __ata_qc_complete(struct ata_queued_cmd *qc); From fee6073051c394dbec21d7024d90e31e0ff3f678 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 25 Aug 2022 20:47:28 +0200 Subject: [PATCH 03/45] ata: ahci: Do not check ACPI_FADT_LOW_POWER_S0 The ACPI_FADT_LOW_POWER_S0 flag merely means that it is better to use low-power S0 idle on the given platform than S3 (provided that the latter is supported) and it doesn't preclude using either of them (which of them will be used depends on the choices made by user space). For this reason, there is no benefit from checking that flag in ahci_update_initial_lpm_policy(). First off, it cannot be a bug to do S3 with policy set to either ATA_LPM_MIN_POWER_WITH_PARTIAL or ATA_LPM_MIN_POWER, because S3 can be used on systems with ACPI_FADT_LOW_POWER_S0 set and it must work if really supported, so the ACPI_FADT_LOW_POWER_S0 check is not needed to protect the S3-capable systems from failing. Second, suspend-to-idle can be carried out on a system with ACPI_FADT_LOW_POWER_S0 unset and it is expected to work, so if setting policy to either ATA_LPM_MIN_POWER_WITH_PARTIAL or ATA_LPM_MIN_POWER is needed to handle that case correctly, it should be done regardless of the ACPI_FADT_LOW_POWER_S0 value. Accordingly, replace the ACPI_FADT_LOW_POWER_S0 check in ahci_update_initial_lpm_policy() with pm_suspend_default_s2idle() which is more general and also takes the user's preference into account and drop the CONFIG_ACPI #ifdef around it that is not necessary any more. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello Signed-off-by: Damien Le Moal --- drivers/ata/ahci.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index c1eca72b4575..2371ab5fe8f2 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1609,15 +1609,12 @@ static void ahci_update_initial_lpm_policy(struct ata_port *ap, goto update_policy; } -#ifdef CONFIG_ACPI - if (policy > ATA_LPM_MED_POWER && - (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) { + if (policy > ATA_LPM_MED_POWER && pm_suspend_default_s2idle()) { if (hpriv->cap & HOST_CAP_PART) policy = ATA_LPM_MIN_POWER_WITH_PARTIAL; else if (hpriv->cap & HOST_CAP_SSC) policy = ATA_LPM_MIN_POWER; } -#endif update_policy: if (policy >= ATA_LPM_UNKNOWN && policy <= ATA_LPM_MIN_POWER) From 614065aba7041fab831e7c69ca8c7adebbc0430c Mon Sep 17 00:00:00 2001 From: Jinpeng Cui Date: Tue, 23 Aug 2022 12:29:14 +0000 Subject: [PATCH 04/45] ata: libata-core: remove redundant err_mask variable Return value from ata_exec_internal() directly instead of taking this in another redundant variable. Reported-by: Zeal Robot Signed-off-by: Jinpeng Cui Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 864b26009eae..0ba0e692210f 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4340,7 +4340,6 @@ static unsigned int ata_dev_set_xfermode(struct ata_device *dev) unsigned int ata_dev_set_feature(struct ata_device *dev, u8 subcmd, u8 action) { struct ata_taskfile tf; - unsigned int err_mask; unsigned int timeout = 0; /* set up set-features taskfile */ @@ -4356,9 +4355,8 @@ unsigned int ata_dev_set_feature(struct ata_device *dev, u8 subcmd, u8 action) if (subcmd == SETFEATURES_SPINUP) timeout = ata_probe_timeout ? ata_probe_timeout * 1000 : SETFEATURES_SPINUP_TIMEOUT; - err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, timeout); - return err_mask; + return ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, timeout); } EXPORT_SYMBOL_GPL(ata_dev_set_feature); From e00923c59e68b63c998a0fef4145b5279331968a Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 9 Jun 2022 12:33:13 +0900 Subject: [PATCH 05/45] ata: libata: Rename ATA_DFLAG_NCQ_PRIO_ENABLE Rename ATA_DFLAG_NCQ_PRIO_ENABLE to ATA_DFLAG_NCQ_PRIO_ENABLED to match the fact that this flags indicates if NCQ priority use is enabled by the user. Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 4 ++-- drivers/ata/libata-sata.c | 6 +++--- include/linux/libata.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 0ba0e692210f..a5c51da10638 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -719,7 +719,7 @@ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, if (tf->flags & ATA_TFLAG_FUA) tf->device |= 1 << 7; - if (dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLE && + if (dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLED && class == IOPRIO_CLASS_RT) tf->hob_nsect |= ATA_PRIO_HIGH << ATA_SHIFT_PRIO; } else if (dev->flags & ATA_DFLAG_LBA) { @@ -2171,7 +2171,7 @@ static void ata_dev_config_ncq_prio(struct ata_device *dev) return; not_supported: - dev->flags &= ~ATA_DFLAG_NCQ_PRIO_ENABLE; + dev->flags &= ~ATA_DFLAG_NCQ_PRIO_ENABLED; dev->flags &= ~ATA_DFLAG_NCQ_PRIO; } diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index 7a5fe41aa5ae..eef57d101ed1 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -870,7 +870,7 @@ static ssize_t ata_ncq_prio_enable_show(struct device *device, if (!dev) rc = -ENODEV; else - ncq_prio_enable = dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLE; + ncq_prio_enable = dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLED; spin_unlock_irq(ap->lock); return rc ? rc : sysfs_emit(buf, "%u\n", ncq_prio_enable); @@ -905,9 +905,9 @@ static ssize_t ata_ncq_prio_enable_store(struct device *device, } if (input) - dev->flags |= ATA_DFLAG_NCQ_PRIO_ENABLE; + dev->flags |= ATA_DFLAG_NCQ_PRIO_ENABLED; else - dev->flags &= ~ATA_DFLAG_NCQ_PRIO_ENABLE; + dev->flags &= ~ATA_DFLAG_NCQ_PRIO_ENABLED; unlock: spin_unlock_irq(ap->lock); diff --git a/include/linux/libata.h b/include/linux/libata.h index 0269ff114f5a..a505cfb92ab3 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -101,7 +101,7 @@ enum { ATA_DFLAG_UNLOCK_HPA = (1 << 18), /* unlock HPA */ ATA_DFLAG_NCQ_SEND_RECV = (1 << 19), /* device supports NCQ SEND and RECV */ ATA_DFLAG_NCQ_PRIO = (1 << 20), /* device supports NCQ priority */ - ATA_DFLAG_NCQ_PRIO_ENABLE = (1 << 21), /* Priority cmds sent to dev */ + ATA_DFLAG_NCQ_PRIO_ENABLED = (1 << 21), /* Priority cmds sent to dev */ ATA_DFLAG_INIT_MASK = (1 << 24) - 1, ATA_DFLAG_DETACH = (1 << 24), From 066de3b9d93b6564e2f68005484d8c0597620748 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Mon, 25 Jul 2022 10:01:27 +0900 Subject: [PATCH 06/45] ata: libata-core: Simplify ata_build_rw_tf() Since ata_build_rw_tf() is only called from ata_scsi_rw_xlat() with the tf, dev and tag arguments obtained from the queued command structure, we can simplify the interface of ata_build_rw_tf() by passing directly the qc structure as argument. Furthermore, since ata_scsi_rw_xlat() is never used for internal commands, we can also remove the internal tag check for the NCQ case. Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 20 ++++++++++---------- drivers/ata/libata-scsi.c | 4 +--- drivers/ata/libata.h | 5 ++--- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index a5c51da10638..0b62fa14a74c 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -665,33 +665,33 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev) /** * ata_build_rw_tf - Build ATA taskfile for given read/write request - * @tf: Target ATA taskfile - * @dev: ATA device @tf belongs to + * @qc: Metadata associated with the taskfile to build * @block: Block address * @n_block: Number of blocks * @tf_flags: RW/FUA etc... - * @tag: tag * @class: IO priority class * * LOCKING: * None. * - * Build ATA taskfile @tf for read/write request described by - * @block, @n_block, @tf_flags and @tag on @dev. + * Build ATA taskfile for the command @qc for read/write request described + * by @block, @n_block, @tf_flags and @class. * * RETURNS: * * 0 on success, -ERANGE if the request is too large for @dev, * -EINVAL if the request is invalid. */ -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, int class) +int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, + unsigned int tf_flags, int class) { + struct ata_taskfile *tf = &qc->tf; + struct ata_device *dev = qc->dev; + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; tf->flags |= tf_flags; - if (ata_ncq_enabled(dev) && !ata_tag_internal(tag)) { + if (ata_ncq_enabled(dev)) { /* yay, NCQ */ if (!lba_48_ok(block, n_block)) return -ERANGE; @@ -704,7 +704,7 @@ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, else tf->command = ATA_CMD_FPDMA_READ; - tf->nsect = tag << 3; + tf->nsect = qc->hw_tag << 3; tf->hob_feature = (n_block >> 8) & 0xff; tf->feature = n_block & 0xff; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 29e2f55c6faa..f3c64e796423 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1605,9 +1605,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) qc->flags |= ATA_QCFLAG_IO; qc->nbytes = n_block * scmd->device->sector_size; - rc = ata_build_rw_tf(&qc->tf, qc->dev, block, n_block, tf_flags, - qc->hw_tag, class); - + rc = ata_build_rw_tf(qc, block, n_block, tf_flags, class); if (likely(rc == 0)) return 0; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index bc84fbb48c0a..2c5c8273af01 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -44,9 +44,8 @@ static inline void ata_force_cbl(struct ata_port *ap) { } #endif extern u64 ata_tf_to_lba(const struct ata_taskfile *tf); extern u64 ata_tf_to_lba48(const struct ata_taskfile *tf); -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, int class); +extern int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, + unsigned int tf_flags, 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, From 024811a2da45a79d58ba61b524441722510d5dc5 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 26 Aug 2022 07:43:30 +0900 Subject: [PATCH 07/45] ata: libata-core: Simplify ata_dev_set_xfermode() The err_mask variable is not useful. Remove it. Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 0b62fa14a74c..d0242c75aac5 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4295,7 +4295,6 @@ static void ata_dev_xfermask(struct ata_device *dev) static unsigned int ata_dev_set_xfermode(struct ata_device *dev) { struct ata_taskfile tf; - unsigned int err_mask; /* set up set-features taskfile */ ata_dev_dbg(dev, "set features - xfer mode\n"); @@ -4317,10 +4316,11 @@ static unsigned int ata_dev_set_xfermode(struct ata_device *dev) else /* In the ancient relic department - skip all of this */ return 0; - /* On some disks, this command causes spin-up, so we need longer timeout */ - err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 15000); - - return err_mask; + /* + * On some disks, this command causes spin-up, so we need longer + * timeout. + */ + return ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 15000); } /** From 0b2436d3d25fe77573669a946aa26f3087918a75 Mon Sep 17 00:00:00 2001 From: Shaomin Deng Date: Thu, 25 Aug 2022 10:52:15 -0400 Subject: [PATCH 08/45] ata: pata_macio: Remove unneeded word in comments There is unneeded word "to" in line 669, so remove it. Signed-off-by: Shaomin Deng Reviewed-by: Sergey Shtylyov Signed-off-by: Damien Le Moal --- drivers/ata/pata_macio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c index bfea2be2959a..9ccaac9e2bc3 100644 --- a/drivers/ata/pata_macio.c +++ b/drivers/ata/pata_macio.c @@ -666,8 +666,7 @@ static u8 pata_macio_bmdma_status(struct ata_port *ap) * a multi-block transfer. * * - The dbdma fifo hasn't yet finished flushing to - * to system memory when the disk interrupt occurs. - * + * system memory when the disk interrupt occurs. */ /* First check for errors */ From 03070458d700242f1caf6ede4225a728145ccd77 Mon Sep 17 00:00:00 2001 From: Shaomin Deng Date: Tue, 30 Aug 2022 03:50:24 -0400 Subject: [PATCH 09/45] ata: libata-sff: Fix double word in comments Remove the repeated word "Transfer" in comments. Signed-off-by: Shaomin Deng Signed-off-by: Damien Le Moal --- drivers/ata/libata-sff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index b1666adc1c3a..7916e369e15e 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -776,7 +776,7 @@ static void atapi_send_cdb(struct ata_port *ap, struct ata_queued_cmd *qc) * @qc: Command on going * @bytes: number of bytes * - * Transfer Transfer data from/to the ATAPI device. + * Transfer data from/to the ATAPI device. * * LOCKING: * Inherited from caller. From 55d5ba550535c970c03cd0d0008ad1d61b238be4 Mon Sep 17 00:00:00 2001 From: Li Zhong Date: Sat, 3 Sep 2022 16:10:39 -0700 Subject: [PATCH 10/45] ata: libata-core: Check errors in sata_print_link_status() sata_scr_read() could return negative error code on failure. Check the return value when reading the control register. Signed-off-by: Li Zhong Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index d0242c75aac5..75b86913db1a 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3021,7 +3021,8 @@ static void sata_print_link_status(struct ata_link *link) if (sata_scr_read(link, SCR_STATUS, &sstatus)) return; - sata_scr_read(link, SCR_CONTROL, &scontrol); + if (sata_scr_read(link, SCR_CONTROL, &scontrol)) + return; if (ata_phys_link_online(link)) { tmp = (sstatus >> 4) & 0xf; From 3ebe59a54111ccc9d4044d73681e93599b5f51fa Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Wed, 14 Sep 2022 16:27:12 +0200 Subject: [PATCH 11/45] ata: clean up how architectures enable PATA_PLATFORM and PATA_OF_PLATFORM There are two options for platform device PATA support: PATA_PLATFORM: Generic platform device PATA support PATA_OF_PLATFORM: OpenFirmware platform device PATA support If an architecture allows the generic platform device PATA support, it shall select HAVE_PATA_PLATFORM. Then, Generic platform device PATA support is available and can be selected. If an architecture has OpenFirmware support, which it indicates by selecting OF, OpenFirmware platform device PATA support is available and can be selected. If OpenFirmware platform device PATA support is selected, then the functionality (code files) from Generic platform device PATA support needs to be integrated in the kernel build for the OpenFirmware platform device PATA support to work. Select PATA_PLATFORM in PATA_OF_PLATFORM to make sure the needed files are added in the build. So, architectures with OpenFirmware support, do not need to additionally select HAVE_PATA_PLATFORM. It is only needed by architecture that want the non-OF pata-platform module. Reflect this way of intended use of config symbols in the ata Kconfig and adjust all architecture definitions. This follows the suggestion from Arnd Bergmann (see Link). Suggested-by: Arnd Bergmann Link: https://lore.kernel.org/all/4b33bffc-2b6d-46b4-9f1d-d18e55975a5a@www.fastmail.com/ Signed-off-by: Lukas Bulwahn Reviewed-by: Arnd Bergmann Signed-off-by: Damien Le Moal --- arch/arm/mach-versatile/Kconfig | 1 - arch/arm64/Kconfig | 1 - drivers/ata/Kconfig | 6 +++--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-versatile/Kconfig b/arch/arm/mach-versatile/Kconfig index 2ef226194c3a..b1519b4dc03a 100644 --- a/arch/arm/mach-versatile/Kconfig +++ b/arch/arm/mach-versatile/Kconfig @@ -256,7 +256,6 @@ menuconfig ARCH_VEXPRESS select GPIOLIB select HAVE_ARM_SCU if SMP select HAVE_ARM_TWD if SMP - select HAVE_PATA_PLATFORM select CLK_ICST select NO_IOPORT_MAP select PLAT_VERSATILE diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 571cc234d0b3..a4eafb9560c9 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -194,7 +194,6 @@ config ARM64 select HAVE_IRQ_TIME_ACCOUNTING select HAVE_KVM select HAVE_NMI - select HAVE_PATA_PLATFORM select HAVE_PERF_EVENTS select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 1c9f4fb2595d..c93d97455744 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -1102,8 +1102,7 @@ config PATA_PCMCIA If unsure, say N. config PATA_PLATFORM - tristate "Generic platform device PATA support" - depends on EXPERT || PPC || HAVE_PATA_PLATFORM + tristate "Generic platform device PATA support" if EXPERT || HAVE_PATA_PLATFORM help This option enables support for generic directly connected ATA devices commonly found on embedded systems. @@ -1112,7 +1111,8 @@ config PATA_PLATFORM config PATA_OF_PLATFORM tristate "OpenFirmware platform device PATA support" - depends on PATA_PLATFORM && OF + depends on OF + select PATA_PLATFORM help This option enables support for generic directly connected ATA devices commonly found on embedded systems with OpenFirmware From d3243965f24ab002b2d8c48a91eb7ce027d3f11a Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Wed, 14 Sep 2022 16:27:13 +0200 Subject: [PATCH 12/45] ata: make PATA_PLATFORM selectable only for suitable architectures It is currently possible to select "Generic platform device PATA support" in two situations: - architecture allows the generic platform device PATA support and indicates that with "select HAVE_PATA_PLATFORM". - if the user claims to be an EXPERT by setting CONFIG_EXPERT to yes However, there is no use case to have Generic platform device PATA support in a kernel build if the architecture definition, i.e., the selection of configs by an architecture, does not support it. If the architecture definition is wrong, i.e., it just misses a 'select HAVE_PATA_PLATFORM', then even an expert that configures the kernel build should not just fix that by overruling the claimed support by an architecture. If the architecture definition is wrong, the expert should just provide a patch to correct the architecture definition instead---in the end, if the user is an expert, sending a quick one-line patch should not be an issue. In other words, I do not see the deeper why an expert can overrule the architecture definition in this case, as the expert may not overrule the config selections defined by the architecture in the large majority ---or probably all other (modulo some mistakes)---of similar cases. Signed-off-by: Lukas Bulwahn Reviewed-by: Arnd Bergmann Signed-off-by: Damien Le Moal --- drivers/ata/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index c93d97455744..fc11d9d30d72 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -1102,7 +1102,7 @@ config PATA_PCMCIA If unsure, say N. config PATA_PLATFORM - tristate "Generic platform device PATA support" if EXPERT || HAVE_PATA_PLATFORM + tristate "Generic platform device PATA support" if HAVE_PATA_PLATFORM help This option enables support for generic directly connected ATA devices commonly found on embedded systems. From 6f997d4bb98becdc5d23affe207b3b0e854bcc3b Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:35:59 +0300 Subject: [PATCH 13/45] dt-bindings: ata: ahci-platform: Move dma-coherent to sata-common.yaml Seeing doubtfully any SATA device working without embedded DMA engine let's permit the device nodes being equipped with the dma-coherent property in case if the platform is capable of cache-coherent DMAs. As a side-effect we can drop the explicit dma-coherent property definition from the particular device schemas. Currently it concerns the Broadcom SATA AHCI controller only. Signed-off-by: Serge Semin Reviewed-by: Florian Fainelli Reviewed-by: Rob Herring Signed-off-by: Damien Le Moal --- Documentation/devicetree/bindings/ata/ahci-platform.yaml | 2 -- Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml | 2 -- Documentation/devicetree/bindings/ata/sata-common.yaml | 2 ++ 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.yaml b/Documentation/devicetree/bindings/ata/ahci-platform.yaml index c146ab8e14e5..9304e4731965 100644 --- a/Documentation/devicetree/bindings/ata/ahci-platform.yaml +++ b/Documentation/devicetree/bindings/ata/ahci-platform.yaml @@ -87,8 +87,6 @@ properties: description: regulator for AHCI controller - dma-coherent: true - phy-supply: description: regulator for PHY power diff --git a/Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml b/Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml index 235a93ac86b0..4ee74df8e58a 100644 --- a/Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml +++ b/Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml @@ -41,8 +41,6 @@ properties: interrupts: maxItems: 1 - dma-coherent: true - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/ata/sata-common.yaml b/Documentation/devicetree/bindings/ata/sata-common.yaml index 7ac77b1c5850..cb88d3e25e73 100644 --- a/Documentation/devicetree/bindings/ata/sata-common.yaml +++ b/Documentation/devicetree/bindings/ata/sata-common.yaml @@ -31,6 +31,8 @@ properties: "#size-cells": const: 0 + dma-coherent: true + patternProperties: "^sata-port@[0-9a-e]$": description: | From 0f3680ed1f4ca682e7a44aade35234632fe94505 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:00 +0300 Subject: [PATCH 14/45] dt-bindings: ata: ahci-platform: Detach common AHCI bindings In order to create a more sophisticated AHCI controller DT bindings let's divide the already available generic AHCI platform YAML schema into the platform part and a set of the common AHCI properties. The former part will be used to evaluate the AHCI DT nodes mainly compatible with the generic AHCI controller while the later schema will be used for more thorough AHCI DT nodes description. For instance such YAML schemas design will be useful for our DW AHCI SATA controller derivative with four clock sources, two reset lines, one system controller reference and specific max Rx/Tx DMA xfers size constraints. Note the phys and target-supply property requirement is preserved in the generic AHCI platform bindings because some platforms can lack of the explicitly specified PHYs or target device power regulators. Also note the SATA/AHCI ports properties have been moved to the $defs-paragraph of the schemas. It's done in order to create the extendable properties hierarchy such that particular AHCI-controller could add vendor-specific port properties. Signed-off-by: Serge Semin Reviewed-by: Rob Herring Signed-off-by: Damien Le Moal --- .../devicetree/bindings/ata/ahci-common.yaml | 100 ++++++++++++++++++ .../bindings/ata/ahci-platform.yaml | 74 ++----------- .../devicetree/bindings/ata/sata-common.yaml | 8 +- 3 files changed, 116 insertions(+), 66 deletions(-) create mode 100644 Documentation/devicetree/bindings/ata/ahci-common.yaml diff --git a/Documentation/devicetree/bindings/ata/ahci-common.yaml b/Documentation/devicetree/bindings/ata/ahci-common.yaml new file mode 100644 index 000000000000..e89bda3b62cc --- /dev/null +++ b/Documentation/devicetree/bindings/ata/ahci-common.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ata/ahci-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Common Properties for Serial ATA AHCI controllers + +maintainers: + - Hans de Goede + - Damien Le Moal + +description: + This document defines device tree properties for a common AHCI SATA + controller implementation. It's hardware interface is supposed to + conform to the technical standard defined by Intel (see Serial ATA + Advanced Host Controller Interface specification for details). The + document doesn't constitute a DT-node binding by itself but merely + defines a set of common properties for the AHCI-compatible devices. + +select: false + +allOf: + - $ref: sata-common.yaml# + +properties: + reg: + description: + Generic AHCI registers space conforming to the Serial ATA AHCI + specification. + + reg-names: + description: CSR space IDs + + interrupts: + description: + Generic AHCI state change interrupt. Can be implemented either as a + single line attached to the controller or as a set of the signals + indicating the particular port events. + + ahci-supply: + description: Power regulator for AHCI controller + + target-supply: + description: Power regulator for SATA target device + + phy-supply: + description: Power regulator for SATA PHY + + phys: + description: Reference to the SATA PHY node + maxItems: 1 + + phy-names: + maxItems: 1 + + ports-implemented: + $ref: '/schemas/types.yaml#/definitions/uint32' + description: + Mask that indicates which ports the HBA supports. Useful if PI is not + programmed by the BIOS, which is true for some embedded SoC's. + maximum: 0x1f + +patternProperties: + "^sata-port@[0-9a-f]+$": + $ref: '#/$defs/ahci-port' + description: + It is optionally possible to describe the ports as sub-nodes so + to enable each port independently when dealing with multiple PHYs. + +required: + - reg + - interrupts + +additionalProperties: true + +$defs: + ahci-port: + $ref: /schemas/ata/sata-common.yaml#/$defs/sata-port + + properties: + reg: + description: AHCI SATA port identifier + maxItems: 1 + + phys: + description: Individual AHCI SATA port PHY + maxItems: 1 + + phy-names: + description: AHCI SATA port PHY ID + maxItems: 1 + + target-supply: + description: Power regulator for SATA port target device + + required: + - reg + +... diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.yaml b/Documentation/devicetree/bindings/ata/ahci-platform.yaml index 9304e4731965..15be98e0385b 100644 --- a/Documentation/devicetree/bindings/ata/ahci-platform.yaml +++ b/Documentation/devicetree/bindings/ata/ahci-platform.yaml @@ -36,8 +36,7 @@ select: - compatible allOf: - - $ref: "sata-common.yaml#" - + - $ref: "ahci-common.yaml#" properties: compatible: @@ -69,90 +68,37 @@ properties: maxItems: 1 clocks: - description: - Clock IDs array as required by the controller. minItems: 1 maxItems: 3 clock-names: - description: - Names of clocks corresponding to IDs in the clock property. minItems: 1 maxItems: 3 interrupts: maxItems: 1 - ahci-supply: - description: - regulator for AHCI controller - - phy-supply: - description: - regulator for PHY power - - phys: - description: - List of all PHYs on this controller - maxItems: 1 - - phy-names: - description: - Name specifier for the PHYs - maxItems: 1 - - ports-implemented: - $ref: '/schemas/types.yaml#/definitions/uint32' - description: | - Mask that indicates which ports that the HBA supports - are available for software to use. Useful if PORTS_IMPL - is not programmed by the BIOS, which is true with - some embedded SoCs. - maximum: 0x1f - power-domains: maxItems: 1 resets: maxItems: 1 - target-supply: - description: - regulator for SATA target power +patternProperties: + "^sata-port@[0-9a-f]+$": + $ref: /schemas/ata/ahci-common.yaml#/$defs/ahci-port + + anyOf: + - required: [ phys ] + - required: [ target-supply ] + + unevaluatedProperties: false required: - compatible - reg - interrupts -patternProperties: - "^sata-port@[0-9a-f]+$": - type: object - additionalProperties: false - description: - Subnode with configuration of the Ports. - - properties: - reg: - maxItems: 1 - - phys: - maxItems: 1 - - phy-names: - maxItems: 1 - - target-supply: - description: - regulator for SATA target power - - required: - - reg - - anyOf: - - required: [ phys ] - - required: [ target-supply ] - unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/ata/sata-common.yaml b/Documentation/devicetree/bindings/ata/sata-common.yaml index cb88d3e25e73..5a31a902618d 100644 --- a/Documentation/devicetree/bindings/ata/sata-common.yaml +++ b/Documentation/devicetree/bindings/ata/sata-common.yaml @@ -35,9 +35,15 @@ properties: patternProperties: "^sata-port@[0-9a-e]$": + $ref: '#/$defs/sata-port' description: | DT nodes for ports connected on the SATA host. The SATA port nodes will be named "sata-port". + +additionalProperties: true + +$defs: + sata-port: type: object properties: @@ -49,6 +55,4 @@ patternProperties: multiplier making it possible to connect up to 15 disks to a single SATA port. -additionalProperties: true - ... From 9bd2407064680ad544d5edcea688cc45843f23ae Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:01 +0300 Subject: [PATCH 15/45] dt-bindings: ata: ahci-platform: Clarify common AHCI props constraints Indeed in accordance with what is implemented in the AHCI platform driver and the way the AHCI DT nodes are defined in the DT files we can add the next AHCI DT properties constraints: AHCI CSR ID is fixed to 'ahci', PHY name is fixed to 'sata-phy', AHCI controller can't have more than 32 ports by design, AHCI controller can have up to 32 IRQ lines. Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Reviewed-by: Rob Herring Signed-off-by: Damien Le Moal --- .../devicetree/bindings/ata/ahci-common.yaml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/ata/ahci-common.yaml b/Documentation/devicetree/bindings/ata/ahci-common.yaml index e89bda3b62cc..12a97b56226f 100644 --- a/Documentation/devicetree/bindings/ata/ahci-common.yaml +++ b/Documentation/devicetree/bindings/ata/ahci-common.yaml @@ -31,12 +31,16 @@ properties: reg-names: description: CSR space IDs + contains: + const: ahci interrupts: description: Generic AHCI state change interrupt. Can be implemented either as a single line attached to the controller or as a set of the signals indicating the particular port events. + minItems: 1 + maxItems: 32 ahci-supply: description: Power regulator for AHCI controller @@ -52,14 +56,13 @@ properties: maxItems: 1 phy-names: - maxItems: 1 + const: sata-phy ports-implemented: $ref: '/schemas/types.yaml#/definitions/uint32' description: Mask that indicates which ports the HBA supports. Useful if PI is not programmed by the BIOS, which is true for some embedded SoC's. - maximum: 0x1f patternProperties: "^sata-port@[0-9a-f]+$": @@ -80,8 +83,12 @@ $defs: properties: reg: - description: AHCI SATA port identifier - maxItems: 1 + description: + AHCI SATA port identifier. By design AHCI controller can't have + more than 32 ports due to the CAP.NP fields and PI register size + constraints. + minimum: 0 + maximum: 31 phys: description: Individual AHCI SATA port PHY @@ -89,7 +96,7 @@ $defs: phy-names: description: AHCI SATA port PHY ID - maxItems: 1 + const: sata-phy target-supply: description: Power regulator for SATA port target device From 388f08ecdc199f6fae2e94f792bffbabcba294f9 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:02 +0300 Subject: [PATCH 16/45] dt-bindings: ata: sata: Extend number of SATA ports The denoted in the description upper limit only concerns the Port Multipliers, but not the actual SATA ports. It's an external device attached to a SATA port in order to access more than one SATA-drive. So when it's attached to a SATA port it just extends the port capability while the number of actual SATA ports stays the same. For instance on AHCI controllers the number of actual ports is determined by the CAP.NP field and the PI (Ports Implemented) register. AFAICS in general the maximum number of SATA ports depends on the particular controller implementation. Generic AHCI controller can't have more than 32 ports (since CAP.NP is of 5 bits wide and PI register is 32-bits size), while DWC AHCI SATA controller can't be configured with more than 8 ports activated. So let's discard the SATA ports reg-property restrictions and just make sure that it consists of a single reg-item. Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Reviewed-by: Rob Herring Signed-off-by: Damien Le Moal --- Documentation/devicetree/bindings/ata/sata-common.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/ata/sata-common.yaml b/Documentation/devicetree/bindings/ata/sata-common.yaml index 5a31a902618d..58c9342b9925 100644 --- a/Documentation/devicetree/bindings/ata/sata-common.yaml +++ b/Documentation/devicetree/bindings/ata/sata-common.yaml @@ -49,10 +49,9 @@ $defs: properties: reg: minimum: 0 - maximum: 14 description: - The ID number of the drive port SATA can potentially use a port - multiplier making it possible to connect up to 15 disks to a single - SATA port. + The ID number of the SATA port. Aside with being directly used, + each port can have a Port Multiplier attached thus allowing to + access more than one drive by means of a single SATA port. ... From 2ea4d52ad11a9234eb7252377ba873c311896997 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:03 +0300 Subject: [PATCH 17/45] dt-bindings: ata: sata-brcm: Apply common AHCI schema The Broadcom SATA controller is obviously based on the AHCI standard. The device driver uses the kernel AHCI library to work with it. Therefore we can be have a more thorough DT-bindings evaluation by referring to the AHCI-common schema instead of using the more relaxed SATA-common one. Signed-off-by: Serge Semin Reviewed-by: Florian Fainelli Reviewed-by: Rob Herring Signed-off-by: Damien Le Moal --- Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml b/Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml index 4ee74df8e58a..fa8ebc8f243f 100644 --- a/Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml +++ b/Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml @@ -14,7 +14,7 @@ maintainers: - Florian Fainelli allOf: - - $ref: sata-common.yaml# + - $ref: ahci-common.yaml# properties: compatible: From 82d437e6dcb10e07e1a109d74924e30b9ec6ea56 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:04 +0300 Subject: [PATCH 18/45] ata: libahci_platform: Convert to using platform devm-ioremap methods Currently the IOMEM AHCI registers space is mapped by means of the two functions invocation: platform_get_resource() is used to get the very first memory resource and devm_ioremap_resource() is called to remap that resource. Device-managed kernel API provides a handy wrapper to perform the same in single function call: devm_platform_ioremap_resource(). While at it seeing many AHCI platform drivers rely on having the AHCI CSR space marked with "ahci" name let's first try to find and remap the CSR IO-mem with that name and only if it fails fallback to getting the very first registers space platform resource. Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Signed-off-by: Damien Le Moal --- drivers/ata/libahci_platform.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 32495ae96567..1e9e825d6cc5 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -402,8 +402,14 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, devres_add(dev, hpriv); - hpriv->mmio = devm_ioremap_resource(dev, - platform_get_resource(pdev, IORESOURCE_MEM, 0)); + /* + * If the DT provided an "ahci" named resource, use it. Otherwise, + * fallback to using the default first resource for the device node. + */ + if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "ahci")) + hpriv->mmio = devm_platform_ioremap_resource_byname(pdev, "ahci"); + else + hpriv->mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hpriv->mmio)) { rc = PTR_ERR(hpriv->mmio); goto err_out; From e28b3abf8020a884bd3b7758ea8915365af8fadf Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:05 +0300 Subject: [PATCH 19/45] ata: libahci_platform: Convert to using devm bulk clocks API In order to simplify the clock-related code there is a way to convert the current fixed clocks array into using the common bulk clocks kernel API with dynamic set of the clock handlers and device-managed clock-resource tracking. It's a bit tricky due to the complication coming from the requirement to support the platforms (da850, spear13xx) with the non-OF-based clock source, but still doable. Before this modification there are two methods have been used to get the clocks connected to an AHCI device: clk_get() - to get the very first clock in the list and of_clk_get() - to get the rest of them. Basically the platforms with non-OF-based clocks definition could specify only a single reference clock source. The platforms with OF-hw clocks have been luckier and could setup up to AHCI_MAX_CLKS clocks. Such semantic can be retained with using devm_clk_bulk_get_all() to retrieve the clocks defined via the DT firmware and devm_clk_get_optional() otherwise. In both cases using the device-managed version of the methods will cause the automatic resources deallocation on the AHCI device removal event. The only complicated part in the suggested approach is the explicit allocation and initialization of the clk_bulk_data structure instance for the non-OF reference clocks. It's required in order to use the Bulk Clocks API for the both denoted cases of the clocks definition. Note aside with the clock-related code reduction and natural simplification, there are several bonuses the suggested modification provides. First of all the limitation of having no greater than AHCI_MAX_CLKS clocks is now removed, since the devm_clk_bulk_get_all() method will allocate as many reference clocks data descriptors as there are clocks specified for the device. Secondly the clock names are auto-detected. So the LLDD (glue) drivers can make sure that the required clocks are specified just by checking the clock IDs in the clk_bulk_data array. Thirdly using the handy Bulk Clocks kernel API improves the clocks-handling code readability. And the last but not least this modification implements a true optional clocks support to the ahci_platform_get_resources() method. Indeed the previous clocks getting procedure just stopped getting the clocks on any errors (aside from non-critical -EPROBE_DEFER) in a way so the callee wasn't even informed about abnormal loop termination. The new implementation lacks of such problem. The ahci_platform_get_resources() will return an error code if the corresponding clocks getting method ends execution abnormally. Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Signed-off-by: Damien Le Moal --- drivers/ata/ahci.h | 4 +- drivers/ata/ahci_da850.c | 45 +++++++----------- drivers/ata/ahci_dm816.c | 4 +- drivers/ata/libahci_platform.c | 85 ++++++++++++++++------------------ 4 files changed, 61 insertions(+), 77 deletions(-) diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index ad11a4c52fbe..c3770a19781b 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -38,7 +38,6 @@ enum { AHCI_MAX_PORTS = 32, - AHCI_MAX_CLKS = 5, AHCI_MAX_SG = 168, /* hardware max is 64K */ AHCI_DMA_BOUNDARY = 0xffffffff, AHCI_MAX_CMDS = 32, @@ -339,7 +338,8 @@ struct ahci_host_priv { u32 em_msg_type; /* EM message type */ u32 remapped_nvme; /* NVMe remapped device count */ bool got_runtime_pm; /* Did we do pm_runtime_get? */ - struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + unsigned int n_clks; + struct clk_bulk_data *clks; /* Optional */ struct reset_control *rsts; /* Optional */ struct regulator **target_pwrs; /* Optional */ struct regulator *ahci_regulator;/* Optional */ diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c index 052c28e250aa..dc8a019b8340 100644 --- a/drivers/ata/ahci_da850.c +++ b/drivers/ata/ahci_da850.c @@ -163,7 +163,6 @@ static int ahci_da850_probe(struct platform_device *pdev) struct ahci_host_priv *hpriv; void __iomem *pwrdn_reg; struct resource *res; - struct clk *clk; u32 mpy; int rc; @@ -172,36 +171,28 @@ static int ahci_da850_probe(struct platform_device *pdev) return PTR_ERR(hpriv); /* - * Internally ahci_platform_get_resources() calls clk_get(dev, NULL) - * when trying to obtain the functional clock. This SATA controller - * uses two clocks for which we specify two connection ids. If we don't - * have the functional clock at this point - call clk_get() again with - * con_id = "fck". + * Internally ahci_platform_get_resources() calls the bulk clocks + * get method or falls back to using a single clk_get_optional(). + * This AHCI SATA controller uses two clocks: functional clock + * with "fck" connection id and external reference clock with + * "refclk" id. If we haven't got all of them re-try the clocks + * getting procedure with the explicitly specified ids. */ - if (!hpriv->clks[0]) { - clk = clk_get(dev, "fck"); - if (IS_ERR(clk)) - return PTR_ERR(clk); + if (hpriv->n_clks < 2) { + hpriv->clks = devm_kcalloc(dev, 2, sizeof(*hpriv->clks), GFP_KERNEL); + if (!hpriv->clks) + return -ENOMEM; - hpriv->clks[0] = clk; + hpriv->clks[0].id = "fck"; + hpriv->clks[1].id = "refclk"; + hpriv->n_clks = 2; + + rc = devm_clk_bulk_get(dev, hpriv->n_clks, hpriv->clks); + if (rc) + return rc; } - /* - * The second clock used by ahci-da850 is the external REFCLK. If we - * didn't get it from ahci_platform_get_resources(), let's try to - * specify the con_id in clk_get(). - */ - if (!hpriv->clks[1]) { - clk = clk_get(dev, "refclk"); - if (IS_ERR(clk)) { - dev_err(dev, "unable to obtain the reference clock"); - return -ENODEV; - } - - hpriv->clks[1] = clk; - } - - mpy = ahci_da850_calculate_mpy(clk_get_rate(hpriv->clks[1])); + mpy = ahci_da850_calculate_mpy(clk_get_rate(hpriv->clks[1].clk)); if (mpy == 0) { dev_err(dev, "invalid REFCLK multiplier value: 0x%x", mpy); return -EINVAL; diff --git a/drivers/ata/ahci_dm816.c b/drivers/ata/ahci_dm816.c index 8a92112dcd59..d26efcd20f64 100644 --- a/drivers/ata/ahci_dm816.c +++ b/drivers/ata/ahci_dm816.c @@ -69,12 +69,12 @@ static int ahci_dm816_phy_init(struct ahci_host_priv *hpriv, struct device *dev) * keep-alive clock and the external reference clock. We need the * rate of the latter to calculate the correct value of MPY bits. */ - if (!hpriv->clks[1]) { + if (hpriv->n_clks < 2) { dev_err(dev, "reference clock not supplied\n"); return -EINVAL; } - refclk_rate = clk_get_rate(hpriv->clks[1]); + refclk_rate = clk_get_rate(hpriv->clks[1].clk); if ((refclk_rate % 100) != 0) { dev_err(dev, "reference clock rate must be divisible by 100\n"); return -EINVAL; diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 1e9e825d6cc5..7366eb0adf41 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -97,28 +97,14 @@ EXPORT_SYMBOL_GPL(ahci_platform_disable_phys); * ahci_platform_enable_clks - Enable platform clocks * @hpriv: host private area to store config values * - * This function enables all the clks found in hpriv->clks, starting at - * index 0. If any clk fails to enable it disables all the clks already - * enabled in reverse order, and then returns an error. + * This function enables all the clks found for the AHCI device. * * RETURNS: * 0 on success otherwise a negative error code */ int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) { - int c, rc; - - for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { - rc = clk_prepare_enable(hpriv->clks[c]); - if (rc) - goto disable_unprepare_clk; - } - return 0; - -disable_unprepare_clk: - while (--c >= 0) - clk_disable_unprepare(hpriv->clks[c]); - return rc; + return clk_bulk_prepare_enable(hpriv->n_clks, hpriv->clks); } EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); @@ -126,16 +112,13 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); * ahci_platform_disable_clks - Disable platform clocks * @hpriv: host private area to store config values * - * This function disables all the clks found in hpriv->clks, in reverse - * order of ahci_platform_enable_clks (starting at the end of the array). + * This function disables all the clocks enabled before + * (bulk-clocks-disable function is supposed to do that in reverse + * from the enabling procedure order). */ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) { - int c; - - for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) - if (hpriv->clks[c]) - clk_disable_unprepare(hpriv->clks[c]); + clk_bulk_disable_unprepare(hpriv->n_clks, hpriv->clks); } EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); @@ -292,8 +275,6 @@ static void ahci_platform_put_resources(struct device *dev, void *res) pm_runtime_disable(dev); } - for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) - clk_put(hpriv->clks[c]); /* * The regulators are tied to child node device and not to the * SATA device itself. So we can't use devm for automatically @@ -374,8 +355,8 @@ static int ahci_platform_get_regulator(struct ahci_host_priv *hpriv, u32 port, * 1) mmio registers (IORESOURCE_MEM 0, mandatory) * 2) regulator for controlling the targets power (optional) * regulator for controlling the AHCI controller (optional) - * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, - * or for non devicetree enabled platforms a single clock + * 3) all clocks specified in the devicetree node, or a single + * clock for non-OF platforms (optional) * 4) resets, if flags has AHCI_PLATFORM_GET_RESETS (optional) * 5) phys (optional) * @@ -385,11 +366,10 @@ static int ahci_platform_get_regulator(struct ahci_host_priv *hpriv, u32 port, struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, unsigned int flags) { + int child_nodes, rc = -ENOMEM, enabled_ports = 0; struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; - struct clk *clk; struct device_node *child; - int i, enabled_ports = 0, rc = -ENOMEM, child_nodes; u32 mask_port_map = 0; if (!devres_open_group(dev, NULL, GFP_KERNEL)) @@ -415,25 +395,38 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, goto err_out; } - for (i = 0; i < AHCI_MAX_CLKS; i++) { - /* - * For now we must use clk_get(dev, NULL) for the first clock, - * because some platforms (da850, spear13xx) are not yet - * converted to use devicetree for clocks. For new platforms - * this is equivalent to of_clk_get(dev->of_node, 0). - */ - if (i == 0) - clk = clk_get(dev, NULL); - else - clk = of_clk_get(dev->of_node, i); + /* + * Bulk clocks getting procedure can fail to find any clock due to + * running on a non-OF platform or due to the clocks being defined in + * bypass of the DT firmware (like da850, spear13xx). In that case we + * fallback to getting a single clock source right from the dev clocks + * list. + */ + rc = devm_clk_bulk_get_all(dev, &hpriv->clks); + if (rc < 0) + goto err_out; - if (IS_ERR(clk)) { - rc = PTR_ERR(clk); - if (rc == -EPROBE_DEFER) - goto err_out; - break; + if (rc > 0) { + /* Got clocks in bulk */ + hpriv->n_clks = rc; + } else { + /* + * No clock bulk found: fallback to manually getting + * the optional clock. + */ + hpriv->clks = devm_kzalloc(dev, sizeof(*hpriv->clks), GFP_KERNEL); + if (!hpriv->clks) { + rc = -ENOMEM; + goto err_out; + } + hpriv->clks->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(hpriv->clks->clk)) { + rc = PTR_ERR(hpriv->clks->clk); + goto err_out; + } else if (hpriv->clks->clk) { + hpriv->clks->id = "ahci"; + hpriv->n_clks = 1; } - hpriv->clks[i] = clk; } hpriv->ahci_regulator = devm_regulator_get(dev, "ahci"); From 3c132ea6508b34956e5ed88d04936983ec230601 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:06 +0300 Subject: [PATCH 20/45] ata: libahci_platform: Sanity check the DT child nodes number Having greater than AHCI_MAX_PORTS (32) ports detected isn't that critical from the further AHCI-platform initialization point of view since exceeding the ports upper limit will cause allocating more resources than will be used afterwards. But detecting too many child DT-nodes doesn't seem right since it's very unlikely to have it on an ordinary platform. In accordance with the AHCI specification there can't be more than 32 ports implemented at least due to having the CAP.NP field of 5 bits wide and the PI register of dword size. Thus if such situation is found the DTB must have been corrupted and the data read from it shouldn't be reliable. Let's consider that as an erroneous situation and halt further resources allocation. Note it's logically more correct to have the nports set only after the initialization value is checked for being sane. So while at it let's make sure nports is assigned with a correct value. Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Signed-off-by: Damien Le Moal --- drivers/ata/libahci_platform.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 7366eb0adf41..bacb974c1b16 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -450,14 +450,24 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, } } - hpriv->nports = child_nodes = of_get_child_count(dev->of_node); + /* + * Too many sub-nodes most likely means having something wrong with + * the firmware. + */ + child_nodes = of_get_child_count(dev->of_node); + if (child_nodes > AHCI_MAX_PORTS) { + rc = -EINVAL; + goto err_out; + } /* * If no sub-node was found, we still need to set nports to * one in order to be able to use the * ahci_platform_[en|dis]able_[phys|regulators] functions. */ - if (!child_nodes) + if (child_nodes) + hpriv->nports = child_nodes; + else hpriv->nports = 1; hpriv->phys = devm_kcalloc(dev, hpriv->nports, sizeof(*hpriv->phys), GFP_KERNEL); From 3f74cd046fbed349be977606f938e6429155e7b5 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:07 +0300 Subject: [PATCH 21/45] ata: libahci_platform: Parse ports-implemented property in resources getter The ports-implemented property is mainly used on the OF-based platforms with no ports mapping initialized by a bootloader/BIOS firmware. Seeing the same of_property_read_u32()-based pattern has already been implemented in the generic AHCI LLDD (glue) driver and in the Mediatek, St AHCI drivers let's move the property read procedure to the generic ahci_platform_get_resources() method. Thus we'll have the forced ports mapping feature supported for each OF-based platform which requires that, and stop re-implementing the same pattern in there a bit simplifying the code. Signed-off-by: Serge Semin Signed-off-by: Damien Le Moal --- drivers/ata/ahci_mtk.c | 2 -- drivers/ata/ahci_platform.c | 3 --- drivers/ata/ahci_st.c | 3 --- drivers/ata/libahci_platform.c | 3 +++ 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/ata/ahci_mtk.c b/drivers/ata/ahci_mtk.c index 1f6c85fde983..c056378e3e72 100644 --- a/drivers/ata/ahci_mtk.c +++ b/drivers/ata/ahci_mtk.c @@ -118,8 +118,6 @@ static int mtk_ahci_parse_property(struct ahci_host_priv *hpriv, SYS_CFG_SATA_EN); } - of_property_read_u32(np, "ports-implemented", &hpriv->force_port_map); - return 0; } diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 28a8de5b48b9..9b56490ecbc3 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -56,9 +56,6 @@ static int ahci_probe(struct platform_device *pdev) if (rc) return rc; - of_property_read_u32(dev->of_node, - "ports-implemented", &hpriv->force_port_map); - if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci")) hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ; diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index 7526653c843b..068621099c00 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -168,9 +168,6 @@ static int st_ahci_probe(struct platform_device *pdev) st_ahci_configure_oob(hpriv->mmio); - of_property_read_u32(dev->of_node, - "ports-implemented", &hpriv->force_port_map); - err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info, &ahci_platform_sht); if (err) { diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index bacb974c1b16..085f99b2eb5a 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -485,6 +485,9 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, goto err_out; } + of_property_read_u32(dev->of_node, + "ports-implemented", &hpriv->force_port_map); + if (child_nodes) { for_each_child_of_node(dev->of_node, child) { u32 port; From f67f12ff57bcfcd7d64280f748787793217faeaf Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:08 +0300 Subject: [PATCH 22/45] ata: libahci_platform: Introduce reset assertion/deassertion methods Currently the ACHI-platform library supports only the assert and deassert reset signals and ignores the platforms with self-deasserting reset lines. That prone to having the platforms with self-deasserting reset method misbehaviour when it comes to resuming from sleep state after the clocks have been fully disabled. For such cases the controller needs to be fully reset all over after the reference clocks are enabled and stable, otherwise the controller state machine might be in an undetermined state. The best solution would be to auto-detect which reset method is supported by the particular platform and use it implicitly in the framework of the ahci_platform_enable_resources()/ahci_platform_disable_resources() methods. Alas it can't be implemented due to the AHCI-platform library already supporting the shared reset control lines. As [1] says in such case we have to use only one of the next methods: + reset_control_assert()/reset_control_deassert(); + reset_control_reset()/reset_control_rearm(). If the driver had an exclusive control over the reset lines we could have been able to manipulate the lines with no much limitation and just used the combination of the methods above to cover all the possible reset-control cases. Since the shared reset control has already been advertised and couldn't be changed with no risk to breaking the platforms relying on it, we have no choice but to make the platform drivers to determine which reset methods the platform reset system supports. In order to implement both types of reset control support we suggest to introduce the new AHCI-platform flag: AHCI_PLATFORM_RST_TRIGGER, which when passed to the ahci_platform_get_resources() method together with the AHCI_PLATFORM_GET_RESETS flag will indicate that the reset lines are self-deasserting thus the reset_control_reset()/reset_control_rearm() will be used to control the reset state. Otherwise the reset_control_deassert()/reset_control_assert() methods will be utilized. [1] Documentation/driver-api/reset.rst Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Signed-off-by: Damien Le Moal --- drivers/ata/ahci.h | 1 + drivers/ata/libahci_platform.c | 50 ++++++++++++++++++++++++++++++---- include/linux/ahci_platform.h | 5 +++- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index c3770a19781b..7d834deefeb9 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -340,6 +340,7 @@ struct ahci_host_priv { bool got_runtime_pm; /* Did we do pm_runtime_get? */ unsigned int n_clks; struct clk_bulk_data *clks; /* Optional */ + unsigned int f_rsts; struct reset_control *rsts; /* Optional */ struct regulator **target_pwrs; /* Optional */ struct regulator *ahci_regulator;/* Optional */ diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 085f99b2eb5a..31be8a10facd 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -122,6 +122,44 @@ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) } EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); +/** + * ahci_platform_deassert_rsts - Deassert/trigger platform resets + * @hpriv: host private area to store config values + * + * This function deasserts or triggers all the reset lines found for + * the AHCI device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_deassert_rsts(struct ahci_host_priv *hpriv) +{ + if (hpriv->f_rsts & AHCI_PLATFORM_RST_TRIGGER) + return reset_control_reset(hpriv->rsts); + + return reset_control_deassert(hpriv->rsts); +} +EXPORT_SYMBOL_GPL(ahci_platform_deassert_rsts); + +/** + * ahci_platform_assert_rsts - Assert/rearm platform resets + * @hpriv: host private area to store config values + * + * This function asserts or rearms (for self-deasserting resets) all + * the reset controls found for the AHCI device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_assert_rsts(struct ahci_host_priv *hpriv) +{ + if (hpriv->f_rsts & AHCI_PLATFORM_RST_TRIGGER) + return reset_control_rearm(hpriv->rsts); + + return reset_control_assert(hpriv->rsts); +} +EXPORT_SYMBOL_GPL(ahci_platform_assert_rsts); + /** * ahci_platform_enable_regulators - Enable regulators * @hpriv: host private area to store config values @@ -219,18 +257,18 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) if (rc) goto disable_regulator; - rc = reset_control_deassert(hpriv->rsts); + rc = ahci_platform_deassert_rsts(hpriv); if (rc) goto disable_clks; rc = ahci_platform_enable_phys(hpriv); if (rc) - goto disable_resets; + goto disable_rsts; return 0; -disable_resets: - reset_control_assert(hpriv->rsts); +disable_rsts: + ahci_platform_assert_rsts(hpriv); disable_clks: ahci_platform_disable_clks(hpriv); @@ -257,7 +295,7 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) { ahci_platform_disable_phys(hpriv); - reset_control_assert(hpriv->rsts); + ahci_platform_assert_rsts(hpriv); ahci_platform_disable_clks(hpriv); @@ -448,6 +486,8 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, rc = PTR_ERR(hpriv->rsts); goto err_out; } + + hpriv->f_rsts = flags & AHCI_PLATFORM_RST_TRIGGER; } /* diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index 49e5383d4222..6d7dd472d370 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h @@ -23,6 +23,8 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv); void ahci_platform_disable_phys(struct ahci_host_priv *hpriv); int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); +int ahci_platform_deassert_rsts(struct ahci_host_priv *hpriv); +int ahci_platform_assert_rsts(struct ahci_host_priv *hpriv); int ahci_platform_enable_regulators(struct ahci_host_priv *hpriv); void ahci_platform_disable_regulators(struct ahci_host_priv *hpriv); int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); @@ -41,6 +43,7 @@ int ahci_platform_resume_host(struct device *dev); int ahci_platform_suspend(struct device *dev); int ahci_platform_resume(struct device *dev); -#define AHCI_PLATFORM_GET_RESETS 0x01 +#define AHCI_PLATFORM_GET_RESETS BIT(0) +#define AHCI_PLATFORM_RST_TRIGGER BIT(1) #endif /* _AHCI_PLATFORM_H */ From 03f1076fbe9fd0edd92011f1c97bf6daad83d01b Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:09 +0300 Subject: [PATCH 23/45] dt-bindings: ata: ahci: Add platform capability properties In case if the platform doesn't have BIOS or a comprehensive firmware installed then the HBA capability flags will be left uninitialized. As a good alternative we suggest to define the DT-properties with the AHCI platform capabilities describing all the HW-init flags of the corresponding capability register. Luckily there aren't too many of them. SSS - Staggered Spin-up support and MPS - Mechanical Presence Switch support determine the corresponding feature availability for the whole HBA by means of the "hba-cap" property. Each port can have the "hba-port-cap" property initialized indicating that the port supports some of the next functionalities: HPCP - HotPlug capable port, MPSP - Mechanical Presence Switch attached to a port, CPD - Cold Plug detection, ESP - External SATA Port (eSATA), FBSCP - FIS-based switching capable port. Signed-off-by: Serge Semin Reviewed-by: Rob Herring Signed-off-by: Damien Le Moal --- .../devicetree/bindings/ata/ahci-common.yaml | 16 +++++++++++++++ .../bindings/ata/ahci-platform.yaml | 10 ++++++++++ include/dt-bindings/ata/ahci.h | 20 +++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 include/dt-bindings/ata/ahci.h diff --git a/Documentation/devicetree/bindings/ata/ahci-common.yaml b/Documentation/devicetree/bindings/ata/ahci-common.yaml index 12a97b56226f..94d72aeaad0f 100644 --- a/Documentation/devicetree/bindings/ata/ahci-common.yaml +++ b/Documentation/devicetree/bindings/ata/ahci-common.yaml @@ -58,6 +58,14 @@ properties: phy-names: const: sata-phy + hba-cap: + $ref: '/schemas/types.yaml#/definitions/uint32' + description: + Bitfield of the HBA generic platform capabilities like Staggered + Spin-up or Mechanical Presence Switch support. It can be used to + appropriately initialize the HWinit fields of the HBA CAP register + in case if the system firmware hasn't done it. + ports-implemented: $ref: '/schemas/types.yaml#/definitions/uint32' description: @@ -101,6 +109,14 @@ $defs: target-supply: description: Power regulator for SATA port target device + hba-port-cap: + $ref: '/schemas/types.yaml#/definitions/uint32' + description: + Bitfield of the HBA port-specific platform capabilities like Hot + plugging, eSATA, FIS-based Switching, etc (see AHCI specification + for details). It can be used to initialize the HWinit fields of + the PxCMD register in case if the system firmware hasn't done it. + required: - reg diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.yaml b/Documentation/devicetree/bindings/ata/ahci-platform.yaml index 15be98e0385b..e19cf9828e68 100644 --- a/Documentation/devicetree/bindings/ata/ahci-platform.yaml +++ b/Documentation/devicetree/bindings/ata/ahci-platform.yaml @@ -111,6 +111,8 @@ examples: - | #include #include + #include + sata@f7e90000 { compatible = "marvell,berlin2q-ahci", "generic-ahci"; reg = <0xf7e90000 0x1000>; @@ -119,15 +121,23 @@ examples: #address-cells = <1>; #size-cells = <0>; + hba-cap = ; + sata0: sata-port@0 { reg = <0>; + phys = <&sata_phy 0>; target-supply = <®_sata0>; + + hba-port-cap = <(HBA_PORT_FBSCP | HBA_PORT_ESP)>; }; sata1: sata-port@1 { reg = <1>; + phys = <&sata_phy 1>; target-supply = <®_sata1>; + + hba-port-cap = <(HBA_PORT_HPCP | HBA_PORT_MPSP | HBA_PORT_FBSCP)>; }; }; diff --git a/include/dt-bindings/ata/ahci.h b/include/dt-bindings/ata/ahci.h new file mode 100644 index 000000000000..77997b35612c --- /dev/null +++ b/include/dt-bindings/ata/ahci.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause */ +/* + * This header provides constants for most AHCI bindings. + */ + +#ifndef _DT_BINDINGS_ATA_AHCI_H +#define _DT_BINDINGS_ATA_AHCI_H + +/* Host Bus Adapter generic platform capabilities */ +#define HBA_SSS (1 << 27) +#define HBA_SMPS (1 << 28) + +/* Host Bus Adapter port-specific platform capabilities */ +#define HBA_PORT_HPCP (1 << 18) +#define HBA_PORT_MPSP (1 << 19) +#define HBA_PORT_CPD (1 << 20) +#define HBA_PORT_ESP (1 << 21) +#define HBA_PORT_FBSCP (1 << 22) + +#endif From eb7cae0b6afda3932d3011285a246b3c6bf26c44 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:10 +0300 Subject: [PATCH 24/45] ata: libahci: Extend port-cmd flags set with port capabilities Currently not all of the Port-specific capabilities listed in the PORT_CMD-enumeration. Let's extend that set with the Cold Presence Detection and Mechanical Presence Switch attached to the Port flags [1] so to closeup the set of the platform-specific port-capabilities flags. Note these flags are supposed to be set by the platform firmware if there is one. Alternatively as we are about to do they can be set by means of the OF properties. While at it replace PORT_IRQ_DEV_ILCK with PORT_IRQ_DMPS and fix the comment there. In accordance with [2] that IRQ flag is supposed to indicate the state of the signal coming from the Mechanical Presence Switch. [1] Serial ATA AHCI 1.3.1 Specification, p.27 [2] Serial ATA AHCI 1.3.1 Specification, p.24, p.88 Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Signed-off-by: Damien Le Moal --- drivers/ata/ahci.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 7d834deefeb9..27cab4e909a5 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -138,7 +138,7 @@ enum { PORT_IRQ_BAD_PMP = (1 << 23), /* incorrect port multiplier */ PORT_IRQ_PHYRDY = (1 << 22), /* PhyRdy changed */ - PORT_IRQ_DEV_ILCK = (1 << 7), /* device interlock */ + PORT_IRQ_DMPS = (1 << 7), /* mechanical presence status */ PORT_IRQ_CONNECT = (1 << 6), /* port connect change status */ PORT_IRQ_SG_DONE = (1 << 5), /* descriptor processed */ PORT_IRQ_UNK_FIS = (1 << 4), /* unknown FIS rx'd */ @@ -166,6 +166,8 @@ enum { PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */ PORT_CMD_FBSCP = (1 << 22), /* FBS Capable Port */ PORT_CMD_ESP = (1 << 21), /* External Sata Port */ + PORT_CMD_CPD = (1 << 20), /* Cold Presence Detection */ + PORT_CMD_MPSP = (1 << 19), /* Mechanical Presence Switch */ PORT_CMD_HPCP = (1 << 18), /* HotPlug Capable Port */ PORT_CMD_PMP = (1 << 17), /* PMP attached */ PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */ @@ -181,6 +183,10 @@ enum { PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ + /* PORT_CMD capabilities mask */ + PORT_CMD_CAP = PORT_CMD_HPCP | PORT_CMD_MPSP | + PORT_CMD_CPD | PORT_CMD_ESP | PORT_CMD_FBSCP, + /* PORT_FBS bits */ PORT_FBS_DWE_OFFSET = 16, /* FBS device with error offset */ PORT_FBS_ADO_OFFSET = 12, /* FBS active dev optimization offset */ From 88589772e80cec5dc2058d7d84a1a97a31674195 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:11 +0300 Subject: [PATCH 25/45] ata: libahci: Discard redundant force_port_map parameter Currently there are four port-map-related fields declared in the ahci_host_priv structure and used to setup the HBA ports mapping. First the ports-mapping is read from the PI register and immediately stored in the saved_port_map field. If forced_port_map is initialized with non-zero value then its value will have greater priority over the value read from PI, thus it will override the saved_port_map field. That value will be then masked by a non-zero mask_port_map field and after some sanity checks it will be stored in the ahci_host_priv.port_map field as a final port mapping. As you can see the logic is a bit too complicated for such a simple task. We can freely get rid from at least one of the fields with no change to the implemented semantic. The force_port_map field can be replaced with taking non-zero saved_port_map value into account. So if saved_port_map is pre-initialized by the low level drivers (platform drivers) then it will have greater priority over the value read from PI register and will be used as actual HBA ports mapping later on. Thus the ports map forcing task will be just transferred from force_port_map to the saved_port_map field. This modification will perfectly fit into the feature of having OF-based initialization of the HW-init HBA CSR fields we are about to introduce in the next commit. Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Signed-off-by: Damien Le Moal --- drivers/ata/ahci.c | 2 +- drivers/ata/ahci.h | 1 - drivers/ata/libahci.c | 10 ++++++---- drivers/ata/libahci_platform.c | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 2371ab5fe8f2..35f6d12889ff 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -657,7 +657,7 @@ static void ahci_pci_save_initial_config(struct pci_dev *pdev, { if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361) { dev_info(&pdev->dev, "JMB361 has only one port\n"); - hpriv->force_port_map = 1; + hpriv->saved_port_map = 1; } /* diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 27cab4e909a5..cc4f40e6c924 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -328,7 +328,6 @@ struct ahci_port_priv { struct ahci_host_priv { /* Input fields */ unsigned int flags; /* AHCI_HFLAG_* */ - u32 force_port_map; /* force port map */ u32 mask_port_map; /* mask out particular bits */ void __iomem * mmio; /* bus-independent mem map */ diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index cf8c7fd59ada..000a7072614f 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -453,7 +453,6 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) * reset. Values without are used for driver operation. */ hpriv->saved_cap = cap = readl(mmio + HOST_CAP); - hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL); /* CAP2 register is only defined for AHCI 1.2 and later */ vers = readl(mmio + HOST_VERSION); @@ -517,10 +516,13 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) cap &= ~HOST_CAP_SXS; } - if (hpriv->force_port_map && port_map != hpriv->force_port_map) { + /* Override the HBA ports mapping if the platform needs it */ + port_map = readl(mmio + HOST_PORTS_IMPL); + if (hpriv->saved_port_map && port_map != hpriv->saved_port_map) { dev_info(dev, "forcing port_map 0x%x -> 0x%x\n", - port_map, hpriv->force_port_map); - port_map = hpriv->force_port_map; + port_map, hpriv->saved_port_map); + port_map = hpriv->saved_port_map; + } else { hpriv->saved_port_map = port_map; } diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 31be8a10facd..01c195b6d9e6 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -526,7 +526,7 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, } of_property_read_u32(dev->of_node, - "ports-implemented", &hpriv->force_port_map); + "ports-implemented", &hpriv->saved_port_map); if (child_nodes) { for_each_child_of_node(dev->of_node, child) { From fad64dc06579ac1cc05d5ab73bdaa62ee6435ed8 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:12 +0300 Subject: [PATCH 26/45] ata: libahci: Don't read AHCI version twice in the save-config method There is no point in reading the AHCI version all over in the tail of the ahci_save_initial_config() method. That register is RO and doesn't change its value even after reset. So just reuse the data, which has already been read from there earlier in the head of the function. Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Signed-off-by: Damien Le Moal --- drivers/ata/libahci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 000a7072614f..1ffaa5f5f21a 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -564,7 +564,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) /* record values to use during operation */ hpriv->cap = cap; hpriv->cap2 = cap2; - hpriv->version = readl(mmio + HOST_VERSION); + hpriv->version = vers; hpriv->port_map = port_map; if (!hpriv->start_engine) From 7cbbfbe01a7284f8003f7a013abb9ece01cb6393 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:13 +0300 Subject: [PATCH 27/45] ata: ahci: Convert __ahci_port_base to accepting hpriv as arguments The port base address may be required even before the ata_host instance is initialized and activated, for instance in the ahci_save_initial_config() method which we are about to update (consider this modification as a preparation for that one). Seeing the __ahci_port_base() function isn't used much it's the best candidate to provide the required functionality. So let's convert it to accepting the ahci_host_priv structure pointer. Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Signed-off-by: Damien Le Moal --- drivers/ata/ahci.c | 2 +- drivers/ata/ahci.h | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 35f6d12889ff..639de2d75d63 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -690,7 +690,7 @@ static void ahci_pci_init_controller(struct ata_host *host) mv = 2; else mv = 4; - port_mmio = __ahci_port_base(host, mv); + port_mmio = __ahci_port_base(hpriv, mv); writel(0, port_mmio + PORT_IRQ_MASK); diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index cc4f40e6c924..5d9db5e7476c 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -432,10 +432,9 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht); void ahci_error_handler(struct ata_port *ap); u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked); -static inline void __iomem *__ahci_port_base(struct ata_host *host, +static inline void __iomem *__ahci_port_base(struct ahci_host_priv *hpriv, unsigned int port_no) { - struct ahci_host_priv *hpriv = host->private_data; void __iomem *mmio = hpriv->mmio; return mmio + 0x100 + (port_no * 0x80); @@ -443,7 +442,9 @@ static inline void __iomem *__ahci_port_base(struct ata_host *host, static inline void __iomem *ahci_port_base(struct ata_port *ap) { - return __ahci_port_base(ap->host, ap->port_no); + struct ahci_host_priv *hpriv = ap->host->private_data; + + return __ahci_port_base(hpriv, ap->port_no); } static inline int ahci_nr_ports(u32 cap) From 18ee7c49f75b642beb6f8d7efdae3a47068d4aa3 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:14 +0300 Subject: [PATCH 28/45] ata: ahci: Introduce firmware-specific caps initialization There are systems with no BIOS or comprehensive embedded firmware which could be able to properly initialize the SATA AHCI controller platform-specific capabilities. In that case a good alternative to having a clever bootloader is to create a device tree node with the properties well describing all the AHCI-related platform specifics. All the settings which are normally detected and marked as available in the HBA and its ports capabilities fields [1] could be defined in the platform DTB by means of a set of the dedicated properties. Such approach perfectly fits to the DTB-philosophy - to provide hardware/platform description. So here we suggest to extend the SATA AHCI device tree bindings with two additional DT-properties: 1) "hba-cap" - HBA platform generic capabilities like: - SSS - Staggered Spin-up support. - SMPS - Mechanical Presence Switch support. 2) "hba-port-cap" - HBA platform port capabilities like: - HPCP - Hot Plug Capable Port. - MPSP - Mechanical Presence Switch Attached to Port. - CPD - Cold Presence Detection. - ESP - External SATA Port. - FBSCP - FIS-based Switching Capable Port. All of these capabilities require to have a corresponding hardware configuration. Thus it's ok to have them defined in DTB. Even though the driver currently takes into account the state of the ESP and FBSCP flags state only, there is nothing wrong with having all of them supported by the generic AHCI library in order to have a complete OF-based platform-capabilities initialization procedure. These properties will be parsed in the ahci_platform_get_resources() method and their values will be stored in the saved_* fields of the ahci_host_priv structure, which in its turn then will be used to restore the H.CAP, H.PI and P#.CMD capability fields on device init and after HBA reset. Please note this modification concerns the HW-init HBA and its ports flags only, which are by specification [1] are supposed to be initialized by the BIOS/platform firmware/expansion ROM and which are normally declared in the one-time-writable-after-reset register fields. Even though these flags aren't supposed to be cleared after HBA reset some AHCI instances may violate that rule so we still need to perform the fields resetting after each reset. Luckily the corresponding functionality has already been partly implemented in the framework of the ahci_save_initial_config() and ahci_restore_initial_config() methods. [1] Serial ATA AHCI 1.3.1 Specification, p. 103 Signed-off-by: Serge Semin Signed-off-by: Damien Le Moal --- drivers/ata/ahci.h | 1 + drivers/ata/libahci.c | 51 ++++++++++++++++++++++++++++------ drivers/ata/libahci_platform.c | 40 ++++++++++++++++++++++++-- 3 files changed, 81 insertions(+), 11 deletions(-) diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 5d9db5e7476c..da7ee8bec165 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -338,6 +338,7 @@ struct ahci_host_priv { u32 saved_cap; /* saved initial cap */ u32 saved_cap2; /* saved initial cap2 */ u32 saved_port_map; /* saved initial port_map */ + u32 saved_port_cap[AHCI_MAX_PORTS]; /* saved port_cap */ u32 em_loc; /* enclosure management location */ u32 em_buf_sz; /* EM buffer size in byte */ u32 em_msg_type; /* EM message type */ diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 1ffaa5f5f21a..954386a2b500 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -16,6 +16,7 @@ * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf */ +#include #include #include #include @@ -443,16 +444,28 @@ static ssize_t ahci_show_em_supported(struct device *dev, void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) { void __iomem *mmio = hpriv->mmio; - u32 cap, cap2, vers, port_map; + void __iomem *port_mmio; + unsigned long port_map; + u32 cap, cap2, vers; int i; /* make sure AHCI mode is enabled before accessing CAP */ ahci_enable_ahci(mmio); - /* Values prefixed with saved_ are written back to host after - * reset. Values without are used for driver operation. + /* + * Values prefixed with saved_ are written back to the HBA and ports + * registers after reset. Values without are used for driver operation. */ - hpriv->saved_cap = cap = readl(mmio + HOST_CAP); + + /* + * Override HW-init HBA capability fields with the platform-specific + * values. The rest of the HBA capabilities are defined as Read-only + * and can't be modified in CSR anyway. + */ + cap = readl(mmio + HOST_CAP); + if (hpriv->saved_cap) + cap = (cap & ~(HOST_CAP_SSS | HOST_CAP_MPS)) | hpriv->saved_cap; + hpriv->saved_cap = cap; /* CAP2 register is only defined for AHCI 1.2 and later */ vers = readl(mmio + HOST_VERSION); @@ -519,7 +532,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) /* Override the HBA ports mapping if the platform needs it */ port_map = readl(mmio + HOST_PORTS_IMPL); if (hpriv->saved_port_map && port_map != hpriv->saved_port_map) { - dev_info(dev, "forcing port_map 0x%x -> 0x%x\n", + dev_info(dev, "forcing port_map 0x%lx -> 0x%x\n", port_map, hpriv->saved_port_map); port_map = hpriv->saved_port_map; } else { @@ -527,7 +540,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) } if (hpriv->mask_port_map) { - dev_warn(dev, "masking port_map 0x%x -> 0x%x\n", + dev_warn(dev, "masking port_map 0x%lx -> 0x%lx\n", port_map, port_map & hpriv->mask_port_map); port_map &= hpriv->mask_port_map; @@ -546,7 +559,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) */ if (map_ports > ahci_nr_ports(cap)) { dev_warn(dev, - "implemented port map (0x%x) contains more ports than nr_ports (%u), using nr_ports\n", + "implemented port map (0x%lx) contains more ports than nr_ports (%u), using nr_ports\n", port_map, ahci_nr_ports(cap)); port_map = 0; } @@ -555,12 +568,26 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) /* fabricate port_map from cap.nr_ports for < AHCI 1.3 */ if (!port_map && vers < 0x10300) { port_map = (1 << ahci_nr_ports(cap)) - 1; - dev_warn(dev, "forcing PORTS_IMPL to 0x%x\n", port_map); + dev_warn(dev, "forcing PORTS_IMPL to 0x%lx\n", port_map); /* write the fixed up value to the PI register */ hpriv->saved_port_map = port_map; } + /* + * Preserve the ports capabilities defined by the platform. Note there + * is no need in storing the rest of the P#.CMD fields since they are + * volatile. + */ + for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) { + if (hpriv->saved_port_cap[i]) + continue; + + port_mmio = __ahci_port_base(hpriv, i); + hpriv->saved_port_cap[i] = + readl(port_mmio + PORT_CMD) & PORT_CMD_CAP; + } + /* record values to use during operation */ hpriv->cap = cap; hpriv->cap2 = cap2; @@ -590,13 +617,21 @@ EXPORT_SYMBOL_GPL(ahci_save_initial_config); static void ahci_restore_initial_config(struct ata_host *host) { struct ahci_host_priv *hpriv = host->private_data; + unsigned long port_map = hpriv->port_map; void __iomem *mmio = hpriv->mmio; + void __iomem *port_mmio; + int i; writel(hpriv->saved_cap, mmio + HOST_CAP); if (hpriv->saved_cap2) writel(hpriv->saved_cap2, mmio + HOST_CAP2); writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL); (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ + + for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) { + port_mmio = __ahci_port_base(hpriv, i); + writel(hpriv->saved_port_cap[i], port_mmio + PORT_CMD); + } } static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg) diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 01c195b6d9e6..86d156075336 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -382,6 +382,34 @@ static int ahci_platform_get_regulator(struct ahci_host_priv *hpriv, u32 port, return rc; } +static int ahci_platform_get_firmware(struct ahci_host_priv *hpriv, + struct device *dev) +{ + struct device_node *child; + u32 port; + + if (!of_property_read_u32(dev->of_node, "hba-cap", &hpriv->saved_cap)) + hpriv->saved_cap &= (HOST_CAP_SSS | HOST_CAP_MPS); + + of_property_read_u32(dev->of_node, + "ports-implemented", &hpriv->saved_port_map); + + for_each_child_of_node(dev->of_node, child) { + if (!of_device_is_available(child)) + continue; + + if (of_property_read_u32(child, "reg", &port)) { + of_node_put(child); + return -EINVAL; + } + + if (!of_property_read_u32(child, "hba-port-cap", &hpriv->saved_port_cap[port])) + hpriv->saved_port_cap[port] &= PORT_CMD_CAP; + } + + return 0; +} + /** * ahci_platform_get_resources - Get platform resources * @pdev: platform device to get resources for @@ -525,9 +553,6 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, goto err_out; } - of_property_read_u32(dev->of_node, - "ports-implemented", &hpriv->saved_port_map); - if (child_nodes) { for_each_child_of_node(dev->of_node, child) { u32 port; @@ -592,6 +617,15 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, if (rc == -EPROBE_DEFER) goto err_out; } + + /* + * Retrieve firmware-specific flags which then will be used to set + * the HW-init fields of HBA and its ports + */ + rc = ahci_platform_get_firmware(hpriv, dev); + if (rc) + goto err_out; + pm_runtime_enable(dev); pm_runtime_get_sync(dev); hpriv->got_runtime_pm = true; From 5c640beccb3465c0dc941f3e7d21fb72e3a5f5f3 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:15 +0300 Subject: [PATCH 29/45] dt-bindings: ata: ahci: Add DWC AHCI SATA controller DT schema Synopsys AHCI SATA controller is mainly compatible with the generic AHCI SATA controller except a few peculiarities and the platform environment requirements. In particular it can have at least two reference clocks to feed up its AHB/AXI interface and SATA PHYs domain and at least one reset control for the application clock domain. In addition to that the DMA interface of each port can be tuned up to work with the predefined maximum data chunk size. Note unlike generic AHCI controller DWC AHCI can't have more than 8 ports. All of that is reflected in the new DWC AHCI SATA device DT binding. Note the DWC AHCI SATA controller DT-schema has been created in a way so to be reused for the vendor-specific DT-schemas (see for example the "snps,dwc-ahci" compatible string binding). One of which we are about to introduce. Signed-off-by: Serge Semin Reviewed-by: Rob Herring Signed-off-by: Damien Le Moal --- .../bindings/ata/ahci-platform.yaml | 8 -- .../bindings/ata/snps,dwc-ahci-common.yaml | 102 ++++++++++++++++++ .../bindings/ata/snps,dwc-ahci.yaml | 75 +++++++++++++ 3 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 Documentation/devicetree/bindings/ata/snps,dwc-ahci-common.yaml create mode 100644 Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.yaml b/Documentation/devicetree/bindings/ata/ahci-platform.yaml index e19cf9828e68..7dc2a2e8f598 100644 --- a/Documentation/devicetree/bindings/ata/ahci-platform.yaml +++ b/Documentation/devicetree/bindings/ata/ahci-platform.yaml @@ -30,8 +30,6 @@ select: - marvell,armada-3700-ahci - marvell,armada-8k-ahci - marvell,berlin2q-ahci - - snps,dwc-ahci - - snps,spear-ahci required: - compatible @@ -48,17 +46,11 @@ properties: - marvell,berlin2-ahci - marvell,berlin2q-ahci - const: generic-ahci - - items: - - enum: - - rockchip,rk3568-dwc-ahci - - const: snps,dwc-ahci - enum: - cavium,octeon-7130-ahci - hisilicon,hisi-ahci - ibm,476gtr-ahci - marvell,armada-3700-ahci - - snps,dwc-ahci - - snps,spear-ahci reg: minItems: 1 diff --git a/Documentation/devicetree/bindings/ata/snps,dwc-ahci-common.yaml b/Documentation/devicetree/bindings/ata/snps,dwc-ahci-common.yaml new file mode 100644 index 000000000000..c1457910520b --- /dev/null +++ b/Documentation/devicetree/bindings/ata/snps,dwc-ahci-common.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ata/snps,dwc-ahci-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys DWC AHCI SATA controller properties + +maintainers: + - Serge Semin + +description: + This document defines device tree schema for the generic Synopsys DWC + AHCI controller properties. + +select: false + +allOf: + - $ref: ahci-common.yaml# + +properties: + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + description: + Basic DWC AHCI SATA clock sources like application AXI/AHB BIU clock, + PM-alive clock, RxOOB detection clock, embedded PHYs reference (Rx/Tx) + clock, etc. + minItems: 1 + maxItems: 4 + + clock-names: + minItems: 1 + maxItems: 4 + items: + oneOf: + - description: Application APB/AHB/AXI BIU clock + enum: + - pclk + - aclk + - hclk + - sata + - description: Power Module keep-alive clock + const: pmalive + - description: RxOOB detection clock + const: rxoob + - description: SATA Ports reference clock + const: ref + + resets: + description: + At least basic application and reference clock domains resets are + normally supported by the DWC AHCI SATA controller. + minItems: 1 + maxItems: 4 + + reset-names: + minItems: 1 + maxItems: 4 + items: + oneOf: + - description: Application AHB/AXI BIU clock domain reset control + enum: + - arst + - hrst + - description: Power Module keep-alive clock domain reset control + const: pmalive + - description: RxOOB detection clock domain reset control + const: rxoob + - description: Reference clock domain reset control + const: ref + +patternProperties: + "^sata-port@[0-9a-e]$": + $ref: '#/$defs/dwc-ahci-port' + +additionalProperties: true + +$defs: + dwc-ahci-port: + $ref: /schemas/ata/ahci-common.yaml#/$defs/ahci-port + + properties: + reg: + minimum: 0 + maximum: 7 + + snps,tx-ts-max: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Maximal size of Tx DMA transactions in FIFO words + enum: [ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 ] + + snps,rx-ts-max: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Maximal size of Rx DMA transactions in FIFO words + enum: [ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 ] + +... diff --git a/Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml b/Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml new file mode 100644 index 000000000000..5afa4b57ce20 --- /dev/null +++ b/Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ata/snps,dwc-ahci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys DWC AHCI SATA controller + +maintainers: + - Serge Semin + +description: + This document defines device tree bindings for the generic Synopsys DWC + implementation of the AHCI SATA controller. + +allOf: + - $ref: snps,dwc-ahci-common.yaml# + +properties: + compatible: + oneOf: + - description: Synopsys AHCI SATA-compatible devices + const: snps,dwc-ahci + - description: SPEAr1340 AHCI SATA device + const: snps,spear-ahci + - description: Rockhip RK3568 AHCI controller + items: + - const: rockchip,rk3568-dwc-ahci + - const: snps,dwc-ahci + +patternProperties: + "^sata-port@[0-9a-e]$": + $ref: /schemas/ata/snps,dwc-ahci-common.yaml#/$defs/dwc-ahci-port + + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include + #include + + sata@122f0000 { + compatible = "snps,dwc-ahci"; + reg = <0x122F0000 0x1ff>; + #address-cells = <1>; + #size-cells = <0>; + + interrupts = ; + + clocks = <&clock1>, <&clock2>; + clock-names = "aclk", "ref"; + + phys = <&sata_phy>; + phy-names = "sata-phy"; + + ports-implemented = <0x1>; + + sata-port@0 { + reg = <0>; + + hba-port-cap = ; + + snps,tx-ts-max = <512>; + snps,rx-ts-max = <512>; + }; + }; + +... From 6ce73f3a6fc0ad6af56ba4c14ab7a410876f8286 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:16 +0300 Subject: [PATCH 30/45] ata: libahci_platform: Add function returning a clock-handle by id Since all the clocks are retrieved by the method ahci_platform_get_resources() there is no need for the LLD (glue) drivers to be looking for some particular of them in the kernel clocks table again. Instead we suggest to add a simple method returning a device-specific clock with passed connection ID if it is managed to be found. Otherwise the function will return NULL. Thus the glue-drivers won't need to either manually touching the hpriv->clks array or calling clk_get()-friends. The AHCI platform drivers will be able to use the new function right after the ahci_platform_get_resources() method invocation and up to the device removal. Note the method is left unused here, but will be utilized in the framework of the DWC AHCI SATA driver being added in the next commit. Signed-off-by: Serge Semin Signed-off-by: Damien Le Moal --- drivers/ata/libahci_platform.c | 24 ++++++++++++++++++++++++ include/linux/ahci_platform.h | 3 +++ 2 files changed, 27 insertions(+) diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 86d156075336..ddf17e2d266c 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -93,6 +93,30 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) } EXPORT_SYMBOL_GPL(ahci_platform_disable_phys); +/** + * ahci_platform_find_clk - Find platform clock + * @hpriv: host private area to store config values + * @con_id: clock connection ID + * + * This function returns a pointer to the clock descriptor of the clock with + * the passed ID. + * + * RETURNS: + * Pointer to the clock descriptor on success otherwise NULL + */ +struct clk *ahci_platform_find_clk(struct ahci_host_priv *hpriv, const char *con_id) +{ + int i; + + for (i = 0; i < hpriv->n_clks; i++) { + if (!strcmp(hpriv->clks[i].id, con_id)) + return hpriv->clks[i].clk; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(ahci_platform_find_clk); + /** * ahci_platform_enable_clks - Enable platform clocks * @hpriv: host private area to store config values diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index 6d7dd472d370..17fa26215292 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h @@ -13,6 +13,7 @@ #include +struct clk; struct device; struct ata_port_info; struct ahci_host_priv; @@ -21,6 +22,8 @@ struct scsi_host_template; int ahci_platform_enable_phys(struct ahci_host_priv *hpriv); void ahci_platform_disable_phys(struct ahci_host_priv *hpriv); +struct clk *ahci_platform_find_clk(struct ahci_host_priv *hpriv, + const char *con_id); int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); int ahci_platform_deassert_rsts(struct ahci_host_priv *hpriv); From 33629d35090f5ce2b1b4ce78aa39954c603536d5 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:17 +0300 Subject: [PATCH 31/45] ata: ahci: Add DWC AHCI SATA controller support Synopsys AHCI SATA controller can work pretty under with the generic AHCI-platform driver control. But there are vendor-specific peculiarities which can tune the device performance up and which may need to be fixed up for proper device functioning. In addition some DWC AHCI-based controllers may require small platform-specific fixups, so adding them in the generic AHCI driver would have ruined the code simplicity. Shortly speaking in order to keep the generic AHCI-platform code clean and have DWC AHCI SATA-specific features supported we suggest to add a dedicated DWC AHCI SATA device driver. Aside with the standard AHCI-platform resources getting, enabling/disabling and the controller registration the new driver performs the next actions. First of all there is a way to verify whether the HBA/ports capabilities activated in OF are correct. Almost all features availability is reflected in the vendor-specific parameters registers. So the DWC AHCI driver does the capabilities sanity check based on the corresponding fields state. Secondly if either the Command Completion Coalescing or the Device Sleep feature is enabled the DWC AHCI-specific internal 1ms timer must be fixed in accordance with the application clock signal frequency. In particular the timer value must be set to be Fapp * 1000. Normally the SoC designers pre-configure the TIMER1MS register to contain a correct value by default. But the platforms can support the application clock rate change. If that happens the 1ms timer value must be accordingly updated otherwise the dependent features won't work as expected. In the DWC AHCI driver we suggest to rely on the "aclk" reference clock rate to set the timer interval up. That clock source is supposed to be the AHCI SATA application clock in accordance with the DT bindings. Finally DWC AHCI SATA controller AXI/AHB bus DMA-engine can be tuned up to transfer up to 1024 * FIFO words at a time by setting the Tx/Rx transaction size in the DMA control register. The maximum value depends on the DMA data bus and AXI/AHB bus maximum burst length. In most of the cases it's better to set the maximum possible value to reach the best AHCI SATA controller performance. But sometimes in order to improve the system interconnect responsiveness, transferring in smaller data chunks may be more preferable. For such cases and for the case when the default value doesn't provide the best DMA bus performance we suggest to use the new HBA-port specific DT-properties "snps,{tx,rx}-ts-max" to tune the DMA transactions size up. After all the settings denoted above are handled the DWC AHCI SATA driver proceeds further with the standard AHCI-platform host initializations. Note since DWC AHCI controller is now have a dedicated driver we can discard the corresponding compatible string from the ahci-platform.c module. The same concerns "snps,spear-ahci" compatible string, which is also based on the DWC AHCI IP-core. Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Signed-off-by: Damien Le Moal --- drivers/ata/Kconfig | 9 + drivers/ata/Makefile | 1 + drivers/ata/ahci_dwc.c | 394 ++++++++++++++++++++++++++++++++++++ drivers/ata/ahci_platform.c | 2 - 4 files changed, 404 insertions(+), 2 deletions(-) create mode 100644 drivers/ata/ahci_dwc.c diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index fc11d9d30d72..12dd147e434e 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -176,6 +176,15 @@ config AHCI_DM816 If unsure, say N. +config AHCI_DWC + tristate "Synopsys DWC AHCI SATA support" + select SATA_HOST + help + This option enables support for the Synopsys DWC AHCI SATA + controller implementation. + + If unsure, say N. + config AHCI_ST tristate "ST AHCI SATA support" depends on ARCH_STI diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index b8aebfb14e82..34623365d9a6 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_AHCI_BRCM) += ahci_brcm.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_DM816) += ahci_dm816.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_DWC) += ahci_dwc.o libahci.o libahci_platform.o obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o obj-$(CONFIG_AHCI_MTK) += ahci_mtk.o libahci.o libahci_platform.o obj-$(CONFIG_AHCI_MVEBU) += ahci_mvebu.o libahci.o libahci_platform.o diff --git a/drivers/ata/ahci_dwc.c b/drivers/ata/ahci_dwc.c new file mode 100644 index 000000000000..40c389078ccc --- /dev/null +++ b/drivers/ata/ahci_dwc.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DWC AHCI SATA Platform driver + * + * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ahci.h" + +#define DRV_NAME "ahci-dwc" + +#define AHCI_DWC_FBS_PMPN_MAX 15 + +/* DWC AHCI SATA controller specific registers */ +#define AHCI_DWC_HOST_OOBR 0xbc +#define AHCI_DWC_HOST_OOB_WE BIT(31) +#define AHCI_DWC_HOST_CWMIN_MASK GENMASK(30, 24) +#define AHCI_DWC_HOST_CWMAX_MASK GENMASK(23, 16) +#define AHCI_DWC_HOST_CIMIN_MASK GENMASK(15, 8) +#define AHCI_DWC_HOST_CIMAX_MASK GENMASK(7, 0) + +#define AHCI_DWC_HOST_GPCR 0xd0 +#define AHCI_DWC_HOST_GPSR 0xd4 + +#define AHCI_DWC_HOST_TIMER1MS 0xe0 +#define AHCI_DWC_HOST_TIMV_MASK GENMASK(19, 0) + +#define AHCI_DWC_HOST_GPARAM1R 0xe8 +#define AHCI_DWC_HOST_ALIGN_M BIT(31) +#define AHCI_DWC_HOST_RX_BUFFER BIT(30) +#define AHCI_DWC_HOST_PHY_DATA_MASK GENMASK(29, 28) +#define AHCI_DWC_HOST_PHY_RST BIT(27) +#define AHCI_DWC_HOST_PHY_CTRL_MASK GENMASK(26, 21) +#define AHCI_DWC_HOST_PHY_STAT_MASK GENMASK(20, 15) +#define AHCI_DWC_HOST_LATCH_M BIT(14) +#define AHCI_DWC_HOST_PHY_TYPE_MASK GENMASK(13, 11) +#define AHCI_DWC_HOST_RET_ERR BIT(10) +#define AHCI_DWC_HOST_AHB_ENDIAN_MASK GENMASK(9, 8) +#define AHCI_DWC_HOST_S_HADDR BIT(7) +#define AHCI_DWC_HOST_M_HADDR BIT(6) +#define AHCI_DWC_HOST_S_HDATA_MASK GENMASK(5, 3) +#define AHCI_DWC_HOST_M_HDATA_MASK GENMASK(2, 0) + +#define AHCI_DWC_HOST_GPARAM2R 0xec +#define AHCI_DWC_HOST_FBS_MEM_S BIT(19) +#define AHCI_DWC_HOST_FBS_PMPN_MASK GENMASK(17, 16) +#define AHCI_DWC_HOST_FBS_SUP BIT(15) +#define AHCI_DWC_HOST_DEV_CP BIT(14) +#define AHCI_DWC_HOST_DEV_MP BIT(13) +#define AHCI_DWC_HOST_ENCODE_M BIT(12) +#define AHCI_DWC_HOST_RXOOB_CLK_M BIT(11) +#define AHCI_DWC_HOST_RXOOB_M BIT(10) +#define AHCI_DWC_HOST_TXOOB_M BIT(9) +#define AHCI_DWC_HOST_RXOOB_M BIT(10) +#define AHCI_DWC_HOST_RXOOB_CLK_MASK GENMASK(8, 0) + +#define AHCI_DWC_HOST_PPARAMR 0xf0 +#define AHCI_DWC_HOST_TX_MEM_M BIT(11) +#define AHCI_DWC_HOST_TX_MEM_S BIT(10) +#define AHCI_DWC_HOST_RX_MEM_M BIT(9) +#define AHCI_DWC_HOST_RX_MEM_S BIT(8) +#define AHCI_DWC_HOST_TXFIFO_DEPTH GENMASK(7, 4) +#define AHCI_DWC_HOST_RXFIFO_DEPTH GENMASK(3, 0) + +#define AHCI_DWC_HOST_TESTR 0xf4 +#define AHCI_DWC_HOST_PSEL_MASK GENMASK(18, 16) +#define AHCI_DWC_HOST_TEST_IF BIT(0) + +#define AHCI_DWC_HOST_VERSIONR 0xf8 +#define AHCI_DWC_HOST_IDR 0xfc + +#define AHCI_DWC_PORT_DMACR 0x70 +#define AHCI_DWC_PORT_RXABL_MASK GENMASK(15, 12) +#define AHCI_DWC_PORT_TXABL_MASK GENMASK(11, 8) +#define AHCI_DWC_PORT_RXTS_MASK GENMASK(7, 4) +#define AHCI_DWC_PORT_TXTS_MASK GENMASK(3, 0) +#define AHCI_DWC_PORT_PHYCR 0x74 +#define AHCI_DWC_PORT_PHYSR 0x78 + +struct ahci_dwc_host_priv { + struct platform_device *pdev; + + u32 timv; + u32 dmacr[AHCI_MAX_PORTS]; +}; + +static struct ahci_host_priv *ahci_dwc_get_resources(struct platform_device *pdev) +{ + struct ahci_dwc_host_priv *dpriv; + struct ahci_host_priv *hpriv; + + dpriv = devm_kzalloc(&pdev->dev, sizeof(*dpriv), GFP_KERNEL); + if (!dpriv) + return ERR_PTR(-ENOMEM); + + dpriv->pdev = pdev; + + hpriv = ahci_platform_get_resources(pdev, AHCI_PLATFORM_GET_RESETS); + if (IS_ERR(hpriv)) + return hpriv; + + hpriv->plat_data = (void *)dpriv; + + return hpriv; +} + +static void ahci_dwc_check_cap(struct ahci_host_priv *hpriv) +{ + unsigned long port_map = hpriv->saved_port_map | hpriv->mask_port_map; + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + bool dev_mp, dev_cp, fbs_sup; + unsigned int fbs_pmp; + u32 param; + int i; + + param = readl(hpriv->mmio + AHCI_DWC_HOST_GPARAM2R); + dev_mp = !!(param & AHCI_DWC_HOST_DEV_MP); + dev_cp = !!(param & AHCI_DWC_HOST_DEV_CP); + fbs_sup = !!(param & AHCI_DWC_HOST_FBS_SUP); + fbs_pmp = 5 * FIELD_GET(AHCI_DWC_HOST_FBS_PMPN_MASK, param); + + if (!dev_mp && hpriv->saved_cap & HOST_CAP_MPS) { + dev_warn(&dpriv->pdev->dev, "MPS is unsupported\n"); + hpriv->saved_cap &= ~HOST_CAP_MPS; + } + + + if (fbs_sup && fbs_pmp < AHCI_DWC_FBS_PMPN_MAX) { + dev_warn(&dpriv->pdev->dev, "PMPn is limited up to %u ports\n", + fbs_pmp); + } + + for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) { + if (!dev_mp && hpriv->saved_port_cap[i] & PORT_CMD_MPSP) { + dev_warn(&dpriv->pdev->dev, "MPS incapable port %d\n", i); + hpriv->saved_port_cap[i] &= ~PORT_CMD_MPSP; + } + + if (!dev_cp && hpriv->saved_port_cap[i] & PORT_CMD_CPD) { + dev_warn(&dpriv->pdev->dev, "CPD incapable port %d\n", i); + hpriv->saved_port_cap[i] &= ~PORT_CMD_CPD; + } + + if (!fbs_sup && hpriv->saved_port_cap[i] & PORT_CMD_FBSCP) { + dev_warn(&dpriv->pdev->dev, "FBS incapable port %d\n", i); + hpriv->saved_port_cap[i] &= ~PORT_CMD_FBSCP; + } + } +} + +static void ahci_dwc_init_timer(struct ahci_host_priv *hpriv) +{ + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + unsigned long rate; + struct clk *aclk; + u32 cap, cap2; + + /* 1ms tick is generated only for the CCC or DevSleep features */ + cap = readl(hpriv->mmio + HOST_CAP); + cap2 = readl(hpriv->mmio + HOST_CAP2); + if (!(cap & HOST_CAP_CCC) && !(cap2 & HOST_CAP2_SDS)) + return; + + /* + * Tick is generated based on the AXI/AHB application clocks signal + * so we need to be sure in the clock we are going to use. + */ + aclk = ahci_platform_find_clk(hpriv, "aclk"); + if (!aclk) + return; + + /* 1ms timer interval is set as TIMV = AMBA_FREQ[MHZ] * 1000 */ + dpriv->timv = readl(hpriv->mmio + AHCI_DWC_HOST_TIMER1MS); + dpriv->timv = FIELD_GET(AHCI_DWC_HOST_TIMV_MASK, dpriv->timv); + rate = clk_get_rate(aclk) / 1000UL; + if (rate == dpriv->timv) + return; + + dev_info(&dpriv->pdev->dev, "Update CCC/DevSlp timer for Fapp %lu MHz\n", + rate / 1000UL); + dpriv->timv = FIELD_PREP(AHCI_DWC_HOST_TIMV_MASK, rate); + writel(dpriv->timv, hpriv->mmio + AHCI_DWC_HOST_TIMER1MS); +} + +static int ahci_dwc_init_dmacr(struct ahci_host_priv *hpriv) +{ + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + struct device_node *child; + void __iomem *port_mmio; + u32 port, dmacr, ts; + + /* + * Update the DMA Tx/Rx transaction sizes in accordance with the + * platform setup. Note values exceeding maximal or minimal limits will + * be automatically clamped. Also note the register isn't affected by + * the HBA global reset so we can freely initialize it once until the + * next system reset. + */ + for_each_child_of_node(dpriv->pdev->dev.of_node, child) { + if (!of_device_is_available(child)) + continue; + + if (of_property_read_u32(child, "reg", &port)) { + of_node_put(child); + return -EINVAL; + } + + port_mmio = __ahci_port_base(hpriv, port); + dmacr = readl(port_mmio + AHCI_DWC_PORT_DMACR); + + if (!of_property_read_u32(child, "snps,tx-ts-max", &ts)) { + ts = ilog2(ts); + dmacr &= ~AHCI_DWC_PORT_TXTS_MASK; + dmacr |= FIELD_PREP(AHCI_DWC_PORT_TXTS_MASK, ts); + } + + if (!of_property_read_u32(child, "snps,rx-ts-max", &ts)) { + ts = ilog2(ts); + dmacr &= ~AHCI_DWC_PORT_RXTS_MASK; + dmacr |= FIELD_PREP(AHCI_DWC_PORT_RXTS_MASK, ts); + } + + writel(dmacr, port_mmio + AHCI_DWC_PORT_DMACR); + dpriv->dmacr[port] = dmacr; + } + + return 0; +} + +static int ahci_dwc_init_host(struct ahci_host_priv *hpriv) +{ + int rc; + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + ahci_dwc_check_cap(hpriv); + + ahci_dwc_init_timer(hpriv); + + rc = ahci_dwc_init_dmacr(hpriv); + if (rc) + goto err_disable_resources; + + return 0; + +err_disable_resources: + ahci_platform_disable_resources(hpriv); + + return rc; +} + +static int ahci_dwc_reinit_host(struct ahci_host_priv *hpriv) +{ + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + unsigned long port_map = hpriv->port_map; + void __iomem *port_mmio; + int i, rc; + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + writel(dpriv->timv, hpriv->mmio + AHCI_DWC_HOST_TIMER1MS); + + for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) { + port_mmio = __ahci_port_base(hpriv, i); + writel(dpriv->dmacr[i], port_mmio + AHCI_DWC_PORT_DMACR); + } + + return 0; +} + +static void ahci_dwc_clear_host(struct ahci_host_priv *hpriv) +{ + ahci_platform_disable_resources(hpriv); +} + +static void ahci_dwc_stop_host(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + + ahci_dwc_clear_host(hpriv); +} + +static struct ata_port_operations ahci_dwc_port_ops = { + .inherits = &ahci_platform_ops, + .host_stop = ahci_dwc_stop_host, +}; + +static const struct ata_port_info ahci_dwc_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_dwc_port_ops, +}; + +static struct scsi_host_template ahci_dwc_scsi_info = { + AHCI_SHT(DRV_NAME), +}; + +static int ahci_dwc_probe(struct platform_device *pdev) +{ + struct ahci_host_priv *hpriv; + int rc; + + hpriv = ahci_dwc_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + rc = ahci_dwc_init_host(hpriv); + if (rc) + return rc; + + rc = ahci_platform_init_host(pdev, hpriv, &ahci_dwc_port_info, + &ahci_dwc_scsi_info); + if (rc) + goto err_clear_host; + + return 0; + +err_clear_host: + ahci_dwc_clear_host(hpriv); + + return rc; +} + +static int ahci_dwc_suspend(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int rc; + + rc = ahci_platform_suspend_host(dev); + if (rc) + return rc; + + ahci_dwc_clear_host(hpriv); + + return 0; +} + +static int ahci_dwc_resume(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int rc; + + rc = ahci_dwc_reinit_host(hpriv); + if (rc) + return rc; + + return ahci_platform_resume_host(dev); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(ahci_dwc_pm_ops, ahci_dwc_suspend, + ahci_dwc_resume); + +static const struct of_device_id ahci_dwc_of_match[] = { + { .compatible = "snps,dwc-ahci", }, + { .compatible = "snps,spear-ahci", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ahci_dwc_of_match); + +static struct platform_driver ahci_dwc_driver = { + .probe = ahci_dwc_probe, + .remove = ata_platform_remove_one, + .shutdown = ahci_platform_shutdown, + .driver = { + .name = DRV_NAME, + .of_match_table = ahci_dwc_of_match, + .pm = &ahci_dwc_pm_ops, + }, +}; +module_platform_driver(ahci_dwc_driver); + +MODULE_DESCRIPTION("DWC AHCI SATA platform driver"); +MODULE_AUTHOR("Serge Semin "); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 9b56490ecbc3..8f5572a9f8f1 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -80,9 +80,7 @@ static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, static const struct of_device_id ahci_of_match[] = { { .compatible = "generic-ahci", }, /* Keep the following compatibles for device tree compatibility */ - { .compatible = "snps,spear-ahci", }, { .compatible = "ibm,476gtr-ahci", }, - { .compatible = "snps,dwc-ahci", }, { .compatible = "hisilicon,hisi-ahci", }, { .compatible = "cavium,octeon-7130-ahci", }, { /* sentinel */ } From 064f14e9df4e7a49940742b8a9af43b48fb4cf00 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:18 +0300 Subject: [PATCH 32/45] dt-bindings: ata: ahci: Add Baikal-T1 AHCI SATA controller DT schema Baikal-T1 AHCI controller is based on the DWC AHCI SATA IP-core v4.10a with the next specific settings: two SATA ports, cascaded CSR access based on two clock domains (APB and AXI), selectable source of the reference clock (though stable work is currently available from the external source only), two reset lanes for the application and SATA ports domains. Other than that the device is fully compatible with the generic DWC AHCI SATA bindings. Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Reviewed-by: Rob Herring Signed-off-by: Damien Le Moal --- .../bindings/ata/baikal,bt1-ahci.yaml | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml diff --git a/Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml b/Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml new file mode 100644 index 000000000000..9b7ca4759bd7 --- /dev/null +++ b/Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml @@ -0,0 +1,115 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ata/baikal,bt1-ahci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Baikal-T1 SoC AHCI SATA controller + +maintainers: + - Serge Semin + +description: + AHCI SATA controller embedded into the Baikal-T1 SoC is based on the + DWC AHCI SATA v4.10a IP-core. + +allOf: + - $ref: snps,dwc-ahci-common.yaml# + +properties: + compatible: + const: baikal,bt1-ahci + + clocks: + items: + - description: Peripheral APB bus clock + - description: Application AXI BIU clock + - description: SATA Ports reference clock + + clock-names: + items: + - const: pclk + - const: aclk + - const: ref + + resets: + items: + - description: Application AXI BIU domain reset + - description: SATA Ports clock domain reset + + reset-names: + items: + - const: arst + - const: ref + + ports-implemented: + maximum: 0x3 + +patternProperties: + "^sata-port@[0-1]$": + $ref: /schemas/ata/snps,dwc-ahci-common.yaml#/$defs/dwc-ahci-port + + properties: + reg: + minimum: 0 + maximum: 1 + + snps,tx-ts-max: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Due to having AXI3 bus interface utilized the maximum Tx DMA + transaction size can't exceed 16 beats (AxLEN[3:0]). + enum: [ 1, 2, 4, 8, 16 ] + + snps,rx-ts-max: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Due to having AXI3 bus interface utilized the maximum Rx DMA + transaction size can't exceed 16 beats (AxLEN[3:0]). + enum: [ 1, 2, 4, 8, 16 ] + + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + +unevaluatedProperties: false + +examples: + - | + sata@1f050000 { + compatible = "baikal,bt1-ahci"; + reg = <0x1f050000 0x2000>; + #address-cells = <1>; + #size-cells = <0>; + + interrupts = <0 64 4>; + + clocks = <&ccu_sys 1>, <&ccu_axi 2>, <&sata_ref_clk>; + clock-names = "pclk", "aclk", "ref"; + + resets = <&ccu_axi 2>, <&ccu_sys 0>; + reset-names = "arst", "ref"; + + ports-implemented = <0x3>; + + sata-port@0 { + reg = <0>; + + snps,tx-ts-max = <4>; + snps,rx-ts-max = <4>; + }; + + sata-port@1 { + reg = <1>; + + snps,tx-ts-max = <4>; + snps,rx-ts-max = <4>; + }; + }; +... From bc7af9100fa8a671298139f87a29f4254c207786 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:19 +0300 Subject: [PATCH 33/45] ata: ahci-dwc: Add platform-specific quirks support Some DWC AHCI SATA IP-core derivatives require to perform small platform or IP-core specific setups. They are too small to be placed in a dedicated driver. It's just much easier to have a set of quirks for them right in the DWC AHCI driver code. Since we are about to add such platform support, as a pre-requisite we introduce a platform-data based DWC AHCI quirks API. The platform data can be used to define the flags passed to the ahci_platform_get_resources() method, additional AHCI host-flags and a set of callbacks to initialize, re-initialize and clear the platform settings. Signed-off-by: Serge Semin Signed-off-by: Damien Le Moal --- drivers/ata/ahci_dwc.c | 52 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/drivers/ata/ahci_dwc.c b/drivers/ata/ahci_dwc.c index 40c389078ccc..6e64d3502669 100644 --- a/drivers/ata/ahci_dwc.c +++ b/drivers/ata/ahci_dwc.c @@ -90,7 +90,16 @@ #define AHCI_DWC_PORT_PHYCR 0x74 #define AHCI_DWC_PORT_PHYSR 0x78 +struct ahci_dwc_plat_data { + unsigned int pflags; + unsigned int hflags; + int (*init)(struct ahci_host_priv *hpriv); + int (*reinit)(struct ahci_host_priv *hpriv); + void (*clear)(struct ahci_host_priv *hpriv); +}; + struct ahci_dwc_host_priv { + const struct ahci_dwc_plat_data *pdata; struct platform_device *pdev; u32 timv; @@ -107,11 +116,15 @@ static struct ahci_host_priv *ahci_dwc_get_resources(struct platform_device *pde return ERR_PTR(-ENOMEM); dpriv->pdev = pdev; + dpriv->pdata = device_get_match_data(&pdev->dev); + if (!dpriv->pdata) + return ERR_PTR(-EINVAL); - hpriv = ahci_platform_get_resources(pdev, AHCI_PLATFORM_GET_RESETS); + hpriv = ahci_platform_get_resources(pdev, dpriv->pdata->pflags); if (IS_ERR(hpriv)) return hpriv; + hpriv->flags |= dpriv->pdata->hflags; hpriv->plat_data = (void *)dpriv; return hpriv; @@ -242,22 +255,33 @@ static int ahci_dwc_init_dmacr(struct ahci_host_priv *hpriv) static int ahci_dwc_init_host(struct ahci_host_priv *hpriv) { + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; int rc; rc = ahci_platform_enable_resources(hpriv); if (rc) return rc; + if (dpriv->pdata->init) { + rc = dpriv->pdata->init(hpriv); + if (rc) + goto err_disable_resources; + } + ahci_dwc_check_cap(hpriv); ahci_dwc_init_timer(hpriv); rc = ahci_dwc_init_dmacr(hpriv); if (rc) - goto err_disable_resources; + goto err_clear_platform; return 0; +err_clear_platform: + if (dpriv->pdata->clear) + dpriv->pdata->clear(hpriv); + err_disable_resources: ahci_platform_disable_resources(hpriv); @@ -275,6 +299,12 @@ static int ahci_dwc_reinit_host(struct ahci_host_priv *hpriv) if (rc) return rc; + if (dpriv->pdata->reinit) { + rc = dpriv->pdata->reinit(hpriv); + if (rc) + goto err_disable_resources; + } + writel(dpriv->timv, hpriv->mmio + AHCI_DWC_HOST_TIMER1MS); for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) { @@ -283,10 +313,20 @@ static int ahci_dwc_reinit_host(struct ahci_host_priv *hpriv) } return 0; + +err_disable_resources: + ahci_platform_disable_resources(hpriv); + + return rc; } static void ahci_dwc_clear_host(struct ahci_host_priv *hpriv) { + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + + if (dpriv->pdata->clear) + dpriv->pdata->clear(hpriv); + ahci_platform_disable_resources(hpriv); } @@ -370,9 +410,13 @@ static int ahci_dwc_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(ahci_dwc_pm_ops, ahci_dwc_suspend, ahci_dwc_resume); +static struct ahci_dwc_plat_data ahci_dwc_plat = { + .pflags = AHCI_PLATFORM_GET_RESETS, +}; + static const struct of_device_id ahci_dwc_of_match[] = { - { .compatible = "snps,dwc-ahci", }, - { .compatible = "snps,spear-ahci", }, + { .compatible = "snps,dwc-ahci", &ahci_dwc_plat }, + { .compatible = "snps,spear-ahci", &ahci_dwc_plat }, {}, }; MODULE_DEVICE_TABLE(of, ahci_dwc_of_match); From 9628711aa649e0134f567505290537460326ddc1 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:20 +0300 Subject: [PATCH 34/45] ata: ahci-dwc: Add Baikal-T1 AHCI SATA interface support It's almost fully compatible DWC AHCI SATA IP-core derivative except the reference clocks source, which need to be very carefully selected. In particular the DWC AHCI SATA PHY can be clocked either from the pads ref_pad_clk_{m,p} or from the internal wires ref_alt_clk_{m,n}. In the later case the clock signal is generated from the Baikal-T1 CCU SATA PLL. The clocks source is selected by means of the ref_use_pad wire connected to the CCU SATA reference clock CSR. In normal situation it would be much more handy to use the internal reference clock source, but alas we haven't managed to make the AHCI controller working well with it so far. So it's preferable to have the controller clocked from the external clock generator and fallback to the internal clock source only as a last resort. Other than that the controller is full compatible with the DWC AHCI SATA IP-core. Signed-off-by: Serge Semin Reviewed-by: Hannes Reinecke Signed-off-by: Damien Le Moal --- drivers/ata/Kconfig | 1 + drivers/ata/ahci_dwc.c | 55 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 12dd147e434e..1a8a1bbc8a0e 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -179,6 +179,7 @@ config AHCI_DM816 config AHCI_DWC tristate "Synopsys DWC AHCI SATA support" select SATA_HOST + select MFD_SYSCON if (MIPS_BAIKAL_T1 || COMPILE_TEST) help This option enables support for the Synopsys DWC AHCI SATA controller implementation. diff --git a/drivers/ata/ahci_dwc.c b/drivers/ata/ahci_dwc.c index 6e64d3502669..8fb66860db31 100644 --- a/drivers/ata/ahci_dwc.c +++ b/drivers/ata/ahci_dwc.c @@ -13,10 +13,12 @@ #include #include #include +#include #include #include #include #include +#include #include "ahci.h" @@ -90,6 +92,20 @@ #define AHCI_DWC_PORT_PHYCR 0x74 #define AHCI_DWC_PORT_PHYSR 0x78 +/* Baikal-T1 AHCI SATA specific registers */ +#define AHCI_BT1_HOST_PHYCR AHCI_DWC_HOST_GPCR +#define AHCI_BT1_HOST_MPLM_MASK GENMASK(29, 23) +#define AHCI_BT1_HOST_LOSDT_MASK GENMASK(22, 20) +#define AHCI_BT1_HOST_CRR BIT(19) +#define AHCI_BT1_HOST_CRW BIT(18) +#define AHCI_BT1_HOST_CRCD BIT(17) +#define AHCI_BT1_HOST_CRCA BIT(16) +#define AHCI_BT1_HOST_CRDI_MASK GENMASK(15, 0) + +#define AHCI_BT1_HOST_PHYSR AHCI_DWC_HOST_GPSR +#define AHCI_BT1_HOST_CRA BIT(16) +#define AHCI_BT1_HOST_CRDO_MASK GENMASK(15, 0) + struct ahci_dwc_plat_data { unsigned int pflags; unsigned int hflags; @@ -106,6 +122,39 @@ struct ahci_dwc_host_priv { u32 dmacr[AHCI_MAX_PORTS]; }; +static int ahci_bt1_init(struct ahci_host_priv *hpriv) +{ + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + int ret; + + /* APB, application and reference clocks are required */ + if (!ahci_platform_find_clk(hpriv, "pclk") || + !ahci_platform_find_clk(hpriv, "aclk") || + !ahci_platform_find_clk(hpriv, "ref")) { + dev_err(&dpriv->pdev->dev, "No system clocks specified\n"); + return -EINVAL; + } + + /* + * Fully reset the SATA AXI and ref clocks domain to ensure the state + * machine is working from scratch especially if the reference clocks + * source has been changed. + */ + ret = ahci_platform_assert_rsts(hpriv); + if (ret) { + dev_err(&dpriv->pdev->dev, "Couldn't assert the resets\n"); + return ret; + } + + ret = ahci_platform_deassert_rsts(hpriv); + if (ret) { + dev_err(&dpriv->pdev->dev, "Couldn't de-assert the resets\n"); + return ret; + } + + return 0; +} + static struct ahci_host_priv *ahci_dwc_get_resources(struct platform_device *pdev) { struct ahci_dwc_host_priv *dpriv; @@ -414,9 +463,15 @@ static struct ahci_dwc_plat_data ahci_dwc_plat = { .pflags = AHCI_PLATFORM_GET_RESETS, }; +static struct ahci_dwc_plat_data ahci_bt1_plat = { + .pflags = AHCI_PLATFORM_GET_RESETS | AHCI_PLATFORM_RST_TRIGGER, + .init = ahci_bt1_init, +}; + static const struct of_device_id ahci_dwc_of_match[] = { { .compatible = "snps,dwc-ahci", &ahci_dwc_plat }, { .compatible = "snps,spear-ahci", &ahci_dwc_plat }, + { .compatible = "baikal,bt1-ahci", &ahci_bt1_plat }, {}, }; MODULE_DEVICE_TABLE(of, ahci_dwc_of_match); From e7840a9aae6f791d8fef47c892821b1915d20747 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 9 Sep 2022 22:36:21 +0300 Subject: [PATCH 35/45] MAINTAINERS: Add maintainers for DWC AHCI SATA driver Add myself as a maintainer of the new DWC AHCI SATA driver and its DT-bindings schema. Signed-off-by: Serge Semin Signed-off-by: Damien Le Moal --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8a5012ba6ff9..07be3c4330e9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11551,6 +11551,15 @@ F: drivers/ata/ahci_platform.c F: drivers/ata/libahci_platform.c F: include/linux/ahci_platform.h +LIBATA SATA AHCI SYNOPSYS DWC CONTROLLER DRIVER +M: Serge Semin +L: linux-ide@vger.kernel.org +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata.git +F: Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml +F: Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml +F: drivers/ata/ahci_dwc.c + LIBATA SATA PROMISE TX2/TX4 CONTROLLER DRIVER M: Mikael Pettersson L: linux-ide@vger.kernel.org From 2d29dd108c787e039593f76c588d8f6d3541eb1c Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Tue, 20 Sep 2022 08:43:07 +0900 Subject: [PATCH 36/45] ata: ahci_st: Fix compilation warning Remove the unused variable dev in st_ahci_probe() to avoid compilation warning and build failures where CONFIG_WERROR is enabled. Fixes: 3f74cd046fbe ("ata: libahci_platform: Parse ports-implemented property in resources getter") Signed-off-by: Damien Le Moal --- drivers/ata/ahci_st.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index 068621099c00..5a2cac60a29a 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -144,7 +144,6 @@ static struct scsi_host_template ahci_platform_sht = { static int st_ahci_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; struct st_ahci_drv_data *drv_data; struct ahci_host_priv *hpriv; int err; From ecf8322f464d62759d838ea62cdeff6966a60134 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Tue, 20 Sep 2022 08:52:02 +0900 Subject: [PATCH 37/45] ata: ahci_st: Enable compile test Enable compiling the ahci_st driver when COMPILE_TEST is enabled. Signed-off-by: Damien Le Moal --- drivers/ata/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 1a8a1bbc8a0e..36833a862998 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -188,7 +188,7 @@ config AHCI_DWC config AHCI_ST tristate "ST AHCI SATA support" - depends on ARCH_STI + depends on ARCH_STI || COMPILE_TEST select SATA_HOST help This option enables support for ST AHCI SATA controller. From cb6e73aaadff73751bb1c01349e58f2c6428e0a8 Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Tue, 20 Sep 2022 06:29:29 +0000 Subject: [PATCH 38/45] ata: libata-eh: Remove the unneeded result variable Return the value ata_port_abort() directly instead of storing it in another redundant variable. Reported-by: Zeal Robot Signed-off-by: ye xingchen Signed-off-by: Damien Le Moal --- drivers/ata/libata-eh.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index ef4508d72c02..c7827ad6360f 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1086,14 +1086,11 @@ static void __ata_port_freeze(struct ata_port *ap) */ int ata_port_freeze(struct ata_port *ap) { - int nr_aborted; - WARN_ON(!ap->ops->error_handler); __ata_port_freeze(ap); - nr_aborted = ata_port_abort(ap); - return nr_aborted; + return ata_port_abort(ap); } EXPORT_SYMBOL_GPL(ata_port_freeze); From 690aa8c3ae308bc696ec8b1b357b995193927083 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 16 Sep 2022 14:28:32 +0200 Subject: [PATCH 39/45] ata: fix ata_id_sense_reporting_enabled() and ata_id_has_sense_reporting() ACS-5 section 7.13.6.41 Words 85..87, 120: Commands and feature sets supported or enabled states that: If bit 15 of word 86 is set to one, bit 14 of word 119 is set to one, and bit 15 of word 119 is cleared to zero, then word 119 is valid. If bit 15 of word 86 is set to one, bit 14 of word 120 is set to one, and bit 15 of word 120 is cleared to zero, then word 120 is valid. (This text also exists in really old ACS standards, e.g. ACS-3.) Currently, ata_id_sense_reporting_enabled() and ata_id_has_sense_reporting() both check bit 15 of word 86, but neither of them check that bit 14 of word 119 is set to one, or that bit 15 of word 119 is cleared to zero. Additionally, make ata_id_sense_reporting_enabled() return false if !ata_id_has_sense_reporting(), similar to how e.g. ata_id_flush_ext_enabled() returns false if !ata_id_has_flush_ext(). Fixes: e87fd28cf9a2 ("libata: Implement support for sense data reporting") Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- include/linux/ata.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/include/linux/ata.h b/include/linux/ata.h index 21292b5bbb55..868bfd503aee 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -771,16 +771,21 @@ static inline bool ata_id_has_read_log_dma_ext(const u16 *id) static inline bool ata_id_has_sense_reporting(const u16 *id) { - if (!(id[ATA_ID_CFS_ENABLE_2] & (1 << 15))) + if (!(id[ATA_ID_CFS_ENABLE_2] & BIT(15))) return false; - return id[ATA_ID_COMMAND_SET_3] & (1 << 6); + if ((id[ATA_ID_COMMAND_SET_3] & (BIT(15) | BIT(14))) != BIT(14)) + return false; + return id[ATA_ID_COMMAND_SET_3] & BIT(6); } static inline bool ata_id_sense_reporting_enabled(const u16 *id) { - if (!(id[ATA_ID_CFS_ENABLE_2] & (1 << 15))) + if (!ata_id_has_sense_reporting(id)) return false; - return id[ATA_ID_COMMAND_SET_4] & (1 << 6); + /* ata_id_has_sense_reporting() == true, word 86 must have bit 15 set */ + if ((id[ATA_ID_COMMAND_SET_4] & (BIT(15) | BIT(14))) != BIT(14)) + return false; + return id[ATA_ID_COMMAND_SET_4] & BIT(6); } /** From 9c6e09a434e1317e09b78b3b69cd384022ec9a03 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 16 Sep 2022 14:28:33 +0200 Subject: [PATCH 40/45] ata: fix ata_id_has_devslp() ACS-5 section 7.13.6.36 Word 78: Serial ATA features supported states that: If word 76 is not 0000h or FFFFh, word 78 reports the features supported by the device. If this word is not supported, the word shall be cleared to zero. (This text also exists in really old ACS standards, e.g. ACS-3.) Additionally, move the macro to the other ATA_ID_FEATURE_SUPP macros (which already have this check), thus making it more likely that the next ATA_ID_FEATURE_SUPP macro that is added will include this check. Fixes: 65fe1f0f66a5 ("ahci: implement aggressive SATA device sleep support") Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- include/linux/ata.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/ata.h b/include/linux/ata.h index 868bfd503aee..bc136a43689f 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -566,6 +566,10 @@ struct ata_bmdma_prd { ((((id)[ATA_ID_SATA_CAPABILITY] != 0x0000) && \ ((id)[ATA_ID_SATA_CAPABILITY] != 0xffff)) && \ ((id)[ATA_ID_FEATURE_SUPP] & (1 << 2))) +#define ata_id_has_devslp(id) \ + ((((id)[ATA_ID_SATA_CAPABILITY] != 0x0000) && \ + ((id)[ATA_ID_SATA_CAPABILITY] != 0xffff)) && \ + ((id)[ATA_ID_FEATURE_SUPP] & (1 << 8))) #define ata_id_iordy_disable(id) ((id)[ATA_ID_CAPABILITY] & (1 << 10)) #define ata_id_has_iordy(id) ((id)[ATA_ID_CAPABILITY] & (1 << 11)) #define ata_id_u32(id,n) \ @@ -578,7 +582,6 @@ struct ata_bmdma_prd { #define ata_id_cdb_intr(id) (((id)[ATA_ID_CONFIG] & 0x60) == 0x20) #define ata_id_has_da(id) ((id)[ATA_ID_SATA_CAPABILITY_2] & (1 << 4)) -#define ata_id_has_devslp(id) ((id)[ATA_ID_FEATURE_SUPP] & (1 << 8)) #define ata_id_has_ncq_autosense(id) \ ((id)[ATA_ID_FEATURE_SUPP] & (1 << 7)) From a5fb6bf853148974dbde092ec1bde553bea5e49f Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 16 Sep 2022 14:28:34 +0200 Subject: [PATCH 41/45] ata: fix ata_id_has_ncq_autosense() ACS-5 section 7.13.6.36 Word 78: Serial ATA features supported states that: If word 76 is not 0000h or FFFFh, word 78 reports the features supported by the device. If this word is not supported, the word shall be cleared to zero. (This text also exists in really old ACS standards, e.g. ACS-3.) Additionally, move the macro to the other ATA_ID_FEATURE_SUPP macros (which already have this check), thus making it more likely that the next ATA_ID_FEATURE_SUPP macro that is added will include this check. Fixes: 5b01e4b9efa0 ("libata: Implement NCQ autosense") Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- include/linux/ata.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/linux/ata.h b/include/linux/ata.h index bc136a43689f..4845443e0f08 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -570,6 +570,10 @@ struct ata_bmdma_prd { ((((id)[ATA_ID_SATA_CAPABILITY] != 0x0000) && \ ((id)[ATA_ID_SATA_CAPABILITY] != 0xffff)) && \ ((id)[ATA_ID_FEATURE_SUPP] & (1 << 8))) +#define ata_id_has_ncq_autosense(id) \ + ((((id)[ATA_ID_SATA_CAPABILITY] != 0x0000) && \ + ((id)[ATA_ID_SATA_CAPABILITY] != 0xffff)) && \ + ((id)[ATA_ID_FEATURE_SUPP] & (1 << 7))) #define ata_id_iordy_disable(id) ((id)[ATA_ID_CAPABILITY] & (1 << 10)) #define ata_id_has_iordy(id) ((id)[ATA_ID_CAPABILITY] & (1 << 11)) #define ata_id_u32(id,n) \ @@ -582,8 +586,6 @@ struct ata_bmdma_prd { #define ata_id_cdb_intr(id) (((id)[ATA_ID_CONFIG] & 0x60) == 0x20) #define ata_id_has_da(id) ((id)[ATA_ID_SATA_CAPABILITY_2] & (1 << 4)) -#define ata_id_has_ncq_autosense(id) \ - ((id)[ATA_ID_FEATURE_SUPP] & (1 << 7)) static inline bool ata_id_has_hipm(const u16 *id) { From 630624cb1b5826d753ac8e01a0e42de43d66dedf Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 16 Sep 2022 14:28:35 +0200 Subject: [PATCH 42/45] ata: fix ata_id_has_dipm() ACS-5 section 7.13.6.36 Word 78: Serial ATA features supported states that: If word 76 is not 0000h or FFFFh, word 78 reports the features supported by the device. If this word is not supported, the word shall be cleared to zero. (This text also exists in really old ACS standards, e.g. ACS-3.) The problem with ata_id_has_dipm() is that the while it performs a check against 0 and 0xffff, it performs the check against ATA_ID_FEATURE_SUPP (word 78), the same word where the feature bit is stored. Fix this by performing the check against ATA_ID_SATA_CAPABILITY (word 76), like required by the spec. The feature bit check itself is of course still performed against ATA_ID_FEATURE_SUPP (word 78). Additionally, move the macro to the other ATA_ID_FEATURE_SUPP macros (which already have this check), thus making it more likely that the next ATA_ID_FEATURE_SUPP macro that is added will include this check. Fixes: ca77329fb713 ("[libata] Link power management infrastructure") Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- include/linux/ata.h | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/include/linux/ata.h b/include/linux/ata.h index 4845443e0f08..e3050e153a71 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -574,6 +574,10 @@ struct ata_bmdma_prd { ((((id)[ATA_ID_SATA_CAPABILITY] != 0x0000) && \ ((id)[ATA_ID_SATA_CAPABILITY] != 0xffff)) && \ ((id)[ATA_ID_FEATURE_SUPP] & (1 << 7))) +#define ata_id_has_dipm(id) \ + ((((id)[ATA_ID_SATA_CAPABILITY] != 0x0000) && \ + ((id)[ATA_ID_SATA_CAPABILITY] != 0xffff)) && \ + ((id)[ATA_ID_FEATURE_SUPP] & (1 << 3))) #define ata_id_iordy_disable(id) ((id)[ATA_ID_CAPABILITY] & (1 << 10)) #define ata_id_has_iordy(id) ((id)[ATA_ID_CAPABILITY] & (1 << 11)) #define ata_id_u32(id,n) \ @@ -597,17 +601,6 @@ static inline bool ata_id_has_hipm(const u16 *id) return val & (1 << 9); } -static inline bool ata_id_has_dipm(const u16 *id) -{ - u16 val = id[ATA_ID_FEATURE_SUPP]; - - if (val == 0 || val == 0xffff) - return false; - - return val & (1 << 3); -} - - static inline bool ata_id_has_fua(const u16 *id) { if ((id[ATA_ID_CFSSE] & 0xC000) != 0x4000) From b46c760e11c8ce59166d2f9038d237dee409f37d Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 16 Sep 2022 14:28:36 +0200 Subject: [PATCH 43/45] ata: libata: drop superfluous ata_eh_request_sense() parameter The parameter can easily be derived from struct ata_queued_cmd. Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-eh.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index c7827ad6360f..91c88f429b4f 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1390,7 +1390,6 @@ unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) /** * ata_eh_request_sense - perform REQUEST_SENSE_DATA_EXT * @qc: qc to perform REQUEST_SENSE_SENSE_DATA_EXT to - * @cmd: scsi command for which the sense code should be set * * Perform REQUEST_SENSE_DATA_EXT after the device reported CHECK * SENSE. This function is an EH helper. @@ -1398,9 +1397,9 @@ unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) * LOCKING: * Kernel thread context (may sleep). */ -static void ata_eh_request_sense(struct ata_queued_cmd *qc, - struct scsi_cmnd *cmd) +static void ata_eh_request_sense(struct ata_queued_cmd *qc) { + struct scsi_cmnd *cmd = qc->scsicmd; struct ata_device *dev = qc->dev; struct ata_taskfile tf; unsigned int err_mask; @@ -1576,7 +1575,7 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc, switch (qc->dev->class) { case ATA_DEV_ZAC: if (stat & ATA_SENSE) - ata_eh_request_sense(qc, qc->scsicmd); + ata_eh_request_sense(qc); fallthrough; case ATA_DEV_ATA: if (err & ATA_ICRC) From e3b1fff6c051a746679da38f970324d1617590fa Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 16 Sep 2022 14:28:37 +0200 Subject: [PATCH 44/45] ata: libata: drop superfluous ata_eh_analyze_tf() parameter The parameter can easily be derived from struct ata_queued_cmd. Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-eh.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 91c88f429b4f..6432e0489dfb 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1537,7 +1537,6 @@ static void ata_eh_analyze_serror(struct ata_link *link) /** * ata_eh_analyze_tf - analyze taskfile of a failed qc * @qc: qc to analyze - * @tf: Taskfile registers to analyze * * Analyze taskfile of @qc and further determine cause of * failure. This function also requests ATAPI sense data if @@ -1549,9 +1548,9 @@ static void ata_eh_analyze_serror(struct ata_link *link) * RETURNS: * Determined recovery action */ -static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc, - const struct ata_taskfile *tf) +static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc) { + const struct ata_taskfile *tf = &qc->result_tf; unsigned int tmp, action = 0; u8 stat = tf->status, err = tf->error; @@ -1953,7 +1952,7 @@ static void ata_eh_link_autopsy(struct ata_link *link) qc->err_mask |= ehc->i.err_mask; /* analyze TF */ - ehc->i.action |= ata_eh_analyze_tf(qc, &qc->result_tf); + ehc->i.action |= ata_eh_analyze_tf(qc); /* DEV errors are probably spurious in case of ATA_BUS error */ if (qc->err_mask & AC_ERR_ATA_BUS) From 71d7b6e51ad3370d850303b61b79528fb2872f0a Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 21 Sep 2022 17:57:52 +0200 Subject: [PATCH 45/45] ata: libata-eh: avoid needless hard reset when revalidating link Performing a revalidation on a AHCI controller supporting LPM, while using a lpm mode of e.g. med_power_with_dip (hipm + dipm) or medium_power (hipm), will currently always lead to a hard reset. The expected behavior is that a hard reset is only performed when revalidate fails, because the properties of the drive has changed. A revalidate performed after e.g. a NCQ error, or such a simple thing as disabling write-caching (hdparm -W 0 /dev/sda), should succeed on the first try (and should therefore not cause the link to be reset). This unwarranted hard reset happens because ata_phys_link_offline() returns true for a link that is in deep sleep. Thus the call to ata_phys_link_offline() in ata_eh_revalidate_and_attach() will cause the revalidation to fail, which causes ata_eh_handle_dev_fail() to be called, which will set ehc->i.action |= ATA_EH_RESET, such that the link is reset before retrying revalidation. When the link is reset, the link is reestablished, so when ata_eh_revalidate_and_attach() is called the second time, directly after the link has been reset, ata_phys_link_offline() will return false, and the revalidation will succeed. Looking at "8.3.1.3 HBA Initiated" in the AHCI 1.3.1 specification, it is clear the when host software writes a new command to memory, by setting a bit in the PxCI/PxSACT HBA port registers, the HBA will automatically bring back the link before sending out the Command FIS. However, simply reading a SCR (like ata_phys_link_offline() does), will not cause the HBA to automatically bring back the link. As long as hipm is enabled, the HBA will put an idle link into deep sleep. Avoid this needless hard reset on revalidation by temporarily disabling hipm, by setting the LPM mode to ATA_LPM_MAX_POWER. After revalidation is complete, ata_eh_recover() will restore the link policy by setting the LPM mode to ap->target_lpm_policy. Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-eh.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 6432e0489dfb..1b82283f4b49 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -151,6 +151,8 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = { #undef CMDS static void __ata_port_freeze(struct ata_port *ap); +static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + struct ata_device **r_failed_dev); #ifdef CONFIG_PM static void ata_eh_handle_port_suspend(struct ata_port *ap); static void ata_eh_handle_port_resume(struct ata_port *ap); @@ -2934,6 +2936,23 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link, if ((action & ATA_EH_REVALIDATE) && ata_dev_enabled(dev)) { WARN_ON(dev->class == ATA_DEV_PMP); + /* + * The link may be in a deep sleep, wake it up. + * + * If the link is in deep sleep, ata_phys_link_offline() + * will return true, causing the revalidation to fail, + * which leads to a (potentially) needless hard reset. + * + * ata_eh_recover() will later restore the link policy + * to ap->target_lpm_policy after revalidation is done. + */ + if (link->lpm_policy > ATA_LPM_MAX_POWER) { + rc = ata_eh_set_lpm(link, ATA_LPM_MAX_POWER, + r_failed_dev); + if (rc) + goto err; + } + if (ata_phys_link_offline(ata_dev_phys_link(dev))) { rc = -EIO; goto err;