hpilo.c 21.6 KB
Newer Older
David Altobelli's avatar
David Altobelli committed
1
/*
2
 * Driver for the HP iLO management processor.
David Altobelli's avatar
David Altobelli committed
3 4 5 6 7 8 9 10 11 12 13 14 15
 *
 * Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
 *	David Altobelli <david.altobelli@hp.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pci.h>
David Altobelli's avatar
David Altobelli committed
16
#include <linux/interrupt.h>
David Altobelli's avatar
David Altobelli committed
17 18 19 20
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/file.h>
#include <linux/cdev.h>
21
#include <linux/sched.h>
David Altobelli's avatar
David Altobelli committed
22 23 24 25
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/io.h>
David Altobelli's avatar
David Altobelli committed
26
#include <linux/wait.h>
David Altobelli's avatar
David Altobelli committed
27
#include <linux/poll.h>
28
#include <linux/slab.h>
David Altobelli's avatar
David Altobelli committed
29 30 31 32
#include "hpilo.h"

static struct class *ilo_class;
static unsigned int ilo_major;
33
static unsigned int max_ccb = 16;
David Altobelli's avatar
David Altobelli committed
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
static char ilo_hwdev[MAX_ILO_DEV];

static inline int get_entry_id(int entry)
{
	return (entry & ENTRY_MASK_DESCRIPTOR) >> ENTRY_BITPOS_DESCRIPTOR;
}

static inline int get_entry_len(int entry)
{
	return ((entry & ENTRY_MASK_QWORDS) >> ENTRY_BITPOS_QWORDS) << 3;
}

static inline int mk_entry(int id, int len)
{
	int qlen = len & 7 ? (len >> 3) + 1 : len >> 3;
	return id << ENTRY_BITPOS_DESCRIPTOR | qlen << ENTRY_BITPOS_QWORDS;
}

static inline int desc_mem_sz(int nr_entry)
{
	return nr_entry << L2_QENTRY_SZ;
}

/*
 * FIFO queues, shared with hardware.
 *
 * If a queue has empty slots, an entry is added to the queue tail,
 * and that entry is marked as occupied.
 * Entries can be dequeued from the head of the list, when the device
 * has marked the entry as consumed.
 *
 * Returns true on successful queue/dequeue, false on failure.
 */
static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry)
{
	struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar);
David Altobelli's avatar
David Altobelli committed
70
	unsigned long flags;
David Altobelli's avatar
David Altobelli committed
71 72
	int ret = 0;

David Altobelli's avatar
David Altobelli committed
73
	spin_lock_irqsave(&hw->fifo_lock, flags);
David Altobelli's avatar
David Altobelli committed
74 75 76 77 78 79 80
	if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask]
	      & ENTRY_MASK_O)) {
		fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |=
				(entry & ENTRY_MASK_NOSTATE) | fifo_q->merge;
		fifo_q->tail += 1;
		ret = 1;
	}
David Altobelli's avatar
David Altobelli committed
81
	spin_unlock_irqrestore(&hw->fifo_lock, flags);
David Altobelli's avatar
David Altobelli committed
82 83 84 85 86 87 88

	return ret;
}

static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry)
{
	struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar);
David Altobelli's avatar
David Altobelli committed
89
	unsigned long flags;
David Altobelli's avatar
David Altobelli committed
90 91 92
	int ret = 0;
	u64 c;

David Altobelli's avatar
David Altobelli committed
93
	spin_lock_irqsave(&hw->fifo_lock, flags);
David Altobelli's avatar
David Altobelli committed
94 95 96 97 98 99 100 101 102 103
	c = fifo_q->fifobar[fifo_q->head & fifo_q->imask];
	if (c & ENTRY_MASK_C) {
		if (entry)
			*entry = c & ENTRY_MASK_NOSTATE;

		fifo_q->fifobar[fifo_q->head & fifo_q->imask] =
							(c | ENTRY_MASK) + 1;
		fifo_q->head += 1;
		ret = 1;
	}
David Altobelli's avatar
David Altobelli committed
104
	spin_unlock_irqrestore(&hw->fifo_lock, flags);
David Altobelli's avatar
David Altobelli committed
105 106 107 108

	return ret;
}

David Altobelli's avatar
David Altobelli committed
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
static int fifo_check_recv(struct ilo_hwinfo *hw, char *fifobar)
{
	struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar);
	unsigned long flags;
	int ret = 0;
	u64 c;

	spin_lock_irqsave(&hw->fifo_lock, flags);
	c = fifo_q->fifobar[fifo_q->head & fifo_q->imask];
	if (c & ENTRY_MASK_C)
		ret = 1;
	spin_unlock_irqrestore(&hw->fifo_lock, flags);

	return ret;
}

