[S390] Convert memory detection into C code.

Hopefully this will make it more maintainable and less error prone.
Code makes use of search_exception_tables(). Since it calls this
function before the kernel exeception table is sorted, there is an
early call to sort_main_extable().

This way it's easy to use the already present infrastructure of fixup
sections. Also this would allows to easily convert the rest of
head[31|64].S into C code.

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index c3e97b4..293e667 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -2,7 +2,8 @@
 # S/390 character devices
 #
 
-obj-y += ctrlchar.o keyboard.o defkeymap.o
+obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
+	 sclp_info.o
 
 obj-$(CONFIG_TN3270) += raw3270.o
 obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
@@ -11,7 +12,6 @@
 
 obj-$(CONFIG_TN3215) += con3215.o
 
-obj-$(CONFIG_SCLP) += sclp.o sclp_rw.o sclp_quiesce.o
 obj-$(CONFIG_SCLP_TTY) += sclp_tty.o
 obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
 obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index c1dd19b..6a83e2d 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -96,8 +96,8 @@
 static int sclp_init(void);
 
 /* Perform service call. Return 0 on success, non-zero otherwise. */
-static int
-service_call(sclp_cmdw_t command, void *sccb)
+int
+sclp_service_call(sclp_cmdw_t command, void *sccb)
 {
 	int cc;
 
@@ -173,7 +173,7 @@
 	if (sclp_running_state != sclp_running_state_idle)
 		return 0;
 	del_timer(&sclp_request_timer);
-	rc = service_call(req->command, req->sccb);
+	rc = sclp_service_call(req->command, req->sccb);
 	req->start_count++;
 
 	if (rc == 0) {
@@ -325,7 +325,7 @@
 	sccb = (struct sccb_header *) sclp_read_sccb;
 	clear_page(sccb);
 	memset(&sclp_read_req, 0, sizeof(struct sclp_req));
-	sclp_read_req.command = SCLP_CMDW_READDATA;
+	sclp_read_req.command = SCLP_CMDW_READ_EVENT_DATA;
 	sclp_read_req.status = SCLP_REQ_QUEUED;
 	sclp_read_req.start_count = 0;
 	sclp_read_req.callback = sclp_read_cb;
@@ -628,7 +628,7 @@
 	sccb = (struct init_sccb *) sclp_init_sccb;
 	clear_page(sccb);
 	memset(&sclp_init_req, 0, sizeof(struct sclp_req));
-	sclp_init_req.command = SCLP_CMDW_WRITEMASK;
+	sclp_init_req.command = SCLP_CMDW_WRITE_EVENT_MASK;
 	sclp_init_req.status = SCLP_REQ_FILLED;
 	sclp_init_req.start_count = 0;
 	sclp_init_req.callback = NULL;
@@ -831,7 +831,7 @@
 	for (retry = 0; retry <= SCLP_INIT_RETRY; retry++) {
 		__sclp_make_init_req(0, 0);
 		sccb = (struct init_sccb *) sclp_init_req.sccb;
-		rc = service_call(sclp_init_req.command, sccb);
+		rc = sclp_service_call(sclp_init_req.command, sccb);
 		if (rc == -EIO)
 			break;
 		sclp_init_req.status = SCLP_REQ_RUNNING;
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index 2c71d6e..7d29ab4 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -12,7 +12,7 @@
 
 #include <linux/types.h>
 #include <linux/list.h>
-
+#include <asm/sclp.h>
 #include <asm/ebcdic.h>
 
 /* maximum number of pages concerning our own memory management */
@@ -49,9 +49,11 @@
 
 typedef unsigned int sclp_cmdw_t;
 
-#define SCLP_CMDW_READDATA	0x00770005
-#define SCLP_CMDW_WRITEDATA	0x00760005
-#define SCLP_CMDW_WRITEMASK	0x00780005
+#define SCLP_CMDW_READ_EVENT_DATA	0x00770005
+#define SCLP_CMDW_WRITE_EVENT_DATA	0x00760005
+#define SCLP_CMDW_WRITE_EVENT_MASK	0x00780005
+#define SCLP_CMDW_READ_SCP_INFO		0x00020001
+#define SCLP_CMDW_READ_SCP_INFO_FORCED	0x00120001
 
 #define GDS_ID_MDSMU		0x1310
 #define GDS_ID_MDSRouteInfo	0x1311
@@ -66,13 +68,6 @@
 
 typedef u32 sccb_mask_t;	/* ATTENTION: assumes 32bit mask !!! */
 
-struct sccb_header {
-	u16	length;
-	u8	function_code;
-	u8	control_mask[3];
-	u16	response_code;
-} __attribute__((packed));
-
 struct gds_subvector {
 	u8	length;
 	u8	key;
@@ -131,6 +126,7 @@
 int sclp_remove_processed(struct sccb_header *sccb);
 int sclp_deactivate(void);
 int sclp_reactivate(void);
+int sclp_service_call(sclp_cmdw_t command, void *sccb);
 
 /* useful inlines */
 
diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c
index 4f873ae..65aa2c8 100644
--- a/drivers/s390/char/sclp_cpi.c
+++ b/drivers/s390/char/sclp_cpi.c
@@ -169,7 +169,7 @@
 	}
 
 	/* prepare request data structure presented to SCLP driver */
-	req->command = SCLP_CMDW_WRITEDATA;
+	req->command = SCLP_CMDW_WRITE_EVENT_DATA;
 	req->sccb = sccb;
 	req->status = SCLP_REQ_FILLED;
 	req->callback = cpi_callback;
diff --git a/drivers/s390/char/sclp_info.c b/drivers/s390/char/sclp_info.c
new file mode 100644
index 0000000..7bcbe64
--- /dev/null
+++ b/drivers/s390/char/sclp_info.c
@@ -0,0 +1,57 @@
+/*
+ *  drivers/s390/char/sclp_info.c
+ *
+ *    Copyright IBM Corp. 2007
+ *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/sclp.h>
+#include "sclp.h"
+
+struct sclp_readinfo_sccb s390_readinfo_sccb;
+
+void __init sclp_readinfo_early(void)
+{
+	sclp_cmdw_t command;
+	struct sccb_header *sccb;
+	int ret;
+
+	__ctl_set_bit(0, 9); /* enable service signal subclass mask */
+
+	sccb = &s390_readinfo_sccb.header;
+	command = SCLP_CMDW_READ_SCP_INFO_FORCED;
+	while (1) {
+		u16 response;
+
+		memset(&s390_readinfo_sccb, 0, sizeof(s390_readinfo_sccb));
+		sccb->length = sizeof(s390_readinfo_sccb);
+		sccb->control_mask[2] = 0x80;
+
+		ret = sclp_service_call(command, &s390_readinfo_sccb);
+
+		if (ret == -EIO)
+			goto out;
+		if (ret == -EBUSY)
+			continue;
+
+		__load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT |
+				PSW_MASK_WAIT | PSW_DEFAULT_KEY);
+		local_irq_disable();
+		barrier();
+
+		response = sccb->response_code;
+
+		if (response == 0x10)
+			break;
+
+		if (response != 0x1f0 || command == SCLP_CMDW_READ_SCP_INFO)
+			break;
+
+		command = SCLP_CMDW_READ_SCP_INFO;
+	}
+out:
+	__ctl_clear_bit(0, 9); /* disable service signal subclass mask */
+}
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index 0c92d39..2486783 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -460,7 +460,7 @@
 		sccb->msg_buf.header.type = EvTyp_PMsgCmd;
 	else
 		return -ENOSYS;
-	buffer->request.command = SCLP_CMDW_WRITEDATA;
+	buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA;
 	buffer->request.status = SCLP_REQ_FILLED;
 	buffer->request.callback = sclp_writedata_callback;
 	buffer->request.callback_data = buffer;
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index d8135cd..544f137 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -207,7 +207,7 @@
 		request->sclp_req.status = SCLP_REQ_FAILED;
 		return -EIO;
 	}
-	request->sclp_req.command = SCLP_CMDW_WRITEDATA;
+	request->sclp_req.command = SCLP_CMDW_WRITE_EVENT_DATA;
 	request->sclp_req.status = SCLP_REQ_FILLED;
 	request->sclp_req.callback = sclp_vt220_callback;
 	request->sclp_req.callback_data = (void *) request;