1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2f5daba1dSHeiko Carstens /* 3f5daba1dSHeiko Carstens * Channel report handling code 4f5daba1dSHeiko Carstens * 5f5daba1dSHeiko Carstens * Copyright IBM Corp. 2000, 2009 6f5daba1dSHeiko Carstens * Author(s): Ingo Adlung <adlung@de.ibm.com>, 7f5daba1dSHeiko Carstens * Martin Schwidefsky <schwidefsky@de.ibm.com>, 8f5daba1dSHeiko Carstens * Cornelia Huck <cornelia.huck@de.ibm.com>, 9f5daba1dSHeiko Carstens * Heiko Carstens <heiko.carstens@de.ibm.com>, 10f5daba1dSHeiko Carstens */ 11f5daba1dSHeiko Carstens 1298c1c682SHeiko Carstens #include <linux/mutex.h> 13f5daba1dSHeiko Carstens #include <linux/kthread.h> 14f5daba1dSHeiko Carstens #include <linux/init.h> 15b4563e89SSebastian Ott #include <linux/wait.h> 16f5daba1dSHeiko Carstens #include <asm/crw.h> 17a0616cdeSDavid Howells #include <asm/ctl_reg.h> 182ab59de7SPeter Oberparleiter #include "ioasm.h" 19f5daba1dSHeiko Carstens 2098c1c682SHeiko Carstens static DEFINE_MUTEX(crw_handler_mutex); 21f5daba1dSHeiko Carstens static crw_handler_t crw_handlers[NR_RSCS]; 22b4563e89SSebastian Ott static atomic_t crw_nr_req = ATOMIC_INIT(0); 23b4563e89SSebastian Ott static DECLARE_WAIT_QUEUE_HEAD(crw_handler_wait_q); 24f5daba1dSHeiko Carstens 25f5daba1dSHeiko Carstens /** 26f5daba1dSHeiko Carstens * crw_register_handler() - register a channel report word handler 27f5daba1dSHeiko Carstens * @rsc: reporting source code to handle 28f5daba1dSHeiko Carstens * @handler: handler to be registered 29f5daba1dSHeiko Carstens * 30f5daba1dSHeiko Carstens * Returns %0 on success and a negative error value otherwise. 31f5daba1dSHeiko Carstens */ 32f5daba1dSHeiko Carstens int crw_register_handler(int rsc, crw_handler_t handler) 33f5daba1dSHeiko Carstens { 3498c1c682SHeiko Carstens int rc = 0; 3598c1c682SHeiko Carstens 36f5daba1dSHeiko Carstens if ((rsc < 0) || (rsc >= NR_RSCS)) 37f5daba1dSHeiko Carstens return -EINVAL; 3898c1c682SHeiko Carstens mutex_lock(&crw_handler_mutex); 3998c1c682SHeiko Carstens if (crw_handlers[rsc]) 4098c1c682SHeiko Carstens rc = -EBUSY; 4198c1c682SHeiko Carstens else 4298c1c682SHeiko Carstens crw_handlers[rsc] = handler; 4398c1c682SHeiko Carstens mutex_unlock(&crw_handler_mutex); 4498c1c682SHeiko Carstens return rc; 45f5daba1dSHeiko Carstens } 46f5daba1dSHeiko Carstens 47f5daba1dSHeiko Carstens /** 48f5daba1dSHeiko Carstens * crw_unregister_handler() - unregister a channel report word handler 49f5daba1dSHeiko Carstens * @rsc: reporting source code to handle 50f5daba1dSHeiko Carstens */ 51f5daba1dSHeiko Carstens void crw_unregister_handler(int rsc) 52f5daba1dSHeiko Carstens { 53f5daba1dSHeiko Carstens if ((rsc < 0) || (rsc >= NR_RSCS)) 54f5daba1dSHeiko Carstens return; 5598c1c682SHeiko Carstens mutex_lock(&crw_handler_mutex); 5698c1c682SHeiko Carstens crw_handlers[rsc] = NULL; 5798c1c682SHeiko Carstens mutex_unlock(&crw_handler_mutex); 58f5daba1dSHeiko Carstens } 59f5daba1dSHeiko Carstens 60f5daba1dSHeiko Carstens /* 61f5daba1dSHeiko Carstens * Retrieve CRWs and call function to handle event. 62f5daba1dSHeiko Carstens */ 63f5daba1dSHeiko Carstens static int crw_collect_info(void *unused) 64f5daba1dSHeiko Carstens { 65f5daba1dSHeiko Carstens struct crw crw[2]; 66b4563e89SSebastian Ott int ccode, signal; 67f5daba1dSHeiko Carstens unsigned int chain; 68f5daba1dSHeiko Carstens 69f5daba1dSHeiko Carstens repeat: 70b4563e89SSebastian Ott signal = wait_event_interruptible(crw_handler_wait_q, 71b4563e89SSebastian Ott atomic_read(&crw_nr_req) > 0); 72b4563e89SSebastian Ott if (unlikely(signal)) 73b4563e89SSebastian Ott atomic_inc(&crw_nr_req); 74f5daba1dSHeiko Carstens chain = 0; 75f5daba1dSHeiko Carstens while (1) { 7698c1c682SHeiko Carstens crw_handler_t handler; 7798c1c682SHeiko Carstens 78f5daba1dSHeiko Carstens if (unlikely(chain > 1)) { 79f5daba1dSHeiko Carstens struct crw tmp_crw; 80f5daba1dSHeiko Carstens 81f5daba1dSHeiko Carstens printk(KERN_WARNING"%s: Code does not support more " 82f5daba1dSHeiko Carstens "than two chained crws; please report to " 83f5daba1dSHeiko Carstens "linux390@de.ibm.com!\n", __func__); 84f5daba1dSHeiko Carstens ccode = stcrw(&tmp_crw); 85f5daba1dSHeiko Carstens printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, " 86f5daba1dSHeiko Carstens "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", 87f5daba1dSHeiko Carstens __func__, tmp_crw.slct, tmp_crw.oflw, 88f5daba1dSHeiko Carstens tmp_crw.chn, tmp_crw.rsc, tmp_crw.anc, 89f5daba1dSHeiko Carstens tmp_crw.erc, tmp_crw.rsid); 90f5daba1dSHeiko Carstens printk(KERN_WARNING"%s: This was crw number %x in the " 91f5daba1dSHeiko Carstens "chain\n", __func__, chain); 92f5daba1dSHeiko Carstens if (ccode != 0) 93f5daba1dSHeiko Carstens break; 94f5daba1dSHeiko Carstens chain = tmp_crw.chn ? chain + 1 : 0; 95f5daba1dSHeiko Carstens continue; 96f5daba1dSHeiko Carstens } 97f5daba1dSHeiko Carstens ccode = stcrw(&crw[chain]); 98f5daba1dSHeiko Carstens if (ccode != 0) 99f5daba1dSHeiko Carstens break; 100f5daba1dSHeiko Carstens printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, " 101f5daba1dSHeiko Carstens "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", 102f5daba1dSHeiko Carstens crw[chain].slct, crw[chain].oflw, crw[chain].chn, 103f5daba1dSHeiko Carstens crw[chain].rsc, crw[chain].anc, crw[chain].erc, 104f5daba1dSHeiko Carstens crw[chain].rsid); 105f5daba1dSHeiko Carstens /* Check for overflows. */ 106f5daba1dSHeiko Carstens if (crw[chain].oflw) { 107f5daba1dSHeiko Carstens int i; 108f5daba1dSHeiko Carstens 109f5daba1dSHeiko Carstens pr_debug("%s: crw overflow detected!\n", __func__); 11098c1c682SHeiko Carstens mutex_lock(&crw_handler_mutex); 111f5daba1dSHeiko Carstens for (i = 0; i < NR_RSCS; i++) { 112f5daba1dSHeiko Carstens if (crw_handlers[i]) 113f5daba1dSHeiko Carstens crw_handlers[i](NULL, NULL, 1); 114f5daba1dSHeiko Carstens } 11598c1c682SHeiko Carstens mutex_unlock(&crw_handler_mutex); 116f5daba1dSHeiko Carstens chain = 0; 117f5daba1dSHeiko Carstens continue; 118f5daba1dSHeiko Carstens } 119f5daba1dSHeiko Carstens if (crw[0].chn && !chain) { 120f5daba1dSHeiko Carstens chain++; 121f5daba1dSHeiko Carstens continue; 122f5daba1dSHeiko Carstens } 12398c1c682SHeiko Carstens mutex_lock(&crw_handler_mutex); 12498c1c682SHeiko Carstens handler = crw_handlers[crw[chain].rsc]; 12598c1c682SHeiko Carstens if (handler) 12698c1c682SHeiko Carstens handler(&crw[0], chain ? &crw[1] : NULL, 0); 12798c1c682SHeiko Carstens mutex_unlock(&crw_handler_mutex); 128f5daba1dSHeiko Carstens /* chain is always 0 or 1 here. */ 129f5daba1dSHeiko Carstens chain = crw[chain].chn ? chain + 1 : 0; 130f5daba1dSHeiko Carstens } 131b4563e89SSebastian Ott if (atomic_dec_and_test(&crw_nr_req)) 132b4563e89SSebastian Ott wake_up(&crw_handler_wait_q); 133f5daba1dSHeiko Carstens goto repeat; 134f5daba1dSHeiko Carstens return 0; 135f5daba1dSHeiko Carstens } 136f5daba1dSHeiko Carstens 137f5daba1dSHeiko Carstens void crw_handle_channel_report(void) 138f5daba1dSHeiko Carstens { 139b4563e89SSebastian Ott atomic_inc(&crw_nr_req); 140b4563e89SSebastian Ott wake_up(&crw_handler_wait_q); 141f5daba1dSHeiko Carstens } 142f5daba1dSHeiko Carstens 143b4563e89SSebastian Ott void crw_wait_for_channel_report(void) 144f5daba1dSHeiko Carstens { 145b4563e89SSebastian Ott crw_handle_channel_report(); 146b4563e89SSebastian Ott wait_event(crw_handler_wait_q, atomic_read(&crw_nr_req) == 0); 147f5daba1dSHeiko Carstens } 148f5daba1dSHeiko Carstens 149f5daba1dSHeiko Carstens /* 150f5daba1dSHeiko Carstens * Machine checks for the channel subsystem must be enabled 151f5daba1dSHeiko Carstens * after the channel subsystem is initialized 152f5daba1dSHeiko Carstens */ 153f5daba1dSHeiko Carstens static int __init crw_machine_check_init(void) 154f5daba1dSHeiko Carstens { 155f5daba1dSHeiko Carstens struct task_struct *task; 156f5daba1dSHeiko Carstens 157f5daba1dSHeiko Carstens task = kthread_run(crw_collect_info, NULL, "kmcheck"); 158f5daba1dSHeiko Carstens if (IS_ERR(task)) 159f5daba1dSHeiko Carstens return PTR_ERR(task); 160f5daba1dSHeiko Carstens ctl_set_bit(14, 28); /* enable channel report MCH */ 161f5daba1dSHeiko Carstens return 0; 162f5daba1dSHeiko Carstens } 163f5daba1dSHeiko Carstens device_initcall(crw_machine_check_init); 164