David Altobelli's avatar
David Altobelli committed
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
static int ilo_pkt_enqueue(struct ilo_hwinfo *hw, struct ccb *ccb,
			   int dir, int id, int len)
{
	char *fifobar;
	int entry;

	if (dir == SENDQ)
		fifobar = ccb->ccb_u1.send_fifobar;
	else
		fifobar = ccb->ccb_u3.recv_fifobar;

	entry = mk_entry(id, len);
	return fifo_enqueue(hw, fifobar, entry);
}

static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb,
			   int dir, int *id, int *len, void **pkt)
{
	char *fifobar, *desc;
	int entry = 0, pkt_id = 0;
	int ret;

	if (dir == SENDQ) {
		fifobar = ccb->ccb_u1.send_fifobar;
		desc = ccb->ccb_u2.send_desc;
	} else {
		fifobar = ccb->ccb_u3.recv_fifobar;
		desc = ccb->ccb_u4.recv_desc;
	}

	ret = fifo_dequeue(hw, fifobar, &entry);
	if (ret) {
		pkt_id = get_entry_id(entry);
		if (id)
			*id = pkt_id;
		if (len)
			*len = get_entry_len(entry);
		if (pkt)
			*pkt = (void *)(desc + desc_mem_sz(pkt_id));
	}

	return ret;
}

David Altobelli's avatar
David Altobelli committed
169 170 171 172 173 174 175
static int ilo_pkt_recv(struct ilo_hwinfo *hw, struct ccb *ccb)
{
	char *fifobar = ccb->ccb_u3.recv_fifobar;

	return fifo_check_recv(hw, fifobar);
}

David Altobelli's avatar
David Altobelli committed
176 177 178 179 180 181 182 183 184
static inline void doorbell_set(struct ccb *ccb)
{
	iowrite8(1, ccb->ccb_u5.db_base);
}

static inline void doorbell_clr(struct ccb *ccb)
{
	iowrite8(2, ccb->ccb_u5.db_base);
}
185

David Altobelli's avatar
David Altobelli committed
186 187 188 189 190 191 192 193 194
static inline int ctrl_set(int l2sz, int idxmask, int desclim)
{
	int active = 0, go = 1;
	return l2sz << CTRL_BITPOS_L2SZ |
	       idxmask << CTRL_BITPOS_FIFOINDEXMASK |
	       desclim << CTRL_BITPOS_DESCLIMIT |
	       active << CTRL_BITPOS_A |
	       go << CTRL_BITPOS_G;
}
195

David Altobelli's avatar
David Altobelli committed
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
static void ctrl_setup(struct ccb *ccb, int nr_desc, int l2desc_sz)
{
	/* for simplicity, use the same parameters for send and recv ctrls */
	ccb->send_ctrl = ctrl_set(l2desc_sz, nr_desc-1, nr_desc-1);
	ccb->recv_ctrl = ctrl_set(l2desc_sz, nr_desc-1, nr_desc-1);
}

static inline int fifo_sz(int nr_entry)
{
	/* size of a fifo is determined by the number of entries it contains */
	return (nr_entry * sizeof(u64)) + FIFOHANDLESIZE;
}

static void fifo_setup(void *base_addr, int nr_entry)
{
	struct fifo *fifo_q = base_addr;
	int i;

	/* set up an empty fifo */
	fifo_q->head = 0;
	fifo_q->tail = 0;
	fifo_q->reset = 0;
	fifo_q->nrents = nr_entry;
	fifo_q->imask = nr_entry - 1;
	fifo_q->merge = ENTRY_MASK_O;

	for (i = 0; i < nr_entry; i++)
		fifo_q->fifobar[i] = 0;
}

static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data)
{
228 229
	struct ccb *driver_ccb = &data->driver_ccb;
	struct ccb __iomem *device_ccb = data->mapped_ccb;
David Altobelli's avatar
David Altobelli committed
230 231 232 233 234 235 236 237 238 239
	int retries;

	/* complicated dance to tell the hw we are stopping */
	doorbell_clr(driver_ccb);
	iowrite32(ioread32(&device_ccb->send_ctrl) & ~(1 << CTRL_BITPOS_G),
		  &device_ccb->send_ctrl);
	iowrite32(ioread32(&device_ccb->recv_ctrl) & ~(1 << CTRL_BITPOS_G),
		  &device_ccb->recv_ctrl);

	/* give iLO some time to process stop request */
David Altobelli's avatar
David Altobelli committed
240
	for (retries = MAX_WAIT; retries > 0; retries--) {
David Altobelli's avatar
David Altobelli committed
241
		doorbell_set(driver_ccb);
242
		udelay(WAIT_TIME);
David Altobelli's avatar
David Altobelli committed
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
		if (!(ioread32(&device_ccb->send_ctrl) & (1 << CTRL_BITPOS_A))
		    &&
		    !(ioread32(&device_ccb->recv_ctrl) & (1 << CTRL_BITPOS_A)))
			break;
	}
	if (retries == 0)
		dev_err(&pdev->dev, "Closing, but controller still active\n");

	/* clear the hw ccb */
	memset_io(device_ccb, 0, sizeof(struct ccb));

	/* free resources used to back send/recv queues */
	pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa);
}

