18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * core function to access sclp interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 1999, 2009 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author(s): Martin Peschke <mpeschke@de.ibm.com> 88c2ecf20Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/timer.h> 178c2ecf20Sopenharmony_ci#include <linux/reboot.h> 188c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/suspend.h> 218c2ecf20Sopenharmony_ci#include <linux/completion.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <asm/types.h> 248c2ecf20Sopenharmony_ci#include <asm/irq.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "sclp.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define SCLP_HEADER "sclp: " 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* Lock to protect internal data consistency. */ 318c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sclp_lock); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* Mask of events that we can send to the sclp interface. */ 348c2ecf20Sopenharmony_cistatic sccb_mask_t sclp_receive_mask; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* Mask of events that we can receive from the sclp interface. */ 378c2ecf20Sopenharmony_cistatic sccb_mask_t sclp_send_mask; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* List of registered event listeners and senders. */ 408c2ecf20Sopenharmony_cistatic struct list_head sclp_reg_list; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* List of queued requests. */ 438c2ecf20Sopenharmony_cistatic struct list_head sclp_req_queue; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Data for read and and init requests. */ 468c2ecf20Sopenharmony_cistatic struct sclp_req sclp_read_req; 478c2ecf20Sopenharmony_cistatic struct sclp_req sclp_init_req; 488c2ecf20Sopenharmony_cistatic void *sclp_read_sccb; 498c2ecf20Sopenharmony_cistatic struct init_sccb *sclp_init_sccb; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Suspend request */ 528c2ecf20Sopenharmony_cistatic DECLARE_COMPLETION(sclp_request_queue_flushed); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Number of console pages to allocate, used by sclp_con.c and sclp_vt220.c */ 558c2ecf20Sopenharmony_ciint sclp_console_pages = SCLP_CONSOLE_PAGES; 568c2ecf20Sopenharmony_ci/* Flag to indicate if buffer pages are dropped on buffer full condition */ 578c2ecf20Sopenharmony_ciint sclp_console_drop = 1; 588c2ecf20Sopenharmony_ci/* Number of times the console dropped buffer pages */ 598c2ecf20Sopenharmony_ciunsigned long sclp_console_full; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void sclp_suspend_req_cb(struct sclp_req *req, void *data) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci complete(&sclp_request_queue_flushed); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int __init sclp_setup_console_pages(char *str) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci int pages, rc; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci rc = kstrtoint(str, 0, &pages); 718c2ecf20Sopenharmony_ci if (!rc && pages >= SCLP_CONSOLE_PAGES) 728c2ecf20Sopenharmony_ci sclp_console_pages = pages; 738c2ecf20Sopenharmony_ci return 1; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci__setup("sclp_con_pages=", sclp_setup_console_pages); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int __init sclp_setup_console_drop(char *str) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci int drop, rc; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci rc = kstrtoint(str, 0, &drop); 838c2ecf20Sopenharmony_ci if (!rc) 848c2ecf20Sopenharmony_ci sclp_console_drop = drop; 858c2ecf20Sopenharmony_ci return 1; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci__setup("sclp_con_drop=", sclp_setup_console_drop); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic struct sclp_req sclp_suspend_req; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* Timer for request retries. */ 938c2ecf20Sopenharmony_cistatic struct timer_list sclp_request_timer; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* Timer for queued requests. */ 968c2ecf20Sopenharmony_cistatic struct timer_list sclp_queue_timer; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Internal state: is a request active at the sclp? */ 998c2ecf20Sopenharmony_cistatic volatile enum sclp_running_state_t { 1008c2ecf20Sopenharmony_ci sclp_running_state_idle, 1018c2ecf20Sopenharmony_ci sclp_running_state_running, 1028c2ecf20Sopenharmony_ci sclp_running_state_reset_pending 1038c2ecf20Sopenharmony_ci} sclp_running_state = sclp_running_state_idle; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* Internal state: is a read request pending? */ 1068c2ecf20Sopenharmony_cistatic volatile enum sclp_reading_state_t { 1078c2ecf20Sopenharmony_ci sclp_reading_state_idle, 1088c2ecf20Sopenharmony_ci sclp_reading_state_reading 1098c2ecf20Sopenharmony_ci} sclp_reading_state = sclp_reading_state_idle; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* Internal state: is the driver currently serving requests? */ 1128c2ecf20Sopenharmony_cistatic volatile enum sclp_activation_state_t { 1138c2ecf20Sopenharmony_ci sclp_activation_state_active, 1148c2ecf20Sopenharmony_ci sclp_activation_state_deactivating, 1158c2ecf20Sopenharmony_ci sclp_activation_state_inactive, 1168c2ecf20Sopenharmony_ci sclp_activation_state_activating 1178c2ecf20Sopenharmony_ci} sclp_activation_state = sclp_activation_state_active; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* Internal state: is an init mask request pending? */ 1208c2ecf20Sopenharmony_cistatic volatile enum sclp_mask_state_t { 1218c2ecf20Sopenharmony_ci sclp_mask_state_idle, 1228c2ecf20Sopenharmony_ci sclp_mask_state_initializing 1238c2ecf20Sopenharmony_ci} sclp_mask_state = sclp_mask_state_idle; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* Internal state: is the driver suspended? */ 1268c2ecf20Sopenharmony_cistatic enum sclp_suspend_state_t { 1278c2ecf20Sopenharmony_ci sclp_suspend_state_running, 1288c2ecf20Sopenharmony_ci sclp_suspend_state_suspended, 1298c2ecf20Sopenharmony_ci} sclp_suspend_state = sclp_suspend_state_running; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* Maximum retry counts */ 1328c2ecf20Sopenharmony_ci#define SCLP_INIT_RETRY 3 1338c2ecf20Sopenharmony_ci#define SCLP_MASK_RETRY 3 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* Timeout intervals in seconds.*/ 1368c2ecf20Sopenharmony_ci#define SCLP_BUSY_INTERVAL 10 1378c2ecf20Sopenharmony_ci#define SCLP_RETRY_INTERVAL 30 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic void sclp_request_timeout(bool force_restart); 1408c2ecf20Sopenharmony_cistatic void sclp_process_queue(void); 1418c2ecf20Sopenharmony_cistatic void __sclp_make_read_req(void); 1428c2ecf20Sopenharmony_cistatic int sclp_init_mask(int calculate); 1438c2ecf20Sopenharmony_cistatic int sclp_init(void); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void 1468c2ecf20Sopenharmony_ci__sclp_queue_read_req(void) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci if (sclp_reading_state == sclp_reading_state_idle) { 1498c2ecf20Sopenharmony_ci sclp_reading_state = sclp_reading_state_reading; 1508c2ecf20Sopenharmony_ci __sclp_make_read_req(); 1518c2ecf20Sopenharmony_ci /* Add request to head of queue */ 1528c2ecf20Sopenharmony_ci list_add(&sclp_read_req.list, &sclp_req_queue); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* Set up request retry timer. Called while sclp_lock is locked. */ 1578c2ecf20Sopenharmony_cistatic inline void 1588c2ecf20Sopenharmony_ci__sclp_set_request_timer(unsigned long time, void (*cb)(struct timer_list *)) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci del_timer(&sclp_request_timer); 1618c2ecf20Sopenharmony_ci sclp_request_timer.function = cb; 1628c2ecf20Sopenharmony_ci sclp_request_timer.expires = jiffies + time; 1638c2ecf20Sopenharmony_ci add_timer(&sclp_request_timer); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void sclp_request_timeout_restart(struct timer_list *unused) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci sclp_request_timeout(true); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void sclp_request_timeout_normal(struct timer_list *unused) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci sclp_request_timeout(false); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* Request timeout handler. Restart the request queue. If force_restart, 1778c2ecf20Sopenharmony_ci * force restart of running request. */ 1788c2ecf20Sopenharmony_cistatic void sclp_request_timeout(bool force_restart) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci unsigned long flags; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 1838c2ecf20Sopenharmony_ci if (force_restart) { 1848c2ecf20Sopenharmony_ci if (sclp_running_state == sclp_running_state_running) { 1858c2ecf20Sopenharmony_ci /* Break running state and queue NOP read event request 1868c2ecf20Sopenharmony_ci * to get a defined interface state. */ 1878c2ecf20Sopenharmony_ci __sclp_queue_read_req(); 1888c2ecf20Sopenharmony_ci sclp_running_state = sclp_running_state_idle; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci } else { 1918c2ecf20Sopenharmony_ci __sclp_set_request_timer(SCLP_BUSY_INTERVAL * HZ, 1928c2ecf20Sopenharmony_ci sclp_request_timeout_normal); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 1958c2ecf20Sopenharmony_ci sclp_process_queue(); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * Returns the expire value in jiffies of the next pending request timeout, 2008c2ecf20Sopenharmony_ci * if any. Needs to be called with sclp_lock. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_cistatic unsigned long __sclp_req_queue_find_next_timeout(void) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci unsigned long expires_next = 0; 2058c2ecf20Sopenharmony_ci struct sclp_req *req; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci list_for_each_entry(req, &sclp_req_queue, list) { 2088c2ecf20Sopenharmony_ci if (!req->queue_expires) 2098c2ecf20Sopenharmony_ci continue; 2108c2ecf20Sopenharmony_ci if (!expires_next || 2118c2ecf20Sopenharmony_ci (time_before(req->queue_expires, expires_next))) 2128c2ecf20Sopenharmony_ci expires_next = req->queue_expires; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci return expires_next; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* 2188c2ecf20Sopenharmony_ci * Returns expired request, if any, and removes it from the list. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_cistatic struct sclp_req *__sclp_req_queue_remove_expired_req(void) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci unsigned long flags, now; 2238c2ecf20Sopenharmony_ci struct sclp_req *req; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 2268c2ecf20Sopenharmony_ci now = jiffies; 2278c2ecf20Sopenharmony_ci /* Don't need list_for_each_safe because we break out after list_del */ 2288c2ecf20Sopenharmony_ci list_for_each_entry(req, &sclp_req_queue, list) { 2298c2ecf20Sopenharmony_ci if (!req->queue_expires) 2308c2ecf20Sopenharmony_ci continue; 2318c2ecf20Sopenharmony_ci if (time_before_eq(req->queue_expires, now)) { 2328c2ecf20Sopenharmony_ci if (req->status == SCLP_REQ_QUEUED) { 2338c2ecf20Sopenharmony_ci req->status = SCLP_REQ_QUEUED_TIMEOUT; 2348c2ecf20Sopenharmony_ci list_del(&req->list); 2358c2ecf20Sopenharmony_ci goto out; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci req = NULL; 2408c2ecf20Sopenharmony_ciout: 2418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 2428c2ecf20Sopenharmony_ci return req; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* 2468c2ecf20Sopenharmony_ci * Timeout handler for queued requests. Removes request from list and 2478c2ecf20Sopenharmony_ci * invokes callback. This timer can be set per request in situations where 2488c2ecf20Sopenharmony_ci * waiting too long would be harmful to the system, e.g. during SE reboot. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistatic void sclp_req_queue_timeout(struct timer_list *unused) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci unsigned long flags, expires_next; 2538c2ecf20Sopenharmony_ci struct sclp_req *req; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci do { 2568c2ecf20Sopenharmony_ci req = __sclp_req_queue_remove_expired_req(); 2578c2ecf20Sopenharmony_ci if (req && req->callback) 2588c2ecf20Sopenharmony_ci req->callback(req, req->callback_data); 2598c2ecf20Sopenharmony_ci } while (req); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 2628c2ecf20Sopenharmony_ci expires_next = __sclp_req_queue_find_next_timeout(); 2638c2ecf20Sopenharmony_ci if (expires_next) 2648c2ecf20Sopenharmony_ci mod_timer(&sclp_queue_timer, expires_next); 2658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/* Try to start a request. Return zero if the request was successfully 2698c2ecf20Sopenharmony_ci * started or if it will be started at a later time. Return non-zero otherwise. 2708c2ecf20Sopenharmony_ci * Called while sclp_lock is locked. */ 2718c2ecf20Sopenharmony_cistatic int 2728c2ecf20Sopenharmony_ci__sclp_start_request(struct sclp_req *req) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci int rc; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (sclp_running_state != sclp_running_state_idle) 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci del_timer(&sclp_request_timer); 2798c2ecf20Sopenharmony_ci rc = sclp_service_call(req->command, req->sccb); 2808c2ecf20Sopenharmony_ci req->start_count++; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (rc == 0) { 2838c2ecf20Sopenharmony_ci /* Successfully started request */ 2848c2ecf20Sopenharmony_ci req->status = SCLP_REQ_RUNNING; 2858c2ecf20Sopenharmony_ci sclp_running_state = sclp_running_state_running; 2868c2ecf20Sopenharmony_ci __sclp_set_request_timer(SCLP_RETRY_INTERVAL * HZ, 2878c2ecf20Sopenharmony_ci sclp_request_timeout_restart); 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci } else if (rc == -EBUSY) { 2908c2ecf20Sopenharmony_ci /* Try again later */ 2918c2ecf20Sopenharmony_ci __sclp_set_request_timer(SCLP_BUSY_INTERVAL * HZ, 2928c2ecf20Sopenharmony_ci sclp_request_timeout_normal); 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci /* Request failed */ 2968c2ecf20Sopenharmony_ci req->status = SCLP_REQ_FAILED; 2978c2ecf20Sopenharmony_ci return rc; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/* Try to start queued requests. */ 3018c2ecf20Sopenharmony_cistatic void 3028c2ecf20Sopenharmony_cisclp_process_queue(void) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct sclp_req *req; 3058c2ecf20Sopenharmony_ci int rc; 3068c2ecf20Sopenharmony_ci unsigned long flags; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 3098c2ecf20Sopenharmony_ci if (sclp_running_state != sclp_running_state_idle) { 3108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 3118c2ecf20Sopenharmony_ci return; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci del_timer(&sclp_request_timer); 3148c2ecf20Sopenharmony_ci while (!list_empty(&sclp_req_queue)) { 3158c2ecf20Sopenharmony_ci req = list_entry(sclp_req_queue.next, struct sclp_req, list); 3168c2ecf20Sopenharmony_ci if (!req->sccb) 3178c2ecf20Sopenharmony_ci goto do_post; 3188c2ecf20Sopenharmony_ci rc = __sclp_start_request(req); 3198c2ecf20Sopenharmony_ci if (rc == 0) 3208c2ecf20Sopenharmony_ci break; 3218c2ecf20Sopenharmony_ci /* Request failed */ 3228c2ecf20Sopenharmony_ci if (req->start_count > 1) { 3238c2ecf20Sopenharmony_ci /* Cannot abort already submitted request - could still 3248c2ecf20Sopenharmony_ci * be active at the SCLP */ 3258c2ecf20Sopenharmony_ci __sclp_set_request_timer(SCLP_BUSY_INTERVAL * HZ, 3268c2ecf20Sopenharmony_ci sclp_request_timeout_normal); 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_cido_post: 3308c2ecf20Sopenharmony_ci /* Post-processing for aborted request */ 3318c2ecf20Sopenharmony_ci list_del(&req->list); 3328c2ecf20Sopenharmony_ci if (req->callback) { 3338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 3348c2ecf20Sopenharmony_ci req->callback(req, req->callback_data); 3358c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int __sclp_can_add_request(struct sclp_req *req) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci if (req == &sclp_suspend_req || req == &sclp_init_req) 3448c2ecf20Sopenharmony_ci return 1; 3458c2ecf20Sopenharmony_ci if (sclp_suspend_state != sclp_suspend_state_running) 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci if (sclp_init_state != sclp_init_state_initialized) 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci if (sclp_activation_state != sclp_activation_state_active) 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci return 1; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/* Queue a new request. Return zero on success, non-zero otherwise. */ 3558c2ecf20Sopenharmony_ciint 3568c2ecf20Sopenharmony_cisclp_add_request(struct sclp_req *req) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci unsigned long flags; 3598c2ecf20Sopenharmony_ci int rc; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 3628c2ecf20Sopenharmony_ci if (!__sclp_can_add_request(req)) { 3638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 3648c2ecf20Sopenharmony_ci return -EIO; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci req->status = SCLP_REQ_QUEUED; 3678c2ecf20Sopenharmony_ci req->start_count = 0; 3688c2ecf20Sopenharmony_ci list_add_tail(&req->list, &sclp_req_queue); 3698c2ecf20Sopenharmony_ci rc = 0; 3708c2ecf20Sopenharmony_ci if (req->queue_timeout) { 3718c2ecf20Sopenharmony_ci req->queue_expires = jiffies + req->queue_timeout * HZ; 3728c2ecf20Sopenharmony_ci if (!timer_pending(&sclp_queue_timer) || 3738c2ecf20Sopenharmony_ci time_after(sclp_queue_timer.expires, req->queue_expires)) 3748c2ecf20Sopenharmony_ci mod_timer(&sclp_queue_timer, req->queue_expires); 3758c2ecf20Sopenharmony_ci } else 3768c2ecf20Sopenharmony_ci req->queue_expires = 0; 3778c2ecf20Sopenharmony_ci /* Start if request is first in list */ 3788c2ecf20Sopenharmony_ci if (sclp_running_state == sclp_running_state_idle && 3798c2ecf20Sopenharmony_ci req->list.prev == &sclp_req_queue) { 3808c2ecf20Sopenharmony_ci if (!req->sccb) { 3818c2ecf20Sopenharmony_ci list_del(&req->list); 3828c2ecf20Sopenharmony_ci rc = -ENODATA; 3838c2ecf20Sopenharmony_ci goto out; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci rc = __sclp_start_request(req); 3868c2ecf20Sopenharmony_ci if (rc) 3878c2ecf20Sopenharmony_ci list_del(&req->list); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ciout: 3908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 3918c2ecf20Sopenharmony_ci return rc; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sclp_add_request); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci/* Dispatch events found in request buffer to registered listeners. Return 0 3978c2ecf20Sopenharmony_ci * if all events were dispatched, non-zero otherwise. */ 3988c2ecf20Sopenharmony_cistatic int 3998c2ecf20Sopenharmony_cisclp_dispatch_evbufs(struct sccb_header *sccb) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci unsigned long flags; 4028c2ecf20Sopenharmony_ci struct evbuf_header *evbuf; 4038c2ecf20Sopenharmony_ci struct list_head *l; 4048c2ecf20Sopenharmony_ci struct sclp_register *reg; 4058c2ecf20Sopenharmony_ci int offset; 4068c2ecf20Sopenharmony_ci int rc; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 4098c2ecf20Sopenharmony_ci rc = 0; 4108c2ecf20Sopenharmony_ci for (offset = sizeof(struct sccb_header); offset < sccb->length; 4118c2ecf20Sopenharmony_ci offset += evbuf->length) { 4128c2ecf20Sopenharmony_ci evbuf = (struct evbuf_header *) ((addr_t) sccb + offset); 4138c2ecf20Sopenharmony_ci /* Check for malformed hardware response */ 4148c2ecf20Sopenharmony_ci if (evbuf->length == 0) 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci /* Search for event handler */ 4178c2ecf20Sopenharmony_ci reg = NULL; 4188c2ecf20Sopenharmony_ci list_for_each(l, &sclp_reg_list) { 4198c2ecf20Sopenharmony_ci reg = list_entry(l, struct sclp_register, list); 4208c2ecf20Sopenharmony_ci if (reg->receive_mask & SCLP_EVTYP_MASK(evbuf->type)) 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci else 4238c2ecf20Sopenharmony_ci reg = NULL; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci if (reg && reg->receiver_fn) { 4268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 4278c2ecf20Sopenharmony_ci reg->receiver_fn(evbuf); 4288c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 4298c2ecf20Sopenharmony_ci } else if (reg == NULL) 4308c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 4338c2ecf20Sopenharmony_ci return rc; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/* Read event data request callback. */ 4378c2ecf20Sopenharmony_cistatic void 4388c2ecf20Sopenharmony_cisclp_read_cb(struct sclp_req *req, void *data) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci unsigned long flags; 4418c2ecf20Sopenharmony_ci struct sccb_header *sccb; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci sccb = (struct sccb_header *) req->sccb; 4448c2ecf20Sopenharmony_ci if (req->status == SCLP_REQ_DONE && (sccb->response_code == 0x20 || 4458c2ecf20Sopenharmony_ci sccb->response_code == 0x220)) 4468c2ecf20Sopenharmony_ci sclp_dispatch_evbufs(sccb); 4478c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 4488c2ecf20Sopenharmony_ci sclp_reading_state = sclp_reading_state_idle; 4498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/* Prepare read event data request. Called while sclp_lock is locked. */ 4538c2ecf20Sopenharmony_cistatic void __sclp_make_read_req(void) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct sccb_header *sccb; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci sccb = (struct sccb_header *) sclp_read_sccb; 4588c2ecf20Sopenharmony_ci clear_page(sccb); 4598c2ecf20Sopenharmony_ci memset(&sclp_read_req, 0, sizeof(struct sclp_req)); 4608c2ecf20Sopenharmony_ci sclp_read_req.command = SCLP_CMDW_READ_EVENT_DATA; 4618c2ecf20Sopenharmony_ci sclp_read_req.status = SCLP_REQ_QUEUED; 4628c2ecf20Sopenharmony_ci sclp_read_req.start_count = 0; 4638c2ecf20Sopenharmony_ci sclp_read_req.callback = sclp_read_cb; 4648c2ecf20Sopenharmony_ci sclp_read_req.sccb = sccb; 4658c2ecf20Sopenharmony_ci sccb->length = PAGE_SIZE; 4668c2ecf20Sopenharmony_ci sccb->function_code = 0; 4678c2ecf20Sopenharmony_ci sccb->control_mask[2] = 0x80; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/* Search request list for request with matching sccb. Return request if found, 4718c2ecf20Sopenharmony_ci * NULL otherwise. Called while sclp_lock is locked. */ 4728c2ecf20Sopenharmony_cistatic inline struct sclp_req * 4738c2ecf20Sopenharmony_ci__sclp_find_req(u32 sccb) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct list_head *l; 4768c2ecf20Sopenharmony_ci struct sclp_req *req; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci list_for_each(l, &sclp_req_queue) { 4798c2ecf20Sopenharmony_ci req = list_entry(l, struct sclp_req, list); 4808c2ecf20Sopenharmony_ci if (sccb == (u32) (addr_t) req->sccb) 4818c2ecf20Sopenharmony_ci return req; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci return NULL; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/* Handler for external interruption. Perform request post-processing. 4878c2ecf20Sopenharmony_ci * Prepare read event data request if necessary. Start processing of next 4888c2ecf20Sopenharmony_ci * request on queue. */ 4898c2ecf20Sopenharmony_cistatic void sclp_interrupt_handler(struct ext_code ext_code, 4908c2ecf20Sopenharmony_ci unsigned int param32, unsigned long param64) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct sclp_req *req; 4938c2ecf20Sopenharmony_ci u32 finished_sccb; 4948c2ecf20Sopenharmony_ci u32 evbuf_pending; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci inc_irq_stat(IRQEXT_SCP); 4978c2ecf20Sopenharmony_ci spin_lock(&sclp_lock); 4988c2ecf20Sopenharmony_ci finished_sccb = param32 & 0xfffffff8; 4998c2ecf20Sopenharmony_ci evbuf_pending = param32 & 0x3; 5008c2ecf20Sopenharmony_ci if (finished_sccb) { 5018c2ecf20Sopenharmony_ci del_timer(&sclp_request_timer); 5028c2ecf20Sopenharmony_ci sclp_running_state = sclp_running_state_reset_pending; 5038c2ecf20Sopenharmony_ci req = __sclp_find_req(finished_sccb); 5048c2ecf20Sopenharmony_ci if (req) { 5058c2ecf20Sopenharmony_ci /* Request post-processing */ 5068c2ecf20Sopenharmony_ci list_del(&req->list); 5078c2ecf20Sopenharmony_ci req->status = SCLP_REQ_DONE; 5088c2ecf20Sopenharmony_ci if (req->callback) { 5098c2ecf20Sopenharmony_ci spin_unlock(&sclp_lock); 5108c2ecf20Sopenharmony_ci req->callback(req, req->callback_data); 5118c2ecf20Sopenharmony_ci spin_lock(&sclp_lock); 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci sclp_running_state = sclp_running_state_idle; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci if (evbuf_pending && 5178c2ecf20Sopenharmony_ci sclp_activation_state == sclp_activation_state_active) 5188c2ecf20Sopenharmony_ci __sclp_queue_read_req(); 5198c2ecf20Sopenharmony_ci spin_unlock(&sclp_lock); 5208c2ecf20Sopenharmony_ci sclp_process_queue(); 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci/* Convert interval in jiffies to TOD ticks. */ 5248c2ecf20Sopenharmony_cistatic inline u64 5258c2ecf20Sopenharmony_cisclp_tod_from_jiffies(unsigned long jiffies) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci return (u64) (jiffies / HZ) << 32; 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci/* Wait until a currently running request finished. Note: while this function 5318c2ecf20Sopenharmony_ci * is running, no timers are served on the calling CPU. */ 5328c2ecf20Sopenharmony_civoid 5338c2ecf20Sopenharmony_cisclp_sync_wait(void) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci unsigned long long old_tick; 5368c2ecf20Sopenharmony_ci unsigned long flags; 5378c2ecf20Sopenharmony_ci unsigned long cr0, cr0_sync; 5388c2ecf20Sopenharmony_ci u64 timeout; 5398c2ecf20Sopenharmony_ci int irq_context; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* We'll be disabling timer interrupts, so we need a custom timeout 5428c2ecf20Sopenharmony_ci * mechanism */ 5438c2ecf20Sopenharmony_ci timeout = 0; 5448c2ecf20Sopenharmony_ci if (timer_pending(&sclp_request_timer)) { 5458c2ecf20Sopenharmony_ci /* Get timeout TOD value */ 5468c2ecf20Sopenharmony_ci timeout = get_tod_clock_fast() + 5478c2ecf20Sopenharmony_ci sclp_tod_from_jiffies(sclp_request_timer.expires - 5488c2ecf20Sopenharmony_ci jiffies); 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci local_irq_save(flags); 5518c2ecf20Sopenharmony_ci /* Prevent bottom half from executing once we force interrupts open */ 5528c2ecf20Sopenharmony_ci irq_context = in_interrupt(); 5538c2ecf20Sopenharmony_ci if (!irq_context) 5548c2ecf20Sopenharmony_ci local_bh_disable(); 5558c2ecf20Sopenharmony_ci /* Enable service-signal interruption, disable timer interrupts */ 5568c2ecf20Sopenharmony_ci old_tick = local_tick_disable(); 5578c2ecf20Sopenharmony_ci trace_hardirqs_on(); 5588c2ecf20Sopenharmony_ci __ctl_store(cr0, 0, 0); 5598c2ecf20Sopenharmony_ci cr0_sync = cr0 & ~CR0_IRQ_SUBCLASS_MASK; 5608c2ecf20Sopenharmony_ci cr0_sync |= 1UL << (63 - 54); 5618c2ecf20Sopenharmony_ci __ctl_load(cr0_sync, 0, 0); 5628c2ecf20Sopenharmony_ci __arch_local_irq_stosm(0x01); 5638c2ecf20Sopenharmony_ci /* Loop until driver state indicates finished request */ 5648c2ecf20Sopenharmony_ci while (sclp_running_state != sclp_running_state_idle) { 5658c2ecf20Sopenharmony_ci /* Check for expired request timer */ 5668c2ecf20Sopenharmony_ci if (timer_pending(&sclp_request_timer) && 5678c2ecf20Sopenharmony_ci get_tod_clock_fast() > timeout && 5688c2ecf20Sopenharmony_ci del_timer(&sclp_request_timer)) 5698c2ecf20Sopenharmony_ci sclp_request_timer.function(&sclp_request_timer); 5708c2ecf20Sopenharmony_ci cpu_relax(); 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci local_irq_disable(); 5738c2ecf20Sopenharmony_ci __ctl_load(cr0, 0, 0); 5748c2ecf20Sopenharmony_ci if (!irq_context) 5758c2ecf20Sopenharmony_ci _local_bh_enable(); 5768c2ecf20Sopenharmony_ci local_tick_enable(old_tick); 5778c2ecf20Sopenharmony_ci local_irq_restore(flags); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sclp_sync_wait); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci/* Dispatch changes in send and receive mask to registered listeners. */ 5828c2ecf20Sopenharmony_cistatic void 5838c2ecf20Sopenharmony_cisclp_dispatch_state_change(void) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct list_head *l; 5868c2ecf20Sopenharmony_ci struct sclp_register *reg; 5878c2ecf20Sopenharmony_ci unsigned long flags; 5888c2ecf20Sopenharmony_ci sccb_mask_t receive_mask; 5898c2ecf20Sopenharmony_ci sccb_mask_t send_mask; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci do { 5928c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 5938c2ecf20Sopenharmony_ci reg = NULL; 5948c2ecf20Sopenharmony_ci list_for_each(l, &sclp_reg_list) { 5958c2ecf20Sopenharmony_ci reg = list_entry(l, struct sclp_register, list); 5968c2ecf20Sopenharmony_ci receive_mask = reg->send_mask & sclp_receive_mask; 5978c2ecf20Sopenharmony_ci send_mask = reg->receive_mask & sclp_send_mask; 5988c2ecf20Sopenharmony_ci if (reg->sclp_receive_mask != receive_mask || 5998c2ecf20Sopenharmony_ci reg->sclp_send_mask != send_mask) { 6008c2ecf20Sopenharmony_ci reg->sclp_receive_mask = receive_mask; 6018c2ecf20Sopenharmony_ci reg->sclp_send_mask = send_mask; 6028c2ecf20Sopenharmony_ci break; 6038c2ecf20Sopenharmony_ci } else 6048c2ecf20Sopenharmony_ci reg = NULL; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 6078c2ecf20Sopenharmony_ci if (reg && reg->state_change_fn) 6088c2ecf20Sopenharmony_ci reg->state_change_fn(reg); 6098c2ecf20Sopenharmony_ci } while (reg); 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistruct sclp_statechangebuf { 6138c2ecf20Sopenharmony_ci struct evbuf_header header; 6148c2ecf20Sopenharmony_ci u8 validity_sclp_active_facility_mask : 1; 6158c2ecf20Sopenharmony_ci u8 validity_sclp_receive_mask : 1; 6168c2ecf20Sopenharmony_ci u8 validity_sclp_send_mask : 1; 6178c2ecf20Sopenharmony_ci u8 validity_read_data_function_mask : 1; 6188c2ecf20Sopenharmony_ci u16 _zeros : 12; 6198c2ecf20Sopenharmony_ci u16 mask_length; 6208c2ecf20Sopenharmony_ci u64 sclp_active_facility_mask; 6218c2ecf20Sopenharmony_ci u8 masks[2 * 1021 + 4]; /* variable length */ 6228c2ecf20Sopenharmony_ci /* 6238c2ecf20Sopenharmony_ci * u8 sclp_receive_mask[mask_length]; 6248c2ecf20Sopenharmony_ci * u8 sclp_send_mask[mask_length]; 6258c2ecf20Sopenharmony_ci * u32 read_data_function_mask; 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_ci} __attribute__((packed)); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci/* State change event callback. Inform listeners of changes. */ 6318c2ecf20Sopenharmony_cistatic void 6328c2ecf20Sopenharmony_cisclp_state_change_cb(struct evbuf_header *evbuf) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci unsigned long flags; 6358c2ecf20Sopenharmony_ci struct sclp_statechangebuf *scbuf; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct sclp_statechangebuf) > PAGE_SIZE); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci scbuf = (struct sclp_statechangebuf *) evbuf; 6408c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 6418c2ecf20Sopenharmony_ci if (scbuf->validity_sclp_receive_mask) 6428c2ecf20Sopenharmony_ci sclp_receive_mask = sccb_get_recv_mask(scbuf); 6438c2ecf20Sopenharmony_ci if (scbuf->validity_sclp_send_mask) 6448c2ecf20Sopenharmony_ci sclp_send_mask = sccb_get_send_mask(scbuf); 6458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 6468c2ecf20Sopenharmony_ci if (scbuf->validity_sclp_active_facility_mask) 6478c2ecf20Sopenharmony_ci sclp.facilities = scbuf->sclp_active_facility_mask; 6488c2ecf20Sopenharmony_ci sclp_dispatch_state_change(); 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic struct sclp_register sclp_state_change_event = { 6528c2ecf20Sopenharmony_ci .receive_mask = EVTYP_STATECHANGE_MASK, 6538c2ecf20Sopenharmony_ci .receiver_fn = sclp_state_change_cb 6548c2ecf20Sopenharmony_ci}; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci/* Calculate receive and send mask of currently registered listeners. 6578c2ecf20Sopenharmony_ci * Called while sclp_lock is locked. */ 6588c2ecf20Sopenharmony_cistatic inline void 6598c2ecf20Sopenharmony_ci__sclp_get_mask(sccb_mask_t *receive_mask, sccb_mask_t *send_mask) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci struct list_head *l; 6628c2ecf20Sopenharmony_ci struct sclp_register *t; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci *receive_mask = 0; 6658c2ecf20Sopenharmony_ci *send_mask = 0; 6668c2ecf20Sopenharmony_ci list_for_each(l, &sclp_reg_list) { 6678c2ecf20Sopenharmony_ci t = list_entry(l, struct sclp_register, list); 6688c2ecf20Sopenharmony_ci *receive_mask |= t->receive_mask; 6698c2ecf20Sopenharmony_ci *send_mask |= t->send_mask; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci/* Register event listener. Return 0 on success, non-zero otherwise. */ 6748c2ecf20Sopenharmony_ciint 6758c2ecf20Sopenharmony_cisclp_register(struct sclp_register *reg) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci unsigned long flags; 6788c2ecf20Sopenharmony_ci sccb_mask_t receive_mask; 6798c2ecf20Sopenharmony_ci sccb_mask_t send_mask; 6808c2ecf20Sopenharmony_ci int rc; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci rc = sclp_init(); 6838c2ecf20Sopenharmony_ci if (rc) 6848c2ecf20Sopenharmony_ci return rc; 6858c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 6868c2ecf20Sopenharmony_ci /* Check event mask for collisions */ 6878c2ecf20Sopenharmony_ci __sclp_get_mask(&receive_mask, &send_mask); 6888c2ecf20Sopenharmony_ci if (reg->receive_mask & receive_mask || reg->send_mask & send_mask) { 6898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 6908c2ecf20Sopenharmony_ci return -EBUSY; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci /* Trigger initial state change callback */ 6938c2ecf20Sopenharmony_ci reg->sclp_receive_mask = 0; 6948c2ecf20Sopenharmony_ci reg->sclp_send_mask = 0; 6958c2ecf20Sopenharmony_ci reg->pm_event_posted = 0; 6968c2ecf20Sopenharmony_ci list_add(®->list, &sclp_reg_list); 6978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 6988c2ecf20Sopenharmony_ci rc = sclp_init_mask(1); 6998c2ecf20Sopenharmony_ci if (rc) { 7008c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 7018c2ecf20Sopenharmony_ci list_del(®->list); 7028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci return rc; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sclp_register); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci/* Unregister event listener. */ 7108c2ecf20Sopenharmony_civoid 7118c2ecf20Sopenharmony_cisclp_unregister(struct sclp_register *reg) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci unsigned long flags; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 7168c2ecf20Sopenharmony_ci list_del(®->list); 7178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 7188c2ecf20Sopenharmony_ci sclp_init_mask(1); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sclp_unregister); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci/* Remove event buffers which are marked processed. Return the number of 7248c2ecf20Sopenharmony_ci * remaining event buffers. */ 7258c2ecf20Sopenharmony_ciint 7268c2ecf20Sopenharmony_cisclp_remove_processed(struct sccb_header *sccb) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci struct evbuf_header *evbuf; 7298c2ecf20Sopenharmony_ci int unprocessed; 7308c2ecf20Sopenharmony_ci u16 remaining; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci evbuf = (struct evbuf_header *) (sccb + 1); 7338c2ecf20Sopenharmony_ci unprocessed = 0; 7348c2ecf20Sopenharmony_ci remaining = sccb->length - sizeof(struct sccb_header); 7358c2ecf20Sopenharmony_ci while (remaining > 0) { 7368c2ecf20Sopenharmony_ci remaining -= evbuf->length; 7378c2ecf20Sopenharmony_ci if (evbuf->flags & 0x80) { 7388c2ecf20Sopenharmony_ci sccb->length -= evbuf->length; 7398c2ecf20Sopenharmony_ci memcpy(evbuf, (void *) ((addr_t) evbuf + evbuf->length), 7408c2ecf20Sopenharmony_ci remaining); 7418c2ecf20Sopenharmony_ci } else { 7428c2ecf20Sopenharmony_ci unprocessed++; 7438c2ecf20Sopenharmony_ci evbuf = (struct evbuf_header *) 7448c2ecf20Sopenharmony_ci ((addr_t) evbuf + evbuf->length); 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci return unprocessed; 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sclp_remove_processed); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci/* Prepare init mask request. Called while sclp_lock is locked. */ 7538c2ecf20Sopenharmony_cistatic inline void 7548c2ecf20Sopenharmony_ci__sclp_make_init_req(sccb_mask_t receive_mask, sccb_mask_t send_mask) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci struct init_sccb *sccb = sclp_init_sccb; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci clear_page(sccb); 7598c2ecf20Sopenharmony_ci memset(&sclp_init_req, 0, sizeof(struct sclp_req)); 7608c2ecf20Sopenharmony_ci sclp_init_req.command = SCLP_CMDW_WRITE_EVENT_MASK; 7618c2ecf20Sopenharmony_ci sclp_init_req.status = SCLP_REQ_FILLED; 7628c2ecf20Sopenharmony_ci sclp_init_req.start_count = 0; 7638c2ecf20Sopenharmony_ci sclp_init_req.callback = NULL; 7648c2ecf20Sopenharmony_ci sclp_init_req.callback_data = NULL; 7658c2ecf20Sopenharmony_ci sclp_init_req.sccb = sccb; 7668c2ecf20Sopenharmony_ci sccb->header.length = sizeof(*sccb); 7678c2ecf20Sopenharmony_ci if (sclp_mask_compat_mode) 7688c2ecf20Sopenharmony_ci sccb->mask_length = SCLP_MASK_SIZE_COMPAT; 7698c2ecf20Sopenharmony_ci else 7708c2ecf20Sopenharmony_ci sccb->mask_length = sizeof(sccb_mask_t); 7718c2ecf20Sopenharmony_ci sccb_set_recv_mask(sccb, receive_mask); 7728c2ecf20Sopenharmony_ci sccb_set_send_mask(sccb, send_mask); 7738c2ecf20Sopenharmony_ci sccb_set_sclp_recv_mask(sccb, 0); 7748c2ecf20Sopenharmony_ci sccb_set_sclp_send_mask(sccb, 0); 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci/* Start init mask request. If calculate is non-zero, calculate the mask as 7788c2ecf20Sopenharmony_ci * requested by registered listeners. Use zero mask otherwise. Return 0 on 7798c2ecf20Sopenharmony_ci * success, non-zero otherwise. */ 7808c2ecf20Sopenharmony_cistatic int 7818c2ecf20Sopenharmony_cisclp_init_mask(int calculate) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci unsigned long flags; 7848c2ecf20Sopenharmony_ci struct init_sccb *sccb = sclp_init_sccb; 7858c2ecf20Sopenharmony_ci sccb_mask_t receive_mask; 7868c2ecf20Sopenharmony_ci sccb_mask_t send_mask; 7878c2ecf20Sopenharmony_ci int retry; 7888c2ecf20Sopenharmony_ci int rc; 7898c2ecf20Sopenharmony_ci unsigned long wait; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 7928c2ecf20Sopenharmony_ci /* Check if interface is in appropriate state */ 7938c2ecf20Sopenharmony_ci if (sclp_mask_state != sclp_mask_state_idle) { 7948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 7958c2ecf20Sopenharmony_ci return -EBUSY; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci if (sclp_activation_state == sclp_activation_state_inactive) { 7988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 7998c2ecf20Sopenharmony_ci return -EINVAL; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci sclp_mask_state = sclp_mask_state_initializing; 8028c2ecf20Sopenharmony_ci /* Determine mask */ 8038c2ecf20Sopenharmony_ci if (calculate) 8048c2ecf20Sopenharmony_ci __sclp_get_mask(&receive_mask, &send_mask); 8058c2ecf20Sopenharmony_ci else { 8068c2ecf20Sopenharmony_ci receive_mask = 0; 8078c2ecf20Sopenharmony_ci send_mask = 0; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci rc = -EIO; 8108c2ecf20Sopenharmony_ci for (retry = 0; retry <= SCLP_MASK_RETRY; retry++) { 8118c2ecf20Sopenharmony_ci /* Prepare request */ 8128c2ecf20Sopenharmony_ci __sclp_make_init_req(receive_mask, send_mask); 8138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 8148c2ecf20Sopenharmony_ci if (sclp_add_request(&sclp_init_req)) { 8158c2ecf20Sopenharmony_ci /* Try again later */ 8168c2ecf20Sopenharmony_ci wait = jiffies + SCLP_BUSY_INTERVAL * HZ; 8178c2ecf20Sopenharmony_ci while (time_before(jiffies, wait)) 8188c2ecf20Sopenharmony_ci sclp_sync_wait(); 8198c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 8208c2ecf20Sopenharmony_ci continue; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci while (sclp_init_req.status != SCLP_REQ_DONE && 8238c2ecf20Sopenharmony_ci sclp_init_req.status != SCLP_REQ_FAILED) 8248c2ecf20Sopenharmony_ci sclp_sync_wait(); 8258c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 8268c2ecf20Sopenharmony_ci if (sclp_init_req.status == SCLP_REQ_DONE && 8278c2ecf20Sopenharmony_ci sccb->header.response_code == 0x20) { 8288c2ecf20Sopenharmony_ci /* Successful request */ 8298c2ecf20Sopenharmony_ci if (calculate) { 8308c2ecf20Sopenharmony_ci sclp_receive_mask = sccb_get_sclp_recv_mask(sccb); 8318c2ecf20Sopenharmony_ci sclp_send_mask = sccb_get_sclp_send_mask(sccb); 8328c2ecf20Sopenharmony_ci } else { 8338c2ecf20Sopenharmony_ci sclp_receive_mask = 0; 8348c2ecf20Sopenharmony_ci sclp_send_mask = 0; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 8378c2ecf20Sopenharmony_ci sclp_dispatch_state_change(); 8388c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 8398c2ecf20Sopenharmony_ci rc = 0; 8408c2ecf20Sopenharmony_ci break; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci sclp_mask_state = sclp_mask_state_idle; 8448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 8458c2ecf20Sopenharmony_ci return rc; 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci/* Deactivate SCLP interface. On success, new requests will be rejected, 8498c2ecf20Sopenharmony_ci * events will no longer be dispatched. Return 0 on success, non-zero 8508c2ecf20Sopenharmony_ci * otherwise. */ 8518c2ecf20Sopenharmony_ciint 8528c2ecf20Sopenharmony_cisclp_deactivate(void) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci unsigned long flags; 8558c2ecf20Sopenharmony_ci int rc; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 8588c2ecf20Sopenharmony_ci /* Deactivate can only be called when active */ 8598c2ecf20Sopenharmony_ci if (sclp_activation_state != sclp_activation_state_active) { 8608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 8618c2ecf20Sopenharmony_ci return -EINVAL; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci sclp_activation_state = sclp_activation_state_deactivating; 8648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 8658c2ecf20Sopenharmony_ci rc = sclp_init_mask(0); 8668c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 8678c2ecf20Sopenharmony_ci if (rc == 0) 8688c2ecf20Sopenharmony_ci sclp_activation_state = sclp_activation_state_inactive; 8698c2ecf20Sopenharmony_ci else 8708c2ecf20Sopenharmony_ci sclp_activation_state = sclp_activation_state_active; 8718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 8728c2ecf20Sopenharmony_ci return rc; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sclp_deactivate); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci/* Reactivate SCLP interface after sclp_deactivate. On success, new 8788c2ecf20Sopenharmony_ci * requests will be accepted, events will be dispatched again. Return 0 on 8798c2ecf20Sopenharmony_ci * success, non-zero otherwise. */ 8808c2ecf20Sopenharmony_ciint 8818c2ecf20Sopenharmony_cisclp_reactivate(void) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci unsigned long flags; 8848c2ecf20Sopenharmony_ci int rc; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 8878c2ecf20Sopenharmony_ci /* Reactivate can only be called when inactive */ 8888c2ecf20Sopenharmony_ci if (sclp_activation_state != sclp_activation_state_inactive) { 8898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 8908c2ecf20Sopenharmony_ci return -EINVAL; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci sclp_activation_state = sclp_activation_state_activating; 8938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 8948c2ecf20Sopenharmony_ci rc = sclp_init_mask(1); 8958c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 8968c2ecf20Sopenharmony_ci if (rc == 0) 8978c2ecf20Sopenharmony_ci sclp_activation_state = sclp_activation_state_active; 8988c2ecf20Sopenharmony_ci else 8998c2ecf20Sopenharmony_ci sclp_activation_state = sclp_activation_state_inactive; 9008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 9018c2ecf20Sopenharmony_ci return rc; 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sclp_reactivate); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci/* Handler for external interruption used during initialization. Modify 9078c2ecf20Sopenharmony_ci * request state to done. */ 9088c2ecf20Sopenharmony_cistatic void sclp_check_handler(struct ext_code ext_code, 9098c2ecf20Sopenharmony_ci unsigned int param32, unsigned long param64) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci u32 finished_sccb; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci inc_irq_stat(IRQEXT_SCP); 9148c2ecf20Sopenharmony_ci finished_sccb = param32 & 0xfffffff8; 9158c2ecf20Sopenharmony_ci /* Is this the interrupt we are waiting for? */ 9168c2ecf20Sopenharmony_ci if (finished_sccb == 0) 9178c2ecf20Sopenharmony_ci return; 9188c2ecf20Sopenharmony_ci if (finished_sccb != (u32) (addr_t) sclp_init_sccb) 9198c2ecf20Sopenharmony_ci panic("sclp: unsolicited interrupt for buffer at 0x%x\n", 9208c2ecf20Sopenharmony_ci finished_sccb); 9218c2ecf20Sopenharmony_ci spin_lock(&sclp_lock); 9228c2ecf20Sopenharmony_ci if (sclp_running_state == sclp_running_state_running) { 9238c2ecf20Sopenharmony_ci sclp_init_req.status = SCLP_REQ_DONE; 9248c2ecf20Sopenharmony_ci sclp_running_state = sclp_running_state_idle; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci spin_unlock(&sclp_lock); 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci/* Initial init mask request timed out. Modify request state to failed. */ 9308c2ecf20Sopenharmony_cistatic void 9318c2ecf20Sopenharmony_cisclp_check_timeout(struct timer_list *unused) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci unsigned long flags; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 9368c2ecf20Sopenharmony_ci if (sclp_running_state == sclp_running_state_running) { 9378c2ecf20Sopenharmony_ci sclp_init_req.status = SCLP_REQ_FAILED; 9388c2ecf20Sopenharmony_ci sclp_running_state = sclp_running_state_idle; 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci/* Perform a check of the SCLP interface. Return zero if the interface is 9448c2ecf20Sopenharmony_ci * available and there are no pending requests from a previous instance. 9458c2ecf20Sopenharmony_ci * Return non-zero otherwise. */ 9468c2ecf20Sopenharmony_cistatic int 9478c2ecf20Sopenharmony_cisclp_check_interface(void) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci struct init_sccb *sccb; 9508c2ecf20Sopenharmony_ci unsigned long flags; 9518c2ecf20Sopenharmony_ci int retry; 9528c2ecf20Sopenharmony_ci int rc; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 9558c2ecf20Sopenharmony_ci /* Prepare init mask command */ 9568c2ecf20Sopenharmony_ci rc = register_external_irq(EXT_IRQ_SERVICE_SIG, sclp_check_handler); 9578c2ecf20Sopenharmony_ci if (rc) { 9588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 9598c2ecf20Sopenharmony_ci return rc; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci for (retry = 0; retry <= SCLP_INIT_RETRY; retry++) { 9628c2ecf20Sopenharmony_ci __sclp_make_init_req(0, 0); 9638c2ecf20Sopenharmony_ci sccb = (struct init_sccb *) sclp_init_req.sccb; 9648c2ecf20Sopenharmony_ci rc = sclp_service_call(sclp_init_req.command, sccb); 9658c2ecf20Sopenharmony_ci if (rc == -EIO) 9668c2ecf20Sopenharmony_ci break; 9678c2ecf20Sopenharmony_ci sclp_init_req.status = SCLP_REQ_RUNNING; 9688c2ecf20Sopenharmony_ci sclp_running_state = sclp_running_state_running; 9698c2ecf20Sopenharmony_ci __sclp_set_request_timer(SCLP_RETRY_INTERVAL * HZ, 9708c2ecf20Sopenharmony_ci sclp_check_timeout); 9718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 9728c2ecf20Sopenharmony_ci /* Enable service-signal interruption - needs to happen 9738c2ecf20Sopenharmony_ci * with IRQs enabled. */ 9748c2ecf20Sopenharmony_ci irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL); 9758c2ecf20Sopenharmony_ci /* Wait for signal from interrupt or timeout */ 9768c2ecf20Sopenharmony_ci sclp_sync_wait(); 9778c2ecf20Sopenharmony_ci /* Disable service-signal interruption - needs to happen 9788c2ecf20Sopenharmony_ci * with IRQs enabled. */ 9798c2ecf20Sopenharmony_ci irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL); 9808c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 9818c2ecf20Sopenharmony_ci del_timer(&sclp_request_timer); 9828c2ecf20Sopenharmony_ci rc = -EBUSY; 9838c2ecf20Sopenharmony_ci if (sclp_init_req.status == SCLP_REQ_DONE) { 9848c2ecf20Sopenharmony_ci if (sccb->header.response_code == 0x20) { 9858c2ecf20Sopenharmony_ci rc = 0; 9868c2ecf20Sopenharmony_ci break; 9878c2ecf20Sopenharmony_ci } else if (sccb->header.response_code == 0x74f0) { 9888c2ecf20Sopenharmony_ci if (!sclp_mask_compat_mode) { 9898c2ecf20Sopenharmony_ci sclp_mask_compat_mode = true; 9908c2ecf20Sopenharmony_ci retry = 0; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci unregister_external_irq(EXT_IRQ_SERVICE_SIG, sclp_check_handler); 9968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 9978c2ecf20Sopenharmony_ci return rc; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci/* Reboot event handler. Reset send and receive mask to prevent pending SCLP 10018c2ecf20Sopenharmony_ci * events from interfering with rebooted system. */ 10028c2ecf20Sopenharmony_cistatic int 10038c2ecf20Sopenharmony_cisclp_reboot_event(struct notifier_block *this, unsigned long event, void *ptr) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci sclp_deactivate(); 10068c2ecf20Sopenharmony_ci return NOTIFY_DONE; 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic struct notifier_block sclp_reboot_notifier = { 10108c2ecf20Sopenharmony_ci .notifier_call = sclp_reboot_event 10118c2ecf20Sopenharmony_ci}; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci/* 10148c2ecf20Sopenharmony_ci * Suspend/resume SCLP notifier implementation 10158c2ecf20Sopenharmony_ci */ 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_cistatic void sclp_pm_event(enum sclp_pm_event sclp_pm_event, int rollback) 10188c2ecf20Sopenharmony_ci{ 10198c2ecf20Sopenharmony_ci struct sclp_register *reg; 10208c2ecf20Sopenharmony_ci unsigned long flags; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (!rollback) { 10238c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 10248c2ecf20Sopenharmony_ci list_for_each_entry(reg, &sclp_reg_list, list) 10258c2ecf20Sopenharmony_ci reg->pm_event_posted = 0; 10268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci do { 10298c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 10308c2ecf20Sopenharmony_ci list_for_each_entry(reg, &sclp_reg_list, list) { 10318c2ecf20Sopenharmony_ci if (rollback && reg->pm_event_posted) 10328c2ecf20Sopenharmony_ci goto found; 10338c2ecf20Sopenharmony_ci if (!rollback && !reg->pm_event_posted) 10348c2ecf20Sopenharmony_ci goto found; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 10378c2ecf20Sopenharmony_ci return; 10388c2ecf20Sopenharmony_cifound: 10398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 10408c2ecf20Sopenharmony_ci if (reg->pm_event_fn) 10418c2ecf20Sopenharmony_ci reg->pm_event_fn(reg, sclp_pm_event); 10428c2ecf20Sopenharmony_ci reg->pm_event_posted = rollback ? 0 : 1; 10438c2ecf20Sopenharmony_ci } while (1); 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci/* 10478c2ecf20Sopenharmony_ci * Susend/resume callbacks for platform device 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic int sclp_freeze(struct device *dev) 10518c2ecf20Sopenharmony_ci{ 10528c2ecf20Sopenharmony_ci unsigned long flags; 10538c2ecf20Sopenharmony_ci int rc; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci sclp_pm_event(SCLP_PM_EVENT_FREEZE, 0); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 10588c2ecf20Sopenharmony_ci sclp_suspend_state = sclp_suspend_state_suspended; 10598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* Init supend data */ 10628c2ecf20Sopenharmony_ci memset(&sclp_suspend_req, 0, sizeof(sclp_suspend_req)); 10638c2ecf20Sopenharmony_ci sclp_suspend_req.callback = sclp_suspend_req_cb; 10648c2ecf20Sopenharmony_ci sclp_suspend_req.status = SCLP_REQ_FILLED; 10658c2ecf20Sopenharmony_ci init_completion(&sclp_request_queue_flushed); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci rc = sclp_add_request(&sclp_suspend_req); 10688c2ecf20Sopenharmony_ci if (rc == 0) 10698c2ecf20Sopenharmony_ci wait_for_completion(&sclp_request_queue_flushed); 10708c2ecf20Sopenharmony_ci else if (rc != -ENODATA) 10718c2ecf20Sopenharmony_ci goto fail_thaw; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci rc = sclp_deactivate(); 10748c2ecf20Sopenharmony_ci if (rc) 10758c2ecf20Sopenharmony_ci goto fail_thaw; 10768c2ecf20Sopenharmony_ci return 0; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_cifail_thaw: 10798c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 10808c2ecf20Sopenharmony_ci sclp_suspend_state = sclp_suspend_state_running; 10818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 10828c2ecf20Sopenharmony_ci sclp_pm_event(SCLP_PM_EVENT_THAW, 1); 10838c2ecf20Sopenharmony_ci return rc; 10848c2ecf20Sopenharmony_ci} 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_cistatic int sclp_undo_suspend(enum sclp_pm_event event) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci unsigned long flags; 10898c2ecf20Sopenharmony_ci int rc; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci rc = sclp_reactivate(); 10928c2ecf20Sopenharmony_ci if (rc) 10938c2ecf20Sopenharmony_ci return rc; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 10968c2ecf20Sopenharmony_ci sclp_suspend_state = sclp_suspend_state_running; 10978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci sclp_pm_event(event, 0); 11008c2ecf20Sopenharmony_ci return 0; 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_cistatic int sclp_thaw(struct device *dev) 11048c2ecf20Sopenharmony_ci{ 11058c2ecf20Sopenharmony_ci return sclp_undo_suspend(SCLP_PM_EVENT_THAW); 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic int sclp_restore(struct device *dev) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci return sclp_undo_suspend(SCLP_PM_EVENT_RESTORE); 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic const struct dev_pm_ops sclp_pm_ops = { 11148c2ecf20Sopenharmony_ci .freeze = sclp_freeze, 11158c2ecf20Sopenharmony_ci .thaw = sclp_thaw, 11168c2ecf20Sopenharmony_ci .restore = sclp_restore, 11178c2ecf20Sopenharmony_ci}; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic ssize_t con_pages_show(struct device_driver *dev, char *buf) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci return sprintf(buf, "%i\n", sclp_console_pages); 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RO(con_pages); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_cistatic ssize_t con_drop_show(struct device_driver *dev, char *buf) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci return sprintf(buf, "%i\n", sclp_console_drop); 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RO(con_drop); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cistatic ssize_t con_full_show(struct device_driver *dev, char *buf) 11348c2ecf20Sopenharmony_ci{ 11358c2ecf20Sopenharmony_ci return sprintf(buf, "%lu\n", sclp_console_full); 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RO(con_full); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic struct attribute *sclp_drv_attrs[] = { 11418c2ecf20Sopenharmony_ci &driver_attr_con_pages.attr, 11428c2ecf20Sopenharmony_ci &driver_attr_con_drop.attr, 11438c2ecf20Sopenharmony_ci &driver_attr_con_full.attr, 11448c2ecf20Sopenharmony_ci NULL, 11458c2ecf20Sopenharmony_ci}; 11468c2ecf20Sopenharmony_cistatic struct attribute_group sclp_drv_attr_group = { 11478c2ecf20Sopenharmony_ci .attrs = sclp_drv_attrs, 11488c2ecf20Sopenharmony_ci}; 11498c2ecf20Sopenharmony_cistatic const struct attribute_group *sclp_drv_attr_groups[] = { 11508c2ecf20Sopenharmony_ci &sclp_drv_attr_group, 11518c2ecf20Sopenharmony_ci NULL, 11528c2ecf20Sopenharmony_ci}; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_cistatic struct platform_driver sclp_pdrv = { 11558c2ecf20Sopenharmony_ci .driver = { 11568c2ecf20Sopenharmony_ci .name = "sclp", 11578c2ecf20Sopenharmony_ci .pm = &sclp_pm_ops, 11588c2ecf20Sopenharmony_ci .groups = sclp_drv_attr_groups, 11598c2ecf20Sopenharmony_ci }, 11608c2ecf20Sopenharmony_ci}; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_cistatic struct platform_device *sclp_pdev; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci/* Initialize SCLP driver. Return zero if driver is operational, non-zero 11658c2ecf20Sopenharmony_ci * otherwise. */ 11668c2ecf20Sopenharmony_cistatic int 11678c2ecf20Sopenharmony_cisclp_init(void) 11688c2ecf20Sopenharmony_ci{ 11698c2ecf20Sopenharmony_ci unsigned long flags; 11708c2ecf20Sopenharmony_ci int rc = 0; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 11738c2ecf20Sopenharmony_ci /* Check for previous or running initialization */ 11748c2ecf20Sopenharmony_ci if (sclp_init_state != sclp_init_state_uninitialized) 11758c2ecf20Sopenharmony_ci goto fail_unlock; 11768c2ecf20Sopenharmony_ci sclp_init_state = sclp_init_state_initializing; 11778c2ecf20Sopenharmony_ci sclp_read_sccb = (void *) __get_free_page(GFP_ATOMIC | GFP_DMA); 11788c2ecf20Sopenharmony_ci sclp_init_sccb = (void *) __get_free_page(GFP_ATOMIC | GFP_DMA); 11798c2ecf20Sopenharmony_ci BUG_ON(!sclp_read_sccb || !sclp_init_sccb); 11808c2ecf20Sopenharmony_ci /* Set up variables */ 11818c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sclp_req_queue); 11828c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sclp_reg_list); 11838c2ecf20Sopenharmony_ci list_add(&sclp_state_change_event.list, &sclp_reg_list); 11848c2ecf20Sopenharmony_ci timer_setup(&sclp_request_timer, NULL, 0); 11858c2ecf20Sopenharmony_ci timer_setup(&sclp_queue_timer, sclp_req_queue_timeout, 0); 11868c2ecf20Sopenharmony_ci /* Check interface */ 11878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 11888c2ecf20Sopenharmony_ci rc = sclp_check_interface(); 11898c2ecf20Sopenharmony_ci spin_lock_irqsave(&sclp_lock, flags); 11908c2ecf20Sopenharmony_ci if (rc) 11918c2ecf20Sopenharmony_ci goto fail_init_state_uninitialized; 11928c2ecf20Sopenharmony_ci /* Register reboot handler */ 11938c2ecf20Sopenharmony_ci rc = register_reboot_notifier(&sclp_reboot_notifier); 11948c2ecf20Sopenharmony_ci if (rc) 11958c2ecf20Sopenharmony_ci goto fail_init_state_uninitialized; 11968c2ecf20Sopenharmony_ci /* Register interrupt handler */ 11978c2ecf20Sopenharmony_ci rc = register_external_irq(EXT_IRQ_SERVICE_SIG, sclp_interrupt_handler); 11988c2ecf20Sopenharmony_ci if (rc) 11998c2ecf20Sopenharmony_ci goto fail_unregister_reboot_notifier; 12008c2ecf20Sopenharmony_ci sclp_init_state = sclp_init_state_initialized; 12018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 12028c2ecf20Sopenharmony_ci /* Enable service-signal external interruption - needs to happen with 12038c2ecf20Sopenharmony_ci * IRQs enabled. */ 12048c2ecf20Sopenharmony_ci irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL); 12058c2ecf20Sopenharmony_ci sclp_init_mask(1); 12068c2ecf20Sopenharmony_ci return 0; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_cifail_unregister_reboot_notifier: 12098c2ecf20Sopenharmony_ci unregister_reboot_notifier(&sclp_reboot_notifier); 12108c2ecf20Sopenharmony_cifail_init_state_uninitialized: 12118c2ecf20Sopenharmony_ci sclp_init_state = sclp_init_state_uninitialized; 12128c2ecf20Sopenharmony_ci free_page((unsigned long) sclp_read_sccb); 12138c2ecf20Sopenharmony_ci free_page((unsigned long) sclp_init_sccb); 12148c2ecf20Sopenharmony_cifail_unlock: 12158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sclp_lock, flags); 12168c2ecf20Sopenharmony_ci return rc; 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci/* 12208c2ecf20Sopenharmony_ci * SCLP panic notifier: If we are suspended, we thaw SCLP in order to be able 12218c2ecf20Sopenharmony_ci * to print the panic message. 12228c2ecf20Sopenharmony_ci */ 12238c2ecf20Sopenharmony_cistatic int sclp_panic_notify(struct notifier_block *self, 12248c2ecf20Sopenharmony_ci unsigned long event, void *data) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci if (sclp_suspend_state == sclp_suspend_state_suspended) 12278c2ecf20Sopenharmony_ci sclp_undo_suspend(SCLP_PM_EVENT_THAW); 12288c2ecf20Sopenharmony_ci return NOTIFY_OK; 12298c2ecf20Sopenharmony_ci} 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_cistatic struct notifier_block sclp_on_panic_nb = { 12328c2ecf20Sopenharmony_ci .notifier_call = sclp_panic_notify, 12338c2ecf20Sopenharmony_ci .priority = SCLP_PANIC_PRIO, 12348c2ecf20Sopenharmony_ci}; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic __init int sclp_initcall(void) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci int rc; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci rc = platform_driver_register(&sclp_pdrv); 12418c2ecf20Sopenharmony_ci if (rc) 12428c2ecf20Sopenharmony_ci return rc; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci sclp_pdev = platform_device_register_simple("sclp", -1, NULL, 0); 12458c2ecf20Sopenharmony_ci rc = PTR_ERR_OR_ZERO(sclp_pdev); 12468c2ecf20Sopenharmony_ci if (rc) 12478c2ecf20Sopenharmony_ci goto fail_platform_driver_unregister; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci rc = atomic_notifier_chain_register(&panic_notifier_list, 12508c2ecf20Sopenharmony_ci &sclp_on_panic_nb); 12518c2ecf20Sopenharmony_ci if (rc) 12528c2ecf20Sopenharmony_ci goto fail_platform_device_unregister; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci return sclp_init(); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_cifail_platform_device_unregister: 12578c2ecf20Sopenharmony_ci platform_device_unregister(sclp_pdev); 12588c2ecf20Sopenharmony_cifail_platform_driver_unregister: 12598c2ecf20Sopenharmony_ci platform_driver_unregister(&sclp_pdrv); 12608c2ecf20Sopenharmony_ci return rc; 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ciarch_initcall(sclp_initcall); 1264