⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mesh.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * SCSI low-level driver for the MESH (Macintosh Enhanced SCSI Hardware) * bus adaptor found on Power Macintosh computers. * We assume the MESH is connected to a DBDMA (descriptor-based DMA) * controller. * * Paul Mackerras, August 1996. * Copyright (C) 1996 Paul Mackerras. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/types.h>#include <linux/string.h>#include <linux/malloc.h>#include <linux/blk.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/tqueue.h>#include <linux/interrupt.h>#include <linux/reboot.h>#include <linux/spinlock.h>#include <asm/dbdma.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/prom.h>#include <asm/system.h>#include <asm/irq.h>#include <asm/hydra.h>#include <asm/processor.h>#include <asm/feature.h>#include "scsi.h"#include "hosts.h"#include "mesh.h"/* * To do: * - handle aborts correctly * - retry arbitration if lost (unless higher levels do this for us) */#define MESH_NEW_STYLE_EH#if 1#undef KERN_DEBUG#define KERN_DEBUG KERN_WARNING#endif#if CONFIG_SCSI_MESH_SYNC_RATE == 0int mesh_sync_period = 100;int mesh_sync_offset = 0;#elseint mesh_sync_period = 1000 / CONFIG_SCSI_MESH_SYNC_RATE;	/* ns */int mesh_sync_offset = 15;#endifint mesh_sync_targets = 0xff;	/* targets to set synchronous (bitmap) */int mesh_resel_targets = 0xff;	/* targets that we let disconnect (bitmap) */int mesh_debug_targets = 0;	/* print debug for these targets */unsigned char use_active_neg = 0;  /* bit mask for SEQ_ACTIVE_NEG if used */#define ALLOW_SYNC(tgt)		((mesh_sync_targets >> (tgt)) & 1)#define ALLOW_RESEL(tgt)	((mesh_resel_targets >> (tgt)) & 1)#define ALLOW_DEBUG(tgt)	((mesh_debug_targets >> (tgt)) & 1)#define DEBUG_TARGET(cmd)	((cmd) && ALLOW_DEBUG((cmd)->target))#undef MESH_DBG#define N_DBG_LOG	50#define N_DBG_SLOG	20#define NUM_DBG_EVENTS	13#undef	DBG_USE_TB		/* bombs on 601 */struct dbglog {	char	*fmt;	u32	tb;	u8	phase;	u8	bs0;	u8	bs1;	u8	tgt;	int	d;};enum mesh_phase {	idle,	arbitrating,	selecting,	commanding,	dataing,	statusing,	busfreeing,	disconnecting,	reselecting};enum msg_phase {	msg_none,	msg_out,	msg_out_xxx,	msg_out_last,	msg_in,	msg_in_bad,};enum sdtr_phase {	do_sdtr,	sdtr_sent,	sdtr_done};struct mesh_target {	enum sdtr_phase sdtr_state;	int	sync_params;	int	data_goes_out;		/* guess as to data direction */	Scsi_Cmnd *current_req;	u32	saved_ptr;	int	want_abort;#ifdef MESH_DBG	int	log_ix;	int	n_log;	struct dbglog log[N_DBG_LOG];#endif};struct mesh_state {	volatile struct	mesh_regs *mesh;	int	meshintr;	volatile struct	dbdma_regs *dma;	int	dmaintr;	struct	Scsi_Host *host;	struct	mesh_state *next;	Scsi_Cmnd *request_q;	Scsi_Cmnd *request_qtail;	enum mesh_phase phase;		/* what we're currently trying to do */	enum msg_phase msgphase;	int	conn_tgt;		/* target we're connected to */	Scsi_Cmnd *current_req;		/* req we're currently working on */	int	data_ptr;	int	dma_started;	int	dma_count;	int	stat;	int	aborting;	int	expect_reply;	int	n_msgin;	u8	msgin[16];	int	n_msgout;	int	last_n_msgout;	u8	msgout[16];	struct dbdma_cmd *dma_cmds;	/* space for dbdma commands, aligned */	int	clk_freq;	struct mesh_target tgts[8];	void	*dma_cmd_space;	struct device_node *ofnode;#ifndef MESH_NEW_STYLE_EH	Scsi_Cmnd *completed_q;	Scsi_Cmnd *completed_qtail;	struct tq_struct tqueue;#endif#ifdef MESH_DBG	int	log_ix;	int	n_log;	struct dbglog log[N_DBG_SLOG];#endif};#ifdef MESH_DBGstatic void dlog(struct mesh_state *ms, char *fmt, int a);static void dumplog(struct mesh_state *ms, int tgt);static void dumpslog(struct mesh_state *ms);#elsestatic inline void dlog(struct mesh_state *ms, char *fmt, int a){}static inline void dumplog(struct mesh_state *ms, int tgt){}static inline void dumpslog(struct mesh_state *ms){}#endif /* MESH_DBG */#define MKWORD(a, b, c, d)	(((a) << 24) + ((b) << 16) + ((c) << 8) + (d))static struct mesh_state *all_meshes;static void mesh_init(struct mesh_state *);static int mesh_notify_reboot(struct notifier_block *, unsigned long, void *);static void mesh_dump_regs(struct mesh_state *);static void mesh_start(struct mesh_state *);static void mesh_start_cmd(struct mesh_state *, Scsi_Cmnd *);#ifndef MESH_NEW_STYLE_EHstatic void finish_cmds(void *);#endifstatic void add_sdtr_msg(struct mesh_state *);static void set_sdtr(struct mesh_state *, int, int);static void start_phase(struct mesh_state *);static void get_msgin(struct mesh_state *);static int msgin_length(struct mesh_state *);static void cmd_complete(struct mesh_state *);static void phase_mismatch(struct mesh_state *);static void reselected(struct mesh_state *);static void handle_reset(struct mesh_state *);static void handle_error(struct mesh_state *);static void handle_exception(struct mesh_state *);static void mesh_interrupt(int, void *, struct pt_regs *);static void do_mesh_interrupt(int, void *, struct pt_regs *);static void handle_msgin(struct mesh_state *);static void mesh_done(struct mesh_state *, int);static void mesh_completed(struct mesh_state *, Scsi_Cmnd *);static void set_dma_cmds(struct mesh_state *, Scsi_Cmnd *);static void halt_dma(struct mesh_state *);static int data_goes_out(Scsi_Cmnd *);static void do_abort(struct mesh_state *ms);static struct notifier_block mesh_notifier = {	mesh_notify_reboot,	NULL,	0};intmesh_detect(Scsi_Host_Template *tp){	struct device_node *mesh;	int nmeshes, tgt, *cfp, minper;	struct mesh_state *ms, **prev_statep;	struct Scsi_Host *mesh_host;	void *dma_cmd_space;	if (_machine == _MACH_Pmac) {	    use_active_neg = (find_devices("mac-io") ? 0 : SEQ_ACTIVE_NEG);	} else {	    /* CHRP mac-io */	    use_active_neg = SEQ_ACTIVE_NEG;	}	nmeshes = 0;	prev_statep = &all_meshes;	/*	 * On powermacs, the MESH node has device_type "mesh".	 * On chrp machines, its device_type is "scsi" with	 * "chrp,mesh0" as its `compatible' property.	 */	mesh = find_devices("mesh");	if (mesh == 0)		mesh = find_compatible_devices("scsi", "chrp,mesh0");	for (; mesh != 0; mesh = mesh->next) {		if (mesh->n_addrs != 2 || mesh->n_intrs != 2) {			printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs"			       " (got %d,%d)", mesh->n_addrs, mesh->n_intrs);			continue;		}		mesh_host = scsi_register(tp, sizeof(struct mesh_state));		if (mesh_host == 0) {			printk(KERN_ERR "mesh: couldn't register host");			continue;		}		mesh_host->unique_id = nmeshes;#if !defined(MODULE)		note_scsi_host(mesh, mesh_host);#endif		ms = (struct mesh_state *) mesh_host->hostdata;		if (ms == 0)			panic("no mesh state");		memset(ms, 0, sizeof(*ms));		ms->host = mesh_host;		ms->ofnode = mesh;		ms->mesh = (volatile struct mesh_regs *)			ioremap(mesh->addrs[0].address, 0x1000);		ms->dma = (volatile struct dbdma_regs *)			ioremap(mesh->addrs[1].address, 0x1000);		ms->meshintr = mesh->intrs[0].line;		ms->dmaintr = mesh->intrs[1].line;		/* Space for dma command list: +1 for stop command,		   +1 to allow for aligning. */		dma_cmd_space = kmalloc((mesh_host->sg_tablesize + 2) *					sizeof(struct dbdma_cmd), GFP_KERNEL);		if (dma_cmd_space == 0)			panic("mesh: couldn't allocate dma command space");		ms->dma_cmds = (struct dbdma_cmd *) DBDMA_ALIGN(dma_cmd_space);		memset(ms->dma_cmds, 0, (mesh_host->sg_tablesize + 1)		       * sizeof(struct dbdma_cmd));		ms->dma_cmd_space = dma_cmd_space;		ms->current_req = 0;		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;		}#ifndef MESH_NEW_STYLE_EH		ms->tqueue.routine = finish_cmds;		ms->tqueue.data = ms;#endif		*prev_statep = ms;		prev_statep = &ms->next;		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;		feature_set(mesh, FEATURE_MESH_enable);		mdelay(200);		mesh_init(ms);		if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms)) {			printk(KERN_ERR "MESH: can't get irq %d\n", ms->meshintr);		}		++nmeshes;	}	if ((_machine == _MACH_Pmac) && (nmeshes > 0))		register_reboot_notifier(&mesh_notifier);	return nmeshes;}intmesh_release(struct Scsi_Host *host){	struct mesh_state *ms = (struct mesh_state *) host->hostdata;	if (ms == 0)		return 0;	if (ms->mesh)		iounmap((void *) ms->mesh);	if (ms->dma)		iounmap((void *) ms->dma);	kfree(ms->dma_cmd_space);	free_irq(ms->meshintr, ms);	feature_clear(ms->ofnode, FEATURE_MESH_enable);	return 0;}intmesh_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)){	unsigned long flags;	struct mesh_state *ms;	cmd->scsi_done = done;	cmd->host_scribble = NULL;	ms = (struct mesh_state *) cmd->host->hostdata;	save_flags(flags);	cli();	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);	restore_flags(flags);	return 0;}intmesh_abort(Scsi_Cmnd *cmd){	struct mesh_state *ms = (struct mesh_state *) cmd->host->hostdata;	printk(KERN_DEBUG "mesh_abort(%p)\n", cmd);	mesh_dump_regs(ms);	dumplog(ms, cmd->target);	dumpslog(ms);	return SCSI_ABORT_SNOOZE;}static voidmesh_dump_regs(struct mesh_state *ms){	volatile struct mesh_regs *mr = ms->mesh;	volatile struct dbdma_regs *md = ms->dma;	int t;	struct mesh_target *tp;	printk(KERN_DEBUG "mesh: state at %p, regs at %p, dma at %p\n",	       ms, mr, md);	printk(KERN_DEBUG "    ct=%4x seq=%2x bs=%4x fc=%2x "	       "exc=%2x err=%2x im=%2x int=%2x sp=%2x\n",	       (mr->count_hi << 8) + mr->count_lo, mr->sequence,	       (mr->bus_status1 << 8) + mr->bus_status0, mr->fifo_count,	       mr->exception, mr->error, mr->intr_mask, mr->interrupt,	       mr->sync_params);	while(in_8(&mr->fifo_count))		printk(KERN_DEBUG " fifo data=%.2x\n",in_8(&mr->fifo));	printk(KERN_DEBUG "    dma stat=%x cmdptr=%x\n",	       in_le32(&md->status), in_le32(&md->cmdptr));	printk(KERN_DEBUG "    phase=%d msgphase=%d conn_tgt=%d data_ptr=%d\n",	       ms->phase, ms->msgphase, ms->conn_tgt, ms->data_ptr);	printk(KERN_DEBUG "    dma_st=%d dma_ct=%d n_msgout=%d\n",	       ms->dma_started, ms->dma_count, ms->n_msgout);	for (t = 0; t < 8; ++t) {		tp = &ms->tgts[t];		if (tp->current_req == NULL)			continue;		printk(KERN_DEBUG "    target %d: req=%p goes_out=%d saved_ptr=%d\n",		       t, tp->current_req, tp->data_goes_out, tp->saved_ptr);	}}intmesh_reset(Scsi_Cmnd *cmd, unsigned how){	struct mesh_state *ms = (struct mesh_state *) cmd->host->hostdata;	volatile struct mesh_regs *mr = ms->mesh;	volatile struct dbdma_regs *md = ms->dma;	unsigned long flags;	int ret;	printk(KERN_DEBUG "mesh_reset %x\n", how);	ret = SCSI_RESET_BUS_RESET;	save_flags(flags);	cli();	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 */	if (how & SCSI_RESET_SUGGEST_HOST_RESET) {		out_8(&mr->sequence, SEQ_RESETMESH);		ret |= SCSI_RESET_HOST_RESET;		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);	}	out_8(&mr->bus_status1, BS1_RST);	/* assert RST */	udelay(30);			/* leave it on for >= 25us */	out_8(&mr->bus_status1, 0);	/* negate RST */#ifdef DO_ASYNC_RESET	if (how & SCSI_RESET_ASYNCHRONOUS) {		restore_flags(flags);		ret |= SCSI_RESET_PENDING;	} else#endif	{		handle_reset(ms);		restore_flags(flags);#ifndef MESH_NEW_STYLE_EH		finish_cmds(ms);#endif		ret |= SCSI_RESET_SUCCESS;	}	return ret;}/* * 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 intmesh_notify_reboot(struct notifier_block *this, unsigned long code, void *x){	struct mesh_state *ms;	volatile struct mesh_regs *mr;	if (code == SYS_DOWN) {		printk(KERN_INFO "resetting MESH scsi bus(es)\n");		for (ms = all_meshes; ms != 0; ms = ms->next) {			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);			udelay(30);			out_8(&mr->bus_status1, 0);		}	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -