13db880b6SDavid Hildenbrand /* 23db880b6SDavid Hildenbrand * s390x SCLP driver 33db880b6SDavid Hildenbrand * 43db880b6SDavid Hildenbrand * Copyright (c) 2017 Red Hat Inc 53db880b6SDavid Hildenbrand * 63db880b6SDavid Hildenbrand * Authors: 73db880b6SDavid Hildenbrand * David Hildenbrand <david@redhat.com> 83db880b6SDavid Hildenbrand * 93db880b6SDavid Hildenbrand * This code is free software; you can redistribute it and/or modify it 103db880b6SDavid Hildenbrand * under the terms of the GNU Library General Public License version 2. 113db880b6SDavid Hildenbrand */ 123db880b6SDavid Hildenbrand 133db880b6SDavid Hildenbrand #include <libcflat.h> 143db880b6SDavid Hildenbrand #include <asm/page.h> 153db880b6SDavid Hildenbrand #include <asm/arch_def.h> 163db880b6SDavid Hildenbrand #include <asm/interrupt.h> 173db880b6SDavid Hildenbrand #include "sclp.h" 1892bbd322SDavid Hildenbrand #include <alloc_phys.h> 1992bbd322SDavid Hildenbrand 2092bbd322SDavid Hildenbrand extern unsigned long stacktop; 213db880b6SDavid Hildenbrand 223db880b6SDavid Hildenbrand static uint64_t storage_increment_size; 233db880b6SDavid Hildenbrand static uint64_t max_ram_size; 243db880b6SDavid Hildenbrand static uint64_t ram_size; 253db880b6SDavid Hildenbrand 26*35bfd1c4SJanosch Frank char _sccb[PAGE_SIZE] __attribute__((__aligned__(4096))); 27*35bfd1c4SJanosch Frank 2892bbd322SDavid Hildenbrand static void mem_init(phys_addr_t mem_end) 2992bbd322SDavid Hildenbrand { 3092bbd322SDavid Hildenbrand phys_addr_t freemem_start = (phys_addr_t)&stacktop; 3192bbd322SDavid Hildenbrand 3292bbd322SDavid Hildenbrand phys_alloc_init(freemem_start, mem_end - freemem_start); 3392bbd322SDavid Hildenbrand } 3492bbd322SDavid Hildenbrand 35a45cb177SDavid Hildenbrand static void sclp_read_scp_info(ReadInfo *ri, int length) 36a45cb177SDavid Hildenbrand { 37a45cb177SDavid Hildenbrand unsigned int commands[] = { SCLP_CMDW_READ_SCP_INFO_FORCED, 38a45cb177SDavid Hildenbrand SCLP_CMDW_READ_SCP_INFO }; 39a45cb177SDavid Hildenbrand int i; 40a45cb177SDavid Hildenbrand 41a45cb177SDavid Hildenbrand for (i = 0; i < ARRAY_SIZE(commands); i++) { 42a45cb177SDavid Hildenbrand memset(&ri->h, 0, sizeof(ri->h)); 43a45cb177SDavid Hildenbrand ri->h.length = length; 44a45cb177SDavid Hildenbrand 45a45cb177SDavid Hildenbrand if (sclp_service_call(commands[i], ri)) 46a45cb177SDavid Hildenbrand break; 47a45cb177SDavid Hildenbrand if (ri->h.response_code == SCLP_RC_NORMAL_READ_COMPLETION) 48a45cb177SDavid Hildenbrand return; 49a45cb177SDavid Hildenbrand if (ri->h.response_code != SCLP_RC_INVALID_SCLP_COMMAND) 50a45cb177SDavid Hildenbrand break; 51a45cb177SDavid Hildenbrand } 52a45cb177SDavid Hildenbrand report_abort("READ_SCP_INFO failed"); 53a45cb177SDavid Hildenbrand } 54a45cb177SDavid Hildenbrand 55*35bfd1c4SJanosch Frank /* Perform service call. Return 0 on success, non-zero otherwise. */ 56*35bfd1c4SJanosch Frank int sclp_service_call(unsigned int command, void *sccb) 57*35bfd1c4SJanosch Frank { 58*35bfd1c4SJanosch Frank int cc; 59*35bfd1c4SJanosch Frank 60*35bfd1c4SJanosch Frank asm volatile( 61*35bfd1c4SJanosch Frank " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ 62*35bfd1c4SJanosch Frank " ipm %0\n" 63*35bfd1c4SJanosch Frank " srl %0,28" 64*35bfd1c4SJanosch Frank : "=&d" (cc) : "d" (command), "a" (__pa(sccb)) 65*35bfd1c4SJanosch Frank : "cc", "memory"); 66*35bfd1c4SJanosch Frank if (cc == 3) 67*35bfd1c4SJanosch Frank return -1; 68*35bfd1c4SJanosch Frank if (cc == 2) 69*35bfd1c4SJanosch Frank return -1; 70*35bfd1c4SJanosch Frank return 0; 71*35bfd1c4SJanosch Frank } 72*35bfd1c4SJanosch Frank 733db880b6SDavid Hildenbrand void sclp_memory_setup(void) 743db880b6SDavid Hildenbrand { 753db880b6SDavid Hildenbrand ReadInfo *ri = (void *)_sccb; 763db880b6SDavid Hildenbrand uint64_t rnmax, rnsize; 773db880b6SDavid Hildenbrand int cc; 783db880b6SDavid Hildenbrand 79a45cb177SDavid Hildenbrand sclp_read_scp_info(ri, SCCB_SIZE); 803db880b6SDavid Hildenbrand 813db880b6SDavid Hildenbrand /* calculate the storage increment size */ 823db880b6SDavid Hildenbrand rnsize = ri->rnsize; 833db880b6SDavid Hildenbrand if (!rnsize) { 843db880b6SDavid Hildenbrand rnsize = ri->rnsize2; 853db880b6SDavid Hildenbrand } 863db880b6SDavid Hildenbrand storage_increment_size = rnsize << 20; 873db880b6SDavid Hildenbrand 883db880b6SDavid Hildenbrand /* calculate the maximum memory size */ 893db880b6SDavid Hildenbrand rnmax = ri->rnmax; 903db880b6SDavid Hildenbrand if (!rnmax) { 913db880b6SDavid Hildenbrand rnmax = ri->rnmax2; 923db880b6SDavid Hildenbrand } 933db880b6SDavid Hildenbrand max_ram_size = rnmax * storage_increment_size; 943db880b6SDavid Hildenbrand 953db880b6SDavid Hildenbrand /* lowcore is always accessible, so the first increment is accessible */ 963db880b6SDavid Hildenbrand ram_size = storage_increment_size; 973db880b6SDavid Hildenbrand 983db880b6SDavid Hildenbrand /* probe for r/w memory up to max memory size */ 993db880b6SDavid Hildenbrand while (ram_size < max_ram_size) { 1003db880b6SDavid Hildenbrand expect_pgm_int(); 1013db880b6SDavid Hildenbrand cc = tprot(ram_size + storage_increment_size - 1); 1023db880b6SDavid Hildenbrand /* stop once we receive an exception or have protected memory */ 1033db880b6SDavid Hildenbrand if (clear_pgm_int() || cc != 0) 1043db880b6SDavid Hildenbrand break; 1053db880b6SDavid Hildenbrand ram_size += storage_increment_size; 1063db880b6SDavid Hildenbrand } 10792bbd322SDavid Hildenbrand 10892bbd322SDavid Hildenbrand mem_init(ram_size); 1093db880b6SDavid Hildenbrand } 110