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