diff --git a/debian/changelog b/debian/changelog index 087926725..fee074b42 100644 --- a/debian/changelog +++ b/debian/changelog @@ -74,6 +74,7 @@ linux-2.6 (2.6.26-1~experimental.1) UNRELEASED; urgency=low * 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 diff --git a/debian/config/config b/debian/config/config index 8429c08a3..b4d67a7cb 100644 --- a/debian/config/config +++ b/debian/config/config @@ -893,6 +893,7 @@ CONFIG_FUJITSU_LAPTOP=m CONFIG_MSI_LAPTOP=m CONFIG_SONY_LAPTOP=m CONFIG_ENCLOSURE_SERVICES=m +CONFIG_HP_ILO=m ## ## file: drivers/mmc/card/Kconfig diff --git a/debian/patches/features/all/drivers-hp_ilo.patch b/debian/patches/features/all/drivers-hp_ilo.patch new file mode 100644 index 000000000..83f7673e3 --- /dev/null +++ b/debian/patches/features/all/drivers-hp_ilo.patch @@ -0,0 +1,1016 @@ +commit 89bcb05d9bbf8bd559988bca4f2579defd28d008 +Author: David Altobelli +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 + Signed-off-by: Greg Kroah-Hartman + +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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 "); ++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 ++ * ++ * 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 */ diff --git a/debian/patches/series/1~experimental.1 b/debian/patches/series/1~experimental.1 index f5bf6a666..c1f0b71e1 100644 --- a/debian/patches/series/1~experimental.1 +++ b/debian/patches/series/1~experimental.1 @@ -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