17eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky/*
27eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky *    SCLP OCF communication parameters sysfs interface
37eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky *
47eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky *    Copyright IBM Corp. 2011
57eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
67eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky */
77eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
87eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#define KMSG_COMPONENT "sclp_ocf"
97eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
107eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
117eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#include <linux/kernel.h>
127eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#include <linux/init.h>
137eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#include <linux/stat.h>
147eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#include <linux/device.h>
157eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#include <linux/string.h>
167eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#include <linux/ctype.h>
177eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#include <linux/kmod.h>
187eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#include <linux/timer.h>
197eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#include <linux/err.h>
207eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#include <asm/ebcdic.h>
217eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#include <asm/sclp.h>
227eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
237eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#include "sclp.h"
247eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
257eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#define OCF_LENGTH_HMC_NETWORK 8UL
267eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky#define OCF_LENGTH_CPC_NAME 8UL
277eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
287eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic char hmc_network[OCF_LENGTH_HMC_NETWORK + 1];
297eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic char cpc_name[OCF_LENGTH_CPC_NAME + 1];
307eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
317eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic DEFINE_SPINLOCK(sclp_ocf_lock);
327eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic struct work_struct sclp_ocf_change_work;
337eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
347eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic struct kset *ocf_kset;
357eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
367eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic void sclp_ocf_change_notify(struct work_struct *work)
377eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky{
387eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	kobject_uevent(&ocf_kset->kobj, KOBJ_CHANGE);
397eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky}
407eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
417eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky/* Handler for OCF event. Look for the CPC image name. */
427eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic void sclp_ocf_handler(struct evbuf_header *evbuf)
437eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky{
447eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	struct gds_vector *v;
457eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	struct gds_subvector *sv, *netid, *cpc;
467eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	size_t size;
477eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
487eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	/* Find the 0x9f00 block. */
497eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length,
507eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky				 0x9f00);
517eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	if (!v)
527eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		return;
537eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	/* Find the 0x9f22 block inside the 0x9f00 block. */
547eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	v = sclp_find_gds_vector(v + 1, (void *) v + v->length, 0x9f22);
557eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	if (!v)
567eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		return;
577eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	/* Find the 0x81 block inside the 0x9f22 block. */
587eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	sv = sclp_find_gds_subvector(v + 1, (void *) v + v->length, 0x81);
597eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	if (!sv)
607eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		return;
617eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	/* Find the 0x01 block inside the 0x81 block. */
627eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	netid = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 1);
637eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	/* Find the 0x02 block inside the 0x81 block. */
647eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	cpc = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 2);
657eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	/* Copy network name and cpc name. */
667eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	spin_lock(&sclp_ocf_lock);
677eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	if (netid) {
687eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		size = min(OCF_LENGTH_HMC_NETWORK, (size_t) netid->length);
697eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		memcpy(hmc_network, netid + 1, size);
707eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		EBCASC(hmc_network, size);
717eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		hmc_network[size] = 0;
727eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	}
737eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	if (cpc) {
747eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		size = min(OCF_LENGTH_CPC_NAME, (size_t) cpc->length);
757eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		memcpy(cpc_name, cpc + 1, size);
767eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		EBCASC(cpc_name, size);
777eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		cpc_name[size] = 0;
787eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	}
797eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	spin_unlock(&sclp_ocf_lock);
807eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	schedule_work(&sclp_ocf_change_work);
817eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky}
827eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
837eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic struct sclp_register sclp_ocf_event = {
847eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	.receive_mask = EVTYP_OCF_MASK,
857eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	.receiver_fn = sclp_ocf_handler,
867eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky};
877eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
887eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic ssize_t cpc_name_show(struct kobject *kobj,
897eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky			     struct kobj_attribute *attr, char *page)
907eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky{
917eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	int rc;
927eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
937eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	spin_lock_irq(&sclp_ocf_lock);
947eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	rc = snprintf(page, PAGE_SIZE, "%s\n", cpc_name);
957eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	spin_unlock_irq(&sclp_ocf_lock);
967eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	return rc;
977eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky}
987eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
997eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic struct kobj_attribute cpc_name_attr =
1007eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	__ATTR(cpc_name, 0444, cpc_name_show, NULL);
1017eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
1027eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic ssize_t hmc_network_show(struct kobject *kobj,
1037eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky				struct kobj_attribute *attr, char *page)
1047eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky{
1057eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	int rc;
1067eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
1077eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	spin_lock_irq(&sclp_ocf_lock);
1087eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	rc = snprintf(page, PAGE_SIZE, "%s\n", hmc_network);
1097eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	spin_unlock_irq(&sclp_ocf_lock);
1107eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	return rc;
1117eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky}
1127eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
1137eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic struct kobj_attribute hmc_network_attr =
1147eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	__ATTR(hmc_network, 0444, hmc_network_show, NULL);
1157eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
1167eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic struct attribute *ocf_attrs[] = {
1177eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	&cpc_name_attr.attr,
1187eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	&hmc_network_attr.attr,
1197eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	NULL,
1207eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky};
1217eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
1227eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic struct attribute_group ocf_attr_group = {
1237eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	.attrs = ocf_attrs,
1247eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky};
1257eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
1267eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskystatic int __init ocf_init(void)
1277eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky{
1287eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	int rc;
1297eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
1307eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	INIT_WORK(&sclp_ocf_change_work, sclp_ocf_change_notify);
1317eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	ocf_kset = kset_create_and_add("ocf", NULL, firmware_kobj);
1327eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	if (!ocf_kset)
1337eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		return -ENOMEM;
1347eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
1357eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	rc = sysfs_create_group(&ocf_kset->kobj, &ocf_attr_group);
1367eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	if (rc) {
1377eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		kset_unregister(ocf_kset);
1387eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky		return rc;
1397eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	}
1407eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
1417eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky	return sclp_register(&sclp_ocf_event);
1427eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky}
1437eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefsky
1447eb9d5bec552eff896ebf079386dc47e9dc2fc89Martin Schwidefskydevice_initcall(ocf_init);
145