Blame view

drivers/ata/ahci_qoriq.c 9.78 KB
8d7c56d08   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
ecfb45985   Tang Yuantian   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   Tang Yuantian   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   Tang Yuantian   ahci: qoriq: enab...
29
  #define PORT_AXICC	0xBC
ecfb45985   Tang Yuantian   ahci: added a new...
30
31
32
33
  #define PORT_TRANS	0xC8
  
  /* port register default value */
  #define AHCI_PORT_PHY_1_CFG	0xa003fffe
ce179cbde   Yuantian Tang   ahci: qoriq: refi...
34
35
  #define AHCI_PORT_PHY2_CFG	0x28184d1f
  #define AHCI_PORT_PHY3_CFG	0x0e081509
e3a6dadc6   Tang Yuantian   ahci: qoriq: Upda...
36
  #define AHCI_PORT_TRANS_CFG	0x08000029
16af080e4   Tang Yuantian   ahci: qoriq: enab...
37
  #define AHCI_PORT_AXICC_CFG	0x3fffffff
dfcdc5fe0   Tang Yuantian   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   Tang Yuantian   ahci: qoriq: enab...
44
  #define LS1021A_AXICC_ADDR	0xC0
ecfb45985   Tang Yuantian   ahci: added a new...
45
46
  
  #define SATA_ECC_DISABLE	0x00020000
01f2901a2   Tang Yuantian   ahci: qoriq: repo...
47
  #define ECC_DIS_ARMV8_CH2	0x80000000
0cee73f75   Yuantian Tang   ahci: qoriq: add ...
48
  #define ECC_DIS_LS1088A		0x40000000
ecfb45985   Tang Yuantian   ahci: added a new...
49
50
51
  
  enum ahci_qoriq_type {
  	AHCI_LS1021A,
a1d78317f   Peng Ma   ahci: qoriq: add ...
52
  	AHCI_LS1028A,
ecfb45985   Tang Yuantian   ahci: added a new...
53
  	AHCI_LS1043A,
d19f9aaf0   Tang Yuantian   ahci: qoriq: Rena...
54
  	AHCI_LS2080A,
2facc6dac   Tang Yuantian   ahci: qoriq: adde...
55
  	AHCI_LS1046A,
0cee73f75   Yuantian Tang   ahci: qoriq: add ...
56
  	AHCI_LS1088A,
ce8f45370   Tang Yuantian   ahci: qoriq: adde...
57
  	AHCI_LS2088A,
2be8481a8   Peng Ma   ahci: qoriq: add ...
58
  	AHCI_LX2160A,
ecfb45985   Tang Yuantian   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   Tang Yuantian   ahci: qoriq: adde...
65
  	bool is_dmacoherent;
ecfb45985   Tang Yuantian   ahci: added a new...
66
  };
2be8481a8   Peng Ma   ahci: qoriq: add ...
67
  static bool ecc_initialized;
ecfb45985   Tang Yuantian   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   Peng Ma   ahci: qoriq: add ...
70
  	{ .compatible = "fsl,ls1028a-ahci", .data = (void *)AHCI_LS1028A},
ecfb45985   Tang Yuantian   ahci: added a new...
71
  	{ .compatible = "fsl,ls1043a-ahci", .data = (void *)AHCI_LS1043A},
d19f9aaf0   Tang Yuantian   ahci: qoriq: Rena...
72
  	{ .compatible = "fsl,ls2080a-ahci", .data = (void *)AHCI_LS2080A},
2facc6dac   Tang Yuantian   ahci: qoriq: adde...
73
  	{ .compatible = "fsl,ls1046a-ahci", .data = (void *)AHCI_LS1046A},
0cee73f75   Yuantian Tang   ahci: qoriq: add ...
74
  	{ .compatible = "fsl,ls1088a-ahci", .data = (void *)AHCI_LS1088A},
ce8f45370   Tang Yuantian   ahci: qoriq: adde...
75
  	{ .compatible = "fsl,ls2088a-ahci", .data = (void *)AHCI_LS2088A},
2be8481a8   Peng Ma   ahci: qoriq: add ...
76
  	{ .compatible = "fsl,lx2160a-ahci", .data = (void *)AHCI_LX2160A},
ecfb45985   Tang Yuantian   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   Arnd Bergmann   ahci: qoriq: Fix ...
95
  	bool ls1021a_workaround = (qoriq_priv->type == AHCI_LS1021A);