258
static int ilo_ccb_setup(struct ilo_hwinfo *hw, struct ccb_data *data, int slot)
David Altobelli's avatar
David Altobelli committed
259
{
260 261
	char *dma_va;
	dma_addr_t dma_pa;
David Altobelli's avatar
David Altobelli committed
262 263 264 265 266 267 268 269 270
	struct ccb *driver_ccb, *ilo_ccb;

	driver_ccb = &data->driver_ccb;
	ilo_ccb = &data->ilo_ccb;

	data->dma_size = 2 * fifo_sz(NR_QENTRY) +
			 2 * desc_mem_sz(NR_QENTRY) +
			 ILO_START_ALIGN + ILO_CACHE_SZ;

271
	data->dma_va = pci_alloc_consistent(hw->ilo_dev, data->dma_size,
David Altobelli's avatar
David Altobelli committed
272 273
					    &data->dma_pa);
	if (!data->dma_va)
274
		return -ENOMEM;
David Altobelli's avatar
David Altobelli committed
275 276

	dma_va = (char *)data->dma_va;
277
	dma_pa = data->dma_pa;
David Altobelli's avatar
David Altobelli committed
278 279 280 281

	memset(dma_va, 0, data->dma_size);

	dma_va = (char *)roundup((unsigned long)dma_va, ILO_START_ALIGN);
282
	dma_pa = roundup(dma_pa, ILO_START_ALIGN);
David Altobelli's avatar
David Altobelli committed
283 284 285 286 287 288 289 290 291 292

	/*
	 * Create two ccb's, one with virt addrs, one with phys addrs.
	 * Copy the phys addr ccb to device shared mem.
	 */
	ctrl_setup(driver_ccb, NR_QENTRY, L2_QENTRY_SZ);
	ctrl_setup(ilo_ccb, NR_QENTRY, L2_QENTRY_SZ);

	fifo_setup(dma_va, NR_QENTRY);
	driver_ccb->ccb_u1.send_fifobar = dma_va + FIFOHANDLESIZE;
293
	ilo_ccb->ccb_u1.send_fifobar_pa = dma_pa + FIFOHANDLESIZE;
David Altobelli's avatar
David Altobelli committed
294 295 296 297
	dma_va += fifo_sz(NR_QENTRY);
	dma_pa += fifo_sz(NR_QENTRY);

	dma_va = (char *)roundup((unsigned long)dma_va, ILO_CACHE_SZ);
298
	dma_pa = roundup(dma_pa, ILO_CACHE_SZ);
David Altobelli's avatar
David Altobelli committed
299 300 301

	fifo_setup(dma_va, NR_QENTRY);
	driver_ccb->ccb_u3.recv_fifobar = dma_va + FIFOHANDLESIZE;
302
	ilo_ccb->ccb_u3.recv_fifobar_pa = dma_pa + FIFOHANDLESIZE;
David Altobelli's avatar
David Altobelli committed
303 304 305 306
	dma_va += fifo_sz(NR_QENTRY);
	dma_pa += fifo_sz(NR_QENTRY);

	driver_ccb->ccb_u2.send_desc = dma_va;
307
	ilo_ccb->ccb_u2.send_desc_pa = dma_pa;
David Altobelli's avatar
David Altobelli committed
308 309 310 311
	dma_pa += desc_mem_sz(NR_QENTRY);
	dma_va += desc_mem_sz(NR_QENTRY);

	driver_ccb->ccb_u4.recv_desc = dma_va;
312
	ilo_ccb->ccb_u4.recv_desc_pa = dma_pa;
David Altobelli's avatar
David Altobelli committed
313 314 315 316 317 318 319

	driver_ccb->channel = slot;
	ilo_ccb->channel = slot;

	driver_ccb->ccb_u5.db_base = hw->db_vaddr + (slot << L2_DB_SIZE);
	ilo_ccb->ccb_u5.db_base = NULL; /* hw ccb's doorbell is not used */

320 321 322 323 324 325 326 327
	return 0;
}

