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