162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright IBM Corp. 2002, 2009 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) 662306a36Sopenharmony_ci * Cornelia Huck (cornelia.huck@de.ibm.com) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/export.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/list.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/completion.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/ccwdev.h> 1862306a36Sopenharmony_ci#include <asm/idals.h> 1962306a36Sopenharmony_ci#include <asm/chpid.h> 2062306a36Sopenharmony_ci#include <asm/fcx.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "cio.h" 2362306a36Sopenharmony_ci#include "cio_debug.h" 2462306a36Sopenharmony_ci#include "css.h" 2562306a36Sopenharmony_ci#include "chsc.h" 2662306a36Sopenharmony_ci#include "device.h" 2762306a36Sopenharmony_ci#include "chp.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/** 3062306a36Sopenharmony_ci * ccw_device_set_options_mask() - set some options and unset the rest 3162306a36Sopenharmony_ci * @cdev: device for which the options are to be set 3262306a36Sopenharmony_ci * @flags: options to be set 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * All flags specified in @flags are set, all flags not specified in @flags 3562306a36Sopenharmony_ci * are cleared. 3662306a36Sopenharmony_ci * Returns: 3762306a36Sopenharmony_ci * %0 on success, -%EINVAL on an invalid flag combination. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ciint ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci /* 4262306a36Sopenharmony_ci * The flag usage is mutal exclusive ... 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci if ((flags & CCWDEV_EARLY_NOTIFICATION) && 4562306a36Sopenharmony_ci (flags & CCWDEV_REPORT_ALL)) 4662306a36Sopenharmony_ci return -EINVAL; 4762306a36Sopenharmony_ci cdev->private->options.fast = (flags & CCWDEV_EARLY_NOTIFICATION) != 0; 4862306a36Sopenharmony_ci cdev->private->options.repall = (flags & CCWDEV_REPORT_ALL) != 0; 4962306a36Sopenharmony_ci cdev->private->options.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0; 5062306a36Sopenharmony_ci cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0; 5162306a36Sopenharmony_ci cdev->private->options.mpath = (flags & CCWDEV_DO_MULTIPATH) != 0; 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/** 5662306a36Sopenharmony_ci * ccw_device_set_options() - set some options 5762306a36Sopenharmony_ci * @cdev: device for which the options are to be set 5862306a36Sopenharmony_ci * @flags: options to be set 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * All flags specified in @flags are set, the remainder is left untouched. 6162306a36Sopenharmony_ci * Returns: 6262306a36Sopenharmony_ci * %0 on success, -%EINVAL if an invalid flag combination would ensue. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ciint ccw_device_set_options(struct ccw_device *cdev, unsigned long flags) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci /* 6762306a36Sopenharmony_ci * The flag usage is mutal exclusive ... 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci if (((flags & CCWDEV_EARLY_NOTIFICATION) && 7062306a36Sopenharmony_ci (flags & CCWDEV_REPORT_ALL)) || 7162306a36Sopenharmony_ci ((flags & CCWDEV_EARLY_NOTIFICATION) && 7262306a36Sopenharmony_ci cdev->private->options.repall) || 7362306a36Sopenharmony_ci ((flags & CCWDEV_REPORT_ALL) && 7462306a36Sopenharmony_ci cdev->private->options.fast)) 7562306a36Sopenharmony_ci return -EINVAL; 7662306a36Sopenharmony_ci cdev->private->options.fast |= (flags & CCWDEV_EARLY_NOTIFICATION) != 0; 7762306a36Sopenharmony_ci cdev->private->options.repall |= (flags & CCWDEV_REPORT_ALL) != 0; 7862306a36Sopenharmony_ci cdev->private->options.pgroup |= (flags & CCWDEV_DO_PATHGROUP) != 0; 7962306a36Sopenharmony_ci cdev->private->options.force |= (flags & CCWDEV_ALLOW_FORCE) != 0; 8062306a36Sopenharmony_ci cdev->private->options.mpath |= (flags & CCWDEV_DO_MULTIPATH) != 0; 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/** 8562306a36Sopenharmony_ci * ccw_device_clear_options() - clear some options 8662306a36Sopenharmony_ci * @cdev: device for which the options are to be cleared 8762306a36Sopenharmony_ci * @flags: options to be cleared 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * All flags specified in @flags are cleared, the remainder is left untouched. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_civoid ccw_device_clear_options(struct ccw_device *cdev, unsigned long flags) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci cdev->private->options.fast &= (flags & CCWDEV_EARLY_NOTIFICATION) == 0; 9462306a36Sopenharmony_ci cdev->private->options.repall &= (flags & CCWDEV_REPORT_ALL) == 0; 9562306a36Sopenharmony_ci cdev->private->options.pgroup &= (flags & CCWDEV_DO_PATHGROUP) == 0; 9662306a36Sopenharmony_ci cdev->private->options.force &= (flags & CCWDEV_ALLOW_FORCE) == 0; 9762306a36Sopenharmony_ci cdev->private->options.mpath &= (flags & CCWDEV_DO_MULTIPATH) == 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/** 10162306a36Sopenharmony_ci * ccw_device_is_pathgroup() - determine if paths to this device are grouped 10262306a36Sopenharmony_ci * @cdev: ccw device 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * Return non-zero if there is a path group, zero otherwise. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ciint ccw_device_is_pathgroup(struct ccw_device *cdev) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci return cdev->private->flags.pgroup; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_is_pathgroup); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/** 11362306a36Sopenharmony_ci * ccw_device_is_multipath() - determine if device is operating in multipath mode 11462306a36Sopenharmony_ci * @cdev: ccw device 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * Return non-zero if device is operating in multipath mode, zero otherwise. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ciint ccw_device_is_multipath(struct ccw_device *cdev) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci return cdev->private->flags.mpath; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_is_multipath); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/** 12562306a36Sopenharmony_ci * ccw_device_clear() - terminate I/O request processing 12662306a36Sopenharmony_ci * @cdev: target ccw device 12762306a36Sopenharmony_ci * @intparm: interruption parameter to be returned upon conclusion of csch 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * ccw_device_clear() calls csch on @cdev's subchannel. 13062306a36Sopenharmony_ci * Returns: 13162306a36Sopenharmony_ci * %0 on success, 13262306a36Sopenharmony_ci * -%ENODEV on device not operational, 13362306a36Sopenharmony_ci * -%EINVAL on invalid device state. 13462306a36Sopenharmony_ci * Context: 13562306a36Sopenharmony_ci * Interrupts disabled, ccw device lock held 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ciint ccw_device_clear(struct ccw_device *cdev, unsigned long intparm) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct subchannel *sch; 14062306a36Sopenharmony_ci int ret; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (!cdev || !cdev->dev.parent) 14362306a36Sopenharmony_ci return -ENODEV; 14462306a36Sopenharmony_ci sch = to_subchannel(cdev->dev.parent); 14562306a36Sopenharmony_ci if (!sch->schib.pmcw.ena) 14662306a36Sopenharmony_ci return -EINVAL; 14762306a36Sopenharmony_ci if (cdev->private->state == DEV_STATE_NOT_OPER) 14862306a36Sopenharmony_ci return -ENODEV; 14962306a36Sopenharmony_ci if (cdev->private->state != DEV_STATE_ONLINE && 15062306a36Sopenharmony_ci cdev->private->state != DEV_STATE_W4SENSE) 15162306a36Sopenharmony_ci return -EINVAL; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci ret = cio_clear(sch); 15462306a36Sopenharmony_ci if (ret == 0) 15562306a36Sopenharmony_ci cdev->private->intparm = intparm; 15662306a36Sopenharmony_ci return ret; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/** 16062306a36Sopenharmony_ci * ccw_device_start_timeout_key() - start a s390 channel program with timeout and key 16162306a36Sopenharmony_ci * @cdev: target ccw device 16262306a36Sopenharmony_ci * @cpa: logical start address of channel program 16362306a36Sopenharmony_ci * @intparm: user specific interruption parameter; will be presented back to 16462306a36Sopenharmony_ci * @cdev's interrupt handler. Allows a device driver to associate 16562306a36Sopenharmony_ci * the interrupt with a particular I/O request. 16662306a36Sopenharmony_ci * @lpm: defines the channel path to be used for a specific I/O request. A 16762306a36Sopenharmony_ci * value of 0 will make cio use the opm. 16862306a36Sopenharmony_ci * @key: storage key to be used for the I/O 16962306a36Sopenharmony_ci * @flags: additional flags; defines the action to be performed for I/O 17062306a36Sopenharmony_ci * processing. 17162306a36Sopenharmony_ci * @expires: timeout value in jiffies 17262306a36Sopenharmony_ci * 17362306a36Sopenharmony_ci * Start a S/390 channel program. When the interrupt arrives, the 17462306a36Sopenharmony_ci * IRQ handler is called, either immediately, delayed (dev-end missing, 17562306a36Sopenharmony_ci * or sense required) or never (no IRQ handler registered). 17662306a36Sopenharmony_ci * This function notifies the device driver if the channel program has not 17762306a36Sopenharmony_ci * completed during the time specified by @expires. If a timeout occurs, the 17862306a36Sopenharmony_ci * channel program is terminated via xsch, hsch or csch, and the device's 17962306a36Sopenharmony_ci * interrupt handler will be called with an irb containing ERR_PTR(-%ETIMEDOUT). 18062306a36Sopenharmony_ci * The interruption handler will echo back the @intparm specified here, unless 18162306a36Sopenharmony_ci * another interruption parameter is specified by a subsequent invocation of 18262306a36Sopenharmony_ci * ccw_device_halt() or ccw_device_clear(). 18362306a36Sopenharmony_ci * Returns: 18462306a36Sopenharmony_ci * %0, if the operation was successful; 18562306a36Sopenharmony_ci * -%EBUSY, if the device is busy, or status pending; 18662306a36Sopenharmony_ci * -%EACCES, if no path specified in @lpm is operational; 18762306a36Sopenharmony_ci * -%ENODEV, if the device is not operational. 18862306a36Sopenharmony_ci * Context: 18962306a36Sopenharmony_ci * Interrupts disabled, ccw device lock held 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ciint ccw_device_start_timeout_key(struct ccw_device *cdev, struct ccw1 *cpa, 19262306a36Sopenharmony_ci unsigned long intparm, __u8 lpm, __u8 key, 19362306a36Sopenharmony_ci unsigned long flags, int expires) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct subchannel *sch; 19662306a36Sopenharmony_ci int ret; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (!cdev || !cdev->dev.parent) 19962306a36Sopenharmony_ci return -ENODEV; 20062306a36Sopenharmony_ci sch = to_subchannel(cdev->dev.parent); 20162306a36Sopenharmony_ci if (!sch->schib.pmcw.ena) 20262306a36Sopenharmony_ci return -EINVAL; 20362306a36Sopenharmony_ci if (cdev->private->state == DEV_STATE_NOT_OPER) 20462306a36Sopenharmony_ci return -ENODEV; 20562306a36Sopenharmony_ci if (cdev->private->state == DEV_STATE_VERIFY || 20662306a36Sopenharmony_ci cdev->private->flags.doverify) { 20762306a36Sopenharmony_ci /* Remember to fake irb when finished. */ 20862306a36Sopenharmony_ci if (!cdev->private->flags.fake_irb) { 20962306a36Sopenharmony_ci cdev->private->flags.fake_irb = FAKE_CMD_IRB; 21062306a36Sopenharmony_ci cdev->private->intparm = intparm; 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci } else 21362306a36Sopenharmony_ci /* There's already a fake I/O around. */ 21462306a36Sopenharmony_ci return -EBUSY; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci if (cdev->private->state != DEV_STATE_ONLINE || 21762306a36Sopenharmony_ci ((sch->schib.scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) && 21862306a36Sopenharmony_ci !(sch->schib.scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS))) 21962306a36Sopenharmony_ci return -EBUSY; 22062306a36Sopenharmony_ci ret = cio_set_options (sch, flags); 22162306a36Sopenharmony_ci if (ret) 22262306a36Sopenharmony_ci return ret; 22362306a36Sopenharmony_ci /* Adjust requested path mask to exclude unusable paths. */ 22462306a36Sopenharmony_ci if (lpm) { 22562306a36Sopenharmony_ci lpm &= sch->lpm; 22662306a36Sopenharmony_ci if (lpm == 0) 22762306a36Sopenharmony_ci return -EACCES; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci ret = cio_start_key (sch, cpa, lpm, key); 23062306a36Sopenharmony_ci switch (ret) { 23162306a36Sopenharmony_ci case 0: 23262306a36Sopenharmony_ci cdev->private->intparm = intparm; 23362306a36Sopenharmony_ci if (expires) 23462306a36Sopenharmony_ci ccw_device_set_timeout(cdev, expires); 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci case -EACCES: 23762306a36Sopenharmony_ci case -ENODEV: 23862306a36Sopenharmony_ci dev_fsm_event(cdev, DEV_EVENT_VERIFY); 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci return ret; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/** 24562306a36Sopenharmony_ci * ccw_device_start_key() - start a s390 channel program with key 24662306a36Sopenharmony_ci * @cdev: target ccw device 24762306a36Sopenharmony_ci * @cpa: logical start address of channel program 24862306a36Sopenharmony_ci * @intparm: user specific interruption parameter; will be presented back to 24962306a36Sopenharmony_ci * @cdev's interrupt handler. Allows a device driver to associate 25062306a36Sopenharmony_ci * the interrupt with a particular I/O request. 25162306a36Sopenharmony_ci * @lpm: defines the channel path to be used for a specific I/O request. A 25262306a36Sopenharmony_ci * value of 0 will make cio use the opm. 25362306a36Sopenharmony_ci * @key: storage key to be used for the I/O 25462306a36Sopenharmony_ci * @flags: additional flags; defines the action to be performed for I/O 25562306a36Sopenharmony_ci * processing. 25662306a36Sopenharmony_ci * 25762306a36Sopenharmony_ci * Start a S/390 channel program. When the interrupt arrives, the 25862306a36Sopenharmony_ci * IRQ handler is called, either immediately, delayed (dev-end missing, 25962306a36Sopenharmony_ci * or sense required) or never (no IRQ handler registered). 26062306a36Sopenharmony_ci * The interruption handler will echo back the @intparm specified here, unless 26162306a36Sopenharmony_ci * another interruption parameter is specified by a subsequent invocation of 26262306a36Sopenharmony_ci * ccw_device_halt() or ccw_device_clear(). 26362306a36Sopenharmony_ci * Returns: 26462306a36Sopenharmony_ci * %0, if the operation was successful; 26562306a36Sopenharmony_ci * -%EBUSY, if the device is busy, or status pending; 26662306a36Sopenharmony_ci * -%EACCES, if no path specified in @lpm is operational; 26762306a36Sopenharmony_ci * -%ENODEV, if the device is not operational. 26862306a36Sopenharmony_ci * Context: 26962306a36Sopenharmony_ci * Interrupts disabled, ccw device lock held 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_ciint ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, 27262306a36Sopenharmony_ci unsigned long intparm, __u8 lpm, __u8 key, 27362306a36Sopenharmony_ci unsigned long flags) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci return ccw_device_start_timeout_key(cdev, cpa, intparm, lpm, key, 27662306a36Sopenharmony_ci flags, 0); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/** 28062306a36Sopenharmony_ci * ccw_device_start() - start a s390 channel program 28162306a36Sopenharmony_ci * @cdev: target ccw device 28262306a36Sopenharmony_ci * @cpa: logical start address of channel program 28362306a36Sopenharmony_ci * @intparm: user specific interruption parameter; will be presented back to 28462306a36Sopenharmony_ci * @cdev's interrupt handler. Allows a device driver to associate 28562306a36Sopenharmony_ci * the interrupt with a particular I/O request. 28662306a36Sopenharmony_ci * @lpm: defines the channel path to be used for a specific I/O request. A 28762306a36Sopenharmony_ci * value of 0 will make cio use the opm. 28862306a36Sopenharmony_ci * @flags: additional flags; defines the action to be performed for I/O 28962306a36Sopenharmony_ci * processing. 29062306a36Sopenharmony_ci * 29162306a36Sopenharmony_ci * Start a S/390 channel program. When the interrupt arrives, the 29262306a36Sopenharmony_ci * IRQ handler is called, either immediately, delayed (dev-end missing, 29362306a36Sopenharmony_ci * or sense required) or never (no IRQ handler registered). 29462306a36Sopenharmony_ci * The interruption handler will echo back the @intparm specified here, unless 29562306a36Sopenharmony_ci * another interruption parameter is specified by a subsequent invocation of 29662306a36Sopenharmony_ci * ccw_device_halt() or ccw_device_clear(). 29762306a36Sopenharmony_ci * Returns: 29862306a36Sopenharmony_ci * %0, if the operation was successful; 29962306a36Sopenharmony_ci * -%EBUSY, if the device is busy, or status pending; 30062306a36Sopenharmony_ci * -%EACCES, if no path specified in @lpm is operational; 30162306a36Sopenharmony_ci * -%ENODEV, if the device is not operational. 30262306a36Sopenharmony_ci * Context: 30362306a36Sopenharmony_ci * Interrupts disabled, ccw device lock held 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ciint ccw_device_start(struct ccw_device *cdev, struct ccw1 *cpa, 30662306a36Sopenharmony_ci unsigned long intparm, __u8 lpm, unsigned long flags) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci return ccw_device_start_key(cdev, cpa, intparm, lpm, 30962306a36Sopenharmony_ci PAGE_DEFAULT_KEY, flags); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/** 31362306a36Sopenharmony_ci * ccw_device_start_timeout() - start a s390 channel program with timeout 31462306a36Sopenharmony_ci * @cdev: target ccw device 31562306a36Sopenharmony_ci * @cpa: logical start address of channel program 31662306a36Sopenharmony_ci * @intparm: user specific interruption parameter; will be presented back to 31762306a36Sopenharmony_ci * @cdev's interrupt handler. Allows a device driver to associate 31862306a36Sopenharmony_ci * the interrupt with a particular I/O request. 31962306a36Sopenharmony_ci * @lpm: defines the channel path to be used for a specific I/O request. A 32062306a36Sopenharmony_ci * value of 0 will make cio use the opm. 32162306a36Sopenharmony_ci * @flags: additional flags; defines the action to be performed for I/O 32262306a36Sopenharmony_ci * processing. 32362306a36Sopenharmony_ci * @expires: timeout value in jiffies 32462306a36Sopenharmony_ci * 32562306a36Sopenharmony_ci * Start a S/390 channel program. When the interrupt arrives, the 32662306a36Sopenharmony_ci * IRQ handler is called, either immediately, delayed (dev-end missing, 32762306a36Sopenharmony_ci * or sense required) or never (no IRQ handler registered). 32862306a36Sopenharmony_ci * This function notifies the device driver if the channel program has not 32962306a36Sopenharmony_ci * completed during the time specified by @expires. If a timeout occurs, the 33062306a36Sopenharmony_ci * channel program is terminated via xsch, hsch or csch, and the device's 33162306a36Sopenharmony_ci * interrupt handler will be called with an irb containing ERR_PTR(-%ETIMEDOUT). 33262306a36Sopenharmony_ci * The interruption handler will echo back the @intparm specified here, unless 33362306a36Sopenharmony_ci * another interruption parameter is specified by a subsequent invocation of 33462306a36Sopenharmony_ci * ccw_device_halt() or ccw_device_clear(). 33562306a36Sopenharmony_ci * Returns: 33662306a36Sopenharmony_ci * %0, if the operation was successful; 33762306a36Sopenharmony_ci * -%EBUSY, if the device is busy, or status pending; 33862306a36Sopenharmony_ci * -%EACCES, if no path specified in @lpm is operational; 33962306a36Sopenharmony_ci * -%ENODEV, if the device is not operational. 34062306a36Sopenharmony_ci * Context: 34162306a36Sopenharmony_ci * Interrupts disabled, ccw device lock held 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_ciint ccw_device_start_timeout(struct ccw_device *cdev, struct ccw1 *cpa, 34462306a36Sopenharmony_ci unsigned long intparm, __u8 lpm, 34562306a36Sopenharmony_ci unsigned long flags, int expires) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci return ccw_device_start_timeout_key(cdev, cpa, intparm, lpm, 34862306a36Sopenharmony_ci PAGE_DEFAULT_KEY, flags, 34962306a36Sopenharmony_ci expires); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/** 35462306a36Sopenharmony_ci * ccw_device_halt() - halt I/O request processing 35562306a36Sopenharmony_ci * @cdev: target ccw device 35662306a36Sopenharmony_ci * @intparm: interruption parameter to be returned upon conclusion of hsch 35762306a36Sopenharmony_ci * 35862306a36Sopenharmony_ci * ccw_device_halt() calls hsch on @cdev's subchannel. 35962306a36Sopenharmony_ci * The interruption handler will echo back the @intparm specified here, unless 36062306a36Sopenharmony_ci * another interruption parameter is specified by a subsequent invocation of 36162306a36Sopenharmony_ci * ccw_device_clear(). 36262306a36Sopenharmony_ci * Returns: 36362306a36Sopenharmony_ci * %0 on success, 36462306a36Sopenharmony_ci * -%ENODEV on device not operational, 36562306a36Sopenharmony_ci * -%EINVAL on invalid device state, 36662306a36Sopenharmony_ci * -%EBUSY on device busy or interrupt pending. 36762306a36Sopenharmony_ci * Context: 36862306a36Sopenharmony_ci * Interrupts disabled, ccw device lock held 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_ciint ccw_device_halt(struct ccw_device *cdev, unsigned long intparm) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct subchannel *sch; 37362306a36Sopenharmony_ci int ret; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (!cdev || !cdev->dev.parent) 37662306a36Sopenharmony_ci return -ENODEV; 37762306a36Sopenharmony_ci sch = to_subchannel(cdev->dev.parent); 37862306a36Sopenharmony_ci if (!sch->schib.pmcw.ena) 37962306a36Sopenharmony_ci return -EINVAL; 38062306a36Sopenharmony_ci if (cdev->private->state == DEV_STATE_NOT_OPER) 38162306a36Sopenharmony_ci return -ENODEV; 38262306a36Sopenharmony_ci if (cdev->private->state != DEV_STATE_ONLINE && 38362306a36Sopenharmony_ci cdev->private->state != DEV_STATE_W4SENSE) 38462306a36Sopenharmony_ci return -EINVAL; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ret = cio_halt(sch); 38762306a36Sopenharmony_ci if (ret == 0) 38862306a36Sopenharmony_ci cdev->private->intparm = intparm; 38962306a36Sopenharmony_ci return ret; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/** 39362306a36Sopenharmony_ci * ccw_device_resume() - resume channel program execution 39462306a36Sopenharmony_ci * @cdev: target ccw device 39562306a36Sopenharmony_ci * 39662306a36Sopenharmony_ci * ccw_device_resume() calls rsch on @cdev's subchannel. 39762306a36Sopenharmony_ci * Returns: 39862306a36Sopenharmony_ci * %0 on success, 39962306a36Sopenharmony_ci * -%ENODEV on device not operational, 40062306a36Sopenharmony_ci * -%EINVAL on invalid device state, 40162306a36Sopenharmony_ci * -%EBUSY on device busy or interrupt pending. 40262306a36Sopenharmony_ci * Context: 40362306a36Sopenharmony_ci * Interrupts disabled, ccw device lock held 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_ciint ccw_device_resume(struct ccw_device *cdev) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct subchannel *sch; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (!cdev || !cdev->dev.parent) 41062306a36Sopenharmony_ci return -ENODEV; 41162306a36Sopenharmony_ci sch = to_subchannel(cdev->dev.parent); 41262306a36Sopenharmony_ci if (!sch->schib.pmcw.ena) 41362306a36Sopenharmony_ci return -EINVAL; 41462306a36Sopenharmony_ci if (cdev->private->state == DEV_STATE_NOT_OPER) 41562306a36Sopenharmony_ci return -ENODEV; 41662306a36Sopenharmony_ci if (cdev->private->state != DEV_STATE_ONLINE || 41762306a36Sopenharmony_ci !(sch->schib.scsw.cmd.actl & SCSW_ACTL_SUSPENDED)) 41862306a36Sopenharmony_ci return -EINVAL; 41962306a36Sopenharmony_ci return cio_resume(sch); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/** 42362306a36Sopenharmony_ci * ccw_device_get_ciw() - Search for CIW command in extended sense data. 42462306a36Sopenharmony_ci * @cdev: ccw device to inspect 42562306a36Sopenharmony_ci * @ct: command type to look for 42662306a36Sopenharmony_ci * 42762306a36Sopenharmony_ci * During SenseID, command information words (CIWs) describing special 42862306a36Sopenharmony_ci * commands available to the device may have been stored in the extended 42962306a36Sopenharmony_ci * sense data. This function searches for CIWs of a specified command 43062306a36Sopenharmony_ci * type in the extended sense data. 43162306a36Sopenharmony_ci * Returns: 43262306a36Sopenharmony_ci * %NULL if no extended sense data has been stored or if no CIW of the 43362306a36Sopenharmony_ci * specified command type could be found, 43462306a36Sopenharmony_ci * else a pointer to the CIW of the specified command type. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_cistruct ciw *ccw_device_get_ciw(struct ccw_device *cdev, __u32 ct) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci int ciw_cnt; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (cdev->private->flags.esid == 0) 44162306a36Sopenharmony_ci return NULL; 44262306a36Sopenharmony_ci for (ciw_cnt = 0; ciw_cnt < MAX_CIWS; ciw_cnt++) 44362306a36Sopenharmony_ci if (cdev->private->dma_area->senseid.ciw[ciw_cnt].ct == ct) 44462306a36Sopenharmony_ci return cdev->private->dma_area->senseid.ciw + ciw_cnt; 44562306a36Sopenharmony_ci return NULL; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci/** 44962306a36Sopenharmony_ci * ccw_device_get_path_mask() - get currently available paths 45062306a36Sopenharmony_ci * @cdev: ccw device to be queried 45162306a36Sopenharmony_ci * Returns: 45262306a36Sopenharmony_ci * %0 if no subchannel for the device is available, 45362306a36Sopenharmony_ci * else the mask of currently available paths for the ccw device's subchannel. 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci__u8 ccw_device_get_path_mask(struct ccw_device *cdev) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct subchannel *sch; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (!cdev->dev.parent) 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci sch = to_subchannel(cdev->dev.parent); 46362306a36Sopenharmony_ci return sch->lpm; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/** 46762306a36Sopenharmony_ci * ccw_device_get_chp_desc() - return newly allocated channel-path descriptor 46862306a36Sopenharmony_ci * @cdev: device to obtain the descriptor for 46962306a36Sopenharmony_ci * @chp_idx: index of the channel path 47062306a36Sopenharmony_ci * 47162306a36Sopenharmony_ci * On success return a newly allocated copy of the channel-path description 47262306a36Sopenharmony_ci * data associated with the given channel path. Return %NULL on error. 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_cistruct channel_path_desc_fmt0 *ccw_device_get_chp_desc(struct ccw_device *cdev, 47562306a36Sopenharmony_ci int chp_idx) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct subchannel *sch; 47862306a36Sopenharmony_ci struct chp_id chpid; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci sch = to_subchannel(cdev->dev.parent); 48162306a36Sopenharmony_ci chp_id_init(&chpid); 48262306a36Sopenharmony_ci chpid.id = sch->schib.pmcw.chpid[chp_idx]; 48362306a36Sopenharmony_ci return chp_get_chp_desc(chpid); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/** 48762306a36Sopenharmony_ci * ccw_device_get_util_str() - return newly allocated utility strings 48862306a36Sopenharmony_ci * @cdev: device to obtain the utility strings for 48962306a36Sopenharmony_ci * @chp_idx: index of the channel path 49062306a36Sopenharmony_ci * 49162306a36Sopenharmony_ci * On success return a newly allocated copy of the utility strings 49262306a36Sopenharmony_ci * associated with the given channel path. Return %NULL on error. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ciu8 *ccw_device_get_util_str(struct ccw_device *cdev, int chp_idx) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 49762306a36Sopenharmony_ci struct channel_path *chp; 49862306a36Sopenharmony_ci struct chp_id chpid; 49962306a36Sopenharmony_ci u8 *util_str; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci chp_id_init(&chpid); 50262306a36Sopenharmony_ci chpid.id = sch->schib.pmcw.chpid[chp_idx]; 50362306a36Sopenharmony_ci chp = chpid_to_chp(chpid); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci util_str = kmalloc(sizeof(chp->desc_fmt3.util_str), GFP_KERNEL); 50662306a36Sopenharmony_ci if (!util_str) 50762306a36Sopenharmony_ci return NULL; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci mutex_lock(&chp->lock); 51062306a36Sopenharmony_ci memcpy(util_str, chp->desc_fmt3.util_str, sizeof(chp->desc_fmt3.util_str)); 51162306a36Sopenharmony_ci mutex_unlock(&chp->lock); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return util_str; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci/** 51762306a36Sopenharmony_ci * ccw_device_get_id() - obtain a ccw device id 51862306a36Sopenharmony_ci * @cdev: device to obtain the id for 51962306a36Sopenharmony_ci * @dev_id: where to fill in the values 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_civoid ccw_device_get_id(struct ccw_device *cdev, struct ccw_dev_id *dev_id) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci *dev_id = cdev->private->dev_id; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_get_id); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/** 52862306a36Sopenharmony_ci * ccw_device_tm_start_timeout_key() - perform start function 52962306a36Sopenharmony_ci * @cdev: ccw device on which to perform the start function 53062306a36Sopenharmony_ci * @tcw: transport-command word to be started 53162306a36Sopenharmony_ci * @intparm: user defined parameter to be passed to the interrupt handler 53262306a36Sopenharmony_ci * @lpm: mask of paths to use 53362306a36Sopenharmony_ci * @key: storage key to use for storage access 53462306a36Sopenharmony_ci * @expires: time span in jiffies after which to abort request 53562306a36Sopenharmony_ci * 53662306a36Sopenharmony_ci * Start the tcw on the given ccw device. Return zero on success, non-zero 53762306a36Sopenharmony_ci * otherwise. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ciint ccw_device_tm_start_timeout_key(struct ccw_device *cdev, struct tcw *tcw, 54062306a36Sopenharmony_ci unsigned long intparm, u8 lpm, u8 key, 54162306a36Sopenharmony_ci int expires) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct subchannel *sch; 54462306a36Sopenharmony_ci int rc; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci sch = to_subchannel(cdev->dev.parent); 54762306a36Sopenharmony_ci if (!sch->schib.pmcw.ena) 54862306a36Sopenharmony_ci return -EINVAL; 54962306a36Sopenharmony_ci if (cdev->private->state == DEV_STATE_VERIFY) { 55062306a36Sopenharmony_ci /* Remember to fake irb when finished. */ 55162306a36Sopenharmony_ci if (!cdev->private->flags.fake_irb) { 55262306a36Sopenharmony_ci cdev->private->flags.fake_irb = FAKE_TM_IRB; 55362306a36Sopenharmony_ci cdev->private->intparm = intparm; 55462306a36Sopenharmony_ci return 0; 55562306a36Sopenharmony_ci } else 55662306a36Sopenharmony_ci /* There's already a fake I/O around. */ 55762306a36Sopenharmony_ci return -EBUSY; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci if (cdev->private->state != DEV_STATE_ONLINE) 56062306a36Sopenharmony_ci return -EIO; 56162306a36Sopenharmony_ci /* Adjust requested path mask to exclude unusable paths. */ 56262306a36Sopenharmony_ci if (lpm) { 56362306a36Sopenharmony_ci lpm &= sch->lpm; 56462306a36Sopenharmony_ci if (lpm == 0) 56562306a36Sopenharmony_ci return -EACCES; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci rc = cio_tm_start_key(sch, tcw, lpm, key); 56862306a36Sopenharmony_ci if (rc == 0) { 56962306a36Sopenharmony_ci cdev->private->intparm = intparm; 57062306a36Sopenharmony_ci if (expires) 57162306a36Sopenharmony_ci ccw_device_set_timeout(cdev, expires); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci return rc; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_tm_start_timeout_key); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/** 57862306a36Sopenharmony_ci * ccw_device_tm_start_key() - perform start function 57962306a36Sopenharmony_ci * @cdev: ccw device on which to perform the start function 58062306a36Sopenharmony_ci * @tcw: transport-command word to be started 58162306a36Sopenharmony_ci * @intparm: user defined parameter to be passed to the interrupt handler 58262306a36Sopenharmony_ci * @lpm: mask of paths to use 58362306a36Sopenharmony_ci * @key: storage key to use for storage access 58462306a36Sopenharmony_ci * 58562306a36Sopenharmony_ci * Start the tcw on the given ccw device. Return zero on success, non-zero 58662306a36Sopenharmony_ci * otherwise. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ciint ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw, 58962306a36Sopenharmony_ci unsigned long intparm, u8 lpm, u8 key) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci return ccw_device_tm_start_timeout_key(cdev, tcw, intparm, lpm, key, 0); 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_tm_start_key); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci/** 59662306a36Sopenharmony_ci * ccw_device_tm_start() - perform start function 59762306a36Sopenharmony_ci * @cdev: ccw device on which to perform the start function 59862306a36Sopenharmony_ci * @tcw: transport-command word to be started 59962306a36Sopenharmony_ci * @intparm: user defined parameter to be passed to the interrupt handler 60062306a36Sopenharmony_ci * @lpm: mask of paths to use 60162306a36Sopenharmony_ci * 60262306a36Sopenharmony_ci * Start the tcw on the given ccw device. Return zero on success, non-zero 60362306a36Sopenharmony_ci * otherwise. 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_ciint ccw_device_tm_start(struct ccw_device *cdev, struct tcw *tcw, 60662306a36Sopenharmony_ci unsigned long intparm, u8 lpm) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci return ccw_device_tm_start_key(cdev, tcw, intparm, lpm, 60962306a36Sopenharmony_ci PAGE_DEFAULT_KEY); 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_tm_start); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci/** 61462306a36Sopenharmony_ci * ccw_device_tm_start_timeout() - perform start function 61562306a36Sopenharmony_ci * @cdev: ccw device on which to perform the start function 61662306a36Sopenharmony_ci * @tcw: transport-command word to be started 61762306a36Sopenharmony_ci * @intparm: user defined parameter to be passed to the interrupt handler 61862306a36Sopenharmony_ci * @lpm: mask of paths to use 61962306a36Sopenharmony_ci * @expires: time span in jiffies after which to abort request 62062306a36Sopenharmony_ci * 62162306a36Sopenharmony_ci * Start the tcw on the given ccw device. Return zero on success, non-zero 62262306a36Sopenharmony_ci * otherwise. 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_ciint ccw_device_tm_start_timeout(struct ccw_device *cdev, struct tcw *tcw, 62562306a36Sopenharmony_ci unsigned long intparm, u8 lpm, int expires) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci return ccw_device_tm_start_timeout_key(cdev, tcw, intparm, lpm, 62862306a36Sopenharmony_ci PAGE_DEFAULT_KEY, expires); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_tm_start_timeout); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci/** 63362306a36Sopenharmony_ci * ccw_device_get_mdc() - accumulate max data count 63462306a36Sopenharmony_ci * @cdev: ccw device for which the max data count is accumulated 63562306a36Sopenharmony_ci * @mask: mask of paths to use 63662306a36Sopenharmony_ci * 63762306a36Sopenharmony_ci * Return the number of 64K-bytes blocks all paths at least support 63862306a36Sopenharmony_ci * for a transport command. Return value 0 indicates failure. 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ciint ccw_device_get_mdc(struct ccw_device *cdev, u8 mask) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 64362306a36Sopenharmony_ci struct channel_path *chp; 64462306a36Sopenharmony_ci struct chp_id chpid; 64562306a36Sopenharmony_ci int mdc = 0, i; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* Adjust requested path mask to excluded varied off paths. */ 64862306a36Sopenharmony_ci if (mask) 64962306a36Sopenharmony_ci mask &= sch->lpm; 65062306a36Sopenharmony_ci else 65162306a36Sopenharmony_ci mask = sch->lpm; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci chp_id_init(&chpid); 65462306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 65562306a36Sopenharmony_ci if (!(mask & (0x80 >> i))) 65662306a36Sopenharmony_ci continue; 65762306a36Sopenharmony_ci chpid.id = sch->schib.pmcw.chpid[i]; 65862306a36Sopenharmony_ci chp = chpid_to_chp(chpid); 65962306a36Sopenharmony_ci if (!chp) 66062306a36Sopenharmony_ci continue; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci mutex_lock(&chp->lock); 66362306a36Sopenharmony_ci if (!chp->desc_fmt1.f) { 66462306a36Sopenharmony_ci mutex_unlock(&chp->lock); 66562306a36Sopenharmony_ci return 0; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci if (!chp->desc_fmt1.r) 66862306a36Sopenharmony_ci mdc = 1; 66962306a36Sopenharmony_ci mdc = mdc ? min_t(int, mdc, chp->desc_fmt1.mdc) : 67062306a36Sopenharmony_ci chp->desc_fmt1.mdc; 67162306a36Sopenharmony_ci mutex_unlock(&chp->lock); 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return mdc; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_get_mdc); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci/** 67962306a36Sopenharmony_ci * ccw_device_tm_intrg() - perform interrogate function 68062306a36Sopenharmony_ci * @cdev: ccw device on which to perform the interrogate function 68162306a36Sopenharmony_ci * 68262306a36Sopenharmony_ci * Perform an interrogate function on the given ccw device. Return zero on 68362306a36Sopenharmony_ci * success, non-zero otherwise. 68462306a36Sopenharmony_ci */ 68562306a36Sopenharmony_ciint ccw_device_tm_intrg(struct ccw_device *cdev) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (!sch->schib.pmcw.ena) 69062306a36Sopenharmony_ci return -EINVAL; 69162306a36Sopenharmony_ci if (cdev->private->state != DEV_STATE_ONLINE) 69262306a36Sopenharmony_ci return -EIO; 69362306a36Sopenharmony_ci if (!scsw_is_tm(&sch->schib.scsw) || 69462306a36Sopenharmony_ci !(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_START_PEND)) 69562306a36Sopenharmony_ci return -EINVAL; 69662306a36Sopenharmony_ci return cio_tm_intrg(sch); 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_tm_intrg); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/** 70162306a36Sopenharmony_ci * ccw_device_get_schid() - obtain a subchannel id 70262306a36Sopenharmony_ci * @cdev: device to obtain the id for 70362306a36Sopenharmony_ci * @schid: where to fill in the values 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_civoid ccw_device_get_schid(struct ccw_device *cdev, struct subchannel_id *schid) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci *schid = sch->schid; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ccw_device_get_schid); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci/** 71462306a36Sopenharmony_ci * ccw_device_pnso() - Perform Network-Subchannel Operation 71562306a36Sopenharmony_ci * @cdev: device on which PNSO is performed 71662306a36Sopenharmony_ci * @pnso_area: request and response block for the operation 71762306a36Sopenharmony_ci * @oc: Operation Code 71862306a36Sopenharmony_ci * @resume_token: resume token for multiblock response 71962306a36Sopenharmony_ci * @cnc: Boolean change-notification control 72062306a36Sopenharmony_ci * 72162306a36Sopenharmony_ci * pnso_area must be allocated by the caller with get_zeroed_page(GFP_KERNEL) 72262306a36Sopenharmony_ci * 72362306a36Sopenharmony_ci * Returns 0 on success. 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_ciint ccw_device_pnso(struct ccw_device *cdev, 72662306a36Sopenharmony_ci struct chsc_pnso_area *pnso_area, u8 oc, 72762306a36Sopenharmony_ci struct chsc_pnso_resume_token resume_token, int cnc) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct subchannel_id schid; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci ccw_device_get_schid(cdev, &schid); 73262306a36Sopenharmony_ci return chsc_pnso(schid, pnso_area, oc, resume_token, cnc); 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ccw_device_pnso); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci/** 73762306a36Sopenharmony_ci * ccw_device_get_cssid() - obtain Channel Subsystem ID 73862306a36Sopenharmony_ci * @cdev: device to obtain the CSSID for 73962306a36Sopenharmony_ci * @cssid: The resulting Channel Subsystem ID 74062306a36Sopenharmony_ci */ 74162306a36Sopenharmony_ciint ccw_device_get_cssid(struct ccw_device *cdev, u8 *cssid) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct device *sch_dev = cdev->dev.parent; 74462306a36Sopenharmony_ci struct channel_subsystem *css = to_css(sch_dev->parent); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (css->id_valid) 74762306a36Sopenharmony_ci *cssid = css->cssid; 74862306a36Sopenharmony_ci return css->id_valid ? 0 : -ENODEV; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ccw_device_get_cssid); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci/** 75362306a36Sopenharmony_ci * ccw_device_get_iid() - obtain MIF-image ID 75462306a36Sopenharmony_ci * @cdev: device to obtain the MIF-image ID for 75562306a36Sopenharmony_ci * @iid: The resulting MIF-image ID 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_ciint ccw_device_get_iid(struct ccw_device *cdev, u8 *iid) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct device *sch_dev = cdev->dev.parent; 76062306a36Sopenharmony_ci struct channel_subsystem *css = to_css(sch_dev->parent); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (css->id_valid) 76362306a36Sopenharmony_ci *iid = css->iid; 76462306a36Sopenharmony_ci return css->id_valid ? 0 : -ENODEV; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ccw_device_get_iid); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci/** 76962306a36Sopenharmony_ci * ccw_device_get_chpid() - obtain Channel Path ID 77062306a36Sopenharmony_ci * @cdev: device to obtain the Channel Path ID for 77162306a36Sopenharmony_ci * @chp_idx: Index of the channel path 77262306a36Sopenharmony_ci * @chpid: The resulting Channel Path ID 77362306a36Sopenharmony_ci */ 77462306a36Sopenharmony_ciint ccw_device_get_chpid(struct ccw_device *cdev, int chp_idx, u8 *chpid) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 77762306a36Sopenharmony_ci int mask; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if ((chp_idx < 0) || (chp_idx > 7)) 78062306a36Sopenharmony_ci return -EINVAL; 78162306a36Sopenharmony_ci mask = 0x80 >> chp_idx; 78262306a36Sopenharmony_ci if (!(sch->schib.pmcw.pim & mask)) 78362306a36Sopenharmony_ci return -ENODEV; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci *chpid = sch->schib.pmcw.chpid[chp_idx]; 78662306a36Sopenharmony_ci return 0; 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ccw_device_get_chpid); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci/** 79162306a36Sopenharmony_ci * ccw_device_get_chid() - obtain Channel ID associated with specified CHPID 79262306a36Sopenharmony_ci * @cdev: device to obtain the Channel ID for 79362306a36Sopenharmony_ci * @chp_idx: Index of the channel path 79462306a36Sopenharmony_ci * @chid: The resulting Channel ID 79562306a36Sopenharmony_ci */ 79662306a36Sopenharmony_ciint ccw_device_get_chid(struct ccw_device *cdev, int chp_idx, u16 *chid) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct chp_id cssid_chpid; 79962306a36Sopenharmony_ci struct channel_path *chp; 80062306a36Sopenharmony_ci int rc; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci chp_id_init(&cssid_chpid); 80362306a36Sopenharmony_ci rc = ccw_device_get_chpid(cdev, chp_idx, &cssid_chpid.id); 80462306a36Sopenharmony_ci if (rc) 80562306a36Sopenharmony_ci return rc; 80662306a36Sopenharmony_ci chp = chpid_to_chp(cssid_chpid); 80762306a36Sopenharmony_ci if (!chp) 80862306a36Sopenharmony_ci return -ENODEV; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci mutex_lock(&chp->lock); 81162306a36Sopenharmony_ci if (chp->desc_fmt1.flags & 0x10) 81262306a36Sopenharmony_ci *chid = chp->desc_fmt1.chid; 81362306a36Sopenharmony_ci else 81462306a36Sopenharmony_ci rc = -ENODEV; 81562306a36Sopenharmony_ci mutex_unlock(&chp->lock); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return rc; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ccw_device_get_chid); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci/* 82262306a36Sopenharmony_ci * Allocate zeroed dma coherent 31 bit addressable memory using 82362306a36Sopenharmony_ci * the subchannels dma pool. Maximal size of allocation supported 82462306a36Sopenharmony_ci * is PAGE_SIZE. 82562306a36Sopenharmony_ci */ 82662306a36Sopenharmony_civoid *ccw_device_dma_zalloc(struct ccw_device *cdev, size_t size) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci void *addr; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (!get_device(&cdev->dev)) 83162306a36Sopenharmony_ci return NULL; 83262306a36Sopenharmony_ci addr = cio_gp_dma_zalloc(cdev->private->dma_pool, &cdev->dev, size); 83362306a36Sopenharmony_ci if (IS_ERR_OR_NULL(addr)) 83462306a36Sopenharmony_ci put_device(&cdev->dev); 83562306a36Sopenharmony_ci return addr; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_dma_zalloc); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_civoid ccw_device_dma_free(struct ccw_device *cdev, void *cpu_addr, size_t size) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci if (!cpu_addr) 84262306a36Sopenharmony_ci return; 84362306a36Sopenharmony_ci cio_gp_dma_free(cdev->private->dma_pool, cpu_addr, size); 84462306a36Sopenharmony_ci put_device(&cdev->dev); 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_dma_free); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_set_options_mask); 84962306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_set_options); 85062306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_clear_options); 85162306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_clear); 85262306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_halt); 85362306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_resume); 85462306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_start_timeout); 85562306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_start); 85662306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_start_timeout_key); 85762306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_start_key); 85862306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_get_ciw); 85962306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_get_path_mask); 86062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ccw_device_get_chp_desc); 86162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ccw_device_get_util_str); 862