static void ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot)
{
	int pkt_id, pkt_sz;
	struct ccb *driver_ccb = &data->driver_ccb;

David Altobelli's avatar
David Altobelli committed
328 329 330
	/* copy the ccb with physical addrs to device memory */
	data->mapped_ccb = (struct ccb __iomem *)
				(hw->ram_vaddr + (slot * ILOHW_CCB_SZ));
331
	memcpy_toio(data->mapped_ccb, &data->ilo_ccb, sizeof(struct ccb));
David Altobelli's avatar
David Altobelli committed
332 333 334 335 336 337 338 339 340 341 342 343

	/* put packets on the send and receive queues */
	pkt_sz = 0;
	for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) {
		ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, pkt_sz);
		doorbell_set(driver_ccb);
	}

	pkt_sz = desc_mem_sz(1);
	for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++)
		ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, pkt_sz);

344
	/* the ccb is ready to use */
David Altobelli's avatar
David Altobelli committed
345
	doorbell_clr(driver_ccb);
346 347 348 349 350 351
}

static int ilo_ccb_verify(struct ilo_hwinfo *hw, struct ccb_data *data)
{
	int pkt_id, i;
	struct ccb *driver_ccb = &data->driver_ccb;
David Altobelli's avatar
David Altobelli committed
352 353

	/* make sure iLO is really handling requests */
David Altobelli's avatar
David Altobelli committed
354
	for (i = MAX_WAIT; i > 0; i--) {
David Altobelli's avatar
David Altobelli committed
355 356
		if (ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, NULL, NULL))
			break;
357
		udelay(WAIT_TIME);
David Altobelli's avatar
David Altobelli committed
358 359
	}

360 361 362
	if (i == 0) {
		dev_err(&hw->ilo_dev->dev, "Open could not dequeue a packet\n");
		return -EBUSY;
David Altobelli's avatar
David Altobelli committed
363 364
	}

365 366
	ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0);
	doorbell_set(driver_ccb);
David Altobelli's avatar
David Altobelli committed
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
	return 0;
}

static inline int is_channel_reset(struct ccb *ccb)
{
	/* check for this particular channel needing a reset */
	return FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset;
}

static inline void set_channel_reset(struct ccb *ccb)
{
	/* set a flag indicating this channel needs a reset */
	FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset = 1;
}

382 383 384 385 386 387 388 389 390 391
static inline int get_device_outbound(struct ilo_hwinfo *hw)
{
	return ioread32(&hw->mmio_vaddr[DB_OUT]);
}

static inline int is_db_reset(int db_out)
{
	return db_out & (1 << DB_RESET);
}

David Altobelli's avatar
David Altobelli committed
392 393 394
static inline int is_device_reset(struct ilo_hwinfo *hw)
{
	/* check for global reset condition */
395 396 397 398 399 400
	return is_db_reset(get_device_outbound(hw));
}

static inline void clear_pending_db(struct ilo_hwinfo *hw, int clr)
{
	iowrite32(clr, &hw->mmio_vaddr[DB_OUT]);
David Altobelli's avatar
David Altobelli committed
401 402 403 404 405
}

static inline void clear_device(struct ilo_hwinfo *hw)
{
	/* clear the device (reset bits, pending channel entries) */
406
	clear_pending_db(hw, -1);
David Altobelli's avatar
David Altobelli committed
407 408
}

David Altobelli's avatar
David Altobelli committed
409 410 411 412 413 414 415 416 417 418 419 420
static inline void ilo_enable_interrupts(struct ilo_hwinfo *hw)
{
	iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) | 1, &hw->mmio_vaddr[DB_IRQ]);
}

static inline void ilo_disable_interrupts(struct ilo_hwinfo *hw)
{
	iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) & ~1,
		 &hw->mmio_vaddr[DB_IRQ]);
}

static void ilo_set_reset(struct ilo_hwinfo *hw)
David Altobelli's avatar
David Altobelli committed
421 422 423 424 425 426 427
{
	int slot;

	/*
	 * Mapped memory is zeroed on ilo reset, so set a per ccb flag
	 * to indicate that this ccb needs to be closed and reopened.
	 */
428
	for (slot = 0; slot < max_ccb; slot++) {
David Altobelli's avatar
David Altobelli committed
429 430 431 432 433 434 435 436 437 438
		if (!hw->ccb_alloc[slot])
			continue;
		set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb);
	}
}

