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