Commit 9a564108 authored by Harald Freudenberger's avatar Harald Freudenberger Committed by Martin Schwidefsky

s390/zcrypt: Introduce QACT support for AP bus devices.

This patch introduces a new ap_qact() function which
exploits the PQAP(QACT) subfunction. QACT is a new
interface to Query the Ap Compatilibity Type based
on a given AP qid, type, mode and version.

Based on this new function the AP bus scan code is
slightly reworked to use this new interface for
querying the compatible type for each new AP queue
device detected. So new and unknown devices can
get automatically mapped to a compatible type and
handled without the need for toleration patches
for every new hardware.

The currently highest known hardware is CEX6S.
With this patch a possible successor can get
queried for a combatible type known by the device
driver without the need for an toleration patch.
Signed-off-by: default avatarHarald Freudenberger <freude@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 0acb1665
...@@ -116,6 +116,49 @@ static inline int ap_qci(void *config) ...@@ -116,6 +116,49 @@ static inline int ap_qci(void *config)
return reg1; return reg1;
} }
/*
* struct ap_qact_ap_info - used together with the
* ap_aqic() function to provide a convenient way
* to handle the ap info needed by the qact function.
*/
struct ap_qact_ap_info {
unsigned int _res1 : 3;
unsigned int mode : 3;
unsigned int _res2 : 26;
unsigned int cat : 8;
unsigned int _res3 : 8;
unsigned char ver[2];
};
/**
* ap_qact(): Query AP combatibility type.
* @qid: The AP queue number
* @apinfo: On input the info about the AP queue (content of GR1
* according to the AR). On output the alternate AP queue
* info provided by the qact function in GR2 is stored in.
*
* Returns AP queue status. Check response_code field for failures.
*/
static inline struct ap_queue_status ap_qact(ap_qid_t qid, int ifbit,
struct ap_qact_ap_info *apinfo)
{
register unsigned long reg0 asm ("0") = qid | (5UL << 24)
| ((ifbit & 0x01) << 22);
register struct ap_qact_ap_info reg1_in asm ("1") = *apinfo;
register struct ap_queue_status reg1_out asm ("1");
register unsigned long reg2_in asm ("2") = 0;
register struct ap_qact_ap_info reg2_out asm ("2");
asm volatile(
".long 0xb2af0000" /* PQAP(QACT) */
: "+d" (reg0), "+d" (reg1_in), "=d" (reg1_out),
"+d" (reg2_in), "=d" (reg2_out)
:
: "cc");
*apinfo = reg2_out;
return reg1_out;
}
/** /**
* ap_nqap(): Send message to adjunct processor queue. * ap_nqap(): Send message to adjunct processor queue.
* @qid: The AP queue number * @qid: The AP queue number
......
...@@ -176,6 +176,18 @@ static int ap_apft_available(void) ...@@ -176,6 +176,18 @@ static int ap_apft_available(void)
return test_facility(15); return test_facility(15);
} }
/*
* ap_qact_available(): Test if the PQAP(QACT) subfunction is available.
*
* Returns 1 if the QACT subfunction is available.
*/
static inline int ap_qact_available(void)
{
if (ap_configuration)
return ap_configuration->qact;
return 0;
}
/** /**
* ap_test_queue(): Test adjunct processor queue. * ap_test_queue(): Test adjunct processor queue.
* @qid: The AP queue number * @qid: The AP queue number
...@@ -987,6 +999,47 @@ static int ap_select_domain(void) ...@@ -987,6 +999,47 @@ static int ap_select_domain(void)
return -ENODEV; return -ENODEV;
} }
/*
* This function checks the type and returns either 0 for not
* supported or the highest compatible type value (which may
* include the input type value).
*/
static int ap_get_compatible_type(ap_qid_t qid, int rawtype, unsigned int func)
{
int comp_type = 0;
/* < CEX2A is not supported */
if (rawtype < AP_DEVICE_TYPE_CEX2A)
return 0;
/* up to CEX6 known and fully supported */
if (rawtype <= AP_DEVICE_TYPE_CEX6)
return rawtype;
/*
* unknown new type > CEX6, check for compatibility
* to the highest known and supported type which is
* currently CEX6 with the help of the QACT function.
*/
if (ap_qact_available()) {
struct ap_queue_status status;
struct ap_qact_ap_info apinfo = {0};
apinfo.mode = (func >> 26) & 0x07;
apinfo.cat = AP_DEVICE_TYPE_CEX6;
status = ap_qact(qid, 0, &apinfo);
if (status.response_code == AP_RESPONSE_NORMAL
&& apinfo.cat >= AP_DEVICE_TYPE_CEX2A
&& apinfo.cat <= AP_DEVICE_TYPE_CEX6)
comp_type = apinfo.cat;
}
if (!comp_type)
AP_DBF(DBF_WARN, "queue=%02x.%04x unable to map type %d\n",
AP_QID_CARD(qid), AP_QID_QUEUE(qid), rawtype);
else if (comp_type != rawtype)
AP_DBF(DBF_INFO, "queue=%02x.%04x map type %d to %d\n",
AP_QID_CARD(qid), AP_QID_QUEUE(qid), rawtype, comp_type);
return comp_type;
}
/* /*
* helper function to be used with bus_find_dev * helper function to be used with bus_find_dev
* matches for the card device with the given id * matches for the card device with the given id
...@@ -1014,8 +1067,8 @@ static void ap_scan_bus(struct work_struct *unused) ...@@ -1014,8 +1067,8 @@ static void ap_scan_bus(struct work_struct *unused)
struct ap_card *ac; struct ap_card *ac;
struct device *dev; struct device *dev;
ap_qid_t qid; ap_qid_t qid;
int depth = 0, type = 0; int comp_type, depth = 0, type = 0;
unsigned int functions = 0; unsigned int func = 0;
int rc, id, dom, borked, domains, defdomdevs = 0; int rc, id, dom, borked, domains, defdomdevs = 0;
AP_DBF(DBF_DEBUG, "ap_scan_bus running\n"); AP_DBF(DBF_DEBUG, "ap_scan_bus running\n");
...@@ -1066,12 +1119,12 @@ static void ap_scan_bus(struct work_struct *unused) ...@@ -1066,12 +1119,12 @@ static void ap_scan_bus(struct work_struct *unused)
} }
continue; continue;
} }
rc = ap_query_queue(qid, &depth, &type, &functions); rc = ap_query_queue(qid, &depth, &type, &func);
if (dev) { if (dev) {
spin_lock_bh(&aq->lock); spin_lock_bh(&aq->lock);
if (rc == -ENODEV || if (rc == -ENODEV ||
/* adapter reconfiguration */ /* adapter reconfiguration */
(ac && ac->functions != functions)) (ac && ac->functions != func))
aq->state = AP_STATE_BORKED; aq->state = AP_STATE_BORKED;
borked = aq->state == AP_STATE_BORKED; borked = aq->state == AP_STATE_BORKED;
spin_unlock_bh(&aq->lock); spin_unlock_bh(&aq->lock);
...@@ -1087,11 +1140,14 @@ static void ap_scan_bus(struct work_struct *unused) ...@@ -1087,11 +1140,14 @@ static void ap_scan_bus(struct work_struct *unused)
} }
if (rc) if (rc)
continue; continue;
/* new queue device needed */ /* a new queue device is needed, check out comp type */
comp_type = ap_get_compatible_type(qid, type, func);
if (!comp_type)
continue;
/* maybe a card device needs to be created first */
if (!ac) { if (!ac) {
/* but first create the card device */ ac = ap_card_create(id, depth, type,
ac = ap_card_create(id, depth, comp_type, func);
type, functions);
if (!ac) if (!ac)
continue; continue;
ac->ap_dev.device.bus = &ap_bus_type; ac->ap_dev.device.bus = &ap_bus_type;
...@@ -1109,7 +1165,7 @@ static void ap_scan_bus(struct work_struct *unused) ...@@ -1109,7 +1165,7 @@ static void ap_scan_bus(struct work_struct *unused)
get_device(&ac->ap_dev.device); get_device(&ac->ap_dev.device);
} }
/* now create the new queue device */ /* now create the new queue device */
aq = ap_queue_create(qid, type); aq = ap_queue_create(qid, comp_type);
if (!aq) if (!aq)
continue; continue;
aq->card = ac; aq->card = ac;
......
...@@ -250,8 +250,8 @@ void ap_queue_remove(struct ap_queue *aq); ...@@ -250,8 +250,8 @@ void ap_queue_remove(struct ap_queue *aq);
void ap_queue_suspend(struct ap_device *ap_dev); void ap_queue_suspend(struct ap_device *ap_dev);
void ap_queue_resume(struct ap_device *ap_dev); void ap_queue_resume(struct ap_device *ap_dev);
struct ap_card *ap_card_create(int id, int queue_depth, int device_type, struct ap_card *ap_card_create(int id, int queue_depth, int raw_device_type,
unsigned int device_functions); int comp_device_type, unsigned int functions);
int ap_module_init(void); int ap_module_init(void);
void ap_module_exit(void); void ap_module_exit(void);
......
...@@ -170,19 +170,20 @@ static void ap_card_device_release(struct device *dev) ...@@ -170,19 +170,20 @@ static void ap_card_device_release(struct device *dev)
kfree(ac); kfree(ac);
} }
struct ap_card *ap_card_create(int id, int queue_depth, int device_type, struct ap_card *ap_card_create(int id, int queue_depth, int raw_type,
unsigned int functions) int comp_type, unsigned int functions)
{ {
struct ap_card *ac; struct ap_card *ac;
ac = kzalloc(sizeof(*ac), GFP_KERNEL); ac = kzalloc(sizeof(*ac), GFP_KERNEL);
if (!ac) if (!ac)
return NULL; return NULL;
INIT_LIST_HEAD(&ac->list);
INIT_LIST_HEAD(&ac->queues); INIT_LIST_HEAD(&ac->queues);
ac->ap_dev.device.release = ap_card_device_release; ac->ap_dev.device.release = ap_card_device_release;
ac->ap_dev.device.type = &ap_card_type; ac->ap_dev.device.type = &ap_card_type;
ac->ap_dev.device_type = device_type; ac->ap_dev.device_type = comp_type;
ac->raw_hwtype = device_type; ac->raw_hwtype = raw_type;
ac->queue_depth = queue_depth; ac->queue_depth = queue_depth;
ac->functions = functions; ac->functions = functions;
ac->id = id; ac->id = id;
......
...@@ -630,6 +630,7 @@ struct ap_queue *ap_queue_create(ap_qid_t qid, int device_type) ...@@ -630,6 +630,7 @@ struct ap_queue *ap_queue_create(ap_qid_t qid, int device_type)
aq->state = AP_STATE_RESET_START; aq->state = AP_STATE_RESET_START;
aq->interrupt = AP_INTR_DISABLED; aq->interrupt = AP_INTR_DISABLED;
spin_lock_init(&aq->lock); spin_lock_init(&aq->lock);
INIT_LIST_HEAD(&aq->list);
INIT_LIST_HEAD(&aq->pendingq); INIT_LIST_HEAD(&aq->pendingq);
INIT_LIST_HEAD(&aq->requestq); INIT_LIST_HEAD(&aq->requestq);
setup_timer(&aq->timeout, ap_request_timeout, (unsigned long) aq); setup_timer(&aq->timeout, ap_request_timeout, (unsigned long) aq);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment