diff options
Diffstat (limited to 'drivers/scsi/mvsas/mv_init.c')
-rw-r--r-- | drivers/scsi/mvsas/mv_init.c | 629 |
1 files changed, 404 insertions, 225 deletions
diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index 258a1a923290..8646a19f999d 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c | |||
@@ -1,38 +1,41 @@ | |||
1 | /* | 1 | /* |
2 | mv_init.c - Marvell 88SE6440 SAS/SATA init support | 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 | */ | ||
3 | 24 | ||
4 | Copyright 2007 Red Hat, Inc. | ||
5 | Copyright 2008 Marvell. <kewei@marvell.com> | ||
6 | |||
7 | This program is free software; you can redistribute it and/or | ||
8 | modify it under the terms of the GNU General Public License as | ||
9 | published by the Free Software Foundation; either version 2, | ||
10 | or (at your option) any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty | ||
14 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
15 | See the GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public | ||
18 | License along with this program; see the file COPYING. If not, | ||
19 | write to the Free Software Foundation, 675 Mass Ave, Cambridge, | ||
20 | MA 02139, USA. | ||
21 | |||
22 | */ | ||
23 | 25 | ||
24 | #include "mv_sas.h" | 26 | #include "mv_sas.h" |
25 | #include "mv_64xx.h" | ||
26 | #include "mv_chips.h" | ||
27 | 27 | ||
28 | static struct scsi_transport_template *mvs_stt; | 28 | static struct scsi_transport_template *mvs_stt; |
29 | |||
30 | static const struct mvs_chip_info mvs_chips[] = { | 29 | static const struct mvs_chip_info mvs_chips[] = { |
31 | [chip_6320] = { 2, 16, 9 }, | 30 | [chip_6320] = { 1, 2, 0x400, 17, 16, 9, &mvs_64xx_dispatch, }, |
32 | [chip_6440] = { 4, 16, 9 }, | 31 | [chip_6440] = { 1, 4, 0x400, 17, 16, 9, &mvs_64xx_dispatch, }, |
33 | [chip_6480] = { 8, 32, 10 }, | 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, }, | ||
34 | }; | 35 | }; |
35 | 36 | ||
37 | #define SOC_SAS_NUM 2 | ||
38 | |||
36 | static struct scsi_host_template mvs_sht = { | 39 | static struct scsi_host_template mvs_sht = { |
37 | .module = THIS_MODULE, | 40 | .module = THIS_MODULE, |
38 | .name = DRV_NAME, | 41 | .name = DRV_NAME, |
@@ -53,17 +56,29 @@ static struct scsi_host_template mvs_sht = { | |||
53 | .use_clustering = ENABLE_CLUSTERING, | 56 | .use_clustering = ENABLE_CLUSTERING, |
54 | .eh_device_reset_handler = sas_eh_device_reset_handler, | 57 | .eh_device_reset_handler = sas_eh_device_reset_handler, |
55 | .eh_bus_reset_handler = sas_eh_bus_reset_handler, | 58 | .eh_bus_reset_handler = sas_eh_bus_reset_handler, |
56 | .slave_alloc = sas_slave_alloc, | 59 | .slave_alloc = mvs_slave_alloc, |
57 | .target_destroy = sas_target_destroy, | 60 | .target_destroy = sas_target_destroy, |
58 | .ioctl = sas_ioctl, | 61 | .ioctl = sas_ioctl, |
59 | }; | 62 | }; |
60 | 63 | ||
61 | static struct sas_domain_function_template mvs_transport_ops = { | 64 | static struct sas_domain_function_template mvs_transport_ops = { |
62 | .lldd_execute_task = mvs_task_exec, | 65 | .lldd_dev_found = mvs_dev_found, |
66 | .lldd_dev_gone = mvs_dev_gone, | ||
67 | |||
68 | .lldd_execute_task = mvs_queue_command, | ||
63 | .lldd_control_phy = mvs_phy_control, | 69 | .lldd_control_phy = mvs_phy_control, |
64 | .lldd_abort_task = mvs_task_abort, | 70 | |
65 | .lldd_port_formed = mvs_port_formed, | 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, | ||
66 | .lldd_I_T_nexus_reset = mvs_I_T_nexus_reset, | 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 | |||
67 | }; | 82 | }; |
68 | 83 | ||
69 | static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id) | 84 | static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id) |
@@ -71,6 +86,8 @@ static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id) | |||
71 | struct mvs_phy *phy = &mvi->phy[phy_id]; | 86 | struct mvs_phy *phy = &mvi->phy[phy_id]; |
72 | struct asd_sas_phy *sas_phy = &phy->sas_phy; | 87 | struct asd_sas_phy *sas_phy = &phy->sas_phy; |
73 | 88 | ||
89 | phy->mvi = mvi; | ||
90 | init_timer(&phy->timer); | ||
74 | sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0; | 91 | sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0; |
75 | sas_phy->class = SAS; | 92 | sas_phy->class = SAS; |
76 | sas_phy->iproto = SAS_PROTOCOL_ALL; | 93 | sas_phy->iproto = SAS_PROTOCOL_ALL; |
@@ -83,248 +100,283 @@ static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id) | |||
83 | sas_phy->id = phy_id; | 100 | sas_phy->id = phy_id; |
84 | sas_phy->sas_addr = &mvi->sas_addr[0]; | 101 | sas_phy->sas_addr = &mvi->sas_addr[0]; |
85 | sas_phy->frame_rcvd = &phy->frame_rcvd[0]; | 102 | sas_phy->frame_rcvd = &phy->frame_rcvd[0]; |
86 | sas_phy->ha = &mvi->sas; | 103 | sas_phy->ha = (struct sas_ha_struct *)mvi->shost->hostdata; |
87 | sas_phy->lldd_phy = phy; | 104 | sas_phy->lldd_phy = phy; |
88 | } | 105 | } |
89 | 106 | ||
90 | static void mvs_free(struct mvs_info *mvi) | 107 | static void mvs_free(struct mvs_info *mvi) |
91 | { | 108 | { |
92 | int i; | 109 | int i; |
110 | struct mvs_wq *mwq; | ||
111 | int slot_nr; | ||
93 | 112 | ||
94 | if (!mvi) | 113 | if (!mvi) |
95 | return; | 114 | return; |
96 | 115 | ||
97 | for (i = 0; i < MVS_SLOTS; i++) { | 116 | if (mvi->flags & MVF_FLAG_SOC) |
98 | struct mvs_slot_info *slot = &mvi->slot_info[i]; | 117 | slot_nr = MVS_SOC_SLOTS; |
118 | else | ||
119 | slot_nr = MVS_SLOTS; | ||
99 | 120 | ||
121 | for (i = 0; i < mvi->tags_num; i++) { | ||
122 | struct mvs_slot_info *slot = &mvi->slot_info[i]; | ||
100 | if (slot->buf) | 123 | if (slot->buf) |
101 | dma_free_coherent(&mvi->pdev->dev, MVS_SLOT_BUF_SZ, | 124 | dma_free_coherent(mvi->dev, MVS_SLOT_BUF_SZ, |
102 | slot->buf, slot->buf_dma); | 125 | slot->buf, slot->buf_dma); |
103 | } | 126 | } |
104 | 127 | ||
105 | if (mvi->tx) | 128 | if (mvi->tx) |
106 | dma_free_coherent(&mvi->pdev->dev, | 129 | dma_free_coherent(mvi->dev, |
107 | sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ, | 130 | sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ, |
108 | mvi->tx, mvi->tx_dma); | 131 | mvi->tx, mvi->tx_dma); |
109 | if (mvi->rx_fis) | 132 | if (mvi->rx_fis) |
110 | dma_free_coherent(&mvi->pdev->dev, MVS_RX_FISL_SZ, | 133 | dma_free_coherent(mvi->dev, MVS_RX_FISL_SZ, |
111 | mvi->rx_fis, mvi->rx_fis_dma); | 134 | mvi->rx_fis, mvi->rx_fis_dma); |
112 | if (mvi->rx) | 135 | if (mvi->rx) |
113 | dma_free_coherent(&mvi->pdev->dev, | 136 | dma_free_coherent(mvi->dev, |
114 | sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1), | 137 | sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1), |
115 | mvi->rx, mvi->rx_dma); | 138 | mvi->rx, mvi->rx_dma); |
116 | if (mvi->slot) | 139 | if (mvi->slot) |
117 | dma_free_coherent(&mvi->pdev->dev, | 140 | dma_free_coherent(mvi->dev, |
118 | sizeof(*mvi->slot) * MVS_SLOTS, | 141 | sizeof(*mvi->slot) * slot_nr, |
119 | mvi->slot, mvi->slot_dma); | 142 | mvi->slot, mvi->slot_dma); |
120 | #ifdef MVS_ENABLE_PERI | 143 | #ifndef DISABLE_HOTPLUG_DMA_FIX |
121 | if (mvi->peri_regs) | 144 | if (mvi->bulk_buffer) |
122 | iounmap(mvi->peri_regs); | 145 | dma_free_coherent(mvi->dev, TRASH_BUCKET_SIZE, |
146 | mvi->bulk_buffer, mvi->bulk_buffer_dma); | ||
123 | #endif | 147 | #endif |
124 | if (mvi->regs) | 148 | |
125 | iounmap(mvi->regs); | 149 | MVS_CHIP_DISP->chip_iounmap(mvi); |
126 | if (mvi->shost) | 150 | if (mvi->shost) |
127 | scsi_host_put(mvi->shost); | 151 | scsi_host_put(mvi->shost); |
128 | kfree(mvi->sas.sas_port); | 152 | list_for_each_entry(mwq, &mvi->wq_list, entry) |
129 | kfree(mvi->sas.sas_phy); | 153 | cancel_delayed_work(&mwq->work_q); |
130 | kfree(mvi); | 154 | kfree(mvi); |
131 | } | 155 | } |
132 | 156 | ||
133 | #ifdef MVS_USE_TASKLET | 157 | #ifdef MVS_USE_TASKLET |
134 | static void mvs_tasklet(unsigned long data) | 158 | struct tasklet_struct mv_tasklet; |
159 | static void mvs_tasklet(unsigned long opaque) | ||
135 | { | 160 | { |
136 | struct mvs_info *mvi = (struct mvs_info *) data; | ||
137 | unsigned long flags; | 161 | unsigned long flags; |
162 | u32 stat; | ||
163 | u16 core_nr, i = 0; | ||
138 | 164 | ||
139 | spin_lock_irqsave(&mvi->lock, flags); | 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 | } | ||
140 | 180 | ||
141 | #ifdef MVS_DISABLE_MSI | ||
142 | mvs_int_full(mvi); | ||
143 | #else | ||
144 | mvs_int_rx(mvi, true); | ||
145 | #endif | ||
146 | spin_unlock_irqrestore(&mvi->lock, flags); | ||
147 | } | 181 | } |
148 | #endif | 182 | #endif |
149 | 183 | ||
150 | static irqreturn_t mvs_interrupt(int irq, void *opaque) | 184 | static irqreturn_t mvs_interrupt(int irq, void *opaque) |
151 | { | 185 | { |
152 | struct mvs_info *mvi = opaque; | 186 | u32 core_nr, i = 0; |
153 | void __iomem *regs = mvi->regs; | ||
154 | u32 stat; | 187 | u32 stat; |
188 | struct mvs_info *mvi; | ||
189 | struct sas_ha_struct *sha = opaque; | ||
155 | 190 | ||
156 | stat = mr32(GBL_INT_STAT); | 191 | core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host; |
192 | mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0]; | ||
157 | 193 | ||
158 | if (stat == 0 || stat == 0xffffffff) | 194 | if (unlikely(!mvi)) |
159 | return IRQ_NONE; | 195 | return IRQ_NONE; |
160 | 196 | ||
161 | /* clear CMD_CMPLT ASAP */ | 197 | stat = MVS_CHIP_DISP->isr_status(mvi, irq); |
162 | mw32_f(INT_STAT, CINT_DONE); | 198 | if (!stat) |
163 | 199 | return IRQ_NONE; | |
164 | #ifndef MVS_USE_TASKLET | ||
165 | spin_lock(&mvi->lock); | ||
166 | |||
167 | mvs_int_full(mvi); | ||
168 | 200 | ||
169 | spin_unlock(&mvi->lock); | 201 | #ifdef MVS_USE_TASKLET |
202 | tasklet_schedule(&mv_tasklet); | ||
170 | #else | 203 | #else |
171 | tasklet_schedule(&mvi->tasklet); | 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 | } | ||
172 | #endif | 208 | #endif |
173 | return IRQ_HANDLED; | 209 | return IRQ_HANDLED; |
174 | } | 210 | } |
175 | 211 | ||
176 | static struct mvs_info *__devinit mvs_alloc(struct pci_dev *pdev, | 212 | static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost) |
177 | const struct pci_device_id *ent) | ||
178 | { | 213 | { |
179 | struct mvs_info *mvi; | 214 | int i, slot_nr; |
180 | unsigned long res_start, res_len, res_flag; | ||
181 | struct asd_sas_phy **arr_phy; | ||
182 | struct asd_sas_port **arr_port; | ||
183 | const struct mvs_chip_info *chip = &mvs_chips[ent->driver_data]; | ||
184 | int i; | ||
185 | 215 | ||
186 | /* | 216 | if (mvi->flags & MVF_FLAG_SOC) |
187 | * alloc and init our per-HBA mvs_info struct | 217 | slot_nr = MVS_SOC_SLOTS; |
188 | */ | 218 | else |
189 | 219 | slot_nr = MVS_SLOTS; | |
190 | mvi = kzalloc(sizeof(*mvi), GFP_KERNEL); | ||
191 | if (!mvi) | ||
192 | return NULL; | ||
193 | 220 | ||
194 | spin_lock_init(&mvi->lock); | 221 | spin_lock_init(&mvi->lock); |
195 | #ifdef MVS_USE_TASKLET | 222 | for (i = 0; i < mvi->chip->n_phy; i++) { |
196 | tasklet_init(&mvi->tasklet, mvs_tasklet, (unsigned long)mvi); | ||
197 | #endif | ||
198 | mvi->pdev = pdev; | ||
199 | mvi->chip = chip; | ||
200 | |||
201 | if (pdev->device == 0x6440 && pdev->revision == 0) | ||
202 | mvi->flags |= MVF_PHY_PWR_FIX; | ||
203 | |||
204 | /* | ||
205 | * alloc and init SCSI, SAS glue | ||
206 | */ | ||
207 | |||
208 | mvi->shost = scsi_host_alloc(&mvs_sht, sizeof(void *)); | ||
209 | if (!mvi->shost) | ||
210 | goto err_out; | ||
211 | |||
212 | arr_phy = kcalloc(MVS_MAX_PHYS, sizeof(void *), GFP_KERNEL); | ||
213 | arr_port = kcalloc(MVS_MAX_PHYS, sizeof(void *), GFP_KERNEL); | ||
214 | if (!arr_phy || !arr_port) | ||
215 | goto err_out; | ||
216 | |||
217 | for (i = 0; i < MVS_MAX_PHYS; i++) { | ||
218 | mvs_phy_init(mvi, i); | 223 | mvs_phy_init(mvi, i); |
219 | arr_phy[i] = &mvi->phy[i].sas_phy; | ||
220 | arr_port[i] = &mvi->port[i].sas_port; | ||
221 | mvi->port[i].taskfileset = MVS_ID_NOT_MAPPED; | ||
222 | mvi->port[i].wide_port_phymap = 0; | 224 | mvi->port[i].wide_port_phymap = 0; |
223 | mvi->port[i].port_attached = 0; | 225 | mvi->port[i].port_attached = 0; |
224 | INIT_LIST_HEAD(&mvi->port[i].list); | 226 | INIT_LIST_HEAD(&mvi->port[i].list); |
225 | } | 227 | } |
226 | 228 | for (i = 0; i < MVS_MAX_DEVICES; i++) { | |
227 | SHOST_TO_SAS_HA(mvi->shost) = &mvi->sas; | 229 | mvi->devices[i].taskfileset = MVS_ID_NOT_MAPPED; |
228 | mvi->shost->transportt = mvs_stt; | 230 | mvi->devices[i].dev_type = NO_DEVICE; |
229 | mvi->shost->max_id = 21; | 231 | mvi->devices[i].device_id = i; |
230 | mvi->shost->max_lun = ~0; | 232 | mvi->devices[i].dev_status = MVS_DEV_NORMAL; |
231 | mvi->shost->max_channel = 0; | 233 | } |
232 | mvi->shost->max_cmd_len = 16; | ||
233 | |||
234 | mvi->sas.sas_ha_name = DRV_NAME; | ||
235 | mvi->sas.dev = &pdev->dev; | ||
236 | mvi->sas.lldd_module = THIS_MODULE; | ||
237 | mvi->sas.sas_addr = &mvi->sas_addr[0]; | ||
238 | mvi->sas.sas_phy = arr_phy; | ||
239 | mvi->sas.sas_port = arr_port; | ||
240 | mvi->sas.num_phys = chip->n_phy; | ||
241 | mvi->sas.lldd_max_execute_num = 1; | ||
242 | mvi->sas.lldd_queue_size = MVS_QUEUE_SIZE; | ||
243 | mvi->shost->can_queue = MVS_CAN_QUEUE; | ||
244 | mvi->shost->cmd_per_lun = MVS_SLOTS / mvi->sas.num_phys; | ||
245 | mvi->sas.lldd_ha = mvi; | ||
246 | mvi->sas.core.shost = mvi->shost; | ||
247 | |||
248 | mvs_tag_init(mvi); | ||
249 | |||
250 | /* | ||
251 | * ioremap main and peripheral registers | ||
252 | */ | ||
253 | |||
254 | #ifdef MVS_ENABLE_PERI | ||
255 | res_start = pci_resource_start(pdev, 2); | ||
256 | res_len = pci_resource_len(pdev, 2); | ||
257 | if (!res_start || !res_len) | ||
258 | goto err_out; | ||
259 | |||
260 | mvi->peri_regs = ioremap_nocache(res_start, res_len); | ||
261 | if (!mvi->peri_regs) | ||
262 | goto err_out; | ||
263 | #endif | ||
264 | |||
265 | res_start = pci_resource_start(pdev, 4); | ||
266 | res_len = pci_resource_len(pdev, 4); | ||
267 | if (!res_start || !res_len) | ||
268 | goto err_out; | ||
269 | |||
270 | res_flag = pci_resource_flags(pdev, 4); | ||
271 | if (res_flag & IORESOURCE_CACHEABLE) | ||
272 | mvi->regs = ioremap(res_start, res_len); | ||
273 | else | ||
274 | mvi->regs = ioremap_nocache(res_start, res_len); | ||
275 | |||
276 | if (!mvi->regs) | ||
277 | goto err_out; | ||
278 | 234 | ||
279 | /* | 235 | /* |
280 | * alloc and init our DMA areas | 236 | * alloc and init our DMA areas |
281 | */ | 237 | */ |
282 | 238 | mvi->tx = dma_alloc_coherent(mvi->dev, | |
283 | mvi->tx = dma_alloc_coherent(&pdev->dev, | ||
284 | sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ, | 239 | sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ, |
285 | &mvi->tx_dma, GFP_KERNEL); | 240 | &mvi->tx_dma, GFP_KERNEL); |
286 | if (!mvi->tx) | 241 | if (!mvi->tx) |
287 | goto err_out; | 242 | goto err_out; |
288 | memset(mvi->tx, 0, sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ); | 243 | memset(mvi->tx, 0, sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ); |
289 | 244 | mvi->rx_fis = dma_alloc_coherent(mvi->dev, MVS_RX_FISL_SZ, | |
290 | mvi->rx_fis = dma_alloc_coherent(&pdev->dev, MVS_RX_FISL_SZ, | ||
291 | &mvi->rx_fis_dma, GFP_KERNEL); | 245 | &mvi->rx_fis_dma, GFP_KERNEL); |
292 | if (!mvi->rx_fis) | 246 | if (!mvi->rx_fis) |
293 | goto err_out; | 247 | goto err_out; |
294 | memset(mvi->rx_fis, 0, MVS_RX_FISL_SZ); | 248 | memset(mvi->rx_fis, 0, MVS_RX_FISL_SZ); |
295 | 249 | ||
296 | mvi->rx = dma_alloc_coherent(&pdev->dev, | 250 | mvi->rx = dma_alloc_coherent(mvi->dev, |
297 | sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1), | 251 | sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1), |
298 | &mvi->rx_dma, GFP_KERNEL); | 252 | &mvi->rx_dma, GFP_KERNEL); |
299 | if (!mvi->rx) | 253 | if (!mvi->rx) |
300 | goto err_out; | 254 | goto err_out; |
301 | memset(mvi->rx, 0, sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1)); | 255 | memset(mvi->rx, 0, sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1)); |
302 | |||
303 | mvi->rx[0] = cpu_to_le32(0xfff); | 256 | mvi->rx[0] = cpu_to_le32(0xfff); |
304 | mvi->rx_cons = 0xfff; | 257 | mvi->rx_cons = 0xfff; |
305 | 258 | ||
306 | mvi->slot = dma_alloc_coherent(&pdev->dev, | 259 | mvi->slot = dma_alloc_coherent(mvi->dev, |
307 | sizeof(*mvi->slot) * MVS_SLOTS, | 260 | sizeof(*mvi->slot) * slot_nr, |
308 | &mvi->slot_dma, GFP_KERNEL); | 261 | &mvi->slot_dma, GFP_KERNEL); |
309 | if (!mvi->slot) | 262 | if (!mvi->slot) |
310 | goto err_out; | 263 | goto err_out; |
311 | memset(mvi->slot, 0, sizeof(*mvi->slot) * MVS_SLOTS); | 264 | memset(mvi->slot, 0, sizeof(*mvi->slot) * slot_nr); |
312 | 265 | ||
313 | for (i = 0; i < MVS_SLOTS; i++) { | 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++) { | ||
314 | struct mvs_slot_info *slot = &mvi->slot_info[i]; | 274 | struct mvs_slot_info *slot = &mvi->slot_info[i]; |
315 | 275 | ||
316 | slot->buf = dma_alloc_coherent(&pdev->dev, MVS_SLOT_BUF_SZ, | 276 | slot->buf = dma_alloc_coherent(mvi->dev, MVS_SLOT_BUF_SZ, |
317 | &slot->buf_dma, GFP_KERNEL); | 277 | &slot->buf_dma, GFP_KERNEL); |
318 | if (!slot->buf) | 278 | if (!slot->buf) { |
279 | printk(KERN_DEBUG"failed to allocate slot->buf.\n"); | ||
319 | goto err_out; | 280 | goto err_out; |
281 | } | ||
320 | memset(slot->buf, 0, MVS_SLOT_BUF_SZ); | 282 | memset(slot->buf, 0, MVS_SLOT_BUF_SZ); |
283 | ++mvi->tags_num; | ||
321 | } | 284 | } |
285 | /* Initialize tags */ | ||
286 | mvs_tag_init(mvi); | ||
287 | return 0; | ||
288 | err_out: | ||
289 | return 1; | ||
290 | } | ||
291 | |||
322 | 292 | ||
323 | /* finally, read NVRAM to get our SAS address */ | 293 | int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex) |
324 | if (mvs_nvram_read(mvi, NVR_SAS_ADDR, &mvi->sas_addr, 8)) | 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; | ||
325 | goto err_out; | 334 | goto err_out; |
326 | return mvi; | 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; | ||
327 | 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; | ||
328 | err_out: | 380 | err_out: |
329 | mvs_free(mvi); | 381 | mvs_free(mvi); |
330 | return NULL; | 382 | return NULL; |
@@ -363,16 +415,111 @@ static int pci_go_64(struct pci_dev *pdev) | |||
363 | return rc; | 415 | return rc; |
364 | } | 416 | } |
365 | 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 | |||
366 | static int __devinit mvs_pci_init(struct pci_dev *pdev, | 509 | static int __devinit mvs_pci_init(struct pci_dev *pdev, |
367 | const struct pci_device_id *ent) | 510 | const struct pci_device_id *ent) |
368 | { | 511 | { |
369 | int rc; | 512 | unsigned int rc, nhost = 0; |
370 | struct mvs_info *mvi; | 513 | struct mvs_info *mvi; |
371 | irq_handler_t irq_handler = mvs_interrupt; | 514 | irq_handler_t irq_handler = mvs_interrupt; |
515 | struct Scsi_Host *shost = NULL; | ||
516 | const struct mvs_chip_info *chip; | ||
372 | 517 | ||
518 | dev_printk(KERN_INFO, &pdev->dev, | ||
519 | "mvsas: driver version %s\n", DRV_VERSION); | ||
373 | rc = pci_enable_device(pdev); | 520 | rc = pci_enable_device(pdev); |
374 | if (rc) | 521 | if (rc) |
375 | return rc; | 522 | goto err_out_enable; |
376 | 523 | ||
377 | pci_set_master(pdev); | 524 | pci_set_master(pdev); |
378 | 525 | ||
@@ -384,84 +531,110 @@ static int __devinit mvs_pci_init(struct pci_dev *pdev, | |||
384 | if (rc) | 531 | if (rc) |
385 | goto err_out_regions; | 532 | goto err_out_regions; |
386 | 533 | ||
387 | mvi = mvs_alloc(pdev, ent); | 534 | shost = scsi_host_alloc(&mvs_sht, sizeof(void *)); |
388 | if (!mvi) { | 535 | if (!shost) { |
389 | rc = -ENOMEM; | 536 | rc = -ENOMEM; |
390 | goto err_out_regions; | 537 | goto err_out_regions; |
391 | } | 538 | } |
392 | 539 | ||
393 | rc = mvs_hw_init(mvi); | 540 | chip = &mvs_chips[ent->driver_data]; |
394 | if (rc) | 541 | SHOST_TO_SAS_HA(shost) = |
395 | goto err_out_mvi; | 542 | kcalloc(1, sizeof(struct sas_ha_struct), GFP_KERNEL); |
396 | 543 | if (!SHOST_TO_SAS_HA(shost)) { | |
397 | #ifndef MVS_DISABLE_MSI | 544 | kfree(shost); |
398 | if (!pci_enable_msi(pdev)) { | 545 | rc = -ENOMEM; |
399 | u32 tmp; | 546 | goto err_out_regions; |
400 | void __iomem *regs = mvi->regs; | ||
401 | mvi->flags |= MVF_MSI; | ||
402 | irq_handler = mvs_msi_interrupt; | ||
403 | tmp = mr32(PCS); | ||
404 | mw32(PCS, tmp | PCS_SELF_CLEAR); | ||
405 | } | 547 | } |
406 | #endif | ||
407 | 548 | ||
408 | rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME, mvi); | 549 | rc = mvs_prep_sas_ha_init(shost, chip); |
409 | if (rc) | 550 | if (rc) { |
410 | goto err_out_msi; | 551 | kfree(shost); |
552 | rc = -ENOMEM; | ||
553 | goto err_out_regions; | ||
554 | } | ||
411 | 555 | ||
412 | rc = scsi_add_host(mvi->shost, &pdev->dev); | 556 | pci_set_drvdata(pdev, SHOST_TO_SAS_HA(shost)); |
413 | if (rc) | ||
414 | goto err_out_irq; | ||
415 | 557 | ||
416 | rc = sas_register_ha(&mvi->sas); | 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); | ||
417 | if (rc) | 579 | if (rc) |
418 | goto err_out_shost; | 580 | goto err_out_shost; |
419 | 581 | ||
420 | pci_set_drvdata(pdev, mvi); | 582 | rc = sas_register_ha(SHOST_TO_SAS_HA(shost)); |
421 | 583 | if (rc) | |
422 | mvs_print_info(mvi); | 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; | ||
423 | 589 | ||
424 | mvs_hba_interrupt_enable(mvi); | 590 | MVS_CHIP_DISP->interrupt_enable(mvi); |
425 | 591 | ||
426 | scsi_scan_host(mvi->shost); | 592 | scsi_scan_host(mvi->shost); |
427 | 593 | ||
428 | return 0; | 594 | return 0; |
429 | 595 | ||
596 | err_not_sas: | ||
597 | sas_unregister_ha(SHOST_TO_SAS_HA(shost)); | ||
430 | err_out_shost: | 598 | err_out_shost: |
431 | scsi_remove_host(mvi->shost); | 599 | scsi_remove_host(mvi->shost); |
432 | err_out_irq: | ||
433 | free_irq(pdev->irq, mvi); | ||
434 | err_out_msi: | ||
435 | if (mvi->flags |= MVF_MSI) | ||
436 | pci_disable_msi(pdev); | ||
437 | err_out_mvi: | ||
438 | mvs_free(mvi); | ||
439 | err_out_regions: | 600 | err_out_regions: |
440 | pci_release_regions(pdev); | 601 | pci_release_regions(pdev); |
441 | err_out_disable: | 602 | err_out_disable: |
442 | pci_disable_device(pdev); | 603 | pci_disable_device(pdev); |
604 | err_out_enable: | ||
443 | return rc; | 605 | return rc; |
444 | } | 606 | } |
445 | 607 | ||
446 | static void __devexit mvs_pci_remove(struct pci_dev *pdev) | 608 | static void __devexit mvs_pci_remove(struct pci_dev *pdev) |
447 | { | 609 | { |
448 | struct mvs_info *mvi = pci_get_drvdata(pdev); | 610 | unsigned short core_nr, i = 0; |
611 | struct sas_ha_struct *sha = pci_get_drvdata(pdev); | ||
612 | struct mvs_info *mvi = NULL; | ||
449 | 613 | ||
450 | pci_set_drvdata(pdev, NULL); | 614 | core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host; |
615 | mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0]; | ||
451 | 616 | ||
452 | if (mvi) { | 617 | #ifdef MVS_USE_TASKLET |
453 | sas_unregister_ha(&mvi->sas); | 618 | tasklet_kill(&mv_tasklet); |
454 | mvs_hba_interrupt_disable(mvi); | 619 | #endif |
455 | sas_remove_host(mvi->shost); | ||
456 | scsi_remove_host(mvi->shost); | ||
457 | 620 | ||
458 | free_irq(pdev->irq, mvi); | 621 | pci_set_drvdata(pdev, NULL); |
459 | if (mvi->flags & MVF_MSI) | 622 | sas_unregister_ha(sha); |
460 | pci_disable_msi(pdev); | 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]; | ||
461 | mvs_free(mvi); | 630 | mvs_free(mvi); |
462 | pci_release_regions(pdev); | ||
463 | } | 631 | } |
632 | kfree(sha->sas_phy); | ||
633 | kfree(sha->sas_port); | ||
634 | kfree(sha); | ||
635 | pci_release_regions(pdev); | ||
464 | pci_disable_device(pdev); | 636 | pci_disable_device(pdev); |
637 | return; | ||
465 | } | 638 | } |
466 | 639 | ||
467 | static struct pci_device_id __devinitdata mvs_pci_table[] = { | 640 | static struct pci_device_id __devinitdata mvs_pci_table[] = { |
@@ -474,10 +647,12 @@ static struct pci_device_id __devinitdata mvs_pci_table[] = { | |||
474 | .subdevice = 0x6480, | 647 | .subdevice = 0x6480, |
475 | .class = 0, | 648 | .class = 0, |
476 | .class_mask = 0, | 649 | .class_mask = 0, |
477 | .driver_data = chip_6480, | 650 | .driver_data = chip_6485, |
478 | }, | 651 | }, |
479 | { PCI_VDEVICE(MARVELL, 0x6440), chip_6440 }, | 652 | { PCI_VDEVICE(MARVELL, 0x6440), chip_6440 }, |
480 | { PCI_VDEVICE(MARVELL, 0x6480), chip_6480 }, | 653 | { PCI_VDEVICE(MARVELL, 0x6485), chip_6485 }, |
654 | { PCI_VDEVICE(MARVELL, 0x9480), chip_9480 }, | ||
655 | { PCI_VDEVICE(MARVELL, 0x9180), chip_9180 }, | ||
481 | 656 | ||
482 | { } /* terminate list */ | 657 | { } /* terminate list */ |
483 | }; | 658 | }; |
@@ -489,15 +664,17 @@ static struct pci_driver mvs_pci_driver = { | |||
489 | .remove = __devexit_p(mvs_pci_remove), | 664 | .remove = __devexit_p(mvs_pci_remove), |
490 | }; | 665 | }; |
491 | 666 | ||
667 | /* task handler */ | ||
668 | struct task_struct *mvs_th; | ||
492 | static int __init mvs_init(void) | 669 | static int __init mvs_init(void) |
493 | { | 670 | { |
494 | int rc; | 671 | int rc; |
495 | |||
496 | mvs_stt = sas_domain_attach_transport(&mvs_transport_ops); | 672 | mvs_stt = sas_domain_attach_transport(&mvs_transport_ops); |
497 | if (!mvs_stt) | 673 | if (!mvs_stt) |
498 | return -ENOMEM; | 674 | return -ENOMEM; |
499 | 675 | ||
500 | rc = pci_register_driver(&mvs_pci_driver); | 676 | rc = pci_register_driver(&mvs_pci_driver); |
677 | |||
501 | if (rc) | 678 | if (rc) |
502 | goto err_out; | 679 | goto err_out; |
503 | 680 | ||
@@ -521,4 +698,6 @@ MODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>"); | |||
521 | MODULE_DESCRIPTION("Marvell 88SE6440 SAS/SATA controller driver"); | 698 | MODULE_DESCRIPTION("Marvell 88SE6440 SAS/SATA controller driver"); |
522 | MODULE_VERSION(DRV_VERSION); | 699 | MODULE_VERSION(DRV_VERSION); |
523 | MODULE_LICENSE("GPL"); | 700 | MODULE_LICENSE("GPL"); |
701 | #ifdef CONFIG_PCI | ||
524 | MODULE_DEVICE_TABLE(pci, mvs_pci_table); | 702 | MODULE_DEVICE_TABLE(pci, mvs_pci_table); |
703 | #endif | ||