static ssize_t ilo_read(struct file *fp, char __user *buf,
			size_t len, loff_t *off)
{
	int err, found, cnt, pkt_id, pkt_len;
439 440 441
	struct ccb_data *data = fp->private_data;
	struct ccb *driver_ccb = &data->driver_ccb;
	struct ilo_hwinfo *hw = data->ilo_hw;
David Altobelli's avatar
David Altobelli committed
442 443
	void *pkt;

David Altobelli's avatar
David Altobelli committed
444
	if (is_channel_reset(driver_ccb)) {
David Altobelli's avatar
David Altobelli committed
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
		/*
		 * If the device has been reset, applications
		 * need to close and reopen all ccbs.
		 */
		return -ENODEV;
	}

	/*
	 * This function is to be called when data is expected
	 * in the channel, and will return an error if no packet is found
	 * during the loop below.  The sleep/retry logic is to allow
	 * applications to call read() immediately post write(),
	 * and give iLO some time to process the sent packet.
	 */
	cnt = 20;
	do {
		/* look for a received packet */
		found = ilo_pkt_dequeue(hw, driver_ccb, RECVQ, &pkt_id,
					&pkt_len, &pkt);
		if (found)
			break;
		cnt--;
		msleep(100);
	} while (!found && cnt);

	if (!found)
		return -EAGAIN;

	/* only copy the length of the received packet */
	if (pkt_len < len)
		len = pkt_len;

	err = copy_to_user(buf, pkt, len);

	/* return the received packet to the queue */
	ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, desc_mem_sz(1));

	return err ? -EFAULT : len;
}

static ssize_t ilo_write(struct file *fp, const char __user *buf,
			 size_t len, loff_t *off)
{
	int err, pkt_id, pkt_len;
489 490 491
	struct ccb_data *data = fp->private_data;
	struct ccb *driver_ccb = &data->driver_ccb;
	struct ilo_hwinfo *hw = data->ilo_hw;
David Altobelli's avatar
David Altobelli committed
492 493
	void *pkt;

David Altobelli's avatar
David Altobelli committed
494
	if (is_channel_reset(driver_ccb))
David Altobelli's avatar
David Altobelli committed
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
		return -ENODEV;

	/* get a packet to send the user command */
	if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt))
		return -EBUSY;

	/* limit the length to the length of the packet */
	if (pkt_len < len)
		len = pkt_len;

	/* on failure, set the len to 0 to return empty packet to the device */
	err = copy_from_user(pkt, buf, len);
	if (err)
		len = 0;

	/* send the packet */
	ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, len);
	doorbell_set(driver_ccb);

	return err ? -EFAULT : len;
}

David Altobelli's avatar
David Altobelli committed
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
static unsigned int ilo_poll(struct file *fp, poll_table *wait)
{
	struct ccb_data *data = fp->private_data;
	struct ccb *driver_ccb = &data->driver_ccb;

	poll_wait(fp, &data->ccb_waitq, wait);

	if (is_channel_reset(driver_ccb))
		return POLLERR;
	else if (ilo_pkt_recv(data->ilo_hw, driver_ccb))
		return POLLIN | POLLRDNORM;

	return 0;
}

David Altobelli's avatar
David Altobelli committed
532 533 534 535 536
static int ilo_close(struct inode *ip, struct file *fp)
{
	int slot;
	struct ccb_data *data;
	struct ilo_hwinfo *hw;
David Altobelli's avatar
David Altobelli committed
537
	unsigned long flags;
David Altobelli's avatar
David Altobelli committed
538

539
	slot = iminor(ip) % max_ccb;
David Altobelli's avatar
David Altobelli committed
540 541
	hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev);

David Altobelli's avatar
David Altobelli committed
542
	spin_lock(&hw->open_lock);
David Altobelli's avatar
David Altobelli committed
543 544 545 546 547

	if (hw->ccb_alloc[slot]->ccb_cnt == 1) {

		data = fp->private_data;

David Altobelli's avatar
David Altobelli committed
548 549 550 551
		spin_lock_irqsave(&hw->alloc_lock, flags);
		hw->ccb_alloc[slot] = NULL;
		spin_unlock_irqrestore(&hw->alloc_lock, flags);

David Altobelli's avatar
David Altobelli committed
552 553 554 555 556 557
		ilo_ccb_close(hw->ilo_dev, data);

		kfree(data);
	} else
		hw->ccb_alloc[slot]->ccb_cnt--;

David Altobelli's avatar
David Altobelli committed
558
	spin_unlock(&hw->open_lock);
David Altobelli's avatar
David Altobelli committed
559 560 561 562 563 564 565 566 567

	return 0;
}

static int ilo_open(struct inode *ip, struct file *fp)
{
	int slot, error;
	struct ccb_data *data;
	struct ilo_hwinfo *hw;
David Altobelli's avatar
David Altobelli committed
568
	unsigned long flags;
David Altobelli's avatar
David Altobelli committed
569

570
	slot = iminor(ip) % max_ccb;
David Altobelli's avatar
David Altobelli committed
571 572 573 574 575 576 577
	hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev);

	/* new ccb allocation */
	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (!data)
		return -ENOMEM;

