]>
Commit | Line | Data |
---|---|---|
0eeca14f SIG |
1 | /** |
2 | * tpci200.c | |
3 | * | |
4 | * driver for the TEWS TPCI-200 device | |
76859725 SIG |
5 | * |
6 | * Copyright (C) 2009-2012 CERN (www.cern.ch) | |
7 | * Author: Nicolas Serafini, EIC2 SA | |
8 | * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com> | |
0eeca14f SIG |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the Free | |
b995069a | 12 | * Software Foundation; version 2 of the License. |
0eeca14f SIG |
13 | */ |
14 | ||
0eeca14f | 15 | #include <linux/module.h> |
9f3059c9 | 16 | #include <linux/slab.h> |
0eeca14f SIG |
17 | #include "tpci200.h" |
18 | ||
6114aeaa | 19 | static const u16 tpci200_status_timeout[] = { |
eb12d88b JT |
20 | TPCI200_A_TIMEOUT, |
21 | TPCI200_B_TIMEOUT, | |
22 | TPCI200_C_TIMEOUT, | |
23 | TPCI200_D_TIMEOUT, | |
24 | }; | |
25 | ||
6114aeaa | 26 | static const u16 tpci200_status_error[] = { |
eb12d88b JT |
27 | TPCI200_A_ERROR, |
28 | TPCI200_B_ERROR, | |
29 | TPCI200_C_ERROR, | |
30 | TPCI200_D_ERROR, | |
31 | }; | |
32 | ||
a19ad7d0 | 33 | static const size_t tpci200_space_size[IPACK_SPACE_COUNT] = { |
fe4a3ed0 JT |
34 | [IPACK_IO_SPACE] = TPCI200_IO_SPACE_SIZE, |
35 | [IPACK_ID_SPACE] = TPCI200_ID_SPACE_SIZE, | |
36 | [IPACK_INT_SPACE] = TPCI200_INT_SPACE_SIZE, | |
37 | [IPACK_MEM8_SPACE] = TPCI200_MEM8_SPACE_SIZE, | |
48a97352 | 38 | [IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_SIZE, |
a19ad7d0 JT |
39 | }; |
40 | ||
41 | static const size_t tpci200_space_interval[IPACK_SPACE_COUNT] = { | |
fe4a3ed0 JT |
42 | [IPACK_IO_SPACE] = TPCI200_IO_SPACE_INTERVAL, |
43 | [IPACK_ID_SPACE] = TPCI200_ID_SPACE_INTERVAL, | |
44 | [IPACK_INT_SPACE] = TPCI200_INT_SPACE_INTERVAL, | |
45 | [IPACK_MEM8_SPACE] = TPCI200_MEM8_SPACE_INTERVAL, | |
48a97352 | 46 | [IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_INTERVAL, |
a19ad7d0 JT |
47 | }; |
48 | ||
0eeca14f SIG |
49 | static struct tpci200_board *check_slot(struct ipack_device *dev) |
50 | { | |
51 | struct tpci200_board *tpci200; | |
0eeca14f | 52 | |
a23d3203 | 53 | if (dev == NULL) |
0eeca14f | 54 | return NULL; |
0eeca14f | 55 | |
0eeca14f | 56 | |
f45651f9 SIG |
57 | tpci200 = dev_get_drvdata(dev->bus->parent); |
58 | ||
59 | if (tpci200 == NULL) { | |
60 | dev_info(&dev->dev, "carrier board not found\n"); | |
0eeca14f SIG |
61 | return NULL; |
62 | } | |
63 | ||
64 | if (dev->slot >= TPCI200_NB_SLOT) { | |
a23d3203 SIG |
65 | dev_info(&dev->dev, |
66 | "Slot [%d:%d] doesn't exist! Last tpci200 slot is %d.\n", | |
f9e314d2 | 67 | dev->bus->bus_nr, dev->slot, TPCI200_NB_SLOT-1); |
0eeca14f SIG |
68 | return NULL; |
69 | } | |
70 | ||
0eeca14f SIG |
71 | return tpci200; |
72 | } | |
73 | ||
0118681b JT |
74 | static void tpci200_clear_mask(struct tpci200_board *tpci200, |
75 | __le16 __iomem *addr, u16 mask) | |
76 | { | |
487e0a60 JT |
77 | unsigned long flags; |
78 | spin_lock_irqsave(&tpci200->regs_lock, flags); | |
79 | iowrite16(ioread16(addr) & (~mask), addr); | |
80 | spin_unlock_irqrestore(&tpci200->regs_lock, flags); | |
0118681b JT |
81 | } |
82 | ||
83 | static void tpci200_set_mask(struct tpci200_board *tpci200, | |
84 | __le16 __iomem *addr, u16 mask) | |
85 | { | |
487e0a60 JT |
86 | unsigned long flags; |
87 | spin_lock_irqsave(&tpci200->regs_lock, flags); | |
88 | iowrite16(ioread16(addr) | mask, addr); | |
89 | spin_unlock_irqrestore(&tpci200->regs_lock, flags); | |
0118681b JT |
90 | } |
91 | ||
0eeca14f SIG |
92 | static void tpci200_unregister(struct tpci200_board *tpci200) |
93 | { | |
0eeca14f SIG |
94 | free_irq(tpci200->info->pdev->irq, (void *) tpci200); |
95 | ||
96 | pci_iounmap(tpci200->info->pdev, tpci200->info->interface_regs); | |
79878122 | 97 | pci_iounmap(tpci200->info->pdev, tpci200->info->cfg_regs); |
0eeca14f SIG |
98 | |
99 | pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); | |
100 | pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); | |
48a97352 | 101 | pci_release_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR); |
0eeca14f | 102 | pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); |
79878122 | 103 | pci_release_region(tpci200->info->pdev, TPCI200_CFG_MEM_BAR); |
0eeca14f SIG |
104 | |
105 | pci_disable_device(tpci200->info->pdev); | |
106 | pci_dev_put(tpci200->info->pdev); | |
0eeca14f SIG |
107 | } |
108 | ||
877adc40 JT |
109 | static void tpci200_enable_irq(struct tpci200_board *tpci200, |
110 | int islot) | |
111 | { | |
112 | tpci200_set_mask(tpci200, | |
113 | &tpci200->info->interface_regs->control[islot], | |
114 | TPCI200_INT0_EN | TPCI200_INT1_EN); | |
115 | } | |
116 | ||
117 | static void tpci200_disable_irq(struct tpci200_board *tpci200, | |
118 | int islot) | |
119 | { | |
120 | tpci200_clear_mask(tpci200, | |
121 | &tpci200->info->interface_regs->control[islot], | |
122 | TPCI200_INT0_EN | TPCI200_INT1_EN); | |
123 | } | |
124 | ||
af2140ce JT |
125 | static irqreturn_t tpci200_slot_irq(struct slot_irq *slot_irq) |
126 | { | |
ab0deffc | 127 | irqreturn_t ret; |
af2140ce | 128 | |
ab0deffc JT |
129 | if (!slot_irq) |
130 | return -ENODEV; | |
131 | ret = slot_irq->handler(slot_irq->arg); | |
132 | ||
af2140ce JT |
133 | return ret; |
134 | } | |
135 | ||
0eeca14f SIG |
136 | static irqreturn_t tpci200_interrupt(int irq, void *dev_id) |
137 | { | |
138 | struct tpci200_board *tpci200 = (struct tpci200_board *) dev_id; | |
6f2c12ae | 139 | struct slot_irq *slot_irq; |
ab0deffc JT |
140 | irqreturn_t ret; |
141 | u16 status_reg; | |
142 | int i; | |
0eeca14f | 143 | |
0eeca14f | 144 | /* Read status register */ |
ab0deffc | 145 | status_reg = ioread16(&tpci200->info->interface_regs->status); |
0eeca14f | 146 | |
ab0deffc JT |
147 | /* Did we cause the interrupt? */ |
148 | if (!(status_reg & TPCI200_SLOT_INT_MASK)) | |
88ff8480 | 149 | return IRQ_NONE; |
ab0deffc JT |
150 | |
151 | /* callback to the IRQ handler for the corresponding slot */ | |
152 | rcu_read_lock(); | |
153 | for (i = 0; i < TPCI200_NB_SLOT; i++) { | |
154 | if (!(status_reg & ((TPCI200_A_INT0 | TPCI200_A_INT1) << (2 * i)))) | |
155 | continue; | |
156 | slot_irq = rcu_dereference(tpci200->slots[i].irq); | |
157 | ret = tpci200_slot_irq(slot_irq); | |
158 | if (ret == -ENODEV) { | |
159 | dev_info(&tpci200->info->pdev->dev, | |
160 | "No registered ISR for slot [%d:%d]!. IRQ will be disabled.\n", | |
161 | tpci200->number, i); | |
877adc40 | 162 | tpci200_disable_irq(tpci200, i); |
ab0deffc | 163 | } |
88ff8480 | 164 | } |
ab0deffc JT |
165 | rcu_read_unlock(); |
166 | ||
167 | return IRQ_HANDLED; | |
0eeca14f SIG |
168 | } |
169 | ||
40733ed7 JT |
170 | static int tpci200_free_irq(struct ipack_device *dev) |
171 | { | |
172 | struct slot_irq *slot_irq; | |
173 | struct tpci200_board *tpci200; | |
174 | ||
175 | tpci200 = check_slot(dev); | |
176 | if (tpci200 == NULL) | |
177 | return -EINVAL; | |
178 | ||
179 | if (mutex_lock_interruptible(&tpci200->mutex)) | |
180 | return -ERESTARTSYS; | |
181 | ||
182 | if (tpci200->slots[dev->slot].irq == NULL) { | |
183 | mutex_unlock(&tpci200->mutex); | |
184 | return -EINVAL; | |
185 | } | |
186 | ||
187 | tpci200_disable_irq(tpci200, dev->slot); | |
188 | slot_irq = tpci200->slots[dev->slot].irq; | |
189 | /* uninstall handler */ | |
190 | RCU_INIT_POINTER(tpci200->slots[dev->slot].irq, NULL); | |
191 | synchronize_rcu(); | |
192 | kfree(slot_irq); | |
193 | mutex_unlock(&tpci200->mutex); | |
194 | return 0; | |
195 | } | |
196 | ||
c6e2dfaa | 197 | static int tpci200_request_irq(struct ipack_device *dev, |
faa75c40 | 198 | irqreturn_t (*handler)(void *), void *arg) |
40733ed7 JT |
199 | { |
200 | int res = 0; | |
201 | struct slot_irq *slot_irq; | |
202 | struct tpci200_board *tpci200; | |
203 | ||
204 | tpci200 = check_slot(dev); | |
205 | if (tpci200 == NULL) | |
206 | return -EINVAL; | |
207 | ||
208 | if (mutex_lock_interruptible(&tpci200->mutex)) | |
209 | return -ERESTARTSYS; | |
210 | ||
211 | if (tpci200->slots[dev->slot].irq != NULL) { | |
212 | dev_err(&dev->dev, | |
9d10b221 JM |
213 | "Slot [%d:%d] IRQ already registered !\n", |
214 | dev->bus->bus_nr, | |
40733ed7 JT |
215 | dev->slot); |
216 | res = -EINVAL; | |
217 | goto out_unlock; | |
218 | } | |
219 | ||
220 | slot_irq = kzalloc(sizeof(struct slot_irq), GFP_KERNEL); | |
221 | if (slot_irq == NULL) { | |
222 | dev_err(&dev->dev, | |
223 | "Slot [%d:%d] unable to allocate memory for IRQ !\n", | |
f9e314d2 | 224 | dev->bus->bus_nr, dev->slot); |
40733ed7 JT |
225 | res = -ENOMEM; |
226 | goto out_unlock; | |
227 | } | |
228 | ||
229 | /* | |
230 | * WARNING: Setup Interrupt Vector in the IndustryPack device | |
231 | * before an IRQ request. | |
232 | * Read the User Manual of your IndustryPack device to know | |
233 | * where to write the vector in memory. | |
234 | */ | |
40733ed7 JT |
235 | slot_irq->handler = handler; |
236 | slot_irq->arg = arg; | |
237 | slot_irq->holder = dev; | |
238 | ||
239 | rcu_assign_pointer(tpci200->slots[dev->slot].irq, slot_irq); | |
240 | tpci200_enable_irq(tpci200, dev->slot); | |
241 | ||
242 | out_unlock: | |
243 | mutex_unlock(&tpci200->mutex); | |
244 | return res; | |
245 | } | |
246 | ||
0eeca14f SIG |
247 | static int tpci200_register(struct tpci200_board *tpci200) |
248 | { | |
862fe87a | 249 | int i; |
0eeca14f | 250 | int res; |
bb29ab86 | 251 | phys_addr_t ioidint_base; |
0eeca14f SIG |
252 | unsigned short slot_ctrl; |
253 | ||
254 | if (pci_enable_device(tpci200->info->pdev) < 0) | |
255 | return -ENODEV; | |
256 | ||
0eeca14f SIG |
257 | /* Request IP interface register (Bar 2) */ |
258 | res = pci_request_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR, | |
259 | "Carrier IP interface registers"); | |
260 | if (res) { | |
a23d3203 SIG |
261 | dev_err(&tpci200->info->pdev->dev, |
262 | "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 2 !", | |
263 | tpci200->info->pdev->bus->number, | |
264 | tpci200->info->pdev->devfn); | |
862fe87a | 265 | goto out_disable_pci; |
0eeca14f SIG |
266 | } |
267 | ||
268 | /* Request IO ID INT space (Bar 3) */ | |
269 | res = pci_request_region(tpci200->info->pdev, | |
270 | TPCI200_IO_ID_INT_SPACES_BAR, | |
271 | "Carrier IO ID INT space"); | |
272 | if (res) { | |
a23d3203 SIG |
273 | dev_err(&tpci200->info->pdev->dev, |
274 | "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 3 !", | |
275 | tpci200->info->pdev->bus->number, | |
276 | tpci200->info->pdev->devfn); | |
0eeca14f SIG |
277 | goto out_release_ip_space; |
278 | } | |
279 | ||
fe4a3ed0 | 280 | /* Request MEM8 space (Bar 5) */ |
0eeca14f | 281 | res = pci_request_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR, |
fe4a3ed0 | 282 | "Carrier MEM8 space"); |
0eeca14f | 283 | if (res) { |
a23d3203 | 284 | dev_err(&tpci200->info->pdev->dev, |
fe4a3ed0 | 285 | "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 5!", |
a23d3203 SIG |
286 | tpci200->info->pdev->bus->number, |
287 | tpci200->info->pdev->devfn); | |
0eeca14f SIG |
288 | goto out_release_ioid_int_space; |
289 | } | |
290 | ||
48a97352 JT |
291 | /* Request MEM16 space (Bar 4) */ |
292 | res = pci_request_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR, | |
293 | "Carrier MEM16 space"); | |
294 | if (res) { | |
295 | dev_err(&tpci200->info->pdev->dev, | |
296 | "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 4!", | |
297 | tpci200->info->pdev->bus->number, | |
298 | tpci200->info->pdev->devfn); | |
299 | goto out_release_mem8_space; | |
300 | } | |
301 | ||
0eeca14f SIG |
302 | /* Map internal tpci200 driver user space */ |
303 | tpci200->info->interface_regs = | |
5b47f3cb | 304 | ioremap_nocache(pci_resource_start(tpci200->info->pdev, |
0eeca14f SIG |
305 | TPCI200_IP_INTERFACE_BAR), |
306 | TPCI200_IFACE_SIZE); | |
43986798 ZJ |
307 | if (!tpci200->info->interface_regs) { |
308 | dev_err(&tpci200->info->pdev->dev, | |
309 | "(bn 0x%X, sn 0x%X) failed to map driver user space!", | |
310 | tpci200->info->pdev->bus->number, | |
311 | tpci200->info->pdev->devfn); | |
312 | goto out_release_mem8_space; | |
313 | } | |
0eeca14f | 314 | |
487e0a60 JT |
315 | /* Initialize lock that protects interface_regs */ |
316 | spin_lock_init(&tpci200->regs_lock); | |
317 | ||
0eeca14f SIG |
318 | ioidint_base = pci_resource_start(tpci200->info->pdev, |
319 | TPCI200_IO_ID_INT_SPACES_BAR); | |
84a08fa9 JT |
320 | tpci200->mod_mem[IPACK_IO_SPACE] = ioidint_base + TPCI200_IO_SPACE_OFF; |
321 | tpci200->mod_mem[IPACK_ID_SPACE] = ioidint_base + TPCI200_ID_SPACE_OFF; | |
322 | tpci200->mod_mem[IPACK_INT_SPACE] = | |
323 | ioidint_base + TPCI200_INT_SPACE_OFF; | |
fe4a3ed0 | 324 | tpci200->mod_mem[IPACK_MEM8_SPACE] = |
84a08fa9 | 325 | pci_resource_start(tpci200->info->pdev, |
6114aeaa | 326 | TPCI200_MEM8_SPACE_BAR); |
48a97352 JT |
327 | tpci200->mod_mem[IPACK_MEM16_SPACE] = |
328 | pci_resource_start(tpci200->info->pdev, | |
329 | TPCI200_MEM16_SPACE_BAR); | |
0eeca14f SIG |
330 | |
331 | /* Set the default parameters of the slot | |
332 | * INT0 disabled, level sensitive | |
333 | * INT1 disabled, level sensitive | |
334 | * error interrupt disabled | |
335 | * timeout interrupt disabled | |
336 | * recover time disabled | |
337 | * clock rate 8 MHz | |
338 | */ | |
339 | slot_ctrl = 0; | |
a93963ab | 340 | for (i = 0; i < TPCI200_NB_SLOT; i++) |
28086cbd | 341 | writew(slot_ctrl, &tpci200->info->interface_regs->control[i]); |
0eeca14f SIG |
342 | |
343 | res = request_irq(tpci200->info->pdev->irq, | |
344 | tpci200_interrupt, IRQF_SHARED, | |
c63a7dd0 | 345 | KBUILD_MODNAME, (void *) tpci200); |
0eeca14f | 346 | if (res) { |
a23d3203 SIG |
347 | dev_err(&tpci200->info->pdev->dev, |
348 | "(bn 0x%X, sn 0x%X) unable to register IRQ !", | |
349 | tpci200->info->pdev->bus->number, | |
350 | tpci200->info->pdev->devfn); | |
862fe87a | 351 | goto out_release_ioid_int_space; |
0eeca14f SIG |
352 | } |
353 | ||
354 | return 0; | |
355 | ||
48a97352 JT |
356 | out_release_mem8_space: |
357 | pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); | |
0eeca14f SIG |
358 | out_release_ioid_int_space: |
359 | pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); | |
360 | out_release_ip_space: | |
361 | pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); | |
0eeca14f SIG |
362 | out_disable_pci: |
363 | pci_disable_device(tpci200->info->pdev); | |
0eeca14f SIG |
364 | return res; |
365 | } | |
366 | ||
eb12d88b JT |
367 | static int tpci200_get_clockrate(struct ipack_device *dev) |
368 | { | |
369 | struct tpci200_board *tpci200 = check_slot(dev); | |
7dd73b86 | 370 | __le16 __iomem *addr; |
eb12d88b JT |
371 | |
372 | if (!tpci200) | |
373 | return -ENODEV; | |
374 | ||
375 | addr = &tpci200->info->interface_regs->control[dev->slot]; | |
376 | return (ioread16(addr) & TPCI200_CLK32) ? 32 : 8; | |
377 | } | |
378 | ||
379 | static int tpci200_set_clockrate(struct ipack_device *dev, int mherz) | |
380 | { | |
381 | struct tpci200_board *tpci200 = check_slot(dev); | |
7dd73b86 | 382 | __le16 __iomem *addr; |
eb12d88b JT |
383 | |
384 | if (!tpci200) | |
385 | return -ENODEV; | |
386 | ||
387 | addr = &tpci200->info->interface_regs->control[dev->slot]; | |
388 | ||
eb12d88b JT |
389 | switch (mherz) { |
390 | case 8: | |
0118681b | 391 | tpci200_clear_mask(tpci200, addr, TPCI200_CLK32); |
eb12d88b JT |
392 | break; |
393 | case 32: | |
0118681b | 394 | tpci200_set_mask(tpci200, addr, TPCI200_CLK32); |
eb12d88b JT |
395 | break; |
396 | default: | |
eb12d88b JT |
397 | return -EINVAL; |
398 | } | |
eb12d88b JT |
399 | return 0; |
400 | } | |
401 | ||
402 | static int tpci200_get_error(struct ipack_device *dev) | |
403 | { | |
404 | struct tpci200_board *tpci200 = check_slot(dev); | |
7dd73b86 | 405 | __le16 __iomem *addr; |
eb12d88b JT |
406 | u16 mask; |
407 | ||
408 | if (!tpci200) | |
409 | return -ENODEV; | |
410 | ||
411 | addr = &tpci200->info->interface_regs->status; | |
412 | mask = tpci200_status_error[dev->slot]; | |
413 | return (ioread16(addr) & mask) ? 1 : 0; | |
414 | } | |
415 | ||
416 | static int tpci200_get_timeout(struct ipack_device *dev) | |
417 | { | |
418 | struct tpci200_board *tpci200 = check_slot(dev); | |
7dd73b86 | 419 | __le16 __iomem *addr; |
eb12d88b JT |
420 | u16 mask; |
421 | ||
422 | if (!tpci200) | |
423 | return -ENODEV; | |
424 | ||
425 | addr = &tpci200->info->interface_regs->status; | |
426 | mask = tpci200_status_timeout[dev->slot]; | |
427 | ||
428 | return (ioread16(addr) & mask) ? 1 : 0; | |
429 | } | |
430 | ||
431 | static int tpci200_reset_timeout(struct ipack_device *dev) | |
432 | { | |
433 | struct tpci200_board *tpci200 = check_slot(dev); | |
7dd73b86 | 434 | __le16 __iomem *addr; |
eb12d88b JT |
435 | u16 mask; |
436 | ||
437 | if (!tpci200) | |
438 | return -ENODEV; | |
439 | ||
440 | addr = &tpci200->info->interface_regs->status; | |
441 | mask = tpci200_status_timeout[dev->slot]; | |
442 | ||
443 | iowrite16(mask, addr); | |
444 | return 0; | |
445 | } | |
446 | ||
0eeca14f SIG |
447 | static void tpci200_uninstall(struct tpci200_board *tpci200) |
448 | { | |
0eeca14f SIG |
449 | tpci200_unregister(tpci200); |
450 | kfree(tpci200->slots); | |
451 | } | |
452 | ||
9869a937 | 453 | static const struct ipack_bus_ops tpci200_bus_ops = { |
0eeca14f SIG |
454 | .request_irq = tpci200_request_irq, |
455 | .free_irq = tpci200_free_irq, | |
eb12d88b JT |
456 | .get_clockrate = tpci200_get_clockrate, |
457 | .set_clockrate = tpci200_set_clockrate, | |
458 | .get_error = tpci200_get_error, | |
459 | .get_timeout = tpci200_get_timeout, | |
460 | .reset_timeout = tpci200_reset_timeout, | |
0eeca14f SIG |
461 | }; |
462 | ||
463 | static int tpci200_install(struct tpci200_board *tpci200) | |
464 | { | |
465 | int res; | |
466 | ||
6396bb22 KC |
467 | tpci200->slots = kcalloc(TPCI200_NB_SLOT, sizeof(struct tpci200_slot), |
468 | GFP_KERNEL); | |
ec0ceb9e MG |
469 | if (tpci200->slots == NULL) |
470 | return -ENOMEM; | |
0eeca14f SIG |
471 | |
472 | res = tpci200_register(tpci200); | |
ec0ceb9e MG |
473 | if (res) { |
474 | kfree(tpci200->slots); | |
475 | tpci200->slots = NULL; | |
476 | return res; | |
477 | } | |
0eeca14f SIG |
478 | |
479 | mutex_init(&tpci200->mutex); | |
480 | return 0; | |
0eeca14f SIG |
481 | } |
482 | ||
1e91795c JT |
483 | static void tpci200_release_device(struct ipack_device *dev) |
484 | { | |
485 | kfree(dev); | |
486 | } | |
487 | ||
488 | static int tpci200_create_device(struct tpci200_board *tpci200, int i) | |
489 | { | |
e926301b | 490 | int ret; |
a19ad7d0 | 491 | enum ipack_space space; |
1e91795c JT |
492 | struct ipack_device *dev = |
493 | kzalloc(sizeof(struct ipack_device), GFP_KERNEL); | |
494 | if (!dev) | |
495 | return -ENOMEM; | |
496 | dev->slot = i; | |
497 | dev->bus = tpci200->info->ipack_bus; | |
498 | dev->release = tpci200_release_device; | |
a19ad7d0 JT |
499 | |
500 | for (space = 0; space < IPACK_SPACE_COUNT; space++) { | |
501 | dev->region[space].start = | |
502 | tpci200->mod_mem[space] | |
503 | + tpci200_space_interval[space] * i; | |
504 | dev->region[space].size = tpci200_space_size[space]; | |
505 | } | |
e926301b SIG |
506 | |
507 | ret = ipack_device_init(dev); | |
508 | if (ret < 0) { | |
509 | ipack_put_device(dev); | |
510 | return ret; | |
511 | } | |
512 | ||
513 | ret = ipack_device_add(dev); | |
514 | if (ret < 0) | |
515 | ipack_put_device(dev); | |
516 | ||
517 | return ret; | |
1e91795c JT |
518 | } |
519 | ||
20659455 SH |
520 | static int tpci200_pci_probe(struct pci_dev *pdev, |
521 | const struct pci_device_id *id) | |
0eeca14f | 522 | { |
862fe87a | 523 | int ret, i; |
0eeca14f | 524 | struct tpci200_board *tpci200; |
eb12d88b | 525 | u32 reg32; |
0eeca14f SIG |
526 | |
527 | tpci200 = kzalloc(sizeof(struct tpci200_board), GFP_KERNEL); | |
528 | if (!tpci200) | |
529 | return -ENOMEM; | |
530 | ||
531 | tpci200->info = kzalloc(sizeof(struct tpci200_infos), GFP_KERNEL); | |
532 | if (!tpci200->info) { | |
5e15a753 JT |
533 | ret = -ENOMEM; |
534 | goto out_err_info; | |
0eeca14f SIG |
535 | } |
536 | ||
b442bf73 SIG |
537 | pci_dev_get(pdev); |
538 | ||
cea2f7cd JT |
539 | /* Obtain a mapping of the carrier's PCI configuration registers */ |
540 | ret = pci_request_region(pdev, TPCI200_CFG_MEM_BAR, | |
541 | KBUILD_MODNAME " Configuration Memory"); | |
542 | if (ret) { | |
543 | dev_err(&pdev->dev, "Failed to allocate PCI Configuration Memory"); | |
544 | ret = -EBUSY; | |
545 | goto out_err_pci_request; | |
546 | } | |
547 | tpci200->info->cfg_regs = ioremap_nocache( | |
548 | pci_resource_start(pdev, TPCI200_CFG_MEM_BAR), | |
549 | pci_resource_len(pdev, TPCI200_CFG_MEM_BAR)); | |
550 | if (!tpci200->info->cfg_regs) { | |
551 | dev_err(&pdev->dev, "Failed to map PCI Configuration Memory"); | |
552 | ret = -EFAULT; | |
553 | goto out_err_ioremap; | |
554 | } | |
555 | ||
556 | /* Disable byte swapping for 16 bit IP module access. This will ensure | |
557 | * that the Industrypack big endian byte order is preserved by the | |
558 | * carrier. */ | |
559 | reg32 = ioread32(tpci200->info->cfg_regs + LAS1_DESC); | |
560 | reg32 |= 1 << LAS_BIT_BIGENDIAN; | |
561 | iowrite32(reg32, tpci200->info->cfg_regs + LAS1_DESC); | |
562 | ||
563 | reg32 = ioread32(tpci200->info->cfg_regs + LAS2_DESC); | |
564 | reg32 |= 1 << LAS_BIT_BIGENDIAN; | |
565 | iowrite32(reg32, tpci200->info->cfg_regs + LAS2_DESC); | |
566 | ||
0eeca14f SIG |
567 | /* Save struct pci_dev pointer */ |
568 | tpci200->info->pdev = pdev; | |
569 | tpci200->info->id_table = (struct pci_device_id *)id; | |
570 | ||
571 | /* register the device and initialize it */ | |
572 | ret = tpci200_install(tpci200); | |
573 | if (ret) { | |
5e15a753 JT |
574 | dev_err(&pdev->dev, "error during tpci200 install\n"); |
575 | ret = -ENODEV; | |
576 | goto out_err_install; | |
0eeca14f SIG |
577 | } |
578 | ||
ec440335 SIG |
579 | /* Register the carrier in the industry pack bus driver */ |
580 | tpci200->info->ipack_bus = ipack_bus_register(&pdev->dev, | |
581 | TPCI200_NB_SLOT, | |
36c53b3c FV |
582 | &tpci200_bus_ops, |
583 | THIS_MODULE); | |
ec440335 | 584 | if (!tpci200->info->ipack_bus) { |
a23d3203 SIG |
585 | dev_err(&pdev->dev, |
586 | "error registering the carrier on ipack driver\n"); | |
5e15a753 JT |
587 | ret = -EFAULT; |
588 | goto out_err_bus_register; | |
0eeca14f | 589 | } |
ec440335 | 590 | |
0eeca14f | 591 | /* save the bus number given by ipack to logging purpose */ |
ec440335 | 592 | tpci200->number = tpci200->info->ipack_bus->bus_nr; |
0eeca14f | 593 | dev_set_drvdata(&pdev->dev, tpci200); |
862fe87a | 594 | |
862fe87a | 595 | for (i = 0; i < TPCI200_NB_SLOT; i++) |
1e91795c | 596 | tpci200_create_device(tpci200, i); |
5e15a753 JT |
597 | return 0; |
598 | ||
599 | out_err_bus_register: | |
600 | tpci200_uninstall(tpci200); | |
601 | out_err_install: | |
cea2f7cd JT |
602 | iounmap(tpci200->info->cfg_regs); |
603 | out_err_ioremap: | |
604 | pci_release_region(pdev, TPCI200_CFG_MEM_BAR); | |
605 | out_err_pci_request: | |
b442bf73 | 606 | pci_dev_put(pdev); |
5e15a753 JT |
607 | kfree(tpci200->info); |
608 | out_err_info: | |
609 | kfree(tpci200); | |
0eeca14f SIG |
610 | return ret; |
611 | } | |
612 | ||
613 | static void __tpci200_pci_remove(struct tpci200_board *tpci200) | |
614 | { | |
ec440335 | 615 | ipack_bus_unregister(tpci200->info->ipack_bus); |
9c0d169e | 616 | tpci200_uninstall(tpci200); |
cea2f7cd | 617 | |
ec440335 | 618 | kfree(tpci200->info); |
0eeca14f SIG |
619 | kfree(tpci200); |
620 | } | |
621 | ||
29c35442 | 622 | static void tpci200_pci_remove(struct pci_dev *dev) |
0eeca14f | 623 | { |
f45651f9 | 624 | struct tpci200_board *tpci200 = pci_get_drvdata(dev); |
0eeca14f | 625 | |
f45651f9 | 626 | __tpci200_pci_remove(tpci200); |
0eeca14f SIG |
627 | } |
628 | ||
7426d29e | 629 | static const struct pci_device_id tpci200_idtable[] = { |
0eeca14f SIG |
630 | { TPCI200_VENDOR_ID, TPCI200_DEVICE_ID, TPCI200_SUBVENDOR_ID, |
631 | TPCI200_SUBDEVICE_ID }, | |
632 | { 0, }, | |
633 | }; | |
634 | ||
39535037 MG |
635 | MODULE_DEVICE_TABLE(pci, tpci200_idtable); |
636 | ||
0eeca14f SIG |
637 | static struct pci_driver tpci200_pci_drv = { |
638 | .name = "tpci200", | |
639 | .id_table = tpci200_idtable, | |
20659455 | 640 | .probe = tpci200_pci_probe, |
c5dee46c | 641 | .remove = tpci200_pci_remove, |
0eeca14f SIG |
642 | }; |
643 | ||
1bc7c1c7 | 644 | module_pci_driver(tpci200_pci_drv); |
0eeca14f SIG |
645 | |
646 | MODULE_DESCRIPTION("TEWS TPCI-200 device driver"); | |
647 | MODULE_LICENSE("GPL"); |