Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6] / drivers / s390 / char / sclp_info.c
1 /*
2  *  drivers/s390/char/sclp_info.c
3  *
4  *    Copyright IBM Corp. 2007
5  *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
6  */
7
8 #include <linux/init.h>
9 #include <linux/errno.h>
10 #include <linux/string.h>
11 #include <asm/sclp.h>
12 #include "sclp.h"
13
14 struct sclp_readinfo_sccb {
15         struct  sccb_header header;     /* 0-7 */
16         u16     rnmax;                  /* 8-9 */
17         u8      rnsize;                 /* 10 */
18         u8      _reserved0[24 - 11];    /* 11-23 */
19         u8      loadparm[8];            /* 24-31 */
20         u8      _reserved1[48 - 32];    /* 32-47 */
21         u64     facilities;             /* 48-55 */
22         u8      _reserved2[91 - 56];    /* 56-90 */
23         u8      flags;                  /* 91 */
24         u8      _reserved3[100 - 92];   /* 92-99 */
25         u32     rnsize2;                /* 100-103 */
26         u64     rnmax2;                 /* 104-111 */
27         u8      _reserved4[4096 - 112]; /* 112-4095 */
28 } __attribute__((packed, aligned(4096)));
29
30 static struct sclp_readinfo_sccb __initdata early_readinfo_sccb;
31 static int __initdata early_readinfo_sccb_valid;
32
33 u64 sclp_facilities;
34
35 void __init sclp_readinfo_early(void)
36 {
37         int ret;
38         int i;
39         struct sclp_readinfo_sccb *sccb;
40         sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
41                                   SCLP_CMDW_READ_SCP_INFO};
42
43         /* Enable service signal subclass mask. */
44         __ctl_set_bit(0, 9);
45         sccb = &early_readinfo_sccb;
46         for (i = 0; i < ARRAY_SIZE(commands); i++) {
47                 do {
48                         memset(sccb, 0, sizeof(*sccb));
49                         sccb->header.length = sizeof(*sccb);
50                         sccb->header.control_mask[2] = 0x80;
51                         ret = sclp_service_call(commands[i], sccb);
52                 } while (ret == -EBUSY);
53
54                 if (ret)
55                         break;
56                 __load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT |
57                                 PSW_MASK_WAIT | PSW_DEFAULT_KEY);
58                 local_irq_disable();
59                 /*
60                  * Contents of the sccb might have changed
61                  * therefore a barrier is needed.
62                  */
63                 barrier();
64                 if (sccb->header.response_code == 0x10) {
65                         early_readinfo_sccb_valid = 1;
66                         break;
67                 }
68                 if (sccb->header.response_code != 0x1f0)
69                         break;
70         }
71         /* Disable service signal subclass mask again. */
72         __ctl_clear_bit(0, 9);
73 }
74
75 void __init sclp_facilities_detect(void)
76 {
77         if (!early_readinfo_sccb_valid)
78                 return;
79         sclp_facilities = early_readinfo_sccb.facilities;
80 }
81
82 unsigned long long __init sclp_memory_detect(void)
83 {
84         unsigned long long memsize;
85         struct sclp_readinfo_sccb *sccb;
86
87         if (!early_readinfo_sccb_valid)
88                 return 0;
89         sccb = &early_readinfo_sccb;
90         if (sccb->rnsize)
91                 memsize = sccb->rnsize << 20;
92         else
93                 memsize = sccb->rnsize2 << 20;
94         if (sccb->rnmax)
95                 memsize *= sccb->rnmax;
96         else
97                 memsize *= sccb->rnmax2;
98         return memsize;
99 }
100
101 /*
102  * This function will be called after sclp_memory_detect(), which gets called
103  * early from early.c code. Therefore the sccb should have valid contents.
104  */
105 void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
106 {
107         struct sclp_readinfo_sccb *sccb;
108
109         if (!early_readinfo_sccb_valid)
110                 return;
111         sccb = &early_readinfo_sccb;
112         info->is_valid = 1;
113         if (sccb->flags & 0x2)
114                 info->has_dump = 1;
115         memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN);
116 }