📄 sx8.c
字号:
/* response to a message we sent */ else if ((status & (1 << 31)) == 0) { VPRINTK("handling msg response on index %u\n", idx); carm_handle_resp(host, resp[idx].ret_handle, status); resp[idx].status = cpu_to_le32(0xffffffff); } /* asynchronous events the hardware throws our way */ else if ((status & 0xff000000) == (1 << 31)) { u8 *evt_type_ptr = (u8 *) &resp[idx]; u8 evt_type = *evt_type_ptr; printk(KERN_WARNING DRV_NAME "(%s): unhandled event type %d\n", pci_name(host->pdev), (int) evt_type); resp[idx].status = cpu_to_le32(0xffffffff); } idx = NEXT_RESP(idx); work++; } VPRINTK("EXIT, work==%u\n", work); host->resp_idx += work;}static irqreturn_t carm_interrupt(int irq, void *__host){ struct carm_host *host = __host; void __iomem *mmio; u32 mask; int handled = 0; unsigned long flags; if (!host) { VPRINTK("no host\n"); return IRQ_NONE; } spin_lock_irqsave(&host->lock, flags); mmio = host->mmio; /* reading should also clear interrupts */ mask = readl(mmio + CARM_INT_STAT); if (mask == 0 || mask == 0xffffffff) { VPRINTK("no work, mask == 0x%x\n", mask); goto out; } if (mask & INT_ACK_MASK) writel(mask, mmio + CARM_INT_STAT); if (unlikely(host->state == HST_INVALID)) { VPRINTK("not initialized yet, mask = 0x%x\n", mask); goto out; } if (mask & CARM_HAVE_RESP) { handled = 1; carm_handle_responses(host); }out: spin_unlock_irqrestore(&host->lock, flags); VPRINTK("EXIT\n"); return IRQ_RETVAL(handled);}static void carm_fsm_task (struct work_struct *work){ struct carm_host *host = container_of(work, struct carm_host, fsm_task); unsigned long flags; unsigned int state; int rc, i, next_dev; int reschedule = 0; int new_state = HST_INVALID; spin_lock_irqsave(&host->lock, flags); state = host->state; spin_unlock_irqrestore(&host->lock, flags); DPRINTK("ENTER, state == %s\n", state_name[state]); switch (state) { case HST_PROBE_START: new_state = HST_ALLOC_BUF; reschedule = 1; break; case HST_ALLOC_BUF: rc = carm_send_special(host, carm_fill_alloc_buf); if (rc) { new_state = HST_ERROR; reschedule = 1; } break; case HST_SYNC_TIME: rc = carm_send_special(host, carm_fill_sync_time); if (rc) { new_state = HST_ERROR; reschedule = 1; } break; case HST_GET_FW_VER: rc = carm_send_special(host, carm_fill_get_fw_ver); if (rc) { new_state = HST_ERROR; reschedule = 1; } break; case HST_PORT_SCAN: rc = carm_send_special(host, carm_fill_scan_channels); if (rc) { new_state = HST_ERROR; reschedule = 1; } break; case HST_DEV_SCAN_START: host->cur_scan_dev = -1; new_state = HST_DEV_SCAN; reschedule = 1; break; case HST_DEV_SCAN: next_dev = -1; for (i = host->cur_scan_dev + 1; i < CARM_MAX_PORTS; i++) if (host->dev_present & (1 << i)) { next_dev = i; break; } if (next_dev >= 0) { host->cur_scan_dev = next_dev; rc = carm_array_info(host, next_dev); if (rc) { new_state = HST_ERROR; reschedule = 1; } } else { new_state = HST_DEV_ACTIVATE; reschedule = 1; } break; case HST_DEV_ACTIVATE: { int activated = 0; for (i = 0; i < CARM_MAX_PORTS; i++) if (host->dev_active & (1 << i)) { struct carm_port *port = &host->port[i]; struct gendisk *disk = port->disk; set_capacity(disk, port->capacity); add_disk(disk); activated++; } printk(KERN_INFO DRV_NAME "(%s): %d ports activated\n", pci_name(host->pdev), activated); new_state = HST_PROBE_FINISHED; reschedule = 1; break; } case HST_PROBE_FINISHED: complete(&host->probe_comp); break; case HST_ERROR: /* FIXME: TODO */ break; default: /* should never occur */ printk(KERN_ERR PFX "BUG: unknown state %d\n", state); assert(0); break; } if (new_state != HST_INVALID) { spin_lock_irqsave(&host->lock, flags); host->state = new_state; spin_unlock_irqrestore(&host->lock, flags); } if (reschedule) schedule_work(&host->fsm_task);}static int carm_init_wait(void __iomem *mmio, u32 bits, unsigned int test_bit){ unsigned int i; for (i = 0; i < 50000; i++) { u32 tmp = readl(mmio + CARM_LMUC); udelay(100); if (test_bit) { if ((tmp & bits) == bits) return 0; } else { if ((tmp & bits) == 0) return 0; } cond_resched(); } printk(KERN_ERR PFX "carm_init_wait timeout, bits == 0x%x, test_bit == %s\n", bits, test_bit ? "yes" : "no"); return -EBUSY;}static void carm_init_responses(struct carm_host *host){ void __iomem *mmio = host->mmio; unsigned int i; struct carm_response *resp = (struct carm_response *) host->shm; for (i = 0; i < RMSG_Q_LEN; i++) resp[i].status = cpu_to_le32(0xffffffff); writel(0, mmio + CARM_RESP_IDX);}static int carm_init_host(struct carm_host *host){ void __iomem *mmio = host->mmio; u32 tmp; u8 tmp8; int rc; DPRINTK("ENTER\n"); writel(0, mmio + CARM_INT_MASK); tmp8 = readb(mmio + CARM_INITC); if (tmp8 & 0x01) { tmp8 &= ~0x01; writeb(tmp8, mmio + CARM_INITC); readb(mmio + CARM_INITC); /* flush */ DPRINTK("snooze...\n"); msleep(5000); } tmp = readl(mmio + CARM_HMUC); if (tmp & CARM_CME) { DPRINTK("CME bit present, waiting\n"); rc = carm_init_wait(mmio, CARM_CME, 1); if (rc) { DPRINTK("EXIT, carm_init_wait 1 failed\n"); return rc; } } if (tmp & CARM_RME) { DPRINTK("RME bit present, waiting\n"); rc = carm_init_wait(mmio, CARM_RME, 1); if (rc) { DPRINTK("EXIT, carm_init_wait 2 failed\n"); return rc; } } tmp &= ~(CARM_RME | CARM_CME); writel(tmp, mmio + CARM_HMUC); readl(mmio + CARM_HMUC); /* flush */ rc = carm_init_wait(mmio, CARM_RME | CARM_CME, 0); if (rc) { DPRINTK("EXIT, carm_init_wait 3 failed\n"); return rc; } carm_init_buckets(mmio); writel(host->shm_dma & 0xffffffff, mmio + RBUF_ADDR_LO); writel((host->shm_dma >> 16) >> 16, mmio + RBUF_ADDR_HI); writel(RBUF_LEN, mmio + RBUF_BYTE_SZ); tmp = readl(mmio + CARM_HMUC); tmp |= (CARM_RME | CARM_CME | CARM_WZBC); writel(tmp, mmio + CARM_HMUC); readl(mmio + CARM_HMUC); /* flush */ rc = carm_init_wait(mmio, CARM_RME | CARM_CME, 1); if (rc) { DPRINTK("EXIT, carm_init_wait 4 failed\n"); return rc; } writel(0, mmio + CARM_HMPHA); writel(INT_DEF_MASK, mmio + CARM_INT_MASK); carm_init_responses(host); /* start initialization, probing state machine */ spin_lock_irq(&host->lock); assert(host->state == HST_INVALID); host->state = HST_PROBE_START; spin_unlock_irq(&host->lock); schedule_work(&host->fsm_task); DPRINTK("EXIT\n"); return 0;}static int carm_init_disks(struct carm_host *host){ unsigned int i; int rc = 0; for (i = 0; i < CARM_MAX_PORTS; i++) { struct gendisk *disk; struct request_queue *q; struct carm_port *port; port = &host->port[i]; port->host = host; port->port_no = i; disk = alloc_disk(CARM_MINORS_PER_MAJOR); if (!disk) { rc = -ENOMEM; break; } port->disk = disk; sprintf(disk->disk_name, DRV_NAME "/%u", (unsigned int) (host->id * CARM_MAX_PORTS) + i); disk->major = host->major; disk->first_minor = i * CARM_MINORS_PER_MAJOR; disk->fops = &carm_bd_ops; disk->private_data = port; q = blk_init_queue(carm_rq_fn, &host->lock); if (!q) { rc = -ENOMEM; break; } disk->queue = q; blk_queue_max_hw_segments(q, CARM_MAX_REQ_SG); blk_queue_max_phys_segments(q, CARM_MAX_REQ_SG); blk_queue_segment_boundary(q, CARM_SG_BOUNDARY); q->queuedata = port; } return rc;}static void carm_free_disks(struct carm_host *host){ unsigned int i; for (i = 0; i < CARM_MAX_PORTS; i++) { struct gendisk *disk = host->port[i].disk; if (disk) { struct request_queue *q = disk->queue; if (disk->flags & GENHD_FL_UP) del_gendisk(disk); if (q) blk_cleanup_queue(q); put_disk(disk); } }}static int carm_init_shm(struct carm_host *host){ host->shm = pci_alloc_consistent(host->pdev, CARM_SHM_SIZE, &host->shm_dma); if (!host->shm) return -ENOMEM; host->msg_base = host->shm + RBUF_LEN; host->msg_dma = host->shm_dma + RBUF_LEN; memset(host->shm, 0xff, RBUF_LEN); memset(host->msg_base, 0, PDC_SHM_SIZE - RBUF_LEN); return 0;}static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent){ static unsigned int printed_version; struct carm_host *host; unsigned int pci_dac; int rc; struct request_queue *q; unsigned int i; if (!printed_version++) printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); rc = pci_enable_device(pdev); if (rc) return rc; rc = pci_request_regions(pdev, DRV_NAME); if (rc) goto err_out;#ifdef IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */ rc = pci_set_dma_mask(pdev, DMA_64BIT_MASK); if (!rc) { rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); if (rc) { printk(KERN_ERR DRV_NAME "(%s): consistent DMA mask failure\n", pci_name(pdev)); goto err_out_regions; } pci_dac = 1; } else {#endif rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { printk(KERN_ERR DRV_NAME "(%s): DMA mask failure\n", pci_name(pdev)); goto err_out_regions; } pci_dac = 0;#ifdef IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */ }#endif host = kzalloc(sizeof(*host), GFP_KERNEL); if (!host) { printk(KERN_ERR DRV_NAME "(%s): memory alloc failure\n", pci_name(pdev)); rc = -ENOMEM; goto err_out_regions; } host->pdev = pdev; host->flags = pci_dac ? FL_DAC : 0; spin_lock_init(&host->lock); INIT_WORK(&host->fsm_task, carm_fsm_task); init_completion(&host->probe_comp); for (i = 0; i < ARRAY_SIZE(host->req); i++) host->req[i].tag = i; host->mmio = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); if (!host->mmio) { printk(KERN_ERR DRV_NAME "(%s): MMIO alloc failure\n", pci_name(pdev)); rc = -ENOMEM; goto err_out_kfree; } rc = carm_init_shm(host); if (rc) { printk(KERN_ERR DRV_NAME "(%s): DMA SHM alloc failure\n", pci_name(pdev)); goto err_out_iounmap; } q = blk_init_queue(carm_oob_rq_fn, &host->lock); if (!q) { printk(KERN_ERR DRV_NAME "(%s): OOB queue alloc failure\n", pci_name(pdev)); rc = -ENOMEM; goto err_out_pci_free; } host->oob_q = q; q->queuedata = host; /* * Figure out which major to use: 160, 161, or dynamic */ if (!test_and_set_bit(0, &carm_major_alloc)) host->major = 160; else if (!test_and_set_bit(1, &carm_major_alloc)) host->major = 161; else host->flags |= FL_DYN_MAJOR; host->id = carm_host_id; sprintf(host->name, DRV_NAME "%d", carm_host_id); rc = register_blkdev(host->major, host->name); if (rc < 0) goto err_out_free_majors; if (host->flags & FL_DYN_MAJOR) host->major = rc; rc = carm_init_disks(host); if (rc) goto err_out_blkdev_disks; pci_set_master(pdev); rc = request_irq(pdev->irq, carm_interrupt, IRQF_SHARED, DRV_NAME, host); if (rc) { printk(KERN_ERR DRV_NAME "(%s): irq alloc failure\n", pci_name(pdev)); goto err_out_blkdev_disks; } rc = carm_init_host(host); if (rc) goto err_out_free_irq; DPRINTK("waiting for probe_comp\n"); wait_for_completion(&host->probe_comp); printk(KERN_INFO "%s: pci %s, ports %d, io %llx, irq %u, major %d\n", host->name, pci_name(pdev), (int) CARM_MAX_PORTS, (unsigned long long)pci_resource_start(pdev, 0), pdev->irq, host->major); carm_host_id++; pci_set_drvdata(pdev, host); return 0;err_out_free_irq: free_irq(pdev->irq, host);err_out_blkdev_disks: carm_free_disks(host); unregister_blkdev(host->major, host->name);err_out_free_majors: if (host->major == 160) clear_bit(0, &carm_major_alloc); else if (host->major == 161) clear_bit(1, &carm_major_alloc); blk_cleanup_queue(host->oob_q);err_out_pci_free: pci_free_consistent(pdev, CARM_SHM_SIZE, host->shm, host->shm_dma);err_out_iounmap: iounmap(host->mmio);err_out_kfree: kfree(host);err_out_regions: pci_release_regions(pdev);err_out: pci_disable_device(pdev); return rc;}static void carm_remove_one (struct pci_dev *pdev){ struct carm_host *host = pci_get_drvdata(pdev); if (!host) { printk(KERN_ERR PFX "BUG: no host data for PCI(%s)\n", pci_name(pdev)); return; } free_irq(pdev->irq, host); carm_free_disks(host); unregister_blkdev(host->major, host->name); if (host->major == 160) clear_bit(0, &carm_major_alloc); else if (host->major == 161) clear_bit(1, &carm_major_alloc); blk_cleanup_queue(host->oob_q); pci_free_consistent(pdev, CARM_SHM_SIZE, host->shm, host->shm_dma); iounmap(host->mmio); kfree(host); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL);}static int __init carm_init(void){ return pci_register_driver(&carm_driver);}static void __exit carm_exit(void){ pci_unregister_driver(&carm_driver);}module_init(carm_init);module_exit(carm_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -