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