David Altobelli's avatar
David Altobelli committed
578
	spin_lock(&hw->open_lock);
David Altobelli's avatar
David Altobelli committed
579 580 581 582

	/* each fd private_data holds sw/hw view of ccb */
	if (hw->ccb_alloc[slot] == NULL) {
		/* create a channel control block for this minor */
583 584
		error = ilo_ccb_setup(hw, data, slot);
		if (error) {
David Altobelli's avatar
David Altobelli committed
585
			kfree(data);
586 587 588
			goto out;
		}

David Altobelli's avatar
David Altobelli committed
589 590 591 592 593
		data->ccb_cnt = 1;
		data->ccb_excl = fp->f_flags & O_EXCL;
		data->ilo_hw = hw;
		init_waitqueue_head(&data->ccb_waitq);

594
		/* write the ccb to hw */
David Altobelli's avatar
David Altobelli committed
595
		spin_lock_irqsave(&hw->alloc_lock, flags);
596
		ilo_ccb_open(hw, data, slot);
David Altobelli's avatar
David Altobelli committed
597 598
		hw->ccb_alloc[slot] = data;
		spin_unlock_irqrestore(&hw->alloc_lock, flags);
599 600 601 602

		/* make sure the channel is functional */
		error = ilo_ccb_verify(hw, data);
		if (error) {
David Altobelli's avatar
David Altobelli committed
603 604 605 606 607

			spin_lock_irqsave(&hw->alloc_lock, flags);
			hw->ccb_alloc[slot] = NULL;
			spin_unlock_irqrestore(&hw->alloc_lock, flags);

608
			ilo_ccb_close(hw->ilo_dev, data);
David Altobelli's avatar
David Altobelli committed
609

610 611 612 613
			kfree(data);
			goto out;
		}

David Altobelli's avatar
David Altobelli committed
614 615 616 617 618 619 620 621 622 623 624 625 626 627
	} else {
		kfree(data);
		if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) {
			/*
			 * The channel exists, and either this open
			 * or a previous open of this channel wants
			 * exclusive access.
			 */
			error = -EBUSY;
		} else {
			hw->ccb_alloc[slot]->ccb_cnt++;
			error = 0;
		}
	}
628
out:
David Altobelli's avatar
David Altobelli committed
629
	spin_unlock(&hw->open_lock);
David Altobelli's avatar
David Altobelli committed
630 631 632 633 634 635 636 637 638 639 640

	if (!error)
		fp->private_data = hw->ccb_alloc[slot];

	return error;
}

static const struct file_operations ilo_fops = {
	.owner		= THIS_MODULE,
	.read		= ilo_read,
	.write		= ilo_write,
David Altobelli's avatar
David Altobelli committed
641
	.poll		= ilo_poll,
David Altobelli's avatar
David Altobelli committed
642 643
	.open 		= ilo_open,
	.release 	= ilo_close,
644
	.llseek		= noop_llseek,
David Altobelli's avatar
David Altobelli committed
645 646
};

David Altobelli's avatar
David Altobelli committed
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
static irqreturn_t ilo_isr(int irq, void *data)
{
	struct ilo_hwinfo *hw = data;
	int pending, i;

	spin_lock(&hw->alloc_lock);

	/* check for ccbs which have data */
	pending = get_device_outbound(hw);
	if (!pending) {
		spin_unlock(&hw->alloc_lock);
		return IRQ_NONE;
	}

	if (is_db_reset(pending)) {
		/* wake up all ccbs if the device was reset */
		pending = -1;
		ilo_set_reset(hw);
	}

667
	for (i = 0; i < max_ccb; i++) {
David Altobelli's avatar
David Altobelli committed
668 669 670 671 672 673 674 675 676 677 678 679 680 681
		if (!hw->ccb_alloc[i])
			continue;
		if (pending & (1 << i))
			wake_up_interruptible(&hw->ccb_alloc[i]->ccb_waitq);
	}

	/* clear the device of the channels that have been handled */
	clear_pending_db(hw, pending);

	spin_unlock(&hw->alloc_lock);

	return IRQ_HANDLED;
}

David Altobelli's avatar
David Altobelli committed
682 683 684 685 686 687 688
static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
{
	pci_iounmap(pdev, hw->db_vaddr);
	pci_iounmap(pdev, hw->ram_vaddr);
	pci_iounmap(pdev, hw->mmio_vaddr);
}

