blob: 23a7b7cb8c3387b15824239df04444a22f5de7d8 [file] [log] [blame] [edit]
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Channel Subsystem tests
*
* Copyright (c) 2020 IBM Corp
*
* Authors:
* Pierre Morel <pmorel@linux.ibm.com>
*/
#include <libcflat.h>
#include <alloc_phys.h>
#include <asm/page.h>
#include <string.h>
#include <interrupt.h>
#include <asm/arch_def.h>
#include <css.h>
#define DEFAULT_CU_TYPE 0x3832 /* virtio-ccw */
static unsigned long cu_type = DEFAULT_CU_TYPE;
static int test_device_sid;
static struct senseid senseid;
static void test_enumerate(void)
{
test_device_sid = css_enumerate();
if (test_device_sid & SCHID_ONE) {
report(1, "Schid of first I/O device: 0x%08x", test_device_sid);
return;
}
report(0, "No I/O device found");
}
static void test_enable(void)
{
int cc;
if (!test_device_sid) {
report_skip("No device");
return;
}
cc = css_enable(test_device_sid, IO_SCH_ISC);
report(cc == 0, "Enable subchannel %08x", test_device_sid);
}
/*
* test_sense
* Pre-requisites:
* - We need the test device as the first recognized
* device by the enumeration.
*/
static void test_sense(void)
{
int ret;
int len;
if (!test_device_sid) {
report_skip("No device");
return;
}
ret = css_enable(test_device_sid, IO_SCH_ISC);
if (ret) {
report(0, "Could not enable the subchannel: %08x",
test_device_sid);
return;
}
ret = register_io_int_func(css_irq_io);
if (ret) {
report(0, "Could not register IRQ handler");
return;
}
lowcore_ptr->io_int_param = 0;
memset(&senseid, 0, sizeof(senseid));
ret = start_single_ccw(test_device_sid, CCW_CMD_SENSE_ID,
&senseid, sizeof(senseid), CCW_F_SLI);
if (ret)
goto error;
if (wait_and_check_io_completion(test_device_sid) < 0)
goto error;
/* Test transfer completion */
report_prefix_push("ssch transfer completion");
ret = css_residual_count(test_device_sid);
if (ret < 0) {
report_info("no valid residual count");
} else if (ret != 0) {
len = sizeof(senseid) - ret;
if (ret && len < CSS_SENSEID_COMMON_LEN) {
report(0, "transferred a too short length: %d", ret);
goto error;
} else if (ret && len)
report_info("transferred a shorter length: %d", len);
}
if (senseid.reserved != 0xff) {
report(0, "transferred garbage: 0x%02x", senseid.reserved);
goto error;
}
report_prefix_pop();
report_info("reserved 0x%02x cu_type 0x%04x cu_model 0x%02x dev_type 0x%04x dev_model 0x%02x",
senseid.reserved, senseid.cu_type, senseid.cu_model,
senseid.dev_type, senseid.dev_model);
report(senseid.cu_type == cu_type, "cu_type expected 0x%04x got 0x%04x",
(uint16_t) cu_type, senseid.cu_type);
error:
unregister_io_int_func(css_irq_io);
}
static struct {
const char *name;
void (*func)(void);
} tests[] = {
{ "enumerate (stsch)", test_enumerate },
{ "enable (msch)", test_enable },
{ "sense (ssch/tsch)", test_sense },
{ NULL, NULL }
};
int main(int argc, char *argv[])
{
int i;
report_prefix_push("Channel Subsystem");
enable_io_isc(0x80 >> IO_SCH_ISC);
for (i = 0; tests[i].name; i++) {
report_prefix_push(tests[i].name);
tests[i].func();
report_prefix_pop();
}
report_prefix_pop();
return report_summary();
}