📄 ap_bus.c
字号:
/* * 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 + -