📄 mesh.c
字号:
} ms->msgphase = msg_out; /* * We need to wait for REQ before dropping ATN. * We wait for at most 30us, then fall back to * a scheme where we issue a SEQ_COMMAND with ATN, * which will give us a phase mismatch interrupt * when REQ does come, and then we send the message. */ t = 230; /* wait up to 230us */ while ((in_8(&mr->bus_status0) & BS0_REQ) == 0) { if (--t < 0) { dlog(ms, "impatient for req", ms->n_msgout); ms->msgphase = msg_none; break; } udelay(1); } break; case dataing: if (ms->dma_count != 0) { start_phase(ms); return; } /* * We can get a phase mismatch here if the target * changes to the status phase, even though we have * had a command complete interrupt. Then, if we * issue the SEQ_STATUS command, we'll get a sequence * error interrupt. Which isn't so bad except that * occasionally the mesh actually executes the * SEQ_STATUS *as well as* giving us the sequence * error and phase mismatch exception. */ out_8(&mr->sequence, 0); out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE); halt_dma(ms); break; case statusing: if (cmd) { cmd->SCp.Status = mr->fifo; if (DEBUG_TARGET(cmd)) printk(KERN_DEBUG "mesh: status is %x\n", cmd->SCp.Status); } ms->msgphase = msg_in; break; case busfreeing: mesh_done(ms, 1); return; case disconnecting: ms->current_req = NULL; ms->phase = idle; mesh_start(ms); return; default: break; } ++ms->phase; start_phase(ms); break; }}/* * Called by midlayer with host locked to queue a new * request */static int mesh_queue(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)){ struct mesh_state *ms; cmd->scsi_done = done; cmd->host_scribble = NULL; ms = (struct mesh_state *) cmd->device->host->hostdata; if (ms->request_q == NULL) ms->request_q = cmd; else ms->request_qtail->host_scribble = (void *) cmd; ms->request_qtail = cmd; if (ms->phase == idle) mesh_start(ms); return 0;}/* * Called to handle interrupts, either call by the interrupt * handler (do_mesh_interrupt) or by other functions in * exceptional circumstances */static void mesh_interrupt(struct mesh_state *ms){ volatile struct mesh_regs __iomem *mr = ms->mesh; int intr;#if 0 if (ALLOW_DEBUG(ms->conn_tgt)) printk(KERN_DEBUG "mesh_intr, bs0=%x int=%x exc=%x err=%x " "phase=%d msgphase=%d\n", mr->bus_status0, mr->interrupt, mr->exception, mr->error, ms->phase, ms->msgphase);#endif while ((intr = in_8(&mr->interrupt)) != 0) { dlog(ms, "interrupt intr/err/exc/seq=%.8x", MKWORD(intr, mr->error, mr->exception, mr->sequence)); if (intr & INT_ERROR) { handle_error(ms); } else if (intr & INT_EXCEPTION) { handle_exception(ms); } else if (intr & INT_CMDDONE) { out_8(&mr->interrupt, INT_CMDDONE); cmd_complete(ms); } }}/* Todo: here we can at least try to remove the command from the * queue if it isn't connected yet, and for pending command, assert * ATN until the bus gets freed. */static int mesh_abort(struct scsi_cmnd *cmd){ struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata; printk(KERN_DEBUG "mesh_abort(%p)\n", cmd); mesh_dump_regs(ms); dumplog(ms, cmd->device->id); dumpslog(ms); return FAILED;}/* * Called by the midlayer with the lock held to reset the * SCSI host and bus. * The midlayer will wait for devices to come back, we don't need * to do that ourselves */static int mesh_host_reset(struct scsi_cmnd *cmd){ struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata; volatile struct mesh_regs __iomem *mr = ms->mesh; volatile struct dbdma_regs __iomem *md = ms->dma; unsigned long flags; printk(KERN_DEBUG "mesh_host_reset\n"); spin_lock_irqsave(ms->host->host_lock, flags); /* Reset the controller & dbdma channel */ out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* stop dma */ out_8(&mr->exception, 0xff); /* clear all exception bits */ out_8(&mr->error, 0xff); /* clear all error bits */ out_8(&mr->sequence, SEQ_RESETMESH); mesh_flush_io(mr); udelay(1); out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE); out_8(&mr->source_id, ms->host->this_id); out_8(&mr->sel_timeout, 25); /* 250ms */ out_8(&mr->sync_params, ASYNC_PARAMS); /* Reset the bus */ out_8(&mr->bus_status1, BS1_RST); /* assert RST */ mesh_flush_io(mr); udelay(30); /* leave it on for >= 25us */ out_8(&mr->bus_status1, 0); /* negate RST */ /* Complete pending commands */ handle_reset(ms); spin_unlock_irqrestore(ms->host->host_lock, flags); return SUCCESS;}static void set_mesh_power(struct mesh_state *ms, int state){ if (!machine_is(powermac)) return; if (state) { pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 1); msleep(200); } else { pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 0); msleep(10); }}#ifdef CONFIG_PMstatic int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg){ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev); unsigned long flags; switch (mesg.event) { case PM_EVENT_SUSPEND: case PM_EVENT_FREEZE: break; default: return 0; } if (mesg.event == mdev->ofdev.dev.power.power_state.event) return 0; scsi_block_requests(ms->host); spin_lock_irqsave(ms->host->host_lock, flags); while(ms->phase != idle) { spin_unlock_irqrestore(ms->host->host_lock, flags); msleep(10); spin_lock_irqsave(ms->host->host_lock, flags); } ms->phase = sleeping; spin_unlock_irqrestore(ms->host->host_lock, flags); disable_irq(ms->meshintr); set_mesh_power(ms, 0); mdev->ofdev.dev.power.power_state = mesg; return 0;}static int mesh_resume(struct macio_dev *mdev){ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev); unsigned long flags; if (mdev->ofdev.dev.power.power_state.event == PM_EVENT_ON) return 0; set_mesh_power(ms, 1); mesh_init(ms); spin_lock_irqsave(ms->host->host_lock, flags); mesh_start(ms); spin_unlock_irqrestore(ms->host->host_lock, flags); enable_irq(ms->meshintr); scsi_unblock_requests(ms->host); mdev->ofdev.dev.power.power_state.event = PM_EVENT_ON; return 0;}#endif /* CONFIG_PM *//* * If we leave drives set for synchronous transfers (especially * CDROMs), and reboot to MacOS, it gets confused, poor thing. * So, on reboot we reset the SCSI bus. */static int mesh_shutdown(struct macio_dev *mdev){ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev); volatile struct mesh_regs __iomem *mr; unsigned long flags; printk(KERN_INFO "resetting MESH scsi bus(es)\n"); spin_lock_irqsave(ms->host->host_lock, flags); mr = ms->mesh; out_8(&mr->intr_mask, 0); out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE); out_8(&mr->bus_status1, BS1_RST); mesh_flush_io(mr); udelay(30); out_8(&mr->bus_status1, 0); spin_unlock_irqrestore(ms->host->host_lock, flags); return 0;}static struct scsi_host_template mesh_template = { .proc_name = "mesh", .name = "MESH", .queuecommand = mesh_queue, .eh_abort_handler = mesh_abort, .eh_host_reset_handler = mesh_host_reset, .can_queue = 20, .this_id = 7, .sg_tablesize = SG_ALL, .cmd_per_lun = 2, .use_clustering = DISABLE_CLUSTERING, .use_sg_chaining = ENABLE_SG_CHAINING,};static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match){ struct device_node *mesh = macio_get_of_node(mdev); struct pci_dev* pdev = macio_get_pci_dev(mdev); int tgt, minper; const int *cfp; struct mesh_state *ms; struct Scsi_Host *mesh_host; void *dma_cmd_space; dma_addr_t dma_cmd_bus; switch (mdev->bus->chip->type) { case macio_heathrow: case macio_gatwick: case macio_paddington: use_active_neg = 0; break; default: use_active_neg = SEQ_ACTIVE_NEG; } if (macio_resource_count(mdev) != 2 || macio_irq_count(mdev) != 2) { printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs" " (got %d,%d)\n", macio_resource_count(mdev), macio_irq_count(mdev)); return -ENODEV; } if (macio_request_resources(mdev, "mesh") != 0) { printk(KERN_ERR "mesh: unable to request memory resources"); return -EBUSY; } mesh_host = scsi_host_alloc(&mesh_template, sizeof(struct mesh_state)); if (mesh_host == NULL) { printk(KERN_ERR "mesh: couldn't register host"); goto out_release; } /* Old junk for root discovery, that will die ultimately */#if !defined(MODULE) note_scsi_host(mesh, mesh_host);#endif mesh_host->base = macio_resource_start(mdev, 0); mesh_host->irq = macio_irq(mdev, 0); ms = (struct mesh_state *) mesh_host->hostdata; macio_set_drvdata(mdev, ms); ms->host = mesh_host; ms->mdev = mdev; ms->pdev = pdev; ms->mesh = ioremap(macio_resource_start(mdev, 0), 0x1000); if (ms->mesh == NULL) { printk(KERN_ERR "mesh: can't map registers\n"); goto out_free; } ms->dma = ioremap(macio_resource_start(mdev, 1), 0x1000); if (ms->dma == NULL) { printk(KERN_ERR "mesh: can't map registers\n"); iounmap(ms->mesh); goto out_free; } ms->meshintr = macio_irq(mdev, 0); ms->dmaintr = macio_irq(mdev, 1); /* Space for dma command list: +1 for stop command, * +1 to allow for aligning. */ ms->dma_cmd_size = (mesh_host->sg_tablesize + 2) * sizeof(struct dbdma_cmd); /* We use the PCI APIs for now until the generic one gets fixed * enough or until we get some macio-specific versions */ dma_cmd_space = pci_alloc_consistent(macio_get_pci_dev(mdev), ms->dma_cmd_size, &dma_cmd_bus); if (dma_cmd_space == NULL) { printk(KERN_ERR "mesh: can't allocate DMA table\n"); goto out_unmap; } memset(dma_cmd_space, 0, ms->dma_cmd_size); ms->dma_cmds = (struct dbdma_cmd *) DBDMA_ALIGN(dma_cmd_space); ms->dma_cmd_space = dma_cmd_space; ms->dma_cmd_bus = dma_cmd_bus + ((unsigned long)ms->dma_cmds) - (unsigned long)dma_cmd_space; ms->current_req = NULL; for (tgt = 0; tgt < 8; ++tgt) { ms->tgts[tgt].sdtr_state = do_sdtr; ms->tgts[tgt].sync_params = ASYNC_PARAMS; ms->tgts[tgt].current_req = NULL; } if ((cfp = of_get_property(mesh, "clock-frequency", NULL))) ms->clk_freq = *cfp; else { printk(KERN_INFO "mesh: assuming 50MHz clock frequency\n"); ms->clk_freq = 50000000; } /* The maximum sync rate is clock / 5; increase * mesh_sync_period if necessary. */ minper = 1000000000 / (ms->clk_freq / 5); /* ns */ if (mesh_sync_period < minper) mesh_sync_period = minper; /* Power up the chip */ set_mesh_power(ms, 1); /* Set it up */ mesh_init(ms); /* Request interrupt */ if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms)) { printk(KERN_ERR "MESH: can't get irq %d\n", ms->meshintr); goto out_shutdown; } /* Add scsi host & scan */ if (scsi_add_host(mesh_host, &mdev->ofdev.dev)) goto out_release_irq; scsi_scan_host(mesh_host); return 0; out_release_irq: free_irq(ms->meshintr, ms); out_shutdown: /* shutdown & reset bus in case of error or macos can be confused * at reboot if the bus was set to synchronous mode already */ mesh_shutdown(mdev); set_mesh_power(ms, 0); pci_free_consistent(macio_get_pci_dev(mdev), ms->dma_cmd_size, ms->dma_cmd_space, ms->dma_cmd_bus); out_unmap: iounmap(ms->dma); iounmap(ms->mesh); out_free: scsi_host_put(mesh_host); out_release: macio_release_resources(mdev); return -ENODEV;}static int mesh_remove(struct macio_dev *mdev){ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev); struct Scsi_Host *mesh_host = ms->host; scsi_remove_host(mesh_host); free_irq(ms->meshintr, ms); /* Reset scsi bus */ mesh_shutdown(mdev); /* Shut down chip & termination */ set_mesh_power(ms, 0); /* Unmap registers & dma controller */ iounmap(ms->mesh); iounmap(ms->dma); /* Free DMA commands memory */ pci_free_consistent(macio_get_pci_dev(mdev), ms->dma_cmd_size, ms->dma_cmd_space, ms->dma_cmd_bus); /* Release memory resources */ macio_release_resources(mdev); scsi_host_put(mesh_host); return 0;}static struct of_device_id mesh_match[] = { { .name = "mesh", }, { .type = "scsi", .compatible = "chrp,mesh0" }, {},};MODULE_DEVICE_TABLE (of, mesh_match);static struct macio_driver mesh_driver = { .name = "mesh", .match_table = mesh_match, .probe = mesh_probe, .remove = mesh_remove, .shutdown = mesh_shutdown,#ifdef CONFIG_PM .suspend = mesh_suspend, .resume = mesh_resume,#endif};static int __init init_mesh(void){ /* Calculate sync rate from module parameters */ if (sync_rate > 10) sync_rate = 10; if (sync_rate > 0) { printk(KERN_INFO "mesh: configured for synchronous %d MB/s\n", sync_rate); mesh_sync_period = 1000 / sync_rate; /* ns */ mesh_sync_offset = 15; } else printk(KERN_INFO "mesh: configured for asynchronous\n"); return macio_register_driver(&mesh_driver);}static void __exit exit_mesh(void){ return macio_unregister_driver(&mesh_driver);}module_init(init_mesh);module_exit(exit_mesh);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -