18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2007,2012
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
68c2ecf20Sopenharmony_ci *	      Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "sclp_cmd"
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/completion.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/errno.h>
158c2ecf20Sopenharmony_ci#include <linux/err.h>
168c2ecf20Sopenharmony_ci#include <linux/export.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/string.h>
198c2ecf20Sopenharmony_ci#include <linux/mm.h>
208c2ecf20Sopenharmony_ci#include <linux/mmzone.h>
218c2ecf20Sopenharmony_ci#include <linux/memory.h>
228c2ecf20Sopenharmony_ci#include <linux/module.h>
238c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
248c2ecf20Sopenharmony_ci#include <asm/ctl_reg.h>
258c2ecf20Sopenharmony_ci#include <asm/chpid.h>
268c2ecf20Sopenharmony_ci#include <asm/setup.h>
278c2ecf20Sopenharmony_ci#include <asm/page.h>
288c2ecf20Sopenharmony_ci#include <asm/sclp.h>
298c2ecf20Sopenharmony_ci#include <asm/numa.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include "sclp.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic void sclp_sync_callback(struct sclp_req *req, void *data)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct completion *completion = data;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	complete(completion);
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ciint sclp_sync_request(sclp_cmdw_t cmd, void *sccb)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	return sclp_sync_request_timeout(cmd, sccb, 0);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciint sclp_sync_request_timeout(sclp_cmdw_t cmd, void *sccb, int timeout)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct completion completion;
488c2ecf20Sopenharmony_ci	struct sclp_req *request;
498c2ecf20Sopenharmony_ci	int rc;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	request = kzalloc(sizeof(*request), GFP_KERNEL);
528c2ecf20Sopenharmony_ci	if (!request)
538c2ecf20Sopenharmony_ci		return -ENOMEM;
548c2ecf20Sopenharmony_ci	if (timeout)
558c2ecf20Sopenharmony_ci		request->queue_timeout = timeout;
568c2ecf20Sopenharmony_ci	request->command = cmd;
578c2ecf20Sopenharmony_ci	request->sccb = sccb;
588c2ecf20Sopenharmony_ci	request->status = SCLP_REQ_FILLED;
598c2ecf20Sopenharmony_ci	request->callback = sclp_sync_callback;
608c2ecf20Sopenharmony_ci	request->callback_data = &completion;
618c2ecf20Sopenharmony_ci	init_completion(&completion);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	/* Perform sclp request. */
648c2ecf20Sopenharmony_ci	rc = sclp_add_request(request);
658c2ecf20Sopenharmony_ci	if (rc)
668c2ecf20Sopenharmony_ci		goto out;
678c2ecf20Sopenharmony_ci	wait_for_completion(&completion);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* Check response. */
708c2ecf20Sopenharmony_ci	if (request->status != SCLP_REQ_DONE) {
718c2ecf20Sopenharmony_ci		pr_warn("sync request failed (cmd=0x%08x, status=0x%02x)\n",
728c2ecf20Sopenharmony_ci			cmd, request->status);
738c2ecf20Sopenharmony_ci		rc = -EIO;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ciout:
768c2ecf20Sopenharmony_ci	kfree(request);
778c2ecf20Sopenharmony_ci	return rc;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * CPU configuration related functions.
828c2ecf20Sopenharmony_ci */
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#define SCLP_CMDW_CONFIGURE_CPU		0x00110001
858c2ecf20Sopenharmony_ci#define SCLP_CMDW_DECONFIGURE_CPU	0x00100001
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ciint _sclp_get_core_info(struct sclp_core_info *info)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	int rc;
908c2ecf20Sopenharmony_ci	struct read_cpu_info_sccb *sccb;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (!SCLP_HAS_CPU_INFO)
938c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
948c2ecf20Sopenharmony_ci	sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
958c2ecf20Sopenharmony_ci	if (!sccb)
968c2ecf20Sopenharmony_ci		return -ENOMEM;
978c2ecf20Sopenharmony_ci	sccb->header.length = sizeof(*sccb);
988c2ecf20Sopenharmony_ci	rc = sclp_sync_request_timeout(SCLP_CMDW_READ_CPU_INFO, sccb,
998c2ecf20Sopenharmony_ci				       SCLP_QUEUE_INTERVAL);
1008c2ecf20Sopenharmony_ci	if (rc)
1018c2ecf20Sopenharmony_ci		goto out;
1028c2ecf20Sopenharmony_ci	if (sccb->header.response_code != 0x0010) {
1038c2ecf20Sopenharmony_ci		pr_warn("readcpuinfo failed (response=0x%04x)\n",
1048c2ecf20Sopenharmony_ci			sccb->header.response_code);
1058c2ecf20Sopenharmony_ci		rc = -EIO;
1068c2ecf20Sopenharmony_ci		goto out;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci	sclp_fill_core_info(info, sccb);
1098c2ecf20Sopenharmony_ciout:
1108c2ecf20Sopenharmony_ci	free_page((unsigned long) sccb);
1118c2ecf20Sopenharmony_ci	return rc;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistruct cpu_configure_sccb {
1158c2ecf20Sopenharmony_ci	struct sccb_header header;
1168c2ecf20Sopenharmony_ci} __attribute__((packed, aligned(8)));
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int do_core_configure(sclp_cmdw_t cmd)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct cpu_configure_sccb *sccb;
1218c2ecf20Sopenharmony_ci	int rc;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (!SCLP_HAS_CPU_RECONFIG)
1248c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1258c2ecf20Sopenharmony_ci	/*
1268c2ecf20Sopenharmony_ci	 * This is not going to cross a page boundary since we force
1278c2ecf20Sopenharmony_ci	 * kmalloc to have a minimum alignment of 8 bytes on s390.
1288c2ecf20Sopenharmony_ci	 */
1298c2ecf20Sopenharmony_ci	sccb = kzalloc(sizeof(*sccb), GFP_KERNEL | GFP_DMA);
1308c2ecf20Sopenharmony_ci	if (!sccb)
1318c2ecf20Sopenharmony_ci		return -ENOMEM;
1328c2ecf20Sopenharmony_ci	sccb->header.length = sizeof(*sccb);
1338c2ecf20Sopenharmony_ci	rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL);
1348c2ecf20Sopenharmony_ci	if (rc)
1358c2ecf20Sopenharmony_ci		goto out;
1368c2ecf20Sopenharmony_ci	switch (sccb->header.response_code) {
1378c2ecf20Sopenharmony_ci	case 0x0020:
1388c2ecf20Sopenharmony_ci	case 0x0120:
1398c2ecf20Sopenharmony_ci		break;
1408c2ecf20Sopenharmony_ci	default:
1418c2ecf20Sopenharmony_ci		pr_warn("configure cpu failed (cmd=0x%08x, response=0x%04x)\n",
1428c2ecf20Sopenharmony_ci			cmd, sccb->header.response_code);
1438c2ecf20Sopenharmony_ci		rc = -EIO;
1448c2ecf20Sopenharmony_ci		break;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ciout:
1478c2ecf20Sopenharmony_ci	kfree(sccb);
1488c2ecf20Sopenharmony_ci	return rc;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ciint sclp_core_configure(u8 core)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	return do_core_configure(SCLP_CMDW_CONFIGURE_CPU | core << 8);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ciint sclp_core_deconfigure(u8 core)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	return do_core_configure(SCLP_CMDW_DECONFIGURE_CPU | core << 8);
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sclp_mem_mutex);
1648c2ecf20Sopenharmony_cistatic LIST_HEAD(sclp_mem_list);
1658c2ecf20Sopenharmony_cistatic u8 sclp_max_storage_id;
1668c2ecf20Sopenharmony_cistatic DECLARE_BITMAP(sclp_storage_ids, 256);
1678c2ecf20Sopenharmony_cistatic int sclp_mem_state_changed;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistruct memory_increment {
1708c2ecf20Sopenharmony_ci	struct list_head list;
1718c2ecf20Sopenharmony_ci	u16 rn;
1728c2ecf20Sopenharmony_ci	int standby;
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistruct assign_storage_sccb {
1768c2ecf20Sopenharmony_ci	struct sccb_header header;
1778c2ecf20Sopenharmony_ci	u16 rn;
1788c2ecf20Sopenharmony_ci} __packed;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ciint arch_get_memory_phys_device(unsigned long start_pfn)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	if (!sclp.rzm)
1838c2ecf20Sopenharmony_ci		return 0;
1848c2ecf20Sopenharmony_ci	return PFN_PHYS(start_pfn) >> ilog2(sclp.rzm);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic unsigned long long rn2addr(u16 rn)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	return (unsigned long long) (rn - 1) * sclp.rzm;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct assign_storage_sccb *sccb;
1958c2ecf20Sopenharmony_ci	int rc;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
1988c2ecf20Sopenharmony_ci	if (!sccb)
1998c2ecf20Sopenharmony_ci		return -ENOMEM;
2008c2ecf20Sopenharmony_ci	sccb->header.length = PAGE_SIZE;
2018c2ecf20Sopenharmony_ci	sccb->rn = rn;
2028c2ecf20Sopenharmony_ci	rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL);
2038c2ecf20Sopenharmony_ci	if (rc)
2048c2ecf20Sopenharmony_ci		goto out;
2058c2ecf20Sopenharmony_ci	switch (sccb->header.response_code) {
2068c2ecf20Sopenharmony_ci	case 0x0020:
2078c2ecf20Sopenharmony_ci	case 0x0120:
2088c2ecf20Sopenharmony_ci		break;
2098c2ecf20Sopenharmony_ci	default:
2108c2ecf20Sopenharmony_ci		pr_warn("assign storage failed (cmd=0x%08x, response=0x%04x, rn=0x%04x)\n",
2118c2ecf20Sopenharmony_ci			cmd, sccb->header.response_code, rn);
2128c2ecf20Sopenharmony_ci		rc = -EIO;
2138c2ecf20Sopenharmony_ci		break;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ciout:
2168c2ecf20Sopenharmony_ci	free_page((unsigned long) sccb);
2178c2ecf20Sopenharmony_ci	return rc;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic int sclp_assign_storage(u16 rn)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	unsigned long long start;
2238c2ecf20Sopenharmony_ci	int rc;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	rc = do_assign_storage(0x000d0001, rn);
2268c2ecf20Sopenharmony_ci	if (rc)
2278c2ecf20Sopenharmony_ci		return rc;
2288c2ecf20Sopenharmony_ci	start = rn2addr(rn);
2298c2ecf20Sopenharmony_ci	storage_key_init_range(start, start + sclp.rzm);
2308c2ecf20Sopenharmony_ci	return 0;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic int sclp_unassign_storage(u16 rn)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	return do_assign_storage(0x000c0001, rn);
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistruct attach_storage_sccb {
2398c2ecf20Sopenharmony_ci	struct sccb_header header;
2408c2ecf20Sopenharmony_ci	u16 :16;
2418c2ecf20Sopenharmony_ci	u16 assigned;
2428c2ecf20Sopenharmony_ci	u32 :32;
2438c2ecf20Sopenharmony_ci	u32 entries[0];
2448c2ecf20Sopenharmony_ci} __packed;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int sclp_attach_storage(u8 id)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct attach_storage_sccb *sccb;
2498c2ecf20Sopenharmony_ci	int rc;
2508c2ecf20Sopenharmony_ci	int i;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
2538c2ecf20Sopenharmony_ci	if (!sccb)
2548c2ecf20Sopenharmony_ci		return -ENOMEM;
2558c2ecf20Sopenharmony_ci	sccb->header.length = PAGE_SIZE;
2568c2ecf20Sopenharmony_ci	sccb->header.function_code = 0x40;
2578c2ecf20Sopenharmony_ci	rc = sclp_sync_request_timeout(0x00080001 | id << 8, sccb,
2588c2ecf20Sopenharmony_ci				       SCLP_QUEUE_INTERVAL);
2598c2ecf20Sopenharmony_ci	if (rc)
2608c2ecf20Sopenharmony_ci		goto out;
2618c2ecf20Sopenharmony_ci	switch (sccb->header.response_code) {
2628c2ecf20Sopenharmony_ci	case 0x0020:
2638c2ecf20Sopenharmony_ci		set_bit(id, sclp_storage_ids);
2648c2ecf20Sopenharmony_ci		for (i = 0; i < sccb->assigned; i++) {
2658c2ecf20Sopenharmony_ci			if (sccb->entries[i])
2668c2ecf20Sopenharmony_ci				sclp_unassign_storage(sccb->entries[i] >> 16);
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci		break;
2698c2ecf20Sopenharmony_ci	default:
2708c2ecf20Sopenharmony_ci		rc = -EIO;
2718c2ecf20Sopenharmony_ci		break;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ciout:
2748c2ecf20Sopenharmony_ci	free_page((unsigned long) sccb);
2758c2ecf20Sopenharmony_ci	return rc;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic int sclp_mem_change_state(unsigned long start, unsigned long size,
2798c2ecf20Sopenharmony_ci				 int online)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct memory_increment *incr;
2828c2ecf20Sopenharmony_ci	unsigned long long istart;
2838c2ecf20Sopenharmony_ci	int rc = 0;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	list_for_each_entry(incr, &sclp_mem_list, list) {
2868c2ecf20Sopenharmony_ci		istart = rn2addr(incr->rn);
2878c2ecf20Sopenharmony_ci		if (start + size - 1 < istart)
2888c2ecf20Sopenharmony_ci			break;
2898c2ecf20Sopenharmony_ci		if (start > istart + sclp.rzm - 1)
2908c2ecf20Sopenharmony_ci			continue;
2918c2ecf20Sopenharmony_ci		if (online)
2928c2ecf20Sopenharmony_ci			rc |= sclp_assign_storage(incr->rn);
2938c2ecf20Sopenharmony_ci		else
2948c2ecf20Sopenharmony_ci			sclp_unassign_storage(incr->rn);
2958c2ecf20Sopenharmony_ci		if (rc == 0)
2968c2ecf20Sopenharmony_ci			incr->standby = online ? 0 : 1;
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci	return rc ? -EIO : 0;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic bool contains_standby_increment(unsigned long start, unsigned long end)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct memory_increment *incr;
3048c2ecf20Sopenharmony_ci	unsigned long istart;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	list_for_each_entry(incr, &sclp_mem_list, list) {
3078c2ecf20Sopenharmony_ci		istart = rn2addr(incr->rn);
3088c2ecf20Sopenharmony_ci		if (end - 1 < istart)
3098c2ecf20Sopenharmony_ci			continue;
3108c2ecf20Sopenharmony_ci		if (start > istart + sclp.rzm - 1)
3118c2ecf20Sopenharmony_ci			continue;
3128c2ecf20Sopenharmony_ci		if (incr->standby)
3138c2ecf20Sopenharmony_ci			return true;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci	return false;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int sclp_mem_notifier(struct notifier_block *nb,
3198c2ecf20Sopenharmony_ci			     unsigned long action, void *data)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	unsigned long start, size;
3228c2ecf20Sopenharmony_ci	struct memory_notify *arg;
3238c2ecf20Sopenharmony_ci	unsigned char id;
3248c2ecf20Sopenharmony_ci	int rc = 0;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	arg = data;
3278c2ecf20Sopenharmony_ci	start = arg->start_pfn << PAGE_SHIFT;
3288c2ecf20Sopenharmony_ci	size = arg->nr_pages << PAGE_SHIFT;
3298c2ecf20Sopenharmony_ci	mutex_lock(&sclp_mem_mutex);
3308c2ecf20Sopenharmony_ci	for_each_clear_bit(id, sclp_storage_ids, sclp_max_storage_id + 1)
3318c2ecf20Sopenharmony_ci		sclp_attach_storage(id);
3328c2ecf20Sopenharmony_ci	switch (action) {
3338c2ecf20Sopenharmony_ci	case MEM_GOING_OFFLINE:
3348c2ecf20Sopenharmony_ci		/*
3358c2ecf20Sopenharmony_ci		 * We do not allow to set memory blocks offline that contain
3368c2ecf20Sopenharmony_ci		 * standby memory. This is done to simplify the "memory online"
3378c2ecf20Sopenharmony_ci		 * case.
3388c2ecf20Sopenharmony_ci		 */
3398c2ecf20Sopenharmony_ci		if (contains_standby_increment(start, start + size))
3408c2ecf20Sopenharmony_ci			rc = -EPERM;
3418c2ecf20Sopenharmony_ci		break;
3428c2ecf20Sopenharmony_ci	case MEM_ONLINE:
3438c2ecf20Sopenharmony_ci	case MEM_CANCEL_OFFLINE:
3448c2ecf20Sopenharmony_ci		break;
3458c2ecf20Sopenharmony_ci	case MEM_GOING_ONLINE:
3468c2ecf20Sopenharmony_ci		rc = sclp_mem_change_state(start, size, 1);
3478c2ecf20Sopenharmony_ci		break;
3488c2ecf20Sopenharmony_ci	case MEM_CANCEL_ONLINE:
3498c2ecf20Sopenharmony_ci		sclp_mem_change_state(start, size, 0);
3508c2ecf20Sopenharmony_ci		break;
3518c2ecf20Sopenharmony_ci	case MEM_OFFLINE:
3528c2ecf20Sopenharmony_ci		sclp_mem_change_state(start, size, 0);
3538c2ecf20Sopenharmony_ci		break;
3548c2ecf20Sopenharmony_ci	default:
3558c2ecf20Sopenharmony_ci		rc = -EINVAL;
3568c2ecf20Sopenharmony_ci		break;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci	if (!rc)
3598c2ecf20Sopenharmony_ci		sclp_mem_state_changed = 1;
3608c2ecf20Sopenharmony_ci	mutex_unlock(&sclp_mem_mutex);
3618c2ecf20Sopenharmony_ci	return rc ? NOTIFY_BAD : NOTIFY_OK;
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic struct notifier_block sclp_mem_nb = {
3658c2ecf20Sopenharmony_ci	.notifier_call = sclp_mem_notifier,
3668c2ecf20Sopenharmony_ci};
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void __init align_to_block_size(unsigned long long *start,
3698c2ecf20Sopenharmony_ci				       unsigned long long *size,
3708c2ecf20Sopenharmony_ci				       unsigned long long alignment)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	unsigned long long start_align, size_align;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	start_align = roundup(*start, alignment);
3758c2ecf20Sopenharmony_ci	size_align = rounddown(*start + *size, alignment) - start_align;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	pr_info("Standby memory at 0x%llx (%lluM of %lluM usable)\n",
3788c2ecf20Sopenharmony_ci		*start, size_align >> 20, *size >> 20);
3798c2ecf20Sopenharmony_ci	*start = start_align;
3808c2ecf20Sopenharmony_ci	*size = size_align;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic void __init add_memory_merged(u16 rn)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	unsigned long long start, size, addr, block_size;
3868c2ecf20Sopenharmony_ci	static u16 first_rn, num;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (rn && first_rn && (first_rn + num == rn)) {
3898c2ecf20Sopenharmony_ci		num++;
3908c2ecf20Sopenharmony_ci		return;
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci	if (!first_rn)
3938c2ecf20Sopenharmony_ci		goto skip_add;
3948c2ecf20Sopenharmony_ci	start = rn2addr(first_rn);
3958c2ecf20Sopenharmony_ci	size = (unsigned long long) num * sclp.rzm;
3968c2ecf20Sopenharmony_ci	if (start >= VMEM_MAX_PHYS)
3978c2ecf20Sopenharmony_ci		goto skip_add;
3988c2ecf20Sopenharmony_ci	if (start + size > VMEM_MAX_PHYS)
3998c2ecf20Sopenharmony_ci		size = VMEM_MAX_PHYS - start;
4008c2ecf20Sopenharmony_ci	if (memory_end_set && (start >= memory_end))
4018c2ecf20Sopenharmony_ci		goto skip_add;
4028c2ecf20Sopenharmony_ci	if (memory_end_set && (start + size > memory_end))
4038c2ecf20Sopenharmony_ci		size = memory_end - start;
4048c2ecf20Sopenharmony_ci	block_size = memory_block_size_bytes();
4058c2ecf20Sopenharmony_ci	align_to_block_size(&start, &size, block_size);
4068c2ecf20Sopenharmony_ci	if (!size)
4078c2ecf20Sopenharmony_ci		goto skip_add;
4088c2ecf20Sopenharmony_ci	for (addr = start; addr < start + size; addr += block_size)
4098c2ecf20Sopenharmony_ci		add_memory(0, addr, block_size, MHP_NONE);
4108c2ecf20Sopenharmony_ciskip_add:
4118c2ecf20Sopenharmony_ci	first_rn = rn;
4128c2ecf20Sopenharmony_ci	num = 1;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic void __init sclp_add_standby_memory(void)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct memory_increment *incr;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	list_for_each_entry(incr, &sclp_mem_list, list)
4208c2ecf20Sopenharmony_ci		if (incr->standby)
4218c2ecf20Sopenharmony_ci			add_memory_merged(incr->rn);
4228c2ecf20Sopenharmony_ci	add_memory_merged(0);
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic void __init insert_increment(u16 rn, int standby, int assigned)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct memory_increment *incr, *new_incr;
4288c2ecf20Sopenharmony_ci	struct list_head *prev;
4298c2ecf20Sopenharmony_ci	u16 last_rn;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	new_incr = kzalloc(sizeof(*new_incr), GFP_KERNEL);
4328c2ecf20Sopenharmony_ci	if (!new_incr)
4338c2ecf20Sopenharmony_ci		return;
4348c2ecf20Sopenharmony_ci	new_incr->rn = rn;
4358c2ecf20Sopenharmony_ci	new_incr->standby = standby;
4368c2ecf20Sopenharmony_ci	last_rn = 0;
4378c2ecf20Sopenharmony_ci	prev = &sclp_mem_list;
4388c2ecf20Sopenharmony_ci	list_for_each_entry(incr, &sclp_mem_list, list) {
4398c2ecf20Sopenharmony_ci		if (assigned && incr->rn > rn)
4408c2ecf20Sopenharmony_ci			break;
4418c2ecf20Sopenharmony_ci		if (!assigned && incr->rn - last_rn > 1)
4428c2ecf20Sopenharmony_ci			break;
4438c2ecf20Sopenharmony_ci		last_rn = incr->rn;
4448c2ecf20Sopenharmony_ci		prev = &incr->list;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci	if (!assigned)
4478c2ecf20Sopenharmony_ci		new_incr->rn = last_rn + 1;
4488c2ecf20Sopenharmony_ci	if (new_incr->rn > sclp.rnmax) {
4498c2ecf20Sopenharmony_ci		kfree(new_incr);
4508c2ecf20Sopenharmony_ci		return;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci	list_add(&new_incr->list, prev);
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic int sclp_mem_freeze(struct device *dev)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	if (!sclp_mem_state_changed)
4588c2ecf20Sopenharmony_ci		return 0;
4598c2ecf20Sopenharmony_ci	pr_err("Memory hotplug state changed, suspend refused.\n");
4608c2ecf20Sopenharmony_ci	return -EPERM;
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic const struct dev_pm_ops sclp_mem_pm_ops = {
4648c2ecf20Sopenharmony_ci	.freeze		= sclp_mem_freeze,
4658c2ecf20Sopenharmony_ci};
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic struct platform_driver sclp_mem_pdrv = {
4688c2ecf20Sopenharmony_ci	.driver = {
4698c2ecf20Sopenharmony_ci		.name	= "sclp_mem",
4708c2ecf20Sopenharmony_ci		.pm	= &sclp_mem_pm_ops,
4718c2ecf20Sopenharmony_ci	},
4728c2ecf20Sopenharmony_ci};
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic int __init sclp_detect_standby_memory(void)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct platform_device *sclp_pdev;
4778c2ecf20Sopenharmony_ci	struct read_storage_sccb *sccb;
4788c2ecf20Sopenharmony_ci	int i, id, assigned, rc;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (OLDMEM_BASE) /* No standby memory in kdump mode */
4818c2ecf20Sopenharmony_ci		return 0;
4828c2ecf20Sopenharmony_ci	if ((sclp.facilities & 0xe00000000000ULL) != 0xe00000000000ULL)
4838c2ecf20Sopenharmony_ci		return 0;
4848c2ecf20Sopenharmony_ci	rc = -ENOMEM;
4858c2ecf20Sopenharmony_ci	sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA);
4868c2ecf20Sopenharmony_ci	if (!sccb)
4878c2ecf20Sopenharmony_ci		goto out;
4888c2ecf20Sopenharmony_ci	assigned = 0;
4898c2ecf20Sopenharmony_ci	for (id = 0; id <= sclp_max_storage_id; id++) {
4908c2ecf20Sopenharmony_ci		memset(sccb, 0, PAGE_SIZE);
4918c2ecf20Sopenharmony_ci		sccb->header.length = PAGE_SIZE;
4928c2ecf20Sopenharmony_ci		rc = sclp_sync_request(SCLP_CMDW_READ_STORAGE_INFO | id << 8, sccb);
4938c2ecf20Sopenharmony_ci		if (rc)
4948c2ecf20Sopenharmony_ci			goto out;
4958c2ecf20Sopenharmony_ci		switch (sccb->header.response_code) {
4968c2ecf20Sopenharmony_ci		case 0x0010:
4978c2ecf20Sopenharmony_ci			set_bit(id, sclp_storage_ids);
4988c2ecf20Sopenharmony_ci			for (i = 0; i < sccb->assigned; i++) {
4998c2ecf20Sopenharmony_ci				if (!sccb->entries[i])
5008c2ecf20Sopenharmony_ci					continue;
5018c2ecf20Sopenharmony_ci				assigned++;
5028c2ecf20Sopenharmony_ci				insert_increment(sccb->entries[i] >> 16, 0, 1);
5038c2ecf20Sopenharmony_ci			}
5048c2ecf20Sopenharmony_ci			break;
5058c2ecf20Sopenharmony_ci		case 0x0310:
5068c2ecf20Sopenharmony_ci			break;
5078c2ecf20Sopenharmony_ci		case 0x0410:
5088c2ecf20Sopenharmony_ci			for (i = 0; i < sccb->assigned; i++) {
5098c2ecf20Sopenharmony_ci				if (!sccb->entries[i])
5108c2ecf20Sopenharmony_ci					continue;
5118c2ecf20Sopenharmony_ci				assigned++;
5128c2ecf20Sopenharmony_ci				insert_increment(sccb->entries[i] >> 16, 1, 1);
5138c2ecf20Sopenharmony_ci			}
5148c2ecf20Sopenharmony_ci			break;
5158c2ecf20Sopenharmony_ci		default:
5168c2ecf20Sopenharmony_ci			rc = -EIO;
5178c2ecf20Sopenharmony_ci			break;
5188c2ecf20Sopenharmony_ci		}
5198c2ecf20Sopenharmony_ci		if (!rc)
5208c2ecf20Sopenharmony_ci			sclp_max_storage_id = sccb->max_id;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci	if (rc || list_empty(&sclp_mem_list))
5238c2ecf20Sopenharmony_ci		goto out;
5248c2ecf20Sopenharmony_ci	for (i = 1; i <= sclp.rnmax - assigned; i++)
5258c2ecf20Sopenharmony_ci		insert_increment(0, 1, 0);
5268c2ecf20Sopenharmony_ci	rc = register_memory_notifier(&sclp_mem_nb);
5278c2ecf20Sopenharmony_ci	if (rc)
5288c2ecf20Sopenharmony_ci		goto out;
5298c2ecf20Sopenharmony_ci	rc = platform_driver_register(&sclp_mem_pdrv);
5308c2ecf20Sopenharmony_ci	if (rc)
5318c2ecf20Sopenharmony_ci		goto out;
5328c2ecf20Sopenharmony_ci	sclp_pdev = platform_device_register_simple("sclp_mem", -1, NULL, 0);
5338c2ecf20Sopenharmony_ci	rc = PTR_ERR_OR_ZERO(sclp_pdev);
5348c2ecf20Sopenharmony_ci	if (rc)
5358c2ecf20Sopenharmony_ci		goto out_driver;
5368c2ecf20Sopenharmony_ci	sclp_add_standby_memory();
5378c2ecf20Sopenharmony_ci	goto out;
5388c2ecf20Sopenharmony_ciout_driver:
5398c2ecf20Sopenharmony_ci	platform_driver_unregister(&sclp_mem_pdrv);
5408c2ecf20Sopenharmony_ciout:
5418c2ecf20Sopenharmony_ci	free_page((unsigned long) sccb);
5428c2ecf20Sopenharmony_ci	return rc;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci__initcall(sclp_detect_standby_memory);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci#endif /* CONFIG_MEMORY_HOTPLUG */
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci/*
5498c2ecf20Sopenharmony_ci * Channel path configuration related functions.
5508c2ecf20Sopenharmony_ci */
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci#define SCLP_CMDW_CONFIGURE_CHPATH		0x000f0001
5538c2ecf20Sopenharmony_ci#define SCLP_CMDW_DECONFIGURE_CHPATH		0x000e0001
5548c2ecf20Sopenharmony_ci#define SCLP_CMDW_READ_CHPATH_INFORMATION	0x00030001
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistruct chp_cfg_sccb {
5578c2ecf20Sopenharmony_ci	struct sccb_header header;
5588c2ecf20Sopenharmony_ci	u8 ccm;
5598c2ecf20Sopenharmony_ci	u8 reserved[6];
5608c2ecf20Sopenharmony_ci	u8 cssid;
5618c2ecf20Sopenharmony_ci} __attribute__((packed));
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic int do_chp_configure(sclp_cmdw_t cmd)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	struct chp_cfg_sccb *sccb;
5668c2ecf20Sopenharmony_ci	int rc;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (!SCLP_HAS_CHP_RECONFIG)
5698c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5708c2ecf20Sopenharmony_ci	/* Prepare sccb. */
5718c2ecf20Sopenharmony_ci	sccb = (struct chp_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
5728c2ecf20Sopenharmony_ci	if (!sccb)
5738c2ecf20Sopenharmony_ci		return -ENOMEM;
5748c2ecf20Sopenharmony_ci	sccb->header.length = sizeof(*sccb);
5758c2ecf20Sopenharmony_ci	rc = sclp_sync_request(cmd, sccb);
5768c2ecf20Sopenharmony_ci	if (rc)
5778c2ecf20Sopenharmony_ci		goto out;
5788c2ecf20Sopenharmony_ci	switch (sccb->header.response_code) {
5798c2ecf20Sopenharmony_ci	case 0x0020:
5808c2ecf20Sopenharmony_ci	case 0x0120:
5818c2ecf20Sopenharmony_ci	case 0x0440:
5828c2ecf20Sopenharmony_ci	case 0x0450:
5838c2ecf20Sopenharmony_ci		break;
5848c2ecf20Sopenharmony_ci	default:
5858c2ecf20Sopenharmony_ci		pr_warn("configure channel-path failed (cmd=0x%08x, response=0x%04x)\n",
5868c2ecf20Sopenharmony_ci			cmd, sccb->header.response_code);
5878c2ecf20Sopenharmony_ci		rc = -EIO;
5888c2ecf20Sopenharmony_ci		break;
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ciout:
5918c2ecf20Sopenharmony_ci	free_page((unsigned long) sccb);
5928c2ecf20Sopenharmony_ci	return rc;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci/**
5968c2ecf20Sopenharmony_ci * sclp_chp_configure - perform configure channel-path sclp command
5978c2ecf20Sopenharmony_ci * @chpid: channel-path ID
5988c2ecf20Sopenharmony_ci *
5998c2ecf20Sopenharmony_ci * Perform configure channel-path command sclp command for specified chpid.
6008c2ecf20Sopenharmony_ci * Return 0 after command successfully finished, non-zero otherwise.
6018c2ecf20Sopenharmony_ci */
6028c2ecf20Sopenharmony_ciint sclp_chp_configure(struct chp_id chpid)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	return do_chp_configure(SCLP_CMDW_CONFIGURE_CHPATH | chpid.id << 8);
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci/**
6088c2ecf20Sopenharmony_ci * sclp_chp_deconfigure - perform deconfigure channel-path sclp command
6098c2ecf20Sopenharmony_ci * @chpid: channel-path ID
6108c2ecf20Sopenharmony_ci *
6118c2ecf20Sopenharmony_ci * Perform deconfigure channel-path command sclp command for specified chpid
6128c2ecf20Sopenharmony_ci * and wait for completion. On success return 0. Return non-zero otherwise.
6138c2ecf20Sopenharmony_ci */
6148c2ecf20Sopenharmony_ciint sclp_chp_deconfigure(struct chp_id chpid)
6158c2ecf20Sopenharmony_ci{
6168c2ecf20Sopenharmony_ci	return do_chp_configure(SCLP_CMDW_DECONFIGURE_CHPATH | chpid.id << 8);
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistruct chp_info_sccb {
6208c2ecf20Sopenharmony_ci	struct sccb_header header;
6218c2ecf20Sopenharmony_ci	u8 recognized[SCLP_CHP_INFO_MASK_SIZE];
6228c2ecf20Sopenharmony_ci	u8 standby[SCLP_CHP_INFO_MASK_SIZE];
6238c2ecf20Sopenharmony_ci	u8 configured[SCLP_CHP_INFO_MASK_SIZE];
6248c2ecf20Sopenharmony_ci	u8 ccm;
6258c2ecf20Sopenharmony_ci	u8 reserved[6];
6268c2ecf20Sopenharmony_ci	u8 cssid;
6278c2ecf20Sopenharmony_ci} __attribute__((packed));
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci/**
6308c2ecf20Sopenharmony_ci * sclp_chp_read_info - perform read channel-path information sclp command
6318c2ecf20Sopenharmony_ci * @info: resulting channel-path information data
6328c2ecf20Sopenharmony_ci *
6338c2ecf20Sopenharmony_ci * Perform read channel-path information sclp command and wait for completion.
6348c2ecf20Sopenharmony_ci * On success, store channel-path information in @info and return 0. Return
6358c2ecf20Sopenharmony_ci * non-zero otherwise.
6368c2ecf20Sopenharmony_ci */
6378c2ecf20Sopenharmony_ciint sclp_chp_read_info(struct sclp_chp_info *info)
6388c2ecf20Sopenharmony_ci{
6398c2ecf20Sopenharmony_ci	struct chp_info_sccb *sccb;
6408c2ecf20Sopenharmony_ci	int rc;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	if (!SCLP_HAS_CHP_INFO)
6438c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6448c2ecf20Sopenharmony_ci	/* Prepare sccb. */
6458c2ecf20Sopenharmony_ci	sccb = (struct chp_info_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
6468c2ecf20Sopenharmony_ci	if (!sccb)
6478c2ecf20Sopenharmony_ci		return -ENOMEM;
6488c2ecf20Sopenharmony_ci	sccb->header.length = sizeof(*sccb);
6498c2ecf20Sopenharmony_ci	rc = sclp_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb);
6508c2ecf20Sopenharmony_ci	if (rc)
6518c2ecf20Sopenharmony_ci		goto out;
6528c2ecf20Sopenharmony_ci	if (sccb->header.response_code != 0x0010) {
6538c2ecf20Sopenharmony_ci		pr_warn("read channel-path info failed (response=0x%04x)\n",
6548c2ecf20Sopenharmony_ci			sccb->header.response_code);
6558c2ecf20Sopenharmony_ci		rc = -EIO;
6568c2ecf20Sopenharmony_ci		goto out;
6578c2ecf20Sopenharmony_ci	}
6588c2ecf20Sopenharmony_ci	memcpy(info->recognized, sccb->recognized, SCLP_CHP_INFO_MASK_SIZE);
6598c2ecf20Sopenharmony_ci	memcpy(info->standby, sccb->standby, SCLP_CHP_INFO_MASK_SIZE);
6608c2ecf20Sopenharmony_ci	memcpy(info->configured, sccb->configured, SCLP_CHP_INFO_MASK_SIZE);
6618c2ecf20Sopenharmony_ciout:
6628c2ecf20Sopenharmony_ci	free_page((unsigned long) sccb);
6638c2ecf20Sopenharmony_ci	return rc;
6648c2ecf20Sopenharmony_ci}
665