Bill Pemberton's avatar
Bill Pemberton committed
689
static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
David Altobelli's avatar
David Altobelli committed
690 691 692 693 694 695 696 697 698 699 700
{
	int error = -ENOMEM;

	/* map the memory mapped i/o registers */
	hw->mmio_vaddr = pci_iomap(pdev, 1, 0);
	if (hw->mmio_vaddr == NULL) {
		dev_err(&pdev->dev, "Error mapping mmio\n");
		goto out;
	}

	/* map the adapter shared memory region */
701
	hw->ram_vaddr = pci_iomap(pdev, 2, max_ccb * ILOHW_CCB_SZ);
David Altobelli's avatar
David Altobelli committed
702 703 704 705 706 707
	if (hw->ram_vaddr == NULL) {
		dev_err(&pdev->dev, "Error mapping shared mem\n");
		goto mmio_free;
	}

	/* map the doorbell aperture */
708
	hw->db_vaddr = pci_iomap(pdev, 3, max_ccb * ONE_DB_SIZE);
David Altobelli's avatar
David Altobelli committed
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
	if (hw->db_vaddr == NULL) {
		dev_err(&pdev->dev, "Error mapping doorbell\n");
		goto ram_free;
	}

	return 0;
ram_free:
	pci_iounmap(pdev, hw->ram_vaddr);
mmio_free:
	pci_iounmap(pdev, hw->mmio_vaddr);
out:
	return error;
}

static void ilo_remove(struct pci_dev *pdev)
{
	int i, minor;
	struct ilo_hwinfo *ilo_hw = pci_get_drvdata(pdev);

728 729 730
	if (!ilo_hw)
		return;

David Altobelli's avatar
David Altobelli committed
731 732 733
	clear_device(ilo_hw);

	minor = MINOR(ilo_hw->cdev.dev);
734
	for (i = minor; i < minor + max_ccb; i++)
David Altobelli's avatar
David Altobelli committed
735 736 737
		device_destroy(ilo_class, MKDEV(ilo_major, i));

	cdev_del(&ilo_hw->cdev);
David Altobelli's avatar
David Altobelli committed
738 739
	ilo_disable_interrupts(ilo_hw);
	free_irq(pdev->irq, ilo_hw);
David Altobelli's avatar
David Altobelli committed
740 741
	ilo_unmap_device(pdev, ilo_hw);
	pci_release_regions(pdev);
742 743 744 745 746 747 748 749
	/*
	 * pci_disable_device(pdev) used to be here. But this PCI device has
	 * two functions with interrupt lines connected to a single pin. The
	 * other one is a USB host controller. So when we disable the PIN here
	 * e.g. by rmmod hpilo, the controller stops working. It is because
	 * the interrupt link is disabled in ACPI since it is not refcounted
	 * yet. See acpi_pci_link_free_irq called from acpi_pci_irq_disable.
	 */
David Altobelli's avatar
David Altobelli committed
750
	kfree(ilo_hw);
751
	ilo_hwdev[(minor / max_ccb)] = 0;
David Altobelli's avatar
David Altobelli committed
752 753
}

Bill Pemberton's avatar
Bill Pemberton committed
754
static int ilo_probe(struct pci_dev *pdev,
David Altobelli's avatar
David Altobelli committed
755 756
			       const struct pci_device_id *ent)
{
757
	int devnum, minor, start, error = 0;
David Altobelli's avatar
David Altobelli committed
758 759
	struct ilo_hwinfo *ilo_hw;

760 761
	/* Ignore subsystem_device = 0x1979 (set by BIOS)  */
	if (pdev->subsystem_device == 0x1979)
762
		return 0;
763

764 765 766 767 768
	if (max_ccb > MAX_CCB)
		max_ccb = MAX_CCB;
	else if (max_ccb < MIN_CCB)
		max_ccb = MIN_CCB;

David Altobelli's avatar
David Altobelli committed
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
	/* find a free range for device files */
	for (devnum = 0; devnum < MAX_ILO_DEV; devnum++) {
		if (ilo_hwdev[devnum] == 0) {
			ilo_hwdev[devnum] = 1;
			break;
		}
	}

	if (devnum == MAX_ILO_DEV) {
		dev_err(&pdev->dev, "Error finding free device\n");
		return -ENODEV;
	}

	/* track global allocations for this device */
	error = -ENOMEM;
	ilo_hw = kzalloc(sizeof(*ilo_hw), GFP_KERNEL);
	if (!ilo_hw)
		goto out;

	ilo_hw->ilo_dev = pdev;
	spin_lock_init(&ilo_hw->alloc_lock);
	spin_lock_init(&ilo_hw->fifo_lock);
David Altobelli's avatar
David Altobelli committed
791
	spin_lock_init(&ilo_hw->open_lock);
David Altobelli's avatar
David Altobelli committed
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809

	error = pci_enable_device(pdev);
	if (error)
		goto free;

	pci_set_master(pdev);

	error = pci_request_regions(pdev, ILO_NAME);
	if (error)
		goto disable;

	error = ilo_map_device(pdev, ilo_hw);
	if (error)
		goto free_regions;

	pci_set_drvdata(pdev, ilo_hw);
	clear_device(ilo_hw);

David Altobelli's avatar
David Altobelli committed
810 811 812 813 814 815
	error = request_irq(pdev->irq, ilo_isr, IRQF_SHARED, "hpilo", ilo_hw);
	if (error)
		goto unmap;

	ilo_enable_interrupts(ilo_hw);

David Altobelli's avatar
David Altobelli committed
816 817
	cdev_init(&ilo_hw->cdev, &ilo_fops);
	ilo_hw->cdev.owner = THIS_MODULE;
818 819
	start = devnum * max_ccb;
	error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), max_ccb);
