summaryrefslogtreecommitdiff
path: root/drivers/ata/libahci_platform.c
diff options
context:
space:
mode:
authorSerge Semin <Sergey.Semin@baikalelectronics.ru>2022-09-09 22:36:14 +0300
committerDamien Le Moal <damien.lemoal@opensource.wdc.com>2022-09-16 19:40:02 +0300
commit18ee7c49f75b642beb6f8d7efdae3a47068d4aa3 (patch)
tree08902a5eb3b0ae8fae4df001fb663647999eb7ad /drivers/ata/libahci_platform.c
parent7cbbfbe01a7284f8003f7a013abb9ece01cb6393 (diff)
downloadlinux-18ee7c49f75b642beb6f8d7efdae3a47068d4aa3.tar.xz
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 <Sergey.Semin@baikalelectronics.ru> Signed-off-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
Diffstat (limited to 'drivers/ata/libahci_platform.c')
-rw-r--r--drivers/ata/libahci_platform.c40
1 files changed, 37 insertions, 3 deletions
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;