Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2b1e3e55 authored by Gerald Schaefer's avatar Gerald Schaefer Committed by Martin Schwidefsky
Browse files

[S390] pm: monreader power management callbacks.

parent fb78140c
Loading
Loading
Loading
Loading
+128 −12
Original line number Diff line number Diff line
/*
 * drivers/s390/char/monreader.c
 *
 * Character device driver for reading z/VM *MONITOR service records.
 *
 *   Copyright IBM Corp. 2004, 2008
 * Copyright IBM Corp. 2004, 2009
 *
 * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
 */

@@ -22,6 +21,7 @@
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <net/iucv/iucv.h>
#include <asm/uaccess.h>
#include <asm/ebcdic.h>
@@ -78,6 +78,7 @@ static u8 user_data_sever[16] = {
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};

static struct device *monreader_device;

/******************************************************************************
 *                             helper functions                               *
@@ -319,11 +320,12 @@ static int mon_open(struct inode *inode, struct file *filp)
		goto out_path;
	}
	filp->private_data = monpriv;
	monreader_device->driver_data = monpriv;
	unlock_kernel();
	return nonseekable_open(inode, filp);

out_path:
	kfree(monpriv->path);
	iucv_path_free(monpriv->path);
out_priv:
	mon_free_mem(monpriv);
out_use:
@@ -341,10 +343,13 @@ static int mon_close(struct inode *inode, struct file *filp)
	/*
	 * Close IUCV connection and unregister
	 */
	if (monpriv->path) {
		rc = iucv_path_sever(monpriv->path, user_data_sever);
		if (rc)
		pr_warning("Disconnecting the z/VM *MONITOR system service "
			   "failed with rc=%i\n", rc);
			pr_warning("Disconnecting the z/VM *MONITOR system "
				   "service failed with rc=%i\n", rc);
		iucv_path_free(monpriv->path);
	}

	atomic_set(&monpriv->iucv_severed, 0);
	atomic_set(&monpriv->iucv_connected, 0);
@@ -452,6 +457,94 @@ static struct miscdevice mon_dev = {
	.minor      = MISC_DYNAMIC_MINOR,
};


/******************************************************************************
 *				suspend / resume			      *
 *****************************************************************************/
static int monreader_freeze(struct device *dev)
{
	struct mon_private *monpriv = dev->driver_data;
	int rc;

	if (!monpriv)
		return 0;
	if (monpriv->path) {
		rc = iucv_path_sever(monpriv->path, user_data_sever);
		if (rc)
			pr_warning("Disconnecting the z/VM *MONITOR system "
				   "service failed with rc=%i\n", rc);
		iucv_path_free(monpriv->path);
	}
	atomic_set(&monpriv->iucv_severed, 0);
	atomic_set(&monpriv->iucv_connected, 0);
	atomic_set(&monpriv->read_ready, 0);
	atomic_set(&monpriv->msglim_count, 0);
	monpriv->write_index  = 0;
	monpriv->read_index   = 0;
	monpriv->path = NULL;
	return 0;
}

static int monreader_thaw(struct device *dev)
{
	struct mon_private *monpriv = dev->driver_data;
	int rc;

	if (!monpriv)
		return 0;
	rc = -ENOMEM;
	monpriv->path = iucv_path_alloc(MON_MSGLIM, IUCV_IPRMDATA, GFP_KERNEL);
	if (!monpriv->path)
		goto out;
	rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler,
			       MON_SERVICE, NULL, user_data_connect, monpriv);
	if (rc) {
		pr_err("Connecting to the z/VM *MONITOR system service "
		       "failed with rc=%i\n", rc);
		goto out_path;
	}
	wait_event(mon_conn_wait_queue,
		   atomic_read(&monpriv->iucv_connected) ||
		   atomic_read(&monpriv->iucv_severed));
	if (atomic_read(&monpriv->iucv_severed))
		goto out_path;
	return 0;
out_path:
	rc = -EIO;
	iucv_path_free(monpriv->path);
	monpriv->path = NULL;
out:
	atomic_set(&monpriv->iucv_severed, 1);
	return rc;
}

static int monreader_restore(struct device *dev)
{
	int rc;

	segment_unload(mon_dcss_name);
	rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
			  &mon_dcss_start, &mon_dcss_end);
	if (rc < 0) {
		segment_warning(rc, mon_dcss_name);
		panic("fatal monreader resume error: no monitor dcss\n");
	}
	return monreader_thaw(dev);
}

static struct dev_pm_ops monreader_pm_ops = {
	.freeze  = monreader_freeze,
	.thaw	 = monreader_thaw,
	.restore = monreader_restore,
};

static struct device_driver monreader_driver = {
	.name = "monreader",
	.bus  = &iucv_bus,
	.pm   = &monreader_pm_ops,
};


/******************************************************************************
 *                              module init/exit                              *
 *****************************************************************************/
@@ -475,16 +568,33 @@ static int __init mon_init(void)
		return rc;
	}

	rc = driver_register(&monreader_driver);
	if (rc)
		goto out_iucv;
	monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL);
	if (!monreader_device)
		goto out_driver;
	dev_set_name(monreader_device, "monreader-dev");
	monreader_device->bus = &iucv_bus;
	monreader_device->parent = iucv_root;
	monreader_device->driver = &monreader_driver;
	monreader_device->release = (void (*)(struct device *))kfree;
	rc = device_register(monreader_device);
	if (rc) {
		kfree(monreader_device);
		goto out_driver;
	}

	rc = segment_type(mon_dcss_name);
	if (rc < 0) {
		segment_warning(rc, mon_dcss_name);
		goto out_iucv;
		goto out_device;
	}
	if (rc != SEG_TYPE_SC) {
		pr_err("The specified *MONITOR DCSS %s does not have the "
		       "required type SC\n", mon_dcss_name);
		rc = -EINVAL;
		goto out_iucv;
		goto out_device;
	}

	rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
@@ -492,7 +602,7 @@ static int __init mon_init(void)
	if (rc < 0) {
		segment_warning(rc, mon_dcss_name);
		rc = -EINVAL;
		goto out_iucv;
		goto out_device;
	}
	dcss_mkname(mon_dcss_name, &user_data_connect[8]);

@@ -503,6 +613,10 @@ static int __init mon_init(void)

out:
	segment_unload(mon_dcss_name);
out_device:
	device_unregister(monreader_device);
out_driver:
	driver_unregister(&monreader_driver);
out_iucv:
	iucv_unregister(&monreader_iucv_handler, 1);
	return rc;
@@ -512,6 +626,8 @@ static void __exit mon_exit(void)
{
	segment_unload(mon_dcss_name);
	WARN_ON(misc_deregister(&mon_dev) != 0);
	device_unregister(monreader_device);
	driver_unregister(&monreader_driver);
	iucv_unregister(&monreader_iucv_handler, 1);
	return;
}