18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2006, 2012
48c2ecf20Sopenharmony_ci * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
58c2ecf20Sopenharmony_ci *	      Martin Schwidefsky <schwidefsky@de.ibm.com>
68c2ecf20Sopenharmony_ci *	      Ralph Wuerthner <rwuerthn@de.ibm.com>
78c2ecf20Sopenharmony_ci *	      Felix Beck <felix.beck@de.ibm.com>
88c2ecf20Sopenharmony_ci *	      Holger Dengler <hd@linux.vnet.ibm.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Adjunct processor bus.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "ap"
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h>
178c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
188c2ecf20Sopenharmony_ci#include <linux/init.h>
198c2ecf20Sopenharmony_ci#include <linux/delay.h>
208c2ecf20Sopenharmony_ci#include <linux/err.h>
218c2ecf20Sopenharmony_ci#include <linux/freezer.h>
228c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
238c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/notifier.h>
268c2ecf20Sopenharmony_ci#include <linux/kthread.h>
278c2ecf20Sopenharmony_ci#include <linux/mutex.h>
288c2ecf20Sopenharmony_ci#include <asm/airq.h>
298c2ecf20Sopenharmony_ci#include <linux/atomic.h>
308c2ecf20Sopenharmony_ci#include <asm/isc.h>
318c2ecf20Sopenharmony_ci#include <linux/hrtimer.h>
328c2ecf20Sopenharmony_ci#include <linux/ktime.h>
338c2ecf20Sopenharmony_ci#include <asm/facility.h>
348c2ecf20Sopenharmony_ci#include <linux/crypto.h>
358c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
368c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
378c2ecf20Sopenharmony_ci#include <linux/ctype.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include "ap_bus.h"
408c2ecf20Sopenharmony_ci#include "ap_debug.h"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * Module parameters; note though this file itself isn't modular.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_ciint ap_domain_index = -1;	/* Adjunct Processor Domain Index */
468c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(ap_domain_lock);
478c2ecf20Sopenharmony_cimodule_param_named(domain, ap_domain_index, int, 0440);
488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(domain, "domain index for ap devices");
498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ap_domain_index);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic int ap_thread_flag;
528c2ecf20Sopenharmony_cimodule_param_named(poll_thread, ap_thread_flag, int, 0440);
538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off).");
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic char *apm_str;
568c2ecf20Sopenharmony_cimodule_param_named(apmask, apm_str, charp, 0440);
578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(apmask, "AP bus adapter mask.");
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic char *aqm_str;
608c2ecf20Sopenharmony_cimodule_param_named(aqmask, aqm_str, charp, 0440);
618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(aqmask, "AP bus domain mask.");
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic struct device *ap_root_device;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* Hashtable of all queue devices on the AP bus */
668c2ecf20Sopenharmony_ciDEFINE_HASHTABLE(ap_queues, 8);
678c2ecf20Sopenharmony_ci/* lock used for the ap_queues hashtable */
688c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(ap_queues_lock);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* Default permissions (ioctl, card and domain masking) */
718c2ecf20Sopenharmony_cistruct ap_perms ap_perms;
728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ap_perms);
738c2ecf20Sopenharmony_ciDEFINE_MUTEX(ap_perms_mutex);
748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ap_perms_mutex);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic struct ap_config_info *ap_qci_info;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/*
798c2ecf20Sopenharmony_ci * AP bus related debug feature things.
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_cidebug_info_t *ap_dbf_info;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/*
848c2ecf20Sopenharmony_ci * Workqueue timer for bus rescan.
858c2ecf20Sopenharmony_ci */
868c2ecf20Sopenharmony_cistatic struct timer_list ap_config_timer;
878c2ecf20Sopenharmony_cistatic int ap_config_time = AP_CONFIG_TIME;
888c2ecf20Sopenharmony_cistatic void ap_scan_bus(struct work_struct *);
898c2ecf20Sopenharmony_cistatic DECLARE_WORK(ap_scan_work, ap_scan_bus);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/*
928c2ecf20Sopenharmony_ci * Tasklet & timer for AP request polling and interrupts
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_cistatic void ap_tasklet_fn(unsigned long);
958c2ecf20Sopenharmony_cistatic DECLARE_TASKLET_OLD(ap_tasklet, ap_tasklet_fn);
968c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
978c2ecf20Sopenharmony_cistatic struct task_struct *ap_poll_kthread;
988c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ap_poll_thread_mutex);
998c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(ap_poll_timer_lock);
1008c2ecf20Sopenharmony_cistatic struct hrtimer ap_poll_timer;
1018c2ecf20Sopenharmony_ci/*
1028c2ecf20Sopenharmony_ci * In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
1038c2ecf20Sopenharmony_ci * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_cistatic unsigned long long poll_timeout = 250000;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* Maximum domain id, if not given via qci */
1088c2ecf20Sopenharmony_cistatic int ap_max_domain_id = 15;
1098c2ecf20Sopenharmony_ci/* Maximum adapter id, if not given via qci */
1108c2ecf20Sopenharmony_cistatic int ap_max_adapter_id = 63;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic struct bus_type ap_bus_type;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* Adapter interrupt definitions */
1158c2ecf20Sopenharmony_cistatic void ap_interrupt_handler(struct airq_struct *airq, bool floating);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic bool ap_irq_flag;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic struct airq_struct ap_airq = {
1208c2ecf20Sopenharmony_ci	.handler = ap_interrupt_handler,
1218c2ecf20Sopenharmony_ci	.isc = AP_ISC,
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/**
1258c2ecf20Sopenharmony_ci * ap_airq_ptr() - Get the address of the adapter interrupt indicator
1268c2ecf20Sopenharmony_ci *
1278c2ecf20Sopenharmony_ci * Returns the address of the local-summary-indicator of the adapter
1288c2ecf20Sopenharmony_ci * interrupt handler for AP, or NULL if adapter interrupts are not
1298c2ecf20Sopenharmony_ci * available.
1308c2ecf20Sopenharmony_ci */
1318c2ecf20Sopenharmony_civoid *ap_airq_ptr(void)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	if (ap_irq_flag)
1348c2ecf20Sopenharmony_ci		return ap_airq.lsi_ptr;
1358c2ecf20Sopenharmony_ci	return NULL;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/**
1398c2ecf20Sopenharmony_ci * ap_interrupts_available(): Test if AP interrupts are available.
1408c2ecf20Sopenharmony_ci *
1418c2ecf20Sopenharmony_ci * Returns 1 if AP interrupts are available.
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_cistatic int ap_interrupts_available(void)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	return test_facility(65);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/**
1498c2ecf20Sopenharmony_ci * ap_qci_available(): Test if AP configuration
1508c2ecf20Sopenharmony_ci * information can be queried via QCI subfunction.
1518c2ecf20Sopenharmony_ci *
1528c2ecf20Sopenharmony_ci * Returns 1 if subfunction PQAP(QCI) is available.
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_cistatic int ap_qci_available(void)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	return test_facility(12);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/**
1608c2ecf20Sopenharmony_ci * ap_apft_available(): Test if AP facilities test (APFT)
1618c2ecf20Sopenharmony_ci * facility is available.
1628c2ecf20Sopenharmony_ci *
1638c2ecf20Sopenharmony_ci * Returns 1 if APFT is is available.
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic int ap_apft_available(void)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	return test_facility(15);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/*
1718c2ecf20Sopenharmony_ci * ap_qact_available(): Test if the PQAP(QACT) subfunction is available.
1728c2ecf20Sopenharmony_ci *
1738c2ecf20Sopenharmony_ci * Returns 1 if the QACT subfunction is available.
1748c2ecf20Sopenharmony_ci */
1758c2ecf20Sopenharmony_cistatic inline int ap_qact_available(void)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	if (ap_qci_info)
1788c2ecf20Sopenharmony_ci		return ap_qci_info->qact;
1798c2ecf20Sopenharmony_ci	return 0;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/*
1838c2ecf20Sopenharmony_ci * ap_fetch_qci_info(): Fetch cryptographic config info
1848c2ecf20Sopenharmony_ci *
1858c2ecf20Sopenharmony_ci * Returns the ap configuration info fetched via PQAP(QCI).
1868c2ecf20Sopenharmony_ci * On success 0 is returned, on failure a negative errno
1878c2ecf20Sopenharmony_ci * is returned, e.g. if the PQAP(QCI) instruction is not
1888c2ecf20Sopenharmony_ci * available, the return value will be -EOPNOTSUPP.
1898c2ecf20Sopenharmony_ci */
1908c2ecf20Sopenharmony_cistatic inline int ap_fetch_qci_info(struct ap_config_info *info)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	if (!ap_qci_available())
1938c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1948c2ecf20Sopenharmony_ci	if (!info)
1958c2ecf20Sopenharmony_ci		return -EINVAL;
1968c2ecf20Sopenharmony_ci	return ap_qci(info);
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/**
2008c2ecf20Sopenharmony_ci * ap_init_qci_info(): Allocate and query qci config info.
2018c2ecf20Sopenharmony_ci * Does also update the static variables ap_max_domain_id
2028c2ecf20Sopenharmony_ci * and ap_max_adapter_id if this info is available.
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci */
2058c2ecf20Sopenharmony_cistatic void __init ap_init_qci_info(void)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	if (!ap_qci_available()) {
2088c2ecf20Sopenharmony_ci		AP_DBF_INFO("%s QCI not supported\n", __func__);
2098c2ecf20Sopenharmony_ci		return;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	ap_qci_info = kzalloc(sizeof(*ap_qci_info), GFP_KERNEL);
2138c2ecf20Sopenharmony_ci	if (!ap_qci_info)
2148c2ecf20Sopenharmony_ci		return;
2158c2ecf20Sopenharmony_ci	if (ap_fetch_qci_info(ap_qci_info) != 0) {
2168c2ecf20Sopenharmony_ci		kfree(ap_qci_info);
2178c2ecf20Sopenharmony_ci		ap_qci_info = NULL;
2188c2ecf20Sopenharmony_ci		return;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci	AP_DBF_INFO("%s successful fetched initial qci info\n", __func__);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (ap_qci_info->apxa) {
2238c2ecf20Sopenharmony_ci		if (ap_qci_info->Na) {
2248c2ecf20Sopenharmony_ci			ap_max_adapter_id = ap_qci_info->Na;
2258c2ecf20Sopenharmony_ci			AP_DBF_INFO("%s new ap_max_adapter_id is %d\n",
2268c2ecf20Sopenharmony_ci				    __func__, ap_max_adapter_id);
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci		if (ap_qci_info->Nd) {
2298c2ecf20Sopenharmony_ci			ap_max_domain_id = ap_qci_info->Nd;
2308c2ecf20Sopenharmony_ci			AP_DBF_INFO("%s new ap_max_domain_id is %d\n",
2318c2ecf20Sopenharmony_ci				    __func__, ap_max_domain_id);
2328c2ecf20Sopenharmony_ci		}
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/*
2378c2ecf20Sopenharmony_ci * ap_test_config(): helper function to extract the nrth bit
2388c2ecf20Sopenharmony_ci *		     within the unsigned int array field.
2398c2ecf20Sopenharmony_ci */
2408c2ecf20Sopenharmony_cistatic inline int ap_test_config(unsigned int *field, unsigned int nr)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	return ap_test_bit((field + (nr >> 5)), (nr & 0x1f));
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci/*
2468c2ecf20Sopenharmony_ci * ap_test_config_card_id(): Test, whether an AP card ID is configured.
2478c2ecf20Sopenharmony_ci *
2488c2ecf20Sopenharmony_ci * Returns 0 if the card is not configured
2498c2ecf20Sopenharmony_ci *	   1 if the card is configured or
2508c2ecf20Sopenharmony_ci *	     if the configuration information is not available
2518c2ecf20Sopenharmony_ci */
2528c2ecf20Sopenharmony_cistatic inline int ap_test_config_card_id(unsigned int id)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	if (id > ap_max_adapter_id)
2558c2ecf20Sopenharmony_ci		return 0;
2568c2ecf20Sopenharmony_ci	if (ap_qci_info)
2578c2ecf20Sopenharmony_ci		return ap_test_config(ap_qci_info->apm, id);
2588c2ecf20Sopenharmony_ci	return 1;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci/*
2628c2ecf20Sopenharmony_ci * ap_test_config_usage_domain(): Test, whether an AP usage domain
2638c2ecf20Sopenharmony_ci * is configured.
2648c2ecf20Sopenharmony_ci *
2658c2ecf20Sopenharmony_ci * Returns 0 if the usage domain is not configured
2668c2ecf20Sopenharmony_ci *	   1 if the usage domain is configured or
2678c2ecf20Sopenharmony_ci *	     if the configuration information is not available
2688c2ecf20Sopenharmony_ci */
2698c2ecf20Sopenharmony_ciint ap_test_config_usage_domain(unsigned int domain)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	if (domain > ap_max_domain_id)
2728c2ecf20Sopenharmony_ci		return 0;
2738c2ecf20Sopenharmony_ci	if (ap_qci_info)
2748c2ecf20Sopenharmony_ci		return ap_test_config(ap_qci_info->aqm, domain);
2758c2ecf20Sopenharmony_ci	return 1;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ap_test_config_usage_domain);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci/*
2808c2ecf20Sopenharmony_ci * ap_test_config_ctrl_domain(): Test, whether an AP control domain
2818c2ecf20Sopenharmony_ci * is configured.
2828c2ecf20Sopenharmony_ci * @domain AP control domain ID
2838c2ecf20Sopenharmony_ci *
2848c2ecf20Sopenharmony_ci * Returns 1 if the control domain is configured
2858c2ecf20Sopenharmony_ci *	   0 in all other cases
2868c2ecf20Sopenharmony_ci */
2878c2ecf20Sopenharmony_ciint ap_test_config_ctrl_domain(unsigned int domain)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	if (!ap_qci_info || domain > ap_max_domain_id)
2908c2ecf20Sopenharmony_ci		return 0;
2918c2ecf20Sopenharmony_ci	return ap_test_config(ap_qci_info->adm, domain);
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ap_test_config_ctrl_domain);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci/*
2968c2ecf20Sopenharmony_ci * ap_queue_info(): Check and get AP queue info.
2978c2ecf20Sopenharmony_ci * Returns true if TAPQ succeeded and the info is filled or
2988c2ecf20Sopenharmony_ci * false otherwise.
2998c2ecf20Sopenharmony_ci */
3008c2ecf20Sopenharmony_cistatic bool ap_queue_info(ap_qid_t qid, int *q_type,
3018c2ecf20Sopenharmony_ci			  unsigned int *q_fac, int *q_depth, bool *q_decfg)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct ap_queue_status status;
3048c2ecf20Sopenharmony_ci	unsigned long info = 0;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* make sure we don't run into a specifiation exception */
3078c2ecf20Sopenharmony_ci	if (AP_QID_CARD(qid) > ap_max_adapter_id ||
3088c2ecf20Sopenharmony_ci	    AP_QID_QUEUE(qid) > ap_max_domain_id)
3098c2ecf20Sopenharmony_ci		return false;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	/* call TAPQ on this APQN */
3128c2ecf20Sopenharmony_ci	status = ap_test_queue(qid, ap_apft_available(), &info);
3138c2ecf20Sopenharmony_ci	switch (status.response_code) {
3148c2ecf20Sopenharmony_ci	case AP_RESPONSE_NORMAL:
3158c2ecf20Sopenharmony_ci	case AP_RESPONSE_RESET_IN_PROGRESS:
3168c2ecf20Sopenharmony_ci	case AP_RESPONSE_DECONFIGURED:
3178c2ecf20Sopenharmony_ci	case AP_RESPONSE_CHECKSTOPPED:
3188c2ecf20Sopenharmony_ci	case AP_RESPONSE_BUSY:
3198c2ecf20Sopenharmony_ci		/*
3208c2ecf20Sopenharmony_ci		 * According to the architecture in all these cases the
3218c2ecf20Sopenharmony_ci		 * info should be filled. All bits 0 is not possible as
3228c2ecf20Sopenharmony_ci		 * there is at least one of the mode bits set.
3238c2ecf20Sopenharmony_ci		 */
3248c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!info))
3258c2ecf20Sopenharmony_ci			return false;
3268c2ecf20Sopenharmony_ci		*q_type = (int)((info >> 24) & 0xff);
3278c2ecf20Sopenharmony_ci		*q_fac = (unsigned int)(info >> 32);
3288c2ecf20Sopenharmony_ci		*q_depth = (int)(info & 0xff);
3298c2ecf20Sopenharmony_ci		*q_decfg = status.response_code == AP_RESPONSE_DECONFIGURED;
3308c2ecf20Sopenharmony_ci		switch (*q_type) {
3318c2ecf20Sopenharmony_ci			/* For CEX2 and CEX3 the available functions
3328c2ecf20Sopenharmony_ci			 * are not reflected by the facilities bits.
3338c2ecf20Sopenharmony_ci			 * Instead it is coded into the type. So here
3348c2ecf20Sopenharmony_ci			 * modify the function bits based on the type.
3358c2ecf20Sopenharmony_ci			 */
3368c2ecf20Sopenharmony_ci		case AP_DEVICE_TYPE_CEX2A:
3378c2ecf20Sopenharmony_ci		case AP_DEVICE_TYPE_CEX3A:
3388c2ecf20Sopenharmony_ci			*q_fac |= 0x08000000;
3398c2ecf20Sopenharmony_ci			break;
3408c2ecf20Sopenharmony_ci		case AP_DEVICE_TYPE_CEX2C:
3418c2ecf20Sopenharmony_ci		case AP_DEVICE_TYPE_CEX3C:
3428c2ecf20Sopenharmony_ci			*q_fac |= 0x10000000;
3438c2ecf20Sopenharmony_ci			break;
3448c2ecf20Sopenharmony_ci		default:
3458c2ecf20Sopenharmony_ci			break;
3468c2ecf20Sopenharmony_ci		}
3478c2ecf20Sopenharmony_ci		return true;
3488c2ecf20Sopenharmony_ci	default:
3498c2ecf20Sopenharmony_ci		/*
3508c2ecf20Sopenharmony_ci		 * A response code which indicates, there is no info available.
3518c2ecf20Sopenharmony_ci		 */
3528c2ecf20Sopenharmony_ci		return false;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_civoid ap_wait(enum ap_sm_wait wait)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	ktime_t hr_time;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	switch (wait) {
3618c2ecf20Sopenharmony_ci	case AP_SM_WAIT_AGAIN:
3628c2ecf20Sopenharmony_ci	case AP_SM_WAIT_INTERRUPT:
3638c2ecf20Sopenharmony_ci		if (ap_irq_flag)
3648c2ecf20Sopenharmony_ci			break;
3658c2ecf20Sopenharmony_ci		if (ap_poll_kthread) {
3668c2ecf20Sopenharmony_ci			wake_up(&ap_poll_wait);
3678c2ecf20Sopenharmony_ci			break;
3688c2ecf20Sopenharmony_ci		}
3698c2ecf20Sopenharmony_ci		fallthrough;
3708c2ecf20Sopenharmony_ci	case AP_SM_WAIT_TIMEOUT:
3718c2ecf20Sopenharmony_ci		spin_lock_bh(&ap_poll_timer_lock);
3728c2ecf20Sopenharmony_ci		if (!hrtimer_is_queued(&ap_poll_timer)) {
3738c2ecf20Sopenharmony_ci			hr_time = poll_timeout;
3748c2ecf20Sopenharmony_ci			hrtimer_forward_now(&ap_poll_timer, hr_time);
3758c2ecf20Sopenharmony_ci			hrtimer_restart(&ap_poll_timer);
3768c2ecf20Sopenharmony_ci		}
3778c2ecf20Sopenharmony_ci		spin_unlock_bh(&ap_poll_timer_lock);
3788c2ecf20Sopenharmony_ci		break;
3798c2ecf20Sopenharmony_ci	case AP_SM_WAIT_NONE:
3808c2ecf20Sopenharmony_ci	default:
3818c2ecf20Sopenharmony_ci		break;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci/**
3868c2ecf20Sopenharmony_ci * ap_request_timeout(): Handling of request timeouts
3878c2ecf20Sopenharmony_ci * @t: timer making this callback
3888c2ecf20Sopenharmony_ci *
3898c2ecf20Sopenharmony_ci * Handles request timeouts.
3908c2ecf20Sopenharmony_ci */
3918c2ecf20Sopenharmony_civoid ap_request_timeout(struct timer_list *t)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct ap_queue *aq = from_timer(aq, t, timeout);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	spin_lock_bh(&aq->lock);
3968c2ecf20Sopenharmony_ci	ap_wait(ap_sm_event(aq, AP_SM_EVENT_TIMEOUT));
3978c2ecf20Sopenharmony_ci	spin_unlock_bh(&aq->lock);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci/**
4018c2ecf20Sopenharmony_ci * ap_poll_timeout(): AP receive polling for finished AP requests.
4028c2ecf20Sopenharmony_ci * @unused: Unused pointer.
4038c2ecf20Sopenharmony_ci *
4048c2ecf20Sopenharmony_ci * Schedules the AP tasklet using a high resolution timer.
4058c2ecf20Sopenharmony_ci */
4068c2ecf20Sopenharmony_cistatic enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	tasklet_schedule(&ap_tasklet);
4098c2ecf20Sopenharmony_ci	return HRTIMER_NORESTART;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci/**
4138c2ecf20Sopenharmony_ci * ap_interrupt_handler() - Schedule ap_tasklet on interrupt
4148c2ecf20Sopenharmony_ci * @airq: pointer to adapter interrupt descriptor
4158c2ecf20Sopenharmony_ci */
4168c2ecf20Sopenharmony_cistatic void ap_interrupt_handler(struct airq_struct *airq, bool floating)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	inc_irq_stat(IRQIO_APB);
4198c2ecf20Sopenharmony_ci	tasklet_schedule(&ap_tasklet);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci/**
4238c2ecf20Sopenharmony_ci * ap_tasklet_fn(): Tasklet to poll all AP devices.
4248c2ecf20Sopenharmony_ci * @dummy: Unused variable
4258c2ecf20Sopenharmony_ci *
4268c2ecf20Sopenharmony_ci * Poll all AP devices on the bus.
4278c2ecf20Sopenharmony_ci */
4288c2ecf20Sopenharmony_cistatic void ap_tasklet_fn(unsigned long dummy)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	int bkt;
4318c2ecf20Sopenharmony_ci	struct ap_queue *aq;
4328c2ecf20Sopenharmony_ci	enum ap_sm_wait wait = AP_SM_WAIT_NONE;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/* Reset the indicator if interrupts are used. Thus new interrupts can
4358c2ecf20Sopenharmony_ci	 * be received. Doing it in the beginning of the tasklet is therefor
4368c2ecf20Sopenharmony_ci	 * important that no requests on any AP get lost.
4378c2ecf20Sopenharmony_ci	 */
4388c2ecf20Sopenharmony_ci	if (ap_irq_flag)
4398c2ecf20Sopenharmony_ci		xchg(ap_airq.lsi_ptr, 0);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	spin_lock_bh(&ap_queues_lock);
4428c2ecf20Sopenharmony_ci	hash_for_each(ap_queues, bkt, aq, hnode) {
4438c2ecf20Sopenharmony_ci		spin_lock_bh(&aq->lock);
4448c2ecf20Sopenharmony_ci		wait = min(wait, ap_sm_event_loop(aq, AP_SM_EVENT_POLL));
4458c2ecf20Sopenharmony_ci		spin_unlock_bh(&aq->lock);
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap_queues_lock);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	ap_wait(wait);
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic int ap_pending_requests(void)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	int bkt;
4558c2ecf20Sopenharmony_ci	struct ap_queue *aq;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	spin_lock_bh(&ap_queues_lock);
4588c2ecf20Sopenharmony_ci	hash_for_each(ap_queues, bkt, aq, hnode) {
4598c2ecf20Sopenharmony_ci		if (aq->queue_count == 0)
4608c2ecf20Sopenharmony_ci			continue;
4618c2ecf20Sopenharmony_ci		spin_unlock_bh(&ap_queues_lock);
4628c2ecf20Sopenharmony_ci		return 1;
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap_queues_lock);
4658c2ecf20Sopenharmony_ci	return 0;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci/**
4698c2ecf20Sopenharmony_ci * ap_poll_thread(): Thread that polls for finished requests.
4708c2ecf20Sopenharmony_ci * @data: Unused pointer
4718c2ecf20Sopenharmony_ci *
4728c2ecf20Sopenharmony_ci * AP bus poll thread. The purpose of this thread is to poll for
4738c2ecf20Sopenharmony_ci * finished requests in a loop if there is a "free" cpu - that is
4748c2ecf20Sopenharmony_ci * a cpu that doesn't have anything better to do. The polling stops
4758c2ecf20Sopenharmony_ci * as soon as there is another task or if all messages have been
4768c2ecf20Sopenharmony_ci * delivered.
4778c2ecf20Sopenharmony_ci */
4788c2ecf20Sopenharmony_cistatic int ap_poll_thread(void *data)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	set_user_nice(current, MAX_NICE);
4838c2ecf20Sopenharmony_ci	set_freezable();
4848c2ecf20Sopenharmony_ci	while (!kthread_should_stop()) {
4858c2ecf20Sopenharmony_ci		add_wait_queue(&ap_poll_wait, &wait);
4868c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
4878c2ecf20Sopenharmony_ci		if (!ap_pending_requests()) {
4888c2ecf20Sopenharmony_ci			schedule();
4898c2ecf20Sopenharmony_ci			try_to_freeze();
4908c2ecf20Sopenharmony_ci		}
4918c2ecf20Sopenharmony_ci		set_current_state(TASK_RUNNING);
4928c2ecf20Sopenharmony_ci		remove_wait_queue(&ap_poll_wait, &wait);
4938c2ecf20Sopenharmony_ci		if (need_resched()) {
4948c2ecf20Sopenharmony_ci			schedule();
4958c2ecf20Sopenharmony_ci			try_to_freeze();
4968c2ecf20Sopenharmony_ci			continue;
4978c2ecf20Sopenharmony_ci		}
4988c2ecf20Sopenharmony_ci		ap_tasklet_fn(0);
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	return 0;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic int ap_poll_thread_start(void)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	int rc;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	if (ap_irq_flag || ap_poll_kthread)
5098c2ecf20Sopenharmony_ci		return 0;
5108c2ecf20Sopenharmony_ci	mutex_lock(&ap_poll_thread_mutex);
5118c2ecf20Sopenharmony_ci	ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll");
5128c2ecf20Sopenharmony_ci	rc = PTR_ERR_OR_ZERO(ap_poll_kthread);
5138c2ecf20Sopenharmony_ci	if (rc)
5148c2ecf20Sopenharmony_ci		ap_poll_kthread = NULL;
5158c2ecf20Sopenharmony_ci	mutex_unlock(&ap_poll_thread_mutex);
5168c2ecf20Sopenharmony_ci	return rc;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic void ap_poll_thread_stop(void)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	if (!ap_poll_kthread)
5228c2ecf20Sopenharmony_ci		return;
5238c2ecf20Sopenharmony_ci	mutex_lock(&ap_poll_thread_mutex);
5248c2ecf20Sopenharmony_ci	kthread_stop(ap_poll_kthread);
5258c2ecf20Sopenharmony_ci	ap_poll_kthread = NULL;
5268c2ecf20Sopenharmony_ci	mutex_unlock(&ap_poll_thread_mutex);
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci#define is_card_dev(x) ((x)->parent == ap_root_device)
5308c2ecf20Sopenharmony_ci#define is_queue_dev(x) ((x)->parent != ap_root_device)
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci/**
5338c2ecf20Sopenharmony_ci * ap_bus_match()
5348c2ecf20Sopenharmony_ci * @dev: Pointer to device
5358c2ecf20Sopenharmony_ci * @drv: Pointer to device_driver
5368c2ecf20Sopenharmony_ci *
5378c2ecf20Sopenharmony_ci * AP bus driver registration/unregistration.
5388c2ecf20Sopenharmony_ci */
5398c2ecf20Sopenharmony_cistatic int ap_bus_match(struct device *dev, struct device_driver *drv)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	struct ap_driver *ap_drv = to_ap_drv(drv);
5428c2ecf20Sopenharmony_ci	struct ap_device_id *id;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	/*
5458c2ecf20Sopenharmony_ci	 * Compare device type of the device with the list of
5468c2ecf20Sopenharmony_ci	 * supported types of the device_driver.
5478c2ecf20Sopenharmony_ci	 */
5488c2ecf20Sopenharmony_ci	for (id = ap_drv->ids; id->match_flags; id++) {
5498c2ecf20Sopenharmony_ci		if (is_card_dev(dev) &&
5508c2ecf20Sopenharmony_ci		    id->match_flags & AP_DEVICE_ID_MATCH_CARD_TYPE &&
5518c2ecf20Sopenharmony_ci		    id->dev_type == to_ap_dev(dev)->device_type)
5528c2ecf20Sopenharmony_ci			return 1;
5538c2ecf20Sopenharmony_ci		if (is_queue_dev(dev) &&
5548c2ecf20Sopenharmony_ci		    id->match_flags & AP_DEVICE_ID_MATCH_QUEUE_TYPE &&
5558c2ecf20Sopenharmony_ci		    id->dev_type == to_ap_dev(dev)->device_type)
5568c2ecf20Sopenharmony_ci			return 1;
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci	return 0;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci/**
5628c2ecf20Sopenharmony_ci * ap_uevent(): Uevent function for AP devices.
5638c2ecf20Sopenharmony_ci * @dev: Pointer to device
5648c2ecf20Sopenharmony_ci * @env: Pointer to kobj_uevent_env
5658c2ecf20Sopenharmony_ci *
5668c2ecf20Sopenharmony_ci * It sets up a single environment variable DEV_TYPE which contains the
5678c2ecf20Sopenharmony_ci * hardware device type.
5688c2ecf20Sopenharmony_ci */
5698c2ecf20Sopenharmony_cistatic int ap_uevent(struct device *dev, struct kobj_uevent_env *env)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	struct ap_device *ap_dev = to_ap_dev(dev);
5728c2ecf20Sopenharmony_ci	int retval = 0;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	if (!ap_dev)
5758c2ecf20Sopenharmony_ci		return -ENODEV;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	/* Set up DEV_TYPE environment variable. */
5788c2ecf20Sopenharmony_ci	retval = add_uevent_var(env, "DEV_TYPE=%04X", ap_dev->device_type);
5798c2ecf20Sopenharmony_ci	if (retval)
5808c2ecf20Sopenharmony_ci		return retval;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* Add MODALIAS= */
5838c2ecf20Sopenharmony_ci	retval = add_uevent_var(env, "MODALIAS=ap:t%02X", ap_dev->device_type);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	return retval;
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic int __ap_queue_devices_with_id_unregister(struct device *dev, void *data)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	if (is_queue_dev(dev) &&
5918c2ecf20Sopenharmony_ci	    AP_QID_CARD(to_ap_queue(dev)->qid) == (int)(long) data)
5928c2ecf20Sopenharmony_ci		device_unregister(dev);
5938c2ecf20Sopenharmony_ci	return 0;
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic struct bus_type ap_bus_type = {
5978c2ecf20Sopenharmony_ci	.name = "ap",
5988c2ecf20Sopenharmony_ci	.match = &ap_bus_match,
5998c2ecf20Sopenharmony_ci	.uevent = &ap_uevent,
6008c2ecf20Sopenharmony_ci};
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic int __ap_revise_reserved(struct device *dev, void *dummy)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	int rc, card, queue, devres, drvres;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if (is_queue_dev(dev)) {
6078c2ecf20Sopenharmony_ci		card = AP_QID_CARD(to_ap_queue(dev)->qid);
6088c2ecf20Sopenharmony_ci		queue = AP_QID_QUEUE(to_ap_queue(dev)->qid);
6098c2ecf20Sopenharmony_ci		mutex_lock(&ap_perms_mutex);
6108c2ecf20Sopenharmony_ci		devres = test_bit_inv(card, ap_perms.apm)
6118c2ecf20Sopenharmony_ci			&& test_bit_inv(queue, ap_perms.aqm);
6128c2ecf20Sopenharmony_ci		mutex_unlock(&ap_perms_mutex);
6138c2ecf20Sopenharmony_ci		drvres = to_ap_drv(dev->driver)->flags
6148c2ecf20Sopenharmony_ci			& AP_DRIVER_FLAG_DEFAULT;
6158c2ecf20Sopenharmony_ci		if (!!devres != !!drvres) {
6168c2ecf20Sopenharmony_ci			AP_DBF_DBG("reprobing queue=%02x.%04x\n",
6178c2ecf20Sopenharmony_ci				   card, queue);
6188c2ecf20Sopenharmony_ci			rc = device_reprobe(dev);
6198c2ecf20Sopenharmony_ci		}
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	return 0;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic void ap_bus_revise_bindings(void)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	bus_for_each_dev(&ap_bus_type, NULL, NULL, __ap_revise_reserved);
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ciint ap_owned_by_def_drv(int card, int queue)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	int rc = 0;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	if (card < 0 || card >= AP_DEVICES || queue < 0 || queue >= AP_DOMAINS)
6358c2ecf20Sopenharmony_ci		return -EINVAL;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	mutex_lock(&ap_perms_mutex);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (test_bit_inv(card, ap_perms.apm)
6408c2ecf20Sopenharmony_ci	    && test_bit_inv(queue, ap_perms.aqm))
6418c2ecf20Sopenharmony_ci		rc = 1;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	mutex_unlock(&ap_perms_mutex);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	return rc;
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ap_owned_by_def_drv);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ciint ap_apqn_in_matrix_owned_by_def_drv(unsigned long *apm,
6508c2ecf20Sopenharmony_ci				       unsigned long *aqm)
6518c2ecf20Sopenharmony_ci{
6528c2ecf20Sopenharmony_ci	int card, queue, rc = 0;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	mutex_lock(&ap_perms_mutex);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	for (card = 0; !rc && card < AP_DEVICES; card++)
6578c2ecf20Sopenharmony_ci		if (test_bit_inv(card, apm) &&
6588c2ecf20Sopenharmony_ci		    test_bit_inv(card, ap_perms.apm))
6598c2ecf20Sopenharmony_ci			for (queue = 0; !rc && queue < AP_DOMAINS; queue++)
6608c2ecf20Sopenharmony_ci				if (test_bit_inv(queue, aqm) &&
6618c2ecf20Sopenharmony_ci				    test_bit_inv(queue, ap_perms.aqm))
6628c2ecf20Sopenharmony_ci					rc = 1;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	mutex_unlock(&ap_perms_mutex);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	return rc;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ap_apqn_in_matrix_owned_by_def_drv);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cistatic int ap_device_probe(struct device *dev)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	struct ap_device *ap_dev = to_ap_dev(dev);
6738c2ecf20Sopenharmony_ci	struct ap_driver *ap_drv = to_ap_drv(dev->driver);
6748c2ecf20Sopenharmony_ci	int card, queue, devres, drvres, rc = -ENODEV;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	if (!get_device(dev))
6778c2ecf20Sopenharmony_ci		return rc;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	if (is_queue_dev(dev)) {
6808c2ecf20Sopenharmony_ci		/*
6818c2ecf20Sopenharmony_ci		 * If the apqn is marked as reserved/used by ap bus and
6828c2ecf20Sopenharmony_ci		 * default drivers, only probe with drivers with the default
6838c2ecf20Sopenharmony_ci		 * flag set. If it is not marked, only probe with drivers
6848c2ecf20Sopenharmony_ci		 * with the default flag not set.
6858c2ecf20Sopenharmony_ci		 */
6868c2ecf20Sopenharmony_ci		card = AP_QID_CARD(to_ap_queue(dev)->qid);
6878c2ecf20Sopenharmony_ci		queue = AP_QID_QUEUE(to_ap_queue(dev)->qid);
6888c2ecf20Sopenharmony_ci		mutex_lock(&ap_perms_mutex);
6898c2ecf20Sopenharmony_ci		devres = test_bit_inv(card, ap_perms.apm)
6908c2ecf20Sopenharmony_ci			&& test_bit_inv(queue, ap_perms.aqm);
6918c2ecf20Sopenharmony_ci		mutex_unlock(&ap_perms_mutex);
6928c2ecf20Sopenharmony_ci		drvres = ap_drv->flags & AP_DRIVER_FLAG_DEFAULT;
6938c2ecf20Sopenharmony_ci		if (!!devres != !!drvres)
6948c2ecf20Sopenharmony_ci			goto out;
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/* Add queue/card to list of active queues/cards */
6988c2ecf20Sopenharmony_ci	spin_lock_bh(&ap_queues_lock);
6998c2ecf20Sopenharmony_ci	if (is_queue_dev(dev))
7008c2ecf20Sopenharmony_ci		hash_add(ap_queues, &to_ap_queue(dev)->hnode,
7018c2ecf20Sopenharmony_ci			 to_ap_queue(dev)->qid);
7028c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap_queues_lock);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	ap_dev->drv = ap_drv;
7058c2ecf20Sopenharmony_ci	rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	if (rc) {
7088c2ecf20Sopenharmony_ci		spin_lock_bh(&ap_queues_lock);
7098c2ecf20Sopenharmony_ci		if (is_queue_dev(dev))
7108c2ecf20Sopenharmony_ci			hash_del(&to_ap_queue(dev)->hnode);
7118c2ecf20Sopenharmony_ci		spin_unlock_bh(&ap_queues_lock);
7128c2ecf20Sopenharmony_ci		ap_dev->drv = NULL;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ciout:
7168c2ecf20Sopenharmony_ci	if (rc)
7178c2ecf20Sopenharmony_ci		put_device(dev);
7188c2ecf20Sopenharmony_ci	return rc;
7198c2ecf20Sopenharmony_ci}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_cistatic int ap_device_remove(struct device *dev)
7228c2ecf20Sopenharmony_ci{
7238c2ecf20Sopenharmony_ci	struct ap_device *ap_dev = to_ap_dev(dev);
7248c2ecf20Sopenharmony_ci	struct ap_driver *ap_drv = ap_dev->drv;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	/* prepare ap queue device removal */
7278c2ecf20Sopenharmony_ci	if (is_queue_dev(dev))
7288c2ecf20Sopenharmony_ci		ap_queue_prepare_remove(to_ap_queue(dev));
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	/* driver's chance to clean up gracefully */
7318c2ecf20Sopenharmony_ci	if (ap_drv->remove)
7328c2ecf20Sopenharmony_ci		ap_drv->remove(ap_dev);
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	/* now do the ap queue device remove */
7358c2ecf20Sopenharmony_ci	if (is_queue_dev(dev))
7368c2ecf20Sopenharmony_ci		ap_queue_remove(to_ap_queue(dev));
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	/* Remove queue/card from list of active queues/cards */
7398c2ecf20Sopenharmony_ci	spin_lock_bh(&ap_queues_lock);
7408c2ecf20Sopenharmony_ci	if (is_queue_dev(dev))
7418c2ecf20Sopenharmony_ci		hash_del(&to_ap_queue(dev)->hnode);
7428c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap_queues_lock);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	put_device(dev);
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	return 0;
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistruct ap_queue *ap_get_qdev(ap_qid_t qid)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	int bkt;
7528c2ecf20Sopenharmony_ci	struct ap_queue *aq;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	spin_lock_bh(&ap_queues_lock);
7558c2ecf20Sopenharmony_ci	hash_for_each(ap_queues, bkt, aq, hnode) {
7568c2ecf20Sopenharmony_ci		if (aq->qid == qid) {
7578c2ecf20Sopenharmony_ci			get_device(&aq->ap_dev.device);
7588c2ecf20Sopenharmony_ci			spin_unlock_bh(&ap_queues_lock);
7598c2ecf20Sopenharmony_ci			return aq;
7608c2ecf20Sopenharmony_ci		}
7618c2ecf20Sopenharmony_ci	}
7628c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap_queues_lock);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	return NULL;
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ap_get_qdev);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ciint ap_driver_register(struct ap_driver *ap_drv, struct module *owner,
7698c2ecf20Sopenharmony_ci		       char *name)
7708c2ecf20Sopenharmony_ci{
7718c2ecf20Sopenharmony_ci	struct device_driver *drv = &ap_drv->driver;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	drv->bus = &ap_bus_type;
7748c2ecf20Sopenharmony_ci	drv->probe = ap_device_probe;
7758c2ecf20Sopenharmony_ci	drv->remove = ap_device_remove;
7768c2ecf20Sopenharmony_ci	drv->owner = owner;
7778c2ecf20Sopenharmony_ci	drv->name = name;
7788c2ecf20Sopenharmony_ci	return driver_register(drv);
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ap_driver_register);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_civoid ap_driver_unregister(struct ap_driver *ap_drv)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	driver_unregister(&ap_drv->driver);
7858c2ecf20Sopenharmony_ci}
7868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ap_driver_unregister);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_civoid ap_bus_force_rescan(void)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	/* processing a asynchronous bus rescan */
7918c2ecf20Sopenharmony_ci	del_timer(&ap_config_timer);
7928c2ecf20Sopenharmony_ci	queue_work(system_long_wq, &ap_scan_work);
7938c2ecf20Sopenharmony_ci	flush_work(&ap_scan_work);
7948c2ecf20Sopenharmony_ci}
7958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ap_bus_force_rescan);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci/*
7988c2ecf20Sopenharmony_ci* A config change has happened, force an ap bus rescan.
7998c2ecf20Sopenharmony_ci*/
8008c2ecf20Sopenharmony_civoid ap_bus_cfg_chg(void)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	AP_DBF_DBG("%s config change, forcing bus rescan\n", __func__);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	ap_bus_force_rescan();
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci/*
8088c2ecf20Sopenharmony_ci * hex2bitmap() - parse hex mask string and set bitmap.
8098c2ecf20Sopenharmony_ci * Valid strings are "0x012345678" with at least one valid hex number.
8108c2ecf20Sopenharmony_ci * Rest of the bitmap to the right is padded with 0. No spaces allowed
8118c2ecf20Sopenharmony_ci * within the string, the leading 0x may be omitted.
8128c2ecf20Sopenharmony_ci * Returns the bitmask with exactly the bits set as given by the hex
8138c2ecf20Sopenharmony_ci * string (both in big endian order).
8148c2ecf20Sopenharmony_ci */
8158c2ecf20Sopenharmony_cistatic int hex2bitmap(const char *str, unsigned long *bitmap, int bits)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	int i, n, b;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	/* bits needs to be a multiple of 8 */
8208c2ecf20Sopenharmony_ci	if (bits & 0x07)
8218c2ecf20Sopenharmony_ci		return -EINVAL;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	if (str[0] == '0' && str[1] == 'x')
8248c2ecf20Sopenharmony_ci		str++;
8258c2ecf20Sopenharmony_ci	if (*str == 'x')
8268c2ecf20Sopenharmony_ci		str++;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	for (i = 0; isxdigit(*str) && i < bits; str++) {
8298c2ecf20Sopenharmony_ci		b = hex_to_bin(*str);
8308c2ecf20Sopenharmony_ci		for (n = 0; n < 4; n++)
8318c2ecf20Sopenharmony_ci			if (b & (0x08 >> n))
8328c2ecf20Sopenharmony_ci				set_bit_inv(i + n, bitmap);
8338c2ecf20Sopenharmony_ci		i += 4;
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (*str == '\n')
8378c2ecf20Sopenharmony_ci		str++;
8388c2ecf20Sopenharmony_ci	if (*str)
8398c2ecf20Sopenharmony_ci		return -EINVAL;
8408c2ecf20Sopenharmony_ci	return 0;
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci/*
8448c2ecf20Sopenharmony_ci * modify_bitmap() - parse bitmask argument and modify an existing
8458c2ecf20Sopenharmony_ci * bit mask accordingly. A concatenation (done with ',') of these
8468c2ecf20Sopenharmony_ci * terms is recognized:
8478c2ecf20Sopenharmony_ci *   +<bitnr>[-<bitnr>] or -<bitnr>[-<bitnr>]
8488c2ecf20Sopenharmony_ci * <bitnr> may be any valid number (hex, decimal or octal) in the range
8498c2ecf20Sopenharmony_ci * 0...bits-1; the leading + or - is required. Here are some examples:
8508c2ecf20Sopenharmony_ci *   +0-15,+32,-128,-0xFF
8518c2ecf20Sopenharmony_ci *   -0-255,+1-16,+0x128
8528c2ecf20Sopenharmony_ci *   +1,+2,+3,+4,-5,-7-10
8538c2ecf20Sopenharmony_ci * Returns the new bitmap after all changes have been applied. Every
8548c2ecf20Sopenharmony_ci * positive value in the string will set a bit and every negative value
8558c2ecf20Sopenharmony_ci * in the string will clear a bit. As a bit may be touched more than once,
8568c2ecf20Sopenharmony_ci * the last 'operation' wins:
8578c2ecf20Sopenharmony_ci * +0-255,-128 = first bits 0-255 will be set, then bit 128 will be
8588c2ecf20Sopenharmony_ci * cleared again. All other bits are unmodified.
8598c2ecf20Sopenharmony_ci */
8608c2ecf20Sopenharmony_cistatic int modify_bitmap(const char *str, unsigned long *bitmap, int bits)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	int a, i, z;
8638c2ecf20Sopenharmony_ci	char *np, sign;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	/* bits needs to be a multiple of 8 */
8668c2ecf20Sopenharmony_ci	if (bits & 0x07)
8678c2ecf20Sopenharmony_ci		return -EINVAL;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	while (*str) {
8708c2ecf20Sopenharmony_ci		sign = *str++;
8718c2ecf20Sopenharmony_ci		if (sign != '+' && sign != '-')
8728c2ecf20Sopenharmony_ci			return -EINVAL;
8738c2ecf20Sopenharmony_ci		a = z = simple_strtoul(str, &np, 0);
8748c2ecf20Sopenharmony_ci		if (str == np || a >= bits)
8758c2ecf20Sopenharmony_ci			return -EINVAL;
8768c2ecf20Sopenharmony_ci		str = np;
8778c2ecf20Sopenharmony_ci		if (*str == '-') {
8788c2ecf20Sopenharmony_ci			z = simple_strtoul(++str, &np, 0);
8798c2ecf20Sopenharmony_ci			if (str == np || a > z || z >= bits)
8808c2ecf20Sopenharmony_ci				return -EINVAL;
8818c2ecf20Sopenharmony_ci			str = np;
8828c2ecf20Sopenharmony_ci		}
8838c2ecf20Sopenharmony_ci		for (i = a; i <= z; i++)
8848c2ecf20Sopenharmony_ci			if (sign == '+')
8858c2ecf20Sopenharmony_ci				set_bit_inv(i, bitmap);
8868c2ecf20Sopenharmony_ci			else
8878c2ecf20Sopenharmony_ci				clear_bit_inv(i, bitmap);
8888c2ecf20Sopenharmony_ci		while (*str == ',' || *str == '\n')
8898c2ecf20Sopenharmony_ci			str++;
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	return 0;
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ciint ap_parse_mask_str(const char *str,
8968c2ecf20Sopenharmony_ci		      unsigned long *bitmap, int bits,
8978c2ecf20Sopenharmony_ci		      struct mutex *lock)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	unsigned long *newmap, size;
9008c2ecf20Sopenharmony_ci	int rc;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	/* bits needs to be a multiple of 8 */
9038c2ecf20Sopenharmony_ci	if (bits & 0x07)
9048c2ecf20Sopenharmony_ci		return -EINVAL;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	size = BITS_TO_LONGS(bits)*sizeof(unsigned long);
9078c2ecf20Sopenharmony_ci	newmap = kmalloc(size, GFP_KERNEL);
9088c2ecf20Sopenharmony_ci	if (!newmap)
9098c2ecf20Sopenharmony_ci		return -ENOMEM;
9108c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(lock)) {
9118c2ecf20Sopenharmony_ci		kfree(newmap);
9128c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	if (*str == '+' || *str == '-') {
9168c2ecf20Sopenharmony_ci		memcpy(newmap, bitmap, size);
9178c2ecf20Sopenharmony_ci		rc = modify_bitmap(str, newmap, bits);
9188c2ecf20Sopenharmony_ci	} else {
9198c2ecf20Sopenharmony_ci		memset(newmap, 0, size);
9208c2ecf20Sopenharmony_ci		rc = hex2bitmap(str, newmap, bits);
9218c2ecf20Sopenharmony_ci	}
9228c2ecf20Sopenharmony_ci	if (rc == 0)
9238c2ecf20Sopenharmony_ci		memcpy(bitmap, newmap, size);
9248c2ecf20Sopenharmony_ci	mutex_unlock(lock);
9258c2ecf20Sopenharmony_ci	kfree(newmap);
9268c2ecf20Sopenharmony_ci	return rc;
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ap_parse_mask_str);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci/*
9318c2ecf20Sopenharmony_ci * AP bus attributes.
9328c2ecf20Sopenharmony_ci */
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_cistatic ssize_t ap_domain_show(struct bus_type *bus, char *buf)
9358c2ecf20Sopenharmony_ci{
9368c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", ap_domain_index);
9378c2ecf20Sopenharmony_ci}
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_cistatic ssize_t ap_domain_store(struct bus_type *bus,
9408c2ecf20Sopenharmony_ci			       const char *buf, size_t count)
9418c2ecf20Sopenharmony_ci{
9428c2ecf20Sopenharmony_ci	int domain;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	if (sscanf(buf, "%i\n", &domain) != 1 ||
9458c2ecf20Sopenharmony_ci	    domain < 0 || domain > ap_max_domain_id ||
9468c2ecf20Sopenharmony_ci	    !test_bit_inv(domain, ap_perms.aqm))
9478c2ecf20Sopenharmony_ci		return -EINVAL;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	spin_lock_bh(&ap_domain_lock);
9508c2ecf20Sopenharmony_ci	ap_domain_index = domain;
9518c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap_domain_lock);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	AP_DBF_INFO("stored new default domain=%d\n", domain);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	return count;
9568c2ecf20Sopenharmony_ci}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_cistatic BUS_ATTR_RW(ap_domain);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_cistatic ssize_t ap_control_domain_mask_show(struct bus_type *bus, char *buf)
9618c2ecf20Sopenharmony_ci{
9628c2ecf20Sopenharmony_ci	if (!ap_qci_info)	/* QCI not supported */
9638c2ecf20Sopenharmony_ci		return scnprintf(buf, PAGE_SIZE, "not supported\n");
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE,
9668c2ecf20Sopenharmony_ci			 "0x%08x%08x%08x%08x%08x%08x%08x%08x\n",
9678c2ecf20Sopenharmony_ci			 ap_qci_info->adm[0], ap_qci_info->adm[1],
9688c2ecf20Sopenharmony_ci			 ap_qci_info->adm[2], ap_qci_info->adm[3],
9698c2ecf20Sopenharmony_ci			 ap_qci_info->adm[4], ap_qci_info->adm[5],
9708c2ecf20Sopenharmony_ci			 ap_qci_info->adm[6], ap_qci_info->adm[7]);
9718c2ecf20Sopenharmony_ci}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_cistatic BUS_ATTR_RO(ap_control_domain_mask);
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_cistatic ssize_t ap_usage_domain_mask_show(struct bus_type *bus, char *buf)
9768c2ecf20Sopenharmony_ci{
9778c2ecf20Sopenharmony_ci	if (!ap_qci_info)	/* QCI not supported */
9788c2ecf20Sopenharmony_ci		return scnprintf(buf, PAGE_SIZE, "not supported\n");
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE,
9818c2ecf20Sopenharmony_ci			 "0x%08x%08x%08x%08x%08x%08x%08x%08x\n",
9828c2ecf20Sopenharmony_ci			 ap_qci_info->aqm[0], ap_qci_info->aqm[1],
9838c2ecf20Sopenharmony_ci			 ap_qci_info->aqm[2], ap_qci_info->aqm[3],
9848c2ecf20Sopenharmony_ci			 ap_qci_info->aqm[4], ap_qci_info->aqm[5],
9858c2ecf20Sopenharmony_ci			 ap_qci_info->aqm[6], ap_qci_info->aqm[7]);
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_cistatic BUS_ATTR_RO(ap_usage_domain_mask);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_cistatic ssize_t ap_adapter_mask_show(struct bus_type *bus, char *buf)
9918c2ecf20Sopenharmony_ci{
9928c2ecf20Sopenharmony_ci	if (!ap_qci_info)	/* QCI not supported */
9938c2ecf20Sopenharmony_ci		return scnprintf(buf, PAGE_SIZE, "not supported\n");
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE,
9968c2ecf20Sopenharmony_ci			 "0x%08x%08x%08x%08x%08x%08x%08x%08x\n",
9978c2ecf20Sopenharmony_ci			 ap_qci_info->apm[0], ap_qci_info->apm[1],
9988c2ecf20Sopenharmony_ci			 ap_qci_info->apm[2], ap_qci_info->apm[3],
9998c2ecf20Sopenharmony_ci			 ap_qci_info->apm[4], ap_qci_info->apm[5],
10008c2ecf20Sopenharmony_ci			 ap_qci_info->apm[6], ap_qci_info->apm[7]);
10018c2ecf20Sopenharmony_ci}
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_cistatic BUS_ATTR_RO(ap_adapter_mask);
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_cistatic ssize_t ap_interrupts_show(struct bus_type *bus, char *buf)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n",
10088c2ecf20Sopenharmony_ci			 ap_irq_flag ? 1 : 0);
10098c2ecf20Sopenharmony_ci}
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_cistatic BUS_ATTR_RO(ap_interrupts);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_cistatic ssize_t config_time_show(struct bus_type *bus, char *buf)
10148c2ecf20Sopenharmony_ci{
10158c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", ap_config_time);
10168c2ecf20Sopenharmony_ci}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_cistatic ssize_t config_time_store(struct bus_type *bus,
10198c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
10208c2ecf20Sopenharmony_ci{
10218c2ecf20Sopenharmony_ci	int time;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	if (sscanf(buf, "%d\n", &time) != 1 || time < 5 || time > 120)
10248c2ecf20Sopenharmony_ci		return -EINVAL;
10258c2ecf20Sopenharmony_ci	ap_config_time = time;
10268c2ecf20Sopenharmony_ci	mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
10278c2ecf20Sopenharmony_ci	return count;
10288c2ecf20Sopenharmony_ci}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_cistatic BUS_ATTR_RW(config_time);
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_cistatic ssize_t poll_thread_show(struct bus_type *bus, char *buf)
10338c2ecf20Sopenharmony_ci{
10348c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", ap_poll_kthread ? 1 : 0);
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_cistatic ssize_t poll_thread_store(struct bus_type *bus,
10388c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
10398c2ecf20Sopenharmony_ci{
10408c2ecf20Sopenharmony_ci	int flag, rc;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	if (sscanf(buf, "%d\n", &flag) != 1)
10438c2ecf20Sopenharmony_ci		return -EINVAL;
10448c2ecf20Sopenharmony_ci	if (flag) {
10458c2ecf20Sopenharmony_ci		rc = ap_poll_thread_start();
10468c2ecf20Sopenharmony_ci		if (rc)
10478c2ecf20Sopenharmony_ci			count = rc;
10488c2ecf20Sopenharmony_ci	} else
10498c2ecf20Sopenharmony_ci		ap_poll_thread_stop();
10508c2ecf20Sopenharmony_ci	return count;
10518c2ecf20Sopenharmony_ci}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_cistatic BUS_ATTR_RW(poll_thread);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_cistatic ssize_t poll_timeout_show(struct bus_type *bus, char *buf)
10568c2ecf20Sopenharmony_ci{
10578c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%llu\n", poll_timeout);
10588c2ecf20Sopenharmony_ci}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_cistatic ssize_t poll_timeout_store(struct bus_type *bus, const char *buf,
10618c2ecf20Sopenharmony_ci				  size_t count)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	unsigned long long time;
10648c2ecf20Sopenharmony_ci	ktime_t hr_time;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	/* 120 seconds = maximum poll interval */
10678c2ecf20Sopenharmony_ci	if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 ||
10688c2ecf20Sopenharmony_ci	    time > 120000000000ULL)
10698c2ecf20Sopenharmony_ci		return -EINVAL;
10708c2ecf20Sopenharmony_ci	poll_timeout = time;
10718c2ecf20Sopenharmony_ci	hr_time = poll_timeout;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	spin_lock_bh(&ap_poll_timer_lock);
10748c2ecf20Sopenharmony_ci	hrtimer_cancel(&ap_poll_timer);
10758c2ecf20Sopenharmony_ci	hrtimer_set_expires(&ap_poll_timer, hr_time);
10768c2ecf20Sopenharmony_ci	hrtimer_start_expires(&ap_poll_timer, HRTIMER_MODE_ABS);
10778c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap_poll_timer_lock);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	return count;
10808c2ecf20Sopenharmony_ci}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_cistatic BUS_ATTR_RW(poll_timeout);
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_cistatic ssize_t ap_max_domain_id_show(struct bus_type *bus, char *buf)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", ap_max_domain_id);
10878c2ecf20Sopenharmony_ci}
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_cistatic BUS_ATTR_RO(ap_max_domain_id);
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_cistatic ssize_t ap_max_adapter_id_show(struct bus_type *bus, char *buf)
10928c2ecf20Sopenharmony_ci{
10938c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", ap_max_adapter_id);
10948c2ecf20Sopenharmony_ci}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_cistatic BUS_ATTR_RO(ap_max_adapter_id);
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_cistatic ssize_t apmask_show(struct bus_type *bus, char *buf)
10998c2ecf20Sopenharmony_ci{
11008c2ecf20Sopenharmony_ci	int rc;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&ap_perms_mutex))
11038c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
11048c2ecf20Sopenharmony_ci	rc = scnprintf(buf, PAGE_SIZE,
11058c2ecf20Sopenharmony_ci		       "0x%016lx%016lx%016lx%016lx\n",
11068c2ecf20Sopenharmony_ci		       ap_perms.apm[0], ap_perms.apm[1],
11078c2ecf20Sopenharmony_ci		       ap_perms.apm[2], ap_perms.apm[3]);
11088c2ecf20Sopenharmony_ci	mutex_unlock(&ap_perms_mutex);
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	return rc;
11118c2ecf20Sopenharmony_ci}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_cistatic ssize_t apmask_store(struct bus_type *bus, const char *buf,
11148c2ecf20Sopenharmony_ci			    size_t count)
11158c2ecf20Sopenharmony_ci{
11168c2ecf20Sopenharmony_ci	int rc;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	rc = ap_parse_mask_str(buf, ap_perms.apm, AP_DEVICES, &ap_perms_mutex);
11198c2ecf20Sopenharmony_ci	if (rc)
11208c2ecf20Sopenharmony_ci		return rc;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	ap_bus_revise_bindings();
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	return count;
11258c2ecf20Sopenharmony_ci}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_cistatic BUS_ATTR_RW(apmask);
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_cistatic ssize_t aqmask_show(struct bus_type *bus, char *buf)
11308c2ecf20Sopenharmony_ci{
11318c2ecf20Sopenharmony_ci	int rc;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&ap_perms_mutex))
11348c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
11358c2ecf20Sopenharmony_ci	rc = scnprintf(buf, PAGE_SIZE,
11368c2ecf20Sopenharmony_ci		       "0x%016lx%016lx%016lx%016lx\n",
11378c2ecf20Sopenharmony_ci		       ap_perms.aqm[0], ap_perms.aqm[1],
11388c2ecf20Sopenharmony_ci		       ap_perms.aqm[2], ap_perms.aqm[3]);
11398c2ecf20Sopenharmony_ci	mutex_unlock(&ap_perms_mutex);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	return rc;
11428c2ecf20Sopenharmony_ci}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_cistatic ssize_t aqmask_store(struct bus_type *bus, const char *buf,
11458c2ecf20Sopenharmony_ci			    size_t count)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	int rc;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	rc = ap_parse_mask_str(buf, ap_perms.aqm, AP_DOMAINS, &ap_perms_mutex);
11508c2ecf20Sopenharmony_ci	if (rc)
11518c2ecf20Sopenharmony_ci		return rc;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	ap_bus_revise_bindings();
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	return count;
11568c2ecf20Sopenharmony_ci}
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_cistatic BUS_ATTR_RW(aqmask);
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_cistatic struct bus_attribute *const ap_bus_attrs[] = {
11618c2ecf20Sopenharmony_ci	&bus_attr_ap_domain,
11628c2ecf20Sopenharmony_ci	&bus_attr_ap_control_domain_mask,
11638c2ecf20Sopenharmony_ci	&bus_attr_ap_usage_domain_mask,
11648c2ecf20Sopenharmony_ci	&bus_attr_ap_adapter_mask,
11658c2ecf20Sopenharmony_ci	&bus_attr_config_time,
11668c2ecf20Sopenharmony_ci	&bus_attr_poll_thread,
11678c2ecf20Sopenharmony_ci	&bus_attr_ap_interrupts,
11688c2ecf20Sopenharmony_ci	&bus_attr_poll_timeout,
11698c2ecf20Sopenharmony_ci	&bus_attr_ap_max_domain_id,
11708c2ecf20Sopenharmony_ci	&bus_attr_ap_max_adapter_id,
11718c2ecf20Sopenharmony_ci	&bus_attr_apmask,
11728c2ecf20Sopenharmony_ci	&bus_attr_aqmask,
11738c2ecf20Sopenharmony_ci	NULL,
11748c2ecf20Sopenharmony_ci};
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci/**
11778c2ecf20Sopenharmony_ci * ap_select_domain(): Select an AP domain if possible and we haven't
11788c2ecf20Sopenharmony_ci * already done so before.
11798c2ecf20Sopenharmony_ci */
11808c2ecf20Sopenharmony_cistatic void ap_select_domain(void)
11818c2ecf20Sopenharmony_ci{
11828c2ecf20Sopenharmony_ci	struct ap_queue_status status;
11838c2ecf20Sopenharmony_ci	int card, dom;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	/*
11868c2ecf20Sopenharmony_ci	 * Choose the default domain. Either the one specified with
11878c2ecf20Sopenharmony_ci	 * the "domain=" parameter or the first domain with at least
11888c2ecf20Sopenharmony_ci	 * one valid APQN.
11898c2ecf20Sopenharmony_ci	 */
11908c2ecf20Sopenharmony_ci	spin_lock_bh(&ap_domain_lock);
11918c2ecf20Sopenharmony_ci	if (ap_domain_index >= 0) {
11928c2ecf20Sopenharmony_ci		/* Domain has already been selected. */
11938c2ecf20Sopenharmony_ci		goto out;
11948c2ecf20Sopenharmony_ci	}
11958c2ecf20Sopenharmony_ci	for (dom = 0; dom <= ap_max_domain_id; dom++) {
11968c2ecf20Sopenharmony_ci		if (!ap_test_config_usage_domain(dom) ||
11978c2ecf20Sopenharmony_ci		    !test_bit_inv(dom, ap_perms.aqm))
11988c2ecf20Sopenharmony_ci			continue;
11998c2ecf20Sopenharmony_ci		for (card = 0; card <= ap_max_adapter_id; card++) {
12008c2ecf20Sopenharmony_ci			if (!ap_test_config_card_id(card) ||
12018c2ecf20Sopenharmony_ci			    !test_bit_inv(card, ap_perms.apm))
12028c2ecf20Sopenharmony_ci				continue;
12038c2ecf20Sopenharmony_ci			status = ap_test_queue(AP_MKQID(card, dom),
12048c2ecf20Sopenharmony_ci					       ap_apft_available(),
12058c2ecf20Sopenharmony_ci					       NULL);
12068c2ecf20Sopenharmony_ci			if (status.response_code == AP_RESPONSE_NORMAL)
12078c2ecf20Sopenharmony_ci				break;
12088c2ecf20Sopenharmony_ci		}
12098c2ecf20Sopenharmony_ci		if (card <= ap_max_adapter_id)
12108c2ecf20Sopenharmony_ci			break;
12118c2ecf20Sopenharmony_ci	}
12128c2ecf20Sopenharmony_ci	if (dom <= ap_max_domain_id) {
12138c2ecf20Sopenharmony_ci		ap_domain_index = dom;
12148c2ecf20Sopenharmony_ci		AP_DBF_INFO("%s new default domain is %d\n",
12158c2ecf20Sopenharmony_ci			    __func__, ap_domain_index);
12168c2ecf20Sopenharmony_ci	}
12178c2ecf20Sopenharmony_ciout:
12188c2ecf20Sopenharmony_ci	spin_unlock_bh(&ap_domain_lock);
12198c2ecf20Sopenharmony_ci}
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci/*
12228c2ecf20Sopenharmony_ci * This function checks the type and returns either 0 for not
12238c2ecf20Sopenharmony_ci * supported or the highest compatible type value (which may
12248c2ecf20Sopenharmony_ci * include the input type value).
12258c2ecf20Sopenharmony_ci */
12268c2ecf20Sopenharmony_cistatic int ap_get_compatible_type(ap_qid_t qid, int rawtype, unsigned int func)
12278c2ecf20Sopenharmony_ci{
12288c2ecf20Sopenharmony_ci	int comp_type = 0;
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	/* < CEX2A is not supported */
12318c2ecf20Sopenharmony_ci	if (rawtype < AP_DEVICE_TYPE_CEX2A) {
12328c2ecf20Sopenharmony_ci		AP_DBF_WARN("get_comp_type queue=%02x.%04x unsupported type %d\n",
12338c2ecf20Sopenharmony_ci			    AP_QID_CARD(qid), AP_QID_QUEUE(qid), rawtype);
12348c2ecf20Sopenharmony_ci		return 0;
12358c2ecf20Sopenharmony_ci	}
12368c2ecf20Sopenharmony_ci	/* up to CEX7 known and fully supported */
12378c2ecf20Sopenharmony_ci	if (rawtype <= AP_DEVICE_TYPE_CEX7)
12388c2ecf20Sopenharmony_ci		return rawtype;
12398c2ecf20Sopenharmony_ci	/*
12408c2ecf20Sopenharmony_ci	 * unknown new type > CEX7, check for compatibility
12418c2ecf20Sopenharmony_ci	 * to the highest known and supported type which is
12428c2ecf20Sopenharmony_ci	 * currently CEX7 with the help of the QACT function.
12438c2ecf20Sopenharmony_ci	 */
12448c2ecf20Sopenharmony_ci	if (ap_qact_available()) {
12458c2ecf20Sopenharmony_ci		struct ap_queue_status status;
12468c2ecf20Sopenharmony_ci		union ap_qact_ap_info apinfo = {0};
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci		apinfo.mode = (func >> 26) & 0x07;
12498c2ecf20Sopenharmony_ci		apinfo.cat = AP_DEVICE_TYPE_CEX7;
12508c2ecf20Sopenharmony_ci		status = ap_qact(qid, 0, &apinfo);
12518c2ecf20Sopenharmony_ci		if (status.response_code == AP_RESPONSE_NORMAL
12528c2ecf20Sopenharmony_ci		    && apinfo.cat >= AP_DEVICE_TYPE_CEX2A
12538c2ecf20Sopenharmony_ci		    && apinfo.cat <= AP_DEVICE_TYPE_CEX7)
12548c2ecf20Sopenharmony_ci			comp_type = apinfo.cat;
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci	if (!comp_type)
12578c2ecf20Sopenharmony_ci		AP_DBF_WARN("get_comp_type queue=%02x.%04x unable to map type %d\n",
12588c2ecf20Sopenharmony_ci			    AP_QID_CARD(qid), AP_QID_QUEUE(qid), rawtype);
12598c2ecf20Sopenharmony_ci	else if (comp_type != rawtype)
12608c2ecf20Sopenharmony_ci		AP_DBF_INFO("get_comp_type queue=%02x.%04x map type %d to %d\n",
12618c2ecf20Sopenharmony_ci			    AP_QID_CARD(qid), AP_QID_QUEUE(qid),
12628c2ecf20Sopenharmony_ci			    rawtype, comp_type);
12638c2ecf20Sopenharmony_ci	return comp_type;
12648c2ecf20Sopenharmony_ci}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci/*
12678c2ecf20Sopenharmony_ci * Helper function to be used with bus_find_dev
12688c2ecf20Sopenharmony_ci * matches for the card device with the given id
12698c2ecf20Sopenharmony_ci */
12708c2ecf20Sopenharmony_cistatic int __match_card_device_with_id(struct device *dev, const void *data)
12718c2ecf20Sopenharmony_ci{
12728c2ecf20Sopenharmony_ci	return is_card_dev(dev) && to_ap_card(dev)->id == (int)(long)(void *) data;
12738c2ecf20Sopenharmony_ci}
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci/*
12768c2ecf20Sopenharmony_ci * Helper function to be used with bus_find_dev
12778c2ecf20Sopenharmony_ci * matches for the queue device with a given qid
12788c2ecf20Sopenharmony_ci */
12798c2ecf20Sopenharmony_cistatic int __match_queue_device_with_qid(struct device *dev, const void *data)
12808c2ecf20Sopenharmony_ci{
12818c2ecf20Sopenharmony_ci	return is_queue_dev(dev) && to_ap_queue(dev)->qid == (int)(long) data;
12828c2ecf20Sopenharmony_ci}
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci/*
12858c2ecf20Sopenharmony_ci * Helper function to be used with bus_find_dev
12868c2ecf20Sopenharmony_ci * matches any queue device with given queue id
12878c2ecf20Sopenharmony_ci */
12888c2ecf20Sopenharmony_cistatic int __match_queue_device_with_queue_id(struct device *dev, const void *data)
12898c2ecf20Sopenharmony_ci{
12908c2ecf20Sopenharmony_ci	return is_queue_dev(dev)
12918c2ecf20Sopenharmony_ci		&& AP_QID_QUEUE(to_ap_queue(dev)->qid) == (int)(long) data;
12928c2ecf20Sopenharmony_ci}
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci/*
12958c2ecf20Sopenharmony_ci * Helper function for ap_scan_bus().
12968c2ecf20Sopenharmony_ci * Remove card device and associated queue devices.
12978c2ecf20Sopenharmony_ci */
12988c2ecf20Sopenharmony_cistatic inline void ap_scan_rm_card_dev_and_queue_devs(struct ap_card *ac)
12998c2ecf20Sopenharmony_ci{
13008c2ecf20Sopenharmony_ci	bus_for_each_dev(&ap_bus_type, NULL,
13018c2ecf20Sopenharmony_ci			 (void *)(long) ac->id,
13028c2ecf20Sopenharmony_ci			 __ap_queue_devices_with_id_unregister);
13038c2ecf20Sopenharmony_ci	device_unregister(&ac->ap_dev.device);
13048c2ecf20Sopenharmony_ci}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci/*
13078c2ecf20Sopenharmony_ci * Helper function for ap_scan_bus().
13088c2ecf20Sopenharmony_ci * Does the scan bus job for all the domains within
13098c2ecf20Sopenharmony_ci * a valid adapter given by an ap_card ptr.
13108c2ecf20Sopenharmony_ci */
13118c2ecf20Sopenharmony_cistatic inline void ap_scan_domains(struct ap_card *ac)
13128c2ecf20Sopenharmony_ci{
13138c2ecf20Sopenharmony_ci	bool decfg;
13148c2ecf20Sopenharmony_ci	ap_qid_t qid;
13158c2ecf20Sopenharmony_ci	unsigned int func;
13168c2ecf20Sopenharmony_ci	struct device *dev;
13178c2ecf20Sopenharmony_ci	struct ap_queue *aq;
13188c2ecf20Sopenharmony_ci	int rc, dom, depth, type;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	/*
13218c2ecf20Sopenharmony_ci	 * Go through the configuration for the domains and compare them
13228c2ecf20Sopenharmony_ci	 * to the existing queue devices. Also take care of the config
13238c2ecf20Sopenharmony_ci	 * and error state for the queue devices.
13248c2ecf20Sopenharmony_ci	 */
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	for (dom = 0; dom <= ap_max_domain_id; dom++) {
13278c2ecf20Sopenharmony_ci		qid = AP_MKQID(ac->id, dom);
13288c2ecf20Sopenharmony_ci		dev = bus_find_device(&ap_bus_type, NULL,
13298c2ecf20Sopenharmony_ci				      (void *)(long) qid,
13308c2ecf20Sopenharmony_ci				      __match_queue_device_with_qid);
13318c2ecf20Sopenharmony_ci		aq = dev ? to_ap_queue(dev) : NULL;
13328c2ecf20Sopenharmony_ci		if (!ap_test_config_usage_domain(dom)) {
13338c2ecf20Sopenharmony_ci			if (dev) {
13348c2ecf20Sopenharmony_ci				AP_DBF_INFO("%s(%d,%d) not in config any more, rm queue device\n",
13358c2ecf20Sopenharmony_ci					    __func__, ac->id, dom);
13368c2ecf20Sopenharmony_ci				device_unregister(dev);
13378c2ecf20Sopenharmony_ci				put_device(dev);
13388c2ecf20Sopenharmony_ci			}
13398c2ecf20Sopenharmony_ci			continue;
13408c2ecf20Sopenharmony_ci		}
13418c2ecf20Sopenharmony_ci		/* domain is valid, get info from this APQN */
13428c2ecf20Sopenharmony_ci		if (!ap_queue_info(qid, &type, &func, &depth, &decfg)) {
13438c2ecf20Sopenharmony_ci			if (aq) {
13448c2ecf20Sopenharmony_ci				AP_DBF_INFO(
13458c2ecf20Sopenharmony_ci					"%s(%d,%d) ap_queue_info() not successful, rm queue device\n",
13468c2ecf20Sopenharmony_ci					__func__, ac->id, dom);
13478c2ecf20Sopenharmony_ci				device_unregister(dev);
13488c2ecf20Sopenharmony_ci				put_device(dev);
13498c2ecf20Sopenharmony_ci			}
13508c2ecf20Sopenharmony_ci			continue;
13518c2ecf20Sopenharmony_ci		}
13528c2ecf20Sopenharmony_ci		/* if no queue device exists, create a new one */
13538c2ecf20Sopenharmony_ci		if (!aq) {
13548c2ecf20Sopenharmony_ci			aq = ap_queue_create(qid, ac->ap_dev.device_type);
13558c2ecf20Sopenharmony_ci			if (!aq) {
13568c2ecf20Sopenharmony_ci				AP_DBF_WARN("%s(%d,%d) ap_queue_create() failed\n",
13578c2ecf20Sopenharmony_ci					    __func__, ac->id, dom);
13588c2ecf20Sopenharmony_ci				continue;
13598c2ecf20Sopenharmony_ci			}
13608c2ecf20Sopenharmony_ci			aq->card = ac;
13618c2ecf20Sopenharmony_ci			aq->config = !decfg;
13628c2ecf20Sopenharmony_ci			dev = &aq->ap_dev.device;
13638c2ecf20Sopenharmony_ci			dev->bus = &ap_bus_type;
13648c2ecf20Sopenharmony_ci			dev->parent = &ac->ap_dev.device;
13658c2ecf20Sopenharmony_ci			dev_set_name(dev, "%02x.%04x", ac->id, dom);
13668c2ecf20Sopenharmony_ci			/* register queue device */
13678c2ecf20Sopenharmony_ci			rc = device_register(dev);
13688c2ecf20Sopenharmony_ci			if (rc) {
13698c2ecf20Sopenharmony_ci				AP_DBF_WARN("%s(%d,%d) device_register() failed\n",
13708c2ecf20Sopenharmony_ci					    __func__, ac->id, dom);
13718c2ecf20Sopenharmony_ci				goto put_dev_and_continue;
13728c2ecf20Sopenharmony_ci			}
13738c2ecf20Sopenharmony_ci			/* get it and thus adjust reference counter */
13748c2ecf20Sopenharmony_ci			get_device(dev);
13758c2ecf20Sopenharmony_ci			if (decfg)
13768c2ecf20Sopenharmony_ci				AP_DBF_INFO("%s(%d,%d) new (decfg) queue device created\n",
13778c2ecf20Sopenharmony_ci					    __func__, ac->id, dom);
13788c2ecf20Sopenharmony_ci			else
13798c2ecf20Sopenharmony_ci				AP_DBF_INFO("%s(%d,%d) new queue device created\n",
13808c2ecf20Sopenharmony_ci					    __func__, ac->id, dom);
13818c2ecf20Sopenharmony_ci			goto put_dev_and_continue;
13828c2ecf20Sopenharmony_ci		}
13838c2ecf20Sopenharmony_ci		/* Check config state on the already existing queue device */
13848c2ecf20Sopenharmony_ci		spin_lock_bh(&aq->lock);
13858c2ecf20Sopenharmony_ci		if (decfg && aq->config) {
13868c2ecf20Sopenharmony_ci			/* config off this queue device */
13878c2ecf20Sopenharmony_ci			aq->config = false;
13888c2ecf20Sopenharmony_ci			if (aq->dev_state > AP_DEV_STATE_UNINITIATED) {
13898c2ecf20Sopenharmony_ci				aq->dev_state = AP_DEV_STATE_ERROR;
13908c2ecf20Sopenharmony_ci				aq->last_err_rc = AP_RESPONSE_DECONFIGURED;
13918c2ecf20Sopenharmony_ci			}
13928c2ecf20Sopenharmony_ci			spin_unlock_bh(&aq->lock);
13938c2ecf20Sopenharmony_ci			AP_DBF_INFO("%s(%d,%d) queue device config off\n",
13948c2ecf20Sopenharmony_ci				    __func__, ac->id, dom);
13958c2ecf20Sopenharmony_ci			/* 'receive' pending messages with -EAGAIN */
13968c2ecf20Sopenharmony_ci			ap_flush_queue(aq);
13978c2ecf20Sopenharmony_ci			goto put_dev_and_continue;
13988c2ecf20Sopenharmony_ci		}
13998c2ecf20Sopenharmony_ci		if (!decfg && !aq->config) {
14008c2ecf20Sopenharmony_ci			/* config on this queue device */
14018c2ecf20Sopenharmony_ci			aq->config = true;
14028c2ecf20Sopenharmony_ci			if (aq->dev_state > AP_DEV_STATE_UNINITIATED) {
14038c2ecf20Sopenharmony_ci				aq->dev_state = AP_DEV_STATE_OPERATING;
14048c2ecf20Sopenharmony_ci				aq->sm_state = AP_SM_STATE_RESET_START;
14058c2ecf20Sopenharmony_ci			}
14068c2ecf20Sopenharmony_ci			spin_unlock_bh(&aq->lock);
14078c2ecf20Sopenharmony_ci			AP_DBF_INFO("%s(%d,%d) queue device config on\n",
14088c2ecf20Sopenharmony_ci				    __func__, ac->id, dom);
14098c2ecf20Sopenharmony_ci			goto put_dev_and_continue;
14108c2ecf20Sopenharmony_ci		}
14118c2ecf20Sopenharmony_ci		/* handle other error states */
14128c2ecf20Sopenharmony_ci		if (!decfg && aq->dev_state == AP_DEV_STATE_ERROR) {
14138c2ecf20Sopenharmony_ci			spin_unlock_bh(&aq->lock);
14148c2ecf20Sopenharmony_ci			/* 'receive' pending messages with -EAGAIN */
14158c2ecf20Sopenharmony_ci			ap_flush_queue(aq);
14168c2ecf20Sopenharmony_ci			/* re-init (with reset) the queue device */
14178c2ecf20Sopenharmony_ci			ap_queue_init_state(aq);
14188c2ecf20Sopenharmony_ci			AP_DBF_INFO("%s(%d,%d) queue device reinit enforced\n",
14198c2ecf20Sopenharmony_ci				    __func__, ac->id, dom);
14208c2ecf20Sopenharmony_ci			goto put_dev_and_continue;
14218c2ecf20Sopenharmony_ci		}
14228c2ecf20Sopenharmony_ci		spin_unlock_bh(&aq->lock);
14238c2ecf20Sopenharmony_ciput_dev_and_continue:
14248c2ecf20Sopenharmony_ci		put_device(dev);
14258c2ecf20Sopenharmony_ci	}
14268c2ecf20Sopenharmony_ci}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci/*
14298c2ecf20Sopenharmony_ci * Helper function for ap_scan_bus().
14308c2ecf20Sopenharmony_ci * Does the scan bus job for the given adapter id.
14318c2ecf20Sopenharmony_ci */
14328c2ecf20Sopenharmony_cistatic inline void ap_scan_adapter(int ap)
14338c2ecf20Sopenharmony_ci{
14348c2ecf20Sopenharmony_ci	bool decfg;
14358c2ecf20Sopenharmony_ci	ap_qid_t qid;
14368c2ecf20Sopenharmony_ci	unsigned int func;
14378c2ecf20Sopenharmony_ci	struct device *dev;
14388c2ecf20Sopenharmony_ci	struct ap_card *ac;
14398c2ecf20Sopenharmony_ci	int rc, dom, depth, type, comp_type;
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	/* Is there currently a card device for this adapter ? */
14428c2ecf20Sopenharmony_ci	dev = bus_find_device(&ap_bus_type, NULL,
14438c2ecf20Sopenharmony_ci			      (void *)(long) ap,
14448c2ecf20Sopenharmony_ci			      __match_card_device_with_id);
14458c2ecf20Sopenharmony_ci	ac = dev ? to_ap_card(dev) : NULL;
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	/* Adapter not in configuration ? */
14488c2ecf20Sopenharmony_ci	if (!ap_test_config_card_id(ap)) {
14498c2ecf20Sopenharmony_ci		if (ac) {
14508c2ecf20Sopenharmony_ci			AP_DBF_INFO("%s(%d) ap not in config any more, rm card and queue devices\n",
14518c2ecf20Sopenharmony_ci				    __func__, ap);
14528c2ecf20Sopenharmony_ci			ap_scan_rm_card_dev_and_queue_devs(ac);
14538c2ecf20Sopenharmony_ci			put_device(dev);
14548c2ecf20Sopenharmony_ci		}
14558c2ecf20Sopenharmony_ci		return;
14568c2ecf20Sopenharmony_ci	}
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	/*
14598c2ecf20Sopenharmony_ci	 * Adapter ap is valid in the current configuration. So do some checks:
14608c2ecf20Sopenharmony_ci	 * If no card device exists, build one. If a card device exists, check
14618c2ecf20Sopenharmony_ci	 * for type and functions changed. For all this we need to find a valid
14628c2ecf20Sopenharmony_ci	 * APQN first.
14638c2ecf20Sopenharmony_ci	 */
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	for (dom = 0; dom <= ap_max_domain_id; dom++)
14668c2ecf20Sopenharmony_ci		if (ap_test_config_usage_domain(dom)) {
14678c2ecf20Sopenharmony_ci			qid = AP_MKQID(ap, dom);
14688c2ecf20Sopenharmony_ci			if (ap_queue_info(qid, &type, &func, &depth, &decfg))
14698c2ecf20Sopenharmony_ci				break;
14708c2ecf20Sopenharmony_ci		}
14718c2ecf20Sopenharmony_ci	if (dom > ap_max_domain_id) {
14728c2ecf20Sopenharmony_ci		/* Could not find a valid APQN for this adapter */
14738c2ecf20Sopenharmony_ci		if (ac) {
14748c2ecf20Sopenharmony_ci			AP_DBF_INFO(
14758c2ecf20Sopenharmony_ci				"%s(%d) no type info (no APQN found), rm card and queue devices\n",
14768c2ecf20Sopenharmony_ci				__func__, ap);
14778c2ecf20Sopenharmony_ci			ap_scan_rm_card_dev_and_queue_devs(ac);
14788c2ecf20Sopenharmony_ci			put_device(dev);
14798c2ecf20Sopenharmony_ci		} else {
14808c2ecf20Sopenharmony_ci			AP_DBF_DBG("%s(%d) no type info (no APQN found), ignored\n",
14818c2ecf20Sopenharmony_ci				   __func__, ap);
14828c2ecf20Sopenharmony_ci		}
14838c2ecf20Sopenharmony_ci		return;
14848c2ecf20Sopenharmony_ci	}
14858c2ecf20Sopenharmony_ci	if (!type) {
14868c2ecf20Sopenharmony_ci		/* No apdater type info available, an unusable adapter */
14878c2ecf20Sopenharmony_ci		if (ac) {
14888c2ecf20Sopenharmony_ci			AP_DBF_INFO("%s(%d) no valid type (0) info, rm card and queue devices\n",
14898c2ecf20Sopenharmony_ci				    __func__, ap);
14908c2ecf20Sopenharmony_ci			ap_scan_rm_card_dev_and_queue_devs(ac);
14918c2ecf20Sopenharmony_ci			put_device(dev);
14928c2ecf20Sopenharmony_ci		} else {
14938c2ecf20Sopenharmony_ci			AP_DBF_DBG("%s(%d) no valid type (0) info, ignored\n",
14948c2ecf20Sopenharmony_ci				   __func__, ap);
14958c2ecf20Sopenharmony_ci		}
14968c2ecf20Sopenharmony_ci		return;
14978c2ecf20Sopenharmony_ci	}
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	if (ac) {
15008c2ecf20Sopenharmony_ci		/* Check APQN against existing card device for changes */
15018c2ecf20Sopenharmony_ci		if (ac->raw_hwtype != type) {
15028c2ecf20Sopenharmony_ci			AP_DBF_INFO("%s(%d) hwtype %d changed, rm card and queue devices\n",
15038c2ecf20Sopenharmony_ci				    __func__, ap, type);
15048c2ecf20Sopenharmony_ci			ap_scan_rm_card_dev_and_queue_devs(ac);
15058c2ecf20Sopenharmony_ci			put_device(dev);
15068c2ecf20Sopenharmony_ci			ac = NULL;
15078c2ecf20Sopenharmony_ci		} else if (ac->functions != func) {
15088c2ecf20Sopenharmony_ci			AP_DBF_INFO("%s(%d) functions 0x%08x changed, rm card and queue devices\n",
15098c2ecf20Sopenharmony_ci				    __func__, ap, type);
15108c2ecf20Sopenharmony_ci			ap_scan_rm_card_dev_and_queue_devs(ac);
15118c2ecf20Sopenharmony_ci			put_device(dev);
15128c2ecf20Sopenharmony_ci			ac = NULL;
15138c2ecf20Sopenharmony_ci		} else {
15148c2ecf20Sopenharmony_ci			if (decfg && ac->config) {
15158c2ecf20Sopenharmony_ci				ac->config = false;
15168c2ecf20Sopenharmony_ci				AP_DBF_INFO("%s(%d) card device config off\n",
15178c2ecf20Sopenharmony_ci					    __func__, ap);
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci			}
15208c2ecf20Sopenharmony_ci			if (!decfg && !ac->config) {
15218c2ecf20Sopenharmony_ci				ac->config = true;
15228c2ecf20Sopenharmony_ci				AP_DBF_INFO("%s(%d) card device config on\n",
15238c2ecf20Sopenharmony_ci					    __func__, ap);
15248c2ecf20Sopenharmony_ci			}
15258c2ecf20Sopenharmony_ci		}
15268c2ecf20Sopenharmony_ci	}
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	if (!ac) {
15298c2ecf20Sopenharmony_ci		/* Build a new card device */
15308c2ecf20Sopenharmony_ci		comp_type = ap_get_compatible_type(qid, type, func);
15318c2ecf20Sopenharmony_ci		if (!comp_type) {
15328c2ecf20Sopenharmony_ci			AP_DBF_WARN("%s(%d) type %d, can't get compatibility type\n",
15338c2ecf20Sopenharmony_ci				    __func__, ap, type);
15348c2ecf20Sopenharmony_ci			return;
15358c2ecf20Sopenharmony_ci		}
15368c2ecf20Sopenharmony_ci		ac = ap_card_create(ap, depth, type, comp_type, func);
15378c2ecf20Sopenharmony_ci		if (!ac) {
15388c2ecf20Sopenharmony_ci			AP_DBF_WARN("%s(%d) ap_card_create() failed\n",
15398c2ecf20Sopenharmony_ci				    __func__, ap);
15408c2ecf20Sopenharmony_ci			return;
15418c2ecf20Sopenharmony_ci		}
15428c2ecf20Sopenharmony_ci		ac->config = !decfg;
15438c2ecf20Sopenharmony_ci		dev = &ac->ap_dev.device;
15448c2ecf20Sopenharmony_ci		dev->bus = &ap_bus_type;
15458c2ecf20Sopenharmony_ci		dev->parent = ap_root_device;
15468c2ecf20Sopenharmony_ci		dev_set_name(dev, "card%02x", ap);
15478c2ecf20Sopenharmony_ci		/* Register the new card device with AP bus */
15488c2ecf20Sopenharmony_ci		rc = device_register(dev);
15498c2ecf20Sopenharmony_ci		if (rc) {
15508c2ecf20Sopenharmony_ci			AP_DBF_WARN("%s(%d) device_register() failed\n",
15518c2ecf20Sopenharmony_ci				    __func__, ap);
15528c2ecf20Sopenharmony_ci			put_device(dev);
15538c2ecf20Sopenharmony_ci			return;
15548c2ecf20Sopenharmony_ci		}
15558c2ecf20Sopenharmony_ci		/* get it and thus adjust reference counter */
15568c2ecf20Sopenharmony_ci		get_device(dev);
15578c2ecf20Sopenharmony_ci		if (decfg)
15588c2ecf20Sopenharmony_ci			AP_DBF_INFO("%s(%d) new (decfg) card device type=%d func=0x%08x created\n",
15598c2ecf20Sopenharmony_ci				    __func__, ap, type, func);
15608c2ecf20Sopenharmony_ci		else
15618c2ecf20Sopenharmony_ci			AP_DBF_INFO("%s(%d) new card device type=%d func=0x%08x created\n",
15628c2ecf20Sopenharmony_ci				    __func__, ap, type, func);
15638c2ecf20Sopenharmony_ci	}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	/* Verify the domains and the queue devices for this card */
15668c2ecf20Sopenharmony_ci	ap_scan_domains(ac);
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	/* release the card device */
15698c2ecf20Sopenharmony_ci	put_device(&ac->ap_dev.device);
15708c2ecf20Sopenharmony_ci}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci/**
15738c2ecf20Sopenharmony_ci * ap_scan_bus(): Scan the AP bus for new devices
15748c2ecf20Sopenharmony_ci * Runs periodically, workqueue timer (ap_config_time)
15758c2ecf20Sopenharmony_ci */
15768c2ecf20Sopenharmony_cistatic void ap_scan_bus(struct work_struct *unused)
15778c2ecf20Sopenharmony_ci{
15788c2ecf20Sopenharmony_ci	int ap;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	ap_fetch_qci_info(ap_qci_info);
15818c2ecf20Sopenharmony_ci	ap_select_domain();
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	AP_DBF_DBG("%s running\n", __func__);
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	/* loop over all possible adapters */
15868c2ecf20Sopenharmony_ci	for (ap = 0; ap <= ap_max_adapter_id; ap++)
15878c2ecf20Sopenharmony_ci		ap_scan_adapter(ap);
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci	/* check if there is at least one queue available with default domain */
15908c2ecf20Sopenharmony_ci	if (ap_domain_index >= 0) {
15918c2ecf20Sopenharmony_ci		struct device *dev =
15928c2ecf20Sopenharmony_ci			bus_find_device(&ap_bus_type, NULL,
15938c2ecf20Sopenharmony_ci					(void *)(long) ap_domain_index,
15948c2ecf20Sopenharmony_ci					__match_queue_device_with_queue_id);
15958c2ecf20Sopenharmony_ci		if (dev)
15968c2ecf20Sopenharmony_ci			put_device(dev);
15978c2ecf20Sopenharmony_ci		else
15988c2ecf20Sopenharmony_ci			AP_DBF_INFO("no queue device with default domain %d available\n",
15998c2ecf20Sopenharmony_ci				    ap_domain_index);
16008c2ecf20Sopenharmony_ci	}
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci	mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
16038c2ecf20Sopenharmony_ci}
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_cistatic void ap_config_timeout(struct timer_list *unused)
16068c2ecf20Sopenharmony_ci{
16078c2ecf20Sopenharmony_ci	queue_work(system_long_wq, &ap_scan_work);
16088c2ecf20Sopenharmony_ci}
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_cistatic int __init ap_debug_init(void)
16118c2ecf20Sopenharmony_ci{
16128c2ecf20Sopenharmony_ci	ap_dbf_info = debug_register("ap", 1, 1,
16138c2ecf20Sopenharmony_ci				     DBF_MAX_SPRINTF_ARGS * sizeof(long));
16148c2ecf20Sopenharmony_ci	debug_register_view(ap_dbf_info, &debug_sprintf_view);
16158c2ecf20Sopenharmony_ci	debug_set_level(ap_dbf_info, DBF_ERR);
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	return 0;
16188c2ecf20Sopenharmony_ci}
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_cistatic void __init ap_perms_init(void)
16218c2ecf20Sopenharmony_ci{
16228c2ecf20Sopenharmony_ci	/* all resources useable if no kernel parameter string given */
16238c2ecf20Sopenharmony_ci	memset(&ap_perms.ioctlm, 0xFF, sizeof(ap_perms.ioctlm));
16248c2ecf20Sopenharmony_ci	memset(&ap_perms.apm, 0xFF, sizeof(ap_perms.apm));
16258c2ecf20Sopenharmony_ci	memset(&ap_perms.aqm, 0xFF, sizeof(ap_perms.aqm));
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	/* apm kernel parameter string */
16288c2ecf20Sopenharmony_ci	if (apm_str) {
16298c2ecf20Sopenharmony_ci		memset(&ap_perms.apm, 0, sizeof(ap_perms.apm));
16308c2ecf20Sopenharmony_ci		ap_parse_mask_str(apm_str, ap_perms.apm, AP_DEVICES,
16318c2ecf20Sopenharmony_ci				  &ap_perms_mutex);
16328c2ecf20Sopenharmony_ci	}
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	/* aqm kernel parameter string */
16358c2ecf20Sopenharmony_ci	if (aqm_str) {
16368c2ecf20Sopenharmony_ci		memset(&ap_perms.aqm, 0, sizeof(ap_perms.aqm));
16378c2ecf20Sopenharmony_ci		ap_parse_mask_str(aqm_str, ap_perms.aqm, AP_DOMAINS,
16388c2ecf20Sopenharmony_ci				  &ap_perms_mutex);
16398c2ecf20Sopenharmony_ci	}
16408c2ecf20Sopenharmony_ci}
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci/**
16438c2ecf20Sopenharmony_ci * ap_module_init(): The module initialization code.
16448c2ecf20Sopenharmony_ci *
16458c2ecf20Sopenharmony_ci * Initializes the module.
16468c2ecf20Sopenharmony_ci */
16478c2ecf20Sopenharmony_cistatic int __init ap_module_init(void)
16488c2ecf20Sopenharmony_ci{
16498c2ecf20Sopenharmony_ci	int rc, i;
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	rc = ap_debug_init();
16528c2ecf20Sopenharmony_ci	if (rc)
16538c2ecf20Sopenharmony_ci		return rc;
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	if (!ap_instructions_available()) {
16568c2ecf20Sopenharmony_ci		pr_warn("The hardware system does not support AP instructions\n");
16578c2ecf20Sopenharmony_ci		return -ENODEV;
16588c2ecf20Sopenharmony_ci	}
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	/* init ap_queue hashtable */
16618c2ecf20Sopenharmony_ci	hash_init(ap_queues);
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	/* set up the AP permissions (ioctls, ap and aq masks) */
16648c2ecf20Sopenharmony_ci	ap_perms_init();
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci	/* Get AP configuration data if available */
16678c2ecf20Sopenharmony_ci	ap_init_qci_info();
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	/* check default domain setting */
16708c2ecf20Sopenharmony_ci	if (ap_domain_index < -1 || ap_domain_index > ap_max_domain_id ||
16718c2ecf20Sopenharmony_ci	    (ap_domain_index >= 0 &&
16728c2ecf20Sopenharmony_ci	     !test_bit_inv(ap_domain_index, ap_perms.aqm))) {
16738c2ecf20Sopenharmony_ci		pr_warn("%d is not a valid cryptographic domain\n",
16748c2ecf20Sopenharmony_ci			ap_domain_index);
16758c2ecf20Sopenharmony_ci		ap_domain_index = -1;
16768c2ecf20Sopenharmony_ci	}
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	/* enable interrupts if available */
16798c2ecf20Sopenharmony_ci	if (ap_interrupts_available()) {
16808c2ecf20Sopenharmony_ci		rc = register_adapter_interrupt(&ap_airq);
16818c2ecf20Sopenharmony_ci		ap_irq_flag = (rc == 0);
16828c2ecf20Sopenharmony_ci	}
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	/* Create /sys/bus/ap. */
16858c2ecf20Sopenharmony_ci	rc = bus_register(&ap_bus_type);
16868c2ecf20Sopenharmony_ci	if (rc)
16878c2ecf20Sopenharmony_ci		goto out;
16888c2ecf20Sopenharmony_ci	for (i = 0; ap_bus_attrs[i]; i++) {
16898c2ecf20Sopenharmony_ci		rc = bus_create_file(&ap_bus_type, ap_bus_attrs[i]);
16908c2ecf20Sopenharmony_ci		if (rc)
16918c2ecf20Sopenharmony_ci			goto out_bus;
16928c2ecf20Sopenharmony_ci	}
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci	/* Create /sys/devices/ap. */
16958c2ecf20Sopenharmony_ci	ap_root_device = root_device_register("ap");
16968c2ecf20Sopenharmony_ci	rc = PTR_ERR_OR_ZERO(ap_root_device);
16978c2ecf20Sopenharmony_ci	if (rc)
16988c2ecf20Sopenharmony_ci		goto out_bus;
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	/* Setup the AP bus rescan timer. */
17018c2ecf20Sopenharmony_ci	timer_setup(&ap_config_timer, ap_config_timeout, 0);
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	/*
17048c2ecf20Sopenharmony_ci	 * Setup the high resultion poll timer.
17058c2ecf20Sopenharmony_ci	 * If we are running under z/VM adjust polling to z/VM polling rate.
17068c2ecf20Sopenharmony_ci	 */
17078c2ecf20Sopenharmony_ci	if (MACHINE_IS_VM)
17088c2ecf20Sopenharmony_ci		poll_timeout = 1500000;
17098c2ecf20Sopenharmony_ci	hrtimer_init(&ap_poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
17108c2ecf20Sopenharmony_ci	ap_poll_timer.function = ap_poll_timeout;
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	/* Start the low priority AP bus poll thread. */
17138c2ecf20Sopenharmony_ci	if (ap_thread_flag) {
17148c2ecf20Sopenharmony_ci		rc = ap_poll_thread_start();
17158c2ecf20Sopenharmony_ci		if (rc)
17168c2ecf20Sopenharmony_ci			goto out_work;
17178c2ecf20Sopenharmony_ci	}
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	queue_work(system_long_wq, &ap_scan_work);
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	return 0;
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ciout_work:
17248c2ecf20Sopenharmony_ci	hrtimer_cancel(&ap_poll_timer);
17258c2ecf20Sopenharmony_ci	root_device_unregister(ap_root_device);
17268c2ecf20Sopenharmony_ciout_bus:
17278c2ecf20Sopenharmony_ci	while (i--)
17288c2ecf20Sopenharmony_ci		bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
17298c2ecf20Sopenharmony_ci	bus_unregister(&ap_bus_type);
17308c2ecf20Sopenharmony_ciout:
17318c2ecf20Sopenharmony_ci	if (ap_irq_flag)
17328c2ecf20Sopenharmony_ci		unregister_adapter_interrupt(&ap_airq);
17338c2ecf20Sopenharmony_ci	kfree(ap_qci_info);
17348c2ecf20Sopenharmony_ci	return rc;
17358c2ecf20Sopenharmony_ci}
17368c2ecf20Sopenharmony_cidevice_initcall(ap_module_init);
1737