18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *   S/390 common I/O routines -- channel subsystem call
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *    Copyright IBM Corp. 1999,2012
68c2ecf20Sopenharmony_ci *    Author(s): Ingo Adlung (adlung@de.ibm.com)
78c2ecf20Sopenharmony_ci *		 Cornelia Huck (cornelia.huck@de.ibm.com)
88c2ecf20Sopenharmony_ci *		 Arnd Bergmann (arndb@de.ibm.com)
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "cio"
128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/device.h>
188c2ecf20Sopenharmony_ci#include <linux/mutex.h>
198c2ecf20Sopenharmony_ci#include <linux/pci.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <asm/cio.h>
228c2ecf20Sopenharmony_ci#include <asm/chpid.h>
238c2ecf20Sopenharmony_ci#include <asm/chsc.h>
248c2ecf20Sopenharmony_ci#include <asm/crw.h>
258c2ecf20Sopenharmony_ci#include <asm/isc.h>
268c2ecf20Sopenharmony_ci#include <asm/ebcdic.h>
278c2ecf20Sopenharmony_ci#include <asm/ap.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include "css.h"
308c2ecf20Sopenharmony_ci#include "cio.h"
318c2ecf20Sopenharmony_ci#include "cio_debug.h"
328c2ecf20Sopenharmony_ci#include "ioasm.h"
338c2ecf20Sopenharmony_ci#include "chp.h"
348c2ecf20Sopenharmony_ci#include "chsc.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic void *sei_page;
378c2ecf20Sopenharmony_cistatic void *chsc_page;
388c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(chsc_page_lock);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/**
418c2ecf20Sopenharmony_ci * chsc_error_from_response() - convert a chsc response to an error
428c2ecf20Sopenharmony_ci * @response: chsc response code
438c2ecf20Sopenharmony_ci *
448c2ecf20Sopenharmony_ci * Returns an appropriate Linux error code for @response.
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_ciint chsc_error_from_response(int response)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	switch (response) {
498c2ecf20Sopenharmony_ci	case 0x0001:
508c2ecf20Sopenharmony_ci		return 0;
518c2ecf20Sopenharmony_ci	case 0x0002:
528c2ecf20Sopenharmony_ci	case 0x0003:
538c2ecf20Sopenharmony_ci	case 0x0006:
548c2ecf20Sopenharmony_ci	case 0x0007:
558c2ecf20Sopenharmony_ci	case 0x0008:
568c2ecf20Sopenharmony_ci	case 0x000a:
578c2ecf20Sopenharmony_ci	case 0x0104:
588c2ecf20Sopenharmony_ci		return -EINVAL;
598c2ecf20Sopenharmony_ci	case 0x0004:
608c2ecf20Sopenharmony_ci	case 0x0106:		/* "Wrong Channel Parm" for the op 0x003d */
618c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
628c2ecf20Sopenharmony_ci	case 0x000b:
638c2ecf20Sopenharmony_ci	case 0x0107:		/* "Channel busy" for the op 0x003d */
648c2ecf20Sopenharmony_ci		return -EBUSY;
658c2ecf20Sopenharmony_ci	case 0x0100:
668c2ecf20Sopenharmony_ci	case 0x0102:
678c2ecf20Sopenharmony_ci		return -ENOMEM;
688c2ecf20Sopenharmony_ci	case 0x0108:		/* "HW limit exceeded" for the op 0x003d */
698c2ecf20Sopenharmony_ci		return -EUSERS;
708c2ecf20Sopenharmony_ci	default:
718c2ecf20Sopenharmony_ci		return -EIO;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(chsc_error_from_response);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistruct chsc_ssd_area {
778c2ecf20Sopenharmony_ci	struct chsc_header request;
788c2ecf20Sopenharmony_ci	u16 :10;
798c2ecf20Sopenharmony_ci	u16 ssid:2;
808c2ecf20Sopenharmony_ci	u16 :4;
818c2ecf20Sopenharmony_ci	u16 f_sch;	  /* first subchannel */
828c2ecf20Sopenharmony_ci	u16 :16;
838c2ecf20Sopenharmony_ci	u16 l_sch;	  /* last subchannel */
848c2ecf20Sopenharmony_ci	u32 :32;
858c2ecf20Sopenharmony_ci	struct chsc_header response;
868c2ecf20Sopenharmony_ci	u32 :32;
878c2ecf20Sopenharmony_ci	u8 sch_valid : 1;
888c2ecf20Sopenharmony_ci	u8 dev_valid : 1;
898c2ecf20Sopenharmony_ci	u8 st	     : 3; /* subchannel type */
908c2ecf20Sopenharmony_ci	u8 zeroes    : 3;
918c2ecf20Sopenharmony_ci	u8  unit_addr;	  /* unit address */
928c2ecf20Sopenharmony_ci	u16 devno;	  /* device number */
938c2ecf20Sopenharmony_ci	u8 path_mask;
948c2ecf20Sopenharmony_ci	u8 fla_valid_mask;
958c2ecf20Sopenharmony_ci	u16 sch;	  /* subchannel */
968c2ecf20Sopenharmony_ci	u8 chpid[8];	  /* chpids 0-7 */
978c2ecf20Sopenharmony_ci	u16 fla[8];	  /* full link addresses 0-7 */
988c2ecf20Sopenharmony_ci} __packed __aligned(PAGE_SIZE);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ciint chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct chsc_ssd_area *ssd_area;
1038c2ecf20Sopenharmony_ci	unsigned long flags;
1048c2ecf20Sopenharmony_ci	int ccode;
1058c2ecf20Sopenharmony_ci	int ret;
1068c2ecf20Sopenharmony_ci	int i;
1078c2ecf20Sopenharmony_ci	int mask;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chsc_page_lock, flags);
1108c2ecf20Sopenharmony_ci	memset(chsc_page, 0, PAGE_SIZE);
1118c2ecf20Sopenharmony_ci	ssd_area = chsc_page;
1128c2ecf20Sopenharmony_ci	ssd_area->request.length = 0x0010;
1138c2ecf20Sopenharmony_ci	ssd_area->request.code = 0x0004;
1148c2ecf20Sopenharmony_ci	ssd_area->ssid = schid.ssid;
1158c2ecf20Sopenharmony_ci	ssd_area->f_sch = schid.sch_no;
1168c2ecf20Sopenharmony_ci	ssd_area->l_sch = schid.sch_no;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	ccode = chsc(ssd_area);
1198c2ecf20Sopenharmony_ci	/* Check response. */
1208c2ecf20Sopenharmony_ci	if (ccode > 0) {
1218c2ecf20Sopenharmony_ci		ret = (ccode == 3) ? -ENODEV : -EBUSY;
1228c2ecf20Sopenharmony_ci		goto out;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci	ret = chsc_error_from_response(ssd_area->response.code);
1258c2ecf20Sopenharmony_ci	if (ret != 0) {
1268c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n",
1278c2ecf20Sopenharmony_ci			      schid.ssid, schid.sch_no,
1288c2ecf20Sopenharmony_ci			      ssd_area->response.code);
1298c2ecf20Sopenharmony_ci		goto out;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci	if (!ssd_area->sch_valid) {
1328c2ecf20Sopenharmony_ci		ret = -ENODEV;
1338c2ecf20Sopenharmony_ci		goto out;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci	/* Copy data */
1368c2ecf20Sopenharmony_ci	ret = 0;
1378c2ecf20Sopenharmony_ci	memset(ssd, 0, sizeof(struct chsc_ssd_info));
1388c2ecf20Sopenharmony_ci	if ((ssd_area->st != SUBCHANNEL_TYPE_IO) &&
1398c2ecf20Sopenharmony_ci	    (ssd_area->st != SUBCHANNEL_TYPE_MSG))
1408c2ecf20Sopenharmony_ci		goto out;
1418c2ecf20Sopenharmony_ci	ssd->path_mask = ssd_area->path_mask;
1428c2ecf20Sopenharmony_ci	ssd->fla_valid_mask = ssd_area->fla_valid_mask;
1438c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
1448c2ecf20Sopenharmony_ci		mask = 0x80 >> i;
1458c2ecf20Sopenharmony_ci		if (ssd_area->path_mask & mask) {
1468c2ecf20Sopenharmony_ci			chp_id_init(&ssd->chpid[i]);
1478c2ecf20Sopenharmony_ci			ssd->chpid[i].id = ssd_area->chpid[i];
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci		if (ssd_area->fla_valid_mask & mask)
1508c2ecf20Sopenharmony_ci			ssd->fla[i] = ssd_area->fla[i];
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ciout:
1538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chsc_page_lock, flags);
1548c2ecf20Sopenharmony_ci	return ret;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/**
1588c2ecf20Sopenharmony_ci * chsc_ssqd() - store subchannel QDIO data (SSQD)
1598c2ecf20Sopenharmony_ci * @schid: id of the subchannel on which SSQD is performed
1608c2ecf20Sopenharmony_ci * @ssqd: request and response block for SSQD
1618c2ecf20Sopenharmony_ci *
1628c2ecf20Sopenharmony_ci * Returns 0 on success.
1638c2ecf20Sopenharmony_ci */
1648c2ecf20Sopenharmony_ciint chsc_ssqd(struct subchannel_id schid, struct chsc_ssqd_area *ssqd)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	memset(ssqd, 0, sizeof(*ssqd));
1678c2ecf20Sopenharmony_ci	ssqd->request.length = 0x0010;
1688c2ecf20Sopenharmony_ci	ssqd->request.code = 0x0024;
1698c2ecf20Sopenharmony_ci	ssqd->first_sch = schid.sch_no;
1708c2ecf20Sopenharmony_ci	ssqd->last_sch = schid.sch_no;
1718c2ecf20Sopenharmony_ci	ssqd->ssid = schid.ssid;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (chsc(ssqd))
1748c2ecf20Sopenharmony_ci		return -EIO;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return chsc_error_from_response(ssqd->response.code);
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(chsc_ssqd);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci/**
1818c2ecf20Sopenharmony_ci * chsc_sadc() - set adapter device controls (SADC)
1828c2ecf20Sopenharmony_ci * @schid: id of the subchannel on which SADC is performed
1838c2ecf20Sopenharmony_ci * @scssc: request and response block for SADC
1848c2ecf20Sopenharmony_ci * @summary_indicator_addr: summary indicator address
1858c2ecf20Sopenharmony_ci * @subchannel_indicator_addr: subchannel indicator address
1868c2ecf20Sopenharmony_ci * @isc: Interruption Subclass for this subchannel
1878c2ecf20Sopenharmony_ci *
1888c2ecf20Sopenharmony_ci * Returns 0 on success.
1898c2ecf20Sopenharmony_ci */
1908c2ecf20Sopenharmony_ciint chsc_sadc(struct subchannel_id schid, struct chsc_scssc_area *scssc,
1918c2ecf20Sopenharmony_ci	      u64 summary_indicator_addr, u64 subchannel_indicator_addr, u8 isc)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	memset(scssc, 0, sizeof(*scssc));
1948c2ecf20Sopenharmony_ci	scssc->request.length = 0x0fe0;
1958c2ecf20Sopenharmony_ci	scssc->request.code = 0x0021;
1968c2ecf20Sopenharmony_ci	scssc->operation_code = 0;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	scssc->summary_indicator_addr = summary_indicator_addr;
1998c2ecf20Sopenharmony_ci	scssc->subchannel_indicator_addr = subchannel_indicator_addr;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	scssc->ks = PAGE_DEFAULT_KEY >> 4;
2028c2ecf20Sopenharmony_ci	scssc->kc = PAGE_DEFAULT_KEY >> 4;
2038c2ecf20Sopenharmony_ci	scssc->isc = isc;
2048c2ecf20Sopenharmony_ci	scssc->schid = schid;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* enable the time delay disablement facility */
2078c2ecf20Sopenharmony_ci	if (css_general_characteristics.aif_tdd)
2088c2ecf20Sopenharmony_ci		scssc->word_with_d_bit = 0x10000000;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (chsc(scssc))
2118c2ecf20Sopenharmony_ci		return -EIO;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return chsc_error_from_response(scssc->response.code);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(chsc_sadc);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
2208c2ecf20Sopenharmony_ci	if (sch->driver && sch->driver->chp_event)
2218c2ecf20Sopenharmony_ci		if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0)
2228c2ecf20Sopenharmony_ci			goto out_unreg;
2238c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
2248c2ecf20Sopenharmony_ci	return 0;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ciout_unreg:
2278c2ecf20Sopenharmony_ci	sch->lpm = 0;
2288c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
2298c2ecf20Sopenharmony_ci	css_schedule_eval(sch->schid);
2308c2ecf20Sopenharmony_ci	return 0;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_civoid chsc_chp_offline(struct chp_id chpid)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct channel_path *chp = chpid_to_chp(chpid);
2368c2ecf20Sopenharmony_ci	struct chp_link link;
2378c2ecf20Sopenharmony_ci	char dbf_txt[15];
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id);
2408c2ecf20Sopenharmony_ci	CIO_TRACE_EVENT(2, dbf_txt);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (chp_get_status(chpid) <= 0)
2438c2ecf20Sopenharmony_ci		return;
2448c2ecf20Sopenharmony_ci	memset(&link, 0, sizeof(struct chp_link));
2458c2ecf20Sopenharmony_ci	link.chpid = chpid;
2468c2ecf20Sopenharmony_ci	/* Wait until previous actions have settled. */
2478c2ecf20Sopenharmony_ci	css_wait_for_slow_path();
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	mutex_lock(&chp->lock);
2508c2ecf20Sopenharmony_ci	chp_update_desc(chp);
2518c2ecf20Sopenharmony_ci	mutex_unlock(&chp->lock);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &link);
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int __s390_process_res_acc(struct subchannel *sch, void *data)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
2598c2ecf20Sopenharmony_ci	if (sch->driver && sch->driver->chp_event)
2608c2ecf20Sopenharmony_ci		sch->driver->chp_event(sch, data, CHP_ONLINE);
2618c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	return 0;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic void s390_process_res_acc(struct chp_link *link)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	char dbf_txt[15];
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	sprintf(dbf_txt, "accpr%x.%02x", link->chpid.cssid,
2718c2ecf20Sopenharmony_ci		link->chpid.id);
2728c2ecf20Sopenharmony_ci	CIO_TRACE_EVENT( 2, dbf_txt);
2738c2ecf20Sopenharmony_ci	if (link->fla != 0) {
2748c2ecf20Sopenharmony_ci		sprintf(dbf_txt, "fla%x", link->fla);
2758c2ecf20Sopenharmony_ci		CIO_TRACE_EVENT( 2, dbf_txt);
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci	/* Wait until previous actions have settled. */
2788c2ecf20Sopenharmony_ci	css_wait_for_slow_path();
2798c2ecf20Sopenharmony_ci	/*
2808c2ecf20Sopenharmony_ci	 * I/O resources may have become accessible.
2818c2ecf20Sopenharmony_ci	 * Scan through all subchannels that may be concerned and
2828c2ecf20Sopenharmony_ci	 * do a validation on those.
2838c2ecf20Sopenharmony_ci	 * The more information we have (info), the less scanning
2848c2ecf20Sopenharmony_ci	 * will we have to do.
2858c2ecf20Sopenharmony_ci	 */
2868c2ecf20Sopenharmony_ci	for_each_subchannel_staged(__s390_process_res_acc, NULL, link);
2878c2ecf20Sopenharmony_ci	css_schedule_reprobe();
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistruct chsc_sei_nt0_area {
2918c2ecf20Sopenharmony_ci	u8  flags;
2928c2ecf20Sopenharmony_ci	u8  vf;				/* validity flags */
2938c2ecf20Sopenharmony_ci	u8  rs;				/* reporting source */
2948c2ecf20Sopenharmony_ci	u8  cc;				/* content code */
2958c2ecf20Sopenharmony_ci	u16 fla;			/* full link address */
2968c2ecf20Sopenharmony_ci	u16 rsid;			/* reporting source id */
2978c2ecf20Sopenharmony_ci	u32 reserved1;
2988c2ecf20Sopenharmony_ci	u32 reserved2;
2998c2ecf20Sopenharmony_ci	/* ccdf has to be big enough for a link-incident record */
3008c2ecf20Sopenharmony_ci	u8  ccdf[PAGE_SIZE - 24 - 16];	/* content-code dependent field */
3018c2ecf20Sopenharmony_ci} __packed;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistruct chsc_sei_nt2_area {
3048c2ecf20Sopenharmony_ci	u8  flags;			/* p and v bit */
3058c2ecf20Sopenharmony_ci	u8  reserved1;
3068c2ecf20Sopenharmony_ci	u8  reserved2;
3078c2ecf20Sopenharmony_ci	u8  cc;				/* content code */
3088c2ecf20Sopenharmony_ci	u32 reserved3[13];
3098c2ecf20Sopenharmony_ci	u8  ccdf[PAGE_SIZE - 24 - 56];	/* content-code dependent field */
3108c2ecf20Sopenharmony_ci} __packed;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci#define CHSC_SEI_NT0	(1ULL << 63)
3138c2ecf20Sopenharmony_ci#define CHSC_SEI_NT2	(1ULL << 61)
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistruct chsc_sei {
3168c2ecf20Sopenharmony_ci	struct chsc_header request;
3178c2ecf20Sopenharmony_ci	u32 reserved1;
3188c2ecf20Sopenharmony_ci	u64 ntsm;			/* notification type mask */
3198c2ecf20Sopenharmony_ci	struct chsc_header response;
3208c2ecf20Sopenharmony_ci	u32 :24;
3218c2ecf20Sopenharmony_ci	u8 nt;
3228c2ecf20Sopenharmony_ci	union {
3238c2ecf20Sopenharmony_ci		struct chsc_sei_nt0_area nt0_area;
3248c2ecf20Sopenharmony_ci		struct chsc_sei_nt2_area nt2_area;
3258c2ecf20Sopenharmony_ci		u8 nt_area[PAGE_SIZE - 24];
3268c2ecf20Sopenharmony_ci	} u;
3278c2ecf20Sopenharmony_ci} __packed __aligned(PAGE_SIZE);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci/*
3308c2ecf20Sopenharmony_ci * Link Incident Record as defined in SA22-7202, "ESCON I/O Interface"
3318c2ecf20Sopenharmony_ci */
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci#define LIR_IQ_CLASS_INFO		0
3348c2ecf20Sopenharmony_ci#define LIR_IQ_CLASS_DEGRADED		1
3358c2ecf20Sopenharmony_ci#define LIR_IQ_CLASS_NOT_OPERATIONAL	2
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistruct lir {
3388c2ecf20Sopenharmony_ci	struct {
3398c2ecf20Sopenharmony_ci		u32 null:1;
3408c2ecf20Sopenharmony_ci		u32 reserved:3;
3418c2ecf20Sopenharmony_ci		u32 class:2;
3428c2ecf20Sopenharmony_ci		u32 reserved2:2;
3438c2ecf20Sopenharmony_ci	} __packed iq;
3448c2ecf20Sopenharmony_ci	u32 ic:8;
3458c2ecf20Sopenharmony_ci	u32 reserved:16;
3468c2ecf20Sopenharmony_ci	struct node_descriptor incident_node;
3478c2ecf20Sopenharmony_ci	struct node_descriptor attached_node;
3488c2ecf20Sopenharmony_ci	u8 reserved2[32];
3498c2ecf20Sopenharmony_ci} __packed;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci#define PARAMS_LEN	10	/* PARAMS=xx,xxxxxx */
3528c2ecf20Sopenharmony_ci#define NODEID_LEN	35	/* NODEID=tttttt/mdl,mmm.ppssssssssssss,xxxx */
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci/* Copy EBCIDC text, convert to ASCII and optionally add delimiter. */
3558c2ecf20Sopenharmony_cistatic char *store_ebcdic(char *dest, const char *src, unsigned long len,
3568c2ecf20Sopenharmony_ci			  char delim)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	memcpy(dest, src, len);
3598c2ecf20Sopenharmony_ci	EBCASC(dest, len);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (delim)
3628c2ecf20Sopenharmony_ci		dest[len++] = delim;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return dest + len;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci/* Format node ID and parameters for output in LIR log message. */
3688c2ecf20Sopenharmony_cistatic void format_node_data(char *params, char *id, struct node_descriptor *nd)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	memset(params, 0, PARAMS_LEN);
3718c2ecf20Sopenharmony_ci	memset(id, 0, NODEID_LEN);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (nd->validity != ND_VALIDITY_VALID) {
3748c2ecf20Sopenharmony_ci		strncpy(params, "n/a", PARAMS_LEN - 1);
3758c2ecf20Sopenharmony_ci		strncpy(id, "n/a", NODEID_LEN - 1);
3768c2ecf20Sopenharmony_ci		return;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/* PARAMS=xx,xxxxxx */
3808c2ecf20Sopenharmony_ci	snprintf(params, PARAMS_LEN, "%02x,%06x", nd->byte0, nd->params);
3818c2ecf20Sopenharmony_ci	/* NODEID=tttttt/mdl,mmm.ppssssssssssss,xxxx */
3828c2ecf20Sopenharmony_ci	id = store_ebcdic(id, nd->type, sizeof(nd->type), '/');
3838c2ecf20Sopenharmony_ci	id = store_ebcdic(id, nd->model, sizeof(nd->model), ',');
3848c2ecf20Sopenharmony_ci	id = store_ebcdic(id, nd->manufacturer, sizeof(nd->manufacturer), '.');
3858c2ecf20Sopenharmony_ci	id = store_ebcdic(id, nd->plant, sizeof(nd->plant), 0);
3868c2ecf20Sopenharmony_ci	id = store_ebcdic(id, nd->seq, sizeof(nd->seq), ',');
3878c2ecf20Sopenharmony_ci	sprintf(id, "%04X", nd->tag);
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic void chsc_process_sei_link_incident(struct chsc_sei_nt0_area *sei_area)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	struct lir *lir = (struct lir *) &sei_area->ccdf;
3938c2ecf20Sopenharmony_ci	char iuparams[PARAMS_LEN], iunodeid[NODEID_LEN], auparams[PARAMS_LEN],
3948c2ecf20Sopenharmony_ci	     aunodeid[NODEID_LEN];
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x, iq=%02x)\n",
3978c2ecf20Sopenharmony_ci		      sei_area->rs, sei_area->rsid, sei_area->ccdf[0]);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* Ignore NULL Link Incident Records. */
4008c2ecf20Sopenharmony_ci	if (lir->iq.null)
4018c2ecf20Sopenharmony_ci		return;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	/* Inform user that a link requires maintenance actions because it has
4048c2ecf20Sopenharmony_ci	 * become degraded or not operational. Note that this log message is
4058c2ecf20Sopenharmony_ci	 * the primary intention behind a Link Incident Record. */
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	format_node_data(iuparams, iunodeid, &lir->incident_node);
4088c2ecf20Sopenharmony_ci	format_node_data(auparams, aunodeid, &lir->attached_node);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	switch (lir->iq.class) {
4118c2ecf20Sopenharmony_ci	case LIR_IQ_CLASS_DEGRADED:
4128c2ecf20Sopenharmony_ci		pr_warn("Link degraded: RS=%02x RSID=%04x IC=%02x "
4138c2ecf20Sopenharmony_ci			"IUPARAMS=%s IUNODEID=%s AUPARAMS=%s AUNODEID=%s\n",
4148c2ecf20Sopenharmony_ci			sei_area->rs, sei_area->rsid, lir->ic, iuparams,
4158c2ecf20Sopenharmony_ci			iunodeid, auparams, aunodeid);
4168c2ecf20Sopenharmony_ci		break;
4178c2ecf20Sopenharmony_ci	case LIR_IQ_CLASS_NOT_OPERATIONAL:
4188c2ecf20Sopenharmony_ci		pr_err("Link stopped: RS=%02x RSID=%04x IC=%02x "
4198c2ecf20Sopenharmony_ci		       "IUPARAMS=%s IUNODEID=%s AUPARAMS=%s AUNODEID=%s\n",
4208c2ecf20Sopenharmony_ci		       sei_area->rs, sei_area->rsid, lir->ic, iuparams,
4218c2ecf20Sopenharmony_ci		       iunodeid, auparams, aunodeid);
4228c2ecf20Sopenharmony_ci		break;
4238c2ecf20Sopenharmony_ci	default:
4248c2ecf20Sopenharmony_ci		break;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic void chsc_process_sei_res_acc(struct chsc_sei_nt0_area *sei_area)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	struct channel_path *chp;
4318c2ecf20Sopenharmony_ci	struct chp_link link;
4328c2ecf20Sopenharmony_ci	struct chp_id chpid;
4338c2ecf20Sopenharmony_ci	int status;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, "
4368c2ecf20Sopenharmony_ci		      "rs_id=%04x)\n", sei_area->rs, sei_area->rsid);
4378c2ecf20Sopenharmony_ci	if (sei_area->rs != 4)
4388c2ecf20Sopenharmony_ci		return;
4398c2ecf20Sopenharmony_ci	chp_id_init(&chpid);
4408c2ecf20Sopenharmony_ci	chpid.id = sei_area->rsid;
4418c2ecf20Sopenharmony_ci	/* allocate a new channel path structure, if needed */
4428c2ecf20Sopenharmony_ci	status = chp_get_status(chpid);
4438c2ecf20Sopenharmony_ci	if (!status)
4448c2ecf20Sopenharmony_ci		return;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (status < 0) {
4478c2ecf20Sopenharmony_ci		chp_new(chpid);
4488c2ecf20Sopenharmony_ci	} else {
4498c2ecf20Sopenharmony_ci		chp = chpid_to_chp(chpid);
4508c2ecf20Sopenharmony_ci		mutex_lock(&chp->lock);
4518c2ecf20Sopenharmony_ci		chp_update_desc(chp);
4528c2ecf20Sopenharmony_ci		mutex_unlock(&chp->lock);
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci	memset(&link, 0, sizeof(struct chp_link));
4558c2ecf20Sopenharmony_ci	link.chpid = chpid;
4568c2ecf20Sopenharmony_ci	if ((sei_area->vf & 0xc0) != 0) {
4578c2ecf20Sopenharmony_ci		link.fla = sei_area->fla;
4588c2ecf20Sopenharmony_ci		if ((sei_area->vf & 0xc0) == 0xc0)
4598c2ecf20Sopenharmony_ci			/* full link address */
4608c2ecf20Sopenharmony_ci			link.fla_mask = 0xffff;
4618c2ecf20Sopenharmony_ci		else
4628c2ecf20Sopenharmony_ci			/* link address */
4638c2ecf20Sopenharmony_ci			link.fla_mask = 0xff00;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci	s390_process_res_acc(&link);
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic void chsc_process_sei_chp_avail(struct chsc_sei_nt0_area *sei_area)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	struct channel_path *chp;
4718c2ecf20Sopenharmony_ci	struct chp_id chpid;
4728c2ecf20Sopenharmony_ci	u8 *data;
4738c2ecf20Sopenharmony_ci	int num;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	CIO_CRW_EVENT(4, "chsc: channel path availability information\n");
4768c2ecf20Sopenharmony_ci	if (sei_area->rs != 0)
4778c2ecf20Sopenharmony_ci		return;
4788c2ecf20Sopenharmony_ci	data = sei_area->ccdf;
4798c2ecf20Sopenharmony_ci	chp_id_init(&chpid);
4808c2ecf20Sopenharmony_ci	for (num = 0; num <= __MAX_CHPID; num++) {
4818c2ecf20Sopenharmony_ci		if (!chp_test_bit(data, num))
4828c2ecf20Sopenharmony_ci			continue;
4838c2ecf20Sopenharmony_ci		chpid.id = num;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(4, "Update information for channel path "
4868c2ecf20Sopenharmony_ci			      "%x.%02x\n", chpid.cssid, chpid.id);
4878c2ecf20Sopenharmony_ci		chp = chpid_to_chp(chpid);
4888c2ecf20Sopenharmony_ci		if (!chp) {
4898c2ecf20Sopenharmony_ci			chp_new(chpid);
4908c2ecf20Sopenharmony_ci			continue;
4918c2ecf20Sopenharmony_ci		}
4928c2ecf20Sopenharmony_ci		mutex_lock(&chp->lock);
4938c2ecf20Sopenharmony_ci		chp_update_desc(chp);
4948c2ecf20Sopenharmony_ci		mutex_unlock(&chp->lock);
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistruct chp_config_data {
4998c2ecf20Sopenharmony_ci	u8 map[32];
5008c2ecf20Sopenharmony_ci	u8 op;
5018c2ecf20Sopenharmony_ci	u8 pc;
5028c2ecf20Sopenharmony_ci};
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic void chsc_process_sei_chp_config(struct chsc_sei_nt0_area *sei_area)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	struct chp_config_data *data;
5078c2ecf20Sopenharmony_ci	struct chp_id chpid;
5088c2ecf20Sopenharmony_ci	int num;
5098c2ecf20Sopenharmony_ci	char *events[3] = {"configure", "deconfigure", "cancel deconfigure"};
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n");
5128c2ecf20Sopenharmony_ci	if (sei_area->rs != 0)
5138c2ecf20Sopenharmony_ci		return;
5148c2ecf20Sopenharmony_ci	data = (struct chp_config_data *) &(sei_area->ccdf);
5158c2ecf20Sopenharmony_ci	chp_id_init(&chpid);
5168c2ecf20Sopenharmony_ci	for (num = 0; num <= __MAX_CHPID; num++) {
5178c2ecf20Sopenharmony_ci		if (!chp_test_bit(data->map, num))
5188c2ecf20Sopenharmony_ci			continue;
5198c2ecf20Sopenharmony_ci		chpid.id = num;
5208c2ecf20Sopenharmony_ci		pr_notice("Processing %s for channel path %x.%02x\n",
5218c2ecf20Sopenharmony_ci			  events[data->op], chpid.cssid, chpid.id);
5228c2ecf20Sopenharmony_ci		switch (data->op) {
5238c2ecf20Sopenharmony_ci		case 0:
5248c2ecf20Sopenharmony_ci			chp_cfg_schedule(chpid, 1);
5258c2ecf20Sopenharmony_ci			break;
5268c2ecf20Sopenharmony_ci		case 1:
5278c2ecf20Sopenharmony_ci			chp_cfg_schedule(chpid, 0);
5288c2ecf20Sopenharmony_ci			break;
5298c2ecf20Sopenharmony_ci		case 2:
5308c2ecf20Sopenharmony_ci			chp_cfg_cancel_deconfigure(chpid);
5318c2ecf20Sopenharmony_ci			break;
5328c2ecf20Sopenharmony_ci		}
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic void chsc_process_sei_scm_change(struct chsc_sei_nt0_area *sei_area)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	int ret;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	CIO_CRW_EVENT(4, "chsc: scm change notification\n");
5418c2ecf20Sopenharmony_ci	if (sei_area->rs != 7)
5428c2ecf20Sopenharmony_ci		return;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	ret = scm_update_information();
5458c2ecf20Sopenharmony_ci	if (ret)
5468c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(0, "chsc: updating change notification"
5478c2ecf20Sopenharmony_ci			      " failed (rc=%d).\n", ret);
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic void chsc_process_sei_scm_avail(struct chsc_sei_nt0_area *sei_area)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	int ret;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	CIO_CRW_EVENT(4, "chsc: scm available information\n");
5558c2ecf20Sopenharmony_ci	if (sei_area->rs != 7)
5568c2ecf20Sopenharmony_ci		return;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	ret = scm_process_availability_information();
5598c2ecf20Sopenharmony_ci	if (ret)
5608c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(0, "chsc: process availability information"
5618c2ecf20Sopenharmony_ci			      " failed (rc=%d).\n", ret);
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic void chsc_process_sei_ap_cfg_chg(struct chsc_sei_nt0_area *sei_area)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	CIO_CRW_EVENT(3, "chsc: ap config changed\n");
5678c2ecf20Sopenharmony_ci	if (sei_area->rs != 5)
5688c2ecf20Sopenharmony_ci		return;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	ap_bus_cfg_chg();
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistatic void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	switch (sei_area->cc) {
5768c2ecf20Sopenharmony_ci	case 1:
5778c2ecf20Sopenharmony_ci		zpci_event_error(sei_area->ccdf);
5788c2ecf20Sopenharmony_ci		break;
5798c2ecf20Sopenharmony_ci	case 2:
5808c2ecf20Sopenharmony_ci		zpci_event_availability(sei_area->ccdf);
5818c2ecf20Sopenharmony_ci		break;
5828c2ecf20Sopenharmony_ci	default:
5838c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(2, "chsc: sei nt2 unhandled cc=%d\n",
5848c2ecf20Sopenharmony_ci			      sei_area->cc);
5858c2ecf20Sopenharmony_ci		break;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	/* which kind of information was stored? */
5928c2ecf20Sopenharmony_ci	switch (sei_area->cc) {
5938c2ecf20Sopenharmony_ci	case 1: /* link incident*/
5948c2ecf20Sopenharmony_ci		chsc_process_sei_link_incident(sei_area);
5958c2ecf20Sopenharmony_ci		break;
5968c2ecf20Sopenharmony_ci	case 2: /* i/o resource accessibility */
5978c2ecf20Sopenharmony_ci		chsc_process_sei_res_acc(sei_area);
5988c2ecf20Sopenharmony_ci		break;
5998c2ecf20Sopenharmony_ci	case 3: /* ap config changed */
6008c2ecf20Sopenharmony_ci		chsc_process_sei_ap_cfg_chg(sei_area);
6018c2ecf20Sopenharmony_ci		break;
6028c2ecf20Sopenharmony_ci	case 7: /* channel-path-availability information */
6038c2ecf20Sopenharmony_ci		chsc_process_sei_chp_avail(sei_area);
6048c2ecf20Sopenharmony_ci		break;
6058c2ecf20Sopenharmony_ci	case 8: /* channel-path-configuration notification */
6068c2ecf20Sopenharmony_ci		chsc_process_sei_chp_config(sei_area);
6078c2ecf20Sopenharmony_ci		break;
6088c2ecf20Sopenharmony_ci	case 12: /* scm change notification */
6098c2ecf20Sopenharmony_ci		chsc_process_sei_scm_change(sei_area);
6108c2ecf20Sopenharmony_ci		break;
6118c2ecf20Sopenharmony_ci	case 14: /* scm available notification */
6128c2ecf20Sopenharmony_ci		chsc_process_sei_scm_avail(sei_area);
6138c2ecf20Sopenharmony_ci		break;
6148c2ecf20Sopenharmony_ci	default: /* other stuff */
6158c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(2, "chsc: sei nt0 unhandled cc=%d\n",
6168c2ecf20Sopenharmony_ci			      sei_area->cc);
6178c2ecf20Sopenharmony_ci		break;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	/* Check if we might have lost some information. */
6218c2ecf20Sopenharmony_ci	if (sei_area->flags & 0x40) {
6228c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(2, "chsc: event overflow\n");
6238c2ecf20Sopenharmony_ci		css_schedule_eval_all();
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic void chsc_process_event_information(struct chsc_sei *sei, u64 ntsm)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	static int ntsm_unsupported;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	while (true) {
6328c2ecf20Sopenharmony_ci		memset(sei, 0, sizeof(*sei));
6338c2ecf20Sopenharmony_ci		sei->request.length = 0x0010;
6348c2ecf20Sopenharmony_ci		sei->request.code = 0x000e;
6358c2ecf20Sopenharmony_ci		if (!ntsm_unsupported)
6368c2ecf20Sopenharmony_ci			sei->ntsm = ntsm;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci		if (chsc(sei))
6398c2ecf20Sopenharmony_ci			break;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci		if (sei->response.code != 0x0001) {
6428c2ecf20Sopenharmony_ci			CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x, ntsm=%llx)\n",
6438c2ecf20Sopenharmony_ci				      sei->response.code, sei->ntsm);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci			if (sei->response.code == 3 && sei->ntsm) {
6468c2ecf20Sopenharmony_ci				/* Fallback for old firmware. */
6478c2ecf20Sopenharmony_ci				ntsm_unsupported = 1;
6488c2ecf20Sopenharmony_ci				continue;
6498c2ecf20Sopenharmony_ci			}
6508c2ecf20Sopenharmony_ci			break;
6518c2ecf20Sopenharmony_ci		}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(2, "chsc: sei successful (nt=%d)\n", sei->nt);
6548c2ecf20Sopenharmony_ci		switch (sei->nt) {
6558c2ecf20Sopenharmony_ci		case 0:
6568c2ecf20Sopenharmony_ci			chsc_process_sei_nt0(&sei->u.nt0_area);
6578c2ecf20Sopenharmony_ci			break;
6588c2ecf20Sopenharmony_ci		case 2:
6598c2ecf20Sopenharmony_ci			chsc_process_sei_nt2(&sei->u.nt2_area);
6608c2ecf20Sopenharmony_ci			break;
6618c2ecf20Sopenharmony_ci		default:
6628c2ecf20Sopenharmony_ci			CIO_CRW_EVENT(2, "chsc: unhandled nt: %d\n", sei->nt);
6638c2ecf20Sopenharmony_ci			break;
6648c2ecf20Sopenharmony_ci		}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci		if (!(sei->u.nt0_area.flags & 0x80))
6678c2ecf20Sopenharmony_ci			break;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci/*
6728c2ecf20Sopenharmony_ci * Handle channel subsystem related CRWs.
6738c2ecf20Sopenharmony_ci * Use store event information to find out what's going on.
6748c2ecf20Sopenharmony_ci *
6758c2ecf20Sopenharmony_ci * Note: Access to sei_page is serialized through machine check handler
6768c2ecf20Sopenharmony_ci * thread, so no need for locking.
6778c2ecf20Sopenharmony_ci */
6788c2ecf20Sopenharmony_cistatic void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	struct chsc_sei *sei = sei_page;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	if (overflow) {
6838c2ecf20Sopenharmony_ci		css_schedule_eval_all();
6848c2ecf20Sopenharmony_ci		return;
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci	CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, "
6878c2ecf20Sopenharmony_ci		      "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
6888c2ecf20Sopenharmony_ci		      crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
6898c2ecf20Sopenharmony_ci		      crw0->erc, crw0->rsid);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	CIO_TRACE_EVENT(2, "prcss");
6928c2ecf20Sopenharmony_ci	chsc_process_event_information(sei, CHSC_SEI_NT0 | CHSC_SEI_NT2);
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_civoid chsc_chp_online(struct chp_id chpid)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	struct channel_path *chp = chpid_to_chp(chpid);
6988c2ecf20Sopenharmony_ci	struct chp_link link;
6998c2ecf20Sopenharmony_ci	char dbf_txt[15];
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
7028c2ecf20Sopenharmony_ci	CIO_TRACE_EVENT(2, dbf_txt);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (chp_get_status(chpid) != 0) {
7058c2ecf20Sopenharmony_ci		memset(&link, 0, sizeof(struct chp_link));
7068c2ecf20Sopenharmony_ci		link.chpid = chpid;
7078c2ecf20Sopenharmony_ci		/* Wait until previous actions have settled. */
7088c2ecf20Sopenharmony_ci		css_wait_for_slow_path();
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci		mutex_lock(&chp->lock);
7118c2ecf20Sopenharmony_ci		chp_update_desc(chp);
7128c2ecf20Sopenharmony_ci		mutex_unlock(&chp->lock);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci		for_each_subchannel_staged(__s390_process_res_acc, NULL,
7158c2ecf20Sopenharmony_ci					   &link);
7168c2ecf20Sopenharmony_ci		css_schedule_reprobe();
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cistatic void __s390_subchannel_vary_chpid(struct subchannel *sch,
7218c2ecf20Sopenharmony_ci					 struct chp_id chpid, int on)
7228c2ecf20Sopenharmony_ci{
7238c2ecf20Sopenharmony_ci	unsigned long flags;
7248c2ecf20Sopenharmony_ci	struct chp_link link;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	memset(&link, 0, sizeof(struct chp_link));
7278c2ecf20Sopenharmony_ci	link.chpid = chpid;
7288c2ecf20Sopenharmony_ci	spin_lock_irqsave(sch->lock, flags);
7298c2ecf20Sopenharmony_ci	if (sch->driver && sch->driver->chp_event)
7308c2ecf20Sopenharmony_ci		sch->driver->chp_event(sch, &link,
7318c2ecf20Sopenharmony_ci				       on ? CHP_VARY_ON : CHP_VARY_OFF);
7328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(sch->lock, flags);
7338c2ecf20Sopenharmony_ci}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_cistatic int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data)
7368c2ecf20Sopenharmony_ci{
7378c2ecf20Sopenharmony_ci	struct chp_id *chpid = data;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	__s390_subchannel_vary_chpid(sch, *chpid, 0);
7408c2ecf20Sopenharmony_ci	return 0;
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_cistatic int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	struct chp_id *chpid = data;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	__s390_subchannel_vary_chpid(sch, *chpid, 1);
7488c2ecf20Sopenharmony_ci	return 0;
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci/**
7528c2ecf20Sopenharmony_ci * chsc_chp_vary - propagate channel-path vary operation to subchannels
7538c2ecf20Sopenharmony_ci * @chpid: channl-path ID
7548c2ecf20Sopenharmony_ci * @on: non-zero for vary online, zero for vary offline
7558c2ecf20Sopenharmony_ci */
7568c2ecf20Sopenharmony_ciint chsc_chp_vary(struct chp_id chpid, int on)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	struct channel_path *chp = chpid_to_chp(chpid);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	/*
7618c2ecf20Sopenharmony_ci	 * Redo PathVerification on the devices the chpid connects to
7628c2ecf20Sopenharmony_ci	 */
7638c2ecf20Sopenharmony_ci	if (on) {
7648c2ecf20Sopenharmony_ci		/* Try to update the channel path description. */
7658c2ecf20Sopenharmony_ci		chp_update_desc(chp);
7668c2ecf20Sopenharmony_ci		for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
7678c2ecf20Sopenharmony_ci					   NULL, &chpid);
7688c2ecf20Sopenharmony_ci		css_schedule_reprobe();
7698c2ecf20Sopenharmony_ci	} else
7708c2ecf20Sopenharmony_ci		for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
7718c2ecf20Sopenharmony_ci					   NULL, &chpid);
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	return 0;
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_cistatic void
7778c2ecf20Sopenharmony_cichsc_remove_cmg_attr(struct channel_subsystem *css)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	int i;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	for (i = 0; i <= __MAX_CHPID; i++) {
7828c2ecf20Sopenharmony_ci		if (!css->chps[i])
7838c2ecf20Sopenharmony_ci			continue;
7848c2ecf20Sopenharmony_ci		chp_remove_cmg_attr(css->chps[i]);
7858c2ecf20Sopenharmony_ci	}
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cistatic int
7898c2ecf20Sopenharmony_cichsc_add_cmg_attr(struct channel_subsystem *css)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	int i, ret;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	ret = 0;
7948c2ecf20Sopenharmony_ci	for (i = 0; i <= __MAX_CHPID; i++) {
7958c2ecf20Sopenharmony_ci		if (!css->chps[i])
7968c2ecf20Sopenharmony_ci			continue;
7978c2ecf20Sopenharmony_ci		ret = chp_add_cmg_attr(css->chps[i]);
7988c2ecf20Sopenharmony_ci		if (ret)
7998c2ecf20Sopenharmony_ci			goto cleanup;
8008c2ecf20Sopenharmony_ci	}
8018c2ecf20Sopenharmony_ci	return ret;
8028c2ecf20Sopenharmony_cicleanup:
8038c2ecf20Sopenharmony_ci	for (--i; i >= 0; i--) {
8048c2ecf20Sopenharmony_ci		if (!css->chps[i])
8058c2ecf20Sopenharmony_ci			continue;
8068c2ecf20Sopenharmony_ci		chp_remove_cmg_attr(css->chps[i]);
8078c2ecf20Sopenharmony_ci	}
8088c2ecf20Sopenharmony_ci	return ret;
8098c2ecf20Sopenharmony_ci}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ciint __chsc_do_secm(struct channel_subsystem *css, int enable)
8128c2ecf20Sopenharmony_ci{
8138c2ecf20Sopenharmony_ci	struct {
8148c2ecf20Sopenharmony_ci		struct chsc_header request;
8158c2ecf20Sopenharmony_ci		u32 operation_code : 2;
8168c2ecf20Sopenharmony_ci		u32 : 30;
8178c2ecf20Sopenharmony_ci		u32 key : 4;
8188c2ecf20Sopenharmony_ci		u32 : 28;
8198c2ecf20Sopenharmony_ci		u32 zeroes1;
8208c2ecf20Sopenharmony_ci		u32 cub_addr1;
8218c2ecf20Sopenharmony_ci		u32 zeroes2;
8228c2ecf20Sopenharmony_ci		u32 cub_addr2;
8238c2ecf20Sopenharmony_ci		u32 reserved[13];
8248c2ecf20Sopenharmony_ci		struct chsc_header response;
8258c2ecf20Sopenharmony_ci		u32 status : 8;
8268c2ecf20Sopenharmony_ci		u32 : 4;
8278c2ecf20Sopenharmony_ci		u32 fmt : 4;
8288c2ecf20Sopenharmony_ci		u32 : 16;
8298c2ecf20Sopenharmony_ci	} *secm_area;
8308c2ecf20Sopenharmony_ci	unsigned long flags;
8318c2ecf20Sopenharmony_ci	int ret, ccode;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chsc_page_lock, flags);
8348c2ecf20Sopenharmony_ci	memset(chsc_page, 0, PAGE_SIZE);
8358c2ecf20Sopenharmony_ci	secm_area = chsc_page;
8368c2ecf20Sopenharmony_ci	secm_area->request.length = 0x0050;
8378c2ecf20Sopenharmony_ci	secm_area->request.code = 0x0016;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	secm_area->key = PAGE_DEFAULT_KEY >> 4;
8408c2ecf20Sopenharmony_ci	secm_area->cub_addr1 = (u64)(unsigned long)css->cub_addr1;
8418c2ecf20Sopenharmony_ci	secm_area->cub_addr2 = (u64)(unsigned long)css->cub_addr2;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	secm_area->operation_code = enable ? 0 : 1;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	ccode = chsc(secm_area);
8468c2ecf20Sopenharmony_ci	if (ccode > 0) {
8478c2ecf20Sopenharmony_ci		ret = (ccode == 3) ? -ENODEV : -EBUSY;
8488c2ecf20Sopenharmony_ci		goto out;
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	switch (secm_area->response.code) {
8528c2ecf20Sopenharmony_ci	case 0x0102:
8538c2ecf20Sopenharmony_ci	case 0x0103:
8548c2ecf20Sopenharmony_ci		ret = -EINVAL;
8558c2ecf20Sopenharmony_ci		break;
8568c2ecf20Sopenharmony_ci	default:
8578c2ecf20Sopenharmony_ci		ret = chsc_error_from_response(secm_area->response.code);
8588c2ecf20Sopenharmony_ci	}
8598c2ecf20Sopenharmony_ci	if (ret != 0)
8608c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n",
8618c2ecf20Sopenharmony_ci			      secm_area->response.code);
8628c2ecf20Sopenharmony_ciout:
8638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chsc_page_lock, flags);
8648c2ecf20Sopenharmony_ci	return ret;
8658c2ecf20Sopenharmony_ci}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ciint
8688c2ecf20Sopenharmony_cichsc_secm(struct channel_subsystem *css, int enable)
8698c2ecf20Sopenharmony_ci{
8708c2ecf20Sopenharmony_ci	int ret;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	if (enable && !css->cm_enabled) {
8738c2ecf20Sopenharmony_ci		css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
8748c2ecf20Sopenharmony_ci		css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
8758c2ecf20Sopenharmony_ci		if (!css->cub_addr1 || !css->cub_addr2) {
8768c2ecf20Sopenharmony_ci			free_page((unsigned long)css->cub_addr1);
8778c2ecf20Sopenharmony_ci			free_page((unsigned long)css->cub_addr2);
8788c2ecf20Sopenharmony_ci			return -ENOMEM;
8798c2ecf20Sopenharmony_ci		}
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci	ret = __chsc_do_secm(css, enable);
8828c2ecf20Sopenharmony_ci	if (!ret) {
8838c2ecf20Sopenharmony_ci		css->cm_enabled = enable;
8848c2ecf20Sopenharmony_ci		if (css->cm_enabled) {
8858c2ecf20Sopenharmony_ci			ret = chsc_add_cmg_attr(css);
8868c2ecf20Sopenharmony_ci			if (ret) {
8878c2ecf20Sopenharmony_ci				__chsc_do_secm(css, 0);
8888c2ecf20Sopenharmony_ci				css->cm_enabled = 0;
8898c2ecf20Sopenharmony_ci			}
8908c2ecf20Sopenharmony_ci		} else
8918c2ecf20Sopenharmony_ci			chsc_remove_cmg_attr(css);
8928c2ecf20Sopenharmony_ci	}
8938c2ecf20Sopenharmony_ci	if (!css->cm_enabled) {
8948c2ecf20Sopenharmony_ci		free_page((unsigned long)css->cub_addr1);
8958c2ecf20Sopenharmony_ci		free_page((unsigned long)css->cub_addr2);
8968c2ecf20Sopenharmony_ci	}
8978c2ecf20Sopenharmony_ci	return ret;
8988c2ecf20Sopenharmony_ci}
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ciint chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
9018c2ecf20Sopenharmony_ci				     int c, int m, void *page)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	struct chsc_scpd *scpd_area;
9048c2ecf20Sopenharmony_ci	int ccode, ret;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	if ((rfmt == 1 || rfmt == 0) && c == 1 &&
9078c2ecf20Sopenharmony_ci	    !css_general_characteristics.fcs)
9088c2ecf20Sopenharmony_ci		return -EINVAL;
9098c2ecf20Sopenharmony_ci	if ((rfmt == 2) && !css_general_characteristics.cib)
9108c2ecf20Sopenharmony_ci		return -EINVAL;
9118c2ecf20Sopenharmony_ci	if ((rfmt == 3) && !css_general_characteristics.util_str)
9128c2ecf20Sopenharmony_ci		return -EINVAL;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	memset(page, 0, PAGE_SIZE);
9158c2ecf20Sopenharmony_ci	scpd_area = page;
9168c2ecf20Sopenharmony_ci	scpd_area->request.length = 0x0010;
9178c2ecf20Sopenharmony_ci	scpd_area->request.code = 0x0002;
9188c2ecf20Sopenharmony_ci	scpd_area->cssid = chpid.cssid;
9198c2ecf20Sopenharmony_ci	scpd_area->first_chpid = chpid.id;
9208c2ecf20Sopenharmony_ci	scpd_area->last_chpid = chpid.id;
9218c2ecf20Sopenharmony_ci	scpd_area->m = m;
9228c2ecf20Sopenharmony_ci	scpd_area->c = c;
9238c2ecf20Sopenharmony_ci	scpd_area->fmt = fmt;
9248c2ecf20Sopenharmony_ci	scpd_area->rfmt = rfmt;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	ccode = chsc(scpd_area);
9278c2ecf20Sopenharmony_ci	if (ccode > 0)
9288c2ecf20Sopenharmony_ci		return (ccode == 3) ? -ENODEV : -EBUSY;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	ret = chsc_error_from_response(scpd_area->response.code);
9318c2ecf20Sopenharmony_ci	if (ret)
9328c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n",
9338c2ecf20Sopenharmony_ci			      scpd_area->response.code);
9348c2ecf20Sopenharmony_ci	return ret;
9358c2ecf20Sopenharmony_ci}
9368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci#define chsc_det_chp_desc(FMT, c)					\
9398c2ecf20Sopenharmony_ciint chsc_determine_fmt##FMT##_channel_path_desc(			\
9408c2ecf20Sopenharmony_ci	struct chp_id chpid, struct channel_path_desc_fmt##FMT *desc)	\
9418c2ecf20Sopenharmony_ci{									\
9428c2ecf20Sopenharmony_ci	struct chsc_scpd *scpd_area;					\
9438c2ecf20Sopenharmony_ci	unsigned long flags;						\
9448c2ecf20Sopenharmony_ci	int ret;							\
9458c2ecf20Sopenharmony_ci									\
9468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chsc_page_lock, flags);			\
9478c2ecf20Sopenharmony_ci	scpd_area = chsc_page;						\
9488c2ecf20Sopenharmony_ci	ret = chsc_determine_channel_path_desc(chpid, 0, FMT, c, 0,	\
9498c2ecf20Sopenharmony_ci					       scpd_area);		\
9508c2ecf20Sopenharmony_ci	if (ret)							\
9518c2ecf20Sopenharmony_ci		goto out;						\
9528c2ecf20Sopenharmony_ci									\
9538c2ecf20Sopenharmony_ci	memcpy(desc, scpd_area->data, sizeof(*desc));			\
9548c2ecf20Sopenharmony_ciout:									\
9558c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chsc_page_lock, flags);			\
9568c2ecf20Sopenharmony_ci	return ret;							\
9578c2ecf20Sopenharmony_ci}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_cichsc_det_chp_desc(0, 0)
9608c2ecf20Sopenharmony_cichsc_det_chp_desc(1, 1)
9618c2ecf20Sopenharmony_cichsc_det_chp_desc(3, 0)
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_cistatic void
9648c2ecf20Sopenharmony_cichsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
9658c2ecf20Sopenharmony_ci			  struct cmg_chars *chars)
9668c2ecf20Sopenharmony_ci{
9678c2ecf20Sopenharmony_ci	int i, mask;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	for (i = 0; i < NR_MEASUREMENT_CHARS; i++) {
9708c2ecf20Sopenharmony_ci		mask = 0x80 >> (i + 3);
9718c2ecf20Sopenharmony_ci		if (cmcv & mask)
9728c2ecf20Sopenharmony_ci			chp->cmg_chars.values[i] = chars->values[i];
9738c2ecf20Sopenharmony_ci		else
9748c2ecf20Sopenharmony_ci			chp->cmg_chars.values[i] = 0;
9758c2ecf20Sopenharmony_ci	}
9768c2ecf20Sopenharmony_ci}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ciint chsc_get_channel_measurement_chars(struct channel_path *chp)
9798c2ecf20Sopenharmony_ci{
9808c2ecf20Sopenharmony_ci	unsigned long flags;
9818c2ecf20Sopenharmony_ci	int ccode, ret;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	struct {
9848c2ecf20Sopenharmony_ci		struct chsc_header request;
9858c2ecf20Sopenharmony_ci		u32 : 24;
9868c2ecf20Sopenharmony_ci		u32 first_chpid : 8;
9878c2ecf20Sopenharmony_ci		u32 : 24;
9888c2ecf20Sopenharmony_ci		u32 last_chpid : 8;
9898c2ecf20Sopenharmony_ci		u32 zeroes1;
9908c2ecf20Sopenharmony_ci		struct chsc_header response;
9918c2ecf20Sopenharmony_ci		u32 zeroes2;
9928c2ecf20Sopenharmony_ci		u32 not_valid : 1;
9938c2ecf20Sopenharmony_ci		u32 shared : 1;
9948c2ecf20Sopenharmony_ci		u32 : 22;
9958c2ecf20Sopenharmony_ci		u32 chpid : 8;
9968c2ecf20Sopenharmony_ci		u32 cmcv : 5;
9978c2ecf20Sopenharmony_ci		u32 : 11;
9988c2ecf20Sopenharmony_ci		u32 cmgq : 8;
9998c2ecf20Sopenharmony_ci		u32 cmg : 8;
10008c2ecf20Sopenharmony_ci		u32 zeroes3;
10018c2ecf20Sopenharmony_ci		u32 data[NR_MEASUREMENT_CHARS];
10028c2ecf20Sopenharmony_ci	} *scmc_area;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	chp->shared = -1;
10058c2ecf20Sopenharmony_ci	chp->cmg = -1;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if (!css_chsc_characteristics.scmc || !css_chsc_characteristics.secm)
10088c2ecf20Sopenharmony_ci		return -EINVAL;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chsc_page_lock, flags);
10118c2ecf20Sopenharmony_ci	memset(chsc_page, 0, PAGE_SIZE);
10128c2ecf20Sopenharmony_ci	scmc_area = chsc_page;
10138c2ecf20Sopenharmony_ci	scmc_area->request.length = 0x0010;
10148c2ecf20Sopenharmony_ci	scmc_area->request.code = 0x0022;
10158c2ecf20Sopenharmony_ci	scmc_area->first_chpid = chp->chpid.id;
10168c2ecf20Sopenharmony_ci	scmc_area->last_chpid = chp->chpid.id;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	ccode = chsc(scmc_area);
10198c2ecf20Sopenharmony_ci	if (ccode > 0) {
10208c2ecf20Sopenharmony_ci		ret = (ccode == 3) ? -ENODEV : -EBUSY;
10218c2ecf20Sopenharmony_ci		goto out;
10228c2ecf20Sopenharmony_ci	}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	ret = chsc_error_from_response(scmc_area->response.code);
10258c2ecf20Sopenharmony_ci	if (ret) {
10268c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(2, "chsc: scmc failed (rc=%04x)\n",
10278c2ecf20Sopenharmony_ci			      scmc_area->response.code);
10288c2ecf20Sopenharmony_ci		goto out;
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci	if (scmc_area->not_valid)
10318c2ecf20Sopenharmony_ci		goto out;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	chp->cmg = scmc_area->cmg;
10348c2ecf20Sopenharmony_ci	chp->shared = scmc_area->shared;
10358c2ecf20Sopenharmony_ci	if (chp->cmg != 2 && chp->cmg != 3) {
10368c2ecf20Sopenharmony_ci		/* No cmg-dependent data. */
10378c2ecf20Sopenharmony_ci		goto out;
10388c2ecf20Sopenharmony_ci	}
10398c2ecf20Sopenharmony_ci	chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
10408c2ecf20Sopenharmony_ci				  (struct cmg_chars *) &scmc_area->data);
10418c2ecf20Sopenharmony_ciout:
10428c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chsc_page_lock, flags);
10438c2ecf20Sopenharmony_ci	return ret;
10448c2ecf20Sopenharmony_ci}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ciint __init chsc_init(void)
10478c2ecf20Sopenharmony_ci{
10488c2ecf20Sopenharmony_ci	int ret;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
10518c2ecf20Sopenharmony_ci	chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
10528c2ecf20Sopenharmony_ci	if (!sei_page || !chsc_page) {
10538c2ecf20Sopenharmony_ci		ret = -ENOMEM;
10548c2ecf20Sopenharmony_ci		goto out_err;
10558c2ecf20Sopenharmony_ci	}
10568c2ecf20Sopenharmony_ci	ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw);
10578c2ecf20Sopenharmony_ci	if (ret)
10588c2ecf20Sopenharmony_ci		goto out_err;
10598c2ecf20Sopenharmony_ci	return ret;
10608c2ecf20Sopenharmony_ciout_err:
10618c2ecf20Sopenharmony_ci	free_page((unsigned long)chsc_page);
10628c2ecf20Sopenharmony_ci	free_page((unsigned long)sei_page);
10638c2ecf20Sopenharmony_ci	return ret;
10648c2ecf20Sopenharmony_ci}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_civoid __init chsc_init_cleanup(void)
10678c2ecf20Sopenharmony_ci{
10688c2ecf20Sopenharmony_ci	crw_unregister_handler(CRW_RSC_CSS);
10698c2ecf20Sopenharmony_ci	free_page((unsigned long)chsc_page);
10708c2ecf20Sopenharmony_ci	free_page((unsigned long)sei_page);
10718c2ecf20Sopenharmony_ci}
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ciint __chsc_enable_facility(struct chsc_sda_area *sda_area, int operation_code)
10748c2ecf20Sopenharmony_ci{
10758c2ecf20Sopenharmony_ci	int ret;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	sda_area->request.length = 0x0400;
10788c2ecf20Sopenharmony_ci	sda_area->request.code = 0x0031;
10798c2ecf20Sopenharmony_ci	sda_area->operation_code = operation_code;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	ret = chsc(sda_area);
10828c2ecf20Sopenharmony_ci	if (ret > 0) {
10838c2ecf20Sopenharmony_ci		ret = (ret == 3) ? -ENODEV : -EBUSY;
10848c2ecf20Sopenharmony_ci		goto out;
10858c2ecf20Sopenharmony_ci	}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	switch (sda_area->response.code) {
10888c2ecf20Sopenharmony_ci	case 0x0101:
10898c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
10908c2ecf20Sopenharmony_ci		break;
10918c2ecf20Sopenharmony_ci	default:
10928c2ecf20Sopenharmony_ci		ret = chsc_error_from_response(sda_area->response.code);
10938c2ecf20Sopenharmony_ci	}
10948c2ecf20Sopenharmony_ciout:
10958c2ecf20Sopenharmony_ci	return ret;
10968c2ecf20Sopenharmony_ci}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ciint chsc_enable_facility(int operation_code)
10998c2ecf20Sopenharmony_ci{
11008c2ecf20Sopenharmony_ci	struct chsc_sda_area *sda_area;
11018c2ecf20Sopenharmony_ci	unsigned long flags;
11028c2ecf20Sopenharmony_ci	int ret;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chsc_page_lock, flags);
11058c2ecf20Sopenharmony_ci	memset(chsc_page, 0, PAGE_SIZE);
11068c2ecf20Sopenharmony_ci	sda_area = chsc_page;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	ret = __chsc_enable_facility(sda_area, operation_code);
11098c2ecf20Sopenharmony_ci	if (ret != 0)
11108c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n",
11118c2ecf20Sopenharmony_ci			      operation_code, sda_area->response.code);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chsc_page_lock, flags);
11148c2ecf20Sopenharmony_ci	return ret;
11158c2ecf20Sopenharmony_ci}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ciint __init chsc_get_cssid_iid(int idx, u8 *cssid, u8 *iid)
11188c2ecf20Sopenharmony_ci{
11198c2ecf20Sopenharmony_ci	struct {
11208c2ecf20Sopenharmony_ci		struct chsc_header request;
11218c2ecf20Sopenharmony_ci		u8 atype;
11228c2ecf20Sopenharmony_ci		u32 : 24;
11238c2ecf20Sopenharmony_ci		u32 reserved1[6];
11248c2ecf20Sopenharmony_ci		struct chsc_header response;
11258c2ecf20Sopenharmony_ci		u32 reserved2[3];
11268c2ecf20Sopenharmony_ci		struct {
11278c2ecf20Sopenharmony_ci			u8 cssid;
11288c2ecf20Sopenharmony_ci			u8 iid;
11298c2ecf20Sopenharmony_ci			u32 : 16;
11308c2ecf20Sopenharmony_ci		} list[0];
11318c2ecf20Sopenharmony_ci	} *sdcal_area;
11328c2ecf20Sopenharmony_ci	int ret;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	spin_lock_irq(&chsc_page_lock);
11358c2ecf20Sopenharmony_ci	memset(chsc_page, 0, PAGE_SIZE);
11368c2ecf20Sopenharmony_ci	sdcal_area = chsc_page;
11378c2ecf20Sopenharmony_ci	sdcal_area->request.length = 0x0020;
11388c2ecf20Sopenharmony_ci	sdcal_area->request.code = 0x0034;
11398c2ecf20Sopenharmony_ci	sdcal_area->atype = 4;
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	ret = chsc(sdcal_area);
11428c2ecf20Sopenharmony_ci	if (ret) {
11438c2ecf20Sopenharmony_ci		ret = (ret == 3) ? -ENODEV : -EBUSY;
11448c2ecf20Sopenharmony_ci		goto exit;
11458c2ecf20Sopenharmony_ci	}
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	ret = chsc_error_from_response(sdcal_area->response.code);
11488c2ecf20Sopenharmony_ci	if (ret) {
11498c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(2, "chsc: sdcal failed (rc=%04x)\n",
11508c2ecf20Sopenharmony_ci			      sdcal_area->response.code);
11518c2ecf20Sopenharmony_ci		goto exit;
11528c2ecf20Sopenharmony_ci	}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	if ((addr_t) &sdcal_area->list[idx] <
11558c2ecf20Sopenharmony_ci	    (addr_t) &sdcal_area->response + sdcal_area->response.length) {
11568c2ecf20Sopenharmony_ci		*cssid = sdcal_area->list[idx].cssid;
11578c2ecf20Sopenharmony_ci		*iid = sdcal_area->list[idx].iid;
11588c2ecf20Sopenharmony_ci	}
11598c2ecf20Sopenharmony_ci	else
11608c2ecf20Sopenharmony_ci		ret = -ENODEV;
11618c2ecf20Sopenharmony_ciexit:
11628c2ecf20Sopenharmony_ci	spin_unlock_irq(&chsc_page_lock);
11638c2ecf20Sopenharmony_ci	return ret;
11648c2ecf20Sopenharmony_ci}
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_cistruct css_general_char css_general_characteristics;
11678c2ecf20Sopenharmony_cistruct css_chsc_char css_chsc_characteristics;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ciint __init
11708c2ecf20Sopenharmony_cichsc_determine_css_characteristics(void)
11718c2ecf20Sopenharmony_ci{
11728c2ecf20Sopenharmony_ci	unsigned long flags;
11738c2ecf20Sopenharmony_ci	int result;
11748c2ecf20Sopenharmony_ci	struct {
11758c2ecf20Sopenharmony_ci		struct chsc_header request;
11768c2ecf20Sopenharmony_ci		u32 reserved1;
11778c2ecf20Sopenharmony_ci		u32 reserved2;
11788c2ecf20Sopenharmony_ci		u32 reserved3;
11798c2ecf20Sopenharmony_ci		struct chsc_header response;
11808c2ecf20Sopenharmony_ci		u32 reserved4;
11818c2ecf20Sopenharmony_ci		u32 general_char[510];
11828c2ecf20Sopenharmony_ci		u32 chsc_char[508];
11838c2ecf20Sopenharmony_ci	} *scsc_area;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chsc_page_lock, flags);
11868c2ecf20Sopenharmony_ci	memset(chsc_page, 0, PAGE_SIZE);
11878c2ecf20Sopenharmony_ci	scsc_area = chsc_page;
11888c2ecf20Sopenharmony_ci	scsc_area->request.length = 0x0010;
11898c2ecf20Sopenharmony_ci	scsc_area->request.code = 0x0010;
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	result = chsc(scsc_area);
11928c2ecf20Sopenharmony_ci	if (result) {
11938c2ecf20Sopenharmony_ci		result = (result == 3) ? -ENODEV : -EBUSY;
11948c2ecf20Sopenharmony_ci		goto exit;
11958c2ecf20Sopenharmony_ci	}
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	result = chsc_error_from_response(scsc_area->response.code);
11988c2ecf20Sopenharmony_ci	if (result == 0) {
11998c2ecf20Sopenharmony_ci		memcpy(&css_general_characteristics, scsc_area->general_char,
12008c2ecf20Sopenharmony_ci		       sizeof(css_general_characteristics));
12018c2ecf20Sopenharmony_ci		memcpy(&css_chsc_characteristics, scsc_area->chsc_char,
12028c2ecf20Sopenharmony_ci		       sizeof(css_chsc_characteristics));
12038c2ecf20Sopenharmony_ci	} else
12048c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n",
12058c2ecf20Sopenharmony_ci			      scsc_area->response.code);
12068c2ecf20Sopenharmony_ciexit:
12078c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chsc_page_lock, flags);
12088c2ecf20Sopenharmony_ci	return result;
12098c2ecf20Sopenharmony_ci}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(css_general_characteristics);
12128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(css_chsc_characteristics);
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ciint chsc_sstpc(void *page, unsigned int op, u16 ctrl, u64 *clock_delta)
12158c2ecf20Sopenharmony_ci{
12168c2ecf20Sopenharmony_ci	struct {
12178c2ecf20Sopenharmony_ci		struct chsc_header request;
12188c2ecf20Sopenharmony_ci		unsigned int rsvd0;
12198c2ecf20Sopenharmony_ci		unsigned int op : 8;
12208c2ecf20Sopenharmony_ci		unsigned int rsvd1 : 8;
12218c2ecf20Sopenharmony_ci		unsigned int ctrl : 16;
12228c2ecf20Sopenharmony_ci		unsigned int rsvd2[5];
12238c2ecf20Sopenharmony_ci		struct chsc_header response;
12248c2ecf20Sopenharmony_ci		unsigned int rsvd3[3];
12258c2ecf20Sopenharmony_ci		u64 clock_delta;
12268c2ecf20Sopenharmony_ci		unsigned int rsvd4[2];
12278c2ecf20Sopenharmony_ci	} *rr;
12288c2ecf20Sopenharmony_ci	int rc;
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	memset(page, 0, PAGE_SIZE);
12318c2ecf20Sopenharmony_ci	rr = page;
12328c2ecf20Sopenharmony_ci	rr->request.length = 0x0020;
12338c2ecf20Sopenharmony_ci	rr->request.code = 0x0033;
12348c2ecf20Sopenharmony_ci	rr->op = op;
12358c2ecf20Sopenharmony_ci	rr->ctrl = ctrl;
12368c2ecf20Sopenharmony_ci	rc = chsc(rr);
12378c2ecf20Sopenharmony_ci	if (rc)
12388c2ecf20Sopenharmony_ci		return -EIO;
12398c2ecf20Sopenharmony_ci	rc = (rr->response.code == 0x0001) ? 0 : -EIO;
12408c2ecf20Sopenharmony_ci	if (clock_delta)
12418c2ecf20Sopenharmony_ci		*clock_delta = rr->clock_delta;
12428c2ecf20Sopenharmony_ci	return rc;
12438c2ecf20Sopenharmony_ci}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ciint chsc_sstpi(void *page, void *result, size_t size)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	struct {
12488c2ecf20Sopenharmony_ci		struct chsc_header request;
12498c2ecf20Sopenharmony_ci		unsigned int rsvd0[3];
12508c2ecf20Sopenharmony_ci		struct chsc_header response;
12518c2ecf20Sopenharmony_ci		char data[];
12528c2ecf20Sopenharmony_ci	} *rr;
12538c2ecf20Sopenharmony_ci	int rc;
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	memset(page, 0, PAGE_SIZE);
12568c2ecf20Sopenharmony_ci	rr = page;
12578c2ecf20Sopenharmony_ci	rr->request.length = 0x0010;
12588c2ecf20Sopenharmony_ci	rr->request.code = 0x0038;
12598c2ecf20Sopenharmony_ci	rc = chsc(rr);
12608c2ecf20Sopenharmony_ci	if (rc)
12618c2ecf20Sopenharmony_ci		return -EIO;
12628c2ecf20Sopenharmony_ci	memcpy(result, &rr->data, size);
12638c2ecf20Sopenharmony_ci	return (rr->response.code == 0x0001) ? 0 : -EIO;
12648c2ecf20Sopenharmony_ci}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ciint chsc_stzi(void *page, void *result, size_t size)
12678c2ecf20Sopenharmony_ci{
12688c2ecf20Sopenharmony_ci	struct {
12698c2ecf20Sopenharmony_ci		struct chsc_header request;
12708c2ecf20Sopenharmony_ci		unsigned int rsvd0[3];
12718c2ecf20Sopenharmony_ci		struct chsc_header response;
12728c2ecf20Sopenharmony_ci		char data[];
12738c2ecf20Sopenharmony_ci	} *rr;
12748c2ecf20Sopenharmony_ci	int rc;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	memset(page, 0, PAGE_SIZE);
12778c2ecf20Sopenharmony_ci	rr = page;
12788c2ecf20Sopenharmony_ci	rr->request.length = 0x0010;
12798c2ecf20Sopenharmony_ci	rr->request.code = 0x003e;
12808c2ecf20Sopenharmony_ci	rc = chsc(rr);
12818c2ecf20Sopenharmony_ci	if (rc)
12828c2ecf20Sopenharmony_ci		return -EIO;
12838c2ecf20Sopenharmony_ci	memcpy(result, &rr->data, size);
12848c2ecf20Sopenharmony_ci	return (rr->response.code == 0x0001) ? 0 : -EIO;
12858c2ecf20Sopenharmony_ci}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ciint chsc_siosl(struct subchannel_id schid)
12888c2ecf20Sopenharmony_ci{
12898c2ecf20Sopenharmony_ci	struct {
12908c2ecf20Sopenharmony_ci		struct chsc_header request;
12918c2ecf20Sopenharmony_ci		u32 word1;
12928c2ecf20Sopenharmony_ci		struct subchannel_id sid;
12938c2ecf20Sopenharmony_ci		u32 word3;
12948c2ecf20Sopenharmony_ci		struct chsc_header response;
12958c2ecf20Sopenharmony_ci		u32 word[11];
12968c2ecf20Sopenharmony_ci	} *siosl_area;
12978c2ecf20Sopenharmony_ci	unsigned long flags;
12988c2ecf20Sopenharmony_ci	int ccode;
12998c2ecf20Sopenharmony_ci	int rc;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chsc_page_lock, flags);
13028c2ecf20Sopenharmony_ci	memset(chsc_page, 0, PAGE_SIZE);
13038c2ecf20Sopenharmony_ci	siosl_area = chsc_page;
13048c2ecf20Sopenharmony_ci	siosl_area->request.length = 0x0010;
13058c2ecf20Sopenharmony_ci	siosl_area->request.code = 0x0046;
13068c2ecf20Sopenharmony_ci	siosl_area->word1 = 0x80000000;
13078c2ecf20Sopenharmony_ci	siosl_area->sid = schid;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	ccode = chsc(siosl_area);
13108c2ecf20Sopenharmony_ci	if (ccode > 0) {
13118c2ecf20Sopenharmony_ci		if (ccode == 3)
13128c2ecf20Sopenharmony_ci			rc = -ENODEV;
13138c2ecf20Sopenharmony_ci		else
13148c2ecf20Sopenharmony_ci			rc = -EBUSY;
13158c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(2, "chsc: chsc failed for 0.%x.%04x (ccode=%d)\n",
13168c2ecf20Sopenharmony_ci			      schid.ssid, schid.sch_no, ccode);
13178c2ecf20Sopenharmony_ci		goto out;
13188c2ecf20Sopenharmony_ci	}
13198c2ecf20Sopenharmony_ci	rc = chsc_error_from_response(siosl_area->response.code);
13208c2ecf20Sopenharmony_ci	if (rc)
13218c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n",
13228c2ecf20Sopenharmony_ci			      schid.ssid, schid.sch_no,
13238c2ecf20Sopenharmony_ci			      siosl_area->response.code);
13248c2ecf20Sopenharmony_ci	else
13258c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n",
13268c2ecf20Sopenharmony_ci			      schid.ssid, schid.sch_no);
13278c2ecf20Sopenharmony_ciout:
13288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chsc_page_lock, flags);
13298c2ecf20Sopenharmony_ci	return rc;
13308c2ecf20Sopenharmony_ci}
13318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(chsc_siosl);
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci/**
13348c2ecf20Sopenharmony_ci * chsc_scm_info() - store SCM information (SSI)
13358c2ecf20Sopenharmony_ci * @scm_area: request and response block for SSI
13368c2ecf20Sopenharmony_ci * @token: continuation token
13378c2ecf20Sopenharmony_ci *
13388c2ecf20Sopenharmony_ci * Returns 0 on success.
13398c2ecf20Sopenharmony_ci */
13408c2ecf20Sopenharmony_ciint chsc_scm_info(struct chsc_scm_info *scm_area, u64 token)
13418c2ecf20Sopenharmony_ci{
13428c2ecf20Sopenharmony_ci	int ccode, ret;
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	memset(scm_area, 0, sizeof(*scm_area));
13458c2ecf20Sopenharmony_ci	scm_area->request.length = 0x0020;
13468c2ecf20Sopenharmony_ci	scm_area->request.code = 0x004C;
13478c2ecf20Sopenharmony_ci	scm_area->reqtok = token;
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	ccode = chsc(scm_area);
13508c2ecf20Sopenharmony_ci	if (ccode > 0) {
13518c2ecf20Sopenharmony_ci		ret = (ccode == 3) ? -ENODEV : -EBUSY;
13528c2ecf20Sopenharmony_ci		goto out;
13538c2ecf20Sopenharmony_ci	}
13548c2ecf20Sopenharmony_ci	ret = chsc_error_from_response(scm_area->response.code);
13558c2ecf20Sopenharmony_ci	if (ret != 0)
13568c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(2, "chsc: scm info failed (rc=%04x)\n",
13578c2ecf20Sopenharmony_ci			      scm_area->response.code);
13588c2ecf20Sopenharmony_ciout:
13598c2ecf20Sopenharmony_ci	return ret;
13608c2ecf20Sopenharmony_ci}
13618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(chsc_scm_info);
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci/**
13648c2ecf20Sopenharmony_ci * chsc_pnso() - Perform Network-Subchannel Operation
13658c2ecf20Sopenharmony_ci * @schid:		id of the subchannel on which PNSO is performed
13668c2ecf20Sopenharmony_ci * @pnso_area:		request and response block for the operation
13678c2ecf20Sopenharmony_ci * @oc:			Operation Code
13688c2ecf20Sopenharmony_ci * @resume_token:	resume token for multiblock response
13698c2ecf20Sopenharmony_ci * @cnc:		Boolean change-notification control
13708c2ecf20Sopenharmony_ci *
13718c2ecf20Sopenharmony_ci * pnso_area must be allocated by the caller with get_zeroed_page(GFP_KERNEL)
13728c2ecf20Sopenharmony_ci *
13738c2ecf20Sopenharmony_ci * Returns 0 on success.
13748c2ecf20Sopenharmony_ci */
13758c2ecf20Sopenharmony_ciint chsc_pnso(struct subchannel_id schid, struct chsc_pnso_area *pnso_area,
13768c2ecf20Sopenharmony_ci	      u8 oc, struct chsc_pnso_resume_token resume_token, int cnc)
13778c2ecf20Sopenharmony_ci{
13788c2ecf20Sopenharmony_ci	memset(pnso_area, 0, sizeof(*pnso_area));
13798c2ecf20Sopenharmony_ci	pnso_area->request.length = 0x0030;
13808c2ecf20Sopenharmony_ci	pnso_area->request.code = 0x003d; /* network-subchannel operation */
13818c2ecf20Sopenharmony_ci	pnso_area->m	   = schid.m;
13828c2ecf20Sopenharmony_ci	pnso_area->ssid  = schid.ssid;
13838c2ecf20Sopenharmony_ci	pnso_area->sch	 = schid.sch_no;
13848c2ecf20Sopenharmony_ci	pnso_area->cssid = schid.cssid;
13858c2ecf20Sopenharmony_ci	pnso_area->oc	 = oc;
13868c2ecf20Sopenharmony_ci	pnso_area->resume_token = resume_token;
13878c2ecf20Sopenharmony_ci	pnso_area->n	   = (cnc != 0);
13888c2ecf20Sopenharmony_ci	if (chsc(pnso_area))
13898c2ecf20Sopenharmony_ci		return -EIO;
13908c2ecf20Sopenharmony_ci	return chsc_error_from_response(pnso_area->response.code);
13918c2ecf20Sopenharmony_ci}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ciint chsc_sgib(u32 origin)
13948c2ecf20Sopenharmony_ci{
13958c2ecf20Sopenharmony_ci	struct {
13968c2ecf20Sopenharmony_ci		struct chsc_header request;
13978c2ecf20Sopenharmony_ci		u16 op;
13988c2ecf20Sopenharmony_ci		u8  reserved01[2];
13998c2ecf20Sopenharmony_ci		u8  reserved02:4;
14008c2ecf20Sopenharmony_ci		u8  fmt:4;
14018c2ecf20Sopenharmony_ci		u8  reserved03[7];
14028c2ecf20Sopenharmony_ci		/* operation data area begin */
14038c2ecf20Sopenharmony_ci		u8  reserved04[4];
14048c2ecf20Sopenharmony_ci		u32 gib_origin;
14058c2ecf20Sopenharmony_ci		u8  reserved05[10];
14068c2ecf20Sopenharmony_ci		u8  aix;
14078c2ecf20Sopenharmony_ci		u8  reserved06[4029];
14088c2ecf20Sopenharmony_ci		struct chsc_header response;
14098c2ecf20Sopenharmony_ci		u8  reserved07[4];
14108c2ecf20Sopenharmony_ci	} *sgib_area;
14118c2ecf20Sopenharmony_ci	int ret;
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	spin_lock_irq(&chsc_page_lock);
14148c2ecf20Sopenharmony_ci	memset(chsc_page, 0, PAGE_SIZE);
14158c2ecf20Sopenharmony_ci	sgib_area = chsc_page;
14168c2ecf20Sopenharmony_ci	sgib_area->request.length = 0x0fe0;
14178c2ecf20Sopenharmony_ci	sgib_area->request.code = 0x0021;
14188c2ecf20Sopenharmony_ci	sgib_area->op = 0x1;
14198c2ecf20Sopenharmony_ci	sgib_area->gib_origin = origin;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	ret = chsc(sgib_area);
14228c2ecf20Sopenharmony_ci	if (ret == 0)
14238c2ecf20Sopenharmony_ci		ret = chsc_error_from_response(sgib_area->response.code);
14248c2ecf20Sopenharmony_ci	spin_unlock_irq(&chsc_page_lock);
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	return ret;
14278c2ecf20Sopenharmony_ci}
14288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(chsc_sgib);
1429