📄 mesh.c
字号:
return; case selecting: dlog(ms, "Selecting phase at command completion",0); ms->msgout[0] = IDENTIFY(ALLOW_RESEL(ms->conn_tgt), (cmd? cmd->device->lun: 0)); ms->n_msgout = 1; ms->expect_reply = 0; if (ms->aborting) { ms->msgout[0] = ABORT; ms->n_msgout++; } else if (tp->sdtr_state == do_sdtr) { /* add SDTR message */ add_sdtr_msg(ms); ms->expect_reply = 1; tp->sdtr_state = sdtr_sent; } 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 = 0; 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(int irq, void *dev_id, struct pt_regs *ptregs){ struct mesh_state *ms = (struct mesh_state *) dev_id; volatile struct mesh_regs *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 *mr = ms->mesh; volatile struct dbdma_regs *md = ms->dma; printk(KERN_DEBUG "mesh_host_reset\n"); /* 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); return SUCCESS;}static void set_mesh_power(struct mesh_state *ms, int state){ if (_machine != _MACH_Pmac) return; if (state) { pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 1); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/5); } else { pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 0); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/100); }} #ifdef CONFIG_PMstatic int mesh_suspend(struct macio_dev *mdev, u32 state){ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev); unsigned long flags; if (state == mdev->ofdev.dev.power_state || state < 2) 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); current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ/100); 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_state = state; 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_state == 0) 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_state = 0; 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 *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,};static int mesh_probe(struct macio_dev *mdev, const struct of_match *match){ struct device_node *mesh = macio_get_of_node(mdev); struct pci_dev* pdev = macio_get_pci_dev(mdev); int tgt, *cfp, minper; 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", mesh->n_addrs, mesh->n_intrs); 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 = (volatile struct mesh_regs *) 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 = (volatile struct dbdma_regs *) ioremap(macio_resource_start(mdev, 1), 0x1000); if (ms->dma == NULL) { printk(KERN_ERR "mesh: can't map registers\n"); iounmap((void *)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 = 0; } if ((cfp = (int *) 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); /* XXX FIXME: error should be fatal */ if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms)) printk(KERN_ERR "MESH: can't get irq %d\n", ms->meshintr); /* XXX FIXME: handle failure */ scsi_add_host(mesh_host, &mdev->ofdev.dev); scsi_scan_host(mesh_host); return 0;out_unmap: iounmap((void *)ms->dma); iounmap((void *)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((void *) ms->mesh); iounmap((void *) 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_match mesh_match[] = { { .name = "mesh", .type = OF_ANY_MATCH, .compatible = OF_ANY_MATCH }, { .name = OF_ANY_MATCH, .type = "scsi", .compatible = "chrp,mesh0" }, {},};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 + -