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

📄 ap_bus.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * linux/drivers/s390/crypto/ap_bus.c * * Copyright (C) 2006 IBM Corporation * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> *	      Martin Schwidefsky <schwidefsky@de.ibm.com> *	      Ralph Wuerthner <rwuerthn@de.ibm.com> * * Adjunct processor bus. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/module.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/err.h>#include <linux/interrupt.h>#include <linux/workqueue.h>#include <linux/notifier.h>#include <linux/kthread.h>#include <linux/mutex.h>#include <asm/s390_rdev.h>#include <asm/reset.h>#include "ap_bus.h"/* Some prototypes. */static void ap_scan_bus(struct work_struct *);static void ap_poll_all(unsigned long);static void ap_poll_timeout(unsigned long);static int ap_poll_thread_start(void);static void ap_poll_thread_stop(void);static void ap_request_timeout(unsigned long);/** * Module description. */MODULE_AUTHOR("IBM Corporation");MODULE_DESCRIPTION("Adjunct Processor Bus driver, "		   "Copyright 2006 IBM Corporation");MODULE_LICENSE("GPL");/** * Module parameter */int ap_domain_index = -1;	/* Adjunct Processor Domain Index */module_param_named(domain, ap_domain_index, int, 0000);MODULE_PARM_DESC(domain, "domain index for ap devices");EXPORT_SYMBOL(ap_domain_index);static int ap_thread_flag = 1;module_param_named(poll_thread, ap_thread_flag, int, 0000);MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 1 (on).");static struct device *ap_root_device = NULL;static DEFINE_SPINLOCK(ap_device_lock);static LIST_HEAD(ap_device_list);/** * Workqueue & timer for bus rescan. */static struct workqueue_struct *ap_work_queue;static struct timer_list ap_config_timer;static int ap_config_time = AP_CONFIG_TIME;static DECLARE_WORK(ap_config_work, ap_scan_bus);/** * Tasklet & timer for AP request polling. */static struct timer_list ap_poll_timer = TIMER_INITIALIZER(ap_poll_timeout,0,0);static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0);static atomic_t ap_poll_requests = ATOMIC_INIT(0);static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);static struct task_struct *ap_poll_kthread = NULL;static DEFINE_MUTEX(ap_poll_thread_mutex);/** * Test if ap instructions are available. * * Returns 0 if the ap instructions are installed. */static inline int ap_instructions_available(void){	register unsigned long reg0 asm ("0") = AP_MKQID(0,0);	register unsigned long reg1 asm ("1") = -ENODEV;	register unsigned long reg2 asm ("2") = 0UL;	asm volatile(		"   .long 0xb2af0000\n"		/* PQAP(TAPQ) */		"0: la    %1,0\n"		"1:\n"		EX_TABLE(0b, 1b)		: "+d" (reg0), "+d" (reg1), "+d" (reg2) : : "cc" );	return reg1;}/** * Test adjunct processor queue. * @qid: the ap queue number * @queue_depth: pointer to queue depth value * @device_type: pointer to device type value * * Returns ap queue status structure. */static inline struct ap_queue_statusap_test_queue(ap_qid_t qid, int *queue_depth, int *device_type){	register unsigned long reg0 asm ("0") = qid;	register struct ap_queue_status reg1 asm ("1");	register unsigned long reg2 asm ("2") = 0UL;	asm volatile(".long 0xb2af0000"		/* PQAP(TAPQ) */		     : "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc");	*device_type = (int) (reg2 >> 24);	*queue_depth = (int) (reg2 & 0xff);	return reg1;}/** * Reset adjunct processor queue. * @qid: the ap queue number * * Returns ap queue status structure. */static inline struct ap_queue_status ap_reset_queue(ap_qid_t qid){	register unsigned long reg0 asm ("0") = qid | 0x01000000UL;	register struct ap_queue_status reg1 asm ("1");	register unsigned long reg2 asm ("2") = 0UL;	asm volatile(		".long 0xb2af0000"		/* PQAP(RAPQ) */		: "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc");	return reg1;}/** * Send message to adjunct processor queue. * @qid: the ap queue number * @psmid: the program supplied message identifier * @msg: the message text * @length: the message length * * Returns ap queue status structure. * * Condition code 1 on NQAP can't happen because the L bit is 1. * * Condition code 2 on NQAP also means the send is incomplete, * because a segment boundary was reached. The NQAP is repeated. */static inline struct ap_queue_status__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length){	typedef struct { char _[length]; } msgblock;	register unsigned long reg0 asm ("0") = qid | 0x40000000UL;	register struct ap_queue_status reg1 asm ("1");	register unsigned long reg2 asm ("2") = (unsigned long) msg;	register unsigned long reg3 asm ("3") = (unsigned long) length;	register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32);	register unsigned long reg5 asm ("5") = (unsigned int) psmid;	asm volatile (		"0: .long 0xb2ad0042\n"		/* DQAP */		"   brc   2,0b"		: "+d" (reg0), "=d" (reg1), "+d" (reg2), "+d" (reg3)		: "d" (reg4), "d" (reg5), "m" (*(msgblock *) msg)		: "cc" );	return reg1;}int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length){	struct ap_queue_status status;	status = __ap_send(qid, psmid, msg, length);	switch (status.response_code) {	case AP_RESPONSE_NORMAL:		return 0;	case AP_RESPONSE_Q_FULL:	case AP_RESPONSE_RESET_IN_PROGRESS:		return -EBUSY;	default:	/* Device is gone. */		return -ENODEV;	}}EXPORT_SYMBOL(ap_send);/* * Receive message from adjunct processor queue. * @qid: the ap queue number * @psmid: pointer to program supplied message identifier * @msg: the message text * @length: the message length * * Returns ap queue status structure. * * Condition code 1 on DQAP means the receive has taken place * but only partially.	The response is incomplete, hence the * DQAP is repeated. * * Condition code 2 on DQAP also means the receive is incomplete, * this time because a segment boundary was reached. Again, the * DQAP is repeated. * * Note that gpr2 is used by the DQAP instruction to keep track of * any 'residual' length, in case the instruction gets interrupted. * Hence it gets zeroed before the instruction. */static inline struct ap_queue_status__ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length){	typedef struct { char _[length]; } msgblock;	register unsigned long reg0 asm("0") = qid | 0x80000000UL;	register struct ap_queue_status reg1 asm ("1");	register unsigned long reg2 asm("2") = 0UL;	register unsigned long reg4 asm("4") = (unsigned long) msg;	register unsigned long reg5 asm("5") = (unsigned long) length;	register unsigned long reg6 asm("6") = 0UL;	register unsigned long reg7 asm("7") = 0UL;	asm volatile(		"0: .long 0xb2ae0064\n"		"   brc   6,0b\n"		: "+d" (reg0), "=d" (reg1), "+d" (reg2),		"+d" (reg4), "+d" (reg5), "+d" (reg6), "+d" (reg7),		"=m" (*(msgblock *) msg) : : "cc" );	*psmid = (((unsigned long long) reg6) << 32) + reg7;	return reg1;}int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length){	struct ap_queue_status status;	status = __ap_recv(qid, psmid, msg, length);	switch (status.response_code) {	case AP_RESPONSE_NORMAL:		return 0;	case AP_RESPONSE_NO_PENDING_REPLY:		if (status.queue_empty)			return -ENOENT;		return -EBUSY;	case AP_RESPONSE_RESET_IN_PROGRESS:		return -EBUSY;	default:		return -ENODEV;	}}EXPORT_SYMBOL(ap_recv);/** * Check if an AP queue is available. The test is repeated for * AP_MAX_RESET times. * @qid: the ap queue number * @queue_depth: pointer to queue depth value * @device_type: pointer to device type value */static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type){	struct ap_queue_status status;	int t_depth, t_device_type, rc, i;	rc = -EBUSY;	for (i = 0; i < AP_MAX_RESET; i++) {		status = ap_test_queue(qid, &t_depth, &t_device_type);		switch (status.response_code) {		case AP_RESPONSE_NORMAL:			*queue_depth = t_depth + 1;			*device_type = t_device_type;			rc = 0;			break;		case AP_RESPONSE_Q_NOT_AVAIL:			rc = -ENODEV;			break;		case AP_RESPONSE_RESET_IN_PROGRESS:			break;		case AP_RESPONSE_DECONFIGURED:			rc = -ENODEV;			break;		case AP_RESPONSE_CHECKSTOPPED:			rc = -ENODEV;			break;		case AP_RESPONSE_BUSY:			break;		default:			BUG();		}		if (rc != -EBUSY)			break;		if (i < AP_MAX_RESET - 1)			udelay(5);	}	return rc;}/** * Reset an AP queue and wait for it to become available again. * @qid: the ap queue number */static int ap_init_queue(ap_qid_t qid){	struct ap_queue_status status;	int rc, dummy, i;	rc = -ENODEV;	status = ap_reset_queue(qid);	for (i = 0; i < AP_MAX_RESET; i++) {		switch (status.response_code) {		case AP_RESPONSE_NORMAL:			if (status.queue_empty)				rc = 0;			break;		case AP_RESPONSE_Q_NOT_AVAIL:		case AP_RESPONSE_DECONFIGURED:		case AP_RESPONSE_CHECKSTOPPED:			i = AP_MAX_RESET;	/* return with -ENODEV */			break;		case AP_RESPONSE_RESET_IN_PROGRESS:			rc = -EBUSY;		case AP_RESPONSE_BUSY:		default:			break;		}		if (rc != -ENODEV && rc != -EBUSY)			break;		if (i < AP_MAX_RESET - 1) {			udelay(5);			status = ap_test_queue(qid, &dummy, &dummy);		}	}	return rc;}/** * Arm request timeout if a AP device was idle and a new request is submitted. */static void ap_increase_queue_count(struct ap_device *ap_dev){	int timeout = ap_dev->drv->request_timeout;	ap_dev->queue_count++;	if (ap_dev->queue_count == 1) {		mod_timer(&ap_dev->timeout, jiffies + timeout);		ap_dev->reset = AP_RESET_ARMED;	}}/** * AP device is still alive, re-schedule request timeout if there are still * pending requests. */static void ap_decrease_queue_count(struct ap_device *ap_dev){	int timeout = ap_dev->drv->request_timeout;	ap_dev->queue_count--;	if (ap_dev->queue_count > 0)		mod_timer(&ap_dev->timeout, jiffies + timeout);	else		/**		 * The timeout timer should to be disabled now - since		 * del_timer_sync() is very expensive, we just tell via the		 * reset flag to ignore the pending timeout timer.		 */		ap_dev->reset = AP_RESET_IGNORE;}/** * AP device related attributes. */static ssize_t ap_hwtype_show(struct device *dev,			      struct device_attribute *attr, char *buf){	struct ap_device *ap_dev = to_ap_dev(dev);	return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->device_type);}static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL);static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr,			     char *buf){	struct ap_device *ap_dev = to_ap_dev(dev);	return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->queue_depth);}static DEVICE_ATTR(depth, 0444, ap_depth_show, NULL);static ssize_t ap_request_count_show(struct device *dev,				     struct device_attribute *attr,				     char *buf){	struct ap_device *ap_dev = to_ap_dev(dev);	int rc;	spin_lock_bh(&ap_dev->lock);	rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->total_request_count);	spin_unlock_bh(&ap_dev->lock);	return rc;}static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL);static ssize_t ap_modalias_show(struct device *dev,				struct device_attribute *attr, char *buf){	return sprintf(buf, "ap:t%02X", to_ap_dev(dev)->device_type);}static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL);static struct attribute *ap_dev_attrs[] = {	&dev_attr_hwtype.attr,	&dev_attr_depth.attr,	&dev_attr_request_count.attr,	&dev_attr_modalias.attr,	NULL};static struct attribute_group ap_dev_attr_group = {	.attrs = ap_dev_attrs};/** * AP bus driver registration/unregistration. */static int ap_bus_match(struct device *dev, struct device_driver *drv){	struct ap_device *ap_dev = to_ap_dev(dev);	struct ap_driver *ap_drv = to_ap_drv(drv);	struct ap_device_id *id;	/**	 * Compare device type of the device with the list of	 * supported types of the device_driver.	 */	for (id = ap_drv->ids; id->match_flags; id++) {		if ((id->match_flags & AP_DEVICE_ID_MATCH_DEVICE_TYPE) &&		    (id->dev_type != ap_dev->device_type))			continue;		return 1;

⌨️ 快捷键说明

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