]> git.itanic.dy.fi Git - linux-stable/commitdiff
ata: ahci-dwc: Add Baikal-T1 AHCI SATA interface support
authorSerge Semin <Sergey.Semin@baikalelectronics.ru>
Fri, 9 Sep 2022 19:36:20 +0000 (22:36 +0300)
committerDamien Le Moal <damien.lemoal@opensource.wdc.com>
Fri, 16 Sep 2022 16:40:31 +0000 (01:40 +0900)
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 <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
drivers/ata/Kconfig
drivers/ata/ahci_dwc.c

index 12dd147e434e6e8b938c486577dc88aceec0d88d..1a8a1bbc8a0e8f743b120371a902e075c7bc775e 100644 (file)
@@ -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.
index 6e64d3502669fe7491af333d3010e9f02bedfa6e..8fb66860db318d57dd86aab3a895427eb4707e36 100644 (file)
 #include <linux/kernel.h>
 #include <linux/libata.h>
 #include <linux/log2.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/pm.h>
+#include <linux/regmap.h>
 
 #include "ahci.h"
 
 #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);