diff options
Diffstat (limited to 'drivers/misc/hpilo.c')
-rw-r--r-- | drivers/misc/hpilo.c | 290 |
1 files changed, 203 insertions, 87 deletions
diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 880ccf39e23b..1ad27c6abcca 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/fs.h> | 14 | #include <linux/fs.h> |
15 | #include <linux/pci.h> | 15 | #include <linux/pci.h> |
16 | #include <linux/interrupt.h> | ||
16 | #include <linux/ioport.h> | 17 | #include <linux/ioport.h> |
17 | #include <linux/device.h> | 18 | #include <linux/device.h> |
18 | #include <linux/file.h> | 19 | #include <linux/file.h> |
@@ -21,6 +22,8 @@ | |||
21 | #include <linux/delay.h> | 22 | #include <linux/delay.h> |
22 | #include <linux/uaccess.h> | 23 | #include <linux/uaccess.h> |
23 | #include <linux/io.h> | 24 | #include <linux/io.h> |
25 | #include <linux/wait.h> | ||
26 | #include <linux/poll.h> | ||
24 | #include "hpilo.h" | 27 | #include "hpilo.h" |
25 | 28 | ||
26 | static struct class *ilo_class; | 29 | static struct class *ilo_class; |
@@ -61,9 +64,10 @@ static inline int desc_mem_sz(int nr_entry) | |||
61 | static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) | 64 | static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) |
62 | { | 65 | { |
63 | struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); | 66 | struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); |
67 | unsigned long flags; | ||
64 | int ret = 0; | 68 | int ret = 0; |
65 | 69 | ||
66 | spin_lock(&hw->fifo_lock); | 70 | spin_lock_irqsave(&hw->fifo_lock, flags); |
67 | if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask] | 71 | if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask] |
68 | & ENTRY_MASK_O)) { | 72 | & ENTRY_MASK_O)) { |
69 | fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |= | 73 | fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |= |
@@ -71,7 +75,7 @@ static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) | |||
71 | fifo_q->tail += 1; | 75 | fifo_q->tail += 1; |
72 | ret = 1; | 76 | ret = 1; |
73 | } | 77 | } |
74 | spin_unlock(&hw->fifo_lock); | 78 | spin_unlock_irqrestore(&hw->fifo_lock, flags); |
75 | 79 | ||
76 | return ret; | 80 | return ret; |
77 | } | 81 | } |
@@ -79,10 +83,11 @@ static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) | |||
79 | static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) | 83 | static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) |
80 | { | 84 | { |
81 | struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); | 85 | struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); |
86 | unsigned long flags; | ||
82 | int ret = 0; | 87 | int ret = 0; |
83 | u64 c; | 88 | u64 c; |
84 | 89 | ||
85 | spin_lock(&hw->fifo_lock); | 90 | spin_lock_irqsave(&hw->fifo_lock, flags); |
86 | c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; | 91 | c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; |
87 | if (c & ENTRY_MASK_C) { | 92 | if (c & ENTRY_MASK_C) { |
88 | if (entry) | 93 | if (entry) |
@@ -93,7 +98,23 @@ static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) | |||
93 | fifo_q->head += 1; | 98 | fifo_q->head += 1; |
94 | ret = 1; | 99 | ret = 1; |
95 | } | 100 | } |
96 | spin_unlock(&hw->fifo_lock); | 101 | spin_unlock_irqrestore(&hw->fifo_lock, flags); |
102 | |||
103 | return ret; | ||
104 | } | ||
105 | |||
106 | static int fifo_check_recv(struct ilo_hwinfo *hw, char *fifobar) | ||
107 | { | ||
108 | struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); | ||
109 | unsigned long flags; | ||
110 | int ret = 0; | ||
111 | u64 c; | ||
112 | |||
113 | spin_lock_irqsave(&hw->fifo_lock, flags); | ||
114 | c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; | ||
115 | if (c & ENTRY_MASK_C) | ||
116 | ret = 1; | ||
117 | spin_unlock_irqrestore(&hw->fifo_lock, flags); | ||
97 | 118 | ||
98 | return ret; | 119 | return ret; |
99 | } | 120 | } |
@@ -142,6 +163,13 @@ static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb, | |||
142 | return ret; | 163 | return ret; |
143 | } | 164 | } |
144 | 165 | ||
166 | static int ilo_pkt_recv(struct ilo_hwinfo *hw, struct ccb *ccb) | ||
167 | { | ||
168 | char *fifobar = ccb->ccb_u3.recv_fifobar; | ||
169 | |||
170 | return fifo_check_recv(hw, fifobar); | ||
171 | } | ||
172 | |||
145 | static inline void doorbell_set(struct ccb *ccb) | 173 | static inline void doorbell_set(struct ccb *ccb) |
146 | { | 174 | { |
147 | iowrite8(1, ccb->ccb_u5.db_base); | 175 | iowrite8(1, ccb->ccb_u5.db_base); |
@@ -151,6 +179,7 @@ static inline void doorbell_clr(struct ccb *ccb) | |||
151 | { | 179 | { |
152 | iowrite8(2, ccb->ccb_u5.db_base); | 180 | iowrite8(2, ccb->ccb_u5.db_base); |
153 | } | 181 | } |
182 | |||
154 | static inline int ctrl_set(int l2sz, int idxmask, int desclim) | 183 | static inline int ctrl_set(int l2sz, int idxmask, int desclim) |
155 | { | 184 | { |
156 | int active = 0, go = 1; | 185 | int active = 0, go = 1; |
@@ -160,6 +189,7 @@ static inline int ctrl_set(int l2sz, int idxmask, int desclim) | |||
160 | active << CTRL_BITPOS_A | | 189 | active << CTRL_BITPOS_A | |
161 | go << CTRL_BITPOS_G; | 190 | go << CTRL_BITPOS_G; |
162 | } | 191 | } |
192 | |||
163 | static void ctrl_setup(struct ccb *ccb, int nr_desc, int l2desc_sz) | 193 | static void ctrl_setup(struct ccb *ccb, int nr_desc, int l2desc_sz) |
164 | { | 194 | { |
165 | /* for simplicity, use the same parameters for send and recv ctrls */ | 195 | /* for simplicity, use the same parameters for send and recv ctrls */ |
@@ -192,13 +222,10 @@ static void fifo_setup(void *base_addr, int nr_entry) | |||
192 | 222 | ||
193 | static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) | 223 | static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) |
194 | { | 224 | { |
195 | struct ccb *driver_ccb; | 225 | struct ccb *driver_ccb = &data->driver_ccb; |
196 | struct ccb __iomem *device_ccb; | 226 | struct ccb __iomem *device_ccb = data->mapped_ccb; |
197 | int retries; | 227 | int retries; |
198 | 228 | ||
199 | driver_ccb = &data->driver_ccb; | ||
200 | device_ccb = data->mapped_ccb; | ||
201 | |||
202 | /* complicated dance to tell the hw we are stopping */ | 229 | /* complicated dance to tell the hw we are stopping */ |
203 | doorbell_clr(driver_ccb); | 230 | doorbell_clr(driver_ccb); |
204 | iowrite32(ioread32(&device_ccb->send_ctrl) & ~(1 << CTRL_BITPOS_G), | 231 | iowrite32(ioread32(&device_ccb->send_ctrl) & ~(1 << CTRL_BITPOS_G), |
@@ -225,26 +252,22 @@ static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) | |||
225 | pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa); | 252 | pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa); |
226 | } | 253 | } |
227 | 254 | ||
228 | static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) | 255 | static int ilo_ccb_setup(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) |
229 | { | 256 | { |
230 | char *dma_va, *dma_pa; | 257 | char *dma_va, *dma_pa; |
231 | int pkt_id, pkt_sz, i, error; | ||
232 | struct ccb *driver_ccb, *ilo_ccb; | 258 | struct ccb *driver_ccb, *ilo_ccb; |
233 | struct pci_dev *pdev; | ||
234 | 259 | ||
235 | driver_ccb = &data->driver_ccb; | 260 | driver_ccb = &data->driver_ccb; |
236 | ilo_ccb = &data->ilo_ccb; | 261 | ilo_ccb = &data->ilo_ccb; |
237 | pdev = hw->ilo_dev; | ||
238 | 262 | ||
239 | data->dma_size = 2 * fifo_sz(NR_QENTRY) + | 263 | data->dma_size = 2 * fifo_sz(NR_QENTRY) + |
240 | 2 * desc_mem_sz(NR_QENTRY) + | 264 | 2 * desc_mem_sz(NR_QENTRY) + |
241 | ILO_START_ALIGN + ILO_CACHE_SZ; | 265 | ILO_START_ALIGN + ILO_CACHE_SZ; |
242 | 266 | ||
243 | error = -ENOMEM; | 267 | data->dma_va = pci_alloc_consistent(hw->ilo_dev, data->dma_size, |
244 | data->dma_va = pci_alloc_consistent(pdev, data->dma_size, | ||
245 | &data->dma_pa); | 268 | &data->dma_pa); |
246 | if (!data->dma_va) | 269 | if (!data->dma_va) |
247 | goto out; | 270 | return -ENOMEM; |
248 | 271 | ||
249 | dma_va = (char *)data->dma_va; | 272 | dma_va = (char *)data->dma_va; |
250 | dma_pa = (char *)data->dma_pa; | 273 | dma_pa = (char *)data->dma_pa; |
@@ -290,10 +313,18 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) | |||
290 | driver_ccb->ccb_u5.db_base = hw->db_vaddr + (slot << L2_DB_SIZE); | 313 | driver_ccb->ccb_u5.db_base = hw->db_vaddr + (slot << L2_DB_SIZE); |
291 | ilo_ccb->ccb_u5.db_base = NULL; /* hw ccb's doorbell is not used */ | 314 | ilo_ccb->ccb_u5.db_base = NULL; /* hw ccb's doorbell is not used */ |
292 | 315 | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static void ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) | ||
320 | { | ||
321 | int pkt_id, pkt_sz; | ||
322 | struct ccb *driver_ccb = &data->driver_ccb; | ||
323 | |||
293 | /* copy the ccb with physical addrs to device memory */ | 324 | /* copy the ccb with physical addrs to device memory */ |
294 | data->mapped_ccb = (struct ccb __iomem *) | 325 | data->mapped_ccb = (struct ccb __iomem *) |
295 | (hw->ram_vaddr + (slot * ILOHW_CCB_SZ)); | 326 | (hw->ram_vaddr + (slot * ILOHW_CCB_SZ)); |
296 | memcpy_toio(data->mapped_ccb, ilo_ccb, sizeof(struct ccb)); | 327 | memcpy_toio(data->mapped_ccb, &data->ilo_ccb, sizeof(struct ccb)); |
297 | 328 | ||
298 | /* put packets on the send and receive queues */ | 329 | /* put packets on the send and receive queues */ |
299 | pkt_sz = 0; | 330 | pkt_sz = 0; |
@@ -306,7 +337,14 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) | |||
306 | for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) | 337 | for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) |
307 | ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, pkt_sz); | 338 | ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, pkt_sz); |
308 | 339 | ||
340 | /* the ccb is ready to use */ | ||
309 | doorbell_clr(driver_ccb); | 341 | doorbell_clr(driver_ccb); |
342 | } | ||
343 | |||
344 | static int ilo_ccb_verify(struct ilo_hwinfo *hw, struct ccb_data *data) | ||
345 | { | ||
346 | int pkt_id, i; | ||
347 | struct ccb *driver_ccb = &data->driver_ccb; | ||
310 | 348 | ||
311 | /* make sure iLO is really handling requests */ | 349 | /* make sure iLO is really handling requests */ |
312 | for (i = MAX_WAIT; i > 0; i--) { | 350 | for (i = MAX_WAIT; i > 0; i--) { |
@@ -315,20 +353,14 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) | |||
315 | udelay(WAIT_TIME); | 353 | udelay(WAIT_TIME); |
316 | } | 354 | } |
317 | 355 | ||
318 | if (i) { | 356 | if (i == 0) { |
319 | ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0); | 357 | dev_err(&hw->ilo_dev->dev, "Open could not dequeue a packet\n"); |
320 | doorbell_set(driver_ccb); | 358 | return -EBUSY; |
321 | } else { | ||
322 | dev_err(&pdev->dev, "Open could not dequeue a packet\n"); | ||
323 | error = -EBUSY; | ||
324 | goto free; | ||
325 | } | 359 | } |
326 | 360 | ||
361 | ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0); | ||
362 | doorbell_set(driver_ccb); | ||
327 | return 0; | 363 | return 0; |
328 | free: | ||
329 | ilo_ccb_close(pdev, data); | ||
330 | out: | ||
331 | return error; | ||
332 | } | 364 | } |
333 | 365 | ||
334 | static inline int is_channel_reset(struct ccb *ccb) | 366 | static inline int is_channel_reset(struct ccb *ccb) |
@@ -343,19 +375,45 @@ static inline void set_channel_reset(struct ccb *ccb) | |||
343 | FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset = 1; | 375 | FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset = 1; |
344 | } | 376 | } |
345 | 377 | ||
378 | static inline int get_device_outbound(struct ilo_hwinfo *hw) | ||
379 | { | ||
380 | return ioread32(&hw->mmio_vaddr[DB_OUT]); | ||
381 | } | ||
382 | |||
383 | static inline int is_db_reset(int db_out) | ||
384 | { | ||
385 | return db_out & (1 << DB_RESET); | ||
386 | } | ||
387 | |||
346 | static inline int is_device_reset(struct ilo_hwinfo *hw) | 388 | static inline int is_device_reset(struct ilo_hwinfo *hw) |
347 | { | 389 | { |
348 | /* check for global reset condition */ | 390 | /* check for global reset condition */ |
349 | return ioread32(&hw->mmio_vaddr[DB_OUT]) & (1 << DB_RESET); | 391 | return is_db_reset(get_device_outbound(hw)); |
392 | } | ||
393 | |||
394 | static inline void clear_pending_db(struct ilo_hwinfo *hw, int clr) | ||
395 | { | ||
396 | iowrite32(clr, &hw->mmio_vaddr[DB_OUT]); | ||
350 | } | 397 | } |
351 | 398 | ||
352 | static inline void clear_device(struct ilo_hwinfo *hw) | 399 | static inline void clear_device(struct ilo_hwinfo *hw) |
353 | { | 400 | { |
354 | /* clear the device (reset bits, pending channel entries) */ | 401 | /* clear the device (reset bits, pending channel entries) */ |
355 | iowrite32(-1, &hw->mmio_vaddr[DB_OUT]); | 402 | clear_pending_db(hw, -1); |
403 | } | ||
404 | |||
405 | static inline void ilo_enable_interrupts(struct ilo_hwinfo *hw) | ||
406 | { | ||
407 | iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) | 1, &hw->mmio_vaddr[DB_IRQ]); | ||
356 | } | 408 | } |
357 | 409 | ||
358 | static void ilo_locked_reset(struct ilo_hwinfo *hw) | 410 | static inline void ilo_disable_interrupts(struct ilo_hwinfo *hw) |
411 | { | ||
412 | iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) & ~1, | ||
413 | &hw->mmio_vaddr[DB_IRQ]); | ||
414 | } | ||
415 | |||
416 | static void ilo_set_reset(struct ilo_hwinfo *hw) | ||
359 | { | 417 | { |
360 | int slot; | 418 | int slot; |
361 | 419 | ||
@@ -368,40 +426,22 @@ static void ilo_locked_reset(struct ilo_hwinfo *hw) | |||
368 | continue; | 426 | continue; |
369 | set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb); | 427 | set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb); |
370 | } | 428 | } |
371 | |||
372 | clear_device(hw); | ||
373 | } | ||
374 | |||
375 | static void ilo_reset(struct ilo_hwinfo *hw) | ||
376 | { | ||
377 | spin_lock(&hw->alloc_lock); | ||
378 | |||
379 | /* reset might have been handled after lock was taken */ | ||
380 | if (is_device_reset(hw)) | ||
381 | ilo_locked_reset(hw); | ||
382 | |||
383 | spin_unlock(&hw->alloc_lock); | ||
384 | } | 429 | } |
385 | 430 | ||
386 | static ssize_t ilo_read(struct file *fp, char __user *buf, | 431 | static ssize_t ilo_read(struct file *fp, char __user *buf, |
387 | size_t len, loff_t *off) | 432 | size_t len, loff_t *off) |
388 | { | 433 | { |
389 | int err, found, cnt, pkt_id, pkt_len; | 434 | int err, found, cnt, pkt_id, pkt_len; |
390 | struct ccb_data *data; | 435 | struct ccb_data *data = fp->private_data; |
391 | struct ccb *driver_ccb; | 436 | struct ccb *driver_ccb = &data->driver_ccb; |
392 | struct ilo_hwinfo *hw; | 437 | struct ilo_hwinfo *hw = data->ilo_hw; |
393 | void *pkt; | 438 | void *pkt; |
394 | 439 | ||
395 | data = fp->private_data; | 440 | if (is_channel_reset(driver_ccb)) { |
396 | driver_ccb = &data->driver_ccb; | ||
397 | hw = data->ilo_hw; | ||
398 | |||
399 | if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { | ||
400 | /* | 441 | /* |
401 | * If the device has been reset, applications | 442 | * If the device has been reset, applications |
402 | * need to close and reopen all ccbs. | 443 | * need to close and reopen all ccbs. |
403 | */ | 444 | */ |
404 | ilo_reset(hw); | ||
405 | return -ENODEV; | 445 | return -ENODEV; |
406 | } | 446 | } |
407 | 447 | ||
@@ -442,23 +482,13 @@ static ssize_t ilo_write(struct file *fp, const char __user *buf, | |||
442 | size_t len, loff_t *off) | 482 | size_t len, loff_t *off) |
443 | { | 483 | { |
444 | int err, pkt_id, pkt_len; | 484 | int err, pkt_id, pkt_len; |
445 | struct ccb_data *data; | 485 | struct ccb_data *data = fp->private_data; |
446 | struct ccb *driver_ccb; | 486 | struct ccb *driver_ccb = &data->driver_ccb; |
447 | struct ilo_hwinfo *hw; | 487 | struct ilo_hwinfo *hw = data->ilo_hw; |
448 | void *pkt; | 488 | void *pkt; |
449 | 489 | ||
450 | data = fp->private_data; | 490 | if (is_channel_reset(driver_ccb)) |
451 | driver_ccb = &data->driver_ccb; | ||
452 | hw = data->ilo_hw; | ||
453 | |||
454 | if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { | ||
455 | /* | ||
456 | * If the device has been reset, applications | ||
457 | * need to close and reopen all ccbs. | ||
458 | */ | ||
459 | ilo_reset(hw); | ||
460 | return -ENODEV; | 491 | return -ENODEV; |
461 | } | ||
462 | 492 | ||
463 | /* get a packet to send the user command */ | 493 | /* get a packet to send the user command */ |
464 | if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt)) | 494 | if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt)) |
@@ -480,32 +510,48 @@ static ssize_t ilo_write(struct file *fp, const char __user *buf, | |||
480 | return err ? -EFAULT : len; | 510 | return err ? -EFAULT : len; |
481 | } | 511 | } |
482 | 512 | ||
513 | static unsigned int ilo_poll(struct file *fp, poll_table *wait) | ||
514 | { | ||
515 | struct ccb_data *data = fp->private_data; | ||
516 | struct ccb *driver_ccb = &data->driver_ccb; | ||
517 | |||
518 | poll_wait(fp, &data->ccb_waitq, wait); | ||
519 | |||
520 | if (is_channel_reset(driver_ccb)) | ||
521 | return POLLERR; | ||
522 | else if (ilo_pkt_recv(data->ilo_hw, driver_ccb)) | ||
523 | return POLLIN | POLLRDNORM; | ||
524 | |||
525 | return 0; | ||
526 | } | ||
527 | |||
483 | static int ilo_close(struct inode *ip, struct file *fp) | 528 | static int ilo_close(struct inode *ip, struct file *fp) |
484 | { | 529 | { |
485 | int slot; | 530 | int slot; |
486 | struct ccb_data *data; | 531 | struct ccb_data *data; |
487 | struct ilo_hwinfo *hw; | 532 | struct ilo_hwinfo *hw; |
533 | unsigned long flags; | ||
488 | 534 | ||
489 | slot = iminor(ip) % MAX_CCB; | 535 | slot = iminor(ip) % MAX_CCB; |
490 | hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); | 536 | hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); |
491 | 537 | ||
492 | spin_lock(&hw->alloc_lock); | 538 | spin_lock(&hw->open_lock); |
493 | |||
494 | if (is_device_reset(hw)) | ||
495 | ilo_locked_reset(hw); | ||
496 | 539 | ||
497 | if (hw->ccb_alloc[slot]->ccb_cnt == 1) { | 540 | if (hw->ccb_alloc[slot]->ccb_cnt == 1) { |
498 | 541 | ||
499 | data = fp->private_data; | 542 | data = fp->private_data; |
500 | 543 | ||
544 | spin_lock_irqsave(&hw->alloc_lock, flags); | ||
545 | hw->ccb_alloc[slot] = NULL; | ||
546 | spin_unlock_irqrestore(&hw->alloc_lock, flags); | ||
547 | |||
501 | ilo_ccb_close(hw->ilo_dev, data); | 548 | ilo_ccb_close(hw->ilo_dev, data); |
502 | 549 | ||
503 | kfree(data); | 550 | kfree(data); |
504 | hw->ccb_alloc[slot] = NULL; | ||
505 | } else | 551 | } else |
506 | hw->ccb_alloc[slot]->ccb_cnt--; | 552 | hw->ccb_alloc[slot]->ccb_cnt--; |
507 | 553 | ||
508 | spin_unlock(&hw->alloc_lock); | 554 | spin_unlock(&hw->open_lock); |
509 | 555 | ||
510 | return 0; | 556 | return 0; |
511 | } | 557 | } |
@@ -515,6 +561,7 @@ static int ilo_open(struct inode *ip, struct file *fp) | |||
515 | int slot, error; | 561 | int slot, error; |
516 | struct ccb_data *data; | 562 | struct ccb_data *data; |
517 | struct ilo_hwinfo *hw; | 563 | struct ilo_hwinfo *hw; |
564 | unsigned long flags; | ||
518 | 565 | ||
519 | slot = iminor(ip) % MAX_CCB; | 566 | slot = iminor(ip) % MAX_CCB; |
520 | hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); | 567 | hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); |
@@ -524,22 +571,42 @@ static int ilo_open(struct inode *ip, struct file *fp) | |||
524 | if (!data) | 571 | if (!data) |
525 | return -ENOMEM; | 572 | return -ENOMEM; |
526 | 573 | ||
527 | spin_lock(&hw->alloc_lock); | 574 | spin_lock(&hw->open_lock); |
528 | |||
529 | if (is_device_reset(hw)) | ||
530 | ilo_locked_reset(hw); | ||
531 | 575 | ||
532 | /* each fd private_data holds sw/hw view of ccb */ | 576 | /* each fd private_data holds sw/hw view of ccb */ |
533 | if (hw->ccb_alloc[slot] == NULL) { | 577 | if (hw->ccb_alloc[slot] == NULL) { |
534 | /* create a channel control block for this minor */ | 578 | /* create a channel control block for this minor */ |
535 | error = ilo_ccb_open(hw, data, slot); | 579 | error = ilo_ccb_setup(hw, data, slot); |
536 | if (!error) { | 580 | if (error) { |
537 | hw->ccb_alloc[slot] = data; | ||
538 | hw->ccb_alloc[slot]->ccb_cnt = 1; | ||
539 | hw->ccb_alloc[slot]->ccb_excl = fp->f_flags & O_EXCL; | ||
540 | hw->ccb_alloc[slot]->ilo_hw = hw; | ||
541 | } else | ||
542 | kfree(data); | 581 | kfree(data); |
582 | goto out; | ||
583 | } | ||
584 | |||
585 | data->ccb_cnt = 1; | ||
586 | data->ccb_excl = fp->f_flags & O_EXCL; | ||
587 | data->ilo_hw = hw; | ||
588 | init_waitqueue_head(&data->ccb_waitq); | ||
589 | |||
590 | /* write the ccb to hw */ | ||
591 | spin_lock_irqsave(&hw->alloc_lock, flags); | ||
592 | ilo_ccb_open(hw, data, slot); | ||
593 | hw->ccb_alloc[slot] = data; | ||
594 | spin_unlock_irqrestore(&hw->alloc_lock, flags); | ||
595 | |||
596 | /* make sure the channel is functional */ | ||
597 | error = ilo_ccb_verify(hw, data); | ||
598 | if (error) { | ||
599 | |||
600 | spin_lock_irqsave(&hw->alloc_lock, flags); | ||
601 | hw->ccb_alloc[slot] = NULL; | ||
602 | spin_unlock_irqrestore(&hw->alloc_lock, flags); | ||
603 | |||
604 | ilo_ccb_close(hw->ilo_dev, data); | ||
605 | |||
606 | kfree(data); | ||
607 | goto out; | ||
608 | } | ||
609 | |||
543 | } else { | 610 | } else { |
544 | kfree(data); | 611 | kfree(data); |
545 | if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) { | 612 | if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) { |
@@ -554,7 +621,8 @@ static int ilo_open(struct inode *ip, struct file *fp) | |||
554 | error = 0; | 621 | error = 0; |
555 | } | 622 | } |
556 | } | 623 | } |
557 | spin_unlock(&hw->alloc_lock); | 624 | out: |
625 | spin_unlock(&hw->open_lock); | ||
558 | 626 | ||
559 | if (!error) | 627 | if (!error) |
560 | fp->private_data = hw->ccb_alloc[slot]; | 628 | fp->private_data = hw->ccb_alloc[slot]; |
@@ -566,10 +634,46 @@ static const struct file_operations ilo_fops = { | |||
566 | .owner = THIS_MODULE, | 634 | .owner = THIS_MODULE, |
567 | .read = ilo_read, | 635 | .read = ilo_read, |
568 | .write = ilo_write, | 636 | .write = ilo_write, |
637 | .poll = ilo_poll, | ||
569 | .open = ilo_open, | 638 | .open = ilo_open, |
570 | .release = ilo_close, | 639 | .release = ilo_close, |
571 | }; | 640 | }; |
572 | 641 | ||
642 | static irqreturn_t ilo_isr(int irq, void *data) | ||
643 | { | ||
644 | struct ilo_hwinfo *hw = data; | ||
645 | int pending, i; | ||
646 | |||
647 | spin_lock(&hw->alloc_lock); | ||
648 | |||
649 | /* check for ccbs which have data */ | ||
650 | pending = get_device_outbound(hw); | ||
651 | if (!pending) { | ||
652 | spin_unlock(&hw->alloc_lock); | ||
653 | return IRQ_NONE; | ||
654 | } | ||
655 | |||
656 | if (is_db_reset(pending)) { | ||
657 | /* wake up all ccbs if the device was reset */ | ||
658 | pending = -1; | ||
659 | ilo_set_reset(hw); | ||
660 | } | ||
661 | |||
662 | for (i = 0; i < MAX_CCB; i++) { | ||
663 | if (!hw->ccb_alloc[i]) | ||
664 | continue; | ||
665 | if (pending & (1 << i)) | ||
666 | wake_up_interruptible(&hw->ccb_alloc[i]->ccb_waitq); | ||
667 | } | ||
668 | |||
669 | /* clear the device of the channels that have been handled */ | ||
670 | clear_pending_db(hw, pending); | ||
671 | |||
672 | spin_unlock(&hw->alloc_lock); | ||
673 | |||
674 | return IRQ_HANDLED; | ||
675 | } | ||
676 | |||
573 | static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) | 677 | static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) |
574 | { | 678 | { |
575 | pci_iounmap(pdev, hw->db_vaddr); | 679 | pci_iounmap(pdev, hw->db_vaddr); |
@@ -623,6 +727,8 @@ static void ilo_remove(struct pci_dev *pdev) | |||
623 | device_destroy(ilo_class, MKDEV(ilo_major, i)); | 727 | device_destroy(ilo_class, MKDEV(ilo_major, i)); |
624 | 728 | ||
625 | cdev_del(&ilo_hw->cdev); | 729 | cdev_del(&ilo_hw->cdev); |
730 | ilo_disable_interrupts(ilo_hw); | ||
731 | free_irq(pdev->irq, ilo_hw); | ||
626 | ilo_unmap_device(pdev, ilo_hw); | 732 | ilo_unmap_device(pdev, ilo_hw); |
627 | pci_release_regions(pdev); | 733 | pci_release_regions(pdev); |
628 | pci_disable_device(pdev); | 734 | pci_disable_device(pdev); |
@@ -658,6 +764,7 @@ static int __devinit ilo_probe(struct pci_dev *pdev, | |||
658 | ilo_hw->ilo_dev = pdev; | 764 | ilo_hw->ilo_dev = pdev; |
659 | spin_lock_init(&ilo_hw->alloc_lock); | 765 | spin_lock_init(&ilo_hw->alloc_lock); |
660 | spin_lock_init(&ilo_hw->fifo_lock); | 766 | spin_lock_init(&ilo_hw->fifo_lock); |
767 | spin_lock_init(&ilo_hw->open_lock); | ||
661 | 768 | ||
662 | error = pci_enable_device(pdev); | 769 | error = pci_enable_device(pdev); |
663 | if (error) | 770 | if (error) |
@@ -676,13 +783,19 @@ static int __devinit ilo_probe(struct pci_dev *pdev, | |||
676 | pci_set_drvdata(pdev, ilo_hw); | 783 | pci_set_drvdata(pdev, ilo_hw); |
677 | clear_device(ilo_hw); | 784 | clear_device(ilo_hw); |
678 | 785 | ||
786 | error = request_irq(pdev->irq, ilo_isr, IRQF_SHARED, "hpilo", ilo_hw); | ||
787 | if (error) | ||
788 | goto unmap; | ||
789 | |||
790 | ilo_enable_interrupts(ilo_hw); | ||
791 | |||
679 | cdev_init(&ilo_hw->cdev, &ilo_fops); | 792 | cdev_init(&ilo_hw->cdev, &ilo_fops); |
680 | ilo_hw->cdev.owner = THIS_MODULE; | 793 | ilo_hw->cdev.owner = THIS_MODULE; |
681 | start = devnum * MAX_CCB; | 794 | start = devnum * MAX_CCB; |
682 | error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), MAX_CCB); | 795 | error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), MAX_CCB); |
683 | if (error) { | 796 | if (error) { |
684 | dev_err(&pdev->dev, "Could not add cdev\n"); | 797 | dev_err(&pdev->dev, "Could not add cdev\n"); |
685 | goto unmap; | 798 | goto remove_isr; |
686 | } | 799 | } |
687 | 800 | ||
688 | for (minor = 0 ; minor < MAX_CCB; minor++) { | 801 | for (minor = 0 ; minor < MAX_CCB; minor++) { |
@@ -695,6 +808,9 @@ static int __devinit ilo_probe(struct pci_dev *pdev, | |||
695 | } | 808 | } |
696 | 809 | ||
697 | return 0; | 810 | return 0; |
811 | remove_isr: | ||
812 | ilo_disable_interrupts(ilo_hw); | ||
813 | free_irq(pdev->irq, ilo_hw); | ||
698 | unmap: | 814 | unmap: |
699 | ilo_unmap_device(pdev, ilo_hw); | 815 | ilo_unmap_device(pdev, ilo_hw); |
700 | free_regions: | 816 | free_regions: |
@@ -759,7 +875,7 @@ static void __exit ilo_exit(void) | |||
759 | class_destroy(ilo_class); | 875 | class_destroy(ilo_class); |
760 | } | 876 | } |
761 | 877 | ||
762 | MODULE_VERSION("1.1"); | 878 | MODULE_VERSION("1.2"); |
763 | MODULE_ALIAS(ILO_NAME); | 879 | MODULE_ALIAS(ILO_NAME); |
764 | MODULE_DESCRIPTION(ILO_NAME); | 880 | MODULE_DESCRIPTION(ILO_NAME); |
765 | MODULE_AUTHOR("David Altobelli <david.altobelli@hp.com>"); | 881 | MODULE_AUTHOR("David Altobelli <david.altobelli@hp.com>"); |