ecfb45985   Tang Yuantian   ahci: added a new...
96
97
98
  
  	DPRINTK("ENTER
  ");
fa89f53bd   Evan Wang   libahci: Allow dr...
99
  	hpriv->stop_engine(ap);
ecfb45985   Tang Yuantian   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   Arnd Bergmann   ahci: qoriq: Fix ...
112
  	if (ls1021a_workaround) {
ecfb45985   Tang Yuantian   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   Arnd Bergmann   ahci: qoriq: Fix ...
126
  	if (ls1021a_workaround) {
ecfb45985   Tang Yuantian   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   Tang Yuantian   ahci: qoriq: Reve...
150
  static const struct ata_port_info ahci_qoriq_port_info = {
ecfb45985   Tang Yuantian   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   Peng Ma   ahci: qoriq: add ...
168
  		if (!(qpriv->ecc_addr || ecc_initialized))
01f2901a2   Tang Yuantian   ahci: qoriq: repo...
169
  			return -EINVAL;
2be8481a8   Peng Ma   ahci: qoriq: add ...
170
171
  		else if (qpriv->ecc_addr && !ecc_initialized)
  			writel(SATA_ECC_DISABLE, qpriv->ecc_addr);
ecfb45985   Tang Yuantian   ahci: added a new...
172
  		writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
dfcdc5fe0   Tang Yuantian   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   Tang Yuantian   ahci: added a new...
177
  		writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
386dc3b87   Tang Yuantian   ahci: qoriq: adde...
178
179
180
  		if (qpriv->is_dmacoherent)
  			writel(AHCI_PORT_AXICC_CFG,
  					reg_base + LS1021A_AXICC_ADDR);
ecfb45985   Tang Yuantian   ahci: added a new...
181
182
183
  		break;
  
  	case AHCI_LS1043A:
2be8481a8   Peng Ma   ahci: qoriq: add ...
184
  		if (!(qpriv->ecc_addr || ecc_initialized))
01f2901a2   Tang Yuantian   ahci: qoriq: repo...
185
  			return -EINVAL;
2be8481a8   Peng Ma   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   Tang Yuantian   ahci: qoriq: Adju...
190
  		writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
ce179cbde   Yuantian Tang   ahci: qoriq: refi...
191
192
  		writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2);
  		writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3);
ef0cc7fef   Tang Yuantian   ahci: qoriq: Adju...
193
  		writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
386dc3b87   Tang Yuantian   ahci: qoriq: adde...
194
195
  		if (qpriv->is_dmacoherent)
  			writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
ef0cc7fef   Tang Yuantian   ahci: qoriq: Adju...
196
  		break;
d19f9aaf0   Tang Yuantian   ahci: qoriq: Rena...
197
  	case AHCI_LS2080A:
ecfb45985   Tang Yuantian   ahci: added a new...
198
  		writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
ce179cbde   Yuantian Tang   ahci: qoriq: refi...
199
200
  		writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2);
  		writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3);
e3a6dadc6   Tang Yuantian   ahci: qoriq: Upda...
201
  		writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
386dc3b87   Tang Yuantian   ahci: qoriq: adde...
202
203
  		if (qpriv->is_dmacoherent)
  			writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
ecfb45985   Tang Yuantian   ahci: added a new...
204
  		break;
2facc6dac   Tang Yuantian   ahci: qoriq: adde...
205
206
  
  	case AHCI_LS1046A:
2be8481a8   Peng Ma   ahci: qoriq: add ...
207
  		if (!(qpriv->ecc_addr || ecc_initialized))
01f2901a2   Tang Yuantian   ahci: qoriq: repo...
208
  			return -EINVAL;
2be8481a8   Peng Ma   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   Tang Yuantian   ahci: qoriq: adde...
213
  		writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
ce179cbde   Yuantian Tang   ahci: qoriq: refi...
214
215
  		writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2);
  		writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3);
2facc6dac   Tang Yuantian   ahci: qoriq: adde...
216
  		writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
386dc3b87   Tang Yuantian   ahci: qoriq: adde...
217
218
  		if (qpriv->is_dmacoherent)
  			writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
2facc6dac   Tang Yuantian   ahci: qoriq: adde...
219
  		break;
ce8f45370   Tang Yuantian   ahci: qoriq: adde...
220

a1d78317f   Peng Ma   ahci: qoriq: add ...
221
  	case AHCI_LS1028A:
0cee73f75   Yuantian Tang   ahci: qoriq: add ...
222
  	case AHCI_LS1088A:
2be8481a8   Peng Ma   ahci: qoriq: add ...
223
224
  	case AHCI_LX2160A:
  		if (!(qpriv->ecc_addr || ecc_initialized))
0cee73f75   Yuantian Tang   ahci: qoriq: add ...
225
  			return -EINVAL;
2be8481a8   Peng Ma   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   Yuantian Tang   ahci: qoriq: add ...
230
  		writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
ce179cbde   Yuantian Tang   ahci: qoriq: refi...
231
232
  		writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2);
  		writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3);
0cee73f75   Yuantian Tang   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   Tang Yuantian   ahci: qoriq: adde...
237
238
  	case AHCI_LS2088A:
  		writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
ce179cbde   Yuantian Tang   ahci: qoriq: refi...
239
240
  		writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2);
  		writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3);
ce8f45370   Tang Yuantian   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   Tang Yuantian   ahci: added a new...
245
  	}
2be8481a8   Peng Ma   ahci: qoriq: add ...
246
  	ecc_initialized = true;
ecfb45985   Tang Yuantian   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   Kunihiko Hayashi   ata: add an extra...
259
  	hpriv = ahci_platform_get_resources(pdev, 0);
ecfb45985   Tang Yuantian   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   Peng Ma   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   Tang Yuantian   ahci: added a new...
282
  	}
2be8481a8   Peng Ma   ahci: qoriq: add ...
283

386dc3b87   Tang Yuantian   ahci: qoriq: adde...
284
  	qoriq_priv->is_dmacoherent = of_dma_is_coherent(np);
ecfb45985   Tang Yuantian   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");