David Altobelli's avatar
David Altobelli committed
820 821
	if (error) {
		dev_err(&pdev->dev, "Could not add cdev\n");
David Altobelli's avatar
David Altobelli committed
822
		goto remove_isr;
David Altobelli's avatar
David Altobelli committed
823 824
	}

825
	for (minor = 0 ; minor < max_ccb; minor++) {
David Altobelli's avatar
David Altobelli committed
826 827 828 829 830 831 832 833 834
		struct device *dev;
		dev = device_create(ilo_class, &pdev->dev,
				    MKDEV(ilo_major, minor), NULL,
				    "hpilo!d%dccb%d", devnum, minor);
		if (IS_ERR(dev))
			dev_err(&pdev->dev, "Could not create files\n");
	}

	return 0;
David Altobelli's avatar
David Altobelli committed
835 836 837
remove_isr:
	ilo_disable_interrupts(ilo_hw);
	free_irq(pdev->irq, ilo_hw);
David Altobelli's avatar
David Altobelli committed
838 839 840 841 842
unmap:
	ilo_unmap_device(pdev, ilo_hw);
free_regions:
	pci_release_regions(pdev);
disable:
843
/*	pci_disable_device(pdev);  see comment in ilo_remove */
David Altobelli's avatar
David Altobelli committed
844 845 846 847 848 849 850 851 852
free:
	kfree(ilo_hw);
out:
	ilo_hwdev[devnum] = 0;
	return error;
}

static struct pci_device_id ilo_devices[] = {
	{ PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB204) },
David Altobelli's avatar
David Altobelli committed
853
	{ PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3307) },
David Altobelli's avatar
David Altobelli committed
854 855 856 857 858 859 860 861
	{ }
};
MODULE_DEVICE_TABLE(pci, ilo_devices);

static struct pci_driver ilo_driver = {
	.name 	  = ILO_NAME,
	.id_table = ilo_devices,
	.probe 	  = ilo_probe,
862
	.remove   = ilo_remove,
David Altobelli's avatar
David Altobelli committed
863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
};

static int __init ilo_init(void)
{
	int error;
	dev_t dev;

	ilo_class = class_create(THIS_MODULE, "iLO");
	if (IS_ERR(ilo_class)) {
		error = PTR_ERR(ilo_class);
		goto out;
	}

	error = alloc_chrdev_region(&dev, 0, MAX_OPEN, ILO_NAME);
	if (error)
		goto class_destroy;

	ilo_major = MAJOR(dev);

	error =	pci_register_driver(&ilo_driver);
	if (error)
		goto chr_remove;

	return 0;
chr_remove:
	unregister_chrdev_region(dev, MAX_OPEN);
class_destroy:
	class_destroy(ilo_class);
out:
	return error;
}

static void __exit ilo_exit(void)
{
	pci_unregister_driver(&ilo_driver);
	unregister_chrdev_region(MKDEV(ilo_major, 0), MAX_OPEN);
	class_destroy(ilo_class);
}

902
MODULE_VERSION("1.4.1");
David Altobelli's avatar
David Altobelli committed
903 904 905 906 907
MODULE_ALIAS(ILO_NAME);
MODULE_DESCRIPTION(ILO_NAME);
MODULE_AUTHOR("David Altobelli <david.altobelli@hp.com>");
MODULE_LICENSE("GPL v2");

908
module_param(max_ccb, uint, 0444);
909
MODULE_PARM_DESC(max_ccb, "Maximum number of HP iLO channels to attach (16)");
910

David Altobelli's avatar
David Altobelli committed
911 912
module_init(ilo_init);
module_exit(ilo_exit);