All of lore.kernel.org
 help / color / mirror / Atom feed
From: Serge Semin <Sergey.Semin@baikalelectronics.ru>
To: Damien Le Moal <damien.lemoal@opensource.wdc.com>,
	Hans de Goede <hdegoede@redhat.com>, Jens Axboe <axboe@kernel.dk>,
	Hannes Reinecke <hare@suse.de>
Cc: Serge Semin <Sergey.Semin@baikalelectronics.ru>,
	Serge Semin <fancer.lancer@gmail.com>,
	Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>,
	Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>,
	Rob Herring <robh+dt@kernel.org>, <linux-ide@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <devicetree@vger.kernel.org>
Subject: [PATCH v4 16/23] ata: ahci: Introduce firmware-specific caps initialization
Date: Fri, 10 Jun 2022 11:17:54 +0300	[thread overview]
Message-ID: <20220610081801.11854-17-Sergey.Semin@baikalelectronics.ru> (raw)
In-Reply-To: <20220610081801.11854-1-Sergey.Semin@baikalelectronics.ru>

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 <Sergey.Semin@baikalelectronics.ru>

---

Changelog v4:
- Convert the boolean properties to the bitfield DT-properties. (@Rob)
---
 drivers/ata/ahci.h             |  1 +
 drivers/ata/libahci.c          | 51 ++++++++++++++++++++++++++++------
 drivers/ata/libahci_platform.c | 41 +++++++++++++++++++++++++--
 3 files changed, 82 insertions(+), 11 deletions(-)

diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 8b9826533ae5..0de221055961 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -337,6 +337,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 <linux/bitops.h>
 #include <linux/kernel.h>
 #include <linux/gfp.h>
 #include <linux/module.h>
@@ -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.
+	 */
+
+	/*
+	 * 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.
 	 */
-	hpriv->saved_cap = cap = readl(mmio + HOST_CAP);
+	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 efe640603f3f..8b542a8bc487 100644
--- a/drivers/ata/libahci_platform.c
+++ b/drivers/ata/libahci_platform.c
@@ -23,6 +23,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/of_platform.h>
 #include <linux/reset.h>
+
 #include "ahci.h"
 
 static void ahci_host_stop(struct ata_host *host);
@@ -383,6 +384,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
@@ -523,9 +552,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;
@@ -590,6 +616,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;
-- 
2.35.1


  parent reply	other threads:[~2022-06-10  8:18 UTC|newest]

