162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the HP iLO management processor. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. 662306a36Sopenharmony_ci * David Altobelli <david.altobelli@hpe.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/fs.h> 1262306a36Sopenharmony_ci#include <linux/pci.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/ioport.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/file.h> 1762306a36Sopenharmony_ci#include <linux/cdev.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci#include <linux/uaccess.h> 2262306a36Sopenharmony_ci#include <linux/io.h> 2362306a36Sopenharmony_ci#include <linux/wait.h> 2462306a36Sopenharmony_ci#include <linux/poll.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include "hpilo.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const struct class ilo_class = { 2962306a36Sopenharmony_ci .name = "iLO", 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_cistatic unsigned int ilo_major; 3262306a36Sopenharmony_cistatic unsigned int max_ccb = 16; 3362306a36Sopenharmony_cistatic char ilo_hwdev[MAX_ILO_DEV]; 3462306a36Sopenharmony_cistatic const struct pci_device_id ilo_blacklist[] = { 3562306a36Sopenharmony_ci /* auxiliary iLO */ 3662306a36Sopenharmony_ci {PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP, 0x1979)}, 3762306a36Sopenharmony_ci /* CL */ 3862306a36Sopenharmony_ci {PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3307, PCI_VENDOR_ID_HP_3PAR, 0x0289)}, 3962306a36Sopenharmony_ci {} 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic inline int get_entry_id(int entry) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci return (entry & ENTRY_MASK_DESCRIPTOR) >> ENTRY_BITPOS_DESCRIPTOR; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic inline int get_entry_len(int entry) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci return ((entry & ENTRY_MASK_QWORDS) >> ENTRY_BITPOS_QWORDS) << 3; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic inline int mk_entry(int id, int len) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci int qlen = len & 7 ? (len >> 3) + 1 : len >> 3; 5562306a36Sopenharmony_ci return id << ENTRY_BITPOS_DESCRIPTOR | qlen << ENTRY_BITPOS_QWORDS; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic inline int desc_mem_sz(int nr_entry) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci return nr_entry << L2_QENTRY_SZ; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * FIFO queues, shared with hardware. 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * If a queue has empty slots, an entry is added to the queue tail, 6762306a36Sopenharmony_ci * and that entry is marked as occupied. 6862306a36Sopenharmony_ci * Entries can be dequeued from the head of the list, when the device 6962306a36Sopenharmony_ci * has marked the entry as consumed. 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * Returns true on successful queue/dequeue, false on failure. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cistatic int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); 7662306a36Sopenharmony_ci unsigned long flags; 7762306a36Sopenharmony_ci int ret = 0; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci spin_lock_irqsave(&hw->fifo_lock, flags); 8062306a36Sopenharmony_ci if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask] 8162306a36Sopenharmony_ci & ENTRY_MASK_O)) { 8262306a36Sopenharmony_ci fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |= 8362306a36Sopenharmony_ci (entry & ENTRY_MASK_NOSTATE) | fifo_q->merge; 8462306a36Sopenharmony_ci fifo_q->tail += 1; 8562306a36Sopenharmony_ci ret = 1; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->fifo_lock, flags); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return ret; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); 9562306a36Sopenharmony_ci unsigned long flags; 9662306a36Sopenharmony_ci int ret = 0; 9762306a36Sopenharmony_ci u64 c; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci spin_lock_irqsave(&hw->fifo_lock, flags); 10062306a36Sopenharmony_ci c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; 10162306a36Sopenharmony_ci if (c & ENTRY_MASK_C) { 10262306a36Sopenharmony_ci if (entry) 10362306a36Sopenharmony_ci *entry = c & ENTRY_MASK_NOSTATE; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci fifo_q->fifobar[fifo_q->head & fifo_q->imask] = 10662306a36Sopenharmony_ci (c | ENTRY_MASK) + 1; 10762306a36Sopenharmony_ci fifo_q->head += 1; 10862306a36Sopenharmony_ci ret = 1; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->fifo_lock, flags); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return ret; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int fifo_check_recv(struct ilo_hwinfo *hw, char *fifobar) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); 11862306a36Sopenharmony_ci unsigned long flags; 11962306a36Sopenharmony_ci int ret = 0; 12062306a36Sopenharmony_ci u64 c; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci spin_lock_irqsave(&hw->fifo_lock, flags); 12362306a36Sopenharmony_ci c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; 12462306a36Sopenharmony_ci if (c & ENTRY_MASK_C) 12562306a36Sopenharmony_ci ret = 1; 12662306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->fifo_lock, flags); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return ret; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int ilo_pkt_enqueue(struct ilo_hwinfo *hw, struct ccb *ccb, 13262306a36Sopenharmony_ci int dir, int id, int len) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci char *fifobar; 13562306a36Sopenharmony_ci int entry; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (dir == SENDQ) 13862306a36Sopenharmony_ci fifobar = ccb->ccb_u1.send_fifobar; 13962306a36Sopenharmony_ci else 14062306a36Sopenharmony_ci fifobar = ccb->ccb_u3.recv_fifobar; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci entry = mk_entry(id, len); 14362306a36Sopenharmony_ci return fifo_enqueue(hw, fifobar, entry); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb, 14762306a36Sopenharmony_ci int dir, int *id, int *len, void **pkt) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci char *fifobar, *desc; 15062306a36Sopenharmony_ci int entry = 0, pkt_id = 0; 15162306a36Sopenharmony_ci int ret; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (dir == SENDQ) { 15462306a36Sopenharmony_ci fifobar = ccb->ccb_u1.send_fifobar; 15562306a36Sopenharmony_ci desc = ccb->ccb_u2.send_desc; 15662306a36Sopenharmony_ci } else { 15762306a36Sopenharmony_ci fifobar = ccb->ccb_u3.recv_fifobar; 15862306a36Sopenharmony_ci desc = ccb->ccb_u4.recv_desc; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci ret = fifo_dequeue(hw, fifobar, &entry); 16262306a36Sopenharmony_ci if (ret) { 16362306a36Sopenharmony_ci pkt_id = get_entry_id(entry); 16462306a36Sopenharmony_ci if (id) 16562306a36Sopenharmony_ci *id = pkt_id; 16662306a36Sopenharmony_ci if (len) 16762306a36Sopenharmony_ci *len = get_entry_len(entry); 16862306a36Sopenharmony_ci if (pkt) 16962306a36Sopenharmony_ci *pkt = (void *)(desc + desc_mem_sz(pkt_id)); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return ret; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int ilo_pkt_recv(struct ilo_hwinfo *hw, struct ccb *ccb) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci char *fifobar = ccb->ccb_u3.recv_fifobar; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return fifo_check_recv(hw, fifobar); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic inline void doorbell_set(struct ccb *ccb) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci iowrite8(1, ccb->ccb_u5.db_base); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic inline void doorbell_clr(struct ccb *ccb) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci iowrite8(2, ccb->ccb_u5.db_base); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic inline int ctrl_set(int l2sz, int idxmask, int desclim) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci int active = 0, go = 1; 19562306a36Sopenharmony_ci return l2sz << CTRL_BITPOS_L2SZ | 19662306a36Sopenharmony_ci idxmask << CTRL_BITPOS_FIFOINDEXMASK | 19762306a36Sopenharmony_ci desclim << CTRL_BITPOS_DESCLIMIT | 19862306a36Sopenharmony_ci active << CTRL_BITPOS_A | 19962306a36Sopenharmony_ci go << CTRL_BITPOS_G; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic void ctrl_setup(struct ccb *ccb, int nr_desc, int l2desc_sz) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci /* for simplicity, use the same parameters for send and recv ctrls */ 20562306a36Sopenharmony_ci ccb->send_ctrl = ctrl_set(l2desc_sz, nr_desc-1, nr_desc-1); 20662306a36Sopenharmony_ci ccb->recv_ctrl = ctrl_set(l2desc_sz, nr_desc-1, nr_desc-1); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic inline int fifo_sz(int nr_entry) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci /* size of a fifo is determined by the number of entries it contains */ 21262306a36Sopenharmony_ci return nr_entry * sizeof(u64) + FIFOHANDLESIZE; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic void fifo_setup(void *base_addr, int nr_entry) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct fifo *fifo_q = base_addr; 21862306a36Sopenharmony_ci int i; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* set up an empty fifo */ 22162306a36Sopenharmony_ci fifo_q->head = 0; 22262306a36Sopenharmony_ci fifo_q->tail = 0; 22362306a36Sopenharmony_ci fifo_q->reset = 0; 22462306a36Sopenharmony_ci fifo_q->nrents = nr_entry; 22562306a36Sopenharmony_ci fifo_q->imask = nr_entry - 1; 22662306a36Sopenharmony_ci fifo_q->merge = ENTRY_MASK_O; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci for (i = 0; i < nr_entry; i++) 22962306a36Sopenharmony_ci fifo_q->fifobar[i] = 0; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct ccb *driver_ccb = &data->driver_ccb; 23562306a36Sopenharmony_ci struct ccb __iomem *device_ccb = data->mapped_ccb; 23662306a36Sopenharmony_ci int retries; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* complicated dance to tell the hw we are stopping */ 23962306a36Sopenharmony_ci doorbell_clr(driver_ccb); 24062306a36Sopenharmony_ci iowrite32(ioread32(&device_ccb->send_ctrl) & ~(1 << CTRL_BITPOS_G), 24162306a36Sopenharmony_ci &device_ccb->send_ctrl); 24262306a36Sopenharmony_ci iowrite32(ioread32(&device_ccb->recv_ctrl) & ~(1 << CTRL_BITPOS_G), 24362306a36Sopenharmony_ci &device_ccb->recv_ctrl); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* give iLO some time to process stop request */ 24662306a36Sopenharmony_ci for (retries = MAX_WAIT; retries > 0; retries--) { 24762306a36Sopenharmony_ci doorbell_set(driver_ccb); 24862306a36Sopenharmony_ci udelay(WAIT_TIME); 24962306a36Sopenharmony_ci if (!(ioread32(&device_ccb->send_ctrl) & (1 << CTRL_BITPOS_A)) 25062306a36Sopenharmony_ci && 25162306a36Sopenharmony_ci !(ioread32(&device_ccb->recv_ctrl) & (1 << CTRL_BITPOS_A))) 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci if (retries == 0) 25562306a36Sopenharmony_ci dev_err(&pdev->dev, "Closing, but controller still active\n"); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* clear the hw ccb */ 25862306a36Sopenharmony_ci memset_io(device_ccb, 0, sizeof(struct ccb)); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* free resources used to back send/recv queues */ 26162306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, data->dma_size, data->dma_va, 26262306a36Sopenharmony_ci data->dma_pa); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int ilo_ccb_setup(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci char *dma_va; 26862306a36Sopenharmony_ci dma_addr_t dma_pa; 26962306a36Sopenharmony_ci struct ccb *driver_ccb, *ilo_ccb; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci driver_ccb = &data->driver_ccb; 27262306a36Sopenharmony_ci ilo_ccb = &data->ilo_ccb; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci data->dma_size = 2 * fifo_sz(NR_QENTRY) + 27562306a36Sopenharmony_ci 2 * desc_mem_sz(NR_QENTRY) + 27662306a36Sopenharmony_ci ILO_START_ALIGN + ILO_CACHE_SZ; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci data->dma_va = dma_alloc_coherent(&hw->ilo_dev->dev, data->dma_size, 27962306a36Sopenharmony_ci &data->dma_pa, GFP_ATOMIC); 28062306a36Sopenharmony_ci if (!data->dma_va) 28162306a36Sopenharmony_ci return -ENOMEM; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci dma_va = (char *)data->dma_va; 28462306a36Sopenharmony_ci dma_pa = data->dma_pa; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci dma_va = (char *)roundup((unsigned long)dma_va, ILO_START_ALIGN); 28762306a36Sopenharmony_ci dma_pa = roundup(dma_pa, ILO_START_ALIGN); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* 29062306a36Sopenharmony_ci * Create two ccb's, one with virt addrs, one with phys addrs. 29162306a36Sopenharmony_ci * Copy the phys addr ccb to device shared mem. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci ctrl_setup(driver_ccb, NR_QENTRY, L2_QENTRY_SZ); 29462306a36Sopenharmony_ci ctrl_setup(ilo_ccb, NR_QENTRY, L2_QENTRY_SZ); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci fifo_setup(dma_va, NR_QENTRY); 29762306a36Sopenharmony_ci driver_ccb->ccb_u1.send_fifobar = dma_va + FIFOHANDLESIZE; 29862306a36Sopenharmony_ci ilo_ccb->ccb_u1.send_fifobar_pa = dma_pa + FIFOHANDLESIZE; 29962306a36Sopenharmony_ci dma_va += fifo_sz(NR_QENTRY); 30062306a36Sopenharmony_ci dma_pa += fifo_sz(NR_QENTRY); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci dma_va = (char *)roundup((unsigned long)dma_va, ILO_CACHE_SZ); 30362306a36Sopenharmony_ci dma_pa = roundup(dma_pa, ILO_CACHE_SZ); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci fifo_setup(dma_va, NR_QENTRY); 30662306a36Sopenharmony_ci driver_ccb->ccb_u3.recv_fifobar = dma_va + FIFOHANDLESIZE; 30762306a36Sopenharmony_ci ilo_ccb->ccb_u3.recv_fifobar_pa = dma_pa + FIFOHANDLESIZE; 30862306a36Sopenharmony_ci dma_va += fifo_sz(NR_QENTRY); 30962306a36Sopenharmony_ci dma_pa += fifo_sz(NR_QENTRY); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci driver_ccb->ccb_u2.send_desc = dma_va; 31262306a36Sopenharmony_ci ilo_ccb->ccb_u2.send_desc_pa = dma_pa; 31362306a36Sopenharmony_ci dma_pa += desc_mem_sz(NR_QENTRY); 31462306a36Sopenharmony_ci dma_va += desc_mem_sz(NR_QENTRY); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci driver_ccb->ccb_u4.recv_desc = dma_va; 31762306a36Sopenharmony_ci ilo_ccb->ccb_u4.recv_desc_pa = dma_pa; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci driver_ccb->channel = slot; 32062306a36Sopenharmony_ci ilo_ccb->channel = slot; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci driver_ccb->ccb_u5.db_base = hw->db_vaddr + (slot << L2_DB_SIZE); 32362306a36Sopenharmony_ci ilo_ccb->ccb_u5.db_base = NULL; /* hw ccb's doorbell is not used */ 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic void ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci int pkt_id, pkt_sz; 33162306a36Sopenharmony_ci struct ccb *driver_ccb = &data->driver_ccb; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* copy the ccb with physical addrs to device memory */ 33462306a36Sopenharmony_ci data->mapped_ccb = (struct ccb __iomem *) 33562306a36Sopenharmony_ci (hw->ram_vaddr + (slot * ILOHW_CCB_SZ)); 33662306a36Sopenharmony_ci memcpy_toio(data->mapped_ccb, &data->ilo_ccb, sizeof(struct ccb)); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* put packets on the send and receive queues */ 33962306a36Sopenharmony_ci pkt_sz = 0; 34062306a36Sopenharmony_ci for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) { 34162306a36Sopenharmony_ci ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, pkt_sz); 34262306a36Sopenharmony_ci doorbell_set(driver_ccb); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci pkt_sz = desc_mem_sz(1); 34662306a36Sopenharmony_ci for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) 34762306a36Sopenharmony_ci ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, pkt_sz); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* the ccb is ready to use */ 35062306a36Sopenharmony_ci doorbell_clr(driver_ccb); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic int ilo_ccb_verify(struct ilo_hwinfo *hw, struct ccb_data *data) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci int pkt_id, i; 35662306a36Sopenharmony_ci struct ccb *driver_ccb = &data->driver_ccb; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* make sure iLO is really handling requests */ 35962306a36Sopenharmony_ci for (i = MAX_WAIT; i > 0; i--) { 36062306a36Sopenharmony_ci if (ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, NULL, NULL)) 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci udelay(WAIT_TIME); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (i == 0) { 36662306a36Sopenharmony_ci dev_err(&hw->ilo_dev->dev, "Open could not dequeue a packet\n"); 36762306a36Sopenharmony_ci return -EBUSY; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0); 37162306a36Sopenharmony_ci doorbell_set(driver_ccb); 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic inline int is_channel_reset(struct ccb *ccb) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci /* check for this particular channel needing a reset */ 37862306a36Sopenharmony_ci return FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic inline void set_channel_reset(struct ccb *ccb) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci /* set a flag indicating this channel needs a reset */ 38462306a36Sopenharmony_ci FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset = 1; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic inline int get_device_outbound(struct ilo_hwinfo *hw) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci return ioread32(&hw->mmio_vaddr[DB_OUT]); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic inline int is_db_reset(int db_out) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci return db_out & (1 << DB_RESET); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic inline void clear_pending_db(struct ilo_hwinfo *hw, int clr) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci iowrite32(clr, &hw->mmio_vaddr[DB_OUT]); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic inline void clear_device(struct ilo_hwinfo *hw) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci /* clear the device (reset bits, pending channel entries) */ 40562306a36Sopenharmony_ci clear_pending_db(hw, -1); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic inline void ilo_enable_interrupts(struct ilo_hwinfo *hw) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) | 1, &hw->mmio_vaddr[DB_IRQ]); 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic inline void ilo_disable_interrupts(struct ilo_hwinfo *hw) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) & ~1, 41662306a36Sopenharmony_ci &hw->mmio_vaddr[DB_IRQ]); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void ilo_set_reset(struct ilo_hwinfo *hw) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci int slot; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* 42462306a36Sopenharmony_ci * Mapped memory is zeroed on ilo reset, so set a per ccb flag 42562306a36Sopenharmony_ci * to indicate that this ccb needs to be closed and reopened. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci for (slot = 0; slot < max_ccb; slot++) { 42862306a36Sopenharmony_ci if (!hw->ccb_alloc[slot]) 42962306a36Sopenharmony_ci continue; 43062306a36Sopenharmony_ci set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb); 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic ssize_t ilo_read(struct file *fp, char __user *buf, 43562306a36Sopenharmony_ci size_t len, loff_t *off) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci int err, found, cnt, pkt_id, pkt_len; 43862306a36Sopenharmony_ci struct ccb_data *data = fp->private_data; 43962306a36Sopenharmony_ci struct ccb *driver_ccb = &data->driver_ccb; 44062306a36Sopenharmony_ci struct ilo_hwinfo *hw = data->ilo_hw; 44162306a36Sopenharmony_ci void *pkt; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (is_channel_reset(driver_ccb)) { 44462306a36Sopenharmony_ci /* 44562306a36Sopenharmony_ci * If the device has been reset, applications 44662306a36Sopenharmony_ci * need to close and reopen all ccbs. 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ci return -ENODEV; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* 45262306a36Sopenharmony_ci * This function is to be called when data is expected 45362306a36Sopenharmony_ci * in the channel, and will return an error if no packet is found 45462306a36Sopenharmony_ci * during the loop below. The sleep/retry logic is to allow 45562306a36Sopenharmony_ci * applications to call read() immediately post write(), 45662306a36Sopenharmony_ci * and give iLO some time to process the sent packet. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci cnt = 20; 45962306a36Sopenharmony_ci do { 46062306a36Sopenharmony_ci /* look for a received packet */ 46162306a36Sopenharmony_ci found = ilo_pkt_dequeue(hw, driver_ccb, RECVQ, &pkt_id, 46262306a36Sopenharmony_ci &pkt_len, &pkt); 46362306a36Sopenharmony_ci if (found) 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci cnt--; 46662306a36Sopenharmony_ci msleep(100); 46762306a36Sopenharmony_ci } while (!found && cnt); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (!found) 47062306a36Sopenharmony_ci return -EAGAIN; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* only copy the length of the received packet */ 47362306a36Sopenharmony_ci if (pkt_len < len) 47462306a36Sopenharmony_ci len = pkt_len; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci err = copy_to_user(buf, pkt, len); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* return the received packet to the queue */ 47962306a36Sopenharmony_ci ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, desc_mem_sz(1)); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return err ? -EFAULT : len; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic ssize_t ilo_write(struct file *fp, const char __user *buf, 48562306a36Sopenharmony_ci size_t len, loff_t *off) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci int err, pkt_id, pkt_len; 48862306a36Sopenharmony_ci struct ccb_data *data = fp->private_data; 48962306a36Sopenharmony_ci struct ccb *driver_ccb = &data->driver_ccb; 49062306a36Sopenharmony_ci struct ilo_hwinfo *hw = data->ilo_hw; 49162306a36Sopenharmony_ci void *pkt; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (is_channel_reset(driver_ccb)) 49462306a36Sopenharmony_ci return -ENODEV; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* get a packet to send the user command */ 49762306a36Sopenharmony_ci if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt)) 49862306a36Sopenharmony_ci return -EBUSY; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* limit the length to the length of the packet */ 50162306a36Sopenharmony_ci if (pkt_len < len) 50262306a36Sopenharmony_ci len = pkt_len; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* on failure, set the len to 0 to return empty packet to the device */ 50562306a36Sopenharmony_ci err = copy_from_user(pkt, buf, len); 50662306a36Sopenharmony_ci if (err) 50762306a36Sopenharmony_ci len = 0; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* send the packet */ 51062306a36Sopenharmony_ci ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, len); 51162306a36Sopenharmony_ci doorbell_set(driver_ccb); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return err ? -EFAULT : len; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic __poll_t ilo_poll(struct file *fp, poll_table *wait) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct ccb_data *data = fp->private_data; 51962306a36Sopenharmony_ci struct ccb *driver_ccb = &data->driver_ccb; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci poll_wait(fp, &data->ccb_waitq, wait); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (is_channel_reset(driver_ccb)) 52462306a36Sopenharmony_ci return EPOLLERR; 52562306a36Sopenharmony_ci else if (ilo_pkt_recv(data->ilo_hw, driver_ccb)) 52662306a36Sopenharmony_ci return EPOLLIN | EPOLLRDNORM; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci return 0; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic int ilo_close(struct inode *ip, struct file *fp) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci int slot; 53462306a36Sopenharmony_ci struct ccb_data *data; 53562306a36Sopenharmony_ci struct ilo_hwinfo *hw; 53662306a36Sopenharmony_ci unsigned long flags; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci slot = iminor(ip) % max_ccb; 53962306a36Sopenharmony_ci hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci spin_lock(&hw->open_lock); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (hw->ccb_alloc[slot]->ccb_cnt == 1) { 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci data = fp->private_data; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci spin_lock_irqsave(&hw->alloc_lock, flags); 54862306a36Sopenharmony_ci hw->ccb_alloc[slot] = NULL; 54962306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->alloc_lock, flags); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci ilo_ccb_close(hw->ilo_dev, data); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci kfree(data); 55462306a36Sopenharmony_ci } else 55562306a36Sopenharmony_ci hw->ccb_alloc[slot]->ccb_cnt--; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci spin_unlock(&hw->open_lock); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return 0; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic int ilo_open(struct inode *ip, struct file *fp) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci int slot, error; 56562306a36Sopenharmony_ci struct ccb_data *data; 56662306a36Sopenharmony_ci struct ilo_hwinfo *hw; 56762306a36Sopenharmony_ci unsigned long flags; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci slot = iminor(ip) % max_ccb; 57062306a36Sopenharmony_ci hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* new ccb allocation */ 57362306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 57462306a36Sopenharmony_ci if (!data) 57562306a36Sopenharmony_ci return -ENOMEM; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci spin_lock(&hw->open_lock); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* each fd private_data holds sw/hw view of ccb */ 58062306a36Sopenharmony_ci if (hw->ccb_alloc[slot] == NULL) { 58162306a36Sopenharmony_ci /* create a channel control block for this minor */ 58262306a36Sopenharmony_ci error = ilo_ccb_setup(hw, data, slot); 58362306a36Sopenharmony_ci if (error) { 58462306a36Sopenharmony_ci kfree(data); 58562306a36Sopenharmony_ci goto out; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci data->ccb_cnt = 1; 58962306a36Sopenharmony_ci data->ccb_excl = fp->f_flags & O_EXCL; 59062306a36Sopenharmony_ci data->ilo_hw = hw; 59162306a36Sopenharmony_ci init_waitqueue_head(&data->ccb_waitq); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* write the ccb to hw */ 59462306a36Sopenharmony_ci spin_lock_irqsave(&hw->alloc_lock, flags); 59562306a36Sopenharmony_ci ilo_ccb_open(hw, data, slot); 59662306a36Sopenharmony_ci hw->ccb_alloc[slot] = data; 59762306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->alloc_lock, flags); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* make sure the channel is functional */ 60062306a36Sopenharmony_ci error = ilo_ccb_verify(hw, data); 60162306a36Sopenharmony_ci if (error) { 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci spin_lock_irqsave(&hw->alloc_lock, flags); 60462306a36Sopenharmony_ci hw->ccb_alloc[slot] = NULL; 60562306a36Sopenharmony_ci spin_unlock_irqrestore(&hw->alloc_lock, flags); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci ilo_ccb_close(hw->ilo_dev, data); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci kfree(data); 61062306a36Sopenharmony_ci goto out; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci } else { 61462306a36Sopenharmony_ci kfree(data); 61562306a36Sopenharmony_ci if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) { 61662306a36Sopenharmony_ci /* 61762306a36Sopenharmony_ci * The channel exists, and either this open 61862306a36Sopenharmony_ci * or a previous open of this channel wants 61962306a36Sopenharmony_ci * exclusive access. 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_ci error = -EBUSY; 62262306a36Sopenharmony_ci } else { 62362306a36Sopenharmony_ci hw->ccb_alloc[slot]->ccb_cnt++; 62462306a36Sopenharmony_ci error = 0; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ciout: 62862306a36Sopenharmony_ci spin_unlock(&hw->open_lock); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (!error) 63162306a36Sopenharmony_ci fp->private_data = hw->ccb_alloc[slot]; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci return error; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic const struct file_operations ilo_fops = { 63762306a36Sopenharmony_ci .owner = THIS_MODULE, 63862306a36Sopenharmony_ci .read = ilo_read, 63962306a36Sopenharmony_ci .write = ilo_write, 64062306a36Sopenharmony_ci .poll = ilo_poll, 64162306a36Sopenharmony_ci .open = ilo_open, 64262306a36Sopenharmony_ci .release = ilo_close, 64362306a36Sopenharmony_ci .llseek = noop_llseek, 64462306a36Sopenharmony_ci}; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic irqreturn_t ilo_isr(int irq, void *data) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct ilo_hwinfo *hw = data; 64962306a36Sopenharmony_ci int pending, i; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci spin_lock(&hw->alloc_lock); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* check for ccbs which have data */ 65462306a36Sopenharmony_ci pending = get_device_outbound(hw); 65562306a36Sopenharmony_ci if (!pending) { 65662306a36Sopenharmony_ci spin_unlock(&hw->alloc_lock); 65762306a36Sopenharmony_ci return IRQ_NONE; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (is_db_reset(pending)) { 66162306a36Sopenharmony_ci /* wake up all ccbs if the device was reset */ 66262306a36Sopenharmony_ci pending = -1; 66362306a36Sopenharmony_ci ilo_set_reset(hw); 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci for (i = 0; i < max_ccb; i++) { 66762306a36Sopenharmony_ci if (!hw->ccb_alloc[i]) 66862306a36Sopenharmony_ci continue; 66962306a36Sopenharmony_ci if (pending & (1 << i)) 67062306a36Sopenharmony_ci wake_up_interruptible(&hw->ccb_alloc[i]->ccb_waitq); 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* clear the device of the channels that have been handled */ 67462306a36Sopenharmony_ci clear_pending_db(hw, pending); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci spin_unlock(&hw->alloc_lock); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci return IRQ_HANDLED; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci pci_iounmap(pdev, hw->db_vaddr); 68462306a36Sopenharmony_ci pci_iounmap(pdev, hw->ram_vaddr); 68562306a36Sopenharmony_ci pci_iounmap(pdev, hw->mmio_vaddr); 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci int bar; 69162306a36Sopenharmony_ci unsigned long off; 69262306a36Sopenharmony_ci u8 pci_rev_id; 69362306a36Sopenharmony_ci int rc; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* map the memory mapped i/o registers */ 69662306a36Sopenharmony_ci hw->mmio_vaddr = pci_iomap(pdev, 1, 0); 69762306a36Sopenharmony_ci if (hw->mmio_vaddr == NULL) { 69862306a36Sopenharmony_ci dev_err(&pdev->dev, "Error mapping mmio\n"); 69962306a36Sopenharmony_ci goto out; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* map the adapter shared memory region */ 70362306a36Sopenharmony_ci rc = pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev_id); 70462306a36Sopenharmony_ci if (rc != 0) { 70562306a36Sopenharmony_ci dev_err(&pdev->dev, "Error reading PCI rev id: %d\n", rc); 70662306a36Sopenharmony_ci goto out; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (pci_rev_id >= PCI_REV_ID_NECHES) { 71062306a36Sopenharmony_ci bar = 5; 71162306a36Sopenharmony_ci /* Last 8k is reserved for CCBs */ 71262306a36Sopenharmony_ci off = pci_resource_len(pdev, bar) - 0x2000; 71362306a36Sopenharmony_ci } else { 71462306a36Sopenharmony_ci bar = 2; 71562306a36Sopenharmony_ci off = 0; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci hw->ram_vaddr = pci_iomap_range(pdev, bar, off, max_ccb * ILOHW_CCB_SZ); 71862306a36Sopenharmony_ci if (hw->ram_vaddr == NULL) { 71962306a36Sopenharmony_ci dev_err(&pdev->dev, "Error mapping shared mem\n"); 72062306a36Sopenharmony_ci goto mmio_free; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* map the doorbell aperture */ 72462306a36Sopenharmony_ci hw->db_vaddr = pci_iomap(pdev, 3, max_ccb * ONE_DB_SIZE); 72562306a36Sopenharmony_ci if (hw->db_vaddr == NULL) { 72662306a36Sopenharmony_ci dev_err(&pdev->dev, "Error mapping doorbell\n"); 72762306a36Sopenharmony_ci goto ram_free; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return 0; 73162306a36Sopenharmony_ciram_free: 73262306a36Sopenharmony_ci pci_iounmap(pdev, hw->ram_vaddr); 73362306a36Sopenharmony_cimmio_free: 73462306a36Sopenharmony_ci pci_iounmap(pdev, hw->mmio_vaddr); 73562306a36Sopenharmony_ciout: 73662306a36Sopenharmony_ci return -ENOMEM; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic void ilo_remove(struct pci_dev *pdev) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci int i, minor; 74262306a36Sopenharmony_ci struct ilo_hwinfo *ilo_hw = pci_get_drvdata(pdev); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (!ilo_hw) 74562306a36Sopenharmony_ci return; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci clear_device(ilo_hw); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci minor = MINOR(ilo_hw->cdev.dev); 75062306a36Sopenharmony_ci for (i = minor; i < minor + max_ccb; i++) 75162306a36Sopenharmony_ci device_destroy(&ilo_class, MKDEV(ilo_major, i)); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci cdev_del(&ilo_hw->cdev); 75462306a36Sopenharmony_ci ilo_disable_interrupts(ilo_hw); 75562306a36Sopenharmony_ci free_irq(pdev->irq, ilo_hw); 75662306a36Sopenharmony_ci ilo_unmap_device(pdev, ilo_hw); 75762306a36Sopenharmony_ci pci_release_regions(pdev); 75862306a36Sopenharmony_ci /* 75962306a36Sopenharmony_ci * pci_disable_device(pdev) used to be here. But this PCI device has 76062306a36Sopenharmony_ci * two functions with interrupt lines connected to a single pin. The 76162306a36Sopenharmony_ci * other one is a USB host controller. So when we disable the PIN here 76262306a36Sopenharmony_ci * e.g. by rmmod hpilo, the controller stops working. It is because 76362306a36Sopenharmony_ci * the interrupt link is disabled in ACPI since it is not refcounted 76462306a36Sopenharmony_ci * yet. See acpi_pci_link_free_irq called from acpi_pci_irq_disable. 76562306a36Sopenharmony_ci */ 76662306a36Sopenharmony_ci kfree(ilo_hw); 76762306a36Sopenharmony_ci ilo_hwdev[(minor / max_ccb)] = 0; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic int ilo_probe(struct pci_dev *pdev, 77162306a36Sopenharmony_ci const struct pci_device_id *ent) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci int devnum, minor, start, error = 0; 77462306a36Sopenharmony_ci struct ilo_hwinfo *ilo_hw; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (pci_match_id(ilo_blacklist, pdev)) { 77762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Not supported on this device\n"); 77862306a36Sopenharmony_ci return -ENODEV; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (max_ccb > MAX_CCB) 78262306a36Sopenharmony_ci max_ccb = MAX_CCB; 78362306a36Sopenharmony_ci else if (max_ccb < MIN_CCB) 78462306a36Sopenharmony_ci max_ccb = MIN_CCB; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* find a free range for device files */ 78762306a36Sopenharmony_ci for (devnum = 0; devnum < MAX_ILO_DEV; devnum++) { 78862306a36Sopenharmony_ci if (ilo_hwdev[devnum] == 0) { 78962306a36Sopenharmony_ci ilo_hwdev[devnum] = 1; 79062306a36Sopenharmony_ci break; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (devnum == MAX_ILO_DEV) { 79562306a36Sopenharmony_ci dev_err(&pdev->dev, "Error finding free device\n"); 79662306a36Sopenharmony_ci return -ENODEV; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* track global allocations for this device */ 80062306a36Sopenharmony_ci error = -ENOMEM; 80162306a36Sopenharmony_ci ilo_hw = kzalloc(sizeof(*ilo_hw), GFP_KERNEL); 80262306a36Sopenharmony_ci if (!ilo_hw) 80362306a36Sopenharmony_ci goto out; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci ilo_hw->ilo_dev = pdev; 80662306a36Sopenharmony_ci spin_lock_init(&ilo_hw->alloc_lock); 80762306a36Sopenharmony_ci spin_lock_init(&ilo_hw->fifo_lock); 80862306a36Sopenharmony_ci spin_lock_init(&ilo_hw->open_lock); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci error = pci_enable_device(pdev); 81162306a36Sopenharmony_ci if (error) 81262306a36Sopenharmony_ci goto free; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci pci_set_master(pdev); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci error = pci_request_regions(pdev, ILO_NAME); 81762306a36Sopenharmony_ci if (error) 81862306a36Sopenharmony_ci goto disable; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci error = ilo_map_device(pdev, ilo_hw); 82162306a36Sopenharmony_ci if (error) 82262306a36Sopenharmony_ci goto free_regions; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci pci_set_drvdata(pdev, ilo_hw); 82562306a36Sopenharmony_ci clear_device(ilo_hw); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci error = request_irq(pdev->irq, ilo_isr, IRQF_SHARED, "hpilo", ilo_hw); 82862306a36Sopenharmony_ci if (error) 82962306a36Sopenharmony_ci goto unmap; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci ilo_enable_interrupts(ilo_hw); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci cdev_init(&ilo_hw->cdev, &ilo_fops); 83462306a36Sopenharmony_ci ilo_hw->cdev.owner = THIS_MODULE; 83562306a36Sopenharmony_ci start = devnum * max_ccb; 83662306a36Sopenharmony_ci error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), max_ccb); 83762306a36Sopenharmony_ci if (error) { 83862306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not add cdev\n"); 83962306a36Sopenharmony_ci goto remove_isr; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci for (minor = 0 ; minor < max_ccb; minor++) { 84362306a36Sopenharmony_ci struct device *dev; 84462306a36Sopenharmony_ci dev = device_create(&ilo_class, &pdev->dev, 84562306a36Sopenharmony_ci MKDEV(ilo_major, minor), NULL, 84662306a36Sopenharmony_ci "hpilo!d%dccb%d", devnum, minor); 84762306a36Sopenharmony_ci if (IS_ERR(dev)) 84862306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not create files\n"); 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return 0; 85262306a36Sopenharmony_ciremove_isr: 85362306a36Sopenharmony_ci ilo_disable_interrupts(ilo_hw); 85462306a36Sopenharmony_ci free_irq(pdev->irq, ilo_hw); 85562306a36Sopenharmony_ciunmap: 85662306a36Sopenharmony_ci ilo_unmap_device(pdev, ilo_hw); 85762306a36Sopenharmony_cifree_regions: 85862306a36Sopenharmony_ci pci_release_regions(pdev); 85962306a36Sopenharmony_cidisable: 86062306a36Sopenharmony_ci/* pci_disable_device(pdev); see comment in ilo_remove */ 86162306a36Sopenharmony_cifree: 86262306a36Sopenharmony_ci kfree(ilo_hw); 86362306a36Sopenharmony_ciout: 86462306a36Sopenharmony_ci ilo_hwdev[devnum] = 0; 86562306a36Sopenharmony_ci return error; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic const struct pci_device_id ilo_devices[] = { 86962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB204) }, 87062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3307) }, 87162306a36Sopenharmony_ci { } 87262306a36Sopenharmony_ci}; 87362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ilo_devices); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic struct pci_driver ilo_driver = { 87662306a36Sopenharmony_ci .name = ILO_NAME, 87762306a36Sopenharmony_ci .id_table = ilo_devices, 87862306a36Sopenharmony_ci .probe = ilo_probe, 87962306a36Sopenharmony_ci .remove = ilo_remove, 88062306a36Sopenharmony_ci}; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic int __init ilo_init(void) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci int error; 88562306a36Sopenharmony_ci dev_t dev; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci error = class_register(&ilo_class); 88862306a36Sopenharmony_ci if (error) 88962306a36Sopenharmony_ci goto out; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci error = alloc_chrdev_region(&dev, 0, MAX_OPEN, ILO_NAME); 89262306a36Sopenharmony_ci if (error) 89362306a36Sopenharmony_ci goto class_destroy; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci ilo_major = MAJOR(dev); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci error = pci_register_driver(&ilo_driver); 89862306a36Sopenharmony_ci if (error) 89962306a36Sopenharmony_ci goto chr_remove; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci return 0; 90262306a36Sopenharmony_cichr_remove: 90362306a36Sopenharmony_ci unregister_chrdev_region(dev, MAX_OPEN); 90462306a36Sopenharmony_ciclass_destroy: 90562306a36Sopenharmony_ci class_unregister(&ilo_class); 90662306a36Sopenharmony_ciout: 90762306a36Sopenharmony_ci return error; 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic void __exit ilo_exit(void) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci pci_unregister_driver(&ilo_driver); 91362306a36Sopenharmony_ci unregister_chrdev_region(MKDEV(ilo_major, 0), MAX_OPEN); 91462306a36Sopenharmony_ci class_unregister(&ilo_class); 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ciMODULE_VERSION("1.5.0"); 91862306a36Sopenharmony_ciMODULE_ALIAS(ILO_NAME); 91962306a36Sopenharmony_ciMODULE_DESCRIPTION(ILO_NAME); 92062306a36Sopenharmony_ciMODULE_AUTHOR("David Altobelli <david.altobelli@hpe.com>"); 92162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_cimodule_param(max_ccb, uint, 0444); 92462306a36Sopenharmony_ciMODULE_PARM_DESC(max_ccb, "Maximum number of HP iLO channels to attach (8-24)(default=16)"); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cimodule_init(ilo_init); 92762306a36Sopenharmony_cimodule_exit(ilo_exit); 928