/*
 * include/asm-s390/cmb.h
 * include/asm-s390x/cmb.h
 *
 *         Copyright (C) 2003 IBM Corporation
 *         Author: Arnd Bergmann <arndb@de.ibm.com>
 */

#ifndef S390_CMB_H
#define S390_CMB_H
/**
 * struct cmbdata -- channel measurement block data for user space
 *
 * @size:			size of the stored data
 * @elapsed_time:		time since last reset
 * @ssch_rsch_count:		number I/O attempts, may overflow at
 * 				2^16 or 2^32, depending on machine
 * @sample_count:		number of sampled I/O attempts, should
 * 				be identical to @ssch_rsch_count
 * @device_connect_time:	time that device communicated with
 * 				channel
 * @function_pending_time:	time between command initiate and
 * 				start of execution
 * @device_disconnect_time:	time of device-inactive while
 * 				subchannel-active
 * @control_unit_queuing_time:	time that a device is blocked by
 * 				I/O from another system
 * @device_active_only_time:	time of device-active while
 * 				subchannel-inactive
 * @device_busy_time:		time where the device is busy
 * 				when attemting to start I/O
 * @initial_command_response_time: time between sending and 
 * 				the device accepting a command
 *
 * all values are stored as 64 bit for simplicity, especially
 * in 32 bit emulation mode. All time values are normalized to
 * nanoseconds.
 * Currently, two formats are known, which differ by the size of
 * this structure, i.e. the last two members are only set when
 * the extended channel measurement facility (first shipped in
 * z990 machines) is activated.
 * Potentially, more fields could be added, which results in a
 * new ioctl number.
 **/
struct cmbdata {
	__u64 size;
	__u64 elapsed_time;
 /* basic and exended format: */
	__u64 ssch_rsch_count;
	__u64 sample_count;
	__u64 device_connect_time;
	__u64 function_pending_time;
	__u64 device_disconnect_time;
	__u64 control_unit_queuing_time;
	__u64 device_active_only_time;
 /* extended format only: */
	__u64 device_busy_time;
	__u64 initial_command_response_time;
};

/* enable channel measurement */
#define BIODASDCMFENABLE	_IO(DASD_IOCTL_LETTER,32)
/* enable channel measurement */
#define BIODASDCMFDISABLE	_IO(DASD_IOCTL_LETTER,33)
/* read channel measurement data */
#define BIODASDREADALLCMB	_IOWR(DASD_IOCTL_LETTER,33,struct cmbdata)

#ifdef __KERNEL__
#include <linux/config.h>
#include <asm/irq.h>

#if defined(CONFIG_S390_CMF) || defined(CONFIG_S390_CMF_MODULE)
/**
 * struct cmf_device - basic building block of the CMF code
 *
 * @cmb_list:	entry in the global list of cmf_devices
 * @ccwlock:	a pointer to the subchannel's spinlock
 * @cmb_start_time: clock value of previous cmf_reset operation
 * @cmb:	pointer to the hardware channel measurement block
 * @callback:	function pointer called by cmf_device_callback()
 * @irq:	subchannel number of the associated device
 */
struct cmf_device {
	struct list_head cmb_list;
	spinlock_t *ccwlock;
	u64 cmb_start_time;
	void *cmb;
	void (*callback)(struct cmf_device *);
	u16  irq;
};

/**
 * cmf_device_callback - call the callback function for set_cmf
 *
 * cmf_device_callback() can be integrated in an appropriate
 * point in the device driver where no I/O is ongoing on the
 * subchannel and it is safe to call set_cmf.
 * This is a nop when CONFIG_S390_CMF is disabled
 */
static inline void cmf_device_callback(struct cmf_device *cdev)
{
	if (cdev->callback) {
		cdev->callback(cdev);
	}
}

/**
 * cmf_device_init - initialize the cmf_device structure
 *
 * cmf_device_init() needs to be called before doing any other operations
 * on a cmf_device.
 */
static inline void cmf_device_init(struct cmf_device *cdev, int devno)
{
	INIT_LIST_HEAD (&cdev->cmb_list);
	cdev->irq = get_irq_by_devno(devno);
	cdev->ccwlock = get_irq_lock(cdev->irq);
}

#else /* !CONFIG_S390_CMF */
struct cmf_device { };
static inline void cmf_device_callback(struct cmf_device *cdev)        { }
static inline void cmf_device_init(struct cmf_device *cdev, int devno) { }
#endif

/**
 * enable_cmf() - switch on the channel measurement for a specific device
 *  @cdev:	The ccw device to be enabled
 *  returns 0 for success or a negative error value.
 *
 *  This function only allocates memory for the measurement block, the
 *  actual activation is done with set_cmf()
 *
 *  Context:
 *    may sleep, device is disabled
 **/
extern int enable_cmf(struct cmf_device *cdev);

/**
 * disable_cmf() - switch off the channel measurement for a specific device
 *  @cdev:	The ccw device to be disabled
 *  returns 0 for success or a negative error value.
 *
 *  This function only frees the memory allocated with enable_cmf. If
 *  measurement has been activated with set_cmf(), it also needs to be 
 *  deactivated with that function before calling disable_cmf(). 
 *
 *  Context:
 *    may sleep, device is enabled and inactive
 **/
extern void disable_cmf(struct cmf_device *cdev);

/**
 * cmf_readall() - read one value from the current channel measurement block
 * @cmf:	the device to be read
 * @data:	a pointer to a data block that will be filled
 *
 *  Context:
 *    device is active
 **/
extern int cmf_readall(struct cmf_device *cdev, struct cmbdata*data);

/**
 * set_cmf() - Set the measurement mode for a specific device
 * @cmf:	the device to be modified
 * @mme:	measurement mode enable value, can be either
 *		0 for deactivation or 2 for activation
 *
 * It is important to call this function only when there is no I/O
 * activity on the subchannel. Therefore it may be necessary to call
 * it from an interrupt handler at the point where the previous
 * request is finished.
 * In 2.6.x, set_cmf() is integrated within enable_cmf() and disable_cmf(),
 * which makes a lot of sense and life much easier for users.
 *
 *  Context:
 *    device lock held, device is enabled, subchannel is idle
 **/
extern int set_cmf(struct cmf_device *cdev, u32 mme);

/**
 * reset_cmf() - clear a channel measurement block
 * @cmf:	the device to be cleared
 *
 * This function is used to set all values in a channel measurement block
 * to sane values. It should be called between enable_cmf() and set_cmf(cdev,2)
 * but can be called as well when the device is already active.
 *
 *  Context:
 *    device is enabled
 **/
extern void cmf_reset(struct cmf_device *cdev);
#endif /* __KERNEL__ */
#endif /* S390_CMB_H */
