sclp.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 854 行 · 第 1/2 页

C
854
字号
/* *  drivers/s390/char/sclp.c *     core function to access sclp interface * *  S390 version *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation *    Author(s): Martin Peschke <mpeschke@de.ibm.com> *		 Martin Schwidefsky <schwidefsky@de.ibm.com> */#include <linux/config.h>#include <linux/module.h>#include <linux/kmod.h>#include <linux/bootmem.h>#include <linux/err.h>#include <linux/ptrace.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/interrupt.h>#include <linux/timer.h>#include <linux/init.h>#include <linux/cpumask.h>#include <linux/reboot.h>#include <asm/s390_ext.h>#include <asm/processor.h>#include "sclp.h"#define SCLP_CORE_PRINT_HEADER "sclp low level driver: "/* Structure for register_early_external_interrupt. */static ext_int_info_t ext_int_info_hwc;/* spinlock to protect global variables of sclp_core */static spinlock_t sclp_lock;/* Mask of valid sclp events */static sccb_mask_t sclp_receive_mask;static sccb_mask_t sclp_send_mask;/* List of registered event types */static struct list_head sclp_reg_list;/* sccb queue */static struct list_head sclp_req_queue;/* sccb for unconditional read */static struct sclp_req sclp_read_req;static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));/* sccb for write mask sccb */static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));/* Timer for init mask retries. */static struct timer_list retry_timer;/* Timer for busy retries. */static struct timer_list sclp_busy_timer;static volatile unsigned long sclp_status = 0;/* some status flags */#define SCLP_INIT		0#define SCLP_RUNNING		1#define SCLP_READING		2#define SCLP_SHUTDOWN		3#define SCLP_INIT_POLL_INTERVAL	1#define SCLP_BUSY_POLL_INTERVAL	1#define SCLP_COMMAND_INITIATED	0#define SCLP_BUSY		2#define SCLP_NOT_OPERATIONAL	3/* * assembler instruction for Service Call */static int__service_call(sclp_cmdw_t command, void *sccb){	int cc;	/*	 *  Mnemonic:	SERVC	Rx, Ry	[RRE]	 *	 *  Rx: SCLP command word	 *  Ry: address of SCCB	 */	__asm__ __volatile__(		"   .insn rre,0xb2200000,%1,%2\n"  /* servc %1,%2 */		"   ipm	  %0\n"		"   srl	  %0,28"		: "=&d" (cc)		: "d" (command), "a" (__pa(sccb))		: "cc", "memory" );	/*	 * cc == 0:   Service Call succesful initiated	 * cc == 2:   SCLP busy, new Service Call not initiated,	 *	      new SCCB unchanged	 * cc == 3:   SCLP function not operational	 */	if (cc == SCLP_NOT_OPERATIONAL)		return -EIO;	if (cc == SCLP_BUSY)		return -EBUSY;	return 0;}static voidsclp_start_request(void){	struct sclp_req *req;	int rc;	unsigned long flags;	spin_lock_irqsave(&sclp_lock, flags);	/* quick exit if sclp is already in use */	if (test_bit(SCLP_RUNNING, &sclp_status)) {		spin_unlock_irqrestore(&sclp_lock, flags);		return;	}	/* Try to start requests from the request queue. */	while (!list_empty(&sclp_req_queue)) {		req = list_entry(sclp_req_queue.next, struct sclp_req, list);		rc = __service_call(req->command, req->sccb);		if (rc == 0) {			/* Sucessfully started request. */			req->status = SCLP_REQ_RUNNING;			/* Request active. Set running indication. */			set_bit(SCLP_RUNNING, &sclp_status);			break;		}		if (rc == -EBUSY) {			/**			 * SCLP is busy but no request is running.			 * Try again later.			 */			if (!timer_pending(&sclp_busy_timer) ||			    !mod_timer(&sclp_busy_timer,				       jiffies + SCLP_BUSY_POLL_INTERVAL*HZ)) {				sclp_busy_timer.function =					(void *) sclp_start_request;				sclp_busy_timer.expires =					jiffies + SCLP_BUSY_POLL_INTERVAL*HZ;				add_timer(&sclp_busy_timer);			}			break;		}		/* Request failed. */		req->status = SCLP_REQ_FAILED;		list_del(&req->list);		if (req->callback) {			spin_unlock_irqrestore(&sclp_lock, flags);			req->callback(req, req->callback_data);			spin_lock_irqsave(&sclp_lock, flags);		}	}	spin_unlock_irqrestore(&sclp_lock, flags);}static intsclp_process_evbufs(struct sccb_header *sccb){	int result;	unsigned long flags;	struct evbuf_header *evbuf;	struct list_head *l;	struct sclp_register *t;	spin_lock_irqsave(&sclp_lock, flags);	evbuf = (struct evbuf_header *) (sccb + 1);	result = 0;	while ((addr_t) evbuf < (addr_t) sccb + sccb->length) {		/* check registered event */		t = NULL;		list_for_each(l, &sclp_reg_list) {			t = list_entry(l, struct sclp_register, list);			if (t->receive_mask & (1 << (32 - evbuf->type))) {				if (t->receiver_fn != NULL) {					spin_unlock_irqrestore(&sclp_lock,							       flags);					t->receiver_fn(evbuf);					spin_lock_irqsave(&sclp_lock, flags);				}				break;			}			else				t = NULL;		}		/* Check for unrequested event buffer */		if (t == NULL)			result = -ENOSYS;		evbuf = (struct evbuf_header *)				((addr_t) evbuf + evbuf->length);	}	spin_unlock_irqrestore(&sclp_lock, flags);	return result;}char *sclp_error_message(u16 rc){	static struct {		u16 code; char *msg;	} sclp_errors[] = {		{ 0x0000, "No response code stored (machine malfunction)" },		{ 0x0020, "Normal Completion" },		{ 0x0040, "SCLP equipment check" },		{ 0x0100, "SCCB boundary violation" },		{ 0x01f0, "Invalid command" },		{ 0x0220, "Normal Completion; suppressed buffers pending" },		{ 0x0300, "Insufficient SCCB length" },		{ 0x0340, "Contained SCLP equipment check" },		{ 0x05f0, "Target resource in improper state" },		{ 0x40f0, "Invalid function code/not installed" },		{ 0x60f0, "No buffers stored" },		{ 0x62f0, "No buffers stored; suppressed buffers pending" },		{ 0x70f0, "Invalid selection mask" },		{ 0x71f0, "Event buffer exceeds available space" },		{ 0x72f0, "Inconsistent lengths" },		{ 0x73f0, "Event buffer syntax error" }	};	int i;	for (i = 0; i < sizeof(sclp_errors)/sizeof(sclp_errors[0]); i++)		if (rc == sclp_errors[i].code)			return sclp_errors[i].msg;	return "Invalid response code";}/* * postprocessing of unconditional read service call */static voidsclp_unconditional_read_cb(struct sclp_req *read_req, void *data){	struct sccb_header *sccb;	sccb = read_req->sccb;	if (sccb->response_code == 0x0020 ||	    sccb->response_code == 0x0220) {		if (sclp_process_evbufs(sccb) != 0)			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER			       "unconditional read: "			       "unrequested event buffer received.\n");	}	if (sccb->response_code != 0x0020)		printk(KERN_WARNING SCLP_CORE_PRINT_HEADER		       "unconditional read: %s (response code=0x%x).\n",		       sclp_error_message(sccb->response_code),		       sccb->response_code);	clear_bit(SCLP_READING, &sclp_status);}/* * Function to queue Read Event Data/Unconditional Read */static void__sclp_unconditional_read(void){	struct sccb_header *sccb;	struct sclp_req *read_req;	/*	 * Don't try to initiate Unconditional Read if we are not able to	 * receive anything	 */	if (sclp_receive_mask == 0)		return;	/* Don't try reading if a read is already outstanding */	if (test_and_set_bit(SCLP_READING, &sclp_status))		return;	/* Initialize read sccb */	sccb = (struct sccb_header *) sclp_read_sccb;	clear_page(sccb);	sccb->length = PAGE_SIZE;	sccb->function_code = 0;	/* unconditional read */	sccb->control_mask[2] = 0x80;	/* variable length response */	/* Initialize request structure */	read_req = &sclp_read_req;	read_req->command = SCLP_CMDW_READDATA;	read_req->status = SCLP_REQ_QUEUED;	read_req->callback = sclp_unconditional_read_cb;	read_req->sccb = sccb;	/* Add read request to the head of queue */	list_add(&read_req->list, &sclp_req_queue);}/* Bit masks to interpret external interruption parameter contents. */#define EXT_INT_SCCB_MASK		0xfffffff8#define EXT_INT_STATECHANGE_PENDING	0x00000002#define EXT_INT_EVBUF_PENDING		0x00000001/* * Handler for service-signal external interruptions */static voidsclp_interrupt_handler(struct pt_regs *regs, __u16 code){	u32 ext_int_param, finished_sccb, evbuf_pending;	struct list_head *l;	struct sclp_req *req, *tmp;	spin_lock(&sclp_lock);	/*	 * Only process interrupt if sclp is initialized.	 * This avoids strange effects for a pending request	 * from before the last re-ipl.	 */	if (!test_bit(SCLP_INIT, &sclp_status)) {		/* Now clear the running bit */		clear_bit(SCLP_RUNNING, &sclp_status);		spin_unlock(&sclp_lock);		return;	}	ext_int_param = S390_lowcore.ext_params;	finished_sccb = ext_int_param & EXT_INT_SCCB_MASK;	evbuf_pending = ext_int_param & (EXT_INT_EVBUF_PENDING |					 EXT_INT_STATECHANGE_PENDING);	req = NULL;	if (finished_sccb != 0U) {		list_for_each(l, &sclp_req_queue) {			tmp = list_entry(l, struct sclp_req, list);			if (finished_sccb == (u32)(addr_t) tmp->sccb) {				list_del(&tmp->list);				req = tmp;				break;			}		}	}	spin_unlock(&sclp_lock);	/* Perform callback */	if (req != NULL) {		req->status = SCLP_REQ_DONE;		if (req->callback != NULL)			req->callback(req, req->callback_data);	}	spin_lock(&sclp_lock);	/* Head queue a read sccb if an event buffer is pending */	if (evbuf_pending)		__sclp_unconditional_read();	/* Now clear the running bit if SCLP indicated a finished SCCB */	if (finished_sccb != 0U)		clear_bit(SCLP_RUNNING, &sclp_status);	spin_unlock(&sclp_lock);	/* and start next request on the queue */	sclp_start_request();}/* * Wait synchronously for external interrupt of sclp. We may not receive * any other external interrupt, so we disable all other external interrupts * in control register 0. */voidsclp_sync_wait(void){	unsigned long psw_mask;	unsigned long cr0, cr0_sync;	/* Prevent BH from executing. */	local_bh_disable();	/*	 * save cr0	 * enable service signal external interruption (cr0.22)	 * disable cr0.20-21, cr0.25, cr0.27, cr0.30-31	 * don't touch any other bit in cr0	 */	__ctl_store(cr0, 0, 0);	cr0_sync = cr0;	cr0_sync |= 0x00000200;	cr0_sync &= 0xFFFFF3AC;	__ctl_load(cr0_sync, 0, 0);	/* enable external interruptions (PSW-mask.7) */	asm volatile ("STOSM 0(%1),0x01"		      : "=m" (psw_mask) : "a" (&psw_mask) : "memory");	/* wait until ISR signals receipt of interrupt */	while (test_bit(SCLP_RUNNING, &sclp_status)) {		barrier();		cpu_relax();	}	/* disable external interruptions */	asm volatile ("SSM 0(%0)"		      : : "a" (&psw_mask) : "memory");	/* restore cr0 */	__ctl_load(cr0, 0, 0);	__local_bh_enable();}/* * Queue an SCLP request. Request will immediately be processed if queue is * empty. */voidsclp_add_request(struct sclp_req *req){	unsigned long flags;	if (!test_bit(SCLP_INIT, &sclp_status)) {		req->status = SCLP_REQ_FAILED;		if (req->callback != NULL)			req->callback(req, req->callback_data);		return;	}	spin_lock_irqsave(&sclp_lock, flags);	/* queue the request */	req->status = SCLP_REQ_QUEUED;	list_add_tail(&req->list, &sclp_req_queue);	spin_unlock_irqrestore(&sclp_lock, flags);	/* try to start the first request on the queue */	sclp_start_request();}/* state change notification */struct sclp_statechangebuf {	struct evbuf_header	header;	u8		validity_sclp_active_facility_mask : 1;	u8		validity_sclp_receive_mask : 1;	u8		validity_sclp_send_mask : 1;	u8		validity_read_data_function_mask : 1;	u16		_zeros : 12;	u16		mask_length;	u64		sclp_active_facility_mask;	sccb_mask_t	sclp_receive_mask;

⌨️ 快捷键说明

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