diff options
Diffstat (limited to 'drivers/scsi/mvsas/mv_init.c')
-rw-r--r-- | drivers/scsi/mvsas/mv_init.c | 703 |
1 files changed, 703 insertions, 0 deletions
diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c new file mode 100644 index 000000000000..8646a19f999d --- /dev/null +++ b/drivers/scsi/mvsas/mv_init.c | |||
@@ -0,0 +1,703 @@ | |||
1 | /* | ||
2 | * Marvell 88SE64xx/88SE94xx pci init | ||
3 | * | ||
4 | * Copyright 2007 Red Hat, Inc. | ||
5 | * Copyright 2008 Marvell. <kewei@marvell.com> | ||
6 | * | ||
7 | * This file is licensed under GPLv2. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation; version 2 of the | ||
12 | * License. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | ||
22 | * USA | ||
23 | */ | ||
24 | |||
25 | |||
26 | #include "mv_sas.h" | ||
27 | |||
28 | static struct scsi_transport_template *mvs_stt; | ||
29 | static const struct mvs_chip_info mvs_chips[] = { | ||
30 | [chip_6320] = { 1, 2, 0x400, 17, 16, 9, &mvs_64xx_dispatch, }, | ||
31 | [chip_6440] = { 1, 4, 0x400, 17, 16, 9, &mvs_64xx_dispatch, }, | ||
32 | [chip_6485] = { 1, 8, 0x800, 33, 32, 10, &mvs_64xx_dispatch, }, | ||
33 | [chip_9180] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, }, | ||
34 | [chip_9480] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, }, | ||
35 | }; | ||
36 | |||
37 | #define SOC_SAS_NUM 2 | ||
38 | |||
39 | static struct scsi_host_template mvs_sht = { | ||
40 | .module = THIS_MODULE, | ||
41 | .name = DRV_NAME, | ||
42 | .queuecommand = sas_queuecommand, | ||
43 | .target_alloc = sas_target_alloc, | ||
44 | .slave_configure = mvs_slave_configure, | ||
45 | .slave_destroy = sas_slave_destroy, | ||
46 | .scan_finished = mvs_scan_finished, | ||
47 | .scan_start = mvs_scan_start, | ||
48 | .change_queue_depth = sas_change_queue_depth, | ||
49 | .change_queue_type = sas_change_queue_type, | ||
50 | .bios_param = sas_bios_param, | ||
51 | .can_queue = 1, | ||
52 | .cmd_per_lun = 1, | ||
53 | .this_id = -1, | ||
54 | .sg_tablesize = SG_ALL, | ||
55 | .max_sectors = SCSI_DEFAULT_MAX_SECTORS, | ||
56 | .use_clustering = ENABLE_CLUSTERING, | ||
57 | .eh_device_reset_handler = sas_eh_device_reset_handler, | ||
58 | .eh_bus_reset_handler = sas_eh_bus_reset_handler, | ||
59 | .slave_alloc = mvs_slave_alloc, | ||
60 | .target_destroy = sas_target_destroy, | ||
61 | .ioctl = sas_ioctl, | ||
62 | }; | ||
63 | |||
64 | static struct sas_domain_function_template mvs_transport_ops = { | ||
65 | .lldd_dev_found = mvs_dev_found, | ||
66 | .lldd_dev_gone = mvs_dev_gone, | ||
67 | |||
68 | .lldd_execute_task = mvs_queue_command, | ||
69 | .lldd_control_phy = mvs_phy_control, | ||
70 | |||
71 | .lldd_abort_task = mvs_abort_task, | ||
72 | .lldd_abort_task_set = mvs_abort_task_set, | ||
73 | .lldd_clear_aca = mvs_clear_aca, | ||
74 | .lldd_clear_task_set = mvs_clear_task_set, | ||
75 | .lldd_I_T_nexus_reset = mvs_I_T_nexus_reset, | ||
76 | .lldd_lu_reset = mvs_lu_reset, | ||
77 | .lldd_query_task = mvs_query_task, | ||
78 | |||
79 | .lldd_port_formed = mvs_port_formed, | ||
80 | .lldd_port_deformed = mvs_port_deformed, | ||
81 | |||
82 | }; | ||
83 | |||
84 | static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id) | ||
85 | { | ||
86 | struct mvs_phy *phy = &mvi->phy[phy_id]; | ||
87 | struct asd_sas_phy *sas_phy = &phy->sas_phy; | ||
88 | |||
89 | phy->mvi = mvi; | ||
90 | init_timer(&phy->timer); | ||
91 | sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0; | ||
92 | sas_phy->class = SAS; | ||
93 | sas_phy->iproto = SAS_PROTOCOL_ALL; | ||
94 | sas_phy->tproto = 0; | ||
95 | sas_phy->type = PHY_TYPE_PHYSICAL; | ||
96 | sas_phy->role = PHY_ROLE_INITIATOR; | ||
97 | sas_phy->oob_mode = OOB_NOT_CONNECTED; | ||
98 | sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; | ||
99 | |||
100 | sas_phy->id = phy_id; | ||
101 | sas_phy->sas_addr = &mvi->sas_addr[0]; | ||
102 | sas_phy->frame_rcvd = &phy->frame_rcvd[0]; | ||
103 | sas_phy->ha = (struct sas_ha_struct *)mvi->shost->hostdata; | ||
104 | sas_phy->lldd_phy = phy; | ||
105 | } | ||
106 | |||
107 | static void mvs_free(struct mvs_info *mvi) | ||
108 | { | ||
109 | int i; | ||
110 | struct mvs_wq *mwq; | ||
111 | int slot_nr; | ||
112 | |||
113 | if (!mvi) | ||
114 | return; | ||
115 | |||
116 | if (mvi->flags & MVF_FLAG_SOC) | ||
117 | slot_nr = MVS_SOC_SLOTS; | ||
118 | else | ||
119 | slot_nr = MVS_SLOTS; | ||
120 | |||
121 | for (i = 0; i < mvi->tags_num; i++) { | ||
122 | struct mvs_slot_info *slot = &mvi->slot_info[i]; | ||
123 | if (slot->buf) | ||
124 | dma_free_coherent(mvi->dev, MVS_SLOT_BUF_SZ, | ||
125 | slot->buf, slot->buf_dma); | ||
126 | } | ||
127 | |||
128 | if (mvi->tx) | ||
129 | dma_free_coherent(mvi->dev, | ||
130 | sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ, | ||
131 | mvi->tx, mvi->tx_dma); | ||
132 | if (mvi->rx_fis) | ||
133 | dma_free_coherent(mvi->dev, MVS_RX_FISL_SZ, | ||
134 | mvi->rx_fis, mvi->rx_fis_dma); | ||
135 | if (mvi->rx) | ||
136 | dma_free_coherent(mvi->dev, | ||
137 | sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1), | ||
138 | mvi->rx, mvi->rx_dma); | ||
139 | if (mvi->slot) | ||
140 | dma_free_coherent(mvi->dev, | ||
141 | sizeof(*mvi->slot) * slot_nr, | ||
142 | mvi->slot, mvi->slot_dma); | ||
143 | #ifndef DISABLE_HOTPLUG_DMA_FIX | ||
144 | if (mvi->bulk_buffer) | ||
145 | dma_free_coherent(mvi->dev, TRASH_BUCKET_SIZE, | ||
146 | mvi->bulk_buffer, mvi->bulk_buffer_dma); | ||
147 | #endif | ||
148 | |||
149 | MVS_CHIP_DISP->chip_iounmap(mvi); | ||
150 | if (mvi->shost) | ||
151 | scsi_host_put(mvi->shost); | ||
152 | list_for_each_entry(mwq, &mvi->wq_list, entry) | ||
153 | cancel_delayed_work(&mwq->work_q); | ||
154 | kfree(mvi); | ||
155 | } | ||
156 | |||
157 | #ifdef MVS_USE_TASKLET | ||
158 | struct tasklet_struct mv_tasklet; | ||
159 | static void mvs_tasklet(unsigned long opaque) | ||
160 | { | ||
161 | unsigned long flags; | ||
162 | u32 stat; | ||
163 | u16 core_nr, i = 0; | ||
164 | |||
165 | struct mvs_info *mvi; | ||
166 | struct sas_ha_struct *sha = (struct sas_ha_struct *)opaque; | ||
167 | |||
168 | core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host; | ||
169 | mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0]; | ||
170 | |||
171 | if (unlikely(!mvi)) | ||
172 | BUG_ON(1); | ||
173 | |||
174 | for (i = 0; i < core_nr; i++) { | ||
175 | mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i]; | ||
176 | stat = MVS_CHIP_DISP->isr_status(mvi, mvi->irq); | ||
177 | if (stat) | ||
178 | MVS_CHIP_DISP->isr(mvi, mvi->irq, stat); | ||
179 | } | ||
180 | |||
181 | } | ||
182 | #endif | ||
183 | |||
184 | static irqreturn_t mvs_interrupt(int irq, void *opaque) | ||
185 | { | ||
186 | u32 core_nr, i = 0; | ||
187 | u32 stat; | ||
188 | struct mvs_info *mvi; | ||
189 | struct sas_ha_struct *sha = opaque; | ||
190 | |||
191 | core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host; | ||
192 | mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0]; | ||
193 | |||
194 | if (unlikely(!mvi)) | ||
195 | return IRQ_NONE; | ||
196 | |||
197 | stat = MVS_CHIP_DISP->isr_status(mvi, irq); | ||
198 | if (!stat) | ||
199 | return IRQ_NONE; | ||
200 | |||
201 | #ifdef MVS_USE_TASKLET | ||
202 | tasklet_schedule(&mv_tasklet); | ||
203 | #else | ||
204 | for (i = 0; i < core_nr; i++) { | ||
205 | mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i]; | ||
206 | MVS_CHIP_DISP->isr(mvi, irq, stat); | ||
207 | } | ||
208 | #endif | ||
209 | return IRQ_HANDLED; | ||
210 | } | ||
211 | |||
212 | static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost) | ||
213 | { | ||
214 | int i, slot_nr; | ||
215 | |||
216 | if (mvi->flags & MVF_FLAG_SOC) | ||
217 | slot_nr = MVS_SOC_SLOTS; | ||
218 | else | ||
219 | slot_nr = MVS_SLOTS; | ||
220 | |||
221 | spin_lock_init(&mvi->lock); | ||
222 | for (i = 0; i < mvi->chip->n_phy; i++) { | ||
223 | mvs_phy_init(mvi, i); | ||
224 | mvi->port[i].wide_port_phymap = 0; | ||
225 | mvi->port[i].port_attached = 0; | ||
226 | INIT_LIST_HEAD(&mvi->port[i].list); | ||
227 | } | ||
228 | for (i = 0; i < MVS_MAX_DEVICES; i++) { | ||
229 | mvi->devices[i].taskfileset = MVS_ID_NOT_MAPPED; | ||
230 | mvi->devices[i].dev_type = NO_DEVICE; | ||
231 | mvi->devices[i].device_id = i; | ||
232 | mvi->devices[i].dev_status = MVS_DEV_NORMAL; | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * alloc and init our DMA areas | ||
237 | */ | ||
238 | mvi->tx = dma_alloc_coherent(mvi->dev, | ||
239 | sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ, | ||
240 | &mvi->tx_dma, GFP_KERNEL); | ||
241 | if (!mvi->tx) | ||
242 | goto err_out; | ||
243 | memset(mvi->tx, 0, sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ); | ||
244 | mvi->rx_fis = dma_alloc_coherent(mvi->dev, MVS_RX_FISL_SZ, | ||
245 | &mvi->rx_fis_dma, GFP_KERNEL); | ||
246 | if (!mvi->rx_fis) | ||
247 | goto err_out; | ||
248 | memset(mvi->rx_fis, 0, MVS_RX_FISL_SZ); | ||
249 | |||
250 | mvi->rx = dma_alloc_coherent(mvi->dev, | ||
251 | sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1), | ||
252 | &mvi->rx_dma, GFP_KERNEL); | ||
253 | if (!mvi->rx) | ||
254 | goto err_out; | ||
255 | memset(mvi->rx, 0, sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1)); | ||
256 | mvi->rx[0] = cpu_to_le32(0xfff); | ||
257 | mvi->rx_cons = 0xfff; | ||
258 | |||
259 | mvi->slot = dma_alloc_coherent(mvi->dev, | ||
260 | sizeof(*mvi->slot) * slot_nr, | ||
261 | &mvi->slot_dma, GFP_KERNEL); | ||
262 | if (!mvi->slot) | ||
263 | goto err_out; | ||
264 | memset(mvi->slot, 0, sizeof(*mvi->slot) * slot_nr); | ||
265 | |||
266 | #ifndef DISABLE_HOTPLUG_DMA_FIX | ||
267 | mvi->bulk_buffer = dma_alloc_coherent(mvi->dev, | ||
268 | TRASH_BUCKET_SIZE, | ||
269 | &mvi->bulk_buffer_dma, GFP_KERNEL); | ||
270 | if (!mvi->bulk_buffer) | ||
271 | goto err_out; | ||
272 | #endif | ||
273 | for (i = 0; i < slot_nr; i++) { | ||
274 | struct mvs_slot_info *slot = &mvi->slot_info[i]; | ||
275 | |||
276 | slot->buf = dma_alloc_coherent(mvi->dev, MVS_SLOT_BUF_SZ, | ||
277 | &slot->buf_dma, GFP_KERNEL); | ||
278 | if (!slot->buf) { | ||
279 | printk(KERN_DEBUG"failed to allocate slot->buf.\n"); | ||
280 | goto err_out; | ||
281 | } | ||
282 | memset(slot->buf, 0, MVS_SLOT_BUF_SZ); | ||
283 | ++mvi->tags_num; | ||
284 | } | ||
285 | /* Initialize tags */ | ||
286 | mvs_tag_init(mvi); | ||
287 | return 0; | ||
288 | err_out: | ||
289 | return 1; | ||
290 | } | ||
291 | |||
292 | |||
293 | int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex) | ||
294 | { | ||
295 | unsigned long res_start, res_len, res_flag, res_flag_ex = 0; | ||
296 | struct pci_dev *pdev = mvi->pdev; | ||
297 | if (bar_ex != -1) { | ||
298 | /* | ||
299 | * ioremap main and peripheral registers | ||
300 | */ | ||
301 | res_start = pci_resource_start(pdev, bar_ex); | ||
302 | res_len = pci_resource_len(pdev, bar_ex); | ||
303 | if (!res_start || !res_len) | ||
304 | goto err_out; | ||
305 | |||
306 | res_flag_ex = pci_resource_flags(pdev, bar_ex); | ||
307 | if (res_flag_ex & IORESOURCE_MEM) { | ||
308 | if (res_flag_ex & IORESOURCE_CACHEABLE) | ||
309 | mvi->regs_ex = ioremap(res_start, res_len); | ||
310 | else | ||
311 | mvi->regs_ex = ioremap_nocache(res_start, | ||
312 | res_len); | ||
313 | } else | ||
314 | mvi->regs_ex = (void *)res_start; | ||
315 | if (!mvi->regs_ex) | ||
316 | goto err_out; | ||
317 | } | ||
318 | |||
319 | res_start = pci_resource_start(pdev, bar); | ||
320 | res_len = pci_resource_len(pdev, bar); | ||
321 | if (!res_start || !res_len) | ||
322 | goto err_out; | ||
323 | |||
324 | res_flag = pci_resource_flags(pdev, bar); | ||
325 | if (res_flag & IORESOURCE_CACHEABLE) | ||
326 | mvi->regs = ioremap(res_start, res_len); | ||
327 | else | ||
328 | mvi->regs = ioremap_nocache(res_start, res_len); | ||
329 | |||
330 | if (!mvi->regs) { | ||
331 | if (mvi->regs_ex && (res_flag_ex & IORESOURCE_MEM)) | ||
332 | iounmap(mvi->regs_ex); | ||
333 | mvi->regs_ex = NULL; | ||
334 | goto err_out; | ||
335 | } | ||
336 | |||
337 | return 0; | ||
338 | err_out: | ||
339 | return -1; | ||
340 | } | ||
341 | |||
342 | void mvs_iounmap(void __iomem *regs) | ||
343 | { | ||
344 | iounmap(regs); | ||
345 | } | ||
346 | |||
347 | static struct mvs_info *__devinit mvs_pci_alloc(struct pci_dev *pdev, | ||
348 | const struct pci_device_id *ent, | ||
349 | struct Scsi_Host *shost, unsigned int id) | ||
350 | { | ||
351 | struct mvs_info *mvi; | ||
352 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); | ||
353 | |||
354 | mvi = kzalloc(sizeof(*mvi) + MVS_SLOTS * sizeof(struct mvs_slot_info), | ||
355 | GFP_KERNEL); | ||
356 | if (!mvi) | ||
357 | return NULL; | ||
358 | |||
359 | mvi->pdev = pdev; | ||
360 | mvi->dev = &pdev->dev; | ||
361 | mvi->chip_id = ent->driver_data; | ||
362 | mvi->chip = &mvs_chips[mvi->chip_id]; | ||
363 | INIT_LIST_HEAD(&mvi->wq_list); | ||
364 | mvi->irq = pdev->irq; | ||
365 | |||
366 | ((struct mvs_prv_info *)sha->lldd_ha)->mvi[id] = mvi; | ||
367 | ((struct mvs_prv_info *)sha->lldd_ha)->n_phy = mvi->chip->n_phy; | ||
368 | |||
369 | mvi->id = id; | ||
370 | mvi->sas = sha; | ||
371 | mvi->shost = shost; | ||
372 | #ifdef MVS_USE_TASKLET | ||
373 | tasklet_init(&mv_tasklet, mvs_tasklet, (unsigned long)sha); | ||
374 | #endif | ||
375 | |||
376 | if (MVS_CHIP_DISP->chip_ioremap(mvi)) | ||
377 | goto err_out; | ||
378 | if (!mvs_alloc(mvi, shost)) | ||
379 | return mvi; | ||
380 | err_out: | ||
381 | mvs_free(mvi); | ||
382 | return NULL; | ||
383 | } | ||
384 | |||
385 | /* move to PCI layer or libata core? */ | ||
386 | static int pci_go_64(struct pci_dev *pdev) | ||
387 | { | ||
388 | int rc; | ||
389 | |||
390 | if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { | ||
391 | rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); | ||
392 | if (rc) { | ||
393 | rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); | ||
394 | if (rc) { | ||
395 | dev_printk(KERN_ERR, &pdev->dev, | ||
396 | "64-bit DMA enable failed\n"); | ||
397 | return rc; | ||
398 | } | ||
399 | } | ||
400 | } else { | ||
401 | rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); | ||
402 | if (rc) { | ||
403 | dev_printk(KERN_ERR, &pdev->dev, | ||
404 | "32-bit DMA enable failed\n"); | ||
405 | return rc; | ||
406 | } | ||
407 | rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); | ||
408 | if (rc) { | ||
409 | dev_printk(KERN_ERR, &pdev->dev, | ||
410 | "32-bit consistent DMA enable failed\n"); | ||
411 | return rc; | ||
412 | } | ||
413 | } | ||
414 | |||
415 | return rc; | ||
416 | } | ||
417 | |||
418 | static int __devinit mvs_prep_sas_ha_init(struct Scsi_Host *shost, | ||
419 | const struct mvs_chip_info *chip_info) | ||
420 | { | ||
421 | int phy_nr, port_nr; unsigned short core_nr; | ||
422 | struct asd_sas_phy **arr_phy; | ||
423 | struct asd_sas_port **arr_port; | ||
424 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); | ||
425 | |||
426 | core_nr = chip_info->n_host; | ||
427 | phy_nr = core_nr * chip_info->n_phy; | ||
428 | port_nr = phy_nr; | ||
429 | |||
430 | memset(sha, 0x00, sizeof(struct sas_ha_struct)); | ||
431 | arr_phy = kcalloc(phy_nr, sizeof(void *), GFP_KERNEL); | ||
432 | arr_port = kcalloc(port_nr, sizeof(void *), GFP_KERNEL); | ||
433 | if (!arr_phy || !arr_port) | ||
434 | goto exit_free; | ||
435 | |||
436 | sha->sas_phy = arr_phy; | ||
437 | sha->sas_port = arr_port; | ||
438 | |||
439 | sha->lldd_ha = kzalloc(sizeof(struct mvs_prv_info), GFP_KERNEL); | ||
440 | if (!sha->lldd_ha) | ||
441 | goto exit_free; | ||
442 | |||
443 | ((struct mvs_prv_info *)sha->lldd_ha)->n_host = core_nr; | ||
444 | |||
445 | shost->transportt = mvs_stt; | ||
446 | shost->max_id = 128; | ||
447 | shost->max_lun = ~0; | ||
448 | shost->max_channel = 1; | ||
449 | shost->max_cmd_len = 16; | ||
450 | |||
451 | return 0; | ||
452 | exit_free: | ||
453 | kfree(arr_phy); | ||
454 | kfree(arr_port); | ||
455 | return -1; | ||
456 | |||
457 | } | ||
458 | |||
459 | static void __devinit mvs_post_sas_ha_init(struct Scsi_Host *shost, | ||
460 | const struct mvs_chip_info *chip_info) | ||
461 | { | ||
462 | int can_queue, i = 0, j = 0; | ||
463 | struct mvs_info *mvi = NULL; | ||
464 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); | ||
465 | unsigned short nr_core = ((struct mvs_prv_info *)sha->lldd_ha)->n_host; | ||
466 | |||
467 | for (j = 0; j < nr_core; j++) { | ||
468 | mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[j]; | ||
469 | for (i = 0; i < chip_info->n_phy; i++) { | ||
470 | sha->sas_phy[j * chip_info->n_phy + i] = | ||
471 | &mvi->phy[i].sas_phy; | ||
472 | sha->sas_port[j * chip_info->n_phy + i] = | ||
473 | &mvi->port[i].sas_port; | ||
474 | } | ||
475 | } | ||
476 | |||
477 | sha->sas_ha_name = DRV_NAME; | ||
478 | sha->dev = mvi->dev; | ||
479 | sha->lldd_module = THIS_MODULE; | ||
480 | sha->sas_addr = &mvi->sas_addr[0]; | ||
481 | |||
482 | sha->num_phys = nr_core * chip_info->n_phy; | ||
483 | |||
484 | sha->lldd_max_execute_num = 1; | ||
485 | |||
486 | if (mvi->flags & MVF_FLAG_SOC) | ||
487 | can_queue = MVS_SOC_CAN_QUEUE; | ||
488 | else | ||
489 | can_queue = MVS_CAN_QUEUE; | ||
490 | |||
491 | sha->lldd_queue_size = can_queue; | ||
492 | shost->can_queue = can_queue; | ||
493 | mvi->shost->cmd_per_lun = MVS_SLOTS/sha->num_phys; | ||
494 | sha->core.shost = mvi->shost; | ||
495 | } | ||
496 | |||
497 | static void mvs_init_sas_add(struct mvs_info *mvi) | ||
498 | { | ||
499 | u8 i; | ||
500 | for (i = 0; i < mvi->chip->n_phy; i++) { | ||
501 | mvi->phy[i].dev_sas_addr = 0x5005043011ab0000ULL; | ||
502 | mvi->phy[i].dev_sas_addr = | ||
503 | cpu_to_be64((u64)(*(u64 *)&mvi->phy[i].dev_sas_addr)); | ||
504 | } | ||
505 | |||
506 | memcpy(mvi->sas_addr, &mvi->phy[0].dev_sas_addr, SAS_ADDR_SIZE); | ||
507 | } | ||
508 | |||
509 | static int __devinit mvs_pci_init(struct pci_dev *pdev, | ||
510 | const struct pci_device_id *ent) | ||
511 | { | ||
512 | unsigned int rc, nhost = 0; | ||
513 | struct mvs_info *mvi; | ||
514 | irq_handler_t irq_handler = mvs_interrupt; | ||
515 | struct Scsi_Host *shost = NULL; | ||
516 | const struct mvs_chip_info *chip; | ||
517 | |||
518 | dev_printk(KERN_INFO, &pdev->dev, | ||
519 | "mvsas: driver version %s\n", DRV_VERSION); | ||
520 | rc = pci_enable_device(pdev); | ||
521 | if (rc) | ||
522 | goto err_out_enable; | ||
523 | |||
524 | pci_set_master(pdev); | ||
525 | |||
526 | rc = pci_request_regions(pdev, DRV_NAME); | ||
527 | if (rc) | ||
528 | goto err_out_disable; | ||
529 | |||
530 | rc = pci_go_64(pdev); | ||
531 | if (rc) | ||
532 | goto err_out_regions; | ||
533 | |||
534 | shost = scsi_host_alloc(&mvs_sht, sizeof(void *)); | ||
535 | if (!shost) { | ||
536 | rc = -ENOMEM; | ||
537 | goto err_out_regions; | ||
538 | } | ||
539 | |||
540 | chip = &mvs_chips[ent->driver_data]; | ||
541 | SHOST_TO_SAS_HA(shost) = | ||
542 | kcalloc(1, sizeof(struct sas_ha_struct), GFP_KERNEL); | ||
543 | if (!SHOST_TO_SAS_HA(shost)) { | ||
544 | kfree(shost); | ||
545 | rc = -ENOMEM; | ||
546 | goto err_out_regions; | ||
547 | } | ||
548 | |||
549 | rc = mvs_prep_sas_ha_init(shost, chip); | ||
550 | if (rc) { | ||
551 | kfree(shost); | ||
552 | rc = -ENOMEM; | ||
553 | goto err_out_regions; | ||
554 | } | ||
555 | |||
556 | pci_set_drvdata(pdev, SHOST_TO_SAS_HA(shost)); | ||
557 | |||
558 | do { | ||
559 | mvi = mvs_pci_alloc(pdev, ent, shost, nhost); | ||
560 | if (!mvi) { | ||
561 | rc = -ENOMEM; | ||
562 | goto err_out_regions; | ||
563 | } | ||
564 | |||
565 | mvs_init_sas_add(mvi); | ||
566 | |||
567 | mvi->instance = nhost; | ||
568 | rc = MVS_CHIP_DISP->chip_init(mvi); | ||
569 | if (rc) { | ||
570 | mvs_free(mvi); | ||
571 | goto err_out_regions; | ||
572 | } | ||
573 | nhost++; | ||
574 | } while (nhost < chip->n_host); | ||
575 | |||
576 | mvs_post_sas_ha_init(shost, chip); | ||
577 | |||
578 | rc = scsi_add_host(shost, &pdev->dev); | ||
579 | if (rc) | ||
580 | goto err_out_shost; | ||
581 | |||
582 | rc = sas_register_ha(SHOST_TO_SAS_HA(shost)); | ||
583 | if (rc) | ||
584 | goto err_out_shost; | ||
585 | rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, | ||
586 | DRV_NAME, SHOST_TO_SAS_HA(shost)); | ||
587 | if (rc) | ||
588 | goto err_not_sas; | ||
589 | |||
590 | MVS_CHIP_DISP->interrupt_enable(mvi); | ||
591 | |||
592 | scsi_scan_host(mvi->shost); | ||
593 | |||
594 | return 0; | ||
595 | |||
596 | err_not_sas: | ||
597 | sas_unregister_ha(SHOST_TO_SAS_HA(shost)); | ||
598 | err_out_shost: | ||
599 | scsi_remove_host(mvi->shost); | ||
600 | err_out_regions: | ||
601 | pci_release_regions(pdev); | ||
602 | err_out_disable: | ||
603 | pci_disable_device(pdev); | ||
604 | err_out_enable: | ||
605 | return rc; | ||
606 | } | ||
607 | |||
608 | static void __devexit mvs_pci_remove(struct pci_dev *pdev) | ||
609 | { | ||
610 | unsigned short core_nr, i = 0; | ||
611 | struct sas_ha_struct *sha = pci_get_drvdata(pdev); | ||
612 | struct mvs_info *mvi = NULL; | ||
613 | |||
614 | core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host; | ||
615 | mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0]; | ||
616 | |||
617 | #ifdef MVS_USE_TASKLET | ||
618 | tasklet_kill(&mv_tasklet); | ||
619 | #endif | ||
620 | |||
621 | pci_set_drvdata(pdev, NULL); | ||
622 | sas_unregister_ha(sha); | ||
623 | sas_remove_host(mvi->shost); | ||
624 | scsi_remove_host(mvi->shost); | ||
625 | |||
626 | MVS_CHIP_DISP->interrupt_disable(mvi); | ||
627 | free_irq(mvi->irq, sha); | ||
628 | for (i = 0; i < core_nr; i++) { | ||
629 | mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i]; | ||
630 | mvs_free(mvi); | ||
631 | } | ||
632 | kfree(sha->sas_phy); | ||
633 | kfree(sha->sas_port); | ||
634 | kfree(sha); | ||
635 | pci_release_regions(pdev); | ||
636 | pci_disable_device(pdev); | ||
637 | return; | ||
638 | } | ||
639 | |||
640 | static struct pci_device_id __devinitdata mvs_pci_table[] = { | ||
641 | { PCI_VDEVICE(MARVELL, 0x6320), chip_6320 }, | ||
642 | { PCI_VDEVICE(MARVELL, 0x6340), chip_6440 }, | ||
643 | { | ||
644 | .vendor = PCI_VENDOR_ID_MARVELL, | ||
645 | .device = 0x6440, | ||
646 | .subvendor = PCI_ANY_ID, | ||
647 | .subdevice = 0x6480, | ||
648 | .class = 0, | ||
649 | .class_mask = 0, | ||
650 | .driver_data = chip_6485, | ||
651 | }, | ||
652 | { PCI_VDEVICE(MARVELL, 0x6440), chip_6440 }, | ||
653 | { PCI_VDEVICE(MARVELL, 0x6485), chip_6485 }, | ||
654 | { PCI_VDEVICE(MARVELL, 0x9480), chip_9480 }, | ||
655 | { PCI_VDEVICE(MARVELL, 0x9180), chip_9180 }, | ||
656 | |||
657 | { } /* terminate list */ | ||
658 | }; | ||
659 | |||
660 | static struct pci_driver mvs_pci_driver = { | ||
661 | .name = DRV_NAME, | ||
662 | .id_table = mvs_pci_table, | ||
663 | .probe = mvs_pci_init, | ||
664 | .remove = __devexit_p(mvs_pci_remove), | ||
665 | }; | ||
666 | |||
667 | /* task handler */ | ||
668 | struct task_struct *mvs_th; | ||
669 | static int __init mvs_init(void) | ||
670 | { | ||
671 | int rc; | ||
672 | mvs_stt = sas_domain_attach_transport(&mvs_transport_ops); | ||
673 | if (!mvs_stt) | ||
674 | return -ENOMEM; | ||
675 | |||
676 | rc = pci_register_driver(&mvs_pci_driver); | ||
677 | |||
678 | if (rc) | ||
679 | goto err_out; | ||
680 | |||
681 | return 0; | ||
682 | |||
683 | err_out: | ||
684 | sas_release_transport(mvs_stt); | ||
685 | return rc; | ||
686 | } | ||
687 | |||
688 | static void __exit mvs_exit(void) | ||
689 | { | ||
690 | pci_unregister_driver(&mvs_pci_driver); | ||
691 | sas_release_transport(mvs_stt); | ||
692 | } | ||
693 | |||
694 | module_init(mvs_init); | ||
695 | module_exit(mvs_exit); | ||
696 | |||
697 | MODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>"); | ||
698 | MODULE_DESCRIPTION("Marvell 88SE6440 SAS/SATA controller driver"); | ||
699 | MODULE_VERSION(DRV_VERSION); | ||
700 | MODULE_LICENSE("GPL"); | ||
701 | #ifdef CONFIG_PCI | ||
702 | MODULE_DEVICE_TABLE(pci, mvs_pci_table); | ||
703 | #endif | ||