brintos

brintos / linux-shallow public Read only

0
0
Text · 10.2 KiB · b1a4e57 Raw
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