xref: /linux/drivers/s390/char/sclp_early_core.c (revision 4f6b838c378a52ea3ae0b15f12ca8a20849072fa)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
222362a0eSMartin Schwidefsky /*
322362a0eSMartin Schwidefsky  *    Copyright IBM Corp. 2015
422362a0eSMartin Schwidefsky  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
522362a0eSMartin Schwidefsky  */
6d5ab7a34SHeiko Carstens 
722362a0eSMartin Schwidefsky #include <linux/kernel.h>
8d5ab7a34SHeiko Carstens #include <asm/processor.h>
9d5ab7a34SHeiko Carstens #include <asm/lowcore.h>
1022362a0eSMartin Schwidefsky #include <asm/ebcdic.h>
1122362a0eSMartin Schwidefsky #include <asm/irq.h>
1217aacfbfSVasily Gorbik #include <asm/sections.h>
13fddbaa5cSVasily Gorbik #include <asm/mem_detect.h>
14d5ab7a34SHeiko Carstens #include "sclp.h"
15d5ab7a34SHeiko Carstens #include "sclp_rw.h"
1622362a0eSMartin Schwidefsky 
1717aacfbfSVasily Gorbik static struct read_info_sccb __bootdata(sclp_info_sccb);
1817aacfbfSVasily Gorbik static int __bootdata(sclp_info_sccb_valid);
19087c4d74SGerald Schaefer char *sclp_early_sccb = (char *) EARLY_SCCB_OFFSET;
2014ab6224SVasily Gorbik int sclp_init_state = sclp_init_state_uninitialized;
210b0d1173SClaudio Imbrenda /*
220b0d1173SClaudio Imbrenda  * Used to keep track of the size of the event masks. Qemu until version 2.11
230b0d1173SClaudio Imbrenda  * only supports 4 and needs a workaround.
240b0d1173SClaudio Imbrenda  */
2514ab6224SVasily Gorbik bool sclp_mask_compat_mode;
2676fdf141SHeiko Carstens 
27d5ab7a34SHeiko Carstens void sclp_early_wait_irq(void)
2822362a0eSMartin Schwidefsky {
29d5ab7a34SHeiko Carstens 	unsigned long psw_mask, addr;
3022362a0eSMartin Schwidefsky 	psw_t psw_ext_save, psw_wait;
31742dc577SHeiko Carstens 	union ctlreg0 cr0, cr0_new;
3222362a0eSMartin Schwidefsky 
33742dc577SHeiko Carstens 	__ctl_store(cr0.val, 0, 0);
34742dc577SHeiko Carstens 	cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK;
35742dc577SHeiko Carstens 	cr0_new.lap = 0;
36742dc577SHeiko Carstens 	cr0_new.sssm = 1;
37742dc577SHeiko Carstens 	__ctl_load(cr0_new.val, 0, 0);
3822362a0eSMartin Schwidefsky 
3922362a0eSMartin Schwidefsky 	psw_ext_save = S390_lowcore.external_new_psw;
40f07f21b3SSascha Silbe 	psw_mask = __extract_psw();
4122362a0eSMartin Schwidefsky 	S390_lowcore.external_new_psw.mask = psw_mask;
4222362a0eSMartin Schwidefsky 	psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT;
4322362a0eSMartin Schwidefsky 	S390_lowcore.ext_int_code = 0;
4422362a0eSMartin Schwidefsky 
4522362a0eSMartin Schwidefsky 	do {
4622362a0eSMartin Schwidefsky 		asm volatile(
4722362a0eSMartin Schwidefsky 			"	larl	%[addr],0f\n"
4822362a0eSMartin Schwidefsky 			"	stg	%[addr],%[psw_wait_addr]\n"
4922362a0eSMartin Schwidefsky 			"	stg	%[addr],%[psw_ext_addr]\n"
5022362a0eSMartin Schwidefsky 			"	lpswe	%[psw_wait]\n"
5122362a0eSMartin Schwidefsky 			"0:\n"
5222362a0eSMartin Schwidefsky 			: [addr] "=&d" (addr),
5322362a0eSMartin Schwidefsky 			  [psw_wait_addr] "=Q" (psw_wait.addr),
5422362a0eSMartin Schwidefsky 			  [psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr)
5522362a0eSMartin Schwidefsky 			: [psw_wait] "Q" (psw_wait)
5622362a0eSMartin Schwidefsky 			: "cc", "memory");
5722362a0eSMartin Schwidefsky 	} while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG);
5822362a0eSMartin Schwidefsky 
5922362a0eSMartin Schwidefsky 	S390_lowcore.external_new_psw = psw_ext_save;
60742dc577SHeiko Carstens 	__ctl_load(cr0.val, 0, 0);
6122362a0eSMartin Schwidefsky }
6222362a0eSMartin Schwidefsky 
63f694bb3aSHeiko Carstens int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb)
6422362a0eSMartin Schwidefsky {
65d5ab7a34SHeiko Carstens 	unsigned long flags;
6622362a0eSMartin Schwidefsky 	int rc;
6722362a0eSMartin Schwidefsky 
68d5ab7a34SHeiko Carstens 	raw_local_irq_save(flags);
69d5ab7a34SHeiko Carstens 	rc = sclp_service_call(cmd, sccb);
7022362a0eSMartin Schwidefsky 	if (rc)
71d5ab7a34SHeiko Carstens 		goto out;
72d5ab7a34SHeiko Carstens 	sclp_early_wait_irq();
73d5ab7a34SHeiko Carstens out:
74d5ab7a34SHeiko Carstens 	raw_local_irq_restore(flags);
7522362a0eSMartin Schwidefsky 	return rc;
76d5ab7a34SHeiko Carstens }
77d5ab7a34SHeiko Carstens 
78d5ab7a34SHeiko Carstens struct write_sccb {
79d5ab7a34SHeiko Carstens 	struct sccb_header header;
80d5ab7a34SHeiko Carstens 	struct msg_buf msg;
81d5ab7a34SHeiko Carstens } __packed;
8222362a0eSMartin Schwidefsky 
83d5ab7a34SHeiko Carstens /* Output multi-line text using SCLP Message interface. */
84d5ab7a34SHeiko Carstens static void sclp_early_print_lm(const char *str, unsigned int len)
85d5ab7a34SHeiko Carstens {
86d5ab7a34SHeiko Carstens 	unsigned char *ptr, *end, ch;
87d5ab7a34SHeiko Carstens 	unsigned int count, offset;
88d5ab7a34SHeiko Carstens 	struct write_sccb *sccb;
89d5ab7a34SHeiko Carstens 	struct msg_buf *msg;
90d5ab7a34SHeiko Carstens 	struct mdb *mdb;
91d5ab7a34SHeiko Carstens 	struct mto *mto;
92d5ab7a34SHeiko Carstens 	struct go *go;
93d5ab7a34SHeiko Carstens 
94087c4d74SGerald Schaefer 	sccb = (struct write_sccb *) sclp_early_sccb;
95087c4d74SGerald Schaefer 	end = (unsigned char *) sccb + EARLY_SCCB_SIZE - 1;
96d5ab7a34SHeiko Carstens 	memset(sccb, 0, sizeof(*sccb));
97d5ab7a34SHeiko Carstens 	ptr = (unsigned char *) &sccb->msg.mdb.mto;
98d5ab7a34SHeiko Carstens 	offset = 0;
9922362a0eSMartin Schwidefsky 	do {
100d5ab7a34SHeiko Carstens 		for (count = sizeof(*mto); offset < len; count++) {
101d5ab7a34SHeiko Carstens 			ch = str[offset++];
102d5ab7a34SHeiko Carstens 			if ((ch == 0x0a) || (ptr + count > end))
103f0319748SHeiko Carstens 				break;
10422362a0eSMartin Schwidefsky 			ptr[count] = _ascebc[ch];
10522362a0eSMartin Schwidefsky 		}
106d5ab7a34SHeiko Carstens 		mto = (struct mto *) ptr;
107d5ab7a34SHeiko Carstens 		memset(mto, 0, sizeof(*mto));
108d5ab7a34SHeiko Carstens 		mto->length = count;
109d5ab7a34SHeiko Carstens 		mto->type = 4;
110d5ab7a34SHeiko Carstens 		mto->line_type_flags = LNTPFLGS_ENDTEXT;
11122362a0eSMartin Schwidefsky 		ptr += count;
112d5ab7a34SHeiko Carstens 	} while ((offset < len) && (ptr + sizeof(*mto) <= end));
113d5ab7a34SHeiko Carstens 	len = ptr - (unsigned char *) sccb;
114d5ab7a34SHeiko Carstens 	sccb->header.length = len - offsetof(struct write_sccb, header);
115d5ab7a34SHeiko Carstens 	msg = &sccb->msg;
116d5ab7a34SHeiko Carstens 	msg->header.type = EVTYP_MSG;
117d5ab7a34SHeiko Carstens 	msg->header.length = len - offsetof(struct write_sccb, msg.header);
118d5ab7a34SHeiko Carstens 	mdb = &msg->mdb;
119d5ab7a34SHeiko Carstens 	mdb->header.type = 1;
120d5ab7a34SHeiko Carstens 	mdb->header.tag = 0xD4C4C240;
121d5ab7a34SHeiko Carstens 	mdb->header.revision_code = 1;
122d5ab7a34SHeiko Carstens 	mdb->header.length = len - offsetof(struct write_sccb, msg.mdb.header);
123d5ab7a34SHeiko Carstens 	go = &mdb->go;
124d5ab7a34SHeiko Carstens 	go->length = sizeof(*go);
125d5ab7a34SHeiko Carstens 	go->type = 1;
126d5ab7a34SHeiko Carstens 	sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
12722362a0eSMartin Schwidefsky }
12822362a0eSMartin Schwidefsky 
129d5ab7a34SHeiko Carstens struct vt220_sccb {
130d5ab7a34SHeiko Carstens 	struct sccb_header header;
131d5ab7a34SHeiko Carstens 	struct {
132d5ab7a34SHeiko Carstens 		struct evbuf_header header;
133d5ab7a34SHeiko Carstens 		char data[];
134d5ab7a34SHeiko Carstens 	} msg;
135d5ab7a34SHeiko Carstens } __packed;
136d5ab7a34SHeiko Carstens 
13702407baaSHeiko Carstens /* Output multi-line text using SCLP VT220 interface. */
138d5ab7a34SHeiko Carstens static void sclp_early_print_vt220(const char *str, unsigned int len)
13922362a0eSMartin Schwidefsky {
140d5ab7a34SHeiko Carstens 	struct vt220_sccb *sccb;
14122362a0eSMartin Schwidefsky 
142087c4d74SGerald Schaefer 	sccb = (struct vt220_sccb *) sclp_early_sccb;
143087c4d74SGerald Schaefer 	if (sizeof(*sccb) + len >= EARLY_SCCB_SIZE)
144087c4d74SGerald Schaefer 		len = EARLY_SCCB_SIZE - sizeof(*sccb);
145d5ab7a34SHeiko Carstens 	memset(sccb, 0, sizeof(*sccb));
146d5ab7a34SHeiko Carstens 	memcpy(&sccb->msg.data, str, len);
14702407baaSHeiko Carstens 	sccb->header.length = sizeof(*sccb) + len;
14802407baaSHeiko Carstens 	sccb->msg.header.length = sizeof(sccb->msg) + len;
149d5ab7a34SHeiko Carstens 	sccb->msg.header.type = EVTYP_VT220MSG;
150d5ab7a34SHeiko Carstens 	sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
151d5ab7a34SHeiko Carstens }
1523f975df6SSascha Silbe 
153d5ab7a34SHeiko Carstens int sclp_early_set_event_mask(struct init_sccb *sccb,
1540ee5f8dcSClaudio Imbrenda 			      sccb_mask_t receive_mask,
1550ee5f8dcSClaudio Imbrenda 			      sccb_mask_t send_mask)
156d5ab7a34SHeiko Carstens {
1570b0d1173SClaudio Imbrenda retry:
158d5ab7a34SHeiko Carstens 	memset(sccb, 0, sizeof(*sccb));
159d5ab7a34SHeiko Carstens 	sccb->header.length = sizeof(*sccb);
1600b0d1173SClaudio Imbrenda 	if (sclp_mask_compat_mode)
1610b0d1173SClaudio Imbrenda 		sccb->mask_length = SCLP_MASK_SIZE_COMPAT;
1620b0d1173SClaudio Imbrenda 	else
163d5ab7a34SHeiko Carstens 		sccb->mask_length = sizeof(sccb_mask_t);
164b8435635SClaudio Imbrenda 	sccb_set_recv_mask(sccb, receive_mask);
165b8435635SClaudio Imbrenda 	sccb_set_send_mask(sccb, send_mask);
166f694bb3aSHeiko Carstens 	if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb))
167f694bb3aSHeiko Carstens 		return -EIO;
1680b0d1173SClaudio Imbrenda 	if ((sccb->header.response_code == 0x74f0) && !sclp_mask_compat_mode) {
1690b0d1173SClaudio Imbrenda 		sclp_mask_compat_mode = true;
1700b0d1173SClaudio Imbrenda 		goto retry;
1710b0d1173SClaudio Imbrenda 	}
172f694bb3aSHeiko Carstens 	if (sccb->header.response_code != 0x20)
173f694bb3aSHeiko Carstens 		return -EIO;
174f694bb3aSHeiko Carstens 	return 0;
175d5ab7a34SHeiko Carstens }
1763f975df6SSascha Silbe 
177d5ab7a34SHeiko Carstens unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb)
178d5ab7a34SHeiko Carstens {
179b8435635SClaudio Imbrenda 	if (!(sccb_get_sclp_send_mask(sccb) & EVTYP_OPCMD_MASK))
180d5ab7a34SHeiko Carstens 		return 0;
181b8435635SClaudio Imbrenda 	if (!(sccb_get_sclp_recv_mask(sccb) & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
182d5ab7a34SHeiko Carstens 		return 0;
183d5ab7a34SHeiko Carstens 	return 1;
184d5ab7a34SHeiko Carstens }
1853f975df6SSascha Silbe 
186b8435635SClaudio Imbrenda unsigned int sclp_early_con_check_vt220(struct init_sccb *sccb)
187b8435635SClaudio Imbrenda {
188b8435635SClaudio Imbrenda 	if (sccb_get_sclp_send_mask(sccb) & EVTYP_VT220MSG_MASK)
189b8435635SClaudio Imbrenda 		return 1;
190b8435635SClaudio Imbrenda 	return 0;
191b8435635SClaudio Imbrenda }
192b8435635SClaudio Imbrenda 
193d5ab7a34SHeiko Carstens static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
194d5ab7a34SHeiko Carstens {
195d5ab7a34SHeiko Carstens 	unsigned long receive_mask, send_mask;
196d5ab7a34SHeiko Carstens 	struct init_sccb *sccb;
197d5ab7a34SHeiko Carstens 	int rc;
198d5ab7a34SHeiko Carstens 
199b8435635SClaudio Imbrenda 	BUILD_BUG_ON(sizeof(struct init_sccb) > PAGE_SIZE);
200b8435635SClaudio Imbrenda 
201d5ab7a34SHeiko Carstens 	*have_linemode = *have_vt220 = 0;
202087c4d74SGerald Schaefer 	sccb = (struct init_sccb *) sclp_early_sccb;
203d5ab7a34SHeiko Carstens 	receive_mask = disable ? 0 : EVTYP_OPCMD_MASK;
204d5ab7a34SHeiko Carstens 	send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK;
205d5ab7a34SHeiko Carstens 	rc = sclp_early_set_event_mask(sccb, receive_mask, send_mask);
206d5ab7a34SHeiko Carstens 	if (rc)
207d5ab7a34SHeiko Carstens 		return rc;
208d5ab7a34SHeiko Carstens 	*have_linemode = sclp_early_con_check_linemode(sccb);
209b8435635SClaudio Imbrenda 	*have_vt220 = !!(sccb_get_send_mask(sccb) & EVTYP_VT220MSG_MASK);
210d5ab7a34SHeiko Carstens 	return rc;
2113f975df6SSascha Silbe }
2123f975df6SSascha Silbe 
21302407baaSHeiko Carstens /*
21402407baaSHeiko Carstens  * Output one or more lines of text on the SCLP console (VT220 and /
21502407baaSHeiko Carstens  * or line-mode).
2163f975df6SSascha Silbe  */
217*5596c4c1SVasily Gorbik void __sclp_early_printk(const char *str, unsigned int len)
2183f975df6SSascha Silbe {
219d5ab7a34SHeiko Carstens 	int have_linemode, have_vt220;
220d5ab7a34SHeiko Carstens 
221*5596c4c1SVasily Gorbik 	if (sclp_init_state != sclp_init_state_uninitialized)
22276fdf141SHeiko Carstens 		return;
223d5ab7a34SHeiko Carstens 	if (sclp_early_setup(0, &have_linemode, &have_vt220) != 0)
2243f975df6SSascha Silbe 		return;
2253f975df6SSascha Silbe 	if (have_linemode)
226d5ab7a34SHeiko Carstens 		sclp_early_print_lm(str, len);
2273f975df6SSascha Silbe 	if (have_vt220)
228d5ab7a34SHeiko Carstens 		sclp_early_print_vt220(str, len);
229d5ab7a34SHeiko Carstens 	sclp_early_setup(1, &have_linemode, &have_vt220);
23022362a0eSMartin Schwidefsky }
23189175cf7SHeiko Carstens 
232d5ab7a34SHeiko Carstens void sclp_early_printk(const char *str)
23389175cf7SHeiko Carstens {
234*5596c4c1SVasily Gorbik 	__sclp_early_printk(str, strlen(str));
23589175cf7SHeiko Carstens }
23617aacfbfSVasily Gorbik 
23717aacfbfSVasily Gorbik int __init sclp_early_read_info(void)
23817aacfbfSVasily Gorbik {
23917aacfbfSVasily Gorbik 	int i;
24017aacfbfSVasily Gorbik 	struct read_info_sccb *sccb = &sclp_info_sccb;
24117aacfbfSVasily Gorbik 	sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
24217aacfbfSVasily Gorbik 				  SCLP_CMDW_READ_SCP_INFO};
24317aacfbfSVasily Gorbik 
24417aacfbfSVasily Gorbik 	for (i = 0; i < ARRAY_SIZE(commands); i++) {
24517aacfbfSVasily Gorbik 		memset(sccb, 0, sizeof(*sccb));
24617aacfbfSVasily Gorbik 		sccb->header.length = sizeof(*sccb);
24717aacfbfSVasily Gorbik 		sccb->header.function_code = 0x80;
24817aacfbfSVasily Gorbik 		sccb->header.control_mask[2] = 0x80;
24917aacfbfSVasily Gorbik 		if (sclp_early_cmd(commands[i], sccb))
25017aacfbfSVasily Gorbik 			break;
25117aacfbfSVasily Gorbik 		if (sccb->header.response_code == 0x10) {
25217aacfbfSVasily Gorbik 			sclp_info_sccb_valid = 1;
25317aacfbfSVasily Gorbik 			return 0;
25417aacfbfSVasily Gorbik 		}
25517aacfbfSVasily Gorbik 		if (sccb->header.response_code != 0x1f0)
25617aacfbfSVasily Gorbik 			break;
25717aacfbfSVasily Gorbik 	}
25817aacfbfSVasily Gorbik 	return -EIO;
25917aacfbfSVasily Gorbik }
26017aacfbfSVasily Gorbik 
26117aacfbfSVasily Gorbik int __init sclp_early_get_info(struct read_info_sccb *info)
26217aacfbfSVasily Gorbik {
26317aacfbfSVasily Gorbik 	if (!sclp_info_sccb_valid)
26417aacfbfSVasily Gorbik 		return -EIO;
26517aacfbfSVasily Gorbik 
26617aacfbfSVasily Gorbik 	*info = sclp_info_sccb;
26717aacfbfSVasily Gorbik 	return 0;
26817aacfbfSVasily Gorbik }
2696966d604SVasily Gorbik 
27054c57795SVasily Gorbik int __init sclp_early_get_memsize(unsigned long *mem)
2716966d604SVasily Gorbik {
2726966d604SVasily Gorbik 	unsigned long rnmax;
2736966d604SVasily Gorbik 	unsigned long rnsize;
2746966d604SVasily Gorbik 	struct read_info_sccb *sccb = &sclp_info_sccb;
2756966d604SVasily Gorbik 
2766966d604SVasily Gorbik 	if (!sclp_info_sccb_valid)
2776966d604SVasily Gorbik 		return -EIO;
2786966d604SVasily Gorbik 
2796966d604SVasily Gorbik 	rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
2806966d604SVasily Gorbik 	rnsize = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
2816966d604SVasily Gorbik 	rnsize <<= 20;
2826966d604SVasily Gorbik 	*mem = rnsize * rnmax;
2836966d604SVasily Gorbik 	return 0;
2846966d604SVasily Gorbik }
285fddbaa5cSVasily Gorbik 
286b09decfdSVasily Gorbik int __init sclp_early_get_hsa_size(unsigned long *hsa_size)
287b09decfdSVasily Gorbik {
288b09decfdSVasily Gorbik 	if (!sclp_info_sccb_valid)
289b09decfdSVasily Gorbik 		return -EIO;
290b09decfdSVasily Gorbik 
291b09decfdSVasily Gorbik 	*hsa_size = 0;
292b09decfdSVasily Gorbik 	if (sclp_info_sccb.hsa_size)
293b09decfdSVasily Gorbik 		*hsa_size = (sclp_info_sccb.hsa_size - 1) * PAGE_SIZE;
294b09decfdSVasily Gorbik 	return 0;
295b09decfdSVasily Gorbik }
296b09decfdSVasily Gorbik 
297fddbaa5cSVasily Gorbik #define SCLP_STORAGE_INFO_FACILITY     0x0000400000000000UL
298fddbaa5cSVasily Gorbik 
299fddbaa5cSVasily Gorbik void __weak __init add_mem_detect_block(u64 start, u64 end) {}
300fddbaa5cSVasily Gorbik int __init sclp_early_read_storage_info(void)
301fddbaa5cSVasily Gorbik {
302087c4d74SGerald Schaefer 	struct read_storage_sccb *sccb = (struct read_storage_sccb *)sclp_early_sccb;
303fddbaa5cSVasily Gorbik 	int rc, id, max_id = 0;
304fddbaa5cSVasily Gorbik 	unsigned long rn, rzm;
305fddbaa5cSVasily Gorbik 	sclp_cmdw_t command;
306fddbaa5cSVasily Gorbik 	u16 sn;
307fddbaa5cSVasily Gorbik 
308fddbaa5cSVasily Gorbik 	if (!sclp_info_sccb_valid)
309fddbaa5cSVasily Gorbik 		return -EIO;
310fddbaa5cSVasily Gorbik 
311fddbaa5cSVasily Gorbik 	if (!(sclp_info_sccb.facilities & SCLP_STORAGE_INFO_FACILITY))
312fddbaa5cSVasily Gorbik 		return -EOPNOTSUPP;
313fddbaa5cSVasily Gorbik 
314fddbaa5cSVasily Gorbik 	rzm = sclp_info_sccb.rnsize ?: sclp_info_sccb.rnsize2;
315fddbaa5cSVasily Gorbik 	rzm <<= 20;
316fddbaa5cSVasily Gorbik 
317fddbaa5cSVasily Gorbik 	for (id = 0; id <= max_id; id++) {
318087c4d74SGerald Schaefer 		memset(sclp_early_sccb, 0, EARLY_SCCB_SIZE);
319087c4d74SGerald Schaefer 		sccb->header.length = EARLY_SCCB_SIZE;
320fddbaa5cSVasily Gorbik 		command = SCLP_CMDW_READ_STORAGE_INFO | (id << 8);
321fddbaa5cSVasily Gorbik 		rc = sclp_early_cmd(command, sccb);
322fddbaa5cSVasily Gorbik 		if (rc)
323fddbaa5cSVasily Gorbik 			goto fail;
324fddbaa5cSVasily Gorbik 
325fddbaa5cSVasily Gorbik 		max_id = sccb->max_id;
326fddbaa5cSVasily Gorbik 		switch (sccb->header.response_code) {
327fddbaa5cSVasily Gorbik 		case 0x0010:
328fddbaa5cSVasily Gorbik 			for (sn = 0; sn < sccb->assigned; sn++) {
329fddbaa5cSVasily Gorbik 				if (!sccb->entries[sn])
330fddbaa5cSVasily Gorbik 					continue;
331fddbaa5cSVasily Gorbik 				rn = sccb->entries[sn] >> 16;
332fddbaa5cSVasily Gorbik 				add_mem_detect_block((rn - 1) * rzm, rn * rzm);
333fddbaa5cSVasily Gorbik 			}
334fddbaa5cSVasily Gorbik 			break;
335fddbaa5cSVasily Gorbik 		case 0x0310:
336fddbaa5cSVasily Gorbik 		case 0x0410:
337fddbaa5cSVasily Gorbik 			break;
338fddbaa5cSVasily Gorbik 		default:
339fddbaa5cSVasily Gorbik 			goto fail;
340fddbaa5cSVasily Gorbik 		}
341fddbaa5cSVasily Gorbik 	}
342fddbaa5cSVasily Gorbik 
343fddbaa5cSVasily Gorbik 	return 0;
344fddbaa5cSVasily Gorbik fail:
345fddbaa5cSVasily Gorbik 	mem_detect.count = 0;
346fddbaa5cSVasily Gorbik 	return -EIO;
347fddbaa5cSVasily Gorbik }
348