16c9f99dfSJanosch Frank /* SPDX-License-Identifier: GPL-2.0-only */ 23db880b6SDavid Hildenbrand /* 33db880b6SDavid Hildenbrand * s390x SCLP driver 43db880b6SDavid Hildenbrand * 53db880b6SDavid Hildenbrand * Copyright (c) 2017 Red Hat Inc 63db880b6SDavid Hildenbrand * 73db880b6SDavid Hildenbrand * Authors: 83db880b6SDavid Hildenbrand * David Hildenbrand <david@redhat.com> 93db880b6SDavid Hildenbrand */ 103db880b6SDavid Hildenbrand 113db880b6SDavid Hildenbrand #include <libcflat.h> 123db880b6SDavid Hildenbrand #include <asm/page.h> 133db880b6SDavid Hildenbrand #include <asm/arch_def.h> 143db880b6SDavid Hildenbrand #include <asm/interrupt.h> 158ead801eSJanosch Frank #include <asm/barrier.h> 168ead801eSJanosch Frank #include <asm/spinlock.h> 173db880b6SDavid Hildenbrand #include "sclp.h" 1892bbd322SDavid Hildenbrand #include <alloc_phys.h> 19b356c913SJanosch 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; 2652076a63SJanosch Frank char _read_info[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); 2752076a63SJanosch Frank static ReadInfo *read_info; 28*6dff7c9aSJanosch Frank struct sclp_facilities sclp_facilities; 293db880b6SDavid Hildenbrand 3035bfd1c4SJanosch Frank char _sccb[PAGE_SIZE] __attribute__((__aligned__(4096))); 318ead801eSJanosch Frank static volatile bool sclp_busy; 328ead801eSJanosch Frank static struct spinlock sclp_lock; 3335bfd1c4SJanosch Frank 3492bbd322SDavid Hildenbrand static void mem_init(phys_addr_t mem_end) 3592bbd322SDavid Hildenbrand { 3692bbd322SDavid Hildenbrand phys_addr_t freemem_start = (phys_addr_t)&stacktop; 37b356c913SJanosch Frank phys_addr_t base, top; 3892bbd322SDavid Hildenbrand 3992bbd322SDavid Hildenbrand phys_alloc_init(freemem_start, mem_end - freemem_start); 40b356c913SJanosch Frank phys_alloc_get_unused(&base, &top); 418131e91aSClaudio Imbrenda base = PAGE_ALIGN(base) >> PAGE_SHIFT; 428131e91aSClaudio Imbrenda top = top >> PAGE_SHIFT; 43b356c913SJanosch Frank 44b356c913SJanosch Frank /* Make the pages available to the physical allocator */ 458131e91aSClaudio Imbrenda page_alloc_init_area(AREA_ANY_NUMBER, base, top); 46b356c913SJanosch Frank page_alloc_ops_enable(); 4792bbd322SDavid Hildenbrand } 4892bbd322SDavid Hildenbrand 49fd6fc0d9SClaudio Imbrenda void sclp_setup_int(void) 508ead801eSJanosch Frank { 518ead801eSJanosch Frank uint64_t mask; 528ead801eSJanosch Frank 538ead801eSJanosch Frank ctl_set_bit(0, 9); 548ead801eSJanosch Frank 558ead801eSJanosch Frank mask = extract_psw_mask(); 568ead801eSJanosch Frank mask |= PSW_MASK_EXT; 578ead801eSJanosch Frank load_psw_mask(mask); 588ead801eSJanosch Frank } 598ead801eSJanosch Frank 608ead801eSJanosch Frank void sclp_handle_ext(void) 618ead801eSJanosch Frank { 628ead801eSJanosch Frank ctl_clear_bit(0, 9); 638ead801eSJanosch Frank spin_lock(&sclp_lock); 648ead801eSJanosch Frank sclp_busy = false; 658ead801eSJanosch Frank spin_unlock(&sclp_lock); 668ead801eSJanosch Frank } 678ead801eSJanosch Frank 688ead801eSJanosch Frank void sclp_wait_busy(void) 698ead801eSJanosch Frank { 708ead801eSJanosch Frank while (sclp_busy) 718ead801eSJanosch Frank mb(); 728ead801eSJanosch Frank } 738ead801eSJanosch Frank 748ead801eSJanosch Frank void sclp_mark_busy(void) 758ead801eSJanosch Frank { 768ead801eSJanosch Frank /* 778ead801eSJanosch Frank * With multiple CPUs we might need to wait for another CPU's 788ead801eSJanosch Frank * request before grabbing the busy indication. 798ead801eSJanosch Frank */ 808ead801eSJanosch Frank while (true) { 818ead801eSJanosch Frank sclp_wait_busy(); 828ead801eSJanosch Frank spin_lock(&sclp_lock); 838ead801eSJanosch Frank if (!sclp_busy) { 848ead801eSJanosch Frank sclp_busy = true; 858ead801eSJanosch Frank spin_unlock(&sclp_lock); 868ead801eSJanosch Frank return; 878ead801eSJanosch Frank } 888ead801eSJanosch Frank spin_unlock(&sclp_lock); 898ead801eSJanosch Frank } 908ead801eSJanosch Frank } 918ead801eSJanosch Frank 92a45cb177SDavid Hildenbrand static void sclp_read_scp_info(ReadInfo *ri, int length) 93a45cb177SDavid Hildenbrand { 94a45cb177SDavid Hildenbrand unsigned int commands[] = { SCLP_CMDW_READ_SCP_INFO_FORCED, 95a45cb177SDavid Hildenbrand SCLP_CMDW_READ_SCP_INFO }; 968ead801eSJanosch Frank int i, cc; 97a45cb177SDavid Hildenbrand 98a45cb177SDavid Hildenbrand for (i = 0; i < ARRAY_SIZE(commands); i++) { 998ead801eSJanosch Frank sclp_mark_busy(); 100a45cb177SDavid Hildenbrand memset(&ri->h, 0, sizeof(ri->h)); 101a45cb177SDavid Hildenbrand ri->h.length = length; 102a45cb177SDavid Hildenbrand 1038ead801eSJanosch Frank cc = sclp_service_call(commands[i], ri); 1048ead801eSJanosch Frank if (cc) 105a45cb177SDavid Hildenbrand break; 106a45cb177SDavid Hildenbrand if (ri->h.response_code == SCLP_RC_NORMAL_READ_COMPLETION) 107a45cb177SDavid Hildenbrand return; 108a45cb177SDavid Hildenbrand if (ri->h.response_code != SCLP_RC_INVALID_SCLP_COMMAND) 109a45cb177SDavid Hildenbrand break; 110a45cb177SDavid Hildenbrand } 111a45cb177SDavid Hildenbrand report_abort("READ_SCP_INFO failed"); 112a45cb177SDavid Hildenbrand } 113a45cb177SDavid Hildenbrand 11452076a63SJanosch Frank void sclp_read_info(void) 11552076a63SJanosch Frank { 11652076a63SJanosch Frank sclp_read_scp_info((void *)_read_info, SCCB_SIZE); 11752076a63SJanosch Frank read_info = (ReadInfo *)_read_info; 11852076a63SJanosch Frank } 11952076a63SJanosch Frank 12052076a63SJanosch Frank int sclp_get_cpu_num(void) 12152076a63SJanosch Frank { 12252076a63SJanosch Frank assert(read_info); 12352076a63SJanosch Frank return read_info->entries_cpu; 12452076a63SJanosch Frank } 12552076a63SJanosch Frank 12652076a63SJanosch Frank CPUEntry *sclp_get_cpu_entries(void) 12752076a63SJanosch Frank { 12852076a63SJanosch Frank assert(read_info); 12952076a63SJanosch Frank return (CPUEntry *)(_read_info + read_info->offset_cpu); 13052076a63SJanosch Frank } 13152076a63SJanosch Frank 132*6dff7c9aSJanosch Frank void sclp_facilities_setup(void) 133*6dff7c9aSJanosch Frank { 134*6dff7c9aSJanosch Frank unsigned short cpu0_addr = stap(); 135*6dff7c9aSJanosch Frank CPUEntry *cpu; 136*6dff7c9aSJanosch Frank int i; 137*6dff7c9aSJanosch Frank 138*6dff7c9aSJanosch Frank assert(read_info); 139*6dff7c9aSJanosch Frank 140*6dff7c9aSJanosch Frank cpu = sclp_get_cpu_entries(); 141*6dff7c9aSJanosch Frank for (i = 0; i < read_info->entries_cpu; i++, cpu++) { 142*6dff7c9aSJanosch Frank /* 143*6dff7c9aSJanosch Frank * The logic for only reading the facilities from the 144*6dff7c9aSJanosch Frank * boot cpu comes from the kernel. I haven't yet found 145*6dff7c9aSJanosch Frank * documentation that explains why this is necessary 146*6dff7c9aSJanosch Frank * but I figure there's a reason behind doing it this 147*6dff7c9aSJanosch Frank * way. 148*6dff7c9aSJanosch Frank */ 149*6dff7c9aSJanosch Frank if (cpu->address == cpu0_addr) { 150*6dff7c9aSJanosch Frank sclp_facilities.has_sief2 = cpu->feat_sief2; 151*6dff7c9aSJanosch Frank break; 152*6dff7c9aSJanosch Frank } 153*6dff7c9aSJanosch Frank } 154*6dff7c9aSJanosch Frank } 155*6dff7c9aSJanosch Frank 15635bfd1c4SJanosch Frank /* Perform service call. Return 0 on success, non-zero otherwise. */ 15735bfd1c4SJanosch Frank int sclp_service_call(unsigned int command, void *sccb) 15835bfd1c4SJanosch Frank { 15935bfd1c4SJanosch Frank int cc; 16035bfd1c4SJanosch Frank 1618ead801eSJanosch Frank sclp_setup_int(); 162f9395bfeSClaudio Imbrenda cc = servc(command, __pa(sccb)); 1638ead801eSJanosch Frank sclp_wait_busy(); 16435bfd1c4SJanosch Frank if (cc == 3) 16535bfd1c4SJanosch Frank return -1; 16635bfd1c4SJanosch Frank if (cc == 2) 16735bfd1c4SJanosch Frank return -1; 16835bfd1c4SJanosch Frank return 0; 16935bfd1c4SJanosch Frank } 17035bfd1c4SJanosch Frank 1713db880b6SDavid Hildenbrand void sclp_memory_setup(void) 1723db880b6SDavid Hildenbrand { 1733db880b6SDavid Hildenbrand uint64_t rnmax, rnsize; 1743db880b6SDavid Hildenbrand int cc; 1753db880b6SDavid Hildenbrand 17652076a63SJanosch Frank assert(read_info); 1773db880b6SDavid Hildenbrand 1783db880b6SDavid Hildenbrand /* calculate the storage increment size */ 17952076a63SJanosch Frank rnsize = read_info->rnsize; 1803db880b6SDavid Hildenbrand if (!rnsize) { 18152076a63SJanosch Frank rnsize = read_info->rnsize2; 1823db880b6SDavid Hildenbrand } 1833db880b6SDavid Hildenbrand storage_increment_size = rnsize << 20; 1843db880b6SDavid Hildenbrand 1853db880b6SDavid Hildenbrand /* calculate the maximum memory size */ 18652076a63SJanosch Frank rnmax = read_info->rnmax; 1873db880b6SDavid Hildenbrand if (!rnmax) { 18852076a63SJanosch Frank rnmax = read_info->rnmax2; 1893db880b6SDavid Hildenbrand } 1903db880b6SDavid Hildenbrand max_ram_size = rnmax * storage_increment_size; 1913db880b6SDavid Hildenbrand 1923db880b6SDavid Hildenbrand /* lowcore is always accessible, so the first increment is accessible */ 1933db880b6SDavid Hildenbrand ram_size = storage_increment_size; 1943db880b6SDavid Hildenbrand 1953db880b6SDavid Hildenbrand /* probe for r/w memory up to max memory size */ 1963db880b6SDavid Hildenbrand while (ram_size < max_ram_size) { 1973db880b6SDavid Hildenbrand expect_pgm_int(); 1983db880b6SDavid Hildenbrand cc = tprot(ram_size + storage_increment_size - 1); 1993db880b6SDavid Hildenbrand /* stop once we receive an exception or have protected memory */ 2003db880b6SDavid Hildenbrand if (clear_pgm_int() || cc != 0) 2013db880b6SDavid Hildenbrand break; 2023db880b6SDavid Hildenbrand ram_size += storage_increment_size; 2033db880b6SDavid Hildenbrand } 20492bbd322SDavid Hildenbrand 20592bbd322SDavid Hildenbrand mem_init(ram_size); 2063db880b6SDavid Hildenbrand } 20784a79f17SClaudio Imbrenda 20884a79f17SClaudio Imbrenda uint64_t get_ram_size(void) 20984a79f17SClaudio Imbrenda { 21084a79f17SClaudio Imbrenda return ram_size; 21184a79f17SClaudio Imbrenda } 21284a79f17SClaudio Imbrenda 21384a79f17SClaudio Imbrenda uint64_t get_max_ram_size(void) 21484a79f17SClaudio Imbrenda { 21584a79f17SClaudio Imbrenda return max_ram_size; 21684a79f17SClaudio Imbrenda } 217