18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Xpram.c -- the S/390 expanded memory RAM-disk 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * significant parts of this code are based on 68c2ecf20Sopenharmony_ci * the sbull device driver presented in 78c2ecf20Sopenharmony_ci * A. Rubini: Linux Device Drivers 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Author of XPRAM specific coding: Reinhard Buendgen 108c2ecf20Sopenharmony_ci * buendgen@de.ibm.com 118c2ecf20Sopenharmony_ci * Rewrite for 2.5: Martin Schwidefsky <schwidefsky@de.ibm.com> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * External interfaces: 148c2ecf20Sopenharmony_ci * Interfaces to linux kernel 158c2ecf20Sopenharmony_ci * xpram_setup: read kernel parameters 168c2ecf20Sopenharmony_ci * Device specific file operations 178c2ecf20Sopenharmony_ci * xpram_iotcl 188c2ecf20Sopenharmony_ci * xpram_open 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * "ad-hoc" partitioning: 218c2ecf20Sopenharmony_ci * the expanded memory can be partitioned among several devices 228c2ecf20Sopenharmony_ci * (with different minors). The partitioning set up can be 238c2ecf20Sopenharmony_ci * set by kernel or module parameters (int devs & int sizes[]) 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Potential future improvements: 268c2ecf20Sopenharmony_ci * generic hard disk support to replace ad-hoc partitioning 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "xpram" 308c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <linux/module.h> 338c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 348c2ecf20Sopenharmony_ci#include <linux/ctype.h> /* isdigit, isxdigit */ 358c2ecf20Sopenharmony_ci#include <linux/errno.h> 368c2ecf20Sopenharmony_ci#include <linux/init.h> 378c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 388c2ecf20Sopenharmony_ci#include <linux/blkpg.h> 398c2ecf20Sopenharmony_ci#include <linux/hdreg.h> /* HDIO_GETGEO */ 408c2ecf20Sopenharmony_ci#include <linux/device.h> 418c2ecf20Sopenharmony_ci#include <linux/bio.h> 428c2ecf20Sopenharmony_ci#include <linux/suspend.h> 438c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 448c2ecf20Sopenharmony_ci#include <linux/gfp.h> 458c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define XPRAM_NAME "xpram" 488c2ecf20Sopenharmony_ci#define XPRAM_DEVS 1 /* one partition */ 498c2ecf20Sopenharmony_ci#define XPRAM_MAX_DEVS 32 /* maximal number of devices (partitions) */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_citypedef struct { 528c2ecf20Sopenharmony_ci unsigned int size; /* size of xpram segment in pages */ 538c2ecf20Sopenharmony_ci unsigned int offset; /* start page of xpram segment */ 548c2ecf20Sopenharmony_ci} xpram_device_t; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic xpram_device_t xpram_devices[XPRAM_MAX_DEVS]; 578c2ecf20Sopenharmony_cistatic unsigned int xpram_sizes[XPRAM_MAX_DEVS]; 588c2ecf20Sopenharmony_cistatic struct gendisk *xpram_disks[XPRAM_MAX_DEVS]; 598c2ecf20Sopenharmony_cistatic struct request_queue *xpram_queues[XPRAM_MAX_DEVS]; 608c2ecf20Sopenharmony_cistatic unsigned int xpram_pages; 618c2ecf20Sopenharmony_cistatic int xpram_devs; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * Parameter parsing functions. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic int devs = XPRAM_DEVS; 678c2ecf20Sopenharmony_cistatic char *sizes[XPRAM_MAX_DEVS]; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cimodule_param(devs, int, 0); 708c2ecf20Sopenharmony_cimodule_param_array(sizes, charp, NULL, 0); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(devs, "number of devices (\"partitions\"), " \ 738c2ecf20Sopenharmony_ci "the default is " __MODULE_STRING(XPRAM_DEVS) "\n"); 748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sizes, "list of device (partition) sizes " \ 758c2ecf20Sopenharmony_ci "the defaults are 0s \n" \ 768c2ecf20Sopenharmony_ci "All devices with size 0 equally partition the " 778c2ecf20Sopenharmony_ci "remaining space on the expanded strorage not " 788c2ecf20Sopenharmony_ci "claimed by explicit sizes\n"); 798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * Copy expanded memory page (4kB) into main memory 838c2ecf20Sopenharmony_ci * Arguments 848c2ecf20Sopenharmony_ci * page_addr: address of target page 858c2ecf20Sopenharmony_ci * xpage_index: index of expandeded memory page 868c2ecf20Sopenharmony_ci * Return value 878c2ecf20Sopenharmony_ci * 0: if operation succeeds 888c2ecf20Sopenharmony_ci * -EIO: if pgin failed 898c2ecf20Sopenharmony_ci * -ENXIO: if xpram has vanished 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_cistatic int xpram_page_in (unsigned long page_addr, unsigned int xpage_index) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci int cc = 2; /* return unused cc 2 if pgin traps */ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci asm volatile( 968c2ecf20Sopenharmony_ci " .insn rre,0xb22e0000,%1,%2\n" /* pgin %1,%2 */ 978c2ecf20Sopenharmony_ci "0: ipm %0\n" 988c2ecf20Sopenharmony_ci " srl %0,28\n" 998c2ecf20Sopenharmony_ci "1:\n" 1008c2ecf20Sopenharmony_ci EX_TABLE(0b,1b) 1018c2ecf20Sopenharmony_ci : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc"); 1028c2ecf20Sopenharmony_ci if (cc == 3) 1038c2ecf20Sopenharmony_ci return -ENXIO; 1048c2ecf20Sopenharmony_ci if (cc == 2) 1058c2ecf20Sopenharmony_ci return -ENXIO; 1068c2ecf20Sopenharmony_ci if (cc == 1) 1078c2ecf20Sopenharmony_ci return -EIO; 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * Copy a 4kB page of main memory to an expanded memory page 1138c2ecf20Sopenharmony_ci * Arguments 1148c2ecf20Sopenharmony_ci * page_addr: address of source page 1158c2ecf20Sopenharmony_ci * xpage_index: index of expandeded memory page 1168c2ecf20Sopenharmony_ci * Return value 1178c2ecf20Sopenharmony_ci * 0: if operation succeeds 1188c2ecf20Sopenharmony_ci * -EIO: if pgout failed 1198c2ecf20Sopenharmony_ci * -ENXIO: if xpram has vanished 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_cistatic long xpram_page_out (unsigned long page_addr, unsigned int xpage_index) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int cc = 2; /* return unused cc 2 if pgin traps */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci asm volatile( 1268c2ecf20Sopenharmony_ci " .insn rre,0xb22f0000,%1,%2\n" /* pgout %1,%2 */ 1278c2ecf20Sopenharmony_ci "0: ipm %0\n" 1288c2ecf20Sopenharmony_ci " srl %0,28\n" 1298c2ecf20Sopenharmony_ci "1:\n" 1308c2ecf20Sopenharmony_ci EX_TABLE(0b,1b) 1318c2ecf20Sopenharmony_ci : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc"); 1328c2ecf20Sopenharmony_ci if (cc == 3) 1338c2ecf20Sopenharmony_ci return -ENXIO; 1348c2ecf20Sopenharmony_ci if (cc == 2) 1358c2ecf20Sopenharmony_ci return -ENXIO; 1368c2ecf20Sopenharmony_ci if (cc == 1) 1378c2ecf20Sopenharmony_ci return -EIO; 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * Check if xpram is available. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_cistatic int xpram_present(void) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci unsigned long mem_page; 1478c2ecf20Sopenharmony_ci int rc; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci mem_page = (unsigned long) __get_free_page(GFP_KERNEL); 1508c2ecf20Sopenharmony_ci if (!mem_page) 1518c2ecf20Sopenharmony_ci return -ENOMEM; 1528c2ecf20Sopenharmony_ci rc = xpram_page_in(mem_page, 0); 1538c2ecf20Sopenharmony_ci free_page(mem_page); 1548c2ecf20Sopenharmony_ci return rc ? -ENXIO : 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* 1588c2ecf20Sopenharmony_ci * Return index of the last available xpram page. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_cistatic unsigned long xpram_highest_page_index(void) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci unsigned int page_index, add_bit; 1638c2ecf20Sopenharmony_ci unsigned long mem_page; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci mem_page = (unsigned long) __get_free_page(GFP_KERNEL); 1668c2ecf20Sopenharmony_ci if (!mem_page) 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci page_index = 0; 1708c2ecf20Sopenharmony_ci add_bit = 1ULL << (sizeof(unsigned int)*8 - 1); 1718c2ecf20Sopenharmony_ci while (add_bit > 0) { 1728c2ecf20Sopenharmony_ci if (xpram_page_in(mem_page, page_index | add_bit) == 0) 1738c2ecf20Sopenharmony_ci page_index |= add_bit; 1748c2ecf20Sopenharmony_ci add_bit >>= 1; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci free_page (mem_page); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return page_index; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* 1838c2ecf20Sopenharmony_ci * Block device make request function. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistatic blk_qc_t xpram_submit_bio(struct bio *bio) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci xpram_device_t *xdev = bio->bi_disk->private_data; 1888c2ecf20Sopenharmony_ci struct bio_vec bvec; 1898c2ecf20Sopenharmony_ci struct bvec_iter iter; 1908c2ecf20Sopenharmony_ci unsigned int index; 1918c2ecf20Sopenharmony_ci unsigned long page_addr; 1928c2ecf20Sopenharmony_ci unsigned long bytes; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci blk_queue_split(&bio); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if ((bio->bi_iter.bi_sector & 7) != 0 || 1978c2ecf20Sopenharmony_ci (bio->bi_iter.bi_size & 4095) != 0) 1988c2ecf20Sopenharmony_ci /* Request is not page-aligned. */ 1998c2ecf20Sopenharmony_ci goto fail; 2008c2ecf20Sopenharmony_ci if ((bio->bi_iter.bi_size >> 12) > xdev->size) 2018c2ecf20Sopenharmony_ci /* Request size is no page-aligned. */ 2028c2ecf20Sopenharmony_ci goto fail; 2038c2ecf20Sopenharmony_ci if ((bio->bi_iter.bi_sector >> 3) > 0xffffffffU - xdev->offset) 2048c2ecf20Sopenharmony_ci goto fail; 2058c2ecf20Sopenharmony_ci index = (bio->bi_iter.bi_sector >> 3) + xdev->offset; 2068c2ecf20Sopenharmony_ci bio_for_each_segment(bvec, bio, iter) { 2078c2ecf20Sopenharmony_ci page_addr = (unsigned long) 2088c2ecf20Sopenharmony_ci kmap(bvec.bv_page) + bvec.bv_offset; 2098c2ecf20Sopenharmony_ci bytes = bvec.bv_len; 2108c2ecf20Sopenharmony_ci if ((page_addr & 4095) != 0 || (bytes & 4095) != 0) 2118c2ecf20Sopenharmony_ci /* More paranoia. */ 2128c2ecf20Sopenharmony_ci goto fail; 2138c2ecf20Sopenharmony_ci while (bytes > 0) { 2148c2ecf20Sopenharmony_ci if (bio_data_dir(bio) == READ) { 2158c2ecf20Sopenharmony_ci if (xpram_page_in(page_addr, index) != 0) 2168c2ecf20Sopenharmony_ci goto fail; 2178c2ecf20Sopenharmony_ci } else { 2188c2ecf20Sopenharmony_ci if (xpram_page_out(page_addr, index) != 0) 2198c2ecf20Sopenharmony_ci goto fail; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci page_addr += 4096; 2228c2ecf20Sopenharmony_ci bytes -= 4096; 2238c2ecf20Sopenharmony_ci index++; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci bio_endio(bio); 2278c2ecf20Sopenharmony_ci return BLK_QC_T_NONE; 2288c2ecf20Sopenharmony_cifail: 2298c2ecf20Sopenharmony_ci bio_io_error(bio); 2308c2ecf20Sopenharmony_ci return BLK_QC_T_NONE; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int xpram_getgeo(struct block_device *bdev, struct hd_geometry *geo) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci unsigned long size; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* 2388c2ecf20Sopenharmony_ci * get geometry: we have to fake one... trim the size to a 2398c2ecf20Sopenharmony_ci * multiple of 64 (32k): tell we have 16 sectors, 4 heads, 2408c2ecf20Sopenharmony_ci * whatever cylinders. Tell also that data starts at sector. 4. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci size = (xpram_pages * 8) & ~0x3f; 2438c2ecf20Sopenharmony_ci geo->cylinders = size >> 6; 2448c2ecf20Sopenharmony_ci geo->heads = 4; 2458c2ecf20Sopenharmony_ci geo->sectors = 16; 2468c2ecf20Sopenharmony_ci geo->start = 4; 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic const struct block_device_operations xpram_devops = 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2538c2ecf20Sopenharmony_ci .submit_bio = xpram_submit_bio, 2548c2ecf20Sopenharmony_ci .getgeo = xpram_getgeo, 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/* 2588c2ecf20Sopenharmony_ci * Setup xpram_sizes array. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_cistatic int __init xpram_setup_sizes(unsigned long pages) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci unsigned long mem_needed; 2638c2ecf20Sopenharmony_ci unsigned long mem_auto; 2648c2ecf20Sopenharmony_ci unsigned long long size; 2658c2ecf20Sopenharmony_ci char *sizes_end; 2668c2ecf20Sopenharmony_ci int mem_auto_no; 2678c2ecf20Sopenharmony_ci int i; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Check number of devices. */ 2708c2ecf20Sopenharmony_ci if (devs <= 0 || devs > XPRAM_MAX_DEVS) { 2718c2ecf20Sopenharmony_ci pr_err("%d is not a valid number of XPRAM devices\n",devs); 2728c2ecf20Sopenharmony_ci return -EINVAL; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci xpram_devs = devs; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* 2778c2ecf20Sopenharmony_ci * Copy sizes array to xpram_sizes and align partition 2788c2ecf20Sopenharmony_ci * sizes to page boundary. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci mem_needed = 0; 2818c2ecf20Sopenharmony_ci mem_auto_no = 0; 2828c2ecf20Sopenharmony_ci for (i = 0; i < xpram_devs; i++) { 2838c2ecf20Sopenharmony_ci if (sizes[i]) { 2848c2ecf20Sopenharmony_ci size = simple_strtoull(sizes[i], &sizes_end, 0); 2858c2ecf20Sopenharmony_ci switch (*sizes_end) { 2868c2ecf20Sopenharmony_ci case 'g': 2878c2ecf20Sopenharmony_ci case 'G': 2888c2ecf20Sopenharmony_ci size <<= 20; 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case 'm': 2918c2ecf20Sopenharmony_ci case 'M': 2928c2ecf20Sopenharmony_ci size <<= 10; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci xpram_sizes[i] = (size + 3) & -4UL; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci if (xpram_sizes[i]) 2978c2ecf20Sopenharmony_ci mem_needed += xpram_sizes[i]; 2988c2ecf20Sopenharmony_ci else 2998c2ecf20Sopenharmony_ci mem_auto_no++; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci pr_info(" number of devices (partitions): %d \n", xpram_devs); 3038c2ecf20Sopenharmony_ci for (i = 0; i < xpram_devs; i++) { 3048c2ecf20Sopenharmony_ci if (xpram_sizes[i]) 3058c2ecf20Sopenharmony_ci pr_info(" size of partition %d: %u kB\n", 3068c2ecf20Sopenharmony_ci i, xpram_sizes[i]); 3078c2ecf20Sopenharmony_ci else 3088c2ecf20Sopenharmony_ci pr_info(" size of partition %d to be set " 3098c2ecf20Sopenharmony_ci "automatically\n",i); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci pr_info(" memory needed (for sized partitions): %lu kB\n", 3128c2ecf20Sopenharmony_ci mem_needed); 3138c2ecf20Sopenharmony_ci pr_info(" partitions to be sized automatically: %d\n", 3148c2ecf20Sopenharmony_ci mem_auto_no); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (mem_needed > pages * 4) { 3178c2ecf20Sopenharmony_ci pr_err("Not enough expanded memory available\n"); 3188c2ecf20Sopenharmony_ci return -EINVAL; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* 3228c2ecf20Sopenharmony_ci * partitioning: 3238c2ecf20Sopenharmony_ci * xpram_sizes[i] != 0; partition i has size xpram_sizes[i] kB 3248c2ecf20Sopenharmony_ci * else: ; all partitions with zero xpram_sizes[i] 3258c2ecf20Sopenharmony_ci * partition equally the remaining space 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ci if (mem_auto_no) { 3288c2ecf20Sopenharmony_ci mem_auto = ((pages - mem_needed / 4) / mem_auto_no) * 4; 3298c2ecf20Sopenharmony_ci pr_info(" automatically determined " 3308c2ecf20Sopenharmony_ci "partition size: %lu kB\n", mem_auto); 3318c2ecf20Sopenharmony_ci for (i = 0; i < xpram_devs; i++) 3328c2ecf20Sopenharmony_ci if (xpram_sizes[i] == 0) 3338c2ecf20Sopenharmony_ci xpram_sizes[i] = mem_auto; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int __init xpram_setup_blkdev(void) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci unsigned long offset; 3418c2ecf20Sopenharmony_ci int i, rc = -ENOMEM; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci for (i = 0; i < xpram_devs; i++) { 3448c2ecf20Sopenharmony_ci xpram_disks[i] = alloc_disk(1); 3458c2ecf20Sopenharmony_ci if (!xpram_disks[i]) 3468c2ecf20Sopenharmony_ci goto out; 3478c2ecf20Sopenharmony_ci xpram_queues[i] = blk_alloc_queue(NUMA_NO_NODE); 3488c2ecf20Sopenharmony_ci if (!xpram_queues[i]) { 3498c2ecf20Sopenharmony_ci put_disk(xpram_disks[i]); 3508c2ecf20Sopenharmony_ci goto out; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_NONROT, xpram_queues[i]); 3538c2ecf20Sopenharmony_ci blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, xpram_queues[i]); 3548c2ecf20Sopenharmony_ci blk_queue_logical_block_size(xpram_queues[i], 4096); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* 3588c2ecf20Sopenharmony_ci * Register xpram major. 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_ci rc = register_blkdev(XPRAM_MAJOR, XPRAM_NAME); 3618c2ecf20Sopenharmony_ci if (rc < 0) 3628c2ecf20Sopenharmony_ci goto out; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* 3658c2ecf20Sopenharmony_ci * Setup device structures. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci offset = 0; 3688c2ecf20Sopenharmony_ci for (i = 0; i < xpram_devs; i++) { 3698c2ecf20Sopenharmony_ci struct gendisk *disk = xpram_disks[i]; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci xpram_devices[i].size = xpram_sizes[i] / 4; 3728c2ecf20Sopenharmony_ci xpram_devices[i].offset = offset; 3738c2ecf20Sopenharmony_ci offset += xpram_devices[i].size; 3748c2ecf20Sopenharmony_ci disk->major = XPRAM_MAJOR; 3758c2ecf20Sopenharmony_ci disk->first_minor = i; 3768c2ecf20Sopenharmony_ci disk->fops = &xpram_devops; 3778c2ecf20Sopenharmony_ci disk->private_data = &xpram_devices[i]; 3788c2ecf20Sopenharmony_ci disk->queue = xpram_queues[i]; 3798c2ecf20Sopenharmony_ci sprintf(disk->disk_name, "slram%d", i); 3808c2ecf20Sopenharmony_ci set_capacity(disk, xpram_sizes[i] << 1); 3818c2ecf20Sopenharmony_ci add_disk(disk); 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return 0; 3858c2ecf20Sopenharmony_ciout: 3868c2ecf20Sopenharmony_ci while (i--) { 3878c2ecf20Sopenharmony_ci blk_cleanup_queue(xpram_queues[i]); 3888c2ecf20Sopenharmony_ci put_disk(xpram_disks[i]); 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci return rc; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* 3948c2ecf20Sopenharmony_ci * Resume failed: Print error message and call panic. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_cistatic void xpram_resume_error(const char *message) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci pr_err("Resuming the system failed: %s\n", message); 3998c2ecf20Sopenharmony_ci panic("xpram resume error\n"); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci/* 4038c2ecf20Sopenharmony_ci * Check if xpram setup changed between suspend and resume. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_cistatic int xpram_restore(struct device *dev) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci if (!xpram_pages) 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci if (xpram_present() != 0) 4108c2ecf20Sopenharmony_ci xpram_resume_error("xpram disappeared"); 4118c2ecf20Sopenharmony_ci if (xpram_pages != xpram_highest_page_index() + 1) 4128c2ecf20Sopenharmony_ci xpram_resume_error("Size of xpram changed"); 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic const struct dev_pm_ops xpram_pm_ops = { 4178c2ecf20Sopenharmony_ci .restore = xpram_restore, 4188c2ecf20Sopenharmony_ci}; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic struct platform_driver xpram_pdrv = { 4218c2ecf20Sopenharmony_ci .driver = { 4228c2ecf20Sopenharmony_ci .name = XPRAM_NAME, 4238c2ecf20Sopenharmony_ci .pm = &xpram_pm_ops, 4248c2ecf20Sopenharmony_ci }, 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic struct platform_device *xpram_pdev; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci/* 4308c2ecf20Sopenharmony_ci * Finally, the init/exit functions. 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_cistatic void __exit xpram_exit(void) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci int i; 4358c2ecf20Sopenharmony_ci for (i = 0; i < xpram_devs; i++) { 4368c2ecf20Sopenharmony_ci del_gendisk(xpram_disks[i]); 4378c2ecf20Sopenharmony_ci blk_cleanup_queue(xpram_queues[i]); 4388c2ecf20Sopenharmony_ci put_disk(xpram_disks[i]); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME); 4418c2ecf20Sopenharmony_ci platform_device_unregister(xpram_pdev); 4428c2ecf20Sopenharmony_ci platform_driver_unregister(&xpram_pdrv); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic int __init xpram_init(void) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci int rc; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* Find out size of expanded memory. */ 4508c2ecf20Sopenharmony_ci if (xpram_present() != 0) { 4518c2ecf20Sopenharmony_ci pr_err("No expanded memory available\n"); 4528c2ecf20Sopenharmony_ci return -ENODEV; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci xpram_pages = xpram_highest_page_index() + 1; 4558c2ecf20Sopenharmony_ci pr_info(" %u pages expanded memory found (%lu KB).\n", 4568c2ecf20Sopenharmony_ci xpram_pages, (unsigned long) xpram_pages*4); 4578c2ecf20Sopenharmony_ci rc = xpram_setup_sizes(xpram_pages); 4588c2ecf20Sopenharmony_ci if (rc) 4598c2ecf20Sopenharmony_ci return rc; 4608c2ecf20Sopenharmony_ci rc = platform_driver_register(&xpram_pdrv); 4618c2ecf20Sopenharmony_ci if (rc) 4628c2ecf20Sopenharmony_ci return rc; 4638c2ecf20Sopenharmony_ci xpram_pdev = platform_device_register_simple(XPRAM_NAME, -1, NULL, 0); 4648c2ecf20Sopenharmony_ci if (IS_ERR(xpram_pdev)) { 4658c2ecf20Sopenharmony_ci rc = PTR_ERR(xpram_pdev); 4668c2ecf20Sopenharmony_ci goto fail_platform_driver_unregister; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci rc = xpram_setup_blkdev(); 4698c2ecf20Sopenharmony_ci if (rc) 4708c2ecf20Sopenharmony_ci goto fail_platform_device_unregister; 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cifail_platform_device_unregister: 4748c2ecf20Sopenharmony_ci platform_device_unregister(xpram_pdev); 4758c2ecf20Sopenharmony_cifail_platform_driver_unregister: 4768c2ecf20Sopenharmony_ci platform_driver_unregister(&xpram_pdrv); 4778c2ecf20Sopenharmony_ci return rc; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cimodule_init(xpram_init); 4818c2ecf20Sopenharmony_cimodule_exit(xpram_exit); 482