[kernel] r11890 - in dists/trunk/linux-2.6/debian: . config patches/features/all patches/series
Maximilian Attems
maks at alioth.debian.org
Wed Jul 23 22:23:02 UTC 2008
Author: maks
Date: Wed Jul 23 22:23:01 2008
New Revision: 11890
Log:
add hp ilo driver
directly backported from linus tree,
this driver replaces the old hprsm packages.
Added:
dists/trunk/linux-2.6/debian/patches/features/all/drivers-hp_ilo.patch
Modified:
dists/trunk/linux-2.6/debian/changelog
dists/trunk/linux-2.6/debian/config/config
dists/trunk/linux-2.6/debian/patches/series/1~experimental.1
Modified: dists/trunk/linux-2.6/debian/changelog
==============================================================================
--- dists/trunk/linux-2.6/debian/changelog (original)
+++ dists/trunk/linux-2.6/debian/changelog Wed Jul 23 22:23:01 2008
@@ -74,6 +74,7 @@
* atl1e driver for Atheros(R) L1e Fast Ethernet. (closes: #492029)
* [ALSA] hda - Add ICH9 controller support (8086:2911)
* [ALSA] hda - support intel DG33 motherboards
+ * HP iLO driver
[ Martin Michlmayr ]
* [arm/orion5x] Update the config to reflect upstream renaming this
Modified: dists/trunk/linux-2.6/debian/config/config
==============================================================================
--- dists/trunk/linux-2.6/debian/config/config (original)
+++ dists/trunk/linux-2.6/debian/config/config Wed Jul 23 22:23:01 2008
@@ -893,6 +893,7 @@
CONFIG_MSI_LAPTOP=m
CONFIG_SONY_LAPTOP=m
CONFIG_ENCLOSURE_SERVICES=m
+CONFIG_HP_ILO=m
##
## file: drivers/mmc/card/Kconfig
Added: dists/trunk/linux-2.6/debian/patches/features/all/drivers-hp_ilo.patch
==============================================================================
--- (empty file)
+++ dists/trunk/linux-2.6/debian/patches/features/all/drivers-hp_ilo.patch Wed Jul 23 22:23:01 2008
@@ -0,0 +1,1016 @@
+commit 89bcb05d9bbf8bd559988bca4f2579defd28d008
+Author: David Altobelli <david.altobelli at hp.com>
+Date: Wed Jul 2 09:38:53 2008 -0600
+
+ HP iLO driver
+
+ A driver for the HP iLO/iLO2 management processor, which allows userspace
+ programs to query the management processor. Programs can open a channel
+ to the device (/dev/hpilo/dXccbN), and use this to send/receive queries.
+ The O_EXCL open flag is used to indicate that a particular channel cannot
+ be shared between processes. This driver will replace various packages
+ HP has shipped, including hprsm and hp-ilo.
+
+ Signed-off-by: David Altobelli <david.altobelli at hp.com>
+ Signed-off-by: Greg Kroah-Hartman <gregkh at suse.de>
+
+diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
+index 1921b8d..ce67d97 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -420,4 +420,17 @@ config SGI_XP
+ this feature will allow for direct communication between SSIs
+ based on a network adapter and DMA messaging.
+
++config HP_ILO
++ tristate "Channel interface driver for HP iLO/iLO2 processor"
++ default n
++ help
++ The channel interface driver allows applications to communicate
++ with iLO/iLO2 management processors present on HP ProLiant
++ servers. Upon loading, the driver creates /dev/hpilo/dXccbN files,
++ which can be used to gather data from the management processor,
++ via read and write system calls.
++
++ To compile this driver as a module, choose M here: the
++ module will be called hpilo.
++
+ endif # MISC_DEVICES
+diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
+index a6dac6a..688fe76 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -27,3 +27,4 @@ obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
+ obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
+ obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
+ obj-$(CONFIG_SGI_XP) += sgi-xp/
++obj-$(CONFIG_HP_ILO) += hpilo.o
+diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c
+new file mode 100644
+index 0000000..05e2982
+--- /dev/null
++++ b/drivers/misc/hpilo.c
+@@ -0,0 +1,768 @@
++/*
++ * Driver for HP iLO/iLO2 management processor.
++ *
++ * Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
++ * David Altobelli <david.altobelli at 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>
++#include <linux/ioport.h>
++#include <linux/device.h>
++#include <linux/file.h>
++#include <linux/cdev.h>
++#include <linux/spinlock.h>
++#include <linux/delay.h>
++#include <linux/uaccess.h>
++#include <linux/io.h>
++#include "hpilo.h"
++
++static struct class *ilo_class;
++static unsigned int ilo_major;
++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);
++ int ret = 0;
++
++ spin_lock(&hw->fifo_lock);
++ 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(&hw->fifo_lock);
++
++ return ret;
++}
++
++static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry)
++{
++ struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar);
++ int ret = 0;
++ u64 c;
++
++ spin_lock(&hw->fifo_lock);
++ 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(&hw->fifo_lock);
++
++ 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 {
++ 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;
++}
++
++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);
++}
++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;
++}
++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)
++{
++ struct ccb *driver_ccb;
++ struct ccb __iomem *device_ccb;
++ int retries;
++
++ driver_ccb = &data->driver_ccb;
++ device_ccb = data->mapped_ccb;
++
++ /* 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 */
++ for (retries = 1000; retries > 0; retries--) {
++ doorbell_set(driver_ccb);
++ udelay(1);
++ 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);
++}
++
++static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot)
++{
++ char *dma_va, *dma_pa;
++ int pkt_id, pkt_sz, i, error;
++ struct ccb *driver_ccb, *ilo_ccb;
++ struct pci_dev *pdev;
++
++ driver_ccb = &data->driver_ccb;
++ ilo_ccb = &data->ilo_ccb;
++ pdev = hw->ilo_dev;
++
++ data->dma_size = 2 * fifo_sz(NR_QENTRY) +
++ 2 * desc_mem_sz(NR_QENTRY) +
++ ILO_START_ALIGN + ILO_CACHE_SZ;
++
++ error = -ENOMEM;
++ data->dma_va = pci_alloc_consistent(pdev, data->dma_size,
++ &data->dma_pa);
++ if (!data->dma_va)
++ goto out;
++
++ dma_va = (char *)data->dma_va;
++ dma_pa = (char *)data->dma_pa;
++
++ memset(dma_va, 0, data->dma_size);
++
++ dma_va = (char *)roundup((unsigned long)dma_va, ILO_START_ALIGN);
++ dma_pa = (char *)roundup((unsigned long)dma_pa, ILO_START_ALIGN);
++
++ /*
++ * 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;
++ ilo_ccb->ccb_u1.send_fifobar = dma_pa + FIFOHANDLESIZE;
++ dma_va += fifo_sz(NR_QENTRY);
++ dma_pa += fifo_sz(NR_QENTRY);
++
++ dma_va = (char *)roundup((unsigned long)dma_va, ILO_CACHE_SZ);
++ dma_pa = (char *)roundup((unsigned long)dma_pa, ILO_CACHE_SZ);
++
++ fifo_setup(dma_va, NR_QENTRY);
++ driver_ccb->ccb_u3.recv_fifobar = dma_va + FIFOHANDLESIZE;
++ ilo_ccb->ccb_u3.recv_fifobar = dma_pa + FIFOHANDLESIZE;
++ dma_va += fifo_sz(NR_QENTRY);
++ dma_pa += fifo_sz(NR_QENTRY);
++
++ driver_ccb->ccb_u2.send_desc = dma_va;
++ ilo_ccb->ccb_u2.send_desc = dma_pa;
++ dma_pa += desc_mem_sz(NR_QENTRY);
++ dma_va += desc_mem_sz(NR_QENTRY);
++
++ driver_ccb->ccb_u4.recv_desc = dma_va;
++ ilo_ccb->ccb_u4.recv_desc = dma_pa;
++
++ 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 */
++
++ /* copy the ccb with physical addrs to device memory */
++ data->mapped_ccb = (struct ccb __iomem *)
++ (hw->ram_vaddr + (slot * ILOHW_CCB_SZ));
++ memcpy_toio(data->mapped_ccb, ilo_ccb, sizeof(struct ccb));
++
++ /* 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);
++
++ doorbell_clr(driver_ccb);
++
++ /* make sure iLO is really handling requests */
++ for (i = 1000; i > 0; i--) {
++ if (ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, NULL, NULL))
++ break;
++ udelay(1);
++ }
++
++ if (i) {
++ ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0);
++ doorbell_set(driver_ccb);
++ } else {
++ dev_err(&pdev->dev, "Open could not dequeue a packet\n");
++ error = -EBUSY;
++ goto free;
++ }
++
++ return 0;
++free:
++ pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa);
++out:
++ return error;
++}
++
++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;
++}
++
++static inline int is_device_reset(struct ilo_hwinfo *hw)
++{
++ /* check for global reset condition */
++ return ioread32(&hw->mmio_vaddr[DB_OUT]) & (1 << DB_RESET);
++}
++
++static inline void clear_device(struct ilo_hwinfo *hw)
++{
++ /* clear the device (reset bits, pending channel entries) */
++ iowrite32(-1, &hw->mmio_vaddr[DB_OUT]);
++}
++
++static void ilo_locked_reset(struct ilo_hwinfo *hw)
++{
++ 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.
++ */
++ for (slot = 0; slot < MAX_CCB; slot++) {
++ if (!hw->ccb_alloc[slot])
++ continue;
++ set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb);
++ }
++
++ clear_device(hw);
++}
++
++static void ilo_reset(struct ilo_hwinfo *hw)
++{
++ spin_lock(&hw->alloc_lock);
++
++ /* reset might have been handled after lock was taken */
++ if (is_device_reset(hw))
++ ilo_locked_reset(hw);
++
++ spin_unlock(&hw->alloc_lock);
++}
++
++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;
++ struct ccb_data *data;
++ struct ccb *driver_ccb;
++ struct ilo_hwinfo *hw;
++ void *pkt;
++
++ data = fp->private_data;
++ driver_ccb = &data->driver_ccb;
++ hw = data->ilo_hw;
++
++ if (is_device_reset(hw) || is_channel_reset(driver_ccb)) {
++ /*
++ * If the device has been reset, applications
++ * need to close and reopen all ccbs.
++ */
++ ilo_reset(hw);
++ 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;
++ struct ccb_data *data;
++ struct ccb *driver_ccb;
++ struct ilo_hwinfo *hw;
++ void *pkt;
++
++ data = fp->private_data;
++ driver_ccb = &data->driver_ccb;
++ hw = data->ilo_hw;
++
++ if (is_device_reset(hw) || is_channel_reset(driver_ccb)) {
++ /*
++ * If the device has been reset, applications
++ * need to close and reopen all ccbs.
++ */
++ ilo_reset(hw);
++ 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;
++}
++
++static int ilo_close(struct inode *ip, struct file *fp)
++{
++ int slot;
++ struct ccb_data *data;
++ struct ilo_hwinfo *hw;
++
++ slot = iminor(ip) % MAX_CCB;
++ hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev);
++
++ spin_lock(&hw->alloc_lock);
++
++ if (is_device_reset(hw))
++ ilo_locked_reset(hw);
++
++ if (hw->ccb_alloc[slot]->ccb_cnt == 1) {
++
++ data = fp->private_data;
++
++ ilo_ccb_close(hw->ilo_dev, data);
++
++ kfree(data);
++ hw->ccb_alloc[slot] = NULL;
++ } else
++ hw->ccb_alloc[slot]->ccb_cnt--;
++
++ spin_unlock(&hw->alloc_lock);
++
++ return 0;
++}
++
++static int ilo_open(struct inode *ip, struct file *fp)
++{
++ int slot, error;
++ struct ccb_data *data;
++ struct ilo_hwinfo *hw;
++
++ slot = iminor(ip) % MAX_CCB;
++ hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev);
++
++ /* new ccb allocation */
++ data = kzalloc(sizeof(*data), GFP_KERNEL);
++ if (!data)
++ return -ENOMEM;
++
++ spin_lock(&hw->alloc_lock);
++
++ if (is_device_reset(hw))
++ ilo_locked_reset(hw);
++
++ /* each fd private_data holds sw/hw view of ccb */
++ if (hw->ccb_alloc[slot] == NULL) {
++ /* create a channel control block for this minor */
++ error = ilo_ccb_open(hw, data, slot);
++ if (!error) {
++ hw->ccb_alloc[slot] = data;
++ hw->ccb_alloc[slot]->ccb_cnt = 1;
++ hw->ccb_alloc[slot]->ccb_excl = fp->f_flags & O_EXCL;
++ hw->ccb_alloc[slot]->ilo_hw = hw;
++ } else
++ kfree(data);
++ } 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;
++ }
++ }
++ spin_unlock(&hw->alloc_lock);
++
++ 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,
++ .open = ilo_open,
++ .release = ilo_close,
++};
++
++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);
++}
++
++static int __devinit ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
++{
++ 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 */
++ hw->ram_vaddr = pci_iomap(pdev, 2, MAX_CCB * ILOHW_CCB_SZ);
++ if (hw->ram_vaddr == NULL) {
++ dev_err(&pdev->dev, "Error mapping shared mem\n");
++ goto mmio_free;
++ }
++
++ /* map the doorbell aperture */
++ hw->db_vaddr = pci_iomap(pdev, 3, MAX_CCB * ONE_DB_SIZE);
++ 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);
++
++ clear_device(ilo_hw);
++
++ minor = MINOR(ilo_hw->cdev.dev);
++ for (i = minor; i < minor + MAX_CCB; i++)
++ device_destroy(ilo_class, MKDEV(ilo_major, i));
++
++ cdev_del(&ilo_hw->cdev);
++ ilo_unmap_device(pdev, ilo_hw);
++ pci_release_regions(pdev);
++ pci_disable_device(pdev);
++ kfree(ilo_hw);
++ ilo_hwdev[(minor / MAX_CCB)] = 0;
++}
++
++static int __devinit ilo_probe(struct pci_dev *pdev,
++ const struct pci_device_id *ent)
++{
++ int devnum, minor, start, error;
++ struct ilo_hwinfo *ilo_hw;
++
++ /* 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);
++
++ 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);
++
++ cdev_init(&ilo_hw->cdev, &ilo_fops);
++ ilo_hw->cdev.owner = THIS_MODULE;
++ start = devnum * MAX_CCB;
++ error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), MAX_CCB);
++ if (error) {
++ dev_err(&pdev->dev, "Could not add cdev\n");
++ goto unmap;
++ }
++
++ for (minor = 0 ; minor < MAX_CCB; minor++) {
++ 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;
++unmap:
++ ilo_unmap_device(pdev, ilo_hw);
++free_regions:
++ pci_release_regions(pdev);
++disable:
++ pci_disable_device(pdev);
++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) },
++ { }
++};
++MODULE_DEVICE_TABLE(pci, ilo_devices);
++
++static struct pci_driver ilo_driver = {
++ .name = ILO_NAME,
++ .id_table = ilo_devices,
++ .probe = ilo_probe,
++ .remove = __devexit_p(ilo_remove),
++};
++
++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);
++}
++
++MODULE_VERSION("0.05");
++MODULE_ALIAS(ILO_NAME);
++MODULE_DESCRIPTION(ILO_NAME);
++MODULE_AUTHOR("David Altobelli <david.altobelli at hp.com>");
++MODULE_LICENSE("GPL v2");
++
++module_init(ilo_init);
++module_exit(ilo_exit);
+diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h
+new file mode 100644
+index 0000000..a281207
+--- /dev/null
++++ b/drivers/misc/hpilo.h
+@@ -0,0 +1,189 @@
++/*
++ * linux/drivers/char/hpilo.h
++ *
++ * Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
++ * David Altobelli <david.altobelli at 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.
++ */
++#ifndef __HPILO_H
++#define __HPILO_H
++
++#define ILO_NAME "hpilo"
++
++/* max number of open channel control blocks per device, hw limited to 32 */
++#define MAX_CCB 8
++/* max number of supported devices */
++#define MAX_ILO_DEV 1
++/* max number of files */
++#define MAX_OPEN (MAX_CCB * MAX_ILO_DEV)
++
++/*
++ * Per device, used to track global memory allocations.
++ */
++struct ilo_hwinfo {
++ /* mmio registers on device */
++ char __iomem *mmio_vaddr;
++
++ /* doorbell registers on device */
++ char __iomem *db_vaddr;
++
++ /* shared memory on device used for channel control blocks */
++ char __iomem *ram_vaddr;
++
++ /* files corresponding to this device */
++ struct ccb_data *ccb_alloc[MAX_CCB];
++
++ struct pci_dev *ilo_dev;
++
++ spinlock_t alloc_lock;
++ spinlock_t fifo_lock;
++
++ struct cdev cdev;
++};
++
++/* offset from mmio_vaddr */
++#define DB_OUT 0xD4
++/* DB_OUT reset bit */
++#define DB_RESET 26
++
++/*
++ * Channel control block. Used to manage hardware queues.
++ * The format must match hw's version. The hw ccb is 128 bytes,
++ * but the context area shouldn't be touched by the driver.
++ */
++#define ILOSW_CCB_SZ 64
++#define ILOHW_CCB_SZ 128
++struct ccb {
++ union {
++ char *send_fifobar;
++ u64 padding1;
++ } ccb_u1;
++ union {
++ char *send_desc;
++ u64 padding2;
++ } ccb_u2;
++ u64 send_ctrl;
++
++ union {
++ char *recv_fifobar;
++ u64 padding3;
++ } ccb_u3;
++ union {
++ char *recv_desc;
++ u64 padding4;
++ } ccb_u4;
++ u64 recv_ctrl;
++
++ union {
++ char __iomem *db_base;
++ u64 padding5;
++ } ccb_u5;
++
++ u64 channel;
++
++ /* unused context area (64 bytes) */
++};
++
++/* ccb queue parameters */
++#define SENDQ 1
++#define RECVQ 2
++#define NR_QENTRY 4
++#define L2_QENTRY_SZ 12
++
++/* ccb ctrl bitfields */
++#define CTRL_BITPOS_L2SZ 0
++#define CTRL_BITPOS_FIFOINDEXMASK 4
++#define CTRL_BITPOS_DESCLIMIT 18
++#define CTRL_BITPOS_A 30
++#define CTRL_BITPOS_G 31
++
++/* ccb doorbell macros */
++#define L2_DB_SIZE 14
++#define ONE_DB_SIZE (1 << L2_DB_SIZE)
++
++/*
++ * Per fd structure used to track the ccb allocated to that dev file.
++ */
++struct ccb_data {
++ /* software version of ccb, using virtual addrs */
++ struct ccb driver_ccb;
++
++ /* hardware version of ccb, using physical addrs */
++ struct ccb ilo_ccb;
++
++ /* hardware ccb is written to this shared mapped device memory */
++ struct ccb __iomem *mapped_ccb;
++
++ /* dma'able memory used for send/recv queues */
++ void *dma_va;
++ dma_addr_t dma_pa;
++ size_t dma_size;
++
++ /* pointer to hardware device info */
++ struct ilo_hwinfo *ilo_hw;
++
++ /* usage count, to allow for shared ccb's */
++ int ccb_cnt;
++
++ /* open wanted exclusive access to this ccb */
++ int ccb_excl;
++};
++
++/*
++ * FIFO queue structure, shared with hw.
++ */
++#define ILO_START_ALIGN 4096
++#define ILO_CACHE_SZ 128
++struct fifo {
++ u64 nrents; /* user requested number of fifo entries */
++ u64 imask; /* mask to extract valid fifo index */
++ u64 merge; /* O/C bits to merge in during enqueue operation */
++ u64 reset; /* set to non-zero when the target device resets */
++ u8 pad_0[ILO_CACHE_SZ - (sizeof(u64) * 4)];
++
++ u64 head;
++ u8 pad_1[ILO_CACHE_SZ - (sizeof(u64))];
++
++ u64 tail;
++ u8 pad_2[ILO_CACHE_SZ - (sizeof(u64))];
++
++ u64 fifobar[1];
++};
++
++/* convert between struct fifo, and the fifobar, which is saved in the ccb */
++#define FIFOHANDLESIZE (sizeof(struct fifo) - sizeof(u64))
++#define FIFOBARTOHANDLE(_fifo) \
++ ((struct fifo *)(((char *)(_fifo)) - FIFOHANDLESIZE))
++
++/* the number of qwords to consume from the entry descriptor */
++#define ENTRY_BITPOS_QWORDS 0
++/* descriptor index number (within a specified queue) */
++#define ENTRY_BITPOS_DESCRIPTOR 10
++/* state bit, fifo entry consumed by consumer */
++#define ENTRY_BITPOS_C 22
++/* state bit, fifo entry is occupied */
++#define ENTRY_BITPOS_O 23
++
++#define ENTRY_BITS_QWORDS 10
++#define ENTRY_BITS_DESCRIPTOR 12
++#define ENTRY_BITS_C 1
++#define ENTRY_BITS_O 1
++#define ENTRY_BITS_TOTAL \
++ (ENTRY_BITS_C + ENTRY_BITS_O + \
++ ENTRY_BITS_QWORDS + ENTRY_BITS_DESCRIPTOR)
++
++/* extract various entry fields */
++#define ENTRY_MASK ((1 << ENTRY_BITS_TOTAL) - 1)
++#define ENTRY_MASK_C (((1 << ENTRY_BITS_C) - 1) << ENTRY_BITPOS_C)
++#define ENTRY_MASK_O (((1 << ENTRY_BITS_O) - 1) << ENTRY_BITPOS_O)
++#define ENTRY_MASK_QWORDS \
++ (((1 << ENTRY_BITS_QWORDS) - 1) << ENTRY_BITPOS_QWORDS)
++#define ENTRY_MASK_DESCRIPTOR \
++ (((1 << ENTRY_BITS_DESCRIPTOR) - 1) << ENTRY_BITPOS_DESCRIPTOR)
++
++#define ENTRY_MASK_NOSTATE (ENTRY_MASK >> (ENTRY_BITS_C + ENTRY_BITS_O))
++
++#endif /* __HPILO_H */
Modified: dists/trunk/linux-2.6/debian/patches/series/1~experimental.1
==============================================================================
--- dists/trunk/linux-2.6/debian/patches/series/1~experimental.1 (original)
+++ dists/trunk/linux-2.6/debian/patches/series/1~experimental.1 Wed Jul 23 22:23:01 2008
@@ -59,3 +59,4 @@
+ features/all/drivers-net-atl1e.patch
+ bugfix/all/sound_hda_ich9.patch
+ bugfix/all/sound_hda_intel_dg33.patch
++ features/all/drivers-hp_ilo.patch
More information about the Kernel-svn-changes
mailing list