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