From dcf3c1c1e8c7dcb4e129ad370b2aee6b8a7dd4cf Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 16 Feb 2018 16:01:12 +0100 Subject: pata_arasan_cf: Delete an error message for a failed memory allocation in arasan_cf_probe() Omit an extra message for a memory allocation failure in this function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Tejun Heo --- drivers/ata/pata_arasan_cf.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index b4d54771c9fe..be5fbcedecbf 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -809,10 +809,8 @@ static int arasan_cf_probe(struct platform_device *pdev) } acdev = devm_kzalloc(&pdev->dev, sizeof(*acdev), GFP_KERNEL); - if (!acdev) { - dev_warn(&pdev->dev, "kzalloc fail\n"); + if (!acdev) return -ENOMEM; - } if (pdata) quirk = pdata->quirk; -- cgit v1.2.3 From 27c4203a3d48cf161fbd86f2ee48f4eb286b73db Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 16 Feb 2018 16:28:23 +0100 Subject: pata_arasan_cf: Delete an unnecessary variable initialisation in arasan_cf_probe() The local variable "ret" will eventually be set to an appropriate value a bit later. Thus omit the explicit initialisation at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Tejun Heo --- drivers/ata/pata_arasan_cf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index be5fbcedecbf..ebecab8c3f36 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -796,7 +796,7 @@ static int arasan_cf_probe(struct platform_device *pdev) struct resource *res; u32 quirk; irq_handler_t irq_handler = NULL; - int ret = 0; + int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) -- cgit v1.2.3 From b02b54914b2f2eb2c310497cfabb054e95fa1e2e Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 16 Feb 2018 10:55:51 +0100 Subject: pata_samsung_cf: Delete an error message for a failed memory allocation in pata_s3c_probe() Omit an extra message for a memory allocation failure in this function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Tejun Heo --- drivers/ata/pata_samsung_cf.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c index 50801c40b029..bb96dc35950d 100644 --- a/drivers/ata/pata_samsung_cf.c +++ b/drivers/ata/pata_samsung_cf.c @@ -505,10 +505,8 @@ static int __init pata_s3c_probe(struct platform_device *pdev) cpu_type = platform_get_device_id(pdev)->driver_data; info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); - if (!info) { - dev_err(dev, "failed to allocate memory for device data\n"); + if (!info) return -ENOMEM; - } info->irq = platform_get_irq(pdev, 0); -- cgit v1.2.3 From 2c7d7598206d090b48796e66e77a40ed54537310 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 15 Feb 2018 22:15:25 +0100 Subject: sata_dwc_460ex: Delete an error message for a failed memory allocation in sata_dwc_port_start() Omit an extra message for a memory allocation failure in this function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Acked-by: Andy Shevchenko Signed-off-by: Tejun Heo --- drivers/ata/sata_dwc_460ex.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 6af4ec3c88c3..ce3d6674ef80 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -901,7 +901,6 @@ static int sata_dwc_port_start(struct ata_port *ap) /* Allocate Port Struct */ hsdevp = kzalloc(sizeof(*hsdevp), GFP_KERNEL); if (!hsdevp) { - dev_err(ap->dev, "%s: kmalloc failed for hsdevp\n", __func__); err = -ENOMEM; goto CLEANUP; } -- cgit v1.2.3 From 533013d69b509b26ff1b38a7d494db54549ac71b Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 16 Feb 2018 11:34:53 +0100 Subject: pata_mpc52xx: Delete an error message for a failed memory allocation in mpc52xx_ata_probe() Omit an extra message for a memory allocation failure in this function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Tejun Heo --- drivers/ata/pata_mpc52xx.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index 9730125530f6..f1d352d5f128 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -738,7 +738,6 @@ static int mpc52xx_ata_probe(struct platform_device *op) /* Prepare our private structure */ priv = devm_kzalloc(&op->dev, sizeof(*priv), GFP_ATOMIC); if (!priv) { - dev_err(&op->dev, "error allocating private structure\n"); rv = -ENOMEM; goto err1; } -- cgit v1.2.3 From 3e342770a22b84c0246719d22c318c22ae2f782a Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 16 Feb 2018 13:01:45 +0100 Subject: pata_macio: Delete an error message for a failed memory allocation in two functions Omit an extra message for a memory allocation failure in these functions. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Tejun Heo --- drivers/ata/pata_macio.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c index 0adcb40d2794..9588e685d994 100644 --- a/drivers/ata/pata_macio.c +++ b/drivers/ata/pata_macio.c @@ -1131,11 +1131,9 @@ static int pata_macio_attach(struct macio_dev *mdev, /* Allocate and init private data structure */ priv = devm_kzalloc(&mdev->ofdev.dev, sizeof(struct pata_macio_priv), GFP_KERNEL); - if (priv == NULL) { - dev_err(&mdev->ofdev.dev, - "Failed to allocate private memory\n"); + if (!priv) return -ENOMEM; - } + priv->node = of_node_get(mdev->ofdev.dev.of_node); priv->mdev = mdev; priv->dev = &mdev->ofdev.dev; @@ -1277,11 +1275,9 @@ static int pata_macio_pci_attach(struct pci_dev *pdev, /* Allocate and init private data structure */ priv = devm_kzalloc(&pdev->dev, sizeof(struct pata_macio_priv), GFP_KERNEL); - if (priv == NULL) { - dev_err(&pdev->dev, - "Failed to allocate private memory\n"); + if (!priv) return -ENOMEM; - } + priv->node = of_node_get(np); priv->pdev = pdev; priv->dev = &pdev->dev; -- cgit v1.2.3 From ef59a62afdd223dadf74a22e994ab79d6bf4f2df Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 16 Feb 2018 14:04:49 +0100 Subject: pata_it821x: Delete an error message for a failed memory allocation in it821x_firmware_command() Omit an extra message for a memory allocation failure in this function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Tejun Heo --- drivers/ata/pata_it821x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c index 8468b300193b..9bac79edbc2c 100644 --- a/drivers/ata/pata_it821x.c +++ b/drivers/ata/pata_it821x.c @@ -658,10 +658,10 @@ static u8 *it821x_firmware_command(struct ata_port *ap, u8 cmd, int len) u8 status; int n = 0; u16 *buf = kmalloc(len, GFP_KERNEL); - if (buf == NULL) { - printk(KERN_ERR "it821x_firmware_command: Out of memory\n"); + + if (!buf) return NULL; - } + /* This isn't quite a normal ATA command as we are talking to the firmware not the drives */ ap->ctl |= ATA_NIEN; -- cgit v1.2.3 From 7f1d5c9dea8a641bd4a8d600c5feb98ae842c5da Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 1 Mar 2018 12:13:35 +0100 Subject: pata_falcon: clarify license version and use SPDX header - clarify license version (it should be GPL 2.0) - use SPDX header Cc: Michael Schmitz Acked-by: Geert Uytterhoeven Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Tejun Heo --- drivers/ata/pata_falcon.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/ata/pata_falcon.c b/drivers/ata/pata_falcon.c index 5b0c57d1c59f..41e0d6a6cd05 100644 --- a/drivers/ata/pata_falcon.c +++ b/drivers/ata/pata_falcon.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0 + /* * Atari Falcon PATA controller driver * @@ -7,10 +9,6 @@ * Based on falconide.c: * * Created 12 Jul 1997 by Geert Uytterhoeven - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. */ #include @@ -180,5 +178,5 @@ module_init(pata_falcon_init_one); MODULE_AUTHOR("Bartlomiej Zolnierkiewicz"); MODULE_DESCRIPTION("low-level driver for Atari Falcon PATA"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_VERSION(DRV_VERSION); -- cgit v1.2.3 From a80ea4cb944efc38e490a172e7afe635b2800db3 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 1 Mar 2018 12:16:00 +0100 Subject: pata_bk3710: clarify license version and use SPDX header - clarify license version (it should be GPL 2.0) - use SPDX header Acked-by: Sekhar Nori Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Tejun Heo --- drivers/ata/pata_bk3710.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/ata/pata_bk3710.c b/drivers/ata/pata_bk3710.c index 6c3bd5fae3e4..fad95cfecced 100644 --- a/drivers/ata/pata_bk3710.c +++ b/drivers/ata/pata_bk3710.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0 + /* * Palmchip BK3710 PATA controller driver * @@ -8,10 +10,6 @@ * * Copyright (C) 2006 Texas Instruments. * Copyright (C) 2007 MontaVista Software, Inc., - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. */ #include @@ -379,4 +377,4 @@ static int __init pata_bk3710_init(void) } module_init(pata_bk3710_init); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 2623c7a5f2799569d8bb05eb211da524a8144cb3 Mon Sep 17 00:00:00 2001 From: Taras Kondratiuk Date: Fri, 9 Mar 2018 08:34:41 +0000 Subject: libata: add refcounting to ata_host After commit 9a6d6a2ddabb ("ata: make ata port as parent device of scsi host") manual driver unbind/remove causes use-after-free. Unbind unconditionally invokes devres_release_all() which calls ata_host_release() and frees ata_host/ata_port memory while it is still being referenced as a parent of SCSI host. When SCSI host is finally released scsi_host_dev_release() calls put_device(parent) and accesses freed ata_port memory. Add reference counting to make sure that ata_host lives long enough. Bug report: https://lkml.org/lkml/2017/11/1/945 Fixes: 9a6d6a2ddabb ("ata: make ata port as parent device of scsi host") Cc: Tejun Heo Cc: Lin Ming Cc: linux-ide@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Taras Kondratiuk Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 44 ++++++++++++++++++++++++++++++++++-------- drivers/ata/libata-transport.c | 4 ++++ drivers/ata/libata.h | 2 ++ include/linux/libata.h | 1 + 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 61b09968d032..b8b85bf97288 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6005,7 +6005,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host) return ap; } -static void ata_host_release(struct device *gendev, void *res) +static void ata_devres_release(struct device *gendev, void *res) { struct ata_host *host = dev_get_drvdata(gendev); int i; @@ -6019,13 +6019,36 @@ static void ata_host_release(struct device *gendev, void *res) if (ap->scsi_host) scsi_host_put(ap->scsi_host); + } + + dev_set_drvdata(gendev, NULL); + ata_host_put(host); +} + +static void ata_host_release(struct kref *kref) +{ + struct ata_host *host = container_of(kref, struct ata_host, kref); + int i; + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + kfree(ap->pmp_link); kfree(ap->slave_link); kfree(ap); host->ports[i] = NULL; } + kfree(host); +} - dev_set_drvdata(gendev, NULL); +void ata_host_get(struct ata_host *host) +{ + kref_get(&host->kref); +} + +void ata_host_put(struct ata_host *host) +{ + kref_put(&host->kref, ata_host_release); } /** @@ -6053,26 +6076,31 @@ struct ata_host *ata_host_alloc(struct device *dev, int max_ports) struct ata_host *host; size_t sz; int i; + void *dr; DPRINTK("ENTER\n"); - if (!devres_open_group(dev, NULL, GFP_KERNEL)) - return NULL; - /* alloc a container for our list of ATA ports (buses) */ sz = sizeof(struct ata_host) + (max_ports + 1) * sizeof(void *); - /* alloc a container for our list of ATA ports (buses) */ - host = devres_alloc(ata_host_release, sz, GFP_KERNEL); + host = kzalloc(sz, GFP_KERNEL); if (!host) + return NULL; + + if (!devres_open_group(dev, NULL, GFP_KERNEL)) + return NULL; + + dr = devres_alloc(ata_devres_release, 0, GFP_KERNEL); + if (!dr) goto err_out; - devres_add(dev, host); + devres_add(dev, dr); dev_set_drvdata(dev, host); spin_lock_init(&host->lock); mutex_init(&host->eh_mutex); host->dev = dev; host->n_ports = max_ports; + kref_init(&host->kref); /* allocate ports bound to this host */ for (i = 0; i < max_ports; i++) { diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 19e6e539a061..a0b0b4d986f2 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -224,6 +224,8 @@ static DECLARE_TRANSPORT_CLASS(ata_port_class, static void ata_tport_release(struct device *dev) { + struct ata_port *ap = tdev_to_port(dev); + ata_host_put(ap->host); } /** @@ -284,6 +286,7 @@ int ata_tport_add(struct device *parent, dev->type = &ata_port_type; dev->parent = parent; + ata_host_get(ap->host); dev->release = ata_tport_release; dev_set_name(dev, "ata%d", ap->print_id); transport_setup_device(dev); @@ -314,6 +317,7 @@ int ata_tport_add(struct device *parent, tport_err: transport_destroy_device(dev); put_device(dev); + ata_host_put(ap->host); return error; } diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index f953cb4bb1ba..9e21c49cf6be 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -100,6 +100,8 @@ extern int ata_port_probe(struct ata_port *ap); extern void __ata_port_probe(struct ata_port *ap); extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log, u8 page, void *buf, unsigned int sectors); +extern void ata_host_get(struct ata_host *host); +extern void ata_host_put(struct ata_host *host); #define to_ata_port(d) container_of(d, struct ata_port, tdev) diff --git a/include/linux/libata.h b/include/linux/libata.h index ed9826b21c5e..1795fecdea17 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -617,6 +617,7 @@ struct ata_host { void *private_data; struct ata_port_operations *ops; unsigned long flags; + struct kref kref; struct mutex eh_mutex; struct task_struct *eh_owner; -- cgit v1.2.3 From 6431243f1506d1cd964a6c54d3d95e87a30f8a93 Mon Sep 17 00:00:00 2001 From: Preetham Ramchandra Date: Mon, 12 Mar 2018 17:10:30 +0530 Subject: dt-bindings: Tegra210: add binding documentation This adds bindings documentation for the AHCI controller on Tegra210. Signed-off-by: Preetham Chandru R Acked-by: Thierry Reding Signed-off-by: Tejun Heo --- .../bindings/ata/nvidia,tegra124-ahci.txt | 36 ++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/ata/nvidia,tegra124-ahci.txt b/Documentation/devicetree/bindings/ata/nvidia,tegra124-ahci.txt index 66c83c3e8915..12ab2f723eb0 100644 --- a/Documentation/devicetree/bindings/ata/nvidia,tegra124-ahci.txt +++ b/Documentation/devicetree/bindings/ata/nvidia,tegra124-ahci.txt @@ -1,9 +1,10 @@ -Tegra124 SoC SATA AHCI controller +Tegra SoC SATA AHCI controller Required properties : -- compatible : For Tegra124, must contain "nvidia,tegra124-ahci". Otherwise, - must contain '"nvidia,-ahci", "nvidia,tegra124-ahci"', where - is tegra132. +- compatible : Must be one of: + - Tegra124 : "nvidia,tegra124-ahci" + - Tegra132 : "nvidia,tegra132-ahci", "nvidia,tegra124-ahci" + - Tegra210 : "nvidia,tegra210-ahci" - reg : Should contain 2 entries: - AHCI register set (SATA BAR5) - SATA register set @@ -13,8 +14,6 @@ Required properties : - clock-names : Must include the following entries: - sata - sata-oob - - cml1 - - pll_e - resets : Must contain an entry for each entry in reset-names. See ../reset/reset.txt for details. - reset-names : Must include the following entries: @@ -24,9 +23,22 @@ Required properties : - phys : Must contain an entry for each entry in phy-names. See ../phy/phy-bindings.txt for details. - phy-names : Must include the following entries: - - sata-phy : XUSB PADCTL SATA PHY -- hvdd-supply : Defines the SATA HVDD regulator -- vddio-supply : Defines the SATA VDDIO regulator -- avdd-supply : Defines the SATA AVDD regulator -- target-5v-supply : Defines the SATA 5V power regulator -- target-12v-supply : Defines the SATA 12V power regulator + - For Tegra124 and Tegra132: + - sata-phy : XUSB PADCTL SATA PHY +- For Tegra124 and Tegra132: + - hvdd-supply : Defines the SATA HVDD regulator + - vddio-supply : Defines the SATA VDDIO regulator + - avdd-supply : Defines the SATA AVDD regulator + - target-5v-supply : Defines the SATA 5V power regulator + - target-12v-supply : Defines the SATA 12V power regulator + +Optional properties: +- reg : + - AUX register set +- clock-names : + - cml1 : + cml1 clock should be defined here if the PHY driver + doesn't manage them. If it does, they should not be. +- phy-names : + - For T210: + - sata-phy -- cgit v1.2.3 From 56337b5576074ece867054b1ec231017a1a0a6b8 Mon Sep 17 00:00:00 2001 From: Preetham Ramchandra Date: Mon, 12 Mar 2018 17:10:33 +0530 Subject: ata: ahci_tegra: Update initialization sequence Update the controller initialization sequence and move Tegra124 specifics to tegra124_ahci_init. Signed-off-by: Preetham Chandru R Acked-by: Thierry Reding Signed-off-by: Tejun Heo --- drivers/ata/ahci_tegra.c | 288 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 224 insertions(+), 64 deletions(-) diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c index 3a62eb246d80..055c65082a93 100644 --- a/drivers/ata/ahci_tegra.c +++ b/drivers/ata/ahci_tegra.c @@ -34,7 +34,8 @@ #define DRV_NAME "tegra-ahci" #define SATA_CONFIGURATION_0 0x180 -#define SATA_CONFIGURATION_EN_FPCI BIT(0) +#define SATA_CONFIGURATION_0_EN_FPCI BIT(0) +#define SATA_CONFIGURATION_0_CLK_OVERRIDE BIT(31) #define SCFG_OFFSET 0x1000 @@ -45,17 +46,55 @@ #define T_SATA0_CFG_1_SERR BIT(8) #define T_SATA0_CFG_9 0x24 -#define T_SATA0_CFG_9_BASE_ADDRESS_SHIFT 13 +#define T_SATA0_CFG_9_BASE_ADDRESS 0x40020000 #define SATA_FPCI_BAR5 0x94 -#define SATA_FPCI_BAR5_START_SHIFT 4 +#define SATA_FPCI_BAR5_START_MASK (0xfffffff << 4) +#define SATA_FPCI_BAR5_START (0x0040020 << 4) +#define SATA_FPCI_BAR5_ACCESS_TYPE (0x1) #define SATA_INTR_MASK 0x188 #define SATA_INTR_MASK_IP_INT_MASK BIT(16) +#define T_SATA0_CFG_35 0x94 +#define T_SATA0_CFG_35_IDP_INDEX_MASK (0x7ff << 2) +#define T_SATA0_CFG_35_IDP_INDEX (0x2a << 2) + +#define T_SATA0_AHCI_IDP1 0x98 +#define T_SATA0_AHCI_IDP1_DATA (0x400040) + +#define T_SATA0_CFG_PHY_1 0x12c +#define T_SATA0_CFG_PHY_1_PADS_IDDQ_EN BIT(23) +#define T_SATA0_CFG_PHY_1_PAD_PLL_IDDQ_EN BIT(22) + +#define T_SATA0_NVOOB 0x114 +#define T_SATA0_NVOOB_COMMA_CNT_MASK (0xff << 16) +#define T_SATA0_NVOOB_COMMA_CNT (0x07 << 16) +#define T_SATA0_NVOOB_SQUELCH_FILTER_MODE_MASK (0x3 << 24) +#define T_SATA0_NVOOB_SQUELCH_FILTER_MODE (0x1 << 24) +#define T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_MASK (0x3 << 26) +#define T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH (0x3 << 26) + +#define T_SATA_CFG_PHY_0 0x120 +#define T_SATA_CFG_PHY_0_USE_7BIT_ALIGN_DET_FOR_SPD BIT(11) +#define T_SATA_CFG_PHY_0_MASK_SQUELCH BIT(24) + +#define T_SATA0_CFG2NVOOB_2 0x134 +#define T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW_MASK (0x1ff << 18) +#define T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW (0xc << 18) + #define T_SATA0_AHCI_HBA_CAP_BKDR 0x300 +#define T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP BIT(13) +#define T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP BIT(14) +#define T_SATA0_AHCI_HBA_CAP_BKDR_SALP BIT(26) +#define T_SATA0_AHCI_HBA_CAP_BKDR_SUPP_PM BIT(17) +#define T_SATA0_AHCI_HBA_CAP_BKDR_SNCQ BIT(30) #define T_SATA0_BKDOOR_CC 0x4a4 +#define T_SATA0_BKDOOR_CC_CLASS_CODE_MASK (0xffff << 16) +#define T_SATA0_BKDOOR_CC_CLASS_CODE (0x0106 << 16) +#define T_SATA0_BKDOOR_CC_PROG_IF_MASK (0xff << 8) +#define T_SATA0_BKDOOR_CC_PROG_IF (0x01 << 8) #define T_SATA0_CFG_SATA 0x54c #define T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN BIT(12) @@ -82,6 +121,27 @@ #define T_SATA0_CHX_PHY_CTRL11 0x6d0 #define T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ (0x2800 << 16) +#define T_SATA0_CHX_PHY_CTRL17_0 0x6e8 +#define T_SATA0_CHX_PHY_CTRL17_0_RX_EQ_CTRL_L_GEN1 0x55010000 +#define T_SATA0_CHX_PHY_CTRL18_0 0x6ec +#define T_SATA0_CHX_PHY_CTRL18_0_RX_EQ_CTRL_L_GEN2 0x55010000 +#define T_SATA0_CHX_PHY_CTRL20_0 0x6f4 +#define T_SATA0_CHX_PHY_CTRL20_0_RX_EQ_CTRL_H_GEN1 0x1 +#define T_SATA0_CHX_PHY_CTRL21_0 0x6f8 +#define T_SATA0_CHX_PHY_CTRL21_0_RX_EQ_CTRL_H_GEN2 0x1 + +/* AUX Registers */ +#define SATA_AUX_MISC_CNTL_1_0 0x8 +#define SATA_AUX_MISC_CNTL_1_0_DEVSLP_OVERRIDE BIT(17) +#define SATA_AUX_MISC_CNTL_1_0_SDS_SUPPORT BIT(13) +#define SATA_AUX_MISC_CNTL_1_0_DESO_SUPPORT BIT(15) + +#define SATA_AUX_RX_STAT_INT_0 0xc +#define SATA_AUX_RX_STAT_INT_0_SATA_DEVSLP BIT(7) + +#define SATA_AUX_SPARE_CFG0_0 0x18 +#define SATA_AUX_SPARE_CFG0_0_MDAT_TIMER_AFTER_PG_VALID BIT(14) + #define FUSE_SATA_CALIB 0x124 #define FUSE_SATA_CALIB_MASK 0x3 @@ -99,6 +159,14 @@ static const struct sata_pad_calibration tegra124_pad_calibration[] = { {0x14, 0x0e, 0x1a, 0x0e}, }; +struct tegra_ahci_ops { + int (*init)(struct ahci_host_priv *hpriv); +}; + +struct tegra_ahci_soc { + const struct tegra_ahci_ops *ops; +}; + struct tegra_ahci_priv { struct platform_device *pdev; void __iomem *sata_regs; @@ -108,8 +176,53 @@ struct tegra_ahci_priv { /* Needs special handling, cannot use ahci_platform */ struct clk *sata_clk; struct regulator_bulk_data supplies[5]; + const struct tegra_ahci_soc *soc; }; +static int tegra124_ahci_init(struct ahci_host_priv *hpriv) +{ + struct tegra_ahci_priv *tegra = hpriv->plat_data; + struct sata_pad_calibration calib; + int ret; + u32 val; + + /* Pad calibration */ + ret = tegra_fuse_readl(FUSE_SATA_CALIB, &val); + if (ret) + return ret; + + calib = tegra124_pad_calibration[val & FUSE_SATA_CALIB_MASK]; + + writel(BIT(0), tegra->sata_regs + SCFG_OFFSET + T_SATA0_INDEX); + + val = readl(tegra->sata_regs + + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1); + val &= ~T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK; + val &= ~T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK; + val |= calib.gen1_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT; + val |= calib.gen1_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT; + writel(val, tegra->sata_regs + SCFG_OFFSET + + T_SATA0_CHX_PHY_CTRL1_GEN1); + + val = readl(tegra->sata_regs + + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2); + val &= ~T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK; + val &= ~T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK; + val |= calib.gen2_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT; + val |= calib.gen2_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT; + writel(val, tegra->sata_regs + SCFG_OFFSET + + T_SATA0_CHX_PHY_CTRL1_GEN2); + + writel(T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ, + tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL11); + writel(T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1, + tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL2); + + writel(0, tegra->sata_regs + SCFG_OFFSET + T_SATA0_INDEX); + + return 0; +} + static int tegra_ahci_power_on(struct ahci_host_priv *hpriv) { struct tegra_ahci_priv *tegra = hpriv->plat_data; @@ -169,8 +282,7 @@ static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv) { struct tegra_ahci_priv *tegra = hpriv->plat_data; int ret; - unsigned int val; - struct sata_pad_calibration calib; + u32 val; ret = tegra_ahci_power_on(hpriv); if (ret) { @@ -179,78 +291,114 @@ static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv) return ret; } + /* + * Program the following SATA IPFS registers to allow SW accesses to + * SATA's MMIO register range. + */ + val = readl(tegra->sata_regs + SATA_FPCI_BAR5); + val &= ~(SATA_FPCI_BAR5_START_MASK | SATA_FPCI_BAR5_ACCESS_TYPE); + val |= SATA_FPCI_BAR5_START | SATA_FPCI_BAR5_ACCESS_TYPE; + writel(val, tegra->sata_regs + SATA_FPCI_BAR5); + + /* Program the following SATA IPFS register to enable the SATA */ val = readl(tegra->sata_regs + SATA_CONFIGURATION_0); - val |= SATA_CONFIGURATION_EN_FPCI; + val |= SATA_CONFIGURATION_0_EN_FPCI; writel(val, tegra->sata_regs + SATA_CONFIGURATION_0); - /* Pad calibration */ - - ret = tegra_fuse_readl(FUSE_SATA_CALIB, &val); - if (ret) { - dev_err(&tegra->pdev->dev, - "failed to read calibration fuse: %d\n", ret); - return ret; - } - - calib = tegra124_pad_calibration[val & FUSE_SATA_CALIB_MASK]; - - writel(BIT(0), tegra->sata_regs + SCFG_OFFSET + T_SATA0_INDEX); - - val = readl(tegra->sata_regs + - SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1); - val &= ~T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK; - val &= ~T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK; - val |= calib.gen1_tx_amp << - T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT; - val |= calib.gen1_tx_peak << - T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT; - writel(val, tegra->sata_regs + SCFG_OFFSET + - T_SATA0_CHX_PHY_CTRL1_GEN1); - - val = readl(tegra->sata_regs + - SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2); - val &= ~T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK; - val &= ~T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK; - val |= calib.gen2_tx_amp << - T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT; - val |= calib.gen2_tx_peak << - T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT; - writel(val, tegra->sata_regs + SCFG_OFFSET + - T_SATA0_CHX_PHY_CTRL1_GEN2); - - writel(T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ, - tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL11); - writel(T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1, - tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL2); - - writel(0, tegra->sata_regs + SCFG_OFFSET + T_SATA0_INDEX); - - /* Program controller device ID */ + /* Electrical settings for better link stability */ + val = T_SATA0_CHX_PHY_CTRL17_0_RX_EQ_CTRL_L_GEN1; + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL17_0); + val = T_SATA0_CHX_PHY_CTRL18_0_RX_EQ_CTRL_L_GEN2; + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL18_0); + val = T_SATA0_CHX_PHY_CTRL20_0_RX_EQ_CTRL_H_GEN1; + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL20_0); + val = T_SATA0_CHX_PHY_CTRL21_0_RX_EQ_CTRL_H_GEN2; + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL21_0); + + /* For SQUELCH Filter & Gen3 drive getting detected as Gen1 drive */ + + val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA_CFG_PHY_0); + val |= T_SATA_CFG_PHY_0_MASK_SQUELCH; + val &= ~T_SATA_CFG_PHY_0_USE_7BIT_ALIGN_DET_FOR_SPD; + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA_CFG_PHY_0); + + val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_NVOOB); + val &= ~(T_SATA0_NVOOB_COMMA_CNT_MASK | + T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_MASK | + T_SATA0_NVOOB_SQUELCH_FILTER_MODE_MASK); + val |= (T_SATA0_NVOOB_COMMA_CNT | + T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH | + T_SATA0_NVOOB_SQUELCH_FILTER_MODE); + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_NVOOB); + + /* + * Change CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW from 83.3 ns to 58.8ns + */ + val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG2NVOOB_2); + val &= ~T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW_MASK; + val |= T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW; + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG2NVOOB_2); + + if (tegra->soc->ops && tegra->soc->ops->init) + tegra->soc->ops->init(hpriv); + + /* + * Program the following SATA configuration registers to + * initialize SATA + */ + val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_1); + val |= (T_SATA0_CFG_1_IO_SPACE | T_SATA0_CFG_1_MEMORY_SPACE | + T_SATA0_CFG_1_BUS_MASTER | T_SATA0_CFG_1_SERR); + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_1); + val = T_SATA0_CFG_9_BASE_ADDRESS; + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_9); + /* Program Class Code and Programming interface for SATA */ val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_SATA); val |= T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN; writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_SATA); - writel(0x01060100, tegra->sata_regs + SCFG_OFFSET + T_SATA0_BKDOOR_CC); + val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_BKDOOR_CC); + val &= + ~(T_SATA0_BKDOOR_CC_CLASS_CODE_MASK | + T_SATA0_BKDOOR_CC_PROG_IF_MASK); + val |= T_SATA0_BKDOOR_CC_CLASS_CODE | T_SATA0_BKDOOR_CC_PROG_IF; + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_BKDOOR_CC); val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_SATA); val &= ~T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN; writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_SATA); - /* Enable IO & memory access, bus master mode */ - - val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_1); - val |= T_SATA0_CFG_1_IO_SPACE | T_SATA0_CFG_1_MEMORY_SPACE | - T_SATA0_CFG_1_BUS_MASTER | T_SATA0_CFG_1_SERR; - writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_1); - - /* Program SATA MMIO */ - - writel(0x10000 << SATA_FPCI_BAR5_START_SHIFT, - tegra->sata_regs + SATA_FPCI_BAR5); + /* Enabling LPM capabilities through Backdoor Programming */ + val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_AHCI_HBA_CAP_BKDR); + val |= (T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP | + T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP | + T_SATA0_AHCI_HBA_CAP_BKDR_SALP | + T_SATA0_AHCI_HBA_CAP_BKDR_SUPP_PM); + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_AHCI_HBA_CAP_BKDR); + + /* SATA Second Level Clock Gating configuration + * Enabling Gating of Tx/Rx clocks and driving Pad IDDQ and Lane + * IDDQ Signals + */ + val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_35); + val &= ~T_SATA0_CFG_35_IDP_INDEX_MASK; + val |= T_SATA0_CFG_35_IDP_INDEX; + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_35); + + val = T_SATA0_AHCI_IDP1_DATA; + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_AHCI_IDP1); + + val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_PHY_1); + val |= (T_SATA0_CFG_PHY_1_PADS_IDDQ_EN | + T_SATA0_CFG_PHY_1_PAD_PLL_IDDQ_EN); + writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_PHY_1); + + /* Enabling IPFS Clock Gating */ + val = readl(tegra->sata_regs + SATA_CONFIGURATION_0); + val &= ~SATA_CONFIGURATION_0_CLK_OVERRIDE; + writel(val, tegra->sata_regs + SATA_CONFIGURATION_0); - writel(0x08000 << T_SATA0_CFG_9_BASE_ADDRESS_SHIFT, - tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_9); /* Unmask SATA interrupts */ @@ -285,8 +433,19 @@ static const struct ata_port_info ahci_tegra_port_info = { .port_ops = &ahci_tegra_port_ops, }; +static const struct tegra_ahci_ops tegra124_ahci_ops = { + .init = tegra124_ahci_init, +}; + +static const struct tegra_ahci_soc tegra124_ahci_soc = { + .ops = &tegra124_ahci_ops, +}; + static const struct of_device_id tegra_ahci_of_match[] = { - { .compatible = "nvidia,tegra124-ahci" }, + { + .compatible = "nvidia,tegra124-ahci", + .data = &tegra124_ahci_soc + }, {} }; MODULE_DEVICE_TABLE(of, tegra_ahci_of_match); @@ -313,6 +472,7 @@ static int tegra_ahci_probe(struct platform_device *pdev) hpriv->plat_data = tegra; tegra->pdev = pdev; + tegra->soc = of_device_get_match_data(&pdev->dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); tegra->sata_regs = devm_ioremap_resource(&pdev->dev, res); -- cgit v1.2.3 From 43ee827b562b092f594375945aec9178f9b5cca4 Mon Sep 17 00:00:00 2001 From: Preetham Ramchandra Date: Mon, 12 Mar 2018 17:10:34 +0530 Subject: ata: ahci_tegra: initialize regulators from soc struct Get the regulator names to be initialized from soc structure and initialize them. Signed-off-by: Preetham Chandru R Acked-by: Thierry Reding Signed-off-by: Tejun Heo --- drivers/ata/ahci_tegra.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c index 055c65082a93..547a6f93922c 100644 --- a/drivers/ata/ahci_tegra.c +++ b/drivers/ata/ahci_tegra.c @@ -164,6 +164,8 @@ struct tegra_ahci_ops { }; struct tegra_ahci_soc { + const char *const *supply_names; + u32 num_supplies; const struct tegra_ahci_ops *ops; }; @@ -175,7 +177,7 @@ struct tegra_ahci_priv { struct reset_control *sata_cold_rst; /* Needs special handling, cannot use ahci_platform */ struct clk *sata_clk; - struct regulator_bulk_data supplies[5]; + struct regulator_bulk_data *supplies; const struct tegra_ahci_soc *soc; }; @@ -228,7 +230,7 @@ static int tegra_ahci_power_on(struct ahci_host_priv *hpriv) struct tegra_ahci_priv *tegra = hpriv->plat_data; int ret; - ret = regulator_bulk_enable(ARRAY_SIZE(tegra->supplies), + ret = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies); if (ret) return ret; @@ -257,7 +259,7 @@ disable_power: tegra_powergate_power_off(TEGRA_POWERGATE_SATA); disable_regulators: - regulator_bulk_disable(ARRAY_SIZE(tegra->supplies), tegra->supplies); + regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); return ret; } @@ -275,7 +277,7 @@ static void tegra_ahci_power_off(struct ahci_host_priv *hpriv) clk_disable_unprepare(tegra->sata_clk); tegra_powergate_power_off(TEGRA_POWERGATE_SATA); - regulator_bulk_disable(ARRAY_SIZE(tegra->supplies), tegra->supplies); + regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); } static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv) @@ -433,11 +435,17 @@ static const struct ata_port_info ahci_tegra_port_info = { .port_ops = &ahci_tegra_port_ops, }; +static const char *const tegra124_supply_names[] = { + "avdd", "hvdd", "vddio", "target-5v", "target-12v" +}; + static const struct tegra_ahci_ops tegra124_ahci_ops = { .init = tegra124_ahci_init, }; static const struct tegra_ahci_soc tegra124_ahci_soc = { + .supply_names = tegra124_supply_names, + .num_supplies = ARRAY_SIZE(tegra124_supply_names), .ops = &tegra124_ahci_ops, }; @@ -460,6 +468,7 @@ static int tegra_ahci_probe(struct platform_device *pdev) struct tegra_ahci_priv *tegra; struct resource *res; int ret; + unsigned int i; hpriv = ahci_platform_get_resources(pdev); if (IS_ERR(hpriv)) @@ -503,13 +512,17 @@ static int tegra_ahci_probe(struct platform_device *pdev) return PTR_ERR(tegra->sata_clk); } - tegra->supplies[0].supply = "avdd"; - tegra->supplies[1].supply = "hvdd"; - tegra->supplies[2].supply = "vddio"; - tegra->supplies[3].supply = "target-5v"; - tegra->supplies[4].supply = "target-12v"; + tegra->supplies = devm_kcalloc(&pdev->dev, + tegra->soc->num_supplies, + sizeof(*tegra->supplies), GFP_KERNEL); + if (!tegra->supplies) + return -ENOMEM; + + for (i = 0; i < tegra->soc->num_supplies; i++) + tegra->supplies[i].supply = tegra->soc->supply_names[i]; - ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(tegra->supplies), + ret = devm_regulator_bulk_get(&pdev->dev, + tegra->soc->num_supplies, tegra->supplies); if (ret) { dev_err(&pdev->dev, "Failed to get regulators\n"); -- cgit v1.2.3 From 502717ccf7720e785fdc1c9202d1b3930fd08038 Mon Sep 17 00:00:00 2001 From: Preetham Ramchandra Date: Mon, 12 Mar 2018 17:10:35 +0530 Subject: ata: ahci_tegra: disable devslp for Tegra124 Tegra124 does not support devslp and it should be disabled. Signed-off-by: Preetham Chandru R Acked-by: Thierry Reding Signed-off-by: Tejun Heo --- drivers/ata/ahci_tegra.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c index 547a6f93922c..620cdd16ef2f 100644 --- a/drivers/ata/ahci_tegra.c +++ b/drivers/ata/ahci_tegra.c @@ -166,12 +166,14 @@ struct tegra_ahci_ops { struct tegra_ahci_soc { const char *const *supply_names; u32 num_supplies; + bool supports_devslp; const struct tegra_ahci_ops *ops; }; struct tegra_ahci_priv { struct platform_device *pdev; void __iomem *sata_regs; + void __iomem *sata_aux_regs; struct reset_control *sata_rst; struct reset_control *sata_oob_rst; struct reset_control *sata_cold_rst; @@ -181,6 +183,18 @@ struct tegra_ahci_priv { const struct tegra_ahci_soc *soc; }; +static void tegra_ahci_handle_quirks(struct ahci_host_priv *hpriv) +{ + struct tegra_ahci_priv *tegra = hpriv->plat_data; + u32 val; + + if (tegra->sata_aux_regs && !tegra->soc->supports_devslp) { + val = readl(tegra->sata_aux_regs + SATA_AUX_MISC_CNTL_1_0); + val &= ~SATA_AUX_MISC_CNTL_1_0_SDS_SUPPORT; + writel(val, tegra->sata_aux_regs + SATA_AUX_MISC_CNTL_1_0); + } +} + static int tegra124_ahci_init(struct ahci_host_priv *hpriv) { struct tegra_ahci_priv *tegra = hpriv->plat_data; @@ -401,6 +415,7 @@ static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv) val &= ~SATA_CONFIGURATION_0_CLK_OVERRIDE; writel(val, tegra->sata_regs + SATA_CONFIGURATION_0); + tegra_ahci_handle_quirks(hpriv); /* Unmask SATA interrupts */ @@ -446,6 +461,7 @@ static const struct tegra_ahci_ops tegra124_ahci_ops = { static const struct tegra_ahci_soc tegra124_ahci_soc = { .supply_names = tegra124_supply_names, .num_supplies = ARRAY_SIZE(tegra124_supply_names), + .supports_devslp = false, .ops = &tegra124_ahci_ops, }; @@ -488,6 +504,16 @@ static int tegra_ahci_probe(struct platform_device *pdev) if (IS_ERR(tegra->sata_regs)) return PTR_ERR(tegra->sata_regs); + /* + * AUX registers is optional. + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (res) { + tegra->sata_aux_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tegra->sata_aux_regs)) + return PTR_ERR(tegra->sata_aux_regs); + } + tegra->sata_rst = devm_reset_control_get(&pdev->dev, "sata"); if (IS_ERR(tegra->sata_rst)) { dev_err(&pdev->dev, "Failed to get sata reset\n"); -- cgit v1.2.3 From 01fbf60b0e6fb9932a26959bbf338b9b5b193592 Mon Sep 17 00:00:00 2001 From: Preetham Ramchandra Date: Mon, 12 Mar 2018 17:10:36 +0530 Subject: ata: ahci_tegra: disable DIPM Tegra does not support DIPM and it should be disabled. Signed-off-by: Preetham Chandru R Acked-by: Thierry Reding Signed-off-by: Tejun Heo --- drivers/ata/ahci_tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c index 620cdd16ef2f..20c1fccbc669 100644 --- a/drivers/ata/ahci_tegra.c +++ b/drivers/ata/ahci_tegra.c @@ -444,7 +444,7 @@ static struct ata_port_operations ahci_tegra_port_ops = { }; static const struct ata_port_info ahci_tegra_port_info = { - .flags = AHCI_FLAG_COMMON, + .flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_tegra_port_ops, -- cgit v1.2.3 From 294840feefb7fefb49a276544cbd29aac28e5e7d Mon Sep 17 00:00:00 2001 From: Preetham Ramchandra Date: Mon, 12 Mar 2018 17:10:37 +0530 Subject: ata: ahci_tegra: Add AHCI support for Tegra210 Add support for the AHCI-compliant Serial ATA host controller on the Tegra210 system-on-chip. Signed-off-by: Preetham Chandru R Acked-by: Thierry Reding Signed-off-by: Tejun Heo --- drivers/ata/ahci_tegra.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c index 20c1fccbc669..64d848409fe2 100644 --- a/drivers/ata/ahci_tegra.c +++ b/drivers/ata/ahci_tegra.c @@ -465,11 +465,19 @@ static const struct tegra_ahci_soc tegra124_ahci_soc = { .ops = &tegra124_ahci_ops, }; +static const struct tegra_ahci_soc tegra210_ahci_soc = { + .supports_devslp = false, +}; + static const struct of_device_id tegra_ahci_of_match[] = { { .compatible = "nvidia,tegra124-ahci", .data = &tegra124_ahci_soc }, + { + .compatible = "nvidia,tegra210-ahci", + .data = &tegra210_ahci_soc + }, {} }; MODULE_DEVICE_TABLE(of, tegra_ahci_of_match); @@ -584,5 +592,5 @@ static struct platform_driver tegra_ahci_driver = { module_platform_driver(tegra_ahci_driver); MODULE_AUTHOR("Mikko Perttunen "); -MODULE_DESCRIPTION("Tegra124 AHCI SATA driver"); +MODULE_DESCRIPTION("Tegra AHCI SATA driver"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 34d232eb2d0947f1a5430cbb193781e6562496ce Mon Sep 17 00:00:00 2001 From: Preetham Ramchandra Date: Mon, 12 Mar 2018 17:10:38 +0530 Subject: ata: change Tegra124 to Tegra ahci_tegra driver now supports Tegra124, Tegra132 and Tegra210, so change Tegra124 to Tegra. Signed-off-by: Preetham Chandru R Acked-by: Thierry Reding Signed-off-by: Tejun Heo --- drivers/ata/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index a7120d621154..26b79991ba29 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -211,10 +211,10 @@ config AHCI_SUNXI If unsure, say N. config AHCI_TEGRA - tristate "NVIDIA Tegra124 AHCI SATA support" + tristate "NVIDIA Tegra AHCI SATA support" depends on ARCH_TEGRA help - This option enables support for the NVIDIA Tegra124 SoC's + This option enables support for the NVIDIA Tegra SoC's onboard AHCI SATA. If unsure, say N. -- cgit v1.2.3 From e5878732a521dd31ea6377875e49adc424503e5b Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Mon, 19 Mar 2018 10:02:18 +0800 Subject: ahci: imx: add the imx6qp ahci sata support - Regarding to imx6q ahci sata, imx6qp ahci sata has the reset mechanism. Add the imx6qp ahci sata support in this commit. - Use the specific reset callback for imx53 sata, and use the default ahci_ops.softreset for the others. Signed-off-by: Richard Zhu Signed-off-by: Tejun Heo --- Documentation/devicetree/bindings/ata/imx-sata.txt | 1 + drivers/ata/ahci_imx.c | 36 +++++++++++++++++++--- include/linux/mfd/syscon/imx6q-iomuxc-gpr.h | 2 ++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/ata/imx-sata.txt b/Documentation/devicetree/bindings/ata/imx-sata.txt index a3d14719e478..781f88751762 100644 --- a/Documentation/devicetree/bindings/ata/imx-sata.txt +++ b/Documentation/devicetree/bindings/ata/imx-sata.txt @@ -7,6 +7,7 @@ Required properties: - compatible : should be one of the following: - "fsl,imx53-ahci" for i.MX53 SATA controller - "fsl,imx6q-ahci" for i.MX6Q SATA controller + - "fsl,imx6qp-ahci" for i.MX6QP SATA controller - interrupts : interrupt mapping for SATA IRQ - reg : registers mapping - clocks : list of clock specifiers, must contain an entry for each diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index a58bcc069c54..577458fe4aa9 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -58,6 +58,7 @@ enum { enum ahci_imx_type { AHCI_IMX53, AHCI_IMX6Q, + AHCI_IMX6QP, }; struct imx_ahci_priv { @@ -188,11 +189,26 @@ static int imx_phy_reg_read(u16 *val, void __iomem *mmio) static int imx_sata_phy_reset(struct ahci_host_priv *hpriv) { + struct imx_ahci_priv *imxpriv = hpriv->plat_data; void __iomem *mmio = hpriv->mmio; int timeout = 10; u16 val; int ret; + if (imxpriv->type == AHCI_IMX6QP) { + /* 6qp adds the sata reset mechanism, use it for 6qp sata */ + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR5, + IMX6Q_GPR5_SATA_SW_PD, 0); + + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR5, + IMX6Q_GPR5_SATA_SW_RST, 0); + udelay(50); + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR5, + IMX6Q_GPR5_SATA_SW_RST, + IMX6Q_GPR5_SATA_SW_RST); + return 0; + } + /* Reset SATA PHY by setting RESET bit of PHY register CLOCK_RESET */ ret = imx_phy_reg_addressing(IMX_CLOCK_RESET, mmio); if (ret) @@ -408,7 +424,7 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv) if (ret < 0) goto disable_regulator; - if (imxpriv->type == AHCI_IMX6Q) { + if (imxpriv->type == AHCI_IMX6Q || imxpriv->type == AHCI_IMX6QP) { /* * set PHY Paremeters, two steps to configure the GPR13, * one write for rest of parameters, mask of first write @@ -459,10 +475,21 @@ static void imx_sata_disable(struct ahci_host_priv *hpriv) if (imxpriv->no_device) return; - if (imxpriv->type == AHCI_IMX6Q) { + switch (imxpriv->type) { + case AHCI_IMX6QP: + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR5, + IMX6Q_GPR5_SATA_SW_PD, + IMX6Q_GPR5_SATA_SW_PD); regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, IMX6Q_GPR13_SATA_MPLL_CLK_EN, !IMX6Q_GPR13_SATA_MPLL_CLK_EN); + break; + + case AHCI_IMX6Q: + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, + IMX6Q_GPR13_SATA_MPLL_CLK_EN, + !IMX6Q_GPR13_SATA_MPLL_CLK_EN); + break; } clk_disable_unprepare(imxpriv->sata_ref_clk); @@ -513,7 +540,7 @@ static int ahci_imx_softreset(struct ata_link *link, unsigned int *class, if (imxpriv->type == AHCI_IMX53) ret = ahci_pmp_retry_srst_ops.softreset(link, class, deadline); - else if (imxpriv->type == AHCI_IMX6Q) + else ret = ahci_ops.softreset(link, class, deadline); return ret; @@ -536,6 +563,7 @@ static const struct ata_port_info ahci_imx_port_info = { static const struct of_device_id imx_ahci_of_match[] = { { .compatible = "fsl,imx53-ahci", .data = (void *)AHCI_IMX53 }, { .compatible = "fsl,imx6q-ahci", .data = (void *)AHCI_IMX6Q }, + { .compatible = "fsl,imx6qp-ahci", .data = (void *)AHCI_IMX6QP }, {}, }; MODULE_DEVICE_TABLE(of, imx_ahci_of_match); @@ -743,7 +771,7 @@ static int imx_ahci_probe(struct platform_device *pdev) return PTR_ERR(imxpriv->ahb_clk); } - if (imxpriv->type == AHCI_IMX6Q) { + if (imxpriv->type == AHCI_IMX6Q || imxpriv->type == AHCI_IMX6QP) { u32 reg_value; imxpriv->gpr = syscon_regmap_lookup_by_compatible( diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h index c8e0164c5423..e06f5f79eaef 100644 --- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h +++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h @@ -243,6 +243,8 @@ #define IMX6Q_GPR4_IPU_RD_CACHE_CTL BIT(0) #define IMX6Q_GPR5_L2_CLK_STOP BIT(8) +#define IMX6Q_GPR5_SATA_SW_PD BIT(10) +#define IMX6Q_GPR5_SATA_SW_RST BIT(11) #define IMX6Q_GPR6_IPU1_ID00_WR_QOS_MASK (0xf << 0) #define IMX6Q_GPR6_IPU1_ID01_WR_QOS_MASK (0xf << 4) -- cgit v1.2.3 From 9ab27d1d35fda0c5fce624083e92546a8545e7e5 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 16 Mar 2018 17:15:47 +0100 Subject: ata: add Amiga Gayle PATA controller driver Add Amiga Gayle PATA controller driver. It enables libata support for the on-board IDE interfaces on some Amiga models (A600, A1200, A4000 and A4000T) and also for IDE interfaces on the Zorro expansion bus (M-Tech E-Matrix 530 expansion card). Thanks to John Paul Adrian Glaubitz and Michael Schmitz for help with testing the driver. Tested-by: John Paul Adrian Glaubitz Cc: Michael Schmitz Cc: Geert Uytterhoeven Cc: Philippe Ombredanne Cc: Andy Shevchenko Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Tejun Heo --- drivers/ata/Kconfig | 12 +++ drivers/ata/Makefile | 1 + drivers/ata/pata_gayle.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 drivers/ata/pata_gayle.c diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 26b79991ba29..46f5009dcb37 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -963,6 +963,18 @@ config PATA_FALCON If unsure, say N. +config PATA_GAYLE + tristate "Amiga Gayle PATA support" + depends on M68K && AMIGA + help + This option enables support for the on-board IDE + interfaces on some Amiga models (A600, A1200, + A4000 and A4000T) and also for IDE interfaces on + the Zorro expansion bus (M-Tech E-Matrix 530 + expansion card). + + If unsure, say N. + config PATA_ISAPNP tristate "ISA Plug and Play PATA support" depends on ISAPNP diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index f1f5a3fbc777..ab08ba1276af 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -98,6 +98,7 @@ obj-$(CONFIG_PATA_WINBOND) += pata_sl82c105.o # SFF PIO only obj-$(CONFIG_PATA_CMD640_PCI) += pata_cmd640.o obj-$(CONFIG_PATA_FALCON) += pata_falcon.o +obj-$(CONFIG_PATA_GAYLE) += pata_gayle.o obj-$(CONFIG_PATA_ISAPNP) += pata_isapnp.o obj-$(CONFIG_PATA_IXP4XX_CF) += pata_ixp4xx_cf.o obj-$(CONFIG_PATA_MPIIX) += pata_mpiix.o diff --git a/drivers/ata/pata_gayle.c b/drivers/ata/pata_gayle.c new file mode 100644 index 000000000000..65bc9f3042ce --- /dev/null +++ b/drivers/ata/pata_gayle.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Amiga Gayle PATA controller driver + * + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Based on gayle.c: + * + * Created 12 Jul 1997 by Geert Uytterhoeven + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DRV_NAME "pata_gayle" +#define DRV_VERSION "0.1.0" + +#define GAYLE_CONTROL 0x101a + +static struct scsi_host_template pata_gayle_sht = { + ATA_PIO_SHT(DRV_NAME), +}; + +/* FIXME: is this needed? */ +static unsigned int pata_gayle_data_xfer(struct ata_queued_cmd *qc, + unsigned char *buf, + unsigned int buflen, int rw) +{ + struct ata_device *dev = qc->dev; + struct ata_port *ap = dev->link->ap; + void __iomem *data_addr = ap->ioaddr.data_addr; + unsigned int words = buflen >> 1; + + /* Transfer multiple of 2 bytes */ + if (rw == READ) + raw_insw((u16 *)data_addr, (u16 *)buf, words); + else + raw_outsw((u16 *)data_addr, (u16 *)buf, words); + + /* Transfer trailing byte, if any. */ + if (unlikely(buflen & 0x01)) { + unsigned char pad[2] = { }; + + /* Point buf to the tail of buffer */ + buf += buflen - 1; + + if (rw == READ) { + raw_insw((u16 *)data_addr, (u16 *)pad, 1); + *buf = pad[0]; + } else { + pad[0] = *buf; + raw_outsw((u16 *)data_addr, (u16 *)pad, 1); + } + words++; + } + + return words << 1; +} + +/* + * Provide our own set_mode() as we don't want to change anything that has + * already been configured.. + */ +static int pata_gayle_set_mode(struct ata_link *link, + struct ata_device **unused) +{ + struct ata_device *dev; + + ata_for_each_dev(dev, link, ENABLED) { + /* We don't really care */ + dev->pio_mode = dev->xfer_mode = XFER_PIO_0; + dev->xfer_shift = ATA_SHIFT_PIO; + dev->flags |= ATA_DFLAG_PIO; + ata_dev_info(dev, "configured for PIO\n"); + } + return 0; +} + +static bool pata_gayle_irq_check(struct ata_port *ap) +{ + u8 ch; + + ch = z_readb((unsigned long)ap->private_data); + + return !!(ch & GAYLE_IRQ_IDE); +} + +static void pata_gayle_irq_clear(struct ata_port *ap) +{ + (void)z_readb((unsigned long)ap->ioaddr.status_addr); + z_writeb(0x7c, (unsigned long)ap->private_data); +} + +static struct ata_port_operations pata_gayle_a1200_ops = { + .inherits = &ata_sff_port_ops, + .sff_data_xfer = pata_gayle_data_xfer, + .sff_irq_check = pata_gayle_irq_check, + .sff_irq_clear = pata_gayle_irq_clear, + .cable_detect = ata_cable_unknown, + .set_mode = pata_gayle_set_mode, +}; + +static struct ata_port_operations pata_gayle_a4000_ops = { + .inherits = &ata_sff_port_ops, + .sff_data_xfer = pata_gayle_data_xfer, + .cable_detect = ata_cable_unknown, + .set_mode = pata_gayle_set_mode, +}; + +static int __init pata_gayle_init_one(struct platform_device *pdev) +{ + struct resource *res; + struct gayle_ide_platform_data *pdata; + struct ata_host *host; + struct ata_port *ap; + void __iomem *base; + int ret; + + pdata = dev_get_platdata(&pdev->dev); + + dev_info(&pdev->dev, "Amiga Gayle IDE controller (A%u style)\n", + pdata->explicit_ack ? 1200 : 4000); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), DRV_NAME)) { + pr_err(DRV_NAME ": resources busy\n"); + return -EBUSY; + } + + /* allocate host */ + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + return -ENOMEM; + + ap = host->ports[0]; + + if (pdata->explicit_ack) + ap->ops = &pata_gayle_a1200_ops; + else + ap->ops = &pata_gayle_a4000_ops; + + ap->pio_mask = ATA_PIO4; + ap->flags |= ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY; + + base = ZTWO_VADDR(pdata->base); + ap->ioaddr.data_addr = base; + ap->ioaddr.error_addr = base + 2 + 1 * 4; + ap->ioaddr.feature_addr = base + 2 + 1 * 4; + ap->ioaddr.nsect_addr = base + 2 + 2 * 4; + ap->ioaddr.lbal_addr = base + 2 + 3 * 4; + ap->ioaddr.lbam_addr = base + 2 + 4 * 4; + ap->ioaddr.lbah_addr = base + 2 + 5 * 4; + ap->ioaddr.device_addr = base + 2 + 6 * 4; + ap->ioaddr.status_addr = base + 2 + 7 * 4; + ap->ioaddr.command_addr = base + 2 + 7 * 4; + + ap->ioaddr.altstatus_addr = base + GAYLE_CONTROL; + ap->ioaddr.ctl_addr = base + GAYLE_CONTROL; + + ap->private_data = (void *)ZTWO_VADDR(pdata->irqport); + + ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", pdata->base, + pdata->base + GAYLE_CONTROL); + + ret = ata_host_activate(host, IRQ_AMIGA_PORTS, ata_sff_interrupt, + IRQF_SHARED, &pata_gayle_sht); + if (ret) + return ret; + + platform_set_drvdata(pdev, host); + + return 0; +} + +static int __exit pata_gayle_remove_one(struct platform_device *pdev) +{ + struct ata_host *host = platform_get_drvdata(pdev); + + ata_host_detach(host); + + return 0; +} + +static struct platform_driver pata_gayle_driver = { + .remove = __exit_p(pata_gayle_remove_one), + .driver = { + .name = "amiga-gayle-ide", + }, +}; + +module_platform_driver_probe(pata_gayle_driver, pata_gayle_init_one); + +MODULE_AUTHOR("Bartlomiej Zolnierkiewicz"); +MODULE_DESCRIPTION("low-level driver for Amiga Gayle PATA"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:amiga-gayle-ide"); +MODULE_VERSION(DRV_VERSION); -- cgit v1.2.3 From 3d6f22b74d919a5f32a6093666e85be110cc06de Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Wed, 21 Mar 2018 10:37:01 +0800 Subject: ahci: imx: fix the build warning Add the default as the last entry to fix the following build warning introduced by commit. e5878732a521 ("ahci: imx: add the imx6qp ahci sata support") drivers/ata/ahci_imx.c: In function 'imx_sata_disable': drivers/ata/ahci_imx.c:478:2: warning: enumeration value 'AHCI_IMX53' not handled in switch [-Wswitch] switch (imxpriv->type) { ^~~~~~ Signed-off-by: Richard Zhu Signed-off-by: Tejun Heo --- drivers/ata/ahci_imx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index 577458fe4aa9..1c69e88c5a13 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -490,6 +490,9 @@ static void imx_sata_disable(struct ahci_host_priv *hpriv) IMX6Q_GPR13_SATA_MPLL_CLK_EN, !IMX6Q_GPR13_SATA_MPLL_CLK_EN); break; + + default: + break; } clk_disable_unprepare(imxpriv->sata_ref_clk); -- cgit v1.2.3 From f0f56716fc3e5d547fd7811eb218a30ed0695605 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Fri, 23 Mar 2018 10:30:53 +0900 Subject: ata: ahci-platform: add reset control support Add support to get and control a list of resets for the device as optional and shared. These resets must be kept de-asserted until the device is enabled. This is specified as shared because some SoCs like UniPhier series have common reset controls with all ahci controller instances. Signed-off-by: Kunihiko Hayashi Reviewed-by: Hans de Goede Signed-off-by: Tejun Heo --- .../devicetree/bindings/ata/ahci-platform.txt | 1 + drivers/ata/ahci.h | 1 + drivers/ata/libahci_platform.c | 24 +++++++++++++++++++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt index c760ecb81381..f4006d3c9fdf 100644 --- a/Documentation/devicetree/bindings/ata/ahci-platform.txt +++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt @@ -30,6 +30,7 @@ compatible: Optional properties: - dma-coherent : Present if dma operations are coherent - clocks : a list of phandle + clock specifier pairs +- resets : a list of phandle + reset specifier pairs - target-supply : regulator for SATA target power - phys : reference to the SATA PHY node - phy-names : must be "sata-phy" diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index a9d996e17d75..4356ef1d28a8 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -350,6 +350,7 @@ struct ahci_host_priv { u32 em_msg_type; /* EM message type */ bool got_runtime_pm; /* Did we do pm_runtime_get? */ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + struct reset_control *rsts; /* Optional */ struct regulator **target_pwrs; /* Optional */ /* * If platform uses PHYs. There is a 1:1 relation between the port number and diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 30cc8f1a31e1..46a762442dc5 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "ahci.h" static void ahci_host_stop(struct ata_host *host); @@ -195,7 +196,8 @@ EXPORT_SYMBOL_GPL(ahci_platform_disable_regulators); * following order: * 1) Regulator * 2) Clocks (through ahci_platform_enable_clks) - * 3) Phys + * 3) Resets + * 4) Phys * * If resource enabling fails at any point the previous enabled resources * are disabled in reverse order. @@ -215,12 +217,19 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) if (rc) goto disable_regulator; - rc = ahci_platform_enable_phys(hpriv); + rc = reset_control_deassert(hpriv->rsts); if (rc) goto disable_clks; + rc = ahci_platform_enable_phys(hpriv); + if (rc) + goto disable_resets; + return 0; +disable_resets: + reset_control_assert(hpriv->rsts); + disable_clks: ahci_platform_disable_clks(hpriv); @@ -239,12 +248,15 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); * following order: * 1) Phys * 2) Clocks (through ahci_platform_disable_clks) - * 3) Regulator + * 3) Resets + * 4) Regulator */ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) { ahci_platform_disable_phys(hpriv); + reset_control_assert(hpriv->rsts); + ahci_platform_disable_clks(hpriv); ahci_platform_disable_regulators(hpriv); @@ -393,6 +405,12 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) hpriv->clks[i] = clk; } + hpriv->rsts = devm_reset_control_array_get_optional_shared(dev); + if (IS_ERR(hpriv->rsts)) { + rc = PTR_ERR(hpriv->rsts); + goto err_out; + } + hpriv->nports = child_nodes = of_get_child_count(dev->of_node); /* -- cgit v1.2.3 From dafd6c496381c1cd1f5ba9ad953e810bdcc931bc Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 27 Mar 2018 14:26:01 +0100 Subject: libata: ensure host is free'd on error exit paths The host structure is not being kfree'd on two error exit paths leading to memory leaks. Add in new err_free label and kfree host. Detected by CoverityScan, CID#1466103 ("Resource leak") Fixes: 2623c7a5f279 ("libata: add refcounting to ata_host") Signed-off-by: Colin Ian King Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index b8b85bf97288..191a55e760a0 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6087,7 +6087,7 @@ struct ata_host *ata_host_alloc(struct device *dev, int max_ports) return NULL; if (!devres_open_group(dev, NULL, GFP_KERNEL)) - return NULL; + goto err_free; dr = devres_alloc(ata_devres_release, 0, GFP_KERNEL); if (!dr) @@ -6119,6 +6119,8 @@ struct ata_host *ata_host_alloc(struct device *dev, int max_ports) err_out: devres_release_group(dev, NULL); + err_free: + kfree(host); return NULL; } -- cgit v1.2.3 From 027fa4dee935d25cef5994af1b8864eb00dcffac Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Wed, 28 Mar 2018 15:11:01 +0800 Subject: ahci: imx: add the imx8qm ahci sata support - There are three PHY lanes on iMX8QM, and can be used in the following three cases 1. a two lanes PCIE_A, and a single lane SATA. 2. a single lane PCIE_A, a single lane PCIE_B and a single lane SATA. 3. a two lanes PCIE_A, and a single lane PCIE_B. The configuration of the iMX8QM AHCI SATA is relied on the usage of PCIE ports in the case 1 and 2. Use standalone iMX8 AHCI SATA probe and enable functions to enable iMX8QM AHCI SATA support. - To save power consumption, PHY CLKs can be gated off after the configurations are done. - The impedance ratio should be configured refer to differnet REXT values. 0x6c <--> REXT value is 85Ohms 0x80 (default value) <--> REXT value is 100Ohms. In general, REXT value should be 85ohms in standalone PCIE HW board design, and 100ohms in SATA standalone HW board design. When the PCIE and the SATA are enabled simultaneously in the HW board design. The REXT value would be set to 85ohms. Configure the SATA PHY impedance ratio to 0x6c in default. Signed-off-by: Richard Zhu Reviewed-by: Hans de Goede Signed-off-by: Tejun Heo --- drivers/ata/ahci_imx.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index 1c69e88c5a13..6822e2f33f7e 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -53,12 +54,49 @@ enum { /* Clock Reset Register */ IMX_CLOCK_RESET = 0x7f3f, IMX_CLOCK_RESET_RESET = 1 << 0, + /* IMX8QM HSIO AHCI definitions */ + IMX8QM_SATA_PHY_RX_IMPED_RATIO_OFFSET = 0x03, + IMX8QM_SATA_PHY_TX_IMPED_RATIO_OFFSET = 0x09, + IMX8QM_SATA_PHY_IMPED_RATIO_85OHM = 0x6c, + IMX8QM_LPCG_PHYX2_OFFSET = 0x00000, + IMX8QM_CSR_PHYX2_OFFSET = 0x90000, + IMX8QM_CSR_PHYX1_OFFSET = 0xa0000, + IMX8QM_CSR_PHYX_STTS0_OFFSET = 0x4, + IMX8QM_CSR_PCIEA_OFFSET = 0xb0000, + IMX8QM_CSR_PCIEB_OFFSET = 0xc0000, + IMX8QM_CSR_SATA_OFFSET = 0xd0000, + IMX8QM_CSR_PCIE_CTRL2_OFFSET = 0x8, + IMX8QM_CSR_MISC_OFFSET = 0xe0000, + + IMX8QM_LPCG_PHYX2_PCLK0_MASK = (0x3 << 16), + IMX8QM_LPCG_PHYX2_PCLK1_MASK = (0x3 << 20), + IMX8QM_PHY_APB_RSTN_0 = BIT(0), + IMX8QM_PHY_MODE_SATA = BIT(19), + IMX8QM_PHY_MODE_MASK = (0xf << 17), + IMX8QM_PHY_PIPE_RSTN_0 = BIT(24), + IMX8QM_PHY_PIPE_RSTN_OVERRIDE_0 = BIT(25), + IMX8QM_PHY_PIPE_RSTN_1 = BIT(26), + IMX8QM_PHY_PIPE_RSTN_OVERRIDE_1 = BIT(27), + IMX8QM_STTS0_LANE0_TX_PLL_LOCK = BIT(4), + IMX8QM_MISC_IOB_RXENA = BIT(0), + IMX8QM_MISC_IOB_TXENA = BIT(1), + IMX8QM_MISC_PHYX1_EPCS_SEL = BIT(12), + IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_1 = BIT(24), + IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_0 = BIT(25), + IMX8QM_MISC_CLKREQN_IN_OVERRIDE_1 = BIT(28), + IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0 = BIT(29), + IMX8QM_SATA_CTRL_RESET_N = BIT(12), + IMX8QM_SATA_CTRL_EPCS_PHYRESET_N = BIT(7), + IMX8QM_CTRL_BUTTON_RST_N = BIT(21), + IMX8QM_CTRL_POWER_UP_RST_N = BIT(23), + IMX8QM_CTRL_LTSSM_ENABLE = BIT(4), }; enum ahci_imx_type { AHCI_IMX53, AHCI_IMX6Q, AHCI_IMX6QP, + AHCI_IMX8QM, }; struct imx_ahci_priv { @@ -67,10 +105,18 @@ struct imx_ahci_priv { struct clk *sata_clk; struct clk *sata_ref_clk; struct clk *ahb_clk; + struct clk *epcs_tx_clk; + struct clk *epcs_rx_clk; + struct clk *phy_apbclk; + struct clk *phy_pclk0; + struct clk *phy_pclk1; + void __iomem *phy_base; + int clkreq_gpio; struct regmap *gpr; bool no_device; bool first_time; u32 phy_params; + u32 imped_ratio; }; static int ahci_imx_hotplug; @@ -407,6 +453,207 @@ static struct attribute *fsl_sata_ahci_attrs[] = { }; ATTRIBUTE_GROUPS(fsl_sata_ahci); +static int imx8_sata_enable(struct ahci_host_priv *hpriv) +{ + u32 val, reg; + int i, ret; + struct imx_ahci_priv *imxpriv = hpriv->plat_data; + struct device *dev = &imxpriv->ahci_pdev->dev; + + /* configure the hsio for sata */ + ret = clk_prepare_enable(imxpriv->phy_pclk0); + if (ret < 0) { + dev_err(dev, "can't enable phy_pclk0.\n"); + return ret; + } + ret = clk_prepare_enable(imxpriv->phy_pclk1); + if (ret < 0) { + dev_err(dev, "can't enable phy_pclk1.\n"); + goto disable_phy_pclk0; + } + ret = clk_prepare_enable(imxpriv->epcs_tx_clk); + if (ret < 0) { + dev_err(dev, "can't enable epcs_tx_clk.\n"); + goto disable_phy_pclk1; + } + ret = clk_prepare_enable(imxpriv->epcs_rx_clk); + if (ret < 0) { + dev_err(dev, "can't enable epcs_rx_clk.\n"); + goto disable_epcs_tx_clk; + } + ret = clk_prepare_enable(imxpriv->phy_apbclk); + if (ret < 0) { + dev_err(dev, "can't enable phy_apbclk.\n"); + goto disable_epcs_rx_clk; + } + /* Configure PHYx2 PIPE_RSTN */ + regmap_read(imxpriv->gpr, IMX8QM_CSR_PCIEA_OFFSET + + IMX8QM_CSR_PCIE_CTRL2_OFFSET, &val); + if ((val & IMX8QM_CTRL_LTSSM_ENABLE) == 0) { + /* The link of the PCIEA of HSIO is down */ + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_PHYX2_OFFSET, + IMX8QM_PHY_PIPE_RSTN_0 | + IMX8QM_PHY_PIPE_RSTN_OVERRIDE_0, + IMX8QM_PHY_PIPE_RSTN_0 | + IMX8QM_PHY_PIPE_RSTN_OVERRIDE_0); + } + regmap_read(imxpriv->gpr, IMX8QM_CSR_PCIEB_OFFSET + + IMX8QM_CSR_PCIE_CTRL2_OFFSET, ®); + if ((reg & IMX8QM_CTRL_LTSSM_ENABLE) == 0) { + /* The link of the PCIEB of HSIO is down */ + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_PHYX2_OFFSET, + IMX8QM_PHY_PIPE_RSTN_1 | + IMX8QM_PHY_PIPE_RSTN_OVERRIDE_1, + IMX8QM_PHY_PIPE_RSTN_1 | + IMX8QM_PHY_PIPE_RSTN_OVERRIDE_1); + } + if (((reg | val) & IMX8QM_CTRL_LTSSM_ENABLE) == 0) { + /* The links of both PCIA and PCIEB of HSIO are down */ + regmap_update_bits(imxpriv->gpr, + IMX8QM_LPCG_PHYX2_OFFSET, + IMX8QM_LPCG_PHYX2_PCLK0_MASK | + IMX8QM_LPCG_PHYX2_PCLK1_MASK, + 0); + } + + /* set PWR_RST and BT_RST of csr_pciea */ + val = IMX8QM_CSR_PCIEA_OFFSET + IMX8QM_CSR_PCIE_CTRL2_OFFSET; + regmap_update_bits(imxpriv->gpr, + val, + IMX8QM_CTRL_BUTTON_RST_N, + IMX8QM_CTRL_BUTTON_RST_N); + regmap_update_bits(imxpriv->gpr, + val, + IMX8QM_CTRL_POWER_UP_RST_N, + IMX8QM_CTRL_POWER_UP_RST_N); + + /* PHYX1_MODE to SATA */ + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_PHYX1_OFFSET, + IMX8QM_PHY_MODE_MASK, + IMX8QM_PHY_MODE_SATA); + + /* + * BIT0 RXENA 1, BIT1 TXENA 0 + * BIT12 PHY_X1_EPCS_SEL 1. + */ + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_MISC_OFFSET, + IMX8QM_MISC_IOB_RXENA, + IMX8QM_MISC_IOB_RXENA); + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_MISC_OFFSET, + IMX8QM_MISC_IOB_TXENA, + 0); + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_MISC_OFFSET, + IMX8QM_MISC_PHYX1_EPCS_SEL, + IMX8QM_MISC_PHYX1_EPCS_SEL); + /* + * It is possible, for PCIe and SATA are sharing + * the same clock source, HPLL or external oscillator. + * When PCIe is in low power modes (L1.X or L2 etc), + * the clock source can be turned off. In this case, + * if this clock source is required to be toggling by + * SATA, then SATA functions will be abnormal. + * Set the override here to avoid it. + */ + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_MISC_OFFSET, + IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_1 | + IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_0 | + IMX8QM_MISC_CLKREQN_IN_OVERRIDE_1 | + IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0, + IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_1 | + IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_0 | + IMX8QM_MISC_CLKREQN_IN_OVERRIDE_1 | + IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0); + + /* clear PHY RST, then set it */ + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_SATA_OFFSET, + IMX8QM_SATA_CTRL_EPCS_PHYRESET_N, + 0); + + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_SATA_OFFSET, + IMX8QM_SATA_CTRL_EPCS_PHYRESET_N, + IMX8QM_SATA_CTRL_EPCS_PHYRESET_N); + + /* CTRL RST: SET -> delay 1 us -> CLEAR -> SET */ + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_SATA_OFFSET, + IMX8QM_SATA_CTRL_RESET_N, + IMX8QM_SATA_CTRL_RESET_N); + udelay(1); + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_SATA_OFFSET, + IMX8QM_SATA_CTRL_RESET_N, + 0); + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_SATA_OFFSET, + IMX8QM_SATA_CTRL_RESET_N, + IMX8QM_SATA_CTRL_RESET_N); + + /* APB reset */ + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_PHYX1_OFFSET, + IMX8QM_PHY_APB_RSTN_0, + IMX8QM_PHY_APB_RSTN_0); + + for (i = 0; i < 100; i++) { + reg = IMX8QM_CSR_PHYX1_OFFSET + + IMX8QM_CSR_PHYX_STTS0_OFFSET; + regmap_read(imxpriv->gpr, reg, &val); + val &= IMX8QM_STTS0_LANE0_TX_PLL_LOCK; + if (val == IMX8QM_STTS0_LANE0_TX_PLL_LOCK) + break; + udelay(1); + } + + if (val != IMX8QM_STTS0_LANE0_TX_PLL_LOCK) { + dev_err(dev, "TX PLL of the PHY is not locked\n"); + ret = -ENODEV; + } else { + writeb(imxpriv->imped_ratio, imxpriv->phy_base + + IMX8QM_SATA_PHY_RX_IMPED_RATIO_OFFSET); + writeb(imxpriv->imped_ratio, imxpriv->phy_base + + IMX8QM_SATA_PHY_TX_IMPED_RATIO_OFFSET); + reg = readb(imxpriv->phy_base + + IMX8QM_SATA_PHY_RX_IMPED_RATIO_OFFSET); + if (unlikely(reg != imxpriv->imped_ratio)) + dev_info(dev, "Can't set PHY RX impedance ratio.\n"); + reg = readb(imxpriv->phy_base + + IMX8QM_SATA_PHY_TX_IMPED_RATIO_OFFSET); + if (unlikely(reg != imxpriv->imped_ratio)) + dev_info(dev, "Can't set PHY TX impedance ratio.\n"); + usleep_range(50, 100); + + /* + * To reduce the power consumption, gate off + * the PHY clks + */ + clk_disable_unprepare(imxpriv->phy_apbclk); + clk_disable_unprepare(imxpriv->phy_pclk1); + clk_disable_unprepare(imxpriv->phy_pclk0); + return ret; + } + + clk_disable_unprepare(imxpriv->phy_apbclk); +disable_epcs_rx_clk: + clk_disable_unprepare(imxpriv->epcs_rx_clk); +disable_epcs_tx_clk: + clk_disable_unprepare(imxpriv->epcs_tx_clk); +disable_phy_pclk1: + clk_disable_unprepare(imxpriv->phy_pclk1); +disable_phy_pclk0: + clk_disable_unprepare(imxpriv->phy_pclk0); + + return ret; +} + static int imx_sata_enable(struct ahci_host_priv *hpriv) { struct imx_ahci_priv *imxpriv = hpriv->plat_data; @@ -454,6 +701,8 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv) dev_err(dev, "failed to reset phy: %d\n", ret); goto disable_clk; } + } else if (imxpriv->type == AHCI_IMX8QM) { + ret = imx8_sata_enable(hpriv); } usleep_range(1000, 2000); @@ -491,6 +740,11 @@ static void imx_sata_disable(struct ahci_host_priv *hpriv) !IMX6Q_GPR13_SATA_MPLL_CLK_EN); break; + case AHCI_IMX8QM: + clk_disable_unprepare(imxpriv->epcs_rx_clk); + clk_disable_unprepare(imxpriv->epcs_tx_clk); + break; + default: break; } @@ -567,6 +821,7 @@ static const struct of_device_id imx_ahci_of_match[] = { { .compatible = "fsl,imx53-ahci", .data = (void *)AHCI_IMX53 }, { .compatible = "fsl,imx6q-ahci", .data = (void *)AHCI_IMX6Q }, { .compatible = "fsl,imx6qp-ahci", .data = (void *)AHCI_IMX6QP }, + { .compatible = "fsl,imx8qm-ahci", .data = (void *)AHCI_IMX8QM }, {}, }; MODULE_DEVICE_TABLE(of, imx_ahci_of_match); @@ -734,6 +989,79 @@ static struct scsi_host_template ahci_platform_sht = { AHCI_SHT(DRV_NAME), }; +static int imx8_sata_probe(struct device *dev, struct imx_ahci_priv *imxpriv) +{ + int ret; + struct resource *phy_res; + struct platform_device *pdev = imxpriv->ahci_pdev; + struct device_node *np = dev->of_node; + + if (of_property_read_u32(np, "fsl,phy-imp", &imxpriv->imped_ratio)) + imxpriv->imped_ratio = IMX8QM_SATA_PHY_IMPED_RATIO_85OHM; + phy_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); + if (phy_res) { + imxpriv->phy_base = devm_ioremap(dev, phy_res->start, + resource_size(phy_res)); + if (!imxpriv->phy_base) { + dev_err(dev, "error with ioremap\n"); + return -ENOMEM; + } + } else { + dev_err(dev, "missing *phy* reg region.\n"); + return -ENOMEM; + } + imxpriv->gpr = + syscon_regmap_lookup_by_phandle(np, "hsio"); + if (IS_ERR(imxpriv->gpr)) { + dev_err(dev, "unable to find gpr registers\n"); + return PTR_ERR(imxpriv->gpr); + } + + imxpriv->epcs_tx_clk = devm_clk_get(dev, "epcs_tx"); + if (IS_ERR(imxpriv->epcs_tx_clk)) { + dev_err(dev, "can't get epcs_tx_clk clock.\n"); + return PTR_ERR(imxpriv->epcs_tx_clk); + } + imxpriv->epcs_rx_clk = devm_clk_get(dev, "epcs_rx"); + if (IS_ERR(imxpriv->epcs_rx_clk)) { + dev_err(dev, "can't get epcs_rx_clk clock.\n"); + return PTR_ERR(imxpriv->epcs_rx_clk); + } + imxpriv->phy_pclk0 = devm_clk_get(dev, "phy_pclk0"); + if (IS_ERR(imxpriv->phy_pclk0)) { + dev_err(dev, "can't get phy_pclk0 clock.\n"); + return PTR_ERR(imxpriv->phy_pclk0); + } + imxpriv->phy_pclk1 = devm_clk_get(dev, "phy_pclk1"); + if (IS_ERR(imxpriv->phy_pclk1)) { + dev_err(dev, "can't get phy_pclk1 clock.\n"); + return PTR_ERR(imxpriv->phy_pclk1); + } + imxpriv->phy_apbclk = devm_clk_get(dev, "phy_apbclk"); + if (IS_ERR(imxpriv->phy_apbclk)) { + dev_err(dev, "can't get phy_apbclk clock.\n"); + return PTR_ERR(imxpriv->phy_apbclk); + } + + /* Fetch GPIO, then enable the external OSC */ + imxpriv->clkreq_gpio = of_get_named_gpio(np, "clkreq-gpio", 0); + if (gpio_is_valid(imxpriv->clkreq_gpio)) { + ret = devm_gpio_request_one(dev, imxpriv->clkreq_gpio, + GPIOF_OUT_INIT_LOW, + "SATA CLKREQ"); + if (ret == -EBUSY) { + dev_info(dev, "clkreq had been initialized.\n"); + } else if (ret) { + dev_err(dev, "%d unable to get clkreq.\n", ret); + return ret; + } + } else if (imxpriv->clkreq_gpio == -EPROBE_DEFER) { + return imxpriv->clkreq_gpio; + } + + return 0; +} + static int imx_ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -793,6 +1121,10 @@ static int imx_ahci_probe(struct platform_device *pdev) IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F | IMX6Q_GPR13_SATA_SPD_MODE_3P0G | reg_value; + } else if (imxpriv->type == AHCI_IMX8QM) { + ret = imx8_sata_probe(dev, imxpriv); + if (ret) + return ret; } hpriv = ahci_platform_get_resources(pdev); -- cgit v1.2.3