aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata
diff options
context:
space:
mode:
authorMark Langsdorf <mark.langsdorf@calxeda.com>2012-09-06 17:03:30 -0400
committerJeff Garzik <jgarzik@redhat.com>2012-09-13 01:09:41 -0400
commit8996b89d6bc98ae2f6d6e6e624a42a3f89d06949 (patch)
tree2084f726d4a4b4ecc4db807fadcb2c529b73f776 /drivers/ata
parent100f586bd0959fe0e52b8a0b8cb49a3df1c6b044 (diff)
ata: add platform driver for Calxeda AHCI controller
Calxeda highbank SATA phy has intermittent problems bringing up a link with Gen3 drives. Retrying the phy hard reset can work-around this issue, but each reset also disables spread spectrum support. The reset function also needs to reprogram the phy to enable spread spectrum support. Create a new driver based on ahci_platform to support the Calxeda Highbank SATA controller. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/Kconfig8
-rw-r--r--drivers/ata/Makefile1
-rw-r--r--drivers/ata/ahci_platform.c1
-rw-r--r--drivers/ata/sata_highbank.c450
4 files changed, 459 insertions, 1 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 27cecd313e75..e08d322d01d7 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -214,6 +214,14 @@ config SATA_DWC_VDEBUG
214 help 214 help
215 This option enables the taskfile dumping and NCQ debugging. 215 This option enables the taskfile dumping and NCQ debugging.
216 216
217config SATA_HIGHBANK
218 tristate "Calxeda Highbank SATA support"
219 help
220 This option enables support for the Calxeda Highbank SoC's
221 onboard SATA.
222
223 If unsure, say N.
224
217config SATA_MV 225config SATA_MV
218 tristate "Marvell SATA support" 226 tristate "Marvell SATA support"
219 help 227 help
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index a454a139b1d2..8b384f1885f7 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_SATA_FSL) += sata_fsl.o
9obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o 9obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
10obj-$(CONFIG_SATA_SIL24) += sata_sil24.o 10obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
11obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o 11obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
12obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o
12 13
13# SFF w/ custom DMA 14# SFF w/ custom DMA
14obj-$(CONFIG_PDC_ADMA) += pdc_adma.o 15obj-$(CONFIG_PDC_ADMA) += pdc_adma.o
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 09728e09cb31..dc187c746649 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -277,7 +277,6 @@ static int ahci_resume(struct device *dev)
277SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume); 277SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume);
278 278
279static const struct of_device_id ahci_of_match[] = { 279static const struct of_device_id ahci_of_match[] = {
280 { .compatible = "calxeda,hb-ahci", },
281 { .compatible = "snps,spear-ahci", }, 280 { .compatible = "snps,spear-ahci", },
282 {}, 281 {},
283}; 282};
diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c
new file mode 100644
index 000000000000..0d7c4c2cd26f
--- /dev/null
+++ b/drivers/ata/sata_highbank.c
@@ -0,0 +1,450 @@
1/*
2 * Calxeda Highbank AHCI SATA platform driver
3 * Copyright 2012 Calxeda, Inc.
4 *
5 * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms and conditions of the GNU General Public License,
9 * version 2, as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19#include <linux/kernel.h>
20#include <linux/gfp.h>
21#include <linux/module.h>
22#include <linux/init.h>
23#include <linux/types.h>
24#include <linux/err.h>
25#include <linux/io.h>
26#include <linux/spinlock.h>
27#include <linux/device.h>
28#include <linux/of_device.h>
29#include <linux/of_address.h>
30#include <linux/platform_device.h>
31#include <linux/libata.h>
32#include <linux/ahci_platform.h>
33#include <linux/interrupt.h>
34#include <linux/delay.h>
35#include <linux/export.h>
36#include "ahci.h"
37
38#define CPHY_MAP(dev, addr) ((((dev) & 0x1f) << 7) | (((addr) >> 9) & 0x7f))
39#define CPHY_ADDR(addr) (((addr) & 0x1ff) << 2)
40#define SERDES_CR_CTL 0x80a0
41#define SERDES_CR_ADDR 0x80a1
42#define SERDES_CR_DATA 0x80a2
43#define CR_BUSY 0x0001
44#define CR_START 0x0001
45#define CR_WR_RDN 0x0002
46#define CPHY_RX_INPUT_STS 0x2002
47#define CPHY_SATA_OVERRIDE 0x4000
48#define CPHY_OVERRIDE 0x2005
49#define SPHY_LANE 0x100
50#define SPHY_HALF_RATE 0x0001
51#define CPHY_SATA_DPLL_MODE 0x0700
52#define CPHY_SATA_DPLL_SHIFT 8
53#define CPHY_SATA_DPLL_RESET (1 << 11)
54#define CPHY_PHY_COUNT 6
55#define CPHY_LANE_COUNT 4
56#define CPHY_PORT_COUNT (CPHY_PHY_COUNT * CPHY_LANE_COUNT)
57
58static DEFINE_SPINLOCK(cphy_lock);
59/* Each of the 6 phys can have up to 4 sata ports attached to i. Map 0-based
60 * sata ports to their phys and then to their lanes within the phys
61 */
62struct phy_lane_info {
63 void __iomem *phy_base;
64 u8 lane_mapping;
65 u8 phy_devs;
66};
67static struct phy_lane_info port_data[CPHY_PORT_COUNT];
68
69static u32 __combo_phy_reg_read(u8 sata_port, u32 addr)
70{
71 u32 data;
72 u8 dev = port_data[sata_port].phy_devs;
73 spin_lock(&cphy_lock);
74 writel(CPHY_MAP(dev, addr), port_data[sata_port].phy_base + 0x800);
75 data = readl(port_data[sata_port].phy_base + CPHY_ADDR(addr));
76 spin_unlock(&cphy_lock);
77 return data;
78}
79
80static void __combo_phy_reg_write(u8 sata_port, u32 addr, u32 data)
81{
82 u8 dev = port_data[sata_port].phy_devs;
83 spin_lock(&cphy_lock);
84 writel(CPHY_MAP(dev, addr), port_data[sata_port].phy_base + 0x800);
85 writel(data, port_data[sata_port].phy_base + CPHY_ADDR(addr));
86 spin_unlock(&cphy_lock);
87}
88
89static void combo_phy_wait_for_ready(u8 sata_port)
90{
91 while (__combo_phy_reg_read(sata_port, SERDES_CR_CTL) & CR_BUSY)
92 udelay(5);
93}
94
95static u32 combo_phy_read(u8 sata_port, u32 addr)
96{
97 combo_phy_wait_for_ready(sata_port);
98 __combo_phy_reg_write(sata_port, SERDES_CR_ADDR, addr);
99 __combo_phy_reg_write(sata_port, SERDES_CR_CTL, CR_START);
100 combo_phy_wait_for_ready(sata_port);
101 return __combo_phy_reg_read(sata_port, SERDES_CR_DATA);
102}
103
104static void combo_phy_write(u8 sata_port, u32 addr, u32 data)
105{
106 combo_phy_wait_for_ready(sata_port);
107 __combo_phy_reg_write(sata_port, SERDES_CR_ADDR, addr);
108 __combo_phy_reg_write(sata_port, SERDES_CR_DATA, data);
109 __combo_phy_reg_write(sata_port, SERDES_CR_CTL, CR_WR_RDN | CR_START);
110}
111
112static void highbank_cphy_disable_overrides(u8 sata_port)
113{
114 u8 lane = port_data[sata_port].lane_mapping;
115 u32 tmp;
116 if (unlikely(port_data[sata_port].phy_base == NULL))
117 return;
118 tmp = combo_phy_read(sata_port, CPHY_RX_INPUT_STS + lane * SPHY_LANE);
119 tmp &= ~CPHY_SATA_OVERRIDE;
120 combo_phy_write(sata_port, CPHY_OVERRIDE + lane * SPHY_LANE, tmp);
121}
122
123static void cphy_override_rx_mode(u8 sata_port, u32 val)
124{
125 u8 lane = port_data[sata_port].lane_mapping;
126 u32 tmp;
127 tmp = combo_phy_read(sata_port, CPHY_RX_INPUT_STS + lane * SPHY_LANE);
128 tmp &= ~CPHY_SATA_OVERRIDE;
129 combo_phy_write(sata_port, CPHY_OVERRIDE + lane * SPHY_LANE, tmp);
130
131 tmp |= CPHY_SATA_OVERRIDE;
132 combo_phy_write(sata_port, CPHY_OVERRIDE + lane * SPHY_LANE, tmp);
133
134 tmp &= ~CPHY_SATA_DPLL_MODE;
135 tmp |= val << CPHY_SATA_DPLL_SHIFT;
136 combo_phy_write(sata_port, CPHY_OVERRIDE + lane * SPHY_LANE, tmp);
137
138 tmp |= CPHY_SATA_DPLL_RESET;
139 combo_phy_write(sata_port, CPHY_OVERRIDE + lane * SPHY_LANE, tmp);
140
141 tmp &= ~CPHY_SATA_DPLL_RESET;
142 combo_phy_write(sata_port, CPHY_OVERRIDE + lane * SPHY_LANE, tmp);
143
144 msleep(15);
145}
146
147static void highbank_cphy_override_lane(u8 sata_port)
148{
149 u8 lane = port_data[sata_port].lane_mapping;
150 u32 tmp, k = 0;
151
152 if (unlikely(port_data[sata_port].phy_base == NULL))
153 return;
154 do {
155 tmp = combo_phy_read(sata_port, CPHY_RX_INPUT_STS +
156 lane * SPHY_LANE);
157 } while ((tmp & SPHY_HALF_RATE) && (k++ < 1000));
158 cphy_override_rx_mode(sata_port, 3);
159}
160
161static int highbank_initialize_phys(struct device *dev, void __iomem *addr)
162{
163 struct device_node *sata_node = dev->of_node;
164 int phy_count = 0, phy, port = 0;
165 void __iomem *cphy_base[CPHY_PHY_COUNT];
166 struct device_node *phy_nodes[CPHY_PHY_COUNT];
167 memset(port_data, 0, sizeof(struct phy_lane_info) * CPHY_PORT_COUNT);
168 memset(phy_nodes, 0, sizeof(struct device_node*) * CPHY_PHY_COUNT);
169
170 do {
171 u32 tmp;
172 struct of_phandle_args phy_data;
173 if (of_parse_phandle_with_args(sata_node,
174 "calxeda,port-phys", "#phy-cells",
175 port, &phy_data))
176 break;
177 for (phy = 0; phy < phy_count; phy++) {
178 if (phy_nodes[phy] == phy_data.np)
179 break;
180 }
181 if (phy_nodes[phy] == NULL) {
182 phy_nodes[phy] = phy_data.np;
183 cphy_base[phy] = of_iomap(phy_nodes[phy], 0);
184 if (cphy_base[phy] == NULL) {
185 return 0;
186 }
187 phy_count += 1;
188 }
189 port_data[port].lane_mapping = phy_data.args[0];
190 of_property_read_u32(phy_nodes[phy], "phydev", &tmp);
191 port_data[port].phy_devs = tmp;
192 port_data[port].phy_base = cphy_base[phy];
193 of_node_put(phy_data.np);
194 port += 1;
195 } while (port < CPHY_PORT_COUNT);
196 return 0;
197}
198
199static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class,
200 unsigned long deadline)
201{
202 const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
203 struct ata_port *ap = link->ap;
204 struct ahci_port_priv *pp = ap->private_data;
205 u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
206 struct ata_taskfile tf;
207 bool online;
208 u32 sstatus;
209 int rc;
210 int retry = 10;
211
212 ahci_stop_engine(ap);
213
214 /* clear D2H reception area to properly wait for D2H FIS */
215 ata_tf_init(link->device, &tf);
216 tf.command = 0x80;
217 ata_tf_to_fis(&tf, 0, 0, d2h_fis);
218
219 do {
220 highbank_cphy_disable_overrides(link->ap->port_no);
221 rc = sata_link_hardreset(link, timing, deadline, &online, NULL);
222 highbank_cphy_override_lane(link->ap->port_no);
223
224 /* If the status is 1, we are connected, but the link did not
225 * come up. So retry resetting the link again.
226 */
227 if (sata_scr_read(link, SCR_STATUS, &sstatus))
228 break;
229 if (!(sstatus & 0x3))
230 break;
231 } while (!online && retry--);
232
233 ahci_start_engine(ap);
234
235 if (online)
236 *class = ahci_dev_classify(ap);
237
238 return rc;
239}
240
241static struct ata_port_operations ahci_highbank_ops = {
242 .inherits = &ahci_ops,
243 .hardreset = ahci_highbank_hardreset,
244};
245
246static const struct ata_port_info ahci_highbank_port_info = {
247 .flags = AHCI_FLAG_COMMON,
248 .pio_mask = ATA_PIO4,
249 .udma_mask = ATA_UDMA6,
250 .port_ops = &ahci_highbank_ops,
251};
252
253static struct scsi_host_template ahci_highbank_platform_sht = {
254 AHCI_SHT("highbank-ahci"),
255};
256
257static const struct of_device_id ahci_of_match[] = {
258 { .compatible = "calxeda,hb-ahci" },
259 {},
260};
261MODULE_DEVICE_TABLE(of, ahci_of_match);
262
263static int __init ahci_highbank_probe(struct platform_device *pdev)
264{
265 struct device *dev = &pdev->dev;
266 struct ahci_host_priv *hpriv;
267 struct ata_host *host;
268 struct resource *mem;
269 int irq;
270 int n_ports;
271 int i;
272 int rc;
273 struct ata_port_info pi = ahci_highbank_port_info;
274 const struct ata_port_info *ppi[] = { &pi, NULL };
275
276 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
277 if (!mem) {
278 dev_err(dev, "no mmio space\n");
279 return -EINVAL;
280 }
281
282 irq = platform_get_irq(pdev, 0);
283 if (irq <= 0) {
284 dev_err(dev, "no irq\n");
285 return -EINVAL;
286 }
287
288 hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
289 if (!hpriv) {
290 dev_err(dev, "can't alloc ahci_host_priv\n");
291 return -ENOMEM;
292 }
293
294 hpriv->flags |= (unsigned long)pi.private_data;
295
296 hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem));
297 if (!hpriv->mmio) {
298 dev_err(dev, "can't map %pR\n", mem);
299 return -ENOMEM;
300 }
301
302 rc = highbank_initialize_phys(dev, hpriv->mmio);
303 if (rc)
304 return rc;
305
306
307 ahci_save_initial_config(dev, hpriv, 0, 0);
308
309 /* prepare host */
310 if (hpriv->cap & HOST_CAP_NCQ)
311 pi.flags |= ATA_FLAG_NCQ;
312
313 if (hpriv->cap & HOST_CAP_PMP)
314 pi.flags |= ATA_FLAG_PMP;
315
316 ahci_set_em_messages(hpriv, &pi);
317
318 /* CAP.NP sometimes indicate the index of the last enabled
319 * port, at other times, that of the last possible port, so
320 * determining the maximum port number requires looking at
321 * both CAP.NP and port_map.
322 */
323 n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
324
325 host = ata_host_alloc_pinfo(dev, ppi, n_ports);
326 if (!host) {
327 rc = -ENOMEM;
328 goto err0;
329 }
330
331 host->private_data = hpriv;
332
333 if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
334 host->flags |= ATA_HOST_PARALLEL_SCAN;
335
336 if (pi.flags & ATA_FLAG_EM)
337 ahci_reset_em(host);
338
339 for (i = 0; i < host->n_ports; i++) {
340 struct ata_port *ap = host->ports[i];
341
342 ata_port_desc(ap, "mmio %pR", mem);
343 ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80);
344
345 /* set enclosure management message type */
346 if (ap->flags & ATA_FLAG_EM)
347 ap->em_message_type = hpriv->em_msg_type;
348
349 /* disabled/not-implemented port */
350 if (!(hpriv->port_map & (1 << i)))
351 ap->ops = &ata_dummy_port_ops;
352 }
353
354 rc = ahci_reset_controller(host);
355 if (rc)
356 goto err0;
357
358 ahci_init_controller(host);
359 ahci_print_info(host, "platform");
360
361 rc = ata_host_activate(host, irq, ahci_interrupt, 0,
362 &ahci_highbank_platform_sht);
363 if (rc)
364 goto err0;
365
366 return 0;
367err0:
368 return rc;
369}
370
371static int __devexit ahci_highbank_remove(struct platform_device *pdev)
372{
373 struct device *dev = &pdev->dev;
374 struct ata_host *host = dev_get_drvdata(dev);
375
376 ata_host_detach(host);
377
378 return 0;
379}
380
381#ifdef CONFIG_PM
382static int ahci_highbank_suspend(struct device *dev)
383{
384 struct ata_host *host = dev_get_drvdata(dev);
385 struct ahci_host_priv *hpriv = host->private_data;
386 void __iomem *mmio = hpriv->mmio;
387 u32 ctl;
388 int rc;
389
390 if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
391 dev_err(dev, "firmware update required for suspend/resume\n");
392 return -EIO;
393 }
394
395 /*
396 * AHCI spec rev1.1 section 8.3.3:
397 * Software must disable interrupts prior to requesting a
398 * transition of the HBA to D3 state.
399 */
400 ctl = readl(mmio + HOST_CTL);
401 ctl &= ~HOST_IRQ_EN;
402 writel(ctl, mmio + HOST_CTL);
403 readl(mmio + HOST_CTL); /* flush */
404
405 rc = ata_host_suspend(host, PMSG_SUSPEND);
406 if (rc)
407 return rc;
408
409 return 0;
410}
411
412static int ahci_highbank_resume(struct device *dev)
413{
414 struct ata_host *host = dev_get_drvdata(dev);
415 int rc;
416
417 if (dev->power.power_state.event == PM_EVENT_SUSPEND) {
418 rc = ahci_reset_controller(host);
419 if (rc)
420 return rc;
421
422 ahci_init_controller(host);
423 }
424
425 ata_host_resume(host);
426
427 return 0;
428}
429#endif
430
431SIMPLE_DEV_PM_OPS(ahci_highbank_pm_ops,
432 ahci_highbank_suspend, ahci_highbank_resume);
433
434static struct platform_driver ahci_highbank_driver = {
435 .remove = __devexit_p(ahci_highbank_remove),
436 .driver = {
437 .name = "highbank-ahci",
438 .owner = THIS_MODULE,
439 .of_match_table = ahci_of_match,
440 .pm = &ahci_highbank_pm_ops,
441 },
442 .probe = ahci_highbank_probe,
443};
444
445module_platform_driver(ahci_highbank_driver);
446
447MODULE_DESCRIPTION("Calxeda Highbank AHCI SATA platform driver");
448MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>");
449MODULE_LICENSE("GPL");
450MODULE_ALIAS("sata:highbank");