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> 178ead801eSJanosch Frank #include <asm/barrier.h> 188ead801eSJanosch Frank #include <asm/spinlock.h> 193db880b6SDavid Hildenbrand #include "sclp.h" 2092bbd322SDavid Hildenbrand #include <alloc_phys.h> 21b356c913SJanosch Frank #include <alloc_page.h> 2292bbd322SDavid Hildenbrand 2392bbd322SDavid Hildenbrand extern unsigned long stacktop; 243db880b6SDavid Hildenbrand 253db880b6SDavid Hildenbrand static uint64_t storage_increment_size; 263db880b6SDavid Hildenbrand static uint64_t max_ram_size; 273db880b6SDavid Hildenbrand static uint64_t ram_size; 283db880b6SDavid Hildenbrand 2935bfd1c4SJanosch Frank char _sccb[PAGE_SIZE] __attribute__((__aligned__(4096))); 308ead801eSJanosch Frank static volatile bool sclp_busy; 318ead801eSJanosch Frank static struct spinlock sclp_lock; 3235bfd1c4SJanosch Frank 3392bbd322SDavid Hildenbrand static void mem_init(phys_addr_t mem_end) 3492bbd322SDavid Hildenbrand { 3592bbd322SDavid Hildenbrand phys_addr_t freemem_start = (phys_addr_t)&stacktop; 36b356c913SJanosch Frank phys_addr_t base, top; 3792bbd322SDavid Hildenbrand 3892bbd322SDavid Hildenbrand phys_alloc_init(freemem_start, mem_end - freemem_start); 39b356c913SJanosch Frank phys_alloc_get_unused(&base, &top); 40b356c913SJanosch Frank base = (base + PAGE_SIZE - 1) & -PAGE_SIZE; 41b356c913SJanosch Frank top = top & -PAGE_SIZE; 42b356c913SJanosch Frank 43b356c913SJanosch Frank /* Make the pages available to the physical allocator */ 44b356c913SJanosch Frank free_pages((void *)(unsigned long)base, top - base); 45b356c913SJanosch Frank page_alloc_ops_enable(); 4692bbd322SDavid Hildenbrand } 4792bbd322SDavid Hildenbrand 488ead801eSJanosch Frank static void sclp_setup_int(void) 498ead801eSJanosch Frank { 508ead801eSJanosch Frank uint64_t mask; 518ead801eSJanosch Frank 528ead801eSJanosch Frank ctl_set_bit(0, 9); 538ead801eSJanosch Frank 548ead801eSJanosch Frank mask = extract_psw_mask(); 558ead801eSJanosch Frank mask |= PSW_MASK_EXT; 568ead801eSJanosch Frank load_psw_mask(mask); 578ead801eSJanosch Frank } 588ead801eSJanosch Frank 598ead801eSJanosch Frank void sclp_handle_ext(void) 608ead801eSJanosch Frank { 618ead801eSJanosch Frank ctl_clear_bit(0, 9); 628ead801eSJanosch Frank spin_lock(&sclp_lock); 638ead801eSJanosch Frank sclp_busy = false; 648ead801eSJanosch Frank spin_unlock(&sclp_lock); 658ead801eSJanosch Frank } 668ead801eSJanosch Frank 678ead801eSJanosch Frank void sclp_wait_busy(void) 688ead801eSJanosch Frank { 698ead801eSJanosch Frank while (sclp_busy) 708ead801eSJanosch Frank mb(); 718ead801eSJanosch Frank } 728ead801eSJanosch Frank 738ead801eSJanosch Frank void sclp_mark_busy(void) 748ead801eSJanosch Frank { 758ead801eSJanosch Frank /* 768ead801eSJanosch Frank * With multiple CPUs we might need to wait for another CPU's 778ead801eSJanosch Frank * request before grabbing the busy indication. 788ead801eSJanosch Frank */ 798ead801eSJanosch Frank while (true) { 808ead801eSJanosch Frank sclp_wait_busy(); 818ead801eSJanosch Frank spin_lock(&sclp_lock); 828ead801eSJanosch Frank if (!sclp_busy) { 838ead801eSJanosch Frank sclp_busy = true; 848ead801eSJanosch Frank spin_unlock(&sclp_lock); 858ead801eSJanosch Frank return; 868ead801eSJanosch Frank } 878ead801eSJanosch Frank spin_unlock(&sclp_lock); 888ead801eSJanosch Frank } 898ead801eSJanosch Frank } 908ead801eSJanosch Frank 91a45cb177SDavid Hildenbrand static void sclp_read_scp_info(ReadInfo *ri, int length) 92a45cb177SDavid Hildenbrand { 93a45cb177SDavid Hildenbrand unsigned int commands[] = { SCLP_CMDW_READ_SCP_INFO_FORCED, 94a45cb177SDavid Hildenbrand SCLP_CMDW_READ_SCP_INFO }; 958ead801eSJanosch Frank int i, cc; 96a45cb177SDavid Hildenbrand 97a45cb177SDavid Hildenbrand for (i = 0; i < ARRAY_SIZE(commands); i++) { 988ead801eSJanosch Frank sclp_mark_busy(); 99a45cb177SDavid Hildenbrand memset(&ri->h, 0, sizeof(ri->h)); 100a45cb177SDavid Hildenbrand ri->h.length = length; 101a45cb177SDavid Hildenbrand 1028ead801eSJanosch Frank cc = sclp_service_call(commands[i], ri); 1038ead801eSJanosch Frank if (cc) 104a45cb177SDavid Hildenbrand break; 105a45cb177SDavid Hildenbrand if (ri->h.response_code == SCLP_RC_NORMAL_READ_COMPLETION) 106a45cb177SDavid Hildenbrand return; 107a45cb177SDavid Hildenbrand if (ri->h.response_code != SCLP_RC_INVALID_SCLP_COMMAND) 108a45cb177SDavid Hildenbrand break; 109a45cb177SDavid Hildenbrand } 110a45cb177SDavid Hildenbrand report_abort("READ_SCP_INFO failed"); 111a45cb177SDavid Hildenbrand } 112a45cb177SDavid Hildenbrand 11335bfd1c4SJanosch Frank /* Perform service call. Return 0 on success, non-zero otherwise. */ 11435bfd1c4SJanosch Frank int sclp_service_call(unsigned int command, void *sccb) 11535bfd1c4SJanosch Frank { 11635bfd1c4SJanosch Frank int cc; 11735bfd1c4SJanosch Frank 1188ead801eSJanosch Frank sclp_setup_int(); 11935bfd1c4SJanosch Frank asm volatile( 12035bfd1c4SJanosch Frank " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ 12135bfd1c4SJanosch Frank " ipm %0\n" 12235bfd1c4SJanosch Frank " srl %0,28" 12335bfd1c4SJanosch Frank : "=&d" (cc) : "d" (command), "a" (__pa(sccb)) 12435bfd1c4SJanosch Frank : "cc", "memory"); 1258ead801eSJanosch Frank sclp_wait_busy(); 12635bfd1c4SJanosch Frank if (cc == 3) 12735bfd1c4SJanosch Frank return -1; 12835bfd1c4SJanosch Frank if (cc == 2) 12935bfd1c4SJanosch Frank return -1; 13035bfd1c4SJanosch Frank return 0; 13135bfd1c4SJanosch Frank } 13235bfd1c4SJanosch Frank 1333db880b6SDavid Hildenbrand void sclp_memory_setup(void) 1343db880b6SDavid Hildenbrand { 1353db880b6SDavid Hildenbrand ReadInfo *ri = (void *)_sccb; 1363db880b6SDavid Hildenbrand uint64_t rnmax, rnsize; 1373db880b6SDavid Hildenbrand int cc; 1383db880b6SDavid Hildenbrand 139a45cb177SDavid Hildenbrand sclp_read_scp_info(ri, SCCB_SIZE); 1403db880b6SDavid Hildenbrand 1413db880b6SDavid Hildenbrand /* calculate the storage increment size */ 1423db880b6SDavid Hildenbrand rnsize = ri->rnsize; 1433db880b6SDavid Hildenbrand if (!rnsize) { 1443db880b6SDavid Hildenbrand rnsize = ri->rnsize2; 1453db880b6SDavid Hildenbrand } 1463db880b6SDavid Hildenbrand storage_increment_size = rnsize << 20; 1473db880b6SDavid Hildenbrand 1483db880b6SDavid Hildenbrand /* calculate the maximum memory size */ 1493db880b6SDavid Hildenbrand rnmax = ri->rnmax; 1503db880b6SDavid Hildenbrand if (!rnmax) { 1513db880b6SDavid Hildenbrand rnmax = ri->rnmax2; 1523db880b6SDavid Hildenbrand } 1533db880b6SDavid Hildenbrand max_ram_size = rnmax * storage_increment_size; 1543db880b6SDavid Hildenbrand 1553db880b6SDavid Hildenbrand /* lowcore is always accessible, so the first increment is accessible */ 1563db880b6SDavid Hildenbrand ram_size = storage_increment_size; 1573db880b6SDavid Hildenbrand 1583db880b6SDavid Hildenbrand /* probe for r/w memory up to max memory size */ 1593db880b6SDavid Hildenbrand while (ram_size < max_ram_size) { 1603db880b6SDavid Hildenbrand expect_pgm_int(); 1613db880b6SDavid Hildenbrand cc = tprot(ram_size + storage_increment_size - 1); 1623db880b6SDavid Hildenbrand /* stop once we receive an exception or have protected memory */ 1633db880b6SDavid Hildenbrand if (clear_pgm_int() || cc != 0) 1643db880b6SDavid Hildenbrand break; 1653db880b6SDavid Hildenbrand ram_size += storage_increment_size; 1663db880b6SDavid Hildenbrand } 16792bbd322SDavid Hildenbrand 16892bbd322SDavid Hildenbrand mem_init(ram_size); 1693db880b6SDavid Hildenbrand } 170*84a79f17SClaudio Imbrenda 171*84a79f17SClaudio Imbrenda uint64_t get_ram_size(void) 172*84a79f17SClaudio Imbrenda { 173*84a79f17SClaudio Imbrenda return ram_size; 174*84a79f17SClaudio Imbrenda } 175*84a79f17SClaudio Imbrenda 176*84a79f17SClaudio Imbrenda uint64_t get_max_ram_size(void) 177*84a79f17SClaudio Imbrenda { 178*84a79f17SClaudio Imbrenda return max_ram_size; 179*84a79f17SClaudio Imbrenda } 180