blob: 35ed12379cd0653c831436af31bdc02be87503f7 [file] [log] [blame]
David Altobelli89bcb052008-07-02 09:38:53 -06001/*
2 * Driver for HP iLO/iLO2 management processor.
3 *
4 * Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
5 * David Altobelli <david.altobelli@hp.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11#include <linux/kernel.h>
12#include <linux/types.h>
13#include <linux/module.h>
14#include <linux/fs.h>
15#include <linux/pci.h>
16#include <linux/ioport.h>
17#include <linux/device.h>
18#include <linux/file.h>
19#include <linux/cdev.h>
20#include <linux/spinlock.h>
21#include <linux/delay.h>
22#include <linux/uaccess.h>
23#include <linux/io.h>
24#include "hpilo.h"
25
26static struct class *ilo_class;
27static unsigned int ilo_major;
28static char ilo_hwdev[MAX_ILO_DEV];
29
30static inline int get_entry_id(int entry)
31{
32 return (entry & ENTRY_MASK_DESCRIPTOR) >> ENTRY_BITPOS_DESCRIPTOR;
33}
34
35static inline int get_entry_len(int entry)
36{
37 return ((entry & ENTRY_MASK_QWORDS) >> ENTRY_BITPOS_QWORDS) << 3;
38}
39
40static inline int mk_entry(int id, int len)
41{
42 int qlen = len & 7 ? (len >> 3) + 1 : len >> 3;
43 return id << ENTRY_BITPOS_DESCRIPTOR | qlen << ENTRY_BITPOS_QWORDS;
44}
45
46static inline int desc_mem_sz(int nr_entry)
47{
48 return nr_entry << L2_QENTRY_SZ;
49}
50
51/*
52 * FIFO queues, shared with hardware.
53 *
54 * If a queue has empty slots, an entry is added to the queue tail,
55 * and that entry is marked as occupied.
56 * Entries can be dequeued from the head of the list, when the device
57 * has marked the entry as consumed.
58 *
59 * Returns true on successful queue/dequeue, false on failure.
60 */
61static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry)
62{
63 struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar);
64 int ret = 0;
65
66 spin_lock(&hw->fifo_lock);
67 if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask]
68 & ENTRY_MASK_O)) {
69 fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |=
70 (entry & ENTRY_MASK_NOSTATE) | fifo_q->merge;
71 fifo_q->tail += 1;
72 ret = 1;
73 }
74 spin_unlock(&hw->fifo_lock);
75
76 return ret;
77}
78
79static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry)
80{
81 struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar);
82 int ret = 0;
83 u64 c;
84
85 spin_lock(&hw->fifo_lock);
86 c = fifo_q->fifobar[fifo_q->head & fifo_q->imask];
87 if (c & ENTRY_MASK_C) {
88 if (entry)
89 *entry = c & ENTRY_MASK_NOSTATE;
90
91 fifo_q->fifobar[fifo_q->head & fifo_q->imask] =
92 (c | ENTRY_MASK) + 1;
93 fifo_q->head += 1;
94 ret = 1;
95 }
96 spin_unlock(&hw->fifo_lock);
97
98 return ret;
99}
100
101static int ilo_pkt_enqueue(struct ilo_hwinfo *hw, struct ccb *ccb,
102 int dir, int id, int len)
103{
104 char *fifobar;
105 int entry;
106
107 if (dir == SENDQ)
108 fifobar = ccb->ccb_u1.send_fifobar;
109 else
110 fifobar = ccb->ccb_u3.recv_fifobar;
111
112 entry = mk_entry(id, len);
113 return fifo_enqueue(hw, fifobar, entry);
114}
115
116static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb,
117 int dir, int *id, int *len, void **pkt)
118{
119 char *fifobar, *desc;
120 int entry = 0, pkt_id = 0;
121 int ret;
122
123 if (dir == SENDQ) {
124 fifobar = ccb->ccb_u1.send_fifobar;
125 desc = ccb->ccb_u2.send_desc;
126 } else {
127 fifobar = ccb->ccb_u3.recv_fifobar;
128 desc = ccb->ccb_u4.recv_desc;
129 }
130
131 ret = fifo_dequeue(hw, fifobar, &entry);
132 if (ret) {
133 pkt_id = get_entry_id(entry);
134 if (id)
135 *id = pkt_id;
136 if (len)
137 *len = get_entry_len(entry);
138 if (pkt)
139 *pkt = (void *)(desc + desc_mem_sz(pkt_id));
140 }
141
142 return ret;
143}
144
145static inline void doorbell_set(struct ccb *ccb)
146{
147 iowrite8(1, ccb->ccb_u5.db_base);
148}
149
150static inline void doorbell_clr(struct ccb *ccb)
151{
152 iowrite8(2, ccb->ccb_u5.db_base);
153}
David Altobelli66d5e512009-08-17 17:07:03 -0600154
David Altobelli89bcb052008-07-02 09:38:53 -0600155static inline int ctrl_set(int l2sz, int idxmask, int desclim)
156{
157 int active = 0, go = 1;
158 return l2sz << CTRL_BITPOS_L2SZ |
159 idxmask << CTRL_BITPOS_FIFOINDEXMASK |
160 desclim << CTRL_BITPOS_DESCLIMIT |
161 active << CTRL_BITPOS_A |
162 go << CTRL_BITPOS_G;
163}
David Altobelli66d5e512009-08-17 17:07:03 -0600164
David Altobelli89bcb052008-07-02 09:38:53 -0600165static void ctrl_setup(struct ccb *ccb, int nr_desc, int l2desc_sz)
166{
167 /* for simplicity, use the same parameters for send and recv ctrls */
168 ccb->send_ctrl = ctrl_set(l2desc_sz, nr_desc-1, nr_desc-1);
169 ccb->recv_ctrl = ctrl_set(l2desc_sz, nr_desc-1, nr_desc-1);
170}
171
172static inline int fifo_sz(int nr_entry)
173{
174 /* size of a fifo is determined by the number of entries it contains */
175 return (nr_entry * sizeof(u64)) + FIFOHANDLESIZE;
176}
177
178static void fifo_setup(void *base_addr, int nr_entry)
179{
180 struct fifo *fifo_q = base_addr;
181 int i;
182
183 /* set up an empty fifo */
184 fifo_q->head = 0;
185 fifo_q->tail = 0;
186 fifo_q->reset = 0;
187 fifo_q->nrents = nr_entry;
188 fifo_q->imask = nr_entry - 1;
189 fifo_q->merge = ENTRY_MASK_O;
190
191 for (i = 0; i < nr_entry; i++)
192 fifo_q->fifobar[i] = 0;
193}
194
195static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data)
196{
David Altobelli66d5e512009-08-17 17:07:03 -0600197 struct ccb *driver_ccb = &data->driver_ccb;
198 struct ccb __iomem *device_ccb = data->mapped_ccb;
David Altobelli89bcb052008-07-02 09:38:53 -0600199 int retries;
200
David Altobelli89bcb052008-07-02 09:38:53 -0600201 /* complicated dance to tell the hw we are stopping */
202 doorbell_clr(driver_ccb);
203 iowrite32(ioread32(&device_ccb->send_ctrl) & ~(1 << CTRL_BITPOS_G),
204 &device_ccb->send_ctrl);
205 iowrite32(ioread32(&device_ccb->recv_ctrl) & ~(1 << CTRL_BITPOS_G),
206 &device_ccb->recv_ctrl);
207
208 /* give iLO some time to process stop request */
David Altobellic073b2d2009-02-04 15:11:58 -0800209 for (retries = MAX_WAIT; retries > 0; retries--) {
David Altobelli89bcb052008-07-02 09:38:53 -0600210 doorbell_set(driver_ccb);
David Altobelli891f7d72009-03-31 15:23:53 -0700211 udelay(WAIT_TIME);
David Altobelli89bcb052008-07-02 09:38:53 -0600212 if (!(ioread32(&device_ccb->send_ctrl) & (1 << CTRL_BITPOS_A))
213 &&
214 !(ioread32(&device_ccb->recv_ctrl) & (1 << CTRL_BITPOS_A)))
215 break;
216 }
217 if (retries == 0)
218 dev_err(&pdev->dev, "Closing, but controller still active\n");
219
220 /* clear the hw ccb */
221 memset_io(device_ccb, 0, sizeof(struct ccb));
222
223 /* free resources used to back send/recv queues */
224 pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa);
225}
226
David Altobelli66d5e512009-08-17 17:07:03 -0600227static int ilo_ccb_setup(struct ilo_hwinfo *hw, struct ccb_data *data, int slot)
David Altobelli89bcb052008-07-02 09:38:53 -0600228{
229 char *dma_va, *dma_pa;
David Altobelli89bcb052008-07-02 09:38:53 -0600230 struct ccb *driver_ccb, *ilo_ccb;
David Altobelli89bcb052008-07-02 09:38:53 -0600231
232 driver_ccb = &data->driver_ccb;
233 ilo_ccb = &data->ilo_ccb;
David Altobelli89bcb052008-07-02 09:38:53 -0600234
235 data->dma_size = 2 * fifo_sz(NR_QENTRY) +
236 2 * desc_mem_sz(NR_QENTRY) +
237 ILO_START_ALIGN + ILO_CACHE_SZ;
238
David Altobelli66d5e512009-08-17 17:07:03 -0600239 data->dma_va = pci_alloc_consistent(hw->ilo_dev, data->dma_size,
David Altobelli89bcb052008-07-02 09:38:53 -0600240 &data->dma_pa);
241 if (!data->dma_va)
David Altobelli66d5e512009-08-17 17:07:03 -0600242 return -ENOMEM;
David Altobelli89bcb052008-07-02 09:38:53 -0600243
244 dma_va = (char *)data->dma_va;
245 dma_pa = (char *)data->dma_pa;
246
247 memset(dma_va, 0, data->dma_size);
248
249 dma_va = (char *)roundup((unsigned long)dma_va, ILO_START_ALIGN);
250 dma_pa = (char *)roundup((unsigned long)dma_pa, ILO_START_ALIGN);
251
252 /*
253 * Create two ccb's, one with virt addrs, one with phys addrs.
254 * Copy the phys addr ccb to device shared mem.
255 */
256 ctrl_setup(driver_ccb, NR_QENTRY, L2_QENTRY_SZ);
257 ctrl_setup(ilo_ccb, NR_QENTRY, L2_QENTRY_SZ);
258
259 fifo_setup(dma_va, NR_QENTRY);
260 driver_ccb->ccb_u1.send_fifobar = dma_va + FIFOHANDLESIZE;
261 ilo_ccb->ccb_u1.send_fifobar = dma_pa + FIFOHANDLESIZE;
262 dma_va += fifo_sz(NR_QENTRY);
263 dma_pa += fifo_sz(NR_QENTRY);
264
265 dma_va = (char *)roundup((unsigned long)dma_va, ILO_CACHE_SZ);
266 dma_pa = (char *)roundup((unsigned long)dma_pa, ILO_CACHE_SZ);
267
268 fifo_setup(dma_va, NR_QENTRY);
269 driver_ccb->ccb_u3.recv_fifobar = dma_va + FIFOHANDLESIZE;
270 ilo_ccb->ccb_u3.recv_fifobar = dma_pa + FIFOHANDLESIZE;
271 dma_va += fifo_sz(NR_QENTRY);
272 dma_pa += fifo_sz(NR_QENTRY);
273
274 driver_ccb->ccb_u2.send_desc = dma_va;
275 ilo_ccb->ccb_u2.send_desc = dma_pa;
276 dma_pa += desc_mem_sz(NR_QENTRY);
277 dma_va += desc_mem_sz(NR_QENTRY);
278
279 driver_ccb->ccb_u4.recv_desc = dma_va;
280 ilo_ccb->ccb_u4.recv_desc = dma_pa;
281
282 driver_ccb->channel = slot;
283 ilo_ccb->channel = slot;
284
285 driver_ccb->ccb_u5.db_base = hw->db_vaddr + (slot << L2_DB_SIZE);
286 ilo_ccb->ccb_u5.db_base = NULL; /* hw ccb's doorbell is not used */
287
David Altobelli66d5e512009-08-17 17:07:03 -0600288 return 0;
289}
290
291static void ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot)
292{
293 int pkt_id, pkt_sz;
294 struct ccb *driver_ccb = &data->driver_ccb;
295
David Altobelli89bcb052008-07-02 09:38:53 -0600296 /* copy the ccb with physical addrs to device memory */
297 data->mapped_ccb = (struct ccb __iomem *)
298 (hw->ram_vaddr + (slot * ILOHW_CCB_SZ));
David Altobelli66d5e512009-08-17 17:07:03 -0600299 memcpy_toio(data->mapped_ccb, &data->ilo_ccb, sizeof(struct ccb));
David Altobelli89bcb052008-07-02 09:38:53 -0600300
301 /* put packets on the send and receive queues */
302 pkt_sz = 0;
303 for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) {
304 ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, pkt_sz);
305 doorbell_set(driver_ccb);
306 }
307
308 pkt_sz = desc_mem_sz(1);
309 for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++)
310 ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, pkt_sz);
311
David Altobelli66d5e512009-08-17 17:07:03 -0600312 /* the ccb is ready to use */
David Altobelli89bcb052008-07-02 09:38:53 -0600313 doorbell_clr(driver_ccb);
David Altobelli66d5e512009-08-17 17:07:03 -0600314}
315
316static int ilo_ccb_verify(struct ilo_hwinfo *hw, struct ccb_data *data)
317{
318 int pkt_id, i;
319 struct ccb *driver_ccb = &data->driver_ccb;
David Altobelli89bcb052008-07-02 09:38:53 -0600320
321 /* make sure iLO is really handling requests */
David Altobellic073b2d2009-02-04 15:11:58 -0800322 for (i = MAX_WAIT; i > 0; i--) {
David Altobelli89bcb052008-07-02 09:38:53 -0600323 if (ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, NULL, NULL))
324 break;
David Altobelli891f7d72009-03-31 15:23:53 -0700325 udelay(WAIT_TIME);
David Altobelli89bcb052008-07-02 09:38:53 -0600326 }
327
David Altobelli66d5e512009-08-17 17:07:03 -0600328 if (i == 0) {
329 dev_err(&hw->ilo_dev->dev, "Open could not dequeue a packet\n");
330 return -EBUSY;
David Altobelli89bcb052008-07-02 09:38:53 -0600331 }
332
David Altobelli66d5e512009-08-17 17:07:03 -0600333 ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0);
334 doorbell_set(driver_ccb);
David Altobelli89bcb052008-07-02 09:38:53 -0600335 return 0;
David Altobelli89bcb052008-07-02 09:38:53 -0600336}
337
338static inline int is_channel_reset(struct ccb *ccb)
339{
340 /* check for this particular channel needing a reset */
341 return FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset;
342}
343
344static inline void set_channel_reset(struct ccb *ccb)
345{
346 /* set a flag indicating this channel needs a reset */
347 FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset = 1;
348}
349
David Altobelli66d5e512009-08-17 17:07:03 -0600350static inline int get_device_outbound(struct ilo_hwinfo *hw)
351{
352 return ioread32(&hw->mmio_vaddr[DB_OUT]);
353}
354
355static inline int is_db_reset(int db_out)
356{
357 return db_out & (1 << DB_RESET);
358}
359
David Altobelli89bcb052008-07-02 09:38:53 -0600360static inline int is_device_reset(struct ilo_hwinfo *hw)
361{
362 /* check for global reset condition */
David Altobelli66d5e512009-08-17 17:07:03 -0600363 return is_db_reset(get_device_outbound(hw));
364}
365
366static inline void clear_pending_db(struct ilo_hwinfo *hw, int clr)
367{
368 iowrite32(clr, &hw->mmio_vaddr[DB_OUT]);
David Altobelli89bcb052008-07-02 09:38:53 -0600369}
370
371static inline void clear_device(struct ilo_hwinfo *hw)
372{
373 /* clear the device (reset bits, pending channel entries) */
David Altobelli66d5e512009-08-17 17:07:03 -0600374 clear_pending_db(hw, -1);
David Altobelli89bcb052008-07-02 09:38:53 -0600375}
376
377static void ilo_locked_reset(struct ilo_hwinfo *hw)
378{
379 int slot;
380
381 /*
382 * Mapped memory is zeroed on ilo reset, so set a per ccb flag
383 * to indicate that this ccb needs to be closed and reopened.
384 */
385 for (slot = 0; slot < MAX_CCB; slot++) {
386 if (!hw->ccb_alloc[slot])
387 continue;
388 set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb);
389 }
390
391 clear_device(hw);
392}
393
394static void ilo_reset(struct ilo_hwinfo *hw)
395{
396 spin_lock(&hw->alloc_lock);
397
398 /* reset might have been handled after lock was taken */
399 if (is_device_reset(hw))
400 ilo_locked_reset(hw);
401
402 spin_unlock(&hw->alloc_lock);
403}
404
405static ssize_t ilo_read(struct file *fp, char __user *buf,
406 size_t len, loff_t *off)
407{
408 int err, found, cnt, pkt_id, pkt_len;
David Altobelli66d5e512009-08-17 17:07:03 -0600409 struct ccb_data *data = fp->private_data;
410 struct ccb *driver_ccb = &data->driver_ccb;
411 struct ilo_hwinfo *hw = data->ilo_hw;
David Altobelli89bcb052008-07-02 09:38:53 -0600412 void *pkt;
413
David Altobelli89bcb052008-07-02 09:38:53 -0600414 if (is_device_reset(hw) || is_channel_reset(driver_ccb)) {
415 /*
416 * If the device has been reset, applications
417 * need to close and reopen all ccbs.
418 */
419 ilo_reset(hw);
420 return -ENODEV;
421 }
422
423 /*
424 * This function is to be called when data is expected
425 * in the channel, and will return an error if no packet is found
426 * during the loop below. The sleep/retry logic is to allow
427 * applications to call read() immediately post write(),
428 * and give iLO some time to process the sent packet.
429 */
430 cnt = 20;
431 do {
432 /* look for a received packet */
433 found = ilo_pkt_dequeue(hw, driver_ccb, RECVQ, &pkt_id,
434 &pkt_len, &pkt);
435 if (found)
436 break;
437 cnt--;
438 msleep(100);
439 } while (!found && cnt);
440
441 if (!found)
442 return -EAGAIN;
443
444 /* only copy the length of the received packet */
445 if (pkt_len < len)
446 len = pkt_len;
447
448 err = copy_to_user(buf, pkt, len);
449
450 /* return the received packet to the queue */
451 ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, desc_mem_sz(1));
452
453 return err ? -EFAULT : len;
454}
455
456static ssize_t ilo_write(struct file *fp, const char __user *buf,
457 size_t len, loff_t *off)
458{
459 int err, pkt_id, pkt_len;
David Altobelli66d5e512009-08-17 17:07:03 -0600460 struct ccb_data *data = fp->private_data;
461 struct ccb *driver_ccb = &data->driver_ccb;
462 struct ilo_hwinfo *hw = data->ilo_hw;
David Altobelli89bcb052008-07-02 09:38:53 -0600463 void *pkt;
464
David Altobelli89bcb052008-07-02 09:38:53 -0600465 if (is_device_reset(hw) || is_channel_reset(driver_ccb)) {
466 /*
467 * If the device has been reset, applications
468 * need to close and reopen all ccbs.
469 */
470 ilo_reset(hw);
471 return -ENODEV;
472 }
473
474 /* get a packet to send the user command */
475 if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt))
476 return -EBUSY;
477
478 /* limit the length to the length of the packet */
479 if (pkt_len < len)
480 len = pkt_len;
481
482 /* on failure, set the len to 0 to return empty packet to the device */
483 err = copy_from_user(pkt, buf, len);
484 if (err)
485 len = 0;
486
487 /* send the packet */
488 ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, len);
489 doorbell_set(driver_ccb);
490
491 return err ? -EFAULT : len;
492}
493
494static int ilo_close(struct inode *ip, struct file *fp)
495{
496 int slot;
497 struct ccb_data *data;
498 struct ilo_hwinfo *hw;
499
500 slot = iminor(ip) % MAX_CCB;
501 hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev);
502
503 spin_lock(&hw->alloc_lock);
504
505 if (is_device_reset(hw))
506 ilo_locked_reset(hw);
507
508 if (hw->ccb_alloc[slot]->ccb_cnt == 1) {
509
510 data = fp->private_data;
511
512 ilo_ccb_close(hw->ilo_dev, data);
513
514 kfree(data);
515 hw->ccb_alloc[slot] = NULL;
516 } else
517 hw->ccb_alloc[slot]->ccb_cnt--;
518
519 spin_unlock(&hw->alloc_lock);
520
521 return 0;
522}
523
524static int ilo_open(struct inode *ip, struct file *fp)
525{
526 int slot, error;
527 struct ccb_data *data;
528 struct ilo_hwinfo *hw;
529
530 slot = iminor(ip) % MAX_CCB;
531 hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev);
532
533 /* new ccb allocation */
534 data = kzalloc(sizeof(*data), GFP_KERNEL);
535 if (!data)
536 return -ENOMEM;
537
538 spin_lock(&hw->alloc_lock);
539
540 if (is_device_reset(hw))
541 ilo_locked_reset(hw);
542
543 /* each fd private_data holds sw/hw view of ccb */
544 if (hw->ccb_alloc[slot] == NULL) {
545 /* create a channel control block for this minor */
David Altobelli66d5e512009-08-17 17:07:03 -0600546 error = ilo_ccb_setup(hw, data, slot);
547 if (error) {
David Altobelli89bcb052008-07-02 09:38:53 -0600548 kfree(data);
David Altobelli66d5e512009-08-17 17:07:03 -0600549 goto out;
550 }
551
552 /* write the ccb to hw */
553 ilo_ccb_open(hw, data, slot);
554
555 /* make sure the channel is functional */
556 error = ilo_ccb_verify(hw, data);
557 if (error) {
558 ilo_ccb_close(hw->ilo_dev, data);
559 kfree(data);
560 goto out;
561 }
562
563 data->ccb_cnt = 1;
564 data->ccb_excl = fp->f_flags & O_EXCL;
565 data->ilo_hw = hw;
566 hw->ccb_alloc[slot] = data;
567
David Altobelli89bcb052008-07-02 09:38:53 -0600568 } else {
569 kfree(data);
570 if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) {
571 /*
572 * The channel exists, and either this open
573 * or a previous open of this channel wants
574 * exclusive access.
575 */
576 error = -EBUSY;
577 } else {
578 hw->ccb_alloc[slot]->ccb_cnt++;
579 error = 0;
580 }
581 }
David Altobelli66d5e512009-08-17 17:07:03 -0600582out:
David Altobelli89bcb052008-07-02 09:38:53 -0600583 spin_unlock(&hw->alloc_lock);
584
585 if (!error)
586 fp->private_data = hw->ccb_alloc[slot];
587
588 return error;
589}
590
591static const struct file_operations ilo_fops = {
592 .owner = THIS_MODULE,
593 .read = ilo_read,
594 .write = ilo_write,
595 .open = ilo_open,
596 .release = ilo_close,
597};
598
599static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
600{
601 pci_iounmap(pdev, hw->db_vaddr);
602 pci_iounmap(pdev, hw->ram_vaddr);
603 pci_iounmap(pdev, hw->mmio_vaddr);
604}
605
606static int __devinit ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
607{
608 int error = -ENOMEM;
609
610 /* map the memory mapped i/o registers */
611 hw->mmio_vaddr = pci_iomap(pdev, 1, 0);
612 if (hw->mmio_vaddr == NULL) {
613 dev_err(&pdev->dev, "Error mapping mmio\n");
614 goto out;
615 }
616
617 /* map the adapter shared memory region */
618 hw->ram_vaddr = pci_iomap(pdev, 2, MAX_CCB * ILOHW_CCB_SZ);
619 if (hw->ram_vaddr == NULL) {
620 dev_err(&pdev->dev, "Error mapping shared mem\n");
621 goto mmio_free;
622 }
623
624 /* map the doorbell aperture */
625 hw->db_vaddr = pci_iomap(pdev, 3, MAX_CCB * ONE_DB_SIZE);
626 if (hw->db_vaddr == NULL) {
627 dev_err(&pdev->dev, "Error mapping doorbell\n");
628 goto ram_free;
629 }
630
631 return 0;
632ram_free:
633 pci_iounmap(pdev, hw->ram_vaddr);
634mmio_free:
635 pci_iounmap(pdev, hw->mmio_vaddr);
636out:
637 return error;
638}
639
640static void ilo_remove(struct pci_dev *pdev)
641{
642 int i, minor;
643 struct ilo_hwinfo *ilo_hw = pci_get_drvdata(pdev);
644
645 clear_device(ilo_hw);
646
647 minor = MINOR(ilo_hw->cdev.dev);
648 for (i = minor; i < minor + MAX_CCB; i++)
649 device_destroy(ilo_class, MKDEV(ilo_major, i));
650
651 cdev_del(&ilo_hw->cdev);
652 ilo_unmap_device(pdev, ilo_hw);
653 pci_release_regions(pdev);
654 pci_disable_device(pdev);
655 kfree(ilo_hw);
656 ilo_hwdev[(minor / MAX_CCB)] = 0;
657}
658
659static int __devinit ilo_probe(struct pci_dev *pdev,
660 const struct pci_device_id *ent)
661{
662 int devnum, minor, start, error;
663 struct ilo_hwinfo *ilo_hw;
664
665 /* find a free range for device files */
666 for (devnum = 0; devnum < MAX_ILO_DEV; devnum++) {
667 if (ilo_hwdev[devnum] == 0) {
668 ilo_hwdev[devnum] = 1;
669 break;
670 }
671 }
672
673 if (devnum == MAX_ILO_DEV) {
674 dev_err(&pdev->dev, "Error finding free device\n");
675 return -ENODEV;
676 }
677
678 /* track global allocations for this device */
679 error = -ENOMEM;
680 ilo_hw = kzalloc(sizeof(*ilo_hw), GFP_KERNEL);
681 if (!ilo_hw)
682 goto out;
683
684 ilo_hw->ilo_dev = pdev;
685 spin_lock_init(&ilo_hw->alloc_lock);
686 spin_lock_init(&ilo_hw->fifo_lock);
687
688 error = pci_enable_device(pdev);
689 if (error)
690 goto free;
691
692 pci_set_master(pdev);
693
694 error = pci_request_regions(pdev, ILO_NAME);
695 if (error)
696 goto disable;
697
698 error = ilo_map_device(pdev, ilo_hw);
699 if (error)
700 goto free_regions;
701
702 pci_set_drvdata(pdev, ilo_hw);
703 clear_device(ilo_hw);
704
705 cdev_init(&ilo_hw->cdev, &ilo_fops);
706 ilo_hw->cdev.owner = THIS_MODULE;
707 start = devnum * MAX_CCB;
708 error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), MAX_CCB);
709 if (error) {
710 dev_err(&pdev->dev, "Could not add cdev\n");
711 goto unmap;
712 }
713
714 for (minor = 0 ; minor < MAX_CCB; minor++) {
715 struct device *dev;
716 dev = device_create(ilo_class, &pdev->dev,
717 MKDEV(ilo_major, minor), NULL,
718 "hpilo!d%dccb%d", devnum, minor);
719 if (IS_ERR(dev))
720 dev_err(&pdev->dev, "Could not create files\n");
721 }
722
723 return 0;
724unmap:
725 ilo_unmap_device(pdev, ilo_hw);
726free_regions:
727 pci_release_regions(pdev);
728disable:
729 pci_disable_device(pdev);
730free:
731 kfree(ilo_hw);
732out:
733 ilo_hwdev[devnum] = 0;
734 return error;
735}
736
737static struct pci_device_id ilo_devices[] = {
738 { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB204) },
David Altobelli31d8b562009-02-27 14:03:09 -0800739 { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3307) },
David Altobelli89bcb052008-07-02 09:38:53 -0600740 { }
741};
742MODULE_DEVICE_TABLE(pci, ilo_devices);
743
744static struct pci_driver ilo_driver = {
745 .name = ILO_NAME,
746 .id_table = ilo_devices,
747 .probe = ilo_probe,
748 .remove = __devexit_p(ilo_remove),
749};
750
751static int __init ilo_init(void)
752{
753 int error;
754 dev_t dev;
755
756 ilo_class = class_create(THIS_MODULE, "iLO");
757 if (IS_ERR(ilo_class)) {
758 error = PTR_ERR(ilo_class);
759 goto out;
760 }
761
762 error = alloc_chrdev_region(&dev, 0, MAX_OPEN, ILO_NAME);
763 if (error)
764 goto class_destroy;
765
766 ilo_major = MAJOR(dev);
767
768 error = pci_register_driver(&ilo_driver);
769 if (error)
770 goto chr_remove;
771
772 return 0;
773chr_remove:
774 unregister_chrdev_region(dev, MAX_OPEN);
775class_destroy:
776 class_destroy(ilo_class);
777out:
778 return error;
779}
780
781static void __exit ilo_exit(void)
782{
783 pci_unregister_driver(&ilo_driver);
784 unregister_chrdev_region(MKDEV(ilo_major, 0), MAX_OPEN);
785 class_destroy(ilo_class);
786}
787
David Altobelli891f7d72009-03-31 15:23:53 -0700788MODULE_VERSION("1.1");
David Altobelli89bcb052008-07-02 09:38:53 -0600789MODULE_ALIAS(ILO_NAME);
790MODULE_DESCRIPTION(ILO_NAME);
791MODULE_AUTHOR("David Altobelli <david.altobelli@hp.com>");
792MODULE_LICENSE("GPL v2");
793
794module_init(ilo_init);
795module_exit(ilo_exit);