xref: /kvm-unit-tests/lib/s390x/css_lib.c (revision 610c15284a537484682adfb4b6d6313991ab954f)
16c9f99dfSJanosch Frank /* SPDX-License-Identifier: GPL-2.0-only */
23227b5a6SPierre Morel /*
33227b5a6SPierre Morel  * Channel Subsystem tests library
43227b5a6SPierre Morel  *
53227b5a6SPierre Morel  * Copyright (c) 2020 IBM Corp
63227b5a6SPierre Morel  *
73227b5a6SPierre Morel  * Authors:
83227b5a6SPierre Morel  *  Pierre Morel <pmorel@linux.ibm.com>
93227b5a6SPierre Morel  */
103227b5a6SPierre Morel #include <libcflat.h>
113227b5a6SPierre Morel #include <alloc_phys.h>
123227b5a6SPierre Morel #include <asm/page.h>
133227b5a6SPierre Morel #include <string.h>
143227b5a6SPierre Morel #include <interrupt.h>
153227b5a6SPierre Morel #include <asm/arch_def.h>
16551e6622SPierre Morel #include <asm/time.h>
178cb729e4SPierre Morel #include <asm/arch_def.h>
187fbcef02SPierre Morel #include <alloc_page.h>
19a6e5e596SPierre Morel #include <malloc_io.h>
203227b5a6SPierre Morel #include <css.h>
213227b5a6SPierre Morel 
22fa68e2a8SPierre Morel struct schib schib;
237fbcef02SPierre Morel struct chsc_scsc *chsc_scsc;
247fbcef02SPierre Morel 
257fbcef02SPierre Morel static const char * const chsc_rsp_description[] = {
267fbcef02SPierre Morel 	"CHSC unknown error",
277fbcef02SPierre Morel 	"Command executed",
287fbcef02SPierre Morel 	"Invalid command",
297fbcef02SPierre Morel 	"Request-block error",
307fbcef02SPierre Morel 	"Command not installed",
317fbcef02SPierre Morel 	"Data not available",
327fbcef02SPierre Morel 	"Absolute address of channel-subsystem communication block exceeds 2G - 1.",
337fbcef02SPierre Morel 	"Invalid command format",
347fbcef02SPierre Morel 	"Invalid channel-subsystem identification (CSSID)",
357fbcef02SPierre Morel 	"The command-request block specified an invalid format for the command response block.",
367fbcef02SPierre Morel 	"Invalid subchannel-set identification (SSID)",
377fbcef02SPierre Morel 	"A busy condition precludes execution.",
387fbcef02SPierre Morel };
397fbcef02SPierre Morel 
check_response(void * p)407fbcef02SPierre Morel static bool check_response(void *p)
417fbcef02SPierre Morel {
427fbcef02SPierre Morel 	struct chsc_header *h = p;
437fbcef02SPierre Morel 
447fbcef02SPierre Morel 	if (h->code == CHSC_RSP_OK)
457fbcef02SPierre Morel 		return true;
467fbcef02SPierre Morel 
477fbcef02SPierre Morel 	if (h->code > CHSC_RSP_MAX)
487fbcef02SPierre Morel 		h->code = 0;
497fbcef02SPierre Morel 
507fbcef02SPierre Morel 	report_abort("Response code %04x: %s", h->code,
517fbcef02SPierre Morel 		      chsc_rsp_description[h->code]);
527fbcef02SPierre Morel 	return false;
537fbcef02SPierre Morel }
547fbcef02SPierre Morel 
chsc(void * p,uint16_t code,uint16_t len)557fbcef02SPierre Morel bool chsc(void *p, uint16_t code, uint16_t len)
567fbcef02SPierre Morel {
577fbcef02SPierre Morel 	struct chsc_header *h = p;
587fbcef02SPierre Morel 
597fbcef02SPierre Morel 	h->code = code;
607fbcef02SPierre Morel 	h->len = len;
617fbcef02SPierre Morel 
627fbcef02SPierre Morel 	switch (_chsc(p)) {
637fbcef02SPierre Morel 	case 3:
647fbcef02SPierre Morel 		report_abort("Subchannel invalid or not enabled.");
657fbcef02SPierre Morel 		break;
667fbcef02SPierre Morel 	case 2:
677fbcef02SPierre Morel 		report_abort("CHSC subchannel busy.");
687fbcef02SPierre Morel 		break;
697fbcef02SPierre Morel 	case 1:
707fbcef02SPierre Morel 		report_abort("Subchannel invalid or not enabled.");
717fbcef02SPierre Morel 		break;
727fbcef02SPierre Morel 	case 0:
737fbcef02SPierre Morel 		return check_response(p + len);
747fbcef02SPierre Morel 	}
757fbcef02SPierre Morel 	return false;
767fbcef02SPierre Morel }
777fbcef02SPierre Morel 
get_chsc_scsc(void)787fbcef02SPierre Morel bool get_chsc_scsc(void)
797fbcef02SPierre Morel {
807fbcef02SPierre Morel 	int i, n;
817fbcef02SPierre Morel 	char buffer[510];
827fbcef02SPierre Morel 	char *p;
837fbcef02SPierre Morel 
847fbcef02SPierre Morel 	if (chsc_scsc) /* chsc_scsc already initialized */
857fbcef02SPierre Morel 		return true;
867fbcef02SPierre Morel 
877fbcef02SPierre Morel 	chsc_scsc = alloc_page();
887fbcef02SPierre Morel 	if (!chsc_scsc) {
897fbcef02SPierre Morel 		report_abort("could not allocate chsc_scsc page!");
907fbcef02SPierre Morel 		return false;
917fbcef02SPierre Morel 	}
927fbcef02SPierre Morel 
937fbcef02SPierre Morel 	if (!chsc(chsc_scsc, CHSC_SCSC, CHSC_SCSC_LEN))
947fbcef02SPierre Morel 		return false;
957fbcef02SPierre Morel 
967fbcef02SPierre Morel 	for (i = 0, p = buffer; i < CSS_GENERAL_FEAT_BITLEN; i++) {
977fbcef02SPierre Morel 		if (css_test_general_feature(i)) {
987fbcef02SPierre Morel 			n = snprintf(p, sizeof(buffer), "%d,", i);
997fbcef02SPierre Morel 			p += n;
1007fbcef02SPierre Morel 		}
1017fbcef02SPierre Morel 	}
1027fbcef02SPierre Morel 	report_info("General features: %s", buffer);
1037fbcef02SPierre Morel 
1047fbcef02SPierre Morel 	for (i = 0, p = buffer; i < CSS_CHSC_FEAT_BITLEN; i++) {
1057fbcef02SPierre Morel 		if (css_test_chsc_feature(i)) {
1067fbcef02SPierre Morel 			n = snprintf(p, sizeof(buffer), "%d,", i);
1077fbcef02SPierre Morel 			p += n;
1087fbcef02SPierre Morel 		}
1097fbcef02SPierre Morel 	}
1107fbcef02SPierre Morel 	report_info("CHSC features: %s", buffer);
1117fbcef02SPierre Morel 
1127fbcef02SPierre Morel 	return true;
1137fbcef02SPierre Morel }
1143227b5a6SPierre Morel 
1153227b5a6SPierre Morel /*
1163227b5a6SPierre Morel  * css_enumerate:
1173227b5a6SPierre Morel  * On success return the first subchannel ID found.
1183227b5a6SPierre Morel  * On error return an invalid subchannel ID containing cc
1193227b5a6SPierre Morel  */
css_enumerate(void)1203227b5a6SPierre Morel int css_enumerate(void)
1213227b5a6SPierre Morel {
1223227b5a6SPierre Morel 	struct pmcw *pmcw = &schib.pmcw;
1233227b5a6SPierre Morel 	int scn_found = 0;
1243227b5a6SPierre Morel 	int dev_found = 0;
1253227b5a6SPierre Morel 	int schid = 0;
1263227b5a6SPierre Morel 	int cc;
1273227b5a6SPierre Morel 	int scn;
1283227b5a6SPierre Morel 
1293227b5a6SPierre Morel 	for (scn = 0; scn < 0xffff; scn++) {
1303227b5a6SPierre Morel 		cc = stsch(scn | SCHID_ONE, &schib);
1313227b5a6SPierre Morel 		switch (cc) {
1323227b5a6SPierre Morel 		case 0:		/* 0 means SCHIB stored */
1333227b5a6SPierre Morel 			break;
1343227b5a6SPierre Morel 		case 3:		/* 3 means no more channels */
1353227b5a6SPierre Morel 			goto out;
1363227b5a6SPierre Morel 		default:	/* 1 or 2 should never happen for STSCH */
1373227b5a6SPierre Morel 			report_abort("Unexpected error %d on subchannel %08x",
1383227b5a6SPierre Morel 				     cc, scn | SCHID_ONE);
1393227b5a6SPierre Morel 			return cc;
1403227b5a6SPierre Morel 		}
1413227b5a6SPierre Morel 
1423227b5a6SPierre Morel 		/* We currently only support type 0, a.k.a. I/O channels */
1433227b5a6SPierre Morel 		if (PMCW_CHANNEL_TYPE(pmcw) != 0)
1443227b5a6SPierre Morel 			continue;
1453227b5a6SPierre Morel 
1463227b5a6SPierre Morel 		/* We ignore I/O channels without valid devices */
1473227b5a6SPierre Morel 		scn_found++;
1483227b5a6SPierre Morel 		if (!(pmcw->flags & PMCW_DNV))
1493227b5a6SPierre Morel 			continue;
1503227b5a6SPierre Morel 
1513227b5a6SPierre Morel 		/* We keep track of the first device as our test device */
1523227b5a6SPierre Morel 		if (!schid)
1533227b5a6SPierre Morel 			schid = scn | SCHID_ONE;
1543227b5a6SPierre Morel 		report_info("Found subchannel %08x", scn | SCHID_ONE);
1553227b5a6SPierre Morel 		dev_found++;
1563227b5a6SPierre Morel 	}
1573227b5a6SPierre Morel 
1583227b5a6SPierre Morel out:
1593227b5a6SPierre Morel 	report_info("Tested subchannels: %d, I/O subchannels: %d, I/O devices: %d",
1603227b5a6SPierre Morel 		    scn, scn_found, dev_found);
1613227b5a6SPierre Morel 	return schid;
1623227b5a6SPierre Morel }
163551e6622SPierre Morel 
164551e6622SPierre Morel /*
165d3d7fa5aSPierre Morel  * css_enabled: report if the subchannel is enabled
166d3d7fa5aSPierre Morel  * @schid: Subchannel Identifier
167d3d7fa5aSPierre Morel  * Return value:
168d3d7fa5aSPierre Morel  *   true if the subchannel is enabled
169d3d7fa5aSPierre Morel  *   false otherwise
170d3d7fa5aSPierre Morel  */
css_enabled(int schid)171d3d7fa5aSPierre Morel bool css_enabled(int schid)
172d3d7fa5aSPierre Morel {
173d3d7fa5aSPierre Morel 	struct pmcw *pmcw = &schib.pmcw;
174d3d7fa5aSPierre Morel 	int cc;
175d3d7fa5aSPierre Morel 
176d3d7fa5aSPierre Morel 	cc = stsch(schid, &schib);
177d3d7fa5aSPierre Morel 	if (cc) {
178d3d7fa5aSPierre Morel 		report_info("stsch: updating sch %08x failed with cc=%d",
179d3d7fa5aSPierre Morel 			    schid, cc);
180d3d7fa5aSPierre Morel 		return false;
181d3d7fa5aSPierre Morel 	}
182d3d7fa5aSPierre Morel 
183d3d7fa5aSPierre Morel 	if (!(pmcw->flags & PMCW_ENABLE)) {
184d3d7fa5aSPierre Morel 		report_info("stsch: sch %08x not enabled", schid);
185d3d7fa5aSPierre Morel 		return false;
186d3d7fa5aSPierre Morel 	}
187d3d7fa5aSPierre Morel 	return true;
188d3d7fa5aSPierre Morel }
189d3d7fa5aSPierre Morel /*
190551e6622SPierre Morel  * css_enable: enable the subchannel with the specified ISC
191551e6622SPierre Morel  * @schid: Subchannel Identifier
192551e6622SPierre Morel  * @isc  : number of the interruption subclass to use
193551e6622SPierre Morel  * Return value:
194551e6622SPierre Morel  *   On success: 0
195551e6622SPierre Morel  *   On error the CC of the faulty instruction
196551e6622SPierre Morel  *      or -1 if the retry count is exceeded.
197551e6622SPierre Morel  */
css_enable(int schid,int isc)198551e6622SPierre Morel int css_enable(int schid, int isc)
199551e6622SPierre Morel {
200551e6622SPierre Morel 	struct pmcw *pmcw = &schib.pmcw;
201551e6622SPierre Morel 	int retry_count = 0;
202551e6622SPierre Morel 	uint16_t flags;
203551e6622SPierre Morel 	int cc;
204551e6622SPierre Morel 
205551e6622SPierre Morel 	/* Read the SCHIB for this subchannel */
206551e6622SPierre Morel 	cc = stsch(schid, &schib);
207551e6622SPierre Morel 	if (cc) {
208551e6622SPierre Morel 		report_info("stsch: sch %08x failed with cc=%d", schid, cc);
209551e6622SPierre Morel 		return cc;
210551e6622SPierre Morel 	}
211551e6622SPierre Morel 
212551e6622SPierre Morel 	flags = PMCW_ENABLE | (isc << PMCW_ISC_SHIFT);
213551e6622SPierre Morel 	if ((pmcw->flags & (PMCW_ISC_MASK | PMCW_ENABLE)) == flags) {
214551e6622SPierre Morel 		report_info("stsch: sch %08x already enabled", schid);
215551e6622SPierre Morel 		return 0;
216551e6622SPierre Morel 	}
217551e6622SPierre Morel 
218551e6622SPierre Morel retry:
219551e6622SPierre Morel 	/* Update the SCHIB to enable the channel and set the ISC */
220551e6622SPierre Morel 	pmcw->flags &= ~PMCW_ISC_MASK;
221551e6622SPierre Morel 	pmcw->flags |= flags;
222551e6622SPierre Morel 
223551e6622SPierre Morel 	/* Tell the CSS we want to modify the subchannel */
224551e6622SPierre Morel 	cc = msch(schid, &schib);
225551e6622SPierre Morel 	if (cc) {
226551e6622SPierre Morel 		/*
227551e6622SPierre Morel 		 * If the subchannel is status pending or
228551e6622SPierre Morel 		 * if a function is in progress,
229551e6622SPierre Morel 		 * we consider both cases as errors.
230551e6622SPierre Morel 		 */
231551e6622SPierre Morel 		report_info("msch: sch %08x failed with cc=%d", schid, cc);
232551e6622SPierre Morel 		return cc;
233551e6622SPierre Morel 	}
234551e6622SPierre Morel 
235551e6622SPierre Morel 	/*
236551e6622SPierre Morel 	 * Read the SCHIB again to verify the enablement
237551e6622SPierre Morel 	 */
238d3d7fa5aSPierre Morel 	if (css_enabled(schid))
239551e6622SPierre Morel 		return 0;
240551e6622SPierre Morel 
241551e6622SPierre Morel 	if (retry_count++ < MAX_ENABLE_RETRIES) {
242551e6622SPierre Morel 		mdelay(10); /* the hardware was not ready, give it some time */
243551e6622SPierre Morel 		goto retry;
244551e6622SPierre Morel 	}
245551e6622SPierre Morel 
246551e6622SPierre Morel 	report_info("msch: modifying sch %08x failed after %d retries. pmcw flags: %04x",
247551e6622SPierre Morel 		    schid, retry_count, pmcw->flags);
248551e6622SPierre Morel 	return -1;
249551e6622SPierre Morel }
2508cb729e4SPierre Morel 
2513c4ae93fSPierre Morel /*
2523c4ae93fSPierre Morel  * schib_update_mb: update the subchannel Measurement Block
2533c4ae93fSPierre Morel  * @schid: Subchannel Identifier
2543c4ae93fSPierre Morel  * @mb   : 64bit address of the measurement block
2553c4ae93fSPierre Morel  * @mbi : the measurement block offset
2563c4ae93fSPierre Morel  * @flags : PMCW_MBUE to enable measurement block update
2573c4ae93fSPierre Morel  *	    PMCW_DCTME to enable device connect time
2583c4ae93fSPierre Morel  *	    0 to disable measurement
2593c4ae93fSPierre Morel  * @format1: set if format 1 is to be used
2603c4ae93fSPierre Morel  */
schib_update_mb(int schid,uint64_t mb,uint16_t mbi,uint16_t flags,bool format1)2613c4ae93fSPierre Morel static bool schib_update_mb(int schid, uint64_t mb, uint16_t mbi,
2623c4ae93fSPierre Morel 			    uint16_t flags, bool format1)
2633c4ae93fSPierre Morel {
2643c4ae93fSPierre Morel 	struct pmcw *pmcw = &schib.pmcw;
2653c4ae93fSPierre Morel 	int cc;
2663c4ae93fSPierre Morel 
2673c4ae93fSPierre Morel 	/* Read the SCHIB for this subchannel */
2683c4ae93fSPierre Morel 	cc = stsch(schid, &schib);
2693c4ae93fSPierre Morel 	if (cc) {
2703c4ae93fSPierre Morel 		report_info("stsch: sch %08x failed with cc=%d", schid, cc);
2713c4ae93fSPierre Morel 		return false;
2723c4ae93fSPierre Morel 	}
2733c4ae93fSPierre Morel 
2743c4ae93fSPierre Morel 	/* Update the SCHIB to enable the measurement block */
2753c4ae93fSPierre Morel 	if (flags) {
2763c4ae93fSPierre Morel 		pmcw->flags |= flags;
2773c4ae93fSPierre Morel 
2783c4ae93fSPierre Morel 		if (format1)
2793c4ae93fSPierre Morel 			pmcw->flags2 |= PMCW_MBF1;
2803c4ae93fSPierre Morel 		else
2813c4ae93fSPierre Morel 			pmcw->flags2 &= ~PMCW_MBF1;
2823c4ae93fSPierre Morel 
2833c4ae93fSPierre Morel 		pmcw->mbi = mbi;
2843c4ae93fSPierre Morel 		schib.mbo = mb & ~0x3f;
2853c4ae93fSPierre Morel 	} else {
2863c4ae93fSPierre Morel 		pmcw->flags &= ~(PMCW_MBUE | PMCW_DCTME);
2873c4ae93fSPierre Morel 	}
2883c4ae93fSPierre Morel 
2893c4ae93fSPierre Morel 	/* Tell the CSS we want to modify the subchannel */
2903c4ae93fSPierre Morel 	cc = msch(schid, &schib);
2913c4ae93fSPierre Morel 	if (cc) {
2923c4ae93fSPierre Morel 		/*
2933c4ae93fSPierre Morel 		 * If the subchannel is status pending or
2943c4ae93fSPierre Morel 		 * if a function is in progress,
2953c4ae93fSPierre Morel 		 * we consider both cases as errors.
2963c4ae93fSPierre Morel 		 */
2973c4ae93fSPierre Morel 		report_info("msch: sch %08x failed with cc=%d", schid, cc);
2983c4ae93fSPierre Morel 		return false;
2993c4ae93fSPierre Morel 	}
3003c4ae93fSPierre Morel 
3013c4ae93fSPierre Morel 	/*
3023c4ae93fSPierre Morel 	 * Read the SCHIB again
3033c4ae93fSPierre Morel 	 */
3043c4ae93fSPierre Morel 	cc = stsch(schid, &schib);
3053c4ae93fSPierre Morel 	if (cc) {
3063c4ae93fSPierre Morel 		report_info("stsch: updating sch %08x failed with cc=%d",
3073c4ae93fSPierre Morel 			    schid, cc);
3083c4ae93fSPierre Morel 		return false;
3093c4ae93fSPierre Morel 	}
3103c4ae93fSPierre Morel 
3113c4ae93fSPierre Morel 	return true;
3123c4ae93fSPierre Morel }
3133c4ae93fSPierre Morel 
3143c4ae93fSPierre Morel /*
3153c4ae93fSPierre Morel  * css_enable_mb: enable the subchannel Measurement Block
3163c4ae93fSPierre Morel  * @schid: Subchannel Identifier
3173c4ae93fSPierre Morel  * @mb   : 64bit address of the measurement block
3183c4ae93fSPierre Morel  * @format1: set if format 1 is to be used
3193c4ae93fSPierre Morel  * @mbi : the measurement block offset
3203c4ae93fSPierre Morel  * @flags : PMCW_MBUE to enable measurement block update
3213c4ae93fSPierre Morel  *	    PMCW_DCTME to enable device connect time
3223c4ae93fSPierre Morel  */
css_enable_mb(int schid,uint64_t mb,uint16_t mbi,uint16_t flags,bool format1)3233c4ae93fSPierre Morel bool css_enable_mb(int schid, uint64_t mb, uint16_t mbi, uint16_t flags,
3243c4ae93fSPierre Morel 		   bool format1)
3253c4ae93fSPierre Morel {
3263c4ae93fSPierre Morel 	int retry_count = MAX_ENABLE_RETRIES;
3273c4ae93fSPierre Morel 	struct pmcw *pmcw = &schib.pmcw;
3283c4ae93fSPierre Morel 
3293c4ae93fSPierre Morel 	while (retry_count-- &&
3303c4ae93fSPierre Morel 	       !schib_update_mb(schid, mb, mbi, flags, format1))
3313c4ae93fSPierre Morel 		mdelay(10); /* the hardware was not ready, give it some time */
3323c4ae93fSPierre Morel 
3333c4ae93fSPierre Morel 	return schib.mbo == mb && pmcw->mbi == mbi;
3343c4ae93fSPierre Morel }
3353c4ae93fSPierre Morel 
3363c4ae93fSPierre Morel /*
3373c4ae93fSPierre Morel  * css_disable_mb: disable the subchannel Measurement Block
3383c4ae93fSPierre Morel  * @schid: Subchannel Identifier
3393c4ae93fSPierre Morel  */
css_disable_mb(int schid)3403c4ae93fSPierre Morel bool css_disable_mb(int schid)
3413c4ae93fSPierre Morel {
3423c4ae93fSPierre Morel 	int retry_count = MAX_ENABLE_RETRIES;
3433c4ae93fSPierre Morel 
3443c4ae93fSPierre Morel 	while (retry_count-- &&
3453c4ae93fSPierre Morel 	       !schib_update_mb(schid, 0, 0, 0, 0))
3463c4ae93fSPierre Morel 		mdelay(10); /* the hardware was not ready, give it some time */
3473c4ae93fSPierre Morel 
3483c4ae93fSPierre Morel 	return retry_count > 0;
3493c4ae93fSPierre Morel }
3503c4ae93fSPierre Morel 
3518cb729e4SPierre Morel static struct irb irb;
3528cb729e4SPierre Morel 
css_irq_io(void)3538cb729e4SPierre Morel void css_irq_io(void)
3548cb729e4SPierre Morel {
3558cb729e4SPierre Morel 	int ret = 0;
3568cb729e4SPierre Morel 	char *flags;
3578cb729e4SPierre Morel 	int sid;
3588cb729e4SPierre Morel 
3598cb729e4SPierre Morel 	report_prefix_push("Interrupt");
360*cd719531SJanis Schoetterl-Glausch 	sid = lowcore.subsys_id_word;
3618cb729e4SPierre Morel 	/* Lowlevel set the SID as interrupt parameter. */
362*cd719531SJanis Schoetterl-Glausch 	if (lowcore.io_int_param != sid) {
363198dfd0eSJanis Schoetterl-Glausch 		report_fail("io_int_param: %x differs from subsys_id_word: %x",
364*cd719531SJanis Schoetterl-Glausch 			    lowcore.io_int_param, sid);
3658cb729e4SPierre Morel 		goto pop;
3668cb729e4SPierre Morel 	}
3678cb729e4SPierre Morel 	report_prefix_pop();
3688cb729e4SPierre Morel 
3698cb729e4SPierre Morel 	report_prefix_push("tsch");
3708cb729e4SPierre Morel 	ret = tsch(sid, &irb);
3718cb729e4SPierre Morel 	switch (ret) {
3728cb729e4SPierre Morel 	case 1:
3738cb729e4SPierre Morel 		dump_irb(&irb);
3748cb729e4SPierre Morel 		flags = dump_scsw_flags(irb.scsw.ctrl);
375198dfd0eSJanis Schoetterl-Glausch 		report_fail("I/O interrupt, but tsch returns CC 1 for subchannel %08x.SCSW flags: %s",
3768cb729e4SPierre Morel 			    sid, flags);
3778cb729e4SPierre Morel 		break;
3788cb729e4SPierre Morel 	case 2:
379198dfd0eSJanis Schoetterl-Glausch 		report_fail("tsch returns unexpected CC 2");
3808cb729e4SPierre Morel 		break;
3818cb729e4SPierre Morel 	case 3:
382198dfd0eSJanis Schoetterl-Glausch 		report_fail("tsch reporting sch %08x as not operational", sid);
3838cb729e4SPierre Morel 		break;
3848cb729e4SPierre Morel 	case 0:
3858cb729e4SPierre Morel 		/* Stay humble on success */
3868cb729e4SPierre Morel 		break;
3878cb729e4SPierre Morel 	}
3888cb729e4SPierre Morel pop:
3898cb729e4SPierre Morel 	report_prefix_pop();
390*cd719531SJanis Schoetterl-Glausch 	lowcore.io_old_psw.mask &= ~PSW_MASK_WAIT;
3918cb729e4SPierre Morel }
3928cb729e4SPierre Morel 
start_ccw1_chain(unsigned int sid,struct ccw1 * ccw)3938cb729e4SPierre Morel int start_ccw1_chain(unsigned int sid, struct ccw1 *ccw)
3948cb729e4SPierre Morel {
3958cb729e4SPierre Morel 	struct orb orb = {
3968cb729e4SPierre Morel 		.intparm = sid,
3978cb729e4SPierre Morel 		.ctrl = ORB_CTRL_ISIC|ORB_CTRL_FMT|ORB_LPM_DFLT,
3988cb729e4SPierre Morel 		.cpa = (unsigned int) (unsigned long)ccw,
3998cb729e4SPierre Morel 	};
4008cb729e4SPierre Morel 
4018cb729e4SPierre Morel 	return ssch(sid, &orb);
4028cb729e4SPierre Morel }
4038cb729e4SPierre Morel 
ccw_alloc(int code,void * data,int count,unsigned char flags)404a6e5e596SPierre Morel struct ccw1 *ccw_alloc(int code, void *data, int count, unsigned char flags)
4058cb729e4SPierre Morel {
406a6e5e596SPierre Morel 	struct ccw1 *ccw;
4078cb729e4SPierre Morel 
408a6e5e596SPierre Morel 	ccw = alloc_io_mem(sizeof(*ccw), 0);
409a6e5e596SPierre Morel 	if (!ccw)
410a6e5e596SPierre Morel 		return NULL;
411a6e5e596SPierre Morel 
4128cb729e4SPierre Morel 	ccw->code = code;
4138cb729e4SPierre Morel 	ccw->flags = flags;
4148cb729e4SPierre Morel 	ccw->count = count;
4158cb729e4SPierre Morel 	ccw->data_address = (int)(unsigned long)data;
4168cb729e4SPierre Morel 
417a6e5e596SPierre Morel 	return ccw;
4188cb729e4SPierre Morel }
4198cb729e4SPierre Morel 
4208cb729e4SPierre Morel /* wait_and_check_io_completion:
4218cb729e4SPierre Morel  * @schid: the subchannel ID
4228cb729e4SPierre Morel  *
4238cb729e4SPierre Morel  * Makes the most common check to validate a successful I/O
4248cb729e4SPierre Morel  * completion.
4258cb729e4SPierre Morel  * Only report failures.
4268cb729e4SPierre Morel  */
wait_and_check_io_completion(int schid)4278cb729e4SPierre Morel int wait_and_check_io_completion(int schid)
4288cb729e4SPierre Morel {
4298cb729e4SPierre Morel 	int ret = 0;
4308cb729e4SPierre Morel 
4318cb729e4SPierre Morel 	wait_for_interrupt(PSW_MASK_IO);
4328cb729e4SPierre Morel 
4338cb729e4SPierre Morel 	report_prefix_push("check I/O completion");
4348cb729e4SPierre Morel 
435*cd719531SJanis Schoetterl-Glausch 	if (lowcore.io_int_param != schid) {
436198dfd0eSJanis Schoetterl-Glausch 		report_fail("interrupt parameter: expected %08x got %08x",
437*cd719531SJanis Schoetterl-Glausch 			    schid, lowcore.io_int_param);
4388cb729e4SPierre Morel 		ret = -1;
4398cb729e4SPierre Morel 		goto end;
4408cb729e4SPierre Morel 	}
4418cb729e4SPierre Morel 
4428cb729e4SPierre Morel 	/* Verify that device status is valid */
4438cb729e4SPierre Morel 	if (!(irb.scsw.ctrl & SCSW_SC_PENDING)) {
444198dfd0eSJanis Schoetterl-Glausch 		report_fail("No status pending after interrupt. Subch Ctrl: %08x",
4458cb729e4SPierre Morel 			    irb.scsw.ctrl);
4468cb729e4SPierre Morel 		ret = -1;
4478cb729e4SPierre Morel 		goto end;
4488cb729e4SPierre Morel 	}
4498cb729e4SPierre Morel 
4508cb729e4SPierre Morel 	if (!(irb.scsw.ctrl & (SCSW_SC_SECONDARY | SCSW_SC_PRIMARY))) {
451198dfd0eSJanis Schoetterl-Glausch 		report_fail("Primary or secondary status missing. Subch Ctrl: %08x",
4528cb729e4SPierre Morel 			    irb.scsw.ctrl);
4538cb729e4SPierre Morel 		ret = -1;
4548cb729e4SPierre Morel 		goto end;
4558cb729e4SPierre Morel 	}
4568cb729e4SPierre Morel 
4578cb729e4SPierre Morel 	if (!(irb.scsw.dev_stat & (SCSW_DEVS_DEV_END | SCSW_DEVS_SCH_END))) {
458198dfd0eSJanis Schoetterl-Glausch 		report_fail("No device end or sch end. Dev. status: %02x",
4598cb729e4SPierre Morel 			    irb.scsw.dev_stat);
4608cb729e4SPierre Morel 		ret = -1;
4618cb729e4SPierre Morel 		goto end;
4628cb729e4SPierre Morel 	}
4638cb729e4SPierre Morel 
4648cb729e4SPierre Morel 	if (irb.scsw.sch_stat & ~SCSW_SCHS_IL) {
4658cb729e4SPierre Morel 		report_info("Unexpected Subch. status %02x", irb.scsw.sch_stat);
4668cb729e4SPierre Morel 		ret = -1;
4678cb729e4SPierre Morel 		goto end;
4688cb729e4SPierre Morel 	}
4698cb729e4SPierre Morel 
4708cb729e4SPierre Morel end:
4718cb729e4SPierre Morel 	report_prefix_pop();
4728cb729e4SPierre Morel 	return ret;
4738cb729e4SPierre Morel }
4748cb729e4SPierre Morel 
4758cb729e4SPierre Morel /*
4768cb729e4SPierre Morel  * css_residual_count
4778cb729e4SPierre Morel  * Return the residual count, if it is valid.
4788cb729e4SPierre Morel  *
4798cb729e4SPierre Morel  * Return value:
4808cb729e4SPierre Morel  * Success: the residual count
4818cb729e4SPierre Morel  * Not meaningful: -1 (-1 can not be a valid count)
4828cb729e4SPierre Morel  */
css_residual_count(unsigned int schid)4838cb729e4SPierre Morel int css_residual_count(unsigned int schid)
4848cb729e4SPierre Morel {
4858cb729e4SPierre Morel 
4868cb729e4SPierre Morel 	if (!(irb.scsw.ctrl & (SCSW_SC_PENDING | SCSW_SC_PRIMARY)))
4878cb729e4SPierre Morel 		return -1;
4888cb729e4SPierre Morel 
4898cb729e4SPierre Morel 	if (irb.scsw.dev_stat)
4908cb729e4SPierre Morel 		if (irb.scsw.sch_stat & ~(SCSW_SCHS_PCI | SCSW_SCHS_IL))
4918cb729e4SPierre Morel 			return -1;
4928cb729e4SPierre Morel 
4938cb729e4SPierre Morel 	return irb.scsw.count;
4948cb729e4SPierre Morel }
4958cb729e4SPierre Morel 
4968cb729e4SPierre Morel /*
4978cb729e4SPierre Morel  * enable_io_isc: setup ISC in Control Register 6
4988cb729e4SPierre Morel  * @isc: The interruption Sub Class as a bitfield
4998cb729e4SPierre Morel  */
enable_io_isc(uint8_t isc)5008cb729e4SPierre Morel void enable_io_isc(uint8_t isc)
5018cb729e4SPierre Morel {
5028cb729e4SPierre Morel 	uint64_t value;
5038cb729e4SPierre Morel 
5048cb729e4SPierre Morel 	value = (uint64_t)isc << 24;
5058cb729e4SPierre Morel 	lctlg(6, value);
5068cb729e4SPierre Morel }
5072b30c34dSNico Boehr 
is_path_installed(struct schib * schib,int chp_idx)5082b30c34dSNico Boehr static int is_path_installed(struct schib *schib, int chp_idx)
5092b30c34dSNico Boehr {
5102b30c34dSNico Boehr 	return schib->pmcw.pim & BIT(7 - chp_idx);
5112b30c34dSNico Boehr }
5122b30c34dSNico Boehr 
5132b30c34dSNico Boehr /*
5142b30c34dSNico Boehr  * css_find_installed_chpid: find any installed CHPID
5152b30c34dSNico Boehr  * @sid: subsystem-identification word
5162b30c34dSNico Boehr  * @chpid_out: store the found chpid here, left alone if none found
5172b30c34dSNico Boehr  *
5182b30c34dSNico Boehr  * returns 0 on success, -1 if no chpid found any other value
5192b30c34dSNico Boehr  * indicates the condition code of a failing STSCH instruction
5202b30c34dSNico Boehr  */
css_find_installed_chpid(int sid,uint8_t * chpid_out)5212b30c34dSNico Boehr int css_find_installed_chpid(int sid, uint8_t *chpid_out)
5222b30c34dSNico Boehr {
5232b30c34dSNico Boehr 	int cc;
5242b30c34dSNico Boehr 
5252b30c34dSNico Boehr 	cc = stsch(sid, &schib);
5262b30c34dSNico Boehr 	if (cc) {
5272b30c34dSNico Boehr 		report_fail("%s: sch %08x failed with cc=%d", __func__, sid, cc);
5282b30c34dSNico Boehr 		return cc;
5292b30c34dSNico Boehr 	}
5302b30c34dSNico Boehr 
5312b30c34dSNico Boehr 	for (int i = 0; i < ARRAY_SIZE(schib.pmcw.chpid); i++) {
5322b30c34dSNico Boehr 		if (is_path_installed(&schib, i)) {
5332b30c34dSNico Boehr 			*chpid_out = schib.pmcw.chpid[i];
5342b30c34dSNico Boehr 			return 0;
5352b30c34dSNico Boehr 		}
5362b30c34dSNico Boehr 	}
5372b30c34dSNico Boehr 
5382b30c34dSNico Boehr 	return -1;
5392b30c34dSNico Boehr }
5402b30c34dSNico Boehr 
5412b30c34dSNico Boehr /*
5422b30c34dSNico Boehr  * css_generate_crw: Generate a CRW by issuing RCHP on any channel path
5432b30c34dSNico Boehr  * @sid: subsystem-identification word
5442b30c34dSNico Boehr  *
5452b30c34dSNico Boehr  * returns 0 when a CRW was generated, -1 if no chpid found.
5462b30c34dSNico Boehr  */
css_generate_crw(int sid)5472b30c34dSNico Boehr int css_generate_crw(int sid)
5482b30c34dSNico Boehr {
5492b30c34dSNico Boehr 	int ret, cc;
5502b30c34dSNico Boehr 	uint8_t chpid;
5512b30c34dSNico Boehr 
5522b30c34dSNico Boehr 	report_prefix_push("Generate CRW");
5532b30c34dSNico Boehr 
5542b30c34dSNico Boehr 	ret = css_find_installed_chpid(sid, &chpid);
5552b30c34dSNico Boehr 	if (ret) {
5562b30c34dSNico Boehr 		report_fail("No CHPID found: ret=%d", ret);
5572b30c34dSNico Boehr 		return -1;
5582b30c34dSNico Boehr 	}
5592b30c34dSNico Boehr 
5602b30c34dSNico Boehr 	cc = rchp(chpid);
5612b30c34dSNico Boehr 	report(!cc, "rhcp cc != 0");
5622b30c34dSNico Boehr 
5632b30c34dSNico Boehr 	report_prefix_pop();
5642b30c34dSNico Boehr 
5652b30c34dSNico Boehr 	return 0;
5662b30c34dSNico Boehr }
567