373 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Freescale QorIQ AHCI SATA platform driver4 *5 * Copyright 2015 Freescale, Inc.6 * Tang Yuantian <Yuantian.Tang@freescale.com>7 */8 9#include <linux/acpi.h>10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/pm.h>13#include <linux/ahci_platform.h>14#include <linux/device.h>15#include <linux/of.h>16#include <linux/platform_device.h>17#include <linux/libata.h>18#include "ahci.h"19 20#define DRV_NAME "ahci-qoriq"21 22/* port register definition */23#define PORT_PHY1 0xA824#define PORT_PHY2 0xAC25#define PORT_PHY3 0xB026#define PORT_PHY4 0xB427#define PORT_PHY5 0xB828#define PORT_AXICC 0xBC29#define PORT_TRANS 0xC830 31/* port register default value */32#define AHCI_PORT_PHY_1_CFG 0xa003fffe33#define AHCI_PORT_PHY2_CFG 0x28184d1f34#define AHCI_PORT_PHY3_CFG 0x0e08150935#define AHCI_PORT_TRANS_CFG 0x0800002936#define AHCI_PORT_AXICC_CFG 0x3fffffff37 38/* for ls1021a */39#define LS1021A_PORT_PHY2 0x2818341440#define LS1021A_PORT_PHY3 0x0e080e0641#define LS1021A_PORT_PHY4 0x064a080b42#define LS1021A_PORT_PHY5 0x2aa8647043#define LS1021A_AXICC_ADDR 0xC044 45#define SATA_ECC_DISABLE 0x0002000046#define ECC_DIS_ARMV8_CH2 0x8000000047#define ECC_DIS_LS1088A 0x4000000048 49enum ahci_qoriq_type {50 AHCI_LS1021A,51 AHCI_LS1028A,52 AHCI_LS1043A,53 AHCI_LS2080A,54 AHCI_LS1046A,55 AHCI_LS1088A,56 AHCI_LS2088A,57 AHCI_LX2160A,58};59 60struct ahci_qoriq_priv {61 struct ccsr_ahci *reg_base;62 enum ahci_qoriq_type type;63 void __iomem *ecc_addr;64 bool is_dmacoherent;65};66 67static bool ecc_initialized;68 69static const struct of_device_id ahci_qoriq_of_match[] = {70 { .compatible = "fsl,ls1021a-ahci", .data = (void *)AHCI_LS1021A},71 { .compatible = "fsl,ls1028a-ahci", .data = (void *)AHCI_LS1028A},72 { .compatible = "fsl,ls1043a-ahci", .data = (void *)AHCI_LS1043A},73 { .compatible = "fsl,ls2080a-ahci", .data = (void *)AHCI_LS2080A},74 { .compatible = "fsl,ls1046a-ahci", .data = (void *)AHCI_LS1046A},75 { .compatible = "fsl,ls1088a-ahci", .data = (void *)AHCI_LS1088A},76 { .compatible = "fsl,ls2088a-ahci", .data = (void *)AHCI_LS2088A},77 { .compatible = "fsl,lx2160a-ahci", .data = (void *)AHCI_LX2160A},78 { /* sentinel */ }79};80MODULE_DEVICE_TABLE(of, ahci_qoriq_of_match);81 82static const struct acpi_device_id ahci_qoriq_acpi_match[] = {83 {"NXP0004", .driver_data = (kernel_ulong_t)AHCI_LX2160A},84 { }85};86MODULE_DEVICE_TABLE(acpi, ahci_qoriq_acpi_match);87 88static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class,89 unsigned long deadline)90{91 const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context);92 void __iomem *port_mmio = ahci_port_base(link->ap);93 u32 px_cmd, px_is, px_val;94 struct ata_port *ap = link->ap;95 struct ahci_port_priv *pp = ap->private_data;96 struct ahci_host_priv *hpriv = ap->host->private_data;97 struct ahci_qoriq_priv *qoriq_priv = hpriv->plat_data;98 u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;99 struct ata_taskfile tf;100 bool online;101 int rc;102 bool ls1021a_workaround = (qoriq_priv->type == AHCI_LS1021A);103 104 hpriv->stop_engine(ap);105 106 /*107 * There is a errata on ls1021a Rev1.0 and Rev2.0 which is:108 * A-009042: The device detection initialization sequence109 * mistakenly resets some registers.110 *111 * Workaround for this is:112 * The software should read and store PxCMD and PxIS values113 * before issuing the device detection initialization sequence.114 * After the sequence is complete, software should restore the115 * PxCMD and PxIS with the stored values.116 */117 if (ls1021a_workaround) {118 px_cmd = readl(port_mmio + PORT_CMD);119 px_is = readl(port_mmio + PORT_IRQ_STAT);120 }121 122 /* clear D2H reception area to properly wait for D2H FIS */123 ata_tf_init(link->device, &tf);124 tf.status = ATA_BUSY;125 ata_tf_to_fis(&tf, 0, 0, d2h_fis);126 127 rc = sata_link_hardreset(link, timing, deadline, &online,128 ahci_check_ready);129 130 /* restore the PxCMD and PxIS on ls1021 */131 if (ls1021a_workaround) {132 px_val = readl(port_mmio + PORT_CMD);133 if (px_val != px_cmd)134 writel(px_cmd, port_mmio + PORT_CMD);135 136 px_val = readl(port_mmio + PORT_IRQ_STAT);137 if (px_val != px_is)138 writel(px_is, port_mmio + PORT_IRQ_STAT);139 }140 141 hpriv->start_engine(ap);142 143 if (online)144 *class = ahci_dev_classify(ap);145 return rc;146}147 148static struct ata_port_operations ahci_qoriq_ops = {149 .inherits = &ahci_ops,150 .hardreset = ahci_qoriq_hardreset,151};152 153static const struct ata_port_info ahci_qoriq_port_info = {154 .flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ,155 .pio_mask = ATA_PIO4,156 .udma_mask = ATA_UDMA6,157 .port_ops = &ahci_qoriq_ops,158};159 160static const struct scsi_host_template ahci_qoriq_sht = {161 AHCI_SHT(DRV_NAME),162};163 164static int ahci_qoriq_phy_init(struct ahci_host_priv *hpriv)165{166 struct ahci_qoriq_priv *qpriv = hpriv->plat_data;167 void __iomem *reg_base = hpriv->mmio;168 169 switch (qpriv->type) {170 case AHCI_LS1021A:171 if (!(qpriv->ecc_addr || ecc_initialized))172 return -EINVAL;173 else if (qpriv->ecc_addr && !ecc_initialized)174 writel(SATA_ECC_DISABLE, qpriv->ecc_addr);175 writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);176 writel(LS1021A_PORT_PHY2, reg_base + PORT_PHY2);177 writel(LS1021A_PORT_PHY3, reg_base + PORT_PHY3);178 writel(LS1021A_PORT_PHY4, reg_base + PORT_PHY4);179 writel(LS1021A_PORT_PHY5, reg_base + PORT_PHY5);180 writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);181 if (qpriv->is_dmacoherent)182 writel(AHCI_PORT_AXICC_CFG,183 reg_base + LS1021A_AXICC_ADDR);184 break;185 186 case AHCI_LS1043A:187 if (!(qpriv->ecc_addr || ecc_initialized))188 return -EINVAL;189 else if (qpriv->ecc_addr && !ecc_initialized)190 writel(readl(qpriv->ecc_addr) |191 ECC_DIS_ARMV8_CH2,192 qpriv->ecc_addr);193 writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);194 writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2);195 writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3);196 writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);197 if (qpriv->is_dmacoherent)198 writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);199 break;200 201 case AHCI_LS2080A:202 writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);203 writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2);204 writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3);205 writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);206 if (qpriv->is_dmacoherent)207 writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);208 break;209 210 case AHCI_LS1046A:211 if (!(qpriv->ecc_addr || ecc_initialized))212 return -EINVAL;213 else if (qpriv->ecc_addr && !ecc_initialized)214 writel(readl(qpriv->ecc_addr) |215 ECC_DIS_ARMV8_CH2,216 qpriv->ecc_addr);217 writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);218 writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2);219 writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3);220 writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);221 if (qpriv->is_dmacoherent)222 writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);223 break;224 225 case AHCI_LS1028A:226 case AHCI_LS1088A:227 case AHCI_LX2160A:228 if (!(qpriv->ecc_addr || ecc_initialized))229 return -EINVAL;230 else if (qpriv->ecc_addr && !ecc_initialized)231 writel(readl(qpriv->ecc_addr) |232 ECC_DIS_LS1088A,233 qpriv->ecc_addr);234 writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);235 writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2);236 writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3);237 writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);238 if (qpriv->is_dmacoherent)239 writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);240 break;241 242 case AHCI_LS2088A:243 writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);244 writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2);245 writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3);246 writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);247 if (qpriv->is_dmacoherent)248 writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);249 break;250 }251 252 ecc_initialized = true;253 return 0;254}255 256static int ahci_qoriq_probe(struct platform_device *pdev)257{258 struct device_node *np = pdev->dev.of_node;259 const struct acpi_device_id *acpi_id;260 struct device *dev = &pdev->dev;261 struct ahci_host_priv *hpriv;262 struct ahci_qoriq_priv *qoriq_priv;263 const struct of_device_id *of_id;264 struct resource *res;265 int rc;266 267 hpriv = ahci_platform_get_resources(pdev, 0);268 if (IS_ERR(hpriv))269 return PTR_ERR(hpriv);270 271 of_id = of_match_node(ahci_qoriq_of_match, np);272 acpi_id = acpi_match_device(ahci_qoriq_acpi_match, &pdev->dev);273 if (!(of_id || acpi_id))274 return -ENODEV;275 276 qoriq_priv = devm_kzalloc(dev, sizeof(*qoriq_priv), GFP_KERNEL);277 if (!qoriq_priv)278 return -ENOMEM;279 280 if (of_id)281 qoriq_priv->type = (unsigned long)of_id->data;282 else283 qoriq_priv->type = (enum ahci_qoriq_type)acpi_id->driver_data;284 285 if (unlikely(!ecc_initialized)) {286 res = platform_get_resource_byname(pdev,287 IORESOURCE_MEM,288 "sata-ecc");289 if (res) {290 qoriq_priv->ecc_addr =291 devm_ioremap_resource(dev, res);292 if (IS_ERR(qoriq_priv->ecc_addr))293 return PTR_ERR(qoriq_priv->ecc_addr);294 }295 }296 297 if (device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT)298 qoriq_priv->is_dmacoherent = true;299 300 rc = ahci_platform_enable_resources(hpriv);301 if (rc)302 return rc;303 304 hpriv->plat_data = qoriq_priv;305 rc = ahci_qoriq_phy_init(hpriv);306 if (rc)307 goto disable_resources;308 309 rc = ahci_platform_init_host(pdev, hpriv, &ahci_qoriq_port_info,310 &ahci_qoriq_sht);311 if (rc)312 goto disable_resources;313 314 return 0;315 316disable_resources:317 ahci_platform_disable_resources(hpriv);318 319 return rc;320}321 322#ifdef CONFIG_PM_SLEEP323static int ahci_qoriq_resume(struct device *dev)324{325 struct ata_host *host = dev_get_drvdata(dev);326 struct ahci_host_priv *hpriv = host->private_data;327 int rc;328 329 rc = ahci_platform_enable_resources(hpriv);330 if (rc)331 return rc;332 333 rc = ahci_qoriq_phy_init(hpriv);334 if (rc)335 goto disable_resources;336 337 rc = ahci_platform_resume_host(dev);338 if (rc)339 goto disable_resources;340 341 /* We resumed so update PM runtime state */342 pm_runtime_disable(dev);343 pm_runtime_set_active(dev);344 pm_runtime_enable(dev);345 346 return 0;347 348disable_resources:349 ahci_platform_disable_resources(hpriv);350 351 return rc;352}353#endif354 355static SIMPLE_DEV_PM_OPS(ahci_qoriq_pm_ops, ahci_platform_suspend,356 ahci_qoriq_resume);357 358static struct platform_driver ahci_qoriq_driver = {359 .probe = ahci_qoriq_probe,360 .remove_new = ata_platform_remove_one,361 .driver = {362 .name = DRV_NAME,363 .of_match_table = ahci_qoriq_of_match,364 .acpi_match_table = ahci_qoriq_acpi_match,365 .pm = &ahci_qoriq_pm_ops,366 },367};368module_platform_driver(ahci_qoriq_driver);369 370MODULE_DESCRIPTION("Freescale QorIQ AHCI SATA platform driver");371MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");372MODULE_LICENSE("GPL");373