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> 19*b356c913SJanosch Frank #include <alloc_page.h> 2092bbd322SDavid Hildenbrand 2192bbd322SDavid Hildenbrand extern unsigned long stacktop; 223db880b6SDavid Hildenbrand 233db880b6SDavid Hildenbrand static uint64_t storage_increment_size; 243db880b6SDavid Hildenbrand static uint64_t max_ram_size; 253db880b6SDavid Hildenbrand static uint64_t ram_size; 263db880b6SDavid Hildenbrand 2735bfd1c4SJanosch Frank char _sccb[PAGE_SIZE] __attribute__((__aligned__(4096))); 2835bfd1c4SJanosch Frank 2992bbd322SDavid Hildenbrand static void mem_init(phys_addr_t mem_end) 3092bbd322SDavid Hildenbrand { 3192bbd322SDavid Hildenbrand phys_addr_t freemem_start = (phys_addr_t)&stacktop; 32*b356c913SJanosch Frank phys_addr_t base, top; 3392bbd322SDavid Hildenbrand 3492bbd322SDavid Hildenbrand phys_alloc_init(freemem_start, mem_end - freemem_start); 35*b356c913SJanosch Frank phys_alloc_get_unused(&base, &top); 36*b356c913SJanosch Frank base = (base + PAGE_SIZE - 1) & -PAGE_SIZE; 37*b356c913SJanosch Frank top = top & -PAGE_SIZE; 38*b356c913SJanosch Frank 39*b356c913SJanosch Frank /* Make the pages available to the physical allocator */ 40*b356c913SJanosch Frank free_pages((void *)(unsigned long)base, top - base); 41*b356c913SJanosch Frank page_alloc_ops_enable(); 4292bbd322SDavid Hildenbrand } 4392bbd322SDavid Hildenbrand 44a45cb177SDavid Hildenbrand static void sclp_read_scp_info(ReadInfo *ri, int length) 45a45cb177SDavid Hildenbrand { 46a45cb177SDavid Hildenbrand unsigned int commands[] = { SCLP_CMDW_READ_SCP_INFO_FORCED, 47a45cb177SDavid Hildenbrand SCLP_CMDW_READ_SCP_INFO }; 48a45cb177SDavid Hildenbrand int i; 49a45cb177SDavid Hildenbrand 50a45cb177SDavid Hildenbrand for (i = 0; i < ARRAY_SIZE(commands); i++) { 51a45cb177SDavid Hildenbrand memset(&ri->h, 0, sizeof(ri->h)); 52a45cb177SDavid Hildenbrand ri->h.length = length; 53a45cb177SDavid Hildenbrand 54a45cb177SDavid Hildenbrand if (sclp_service_call(commands[i], ri)) 55a45cb177SDavid Hildenbrand break; 56a45cb177SDavid Hildenbrand if (ri->h.response_code == SCLP_RC_NORMAL_READ_COMPLETION) 57a45cb177SDavid Hildenbrand return; 58a45cb177SDavid Hildenbrand if (ri->h.response_code != SCLP_RC_INVALID_SCLP_COMMAND) 59a45cb177SDavid Hildenbrand break; 60a45cb177SDavid Hildenbrand } 61a45cb177SDavid Hildenbrand report_abort("READ_SCP_INFO failed"); 62a45cb177SDavid Hildenbrand } 63a45cb177SDavid Hildenbrand 6435bfd1c4SJanosch Frank /* Perform service call. Return 0 on success, non-zero otherwise. */ 6535bfd1c4SJanosch Frank int sclp_service_call(unsigned int command, void *sccb) 6635bfd1c4SJanosch Frank { 6735bfd1c4SJanosch Frank int cc; 6835bfd1c4SJanosch Frank 6935bfd1c4SJanosch Frank asm volatile( 7035bfd1c4SJanosch Frank " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ 7135bfd1c4SJanosch Frank " ipm %0\n" 7235bfd1c4SJanosch Frank " srl %0,28" 7335bfd1c4SJanosch Frank : "=&d" (cc) : "d" (command), "a" (__pa(sccb)) 7435bfd1c4SJanosch Frank : "cc", "memory"); 7535bfd1c4SJanosch Frank if (cc == 3) 7635bfd1c4SJanosch Frank return -1; 7735bfd1c4SJanosch Frank if (cc == 2) 7835bfd1c4SJanosch Frank return -1; 7935bfd1c4SJanosch Frank return 0; 8035bfd1c4SJanosch Frank } 8135bfd1c4SJanosch Frank 823db880b6SDavid Hildenbrand void sclp_memory_setup(void) 833db880b6SDavid Hildenbrand { 843db880b6SDavid Hildenbrand ReadInfo *ri = (void *)_sccb; 853db880b6SDavid Hildenbrand uint64_t rnmax, rnsize; 863db880b6SDavid Hildenbrand int cc; 873db880b6SDavid Hildenbrand 88a45cb177SDavid Hildenbrand sclp_read_scp_info(ri, SCCB_SIZE); 893db880b6SDavid Hildenbrand 903db880b6SDavid Hildenbrand /* calculate the storage increment size */ 913db880b6SDavid Hildenbrand rnsize = ri->rnsize; 923db880b6SDavid Hildenbrand if (!rnsize) { 933db880b6SDavid Hildenbrand rnsize = ri->rnsize2; 943db880b6SDavid Hildenbrand } 953db880b6SDavid Hildenbrand storage_increment_size = rnsize << 20; 963db880b6SDavid Hildenbrand 973db880b6SDavid Hildenbrand /* calculate the maximum memory size */ 983db880b6SDavid Hildenbrand rnmax = ri->rnmax; 993db880b6SDavid Hildenbrand if (!rnmax) { 1003db880b6SDavid Hildenbrand rnmax = ri->rnmax2; 1013db880b6SDavid Hildenbrand } 1023db880b6SDavid Hildenbrand max_ram_size = rnmax * storage_increment_size; 1033db880b6SDavid Hildenbrand 1043db880b6SDavid Hildenbrand /* lowcore is always accessible, so the first increment is accessible */ 1053db880b6SDavid Hildenbrand ram_size = storage_increment_size; 1063db880b6SDavid Hildenbrand 1073db880b6SDavid Hildenbrand /* probe for r/w memory up to max memory size */ 1083db880b6SDavid Hildenbrand while (ram_size < max_ram_size) { 1093db880b6SDavid Hildenbrand expect_pgm_int(); 1103db880b6SDavid Hildenbrand cc = tprot(ram_size + storage_increment_size - 1); 1113db880b6SDavid Hildenbrand /* stop once we receive an exception or have protected memory */ 1123db880b6SDavid Hildenbrand if (clear_pgm_int() || cc != 0) 1133db880b6SDavid Hildenbrand break; 1143db880b6SDavid Hildenbrand ram_size += storage_increment_size; 1153db880b6SDavid Hildenbrand } 11692bbd322SDavid Hildenbrand 11792bbd322SDavid Hildenbrand mem_init(ram_size); 1183db880b6SDavid Hildenbrand } 119