11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Xpram.c -- the S/390 expanded memory RAM-disk
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * significant parts of this code are based on
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the sbull device driver presented in
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * A. Rubini: Linux Device Drivers
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Author of XPRAM specific coding: Reinhard Buendgen
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *                                  buendgen@de.ibm.com
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Rewrite for 2.5: Martin Schwidefsky <schwidefsky@de.ibm.com>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * External interfaces:
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   Interfaces to linux kernel
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *        xpram_setup: read kernel parameters
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   Device specific file operations
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *        xpram_iotcl
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *        xpram_open
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * "ad-hoc" partitioning:
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *    the expanded memory can be partitioned among several devices
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *    (with different minors). The partitioning set up can be
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *    set by kernel or module parameters (int devs & int sizes[])
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Potential future improvements:
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   generic hard disk support to replace ad-hoc partitioning
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky#define KMSG_COMPONENT "xpram"
29d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
30d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleparam.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ctype.h>  /* isdigit, isxdigit */
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/blkdev.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/blkpg.h>
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/hdreg.h>  /* HDIO_GETGEO */
39edbaa603eb801655e80808a9cf3d3b622e8ac66bKay Sievers#include <linux/device.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/bio.h>
4114532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu#include <linux/suspend.h>
4214532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu#include <linux/platform_device.h>
435a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h>
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define XPRAM_NAME	"xpram"
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define XPRAM_DEVS	1	/* one partition */
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define XPRAM_MAX_DEVS	32	/* maximal number of devices (partitions) */
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldstypedef struct {
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int	size;		/* size of xpram segment in pages */
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int	offset;		/* start page of xpram segment */
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} xpram_device_t;
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic xpram_device_t xpram_devices[XPRAM_MAX_DEVS];
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int xpram_sizes[XPRAM_MAX_DEVS];
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct gendisk *xpram_disks[XPRAM_MAX_DEVS];
583ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefskystatic struct request_queue *xpram_queues[XPRAM_MAX_DEVS];
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int xpram_pages;
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int xpram_devs;
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Parameter parsing functions.
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
650c0db0355bc070b4c623622248d3f577642536b9Martin Schwidefskystatic int devs = XPRAM_DEVS;
660c0db0355bc070b4c623622248d3f577642536b9Martin Schwidefskystatic char *sizes[XPRAM_MAX_DEVS];
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(devs, int, 0);
695c898ba9d4b6c14fdd367b96e3641c2508b4a4a9Heiko Carstensmodule_param_array(sizes, charp, NULL, 0);
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(devs, "number of devices (\"partitions\"), " \
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 "the default is " __MODULE_STRING(XPRAM_DEVS) "\n");
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(sizes, "list of device (partition) sizes " \
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 "the defaults are 0s \n" \
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 "All devices with size 0 equally partition the "
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 "remaining space on the expanded strorage not "
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 "claimed by explicit sizes\n");
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copy expanded memory page (4kB) into main memory
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Arguments
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *           page_addr:    address of target page
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *           xpage_index:  index of expandeded memory page
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Return value
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *           0:            if operation succeeds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *           -EIO:         if pgin failed
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *           -ENXIO:       if xpram has vanished
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int xpram_page_in (unsigned long page_addr, unsigned int xpage_index)
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9294c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky	int cc = 2;	/* return unused cc 2 if pgin traps */
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9494c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky	asm volatile(
9594c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky		"	.insn	rre,0xb22e0000,%1,%2\n"  /* pgin %1,%2 */
9694c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky		"0:	ipm	%0\n"
9794c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky		"	srl	%0,28\n"
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		"1:\n"
9994c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky		EX_TABLE(0b,1b)
10094c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky		: "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc");
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cc == 3)
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENXIO;
1038df22b4b39c6a1679f471bd68f97cf6f6819571aMartin Schwidefsky	if (cc == 2)
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENXIO;
1058df22b4b39c6a1679f471bd68f97cf6f6819571aMartin Schwidefsky	if (cc == 1)
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO;
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copy a 4kB page of main memory to an expanded memory page
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Arguments
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *           page_addr:    address of source page
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *           xpage_index:  index of expandeded memory page
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Return value
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *           0:            if operation succeeds
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *           -EIO:         if pgout failed
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *           -ENXIO:       if xpram has vanished
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic long xpram_page_out (unsigned long page_addr, unsigned int xpage_index)
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
12294c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky	int cc = 2;	/* return unused cc 2 if pgin traps */
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12494c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky	asm volatile(
12594c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky		"	.insn	rre,0xb22f0000,%1,%2\n"  /* pgout %1,%2 */
12694c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky		"0:	ipm	%0\n"
12794c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky		"	srl	%0,28\n"
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		"1:\n"
12994c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky		EX_TABLE(0b,1b)
13094c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky		: "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc");
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cc == 3)
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENXIO;
1338df22b4b39c6a1679f471bd68f97cf6f6819571aMartin Schwidefsky	if (cc == 2)
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENXIO;
1358df22b4b39c6a1679f471bd68f97cf6f6819571aMartin Schwidefsky	if (cc == 1)
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO;
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Check if xpram is available.
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14314532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheustatic int xpram_present(void)
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long mem_page;
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mem_page = (unsigned long) __get_free_page(GFP_KERNEL);
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!mem_page)
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = xpram_page_in(mem_page, 0);
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_page(mem_page);
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc ? -ENXIO : 0;
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Return index of the last available xpram page.
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
15914532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheustatic unsigned long xpram_highest_page_index(void)
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int page_index, add_bit;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long mem_page;
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mem_page = (unsigned long) __get_free_page(GFP_KERNEL);
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!mem_page)
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	page_index = 0;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	add_bit = 1ULL << (sizeof(unsigned int)*8 - 1);
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (add_bit > 0) {
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (xpram_page_in(mem_page, page_index | add_bit) == 0)
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			page_index |= add_bit;
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		add_bit >>= 1;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_page (mem_page);
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return page_index;
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Block device make request function.
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1845a7bbad27a410350e64a2d7f5ec18fc73836c14fChristoph Hellwigstatic void xpram_make_request(struct request_queue *q, struct bio *bio)
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xpram_device_t *xdev = bio->bi_bdev->bd_disk->private_data;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct bio_vec *bvec;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int index;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long page_addr;
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long bytes;
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0)
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Request is not page-aligned. */
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto fail;
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((bio->bi_size >> 12) > xdev->size)
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Request size is no page-aligned. */
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto fail;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((bio->bi_sector >> 3) > 0xffffffffU - xdev->offset)
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto fail;
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	index = (bio->bi_sector >> 3) + xdev->offset;
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	bio_for_each_segment(bvec, bio, i) {
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		page_addr = (unsigned long)
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kmap(bvec->bv_page) + bvec->bv_offset;
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		bytes = bvec->bv_len;
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((page_addr & 4095) != 0 || (bytes & 4095) != 0)
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* More paranoia. */
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto fail;
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (bytes > 0) {
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (bio_data_dir(bio) == READ) {
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (xpram_page_in(page_addr, index) != 0)
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					goto fail;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else {
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (xpram_page_out(page_addr, index) != 0)
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					goto fail;
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			page_addr += 4096;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			bytes -= 4096;
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			index++;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set_bit(BIO_UPTODATE, &bio->bi_flags);
2231462222b76a09a24b240563a51d5f9fbea8bd3e1Martin Schwidefsky	bio_endio(bio, 0);
2245a7bbad27a410350e64a2d7f5ec18fc73836c14fChristoph Hellwig	return;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfail:
2266712ecf8f648118c3363c142196418f89a510b90NeilBrown	bio_io_error(bio);
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
229a885c8c4316e1c1d2d2c8755da3f3d14f852528dChristoph Hellwigstatic int xpram_getgeo(struct block_device *bdev, struct hd_geometry *geo)
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long size;
232a885c8c4316e1c1d2d2c8755da3f3d14f852528dChristoph Hellwig
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * get geometry: we have to fake one...  trim the size to a
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * multiple of 64 (32k): tell we have 16 sectors, 4 heads,
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * whatever cylinders. Tell also that data starts at sector. 4.
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size = (xpram_pages * 8) & ~0x3f;
239a885c8c4316e1c1d2d2c8755da3f3d14f852528dChristoph Hellwig	geo->cylinders = size >> 6;
240a885c8c4316e1c1d2d2c8755da3f3d14f852528dChristoph Hellwig	geo->heads = 4;
241a885c8c4316e1c1d2d2c8755da3f3d14f852528dChristoph Hellwig	geo->sectors = 16;
242a885c8c4316e1c1d2d2c8755da3f3d14f852528dChristoph Hellwig	geo->start = 4;
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24683d5cde47dedf01b6a4a4331882cbc0a7eea3c2eAlexey Dobriyanstatic const struct block_device_operations xpram_devops =
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner	= THIS_MODULE,
249a885c8c4316e1c1d2d2c8755da3f3d14f852528dChristoph Hellwig	.getgeo	= xpram_getgeo,
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Setup xpram_sizes array.
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init xpram_setup_sizes(unsigned long pages)
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long mem_needed;
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long mem_auto;
259f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens	unsigned long long size;
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int mem_auto_no;
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check number of devices. */
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (devs <= 0 || devs > XPRAM_MAX_DEVS) {
265d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky		pr_err("%d is not a valid number of XPRAM devices\n",devs);
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	xpram_devs = devs;
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Copy sizes array to xpram_sizes and align partition
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * sizes to page boundary.
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mem_needed = 0;
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mem_auto_no = 0;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < xpram_devs; i++) {
277f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens		if (sizes[i]) {
278f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens			size = simple_strtoull(sizes[i], &sizes[i], 0);
279f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens			switch (sizes[i][0]) {
280f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens			case 'g':
281f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens			case 'G':
282f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens				size <<= 20;
283f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens				break;
284f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens			case 'm':
285f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens			case 'M':
286f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens				size <<= 10;
287f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens			}
288f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens			xpram_sizes[i] = (size + 3) & -4UL;
289f257b06322c8a30c050a286c45fda68f23b9bc44Heiko Carstens		}
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (xpram_sizes[i])
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			mem_needed += xpram_sizes[i];
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			mem_auto_no++;
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
296d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky	pr_info("  number of devices (partitions): %d \n", xpram_devs);
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < xpram_devs; i++) {
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (xpram_sizes[i])
299d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky			pr_info("  size of partition %d: %u kB\n",
300d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky				i, xpram_sizes[i]);
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
302d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky			pr_info("  size of partition %d to be set "
303d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky				"automatically\n",i);
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
305d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky	pr_info("  memory needed (for sized partitions): %lu kB\n",
306d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky		mem_needed);
307d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky	pr_info("  partitions to be sized automatically: %d\n",
308d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky		mem_auto_no);
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (mem_needed > pages * 4) {
311d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky		pr_err("Not enough expanded memory available\n");
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * partitioning:
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * xpram_sizes[i] != 0; partition i has size xpram_sizes[i] kB
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * else:             ; all partitions with zero xpram_sizes[i]
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *                     partition equally the remaining space
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (mem_auto_no) {
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		mem_auto = ((pages - mem_needed / 4) / mem_auto_no) * 4;
323d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky		pr_info("  automatically determined "
324d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky			"partition size: %lu kB\n", mem_auto);
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < xpram_devs; i++)
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (xpram_sizes[i] == 0)
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				xpram_sizes[i] = mem_auto;
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init xpram_setup_blkdev(void)
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long offset;
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, rc = -ENOMEM;
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < xpram_devs; i++) {
3383ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky		xpram_disks[i] = alloc_disk(1);
3393ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky		if (!xpram_disks[i])
3403ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky			goto out;
3413ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky		xpram_queues[i] = blk_alloc_queue(GFP_KERNEL);
3423ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky		if (!xpram_queues[i]) {
3433ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky			put_disk(xpram_disks[i]);
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
3453ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky		}
3463ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky		blk_queue_make_request(xpram_queues[i], xpram_make_request);
347e1defc4ff0cf57aca6c5e3ff99fa503f5943c1f1Martin K. Petersen		blk_queue_logical_block_size(xpram_queues[i], 4096);
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Register xpram major.
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = register_blkdev(XPRAM_MAJOR, XPRAM_NAME);
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc < 0)
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Setup device structures.
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	offset = 0;
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < xpram_devs; i++) {
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct gendisk *disk = xpram_disks[i];
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		xpram_devices[i].size = xpram_sizes[i] / 4;
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		xpram_devices[i].offset = offset;
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		offset += xpram_devices[i].size;
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		disk->major = XPRAM_MAJOR;
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		disk->first_minor = i;
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		disk->fops = &xpram_devops;
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		disk->private_data = &xpram_devices[i];
3713ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky		disk->queue = xpram_queues[i];
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sprintf(disk->disk_name, "slram%d", i);
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		set_capacity(disk, xpram_sizes[i] << 1);
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		add_disk(disk);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
3793ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky	while (i--) {
3803ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky		blk_cleanup_queue(xpram_queues[i]);
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_disk(xpram_disks[i]);
3823ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky	}
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
38714532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu * Resume failed: Print error message and call panic.
38814532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu */
38914532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheustatic void xpram_resume_error(const char *message)
39014532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu{
3912c48c4d631970b70d60a4f926b0f68f194a0d559Christian Borntraeger	pr_err("Resuming the system failed: %s\n", message);
39214532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	panic("xpram resume error\n");
39314532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu}
39414532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu
39514532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu/*
39614532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu * Check if xpram setup changed between suspend and resume.
39714532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu */
39814532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheustatic int xpram_restore(struct device *dev)
39914532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu{
40014532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	if (!xpram_pages)
40114532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu		return 0;
40214532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	if (xpram_present() != 0)
40314532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu		xpram_resume_error("xpram disappeared");
40414532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	if (xpram_pages != xpram_highest_page_index() + 1)
40514532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu		xpram_resume_error("Size of xpram changed");
40614532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	return 0;
40714532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu}
40814532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu
409471452104b8520337ae2fb48c4e61cd4896e025dAlexey Dobriyanstatic const struct dev_pm_ops xpram_pm_ops = {
41014532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	.restore	= xpram_restore,
41114532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu};
41214532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu
41314532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheustatic struct platform_driver xpram_pdrv = {
41414532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	.driver = {
41514532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu		.name	= XPRAM_NAME,
41614532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu		.owner	= THIS_MODULE,
41714532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu		.pm	= &xpram_pm_ops,
41814532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	},
41914532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu};
42014532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu
42114532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheustatic struct platform_device *xpram_pdev;
42214532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu
42314532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu/*
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Finally, the init/exit functions.
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit xpram_exit(void)
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < xpram_devs; i++) {
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		del_gendisk(xpram_disks[i]);
4313ce66093f5ec5a6ae0ca90c79d81eee15e842293Martin Schwidefsky		blk_cleanup_queue(xpram_queues[i]);
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		put_disk(xpram_disks[i]);
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME);
43514532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	platform_device_unregister(xpram_pdev);
43614532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	platform_driver_unregister(&xpram_pdrv);
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init xpram_init(void)
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Find out size of expanded memory. */
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (xpram_present() != 0) {
445d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky		pr_err("No expanded memory available\n");
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
448e620c4940002348417e8d317d65bc7b152646493Christian Borntraeger	xpram_pages = xpram_highest_page_index() + 1;
449d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky	pr_info("  %u pages expanded memory found (%lu KB).\n",
450d1c2f8928a2feab56a181bb7e9afe8f804fd26cdMartin Schwidefsky		xpram_pages, (unsigned long) xpram_pages*4);
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = xpram_setup_sizes(xpram_pages);
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return rc;
45414532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	rc = platform_driver_register(&xpram_pdrv);
45514532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	if (rc)
45614532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu		return rc;
45714532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	xpram_pdev = platform_device_register_simple(XPRAM_NAME, -1, NULL, 0);
45814532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	if (IS_ERR(xpram_pdev)) {
45914532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu		rc = PTR_ERR(xpram_pdev);
46014532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu		goto fail_platform_driver_unregister;
46114532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	}
46214532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	rc = xpram_setup_blkdev();
46314532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	if (rc)
46414532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu		goto fail_platform_device_unregister;
46514532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	return 0;
46614532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu
46714532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheufail_platform_device_unregister:
46814532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	platform_device_unregister(xpram_pdev);
46914532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheufail_platform_driver_unregister:
47014532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	platform_driver_unregister(&xpram_pdrv);
47114532095dfe9e8faf2d314d9c2170f64737c7dffMichael Holzheu	return rc;
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(xpram_init);
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(xpram_exit);
476