Root/
1 | /* |
2 | * AHCI SATA platform driver |
3 | * |
4 | * Copyright 2004-2005 Red Hat, Inc. |
5 | * Jeff Garzik <jgarzik@pobox.com> |
6 | * Copyright 2010 MontaVista Software, LLC. |
7 | * Anton Vorontsov <avorontsov@ru.mvista.com> |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by |
11 | * the Free Software Foundation; either version 2, or (at your option) |
12 | * any later version. |
13 | */ |
14 | |
15 | #include <linux/kernel.h> |
16 | #include <linux/gfp.h> |
17 | #include <linux/module.h> |
18 | #include <linux/pm.h> |
19 | #include <linux/init.h> |
20 | #include <linux/interrupt.h> |
21 | #include <linux/device.h> |
22 | #include <linux/platform_device.h> |
23 | #include <linux/libata.h> |
24 | #include <linux/ahci_platform.h> |
25 | #include "ahci.h" |
26 | |
27 | enum ahci_type { |
28 | AHCI, /* standard platform ahci */ |
29 | IMX53_AHCI, /* ahci on i.mx53 */ |
30 | STRICT_AHCI, /* delayed DMA engine start */ |
31 | }; |
32 | |
33 | static struct platform_device_id ahci_devtype[] = { |
34 | { |
35 | .name = "ahci", |
36 | .driver_data = AHCI, |
37 | }, { |
38 | .name = "imx53-ahci", |
39 | .driver_data = IMX53_AHCI, |
40 | }, { |
41 | .name = "strict-ahci", |
42 | .driver_data = STRICT_AHCI, |
43 | }, { |
44 | /* sentinel */ |
45 | } |
46 | }; |
47 | MODULE_DEVICE_TABLE(platform, ahci_devtype); |
48 | |
49 | |
50 | static const struct ata_port_info ahci_port_info[] = { |
51 | /* by features */ |
52 | [AHCI] = { |
53 | .flags = AHCI_FLAG_COMMON, |
54 | .pio_mask = ATA_PIO4, |
55 | .udma_mask = ATA_UDMA6, |
56 | .port_ops = &ahci_ops, |
57 | }, |
58 | [IMX53_AHCI] = { |
59 | .flags = AHCI_FLAG_COMMON, |
60 | .pio_mask = ATA_PIO4, |
61 | .udma_mask = ATA_UDMA6, |
62 | .port_ops = &ahci_pmp_retry_srst_ops, |
63 | }, |
64 | [STRICT_AHCI] = { |
65 | AHCI_HFLAGS (AHCI_HFLAG_DELAY_ENGINE), |
66 | .flags = AHCI_FLAG_COMMON, |
67 | .pio_mask = ATA_PIO4, |
68 | .udma_mask = ATA_UDMA6, |
69 | .port_ops = &ahci_ops, |
70 | }, |
71 | }; |
72 | |
73 | static struct scsi_host_template ahci_platform_sht = { |
74 | AHCI_SHT("ahci_platform"), |
75 | }; |
76 | |
77 | static int __init ahci_probe(struct platform_device *pdev) |
78 | { |
79 | struct device *dev = &pdev->dev; |
80 | struct ahci_platform_data *pdata = dev_get_platdata(dev); |
81 | const struct platform_device_id *id = platform_get_device_id(pdev); |
82 | struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0]; |
83 | const struct ata_port_info *ppi[] = { &pi, NULL }; |
84 | struct ahci_host_priv *hpriv; |
85 | struct ata_host *host; |
86 | struct resource *mem; |
87 | int irq; |
88 | int n_ports; |
89 | int i; |
90 | int rc; |
91 | |
92 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
93 | if (!mem) { |
94 | dev_err(dev, "no mmio space\n"); |
95 | return -EINVAL; |
96 | } |
97 | |
98 | irq = platform_get_irq(pdev, 0); |
99 | if (irq <= 0) { |
100 | dev_err(dev, "no irq\n"); |
101 | return -EINVAL; |
102 | } |
103 | |
104 | if (pdata && pdata->ata_port_info) |
105 | pi = *pdata->ata_port_info; |
106 | |
107 | hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); |
108 | if (!hpriv) { |
109 | dev_err(dev, "can't alloc ahci_host_priv\n"); |
110 | return -ENOMEM; |
111 | } |
112 | |
113 | hpriv->flags |= (unsigned long)pi.private_data; |
114 | |
115 | hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); |
116 | if (!hpriv->mmio) { |
117 | dev_err(dev, "can't map %pR\n", mem); |
118 | return -ENOMEM; |
119 | } |
120 | |
121 | /* |
122 | * Some platforms might need to prepare for mmio region access, |
123 | * which could be done in the following init call. So, the mmio |
124 | * region shouldn't be accessed before init (if provided) has |
125 | * returned successfully. |
126 | */ |
127 | if (pdata && pdata->init) { |
128 | rc = pdata->init(dev, hpriv->mmio); |
129 | if (rc) |
130 | return rc; |
131 | } |
132 | |
133 | ahci_save_initial_config(dev, hpriv, |
134 | pdata ? pdata->force_port_map : 0, |
135 | pdata ? pdata->mask_port_map : 0); |
136 | |
137 | /* prepare host */ |
138 | if (hpriv->cap & HOST_CAP_NCQ) |
139 | pi.flags |= ATA_FLAG_NCQ; |
140 | |
141 | if (hpriv->cap & HOST_CAP_PMP) |
142 | pi.flags |= ATA_FLAG_PMP; |
143 | |
144 | ahci_set_em_messages(hpriv, &pi); |
145 | |
146 | /* CAP.NP sometimes indicate the index of the last enabled |
147 | * port, at other times, that of the last possible port, so |
148 | * determining the maximum port number requires looking at |
149 | * both CAP.NP and port_map. |
150 | */ |
151 | n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); |
152 | |
153 | host = ata_host_alloc_pinfo(dev, ppi, n_ports); |
154 | if (!host) { |
155 | rc = -ENOMEM; |
156 | goto err0; |
157 | } |
158 | |
159 | host->private_data = hpriv; |
160 | |
161 | if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) |
162 | host->flags |= ATA_HOST_PARALLEL_SCAN; |
163 | else |
164 | printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n"); |
165 | |
166 | if (pi.flags & ATA_FLAG_EM) |
167 | ahci_reset_em(host); |
168 | |
169 | for (i = 0; i < host->n_ports; i++) { |
170 | struct ata_port *ap = host->ports[i]; |
171 | |
172 | ata_port_desc(ap, "mmio %pR", mem); |
173 | ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); |
174 | |
175 | /* set enclosure management message type */ |
176 | if (ap->flags & ATA_FLAG_EM) |
177 | ap->em_message_type = hpriv->em_msg_type; |
178 | |
179 | /* disabled/not-implemented port */ |
180 | if (!(hpriv->port_map & (1 << i))) |
181 | ap->ops = &ata_dummy_port_ops; |
182 | } |
183 | |
184 | rc = ahci_reset_controller(host); |
185 | if (rc) |
186 | goto err0; |
187 | |
188 | ahci_init_controller(host); |
189 | ahci_print_info(host, "platform"); |
190 | |
191 | rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, |
192 | &ahci_platform_sht); |
193 | if (rc) |
194 | goto err0; |
195 | |
196 | return 0; |
197 | err0: |
198 | if (pdata && pdata->exit) |
199 | pdata->exit(dev); |
200 | return rc; |
201 | } |
202 | |
203 | static int __devexit ahci_remove(struct platform_device *pdev) |
204 | { |
205 | struct device *dev = &pdev->dev; |
206 | struct ahci_platform_data *pdata = dev_get_platdata(dev); |
207 | struct ata_host *host = dev_get_drvdata(dev); |
208 | |
209 | ata_host_detach(host); |
210 | |
211 | if (pdata && pdata->exit) |
212 | pdata->exit(dev); |
213 | |
214 | return 0; |
215 | } |
216 | |
217 | #ifdef CONFIG_PM |
218 | static int ahci_suspend(struct device *dev) |
219 | { |
220 | struct ahci_platform_data *pdata = dev_get_platdata(dev); |
221 | struct ata_host *host = dev_get_drvdata(dev); |
222 | struct ahci_host_priv *hpriv = host->private_data; |
223 | void __iomem *mmio = hpriv->mmio; |
224 | u32 ctl; |
225 | int rc; |
226 | |
227 | if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { |
228 | dev_err(dev, "firmware update required for suspend/resume\n"); |
229 | return -EIO; |
230 | } |
231 | |
232 | /* |
233 | * AHCI spec rev1.1 section 8.3.3: |
234 | * Software must disable interrupts prior to requesting a |
235 | * transition of the HBA to D3 state. |
236 | */ |
237 | ctl = readl(mmio + HOST_CTL); |
238 | ctl &= ~HOST_IRQ_EN; |
239 | writel(ctl, mmio + HOST_CTL); |
240 | readl(mmio + HOST_CTL); /* flush */ |
241 | |
242 | rc = ata_host_suspend(host, PMSG_SUSPEND); |
243 | if (rc) |
244 | return rc; |
245 | |
246 | if (pdata && pdata->suspend) |
247 | return pdata->suspend(dev); |
248 | return 0; |
249 | } |
250 | |
251 | static int ahci_resume(struct device *dev) |
252 | { |
253 | struct ahci_platform_data *pdata = dev_get_platdata(dev); |
254 | struct ata_host *host = dev_get_drvdata(dev); |
255 | int rc; |
256 | |
257 | if (pdata && pdata->resume) { |
258 | rc = pdata->resume(dev); |
259 | if (rc) |
260 | return rc; |
261 | } |
262 | |
263 | if (dev->power.power_state.event == PM_EVENT_SUSPEND) { |
264 | rc = ahci_reset_controller(host); |
265 | if (rc) |
266 | return rc; |
267 | |
268 | ahci_init_controller(host); |
269 | } |
270 | |
271 | ata_host_resume(host); |
272 | |
273 | return 0; |
274 | } |
275 | #endif |
276 | |
277 | SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume); |
278 | |
279 | static const struct of_device_id ahci_of_match[] = { |
280 | { .compatible = "calxeda,hb-ahci", }, |
281 | { .compatible = "snps,spear-ahci", }, |
282 | {}, |
283 | }; |
284 | MODULE_DEVICE_TABLE(of, ahci_of_match); |
285 | |
286 | static struct platform_driver ahci_driver = { |
287 | .remove = __devexit_p(ahci_remove), |
288 | .driver = { |
289 | .name = "ahci", |
290 | .owner = THIS_MODULE, |
291 | .of_match_table = ahci_of_match, |
292 | .pm = &ahci_pm_ops, |
293 | }, |
294 | .id_table = ahci_devtype, |
295 | }; |
296 | |
297 | static int __init ahci_init(void) |
298 | { |
299 | return platform_driver_probe(&ahci_driver, ahci_probe); |
300 | } |
301 | module_init(ahci_init); |
302 | |
303 | static void __exit ahci_exit(void) |
304 | { |
305 | platform_driver_unregister(&ahci_driver); |
306 | } |
307 | module_exit(ahci_exit); |
308 | |
309 | MODULE_DESCRIPTION("AHCI SATA platform driver"); |
310 | MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); |
311 | MODULE_LICENSE("GPL"); |
312 | MODULE_ALIAS("platform:ahci"); |
313 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9