18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *   Channel report handling code
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *    Copyright IBM Corp. 2000, 2009
68c2ecf20Sopenharmony_ci *    Author(s): Ingo Adlung <adlung@de.ibm.com>,
78c2ecf20Sopenharmony_ci *		 Martin Schwidefsky <schwidefsky@de.ibm.com>,
88c2ecf20Sopenharmony_ci *		 Cornelia Huck <cornelia.huck@de.ibm.com>,
98c2ecf20Sopenharmony_ci *		 Heiko Carstens <heiko.carstens@de.ibm.com>,
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/mutex.h>
138c2ecf20Sopenharmony_ci#include <linux/kthread.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/wait.h>
168c2ecf20Sopenharmony_ci#include <asm/crw.h>
178c2ecf20Sopenharmony_ci#include <asm/ctl_reg.h>
188c2ecf20Sopenharmony_ci#include "ioasm.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(crw_handler_mutex);
218c2ecf20Sopenharmony_cistatic crw_handler_t crw_handlers[NR_RSCS];
228c2ecf20Sopenharmony_cistatic atomic_t crw_nr_req = ATOMIC_INIT(0);
238c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(crw_handler_wait_q);
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/**
268c2ecf20Sopenharmony_ci * crw_register_handler() - register a channel report word handler
278c2ecf20Sopenharmony_ci * @rsc: reporting source code to handle
288c2ecf20Sopenharmony_ci * @handler: handler to be registered
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * Returns %0 on success and a negative error value otherwise.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ciint crw_register_handler(int rsc, crw_handler_t handler)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	int rc = 0;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if ((rsc < 0) || (rsc >= NR_RSCS))
378c2ecf20Sopenharmony_ci		return -EINVAL;
388c2ecf20Sopenharmony_ci	mutex_lock(&crw_handler_mutex);
398c2ecf20Sopenharmony_ci	if (crw_handlers[rsc])
408c2ecf20Sopenharmony_ci		rc = -EBUSY;
418c2ecf20Sopenharmony_ci	else
428c2ecf20Sopenharmony_ci		crw_handlers[rsc] = handler;
438c2ecf20Sopenharmony_ci	mutex_unlock(&crw_handler_mutex);
448c2ecf20Sopenharmony_ci	return rc;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/**
488c2ecf20Sopenharmony_ci * crw_unregister_handler() - unregister a channel report word handler
498c2ecf20Sopenharmony_ci * @rsc: reporting source code to handle
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_civoid crw_unregister_handler(int rsc)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	if ((rsc < 0) || (rsc >= NR_RSCS))
548c2ecf20Sopenharmony_ci		return;
558c2ecf20Sopenharmony_ci	mutex_lock(&crw_handler_mutex);
568c2ecf20Sopenharmony_ci	crw_handlers[rsc] = NULL;
578c2ecf20Sopenharmony_ci	mutex_unlock(&crw_handler_mutex);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/*
618c2ecf20Sopenharmony_ci * Retrieve CRWs and call function to handle event.
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_cistatic int crw_collect_info(void *unused)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct crw crw[2];
668c2ecf20Sopenharmony_ci	int ccode, signal;
678c2ecf20Sopenharmony_ci	unsigned int chain;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cirepeat:
708c2ecf20Sopenharmony_ci	signal = wait_event_interruptible(crw_handler_wait_q,
718c2ecf20Sopenharmony_ci					  atomic_read(&crw_nr_req) > 0);
728c2ecf20Sopenharmony_ci	if (unlikely(signal))
738c2ecf20Sopenharmony_ci		atomic_inc(&crw_nr_req);
748c2ecf20Sopenharmony_ci	chain = 0;
758c2ecf20Sopenharmony_ci	while (1) {
768c2ecf20Sopenharmony_ci		crw_handler_t handler;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci		if (unlikely(chain > 1)) {
798c2ecf20Sopenharmony_ci			struct crw tmp_crw;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci			printk(KERN_WARNING"%s: Code does not support more "
828c2ecf20Sopenharmony_ci			       "than two chained crws; please report to "
838c2ecf20Sopenharmony_ci			       "linux390@de.ibm.com!\n", __func__);
848c2ecf20Sopenharmony_ci			ccode = stcrw(&tmp_crw);
858c2ecf20Sopenharmony_ci			printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, "
868c2ecf20Sopenharmony_ci			       "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
878c2ecf20Sopenharmony_ci			       __func__, tmp_crw.slct, tmp_crw.oflw,
888c2ecf20Sopenharmony_ci			       tmp_crw.chn, tmp_crw.rsc, tmp_crw.anc,
898c2ecf20Sopenharmony_ci			       tmp_crw.erc, tmp_crw.rsid);
908c2ecf20Sopenharmony_ci			printk(KERN_WARNING"%s: This was crw number %x in the "
918c2ecf20Sopenharmony_ci			       "chain\n", __func__, chain);
928c2ecf20Sopenharmony_ci			if (ccode != 0)
938c2ecf20Sopenharmony_ci				break;
948c2ecf20Sopenharmony_ci			chain = tmp_crw.chn ? chain + 1 : 0;
958c2ecf20Sopenharmony_ci			continue;
968c2ecf20Sopenharmony_ci		}
978c2ecf20Sopenharmony_ci		ccode = stcrw(&crw[chain]);
988c2ecf20Sopenharmony_ci		if (ccode != 0)
998c2ecf20Sopenharmony_ci			break;
1008c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, "
1018c2ecf20Sopenharmony_ci		       "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
1028c2ecf20Sopenharmony_ci		       crw[chain].slct, crw[chain].oflw, crw[chain].chn,
1038c2ecf20Sopenharmony_ci		       crw[chain].rsc, crw[chain].anc, crw[chain].erc,
1048c2ecf20Sopenharmony_ci		       crw[chain].rsid);
1058c2ecf20Sopenharmony_ci		/* Check for overflows. */
1068c2ecf20Sopenharmony_ci		if (crw[chain].oflw) {
1078c2ecf20Sopenharmony_ci			int i;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci			pr_debug("%s: crw overflow detected!\n", __func__);
1108c2ecf20Sopenharmony_ci			mutex_lock(&crw_handler_mutex);
1118c2ecf20Sopenharmony_ci			for (i = 0; i < NR_RSCS; i++) {
1128c2ecf20Sopenharmony_ci				if (crw_handlers[i])
1138c2ecf20Sopenharmony_ci					crw_handlers[i](NULL, NULL, 1);
1148c2ecf20Sopenharmony_ci			}
1158c2ecf20Sopenharmony_ci			mutex_unlock(&crw_handler_mutex);
1168c2ecf20Sopenharmony_ci			chain = 0;
1178c2ecf20Sopenharmony_ci			continue;
1188c2ecf20Sopenharmony_ci		}
1198c2ecf20Sopenharmony_ci		if (crw[0].chn && !chain) {
1208c2ecf20Sopenharmony_ci			chain++;
1218c2ecf20Sopenharmony_ci			continue;
1228c2ecf20Sopenharmony_ci		}
1238c2ecf20Sopenharmony_ci		mutex_lock(&crw_handler_mutex);
1248c2ecf20Sopenharmony_ci		handler = crw_handlers[crw[chain].rsc];
1258c2ecf20Sopenharmony_ci		if (handler)
1268c2ecf20Sopenharmony_ci			handler(&crw[0], chain ? &crw[1] : NULL, 0);
1278c2ecf20Sopenharmony_ci		mutex_unlock(&crw_handler_mutex);
1288c2ecf20Sopenharmony_ci		/* chain is always 0 or 1 here. */
1298c2ecf20Sopenharmony_ci		chain = crw[chain].chn ? chain + 1 : 0;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&crw_nr_req))
1328c2ecf20Sopenharmony_ci		wake_up(&crw_handler_wait_q);
1338c2ecf20Sopenharmony_ci	goto repeat;
1348c2ecf20Sopenharmony_ci	return 0;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_civoid crw_handle_channel_report(void)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	atomic_inc(&crw_nr_req);
1408c2ecf20Sopenharmony_ci	wake_up(&crw_handler_wait_q);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_civoid crw_wait_for_channel_report(void)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	crw_handle_channel_report();
1468c2ecf20Sopenharmony_ci	wait_event(crw_handler_wait_q, atomic_read(&crw_nr_req) == 0);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/*
1508c2ecf20Sopenharmony_ci * Machine checks for the channel subsystem must be enabled
1518c2ecf20Sopenharmony_ci * after the channel subsystem is initialized
1528c2ecf20Sopenharmony_ci */
1538c2ecf20Sopenharmony_cistatic int __init crw_machine_check_init(void)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct task_struct *task;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	task = kthread_run(crw_collect_info, NULL, "kmcheck");
1588c2ecf20Sopenharmony_ci	if (IS_ERR(task))
1598c2ecf20Sopenharmony_ci		return PTR_ERR(task);
1608c2ecf20Sopenharmony_ci	ctl_set_bit(14, 28);	/* enable channel report MCH */
1618c2ecf20Sopenharmony_ci	return 0;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_cidevice_initcall(crw_machine_check_init);
164