summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/s390/cio/chsc.c23
-rw-r--r--drivers/s390/cio/chsc.h17
-rw-r--r--drivers/s390/cio/qdio.h14
-rw-r--r--drivers/s390/cio/qdio_setup.c47
4 files changed, 58 insertions, 43 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 8ea7d9b2c671..d119d0d87e9b 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -144,6 +144,29 @@ out:
return ret;
}
+/**
+ * chsc_ssqd() - store subchannel QDIO data (SSQD)
+ * @schid: id of the subchannel on which SSQD is performed
+ * @ssqd: request and response block for SSQD
+ *
+ * Returns 0 on success.
+ */
+int chsc_ssqd(struct subchannel_id schid, struct chsc_ssqd_area *ssqd)
+{
+ memset(ssqd, 0, sizeof(*ssqd));
+ ssqd->request.length = 0x0010;
+ ssqd->request.code = 0x0024;
+ ssqd->first_sch = schid.sch_no;
+ ssqd->last_sch = schid.sch_no;
+ ssqd->ssid = schid.ssid;
+
+ if (chsc(ssqd))
+ return -EIO;
+
+ return chsc_error_from_response(ssqd->response.code);
+}
+EXPORT_SYMBOL_GPL(chsc_ssqd);
+
static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
{
spin_lock_irq(sch->lock);
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 62d096f11e65..2b88e74e6b65 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -7,6 +7,7 @@
#include <asm/chpid.h>
#include <asm/chsc.h>
#include <asm/schid.h>
+#include <asm/qdio.h>
#define CHSC_SDA_OC_MSS 0x2
@@ -72,6 +73,20 @@ struct chsc_ssd_info {
u16 fla[8];
};
+struct chsc_ssqd_area {
+ struct chsc_header request;
+ u16:10;
+ u8 ssid:2;
+ u8 fmt:4;
+ u16 first_sch;
+ u16:16;
+ u16 last_sch;
+ u32:32;
+ struct chsc_header response;
+ u32:32;
+ struct qdio_ssqd_desc qdio_ssqd;
+} __packed;
+
struct chsc_scpd {
struct chsc_header request;
u32:2;
@@ -111,7 +126,7 @@ int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid,
void chsc_chp_online(struct chp_id chpid);
void chsc_chp_offline(struct chp_id chpid);
int chsc_get_channel_measurement_chars(struct channel_path *chp);
-
+int chsc_ssqd(struct subchannel_id schid, struct chsc_ssqd_area *ssqd);
int chsc_error_from_response(int response);
int chsc_siosl(struct subchannel_id schid);
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index 5132554d7917..b8bda2175b6c 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -140,20 +140,6 @@ struct siga_flag {
u8:3;
} __attribute__ ((packed));
-struct chsc_ssqd_area {
- struct chsc_header request;
- u16:10;
- u8 ssid:2;
- u8 fmt:4;
- u16 first_sch;
- u16:16;
- u16 last_sch;
- u32:32;
- struct chsc_header response;
- u32:32;
- struct qdio_ssqd_desc qdio_ssqd;
-} __attribute__ ((packed));
-
struct scssc_area {
struct chsc_header request;
u16 operation_code;
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index 16ecd35b8e51..f5f4a91fab44 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -254,40 +254,31 @@ int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
int rc;
DBF_EVENT("getssqd:%4x", schid->sch_no);
- if (irq_ptr != NULL)
- ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
- else
+ if (!irq_ptr) {
ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL);
- memset(ssqd, 0, PAGE_SIZE);
-
- ssqd->request = (struct chsc_header) {
- .length = 0x0010,
- .code = 0x0024,
- };
- ssqd->first_sch = schid->sch_no;
- ssqd->last_sch = schid->sch_no;
- ssqd->ssid = schid->ssid;
-
- if (chsc(ssqd))
- return -EIO;
- rc = chsc_error_from_response(ssqd->response.code);
+ if (!ssqd)
+ return -ENOMEM;
+ } else {
+ ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
+ }
+
+ rc = chsc_ssqd(*schid, ssqd);
if (rc)
- return rc;
+ goto out;
if (!(ssqd->qdio_ssqd.flags & CHSC_FLAG_QDIO_CAPABILITY) ||
!(ssqd->qdio_ssqd.flags & CHSC_FLAG_VALIDITY) ||
(ssqd->qdio_ssqd.sch != schid->sch_no))
- return -EINVAL;
-
- if (irq_ptr != NULL)
- memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd,
- sizeof(struct qdio_ssqd_desc));
- else {
- memcpy(data, &ssqd->qdio_ssqd,
- sizeof(struct qdio_ssqd_desc));
+ rc = -EINVAL;
+
+ if (!rc)
+ memcpy(data, &ssqd->qdio_ssqd, sizeof(*data));
+
+out:
+ if (!irq_ptr)
free_page((unsigned long)ssqd);
- }
- return 0;
+
+ return rc;
}
void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr)
@@ -295,7 +286,7 @@ void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr)
unsigned char qdioac;
int rc;
- rc = qdio_setup_get_ssqd(irq_ptr, &irq_ptr->schid, NULL);
+ rc = qdio_setup_get_ssqd(irq_ptr, &irq_ptr->schid, &irq_ptr->ssqd_desc);
if (rc) {
DBF_ERROR("%4x ssqd ERR", irq_ptr->schid.sch_no);
DBF_ERROR("rc:%x", rc);