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 223227b5a6SPierre Morel static 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 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 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 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 */ 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 /* 165*d3d7fa5aSPierre Morel * css_enabled: report if the subchannel is enabled 166*d3d7fa5aSPierre Morel * @schid: Subchannel Identifier 167*d3d7fa5aSPierre Morel * Return value: 168*d3d7fa5aSPierre Morel * true if the subchannel is enabled 169*d3d7fa5aSPierre Morel * false otherwise 170*d3d7fa5aSPierre Morel */ 171*d3d7fa5aSPierre Morel bool css_enabled(int schid) 172*d3d7fa5aSPierre Morel { 173*d3d7fa5aSPierre Morel struct pmcw *pmcw = &schib.pmcw; 174*d3d7fa5aSPierre Morel int cc; 175*d3d7fa5aSPierre Morel 176*d3d7fa5aSPierre Morel cc = stsch(schid, &schib); 177*d3d7fa5aSPierre Morel if (cc) { 178*d3d7fa5aSPierre Morel report_info("stsch: updating sch %08x failed with cc=%d", 179*d3d7fa5aSPierre Morel schid, cc); 180*d3d7fa5aSPierre Morel return false; 181*d3d7fa5aSPierre Morel } 182*d3d7fa5aSPierre Morel 183*d3d7fa5aSPierre Morel if (!(pmcw->flags & PMCW_ENABLE)) { 184*d3d7fa5aSPierre Morel report_info("stsch: sch %08x not enabled", schid); 185*d3d7fa5aSPierre Morel return false; 186*d3d7fa5aSPierre Morel } 187*d3d7fa5aSPierre Morel return true; 188*d3d7fa5aSPierre Morel } 189*d3d7fa5aSPierre 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 */ 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 */ 238*d3d7fa5aSPierre 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 2518cb729e4SPierre Morel static struct irb irb; 2528cb729e4SPierre Morel 2538cb729e4SPierre Morel void css_irq_io(void) 2548cb729e4SPierre Morel { 2558cb729e4SPierre Morel int ret = 0; 2568cb729e4SPierre Morel char *flags; 2578cb729e4SPierre Morel int sid; 2588cb729e4SPierre Morel 2598cb729e4SPierre Morel report_prefix_push("Interrupt"); 2608cb729e4SPierre Morel sid = lowcore_ptr->subsys_id_word; 2618cb729e4SPierre Morel /* Lowlevel set the SID as interrupt parameter. */ 2628cb729e4SPierre Morel if (lowcore_ptr->io_int_param != sid) { 2638cb729e4SPierre Morel report(0, 2648cb729e4SPierre Morel "io_int_param: %x differs from subsys_id_word: %x", 2658cb729e4SPierre Morel lowcore_ptr->io_int_param, sid); 2668cb729e4SPierre Morel goto pop; 2678cb729e4SPierre Morel } 2688cb729e4SPierre Morel report_prefix_pop(); 2698cb729e4SPierre Morel 2708cb729e4SPierre Morel report_prefix_push("tsch"); 2718cb729e4SPierre Morel ret = tsch(sid, &irb); 2728cb729e4SPierre Morel switch (ret) { 2738cb729e4SPierre Morel case 1: 2748cb729e4SPierre Morel dump_irb(&irb); 2758cb729e4SPierre Morel flags = dump_scsw_flags(irb.scsw.ctrl); 2768cb729e4SPierre Morel report(0, 2778cb729e4SPierre Morel "I/O interrupt, but tsch returns CC 1 for subchannel %08x. SCSW flags: %s", 2788cb729e4SPierre Morel sid, flags); 2798cb729e4SPierre Morel break; 2808cb729e4SPierre Morel case 2: 2818cb729e4SPierre Morel report(0, "tsch returns unexpected CC 2"); 2828cb729e4SPierre Morel break; 2838cb729e4SPierre Morel case 3: 2848cb729e4SPierre Morel report(0, "tsch reporting sch %08x as not operational", sid); 2858cb729e4SPierre Morel break; 2868cb729e4SPierre Morel case 0: 2878cb729e4SPierre Morel /* Stay humble on success */ 2888cb729e4SPierre Morel break; 2898cb729e4SPierre Morel } 2908cb729e4SPierre Morel pop: 2918cb729e4SPierre Morel report_prefix_pop(); 2928cb729e4SPierre Morel lowcore_ptr->io_old_psw.mask &= ~PSW_MASK_WAIT; 2938cb729e4SPierre Morel } 2948cb729e4SPierre Morel 2958cb729e4SPierre Morel int start_ccw1_chain(unsigned int sid, struct ccw1 *ccw) 2968cb729e4SPierre Morel { 2978cb729e4SPierre Morel struct orb orb = { 2988cb729e4SPierre Morel .intparm = sid, 2998cb729e4SPierre Morel .ctrl = ORB_CTRL_ISIC|ORB_CTRL_FMT|ORB_LPM_DFLT, 3008cb729e4SPierre Morel .cpa = (unsigned int) (unsigned long)ccw, 3018cb729e4SPierre Morel }; 3028cb729e4SPierre Morel 3038cb729e4SPierre Morel return ssch(sid, &orb); 3048cb729e4SPierre Morel } 3058cb729e4SPierre Morel 306a6e5e596SPierre Morel struct ccw1 *ccw_alloc(int code, void *data, int count, unsigned char flags) 3078cb729e4SPierre Morel { 308a6e5e596SPierre Morel struct ccw1 *ccw; 3098cb729e4SPierre Morel 310a6e5e596SPierre Morel ccw = alloc_io_mem(sizeof(*ccw), 0); 311a6e5e596SPierre Morel if (!ccw) 312a6e5e596SPierre Morel return NULL; 313a6e5e596SPierre Morel 3148cb729e4SPierre Morel ccw->code = code; 3158cb729e4SPierre Morel ccw->flags = flags; 3168cb729e4SPierre Morel ccw->count = count; 3178cb729e4SPierre Morel ccw->data_address = (int)(unsigned long)data; 3188cb729e4SPierre Morel 319a6e5e596SPierre Morel return ccw; 3208cb729e4SPierre Morel } 3218cb729e4SPierre Morel 3228cb729e4SPierre Morel /* wait_and_check_io_completion: 3238cb729e4SPierre Morel * @schid: the subchannel ID 3248cb729e4SPierre Morel * 3258cb729e4SPierre Morel * Makes the most common check to validate a successful I/O 3268cb729e4SPierre Morel * completion. 3278cb729e4SPierre Morel * Only report failures. 3288cb729e4SPierre Morel */ 3298cb729e4SPierre Morel int wait_and_check_io_completion(int schid) 3308cb729e4SPierre Morel { 3318cb729e4SPierre Morel int ret = 0; 3328cb729e4SPierre Morel 3338cb729e4SPierre Morel wait_for_interrupt(PSW_MASK_IO); 3348cb729e4SPierre Morel 3358cb729e4SPierre Morel report_prefix_push("check I/O completion"); 3368cb729e4SPierre Morel 3378cb729e4SPierre Morel if (lowcore_ptr->io_int_param != schid) { 3388cb729e4SPierre Morel report(0, "interrupt parameter: expected %08x got %08x", 3398cb729e4SPierre Morel schid, lowcore_ptr->io_int_param); 3408cb729e4SPierre Morel ret = -1; 3418cb729e4SPierre Morel goto end; 3428cb729e4SPierre Morel } 3438cb729e4SPierre Morel 3448cb729e4SPierre Morel /* Verify that device status is valid */ 3458cb729e4SPierre Morel if (!(irb.scsw.ctrl & SCSW_SC_PENDING)) { 3468cb729e4SPierre Morel report(0, "No status pending after interrupt. Subch Ctrl: %08x", 3478cb729e4SPierre Morel irb.scsw.ctrl); 3488cb729e4SPierre Morel ret = -1; 3498cb729e4SPierre Morel goto end; 3508cb729e4SPierre Morel } 3518cb729e4SPierre Morel 3528cb729e4SPierre Morel if (!(irb.scsw.ctrl & (SCSW_SC_SECONDARY | SCSW_SC_PRIMARY))) { 3538cb729e4SPierre Morel report(0, "Primary or secondary status missing. Subch Ctrl: %08x", 3548cb729e4SPierre Morel irb.scsw.ctrl); 3558cb729e4SPierre Morel ret = -1; 3568cb729e4SPierre Morel goto end; 3578cb729e4SPierre Morel } 3588cb729e4SPierre Morel 3598cb729e4SPierre Morel if (!(irb.scsw.dev_stat & (SCSW_DEVS_DEV_END | SCSW_DEVS_SCH_END))) { 3608cb729e4SPierre Morel report(0, "No device end or sch end. Dev. status: %02x", 3618cb729e4SPierre Morel irb.scsw.dev_stat); 3628cb729e4SPierre Morel ret = -1; 3638cb729e4SPierre Morel goto end; 3648cb729e4SPierre Morel } 3658cb729e4SPierre Morel 3668cb729e4SPierre Morel if (irb.scsw.sch_stat & ~SCSW_SCHS_IL) { 3678cb729e4SPierre Morel report_info("Unexpected Subch. status %02x", irb.scsw.sch_stat); 3688cb729e4SPierre Morel ret = -1; 3698cb729e4SPierre Morel goto end; 3708cb729e4SPierre Morel } 3718cb729e4SPierre Morel 3728cb729e4SPierre Morel end: 3738cb729e4SPierre Morel report_prefix_pop(); 3748cb729e4SPierre Morel return ret; 3758cb729e4SPierre Morel } 3768cb729e4SPierre Morel 3778cb729e4SPierre Morel /* 3788cb729e4SPierre Morel * css_residual_count 3798cb729e4SPierre Morel * Return the residual count, if it is valid. 3808cb729e4SPierre Morel * 3818cb729e4SPierre Morel * Return value: 3828cb729e4SPierre Morel * Success: the residual count 3838cb729e4SPierre Morel * Not meaningful: -1 (-1 can not be a valid count) 3848cb729e4SPierre Morel */ 3858cb729e4SPierre Morel int css_residual_count(unsigned int schid) 3868cb729e4SPierre Morel { 3878cb729e4SPierre Morel 3888cb729e4SPierre Morel if (!(irb.scsw.ctrl & (SCSW_SC_PENDING | SCSW_SC_PRIMARY))) 3898cb729e4SPierre Morel return -1; 3908cb729e4SPierre Morel 3918cb729e4SPierre Morel if (irb.scsw.dev_stat) 3928cb729e4SPierre Morel if (irb.scsw.sch_stat & ~(SCSW_SCHS_PCI | SCSW_SCHS_IL)) 3938cb729e4SPierre Morel return -1; 3948cb729e4SPierre Morel 3958cb729e4SPierre Morel return irb.scsw.count; 3968cb729e4SPierre Morel } 3978cb729e4SPierre Morel 3988cb729e4SPierre Morel /* 3998cb729e4SPierre Morel * enable_io_isc: setup ISC in Control Register 6 4008cb729e4SPierre Morel * @isc: The interruption Sub Class as a bitfield 4018cb729e4SPierre Morel */ 4028cb729e4SPierre Morel void enable_io_isc(uint8_t isc) 4038cb729e4SPierre Morel { 4048cb729e4SPierre Morel uint64_t value; 4058cb729e4SPierre Morel 4068cb729e4SPierre Morel value = (uint64_t)isc << 24; 4078cb729e4SPierre Morel lctlg(6, value); 4088cb729e4SPierre Morel } 409