Thread overview: 80+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-06-10  8:17 [PATCH v4 00/23] ata: ahci: Add DWC/Baikal-T1 AHCI SATA support Serge Semin
2022-06-10  8:17 ` [PATCH v4 01/23] dt-bindings: ata: ahci-platform: Move dma-coherent to sata-common.yaml Serge Semin
2022-06-14 22:02   ` Rob Herring
2022-06-14 22:15   ` Florian Fainelli
2022-06-10  8:17 ` [PATCH v4 02/23] dt-bindings: ata: ahci-platform: Detach common AHCI bindings Serge Semin
2022-06-14 22:16   ` Rob Herring
2022-06-10  8:17 ` [PATCH v4 03/23] dt-bindings: ata: ahci-platform: Clarify common AHCI props constraints Serge Semin
2022-06-14 22:17   ` Rob Herring
2022-06-10  8:17 ` [PATCH v4 04/23] dt-bindings: ata: sata: Extend number of SATA ports Serge Semin
2022-06-10  8:17 ` [PATCH v4 05/23] dt-bindings: ata: sata-brcm: Apply common AHCI schema Serge Semin
2022-06-14 22:15   ` Florian Fainelli
2022-06-14 22:17   ` Rob Herring
2022-06-10  8:17 ` [PATCH v4 06/23] ata: libahci_platform: Convert to using platform devm-ioremap methods Serge Semin
2022-06-10  8:17 ` [PATCH v4 07/23] ata: libahci_platform: Convert to using devm bulk clocks API Serge Semin
2022-06-14  8:22   ` Damien Le Moal
2022-06-15 20:45     ` Serge Semin
2022-06-16  0:23       ` Damien Le Moal
2022-06-17 19:54         ` Serge Semin
2022-06-10  8:17 ` [PATCH v4 08/23] ata: libahci_platform: Sanity check the DT child nodes number Serge Semin
2022-06-14  8:23   ` Damien Le Moal
2022-06-15 20:53     ` Serge Semin
2022-06-16  0:25       ` Damien Le Moal
2022-06-17 20:18         ` Serge Semin
2022-06-18  6:49           ` Damien Le Moal
2022-06-10  8:17 ` [PATCH v4 09/23] ata: libahci_platform: Parse ports-implemented property in resources getter Serge Semin
2022-06-10  8:17   ` Serge Semin
2022-06-10  8:17   ` Serge Semin
2022-06-10  8:17 ` [PATCH v4 10/23] ata: libahci_platform: Introduce reset assertion/deassertion methods Serge Semin
2022-06-10  8:17 ` [PATCH v4 11/23] dt-bindings: ata: ahci: Add platform capability properties Serge Semin
2022-06-14 22:19   ` Rob Herring
2022-06-15 21:56     ` Serge Semin
2022-06-10  8:17 ` [PATCH v4 12/23] ata: libahci: Extend port-cmd flags set with port capabilities Serge Semin
2022-06-14  8:32   ` Damien Le Moal
2022-06-15 20:58     ` Serge Semin
2022-06-16  0:28       ` Damien Le Moal
2022-06-17 20:31         ` Serge Semin
2022-06-18  6:52           ` Damien Le Moal
2022-06-18  8:10             ` Serge Semin
2022-06-28 12:08               ` Serge Semin
2022-06-29  1:35                 ` Damien Le Moal
2022-06-29  1:47                   ` Serge Semin
2022-06-10  8:17 ` [PATCH v4 13/23] ata: libahci: Discard redundant force_port_map parameter Serge Semin
2022-06-10  8:17 ` [PATCH v4 14/23] ata: libahci: Don't read AHCI version twice in the save-config method Serge Semin
2022-06-10  8:17 ` [PATCH v4 15/23] ata: ahci: Convert __ahci_port_base to accepting hpriv as arguments Serge Semin
2022-06-14  8:38   ` Damien Le Moal
2022-06-15 21:25     ` Serge Semin
2022-06-10  8:17 ` Serge Semin [this message]
2022-06-14  8:42   ` [PATCH v4 16/23] ata: ahci: Introduce firmware-specific caps initialization Damien Le Moal
2022-06-15 21:11     ` Serge Semin
2022-06-16  0:29       ` Damien Le Moal
2022-06-17 20:32         ` Serge Semin
2022-06-10  8:17 ` [PATCH v4 17/23] dt-bindings: ata: ahci: Add DWC AHCI SATA controller DT schema Serge Semin
2022-06-14 22:27   ` Rob Herring
2022-06-17 19:37     ` Serge Semin
2022-06-28 12:10       ` Serge Semin
2022-07-06 22:36       ` Rob Herring
2022-07-07 15:25         ` Serge Semin
2022-07-12 20:13           ` Rob Herring
2022-07-12 20:43             ` Serge Semin
2022-06-10  8:17 ` [PATCH v4 18/23] ata: libahci_platform: Add function returning a clock-handle by id Serge Semin
2022-06-14  8:45   ` Damien Le Moal
2022-06-15 21:24     ` Serge Semin
2022-06-10  8:17 ` [PATCH v4 19/23] ata: ahci: Add DWC AHCI SATA controller support Serge Semin
2022-06-10 16:34   ` Randy Dunlap
2022-06-10 21:58     ` Serge Semin
2022-06-10 23:34       ` Randy Dunlap
2022-06-15 21:30         ` Serge Semin
2022-06-16  0:31           ` Damien Le Moal
2022-06-17 20:36             ` Serge Semin
2022-06-18  6:54               ` Damien Le Moal
2022-06-14  8:53   ` Damien Le Moal
2022-06-15 21:48     ` Serge Semin
2022-06-16  0:33       ` Damien Le Moal
2022-06-17 20:34         ` Serge Semin
2022-06-10  8:17 ` [PATCH v4 20/23] dt-bindings: ata: ahci: Add Baikal-T1 AHCI SATA controller DT schema Serge Semin
2022-06-14 22:29   ` Rob Herring
2022-06-17 19:49     ` Serge Semin
2022-06-10  8:17 ` [PATCH v4 21/23] ata: ahci-dwc: Add platform-specific quirks support Serge Semin
2022-06-10  8:18 ` [PATCH v4 22/23] ata: ahci-dwc: Add Baikal-T1 AHCI SATA interface support Serge Semin
2022-06-10  8:18 ` [PATCH v4 23/23] MAINTAINERS: Add maintainers for DWC AHCI SATA driver Serge Semin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220610081801.11854-17-Sergey.Semin@baikalelectronics.ru \
    --to=sergey.semin@baikalelectronics.ru \
    --cc=Alexey.Malahov@baikalelectronics.ru \
    --cc=Pavel.Parkhomenko@baikalelectronics.ru \
    --cc=axboe@kernel.dk \
    --cc=damien.lemoal@opensource.wdc.com \
    --cc=devicetree@vger.kernel.org \
    --cc=fancer.lancer@gmail.com \
    --cc=hare@suse.de \
    --cc=hdegoede@redhat.com \
    --cc=linux-ide@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=robh+dt@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.