Blame view
drivers/ata/ahci_qoriq.c
9.78 KB
8d7c56d08 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
ecfb45985 ahci: added a new... |
2 3 4 5 6 |
/* * Freescale QorIQ AHCI SATA platform driver * * Copyright 2015 Freescale, Inc. * Tang Yuantian <Yuantian.Tang@freescale.com> |
ecfb45985 ahci: added a new... |
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
*/ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pm.h> #include <linux/ahci_platform.h> #include <linux/device.h> #include <linux/of_address.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/libata.h> #include "ahci.h" #define DRV_NAME "ahci-qoriq" /* port register definition */ #define PORT_PHY1 0xA8 #define PORT_PHY2 0xAC #define PORT_PHY3 0xB0 #define PORT_PHY4 0xB4 #define PORT_PHY5 0xB8 |
16af080e4 ahci: qoriq: enab... |
29 |
#define PORT_AXICC 0xBC |
ecfb45985 ahci: added a new... |
30 31 32 33 |
#define PORT_TRANS 0xC8 /* port register default value */ #define AHCI_PORT_PHY_1_CFG 0xa003fffe |
ce179cbde ahci: qoriq: refi... |
34 35 |
#define AHCI_PORT_PHY2_CFG 0x28184d1f #define AHCI_PORT_PHY3_CFG 0x0e081509 |
e3a6dadc6 ahci: qoriq: Upda... |
36 |
#define AHCI_PORT_TRANS_CFG 0x08000029 |
16af080e4 ahci: qoriq: enab... |
37 |
#define AHCI_PORT_AXICC_CFG 0x3fffffff |
dfcdc5fe0 ahci: qoriq: Adju... |
38 39 40 41 42 43 |
/* for ls1021a */ #define LS1021A_PORT_PHY2 0x28183414 #define LS1021A_PORT_PHY3 0x0e080e06 #define LS1021A_PORT_PHY4 0x064a080b #define LS1021A_PORT_PHY5 0x2aa86470 |
16af080e4 ahci: qoriq: enab... |
44 |
#define LS1021A_AXICC_ADDR 0xC0 |
ecfb45985 ahci: added a new... |
45 46 |
#define SATA_ECC_DISABLE 0x00020000 |
01f2901a2 ahci: qoriq: repo... |
47 |
#define ECC_DIS_ARMV8_CH2 0x80000000 |
0cee73f75 ahci: qoriq: add ... |
48 |
#define ECC_DIS_LS1088A 0x40000000 |
ecfb45985 ahci: added a new... |
49 50 51 |
enum ahci_qoriq_type { AHCI_LS1021A, |
a1d78317f ahci: qoriq: add ... |
52 |
AHCI_LS1028A, |
ecfb45985 ahci: added a new... |
53 |
AHCI_LS1043A, |
d19f9aaf0 ahci: qoriq: Rena... |
54 |
AHCI_LS2080A, |
2facc6dac ahci: qoriq: adde... |
55 |
AHCI_LS1046A, |
0cee73f75 ahci: qoriq: add ... |
56 |
AHCI_LS1088A, |
ce8f45370 ahci: qoriq: adde... |
57 |
AHCI_LS2088A, |
2be8481a8 ahci: qoriq: add ... |
58 |
AHCI_LX2160A, |
ecfb45985 ahci: added a new... |
59 60 61 62 63 64 |
}; struct ahci_qoriq_priv { struct ccsr_ahci *reg_base; enum ahci_qoriq_type type; void __iomem *ecc_addr; |
386dc3b87 ahci: qoriq: adde... |
65 |
bool is_dmacoherent; |
ecfb45985 ahci: added a new... |
66 |
}; |
2be8481a8 ahci: qoriq: add ... |
67 |
static bool ecc_initialized; |
ecfb45985 ahci: added a new... |
68 69 |
static const struct of_device_id ahci_qoriq_of_match[] = { { .compatible = "fsl,ls1021a-ahci", .data = (void *)AHCI_LS1021A}, |
a1d78317f ahci: qoriq: add ... |
70 |
{ .compatible = "fsl,ls1028a-ahci", .data = (void *)AHCI_LS1028A}, |
ecfb45985 ahci: added a new... |
71 |
{ .compatible = "fsl,ls1043a-ahci", .data = (void *)AHCI_LS1043A}, |
d19f9aaf0 ahci: qoriq: Rena... |
72 |
{ .compatible = "fsl,ls2080a-ahci", .data = (void *)AHCI_LS2080A}, |
2facc6dac ahci: qoriq: adde... |
73 |
{ .compatible = "fsl,ls1046a-ahci", .data = (void *)AHCI_LS1046A}, |
0cee73f75 ahci: qoriq: add ... |
74 |
{ .compatible = "fsl,ls1088a-ahci", .data = (void *)AHCI_LS1088A}, |
ce8f45370 ahci: qoriq: adde... |
75 |
{ .compatible = "fsl,ls2088a-ahci", .data = (void *)AHCI_LS2088A}, |
2be8481a8 ahci: qoriq: add ... |
76 |
{ .compatible = "fsl,lx2160a-ahci", .data = (void *)AHCI_LX2160A}, |
ecfb45985 ahci: added a new... |
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
{}, }; MODULE_DEVICE_TABLE(of, ahci_qoriq_of_match); static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); void __iomem *port_mmio = ahci_port_base(link->ap); u32 px_cmd, px_is, px_val; struct ata_port *ap = link->ap; struct ahci_port_priv *pp = ap->private_data; struct ahci_host_priv *hpriv = ap->host->private_data; struct ahci_qoriq_priv *qoriq_priv = hpriv->plat_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; bool online; int rc; |
eb351031a ahci: qoriq: Fix ... |
95 |
bool ls1021a_workaround = (qoriq_priv->type == AHCI_LS1021A); |
ecfb45985 ahci: added a new... |
96 97 98 |
DPRINTK("ENTER "); |
fa89f53bd libahci: Allow dr... |
99 |
hpriv->stop_engine(ap); |
ecfb45985 ahci: added a new... |
100 101 102 103 104 105 106 107 108 109 110 111 |
/* * There is a errata on ls1021a Rev1.0 and Rev2.0 which is: * A-009042: The device detection initialization sequence * mistakenly resets some registers. * * Workaround for this is: * The software should read and store PxCMD and PxIS values * before issuing the device detection initialization sequence. * After the sequence is complete, software should restore the * PxCMD and PxIS with the stored values. */ |
eb351031a ahci: qoriq: Fix ... |
112 |
if (ls1021a_workaround) { |
ecfb45985 ahci: added a new... |
113 114 115 116 117 118 119 120 121 122 123 124 125 |
px_cmd = readl(port_mmio + PORT_CMD); px_is = readl(port_mmio + PORT_IRQ_STAT); } /* clear D2H reception area to properly wait for D2H FIS */ ata_tf_init(link->device, &tf); tf.command = ATA_BUSY; ata_tf_to_fis(&tf, 0, 0, d2h_fis); rc = sata_link_hardreset(link, timing, deadline, &online, ahci_check_ready); /* restore the PxCMD and PxIS on ls1021 */ |
eb351031a ahci: qoriq: Fix ... |
126 |
if (ls1021a_workaround) { |
ecfb45985 ahci: added a new... |
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
px_val = readl(port_mmio + PORT_CMD); if (px_val != px_cmd) writel(px_cmd, port_mmio + PORT_CMD); px_val = readl(port_mmio + PORT_IRQ_STAT); if (px_val != px_is) writel(px_is, port_mmio + PORT_IRQ_STAT); } hpriv->start_engine(ap); if (online) *class = ahci_dev_classify(ap); DPRINTK("EXIT, rc=%d, class=%u ", rc, *class); return rc; } static struct ata_port_operations ahci_qoriq_ops = { .inherits = &ahci_ops, .hardreset = ahci_qoriq_hardreset, }; |
1ce788d24 ahci: qoriq: Reve... |
150 |
static const struct ata_port_info ahci_qoriq_port_info = { |
ecfb45985 ahci: added a new... |
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
.flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_qoriq_ops, }; static struct scsi_host_template ahci_qoriq_sht = { AHCI_SHT(DRV_NAME), }; static int ahci_qoriq_phy_init(struct ahci_host_priv *hpriv) { struct ahci_qoriq_priv *qpriv = hpriv->plat_data; void __iomem *reg_base = hpriv->mmio; switch (qpriv->type) { case AHCI_LS1021A: |
2be8481a8 ahci: qoriq: add ... |
168 |
if (!(qpriv->ecc_addr || ecc_initialized)) |
01f2901a2 ahci: qoriq: repo... |
169 |
return -EINVAL; |
2be8481a8 ahci: qoriq: add ... |
170 171 |
else if (qpriv->ecc_addr && !ecc_initialized) writel(SATA_ECC_DISABLE, qpriv->ecc_addr); |
ecfb45985 ahci: added a new... |
172 |
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1); |
dfcdc5fe0 ahci: qoriq: Adju... |
173 174 175 176 |
writel(LS1021A_PORT_PHY2, reg_base + PORT_PHY2); writel(LS1021A_PORT_PHY3, reg_base + PORT_PHY3); writel(LS1021A_PORT_PHY4, reg_base + PORT_PHY4); writel(LS1021A_PORT_PHY5, reg_base + PORT_PHY5); |
ecfb45985 ahci: added a new... |
177 |
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); |
386dc3b87 ahci: qoriq: adde... |
178 179 180 |
if (qpriv->is_dmacoherent) writel(AHCI_PORT_AXICC_CFG, reg_base + LS1021A_AXICC_ADDR); |
ecfb45985 ahci: added a new... |
181 182 183 |
break; case AHCI_LS1043A: |
2be8481a8 ahci: qoriq: add ... |
184 |
if (!(qpriv->ecc_addr || ecc_initialized)) |
01f2901a2 ahci: qoriq: repo... |
185 |
return -EINVAL; |
2be8481a8 ahci: qoriq: add ... |
186 187 188 189 |
else if (qpriv->ecc_addr && !ecc_initialized) writel(readl(qpriv->ecc_addr) | ECC_DIS_ARMV8_CH2, qpriv->ecc_addr); |
ef0cc7fef ahci: qoriq: Adju... |
190 |
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1); |
ce179cbde ahci: qoriq: refi... |
191 192 |
writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2); writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3); |
ef0cc7fef ahci: qoriq: Adju... |
193 |
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); |
386dc3b87 ahci: qoriq: adde... |
194 195 |
if (qpriv->is_dmacoherent) writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC); |
ef0cc7fef ahci: qoriq: Adju... |
196 |
break; |
d19f9aaf0 ahci: qoriq: Rena... |
197 |
case AHCI_LS2080A: |
ecfb45985 ahci: added a new... |
198 |
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1); |
ce179cbde ahci: qoriq: refi... |
199 200 |
writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2); writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3); |
e3a6dadc6 ahci: qoriq: Upda... |
201 |
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); |
386dc3b87 ahci: qoriq: adde... |
202 203 |
if (qpriv->is_dmacoherent) writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC); |
ecfb45985 ahci: added a new... |
204 |
break; |
2facc6dac ahci: qoriq: adde... |
205 206 |
case AHCI_LS1046A: |
2be8481a8 ahci: qoriq: add ... |
207 |
if (!(qpriv->ecc_addr || ecc_initialized)) |
01f2901a2 ahci: qoriq: repo... |
208 |
return -EINVAL; |
2be8481a8 ahci: qoriq: add ... |
209 210 211 212 |
else if (qpriv->ecc_addr && !ecc_initialized) writel(readl(qpriv->ecc_addr) | ECC_DIS_ARMV8_CH2, qpriv->ecc_addr); |
2facc6dac ahci: qoriq: adde... |
213 |
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1); |
ce179cbde ahci: qoriq: refi... |
214 215 |
writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2); writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3); |
2facc6dac ahci: qoriq: adde... |
216 |
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); |
386dc3b87 ahci: qoriq: adde... |
217 218 |
if (qpriv->is_dmacoherent) writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC); |
2facc6dac ahci: qoriq: adde... |
219 |
break; |
ce8f45370 ahci: qoriq: adde... |
220 |
|
a1d78317f ahci: qoriq: add ... |
221 |
case AHCI_LS1028A: |
0cee73f75 ahci: qoriq: add ... |
222 |
case AHCI_LS1088A: |
2be8481a8 ahci: qoriq: add ... |
223 224 |
case AHCI_LX2160A: if (!(qpriv->ecc_addr || ecc_initialized)) |
0cee73f75 ahci: qoriq: add ... |
225 |
return -EINVAL; |
2be8481a8 ahci: qoriq: add ... |
226 227 228 229 |
else if (qpriv->ecc_addr && !ecc_initialized) writel(readl(qpriv->ecc_addr) | ECC_DIS_LS1088A, qpriv->ecc_addr); |
0cee73f75 ahci: qoriq: add ... |
230 |
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1); |
ce179cbde ahci: qoriq: refi... |
231 232 |
writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2); writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3); |
0cee73f75 ahci: qoriq: add ... |
233 234 235 236 |
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); if (qpriv->is_dmacoherent) writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC); break; |
ce8f45370 ahci: qoriq: adde... |
237 238 |
case AHCI_LS2088A: writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1); |
ce179cbde ahci: qoriq: refi... |
239 240 |
writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2); writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3); |
ce8f45370 ahci: qoriq: adde... |
241 242 243 244 |
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); if (qpriv->is_dmacoherent) writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC); break; |
ecfb45985 ahci: added a new... |
245 |
} |
2be8481a8 ahci: qoriq: add ... |
246 |
ecc_initialized = true; |
ecfb45985 ahci: added a new... |
247 248 249 250 251 252 253 254 255 256 257 258 |
return 0; } static int ahci_qoriq_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; struct ahci_qoriq_priv *qoriq_priv; const struct of_device_id *of_id; struct resource *res; int rc; |
16af2d658 ata: add an extra... |
259 |
hpriv = ahci_platform_get_resources(pdev, 0); |
ecfb45985 ahci: added a new... |
260 261 262 263 264 265 266 267 268 269 270 271 |
if (IS_ERR(hpriv)) return PTR_ERR(hpriv); of_id = of_match_node(ahci_qoriq_of_match, np); if (!of_id) return -ENODEV; qoriq_priv = devm_kzalloc(dev, sizeof(*qoriq_priv), GFP_KERNEL); if (!qoriq_priv) return -ENOMEM; qoriq_priv->type = (enum ahci_qoriq_type)of_id->data; |
2be8481a8 ahci: qoriq: add ... |
272 273 274 275 276 277 278 279 280 281 |
if (unlikely(!ecc_initialized)) { res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sata-ecc"); if (res) { qoriq_priv->ecc_addr = devm_ioremap_resource(dev, res); if (IS_ERR(qoriq_priv->ecc_addr)) return PTR_ERR(qoriq_priv->ecc_addr); } |
ecfb45985 ahci: added a new... |
282 |
} |
2be8481a8 ahci: qoriq: add ... |
283 |
|
386dc3b87 ahci: qoriq: adde... |
284 |
qoriq_priv->is_dmacoherent = of_dma_is_coherent(np); |
ecfb45985 ahci: added a new... |
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
rc = ahci_platform_enable_resources(hpriv); if (rc) return rc; hpriv->plat_data = qoriq_priv; rc = ahci_qoriq_phy_init(hpriv); if (rc) goto disable_resources; rc = ahci_platform_init_host(pdev, hpriv, &ahci_qoriq_port_info, &ahci_qoriq_sht); if (rc) goto disable_resources; return 0; disable_resources: ahci_platform_disable_resources(hpriv); return rc; } #ifdef CONFIG_PM_SLEEP static int ahci_qoriq_resume(struct device *dev) { struct ata_host *host = dev_get_drvdata(dev); struct ahci_host_priv *hpriv = host->private_data; int rc; rc = ahci_platform_enable_resources(hpriv); if (rc) return rc; rc = ahci_qoriq_phy_init(hpriv); if (rc) goto disable_resources; rc = ahci_platform_resume_host(dev); if (rc) goto disable_resources; /* We resumed so update PM runtime state */ pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); return 0; disable_resources: ahci_platform_disable_resources(hpriv); return rc; } #endif static SIMPLE_DEV_PM_OPS(ahci_qoriq_pm_ops, ahci_platform_suspend, ahci_qoriq_resume); static struct platform_driver ahci_qoriq_driver = { .probe = ahci_qoriq_probe, .remove = ata_platform_remove_one, .driver = { .name = DRV_NAME, .of_match_table = ahci_qoriq_of_match, .pm = &ahci_qoriq_pm_ops, }, }; module_platform_driver(ahci_qoriq_driver); MODULE_DESCRIPTION("Freescale QorIQ AHCI SATA platform driver"); MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>"); MODULE_LICENSE("GPL"); |