diff options
-rw-r--r-- | drivers/misc/hpilo.c | 120 |
1 files changed, 73 insertions, 47 deletions
diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 880ccf39e23..35ed12379cd 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -151,6 +151,7 @@ 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; @@ -160,6 +161,7 @@ static inline int ctrl_set(int l2sz, int idxmask, int desclim) 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 */ @@ -192,13 +194,10 @@ static void fifo_setup(void *base_addr, int nr_entry) static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) { - struct ccb *driver_ccb; - struct ccb __iomem *device_ccb; + struct ccb *driver_ccb = &data->driver_ccb; + struct ccb __iomem *device_ccb = data->mapped_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), @@ -225,26 +224,22 @@ static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) 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) +static int ilo_ccb_setup(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_va = pci_alloc_consistent(hw->ilo_dev, data->dma_size, &data->dma_pa); if (!data->dma_va) - goto out; + return -ENOMEM; dma_va = (char *)data->dma_va; dma_pa = (char *)data->dma_pa; @@ -290,10 +285,18 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int 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 */ + 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; + /* 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)); + memcpy_toio(data->mapped_ccb, &data->ilo_ccb, sizeof(struct ccb)); /* put packets on the send and receive queues */ pkt_sz = 0; @@ -306,7 +309,14 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, pkt_sz); + /* the ccb is ready to use */ doorbell_clr(driver_ccb); +} + +static int ilo_ccb_verify(struct ilo_hwinfo *hw, struct ccb_data *data) +{ + int pkt_id, i; + struct ccb *driver_ccb = &data->driver_ccb; /* make sure iLO is really handling requests */ for (i = MAX_WAIT; i > 0; i--) { @@ -315,20 +325,14 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) udelay(WAIT_TIME); } - 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; + if (i == 0) { + dev_err(&hw->ilo_dev->dev, "Open could not dequeue a packet\n"); + return -EBUSY; } + ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0); + doorbell_set(driver_ccb); return 0; -free: - ilo_ccb_close(pdev, data); -out: - return error; } static inline int is_channel_reset(struct ccb *ccb) @@ -343,16 +347,31 @@ static inline void set_channel_reset(struct ccb *ccb) FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset = 1; } +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); +} + 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); + 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]); } static inline void clear_device(struct ilo_hwinfo *hw) { /* clear the device (reset bits, pending channel entries) */ - iowrite32(-1, &hw->mmio_vaddr[DB_OUT]); + clear_pending_db(hw, -1); } static void ilo_locked_reset(struct ilo_hwinfo *hw) @@ -387,15 +406,11 @@ 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; + struct ccb_data *data = fp->private_data; + struct ccb *driver_ccb = &data->driver_ccb; + struct ilo_hwinfo *hw = data->ilo_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 @@ -442,15 +457,11 @@ 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; + struct ccb_data *data = fp->private_data; + struct ccb *driver_ccb = &data->driver_ccb; + struct ilo_hwinfo *hw = data->ilo_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 @@ -532,14 +543,28 @@ static int ilo_open(struct inode *ip, struct file *fp) /* 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 + error = ilo_ccb_setup(hw, data, slot); + if (error) { + kfree(data); + goto out; + } + + /* write the ccb to hw */ + ilo_ccb_open(hw, data, slot); + + /* make sure the channel is functional */ + error = ilo_ccb_verify(hw, data); + if (error) { + ilo_ccb_close(hw->ilo_dev, data); kfree(data); + goto out; + } + + data->ccb_cnt = 1; + data->ccb_excl = fp->f_flags & O_EXCL; + data->ilo_hw = hw; + hw->ccb_alloc[slot] = data; + } else { kfree(data); if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) { @@ -554,6 +579,7 @@ static int ilo_open(struct inode *ip, struct file *fp) error = 0; } } +out: spin_unlock(&hw->alloc_lock); if (!error) |