/*
* Driver for the HP iLO management processor.
*
* Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
* David Altobelli <david.altobelli@hpe.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>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/file.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include "hpilo.h"
static struct class *ilo_class;
static unsigned int ilo_major;
static unsigned int max_ccb = 16;
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);
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&hw->fifo_lock, flags);
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;
}
spin_unlock_irqrestore(&hw->fifo_lock, flags);
return ret;
}
static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry)
{
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) {
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;
}
spin_unlock_irqrestore(&hw->fifo_lock, flags);
return ret;
}
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;
}
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 {