forked from Minki/linux
e657d8fe2f
Currently we have hardcoded the HSA size to 32 MiB. With this patch the HSA size is determined dynamically via SCLP in early.c. Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
112 lines
2.4 KiB
C
112 lines
2.4 KiB
C
/*
|
|
* SCLP early driver
|
|
*
|
|
* Copyright IBM Corp. 2013
|
|
*/
|
|
|
|
#define KMSG_COMPONENT "sclp_early"
|
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
|
|
|
#include <asm/sclp.h>
|
|
#include <asm/ipl.h>
|
|
#include "sclp_sdias.h"
|
|
#include "sclp.h"
|
|
|
|
static __initdata char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE);
|
|
static unsigned long sclp_hsa_size;
|
|
|
|
static int __init sclp_cmd_early(sclp_cmdw_t cmd, void *sccb)
|
|
{
|
|
int rc;
|
|
|
|
do {
|
|
rc = sclp_cmd_sync_early(cmd, sccb);
|
|
} while (rc == -EBUSY);
|
|
|
|
if (rc)
|
|
return -EIO;
|
|
if (((struct sccb_header *) sccb)->response_code != 0x0020)
|
|
return -EIO;
|
|
return 0;
|
|
}
|
|
|
|
static void __init sccb_init_eq_size(struct sdias_sccb *sccb)
|
|
{
|
|
memset(sccb, 0, sizeof(*sccb));
|
|
|
|
sccb->hdr.length = sizeof(*sccb);
|
|
sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);
|
|
sccb->evbuf.hdr.type = EVTYP_SDIAS;
|
|
sccb->evbuf.event_qual = SDIAS_EQ_SIZE;
|
|
sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;
|
|
sccb->evbuf.event_id = 4712;
|
|
sccb->evbuf.dbs = 1;
|
|
}
|
|
|
|
static int __init sclp_set_event_mask(unsigned long receive_mask,
|
|
unsigned long send_mask)
|
|
{
|
|
struct init_sccb *sccb = (void *) &sccb_early;
|
|
|
|
memset(sccb, 0, sizeof(*sccb));
|
|
sccb->header.length = sizeof(*sccb);
|
|
sccb->mask_length = sizeof(sccb_mask_t);
|
|
sccb->receive_mask = receive_mask;
|
|
sccb->send_mask = send_mask;
|
|
return sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_MASK, sccb);
|
|
}
|
|
|
|
static long __init sclp_hsa_size_init(void)
|
|
{
|
|
struct sdias_sccb *sccb = (void *) &sccb_early;
|
|
|
|
sccb_init_eq_size(sccb);
|
|
if (sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_DATA, sccb))
|
|
return -EIO;
|
|
if (sccb->evbuf.blk_cnt != 0)
|
|
return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE;
|
|
return 0;
|
|
}
|
|
|
|
static long __init sclp_hsa_copy_wait(void)
|
|
{
|
|
struct sccb_header *sccb = (void *) &sccb_early;
|
|
|
|
memset(sccb, 0, PAGE_SIZE);
|
|
sccb->length = PAGE_SIZE;
|
|
if (sclp_cmd_early(SCLP_CMDW_READ_EVENT_DATA, sccb))
|
|
return -EIO;
|
|
return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE;
|
|
}
|
|
|
|
unsigned long sclp_get_hsa_size(void)
|
|
{
|
|
return sclp_hsa_size;
|
|
}
|
|
|
|
void __init sclp_hsa_size_detect(void)
|
|
{
|
|
long size;
|
|
|
|
/* First try synchronous interface (LPAR) */
|
|
if (sclp_set_event_mask(0, 0x40000010))
|
|
return;
|
|
size = sclp_hsa_size_init();
|
|
if (size < 0)
|
|
return;
|
|
if (size != 0)
|
|
goto out;
|
|
/* Then try asynchronous interface (z/VM) */
|
|
if (sclp_set_event_mask(0x00000010, 0x40000010))
|
|
return;
|
|
size = sclp_hsa_size_init();
|
|
if (size < 0)
|
|
return;
|
|
size = sclp_hsa_copy_wait();
|
|
if (size < 0)
|
|
return;
|
|
out:
|
|
sclp_set_event_mask(0, 0);
|
|
sclp_hsa_size = size;
|
|
}
|