1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * SCLP OCF communication parameters sysfs interface
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright IBM Corp. 2011
6*4882a593Smuzhiyun * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #define KMSG_COMPONENT "sclp_ocf"
10*4882a593Smuzhiyun #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/kernel.h>
13*4882a593Smuzhiyun #include <linux/init.h>
14*4882a593Smuzhiyun #include <linux/stat.h>
15*4882a593Smuzhiyun #include <linux/device.h>
16*4882a593Smuzhiyun #include <linux/string.h>
17*4882a593Smuzhiyun #include <linux/ctype.h>
18*4882a593Smuzhiyun #include <linux/kmod.h>
19*4882a593Smuzhiyun #include <linux/timer.h>
20*4882a593Smuzhiyun #include <linux/err.h>
21*4882a593Smuzhiyun #include <asm/ebcdic.h>
22*4882a593Smuzhiyun #include <asm/sclp.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #include "sclp.h"
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #define OCF_LENGTH_HMC_NETWORK 8UL
27*4882a593Smuzhiyun #define OCF_LENGTH_CPC_NAME 8UL
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun static char hmc_network[OCF_LENGTH_HMC_NETWORK + 1];
30*4882a593Smuzhiyun static char cpc_name[OCF_LENGTH_CPC_NAME]; /* in EBCDIC */
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun static DEFINE_SPINLOCK(sclp_ocf_lock);
33*4882a593Smuzhiyun static struct work_struct sclp_ocf_change_work;
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun static struct kset *ocf_kset;
36*4882a593Smuzhiyun
sclp_ocf_change_notify(struct work_struct * work)37*4882a593Smuzhiyun static void sclp_ocf_change_notify(struct work_struct *work)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun kobject_uevent(&ocf_kset->kobj, KOBJ_CHANGE);
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun /* Handler for OCF event. Look for the CPC image name. */
sclp_ocf_handler(struct evbuf_header * evbuf)43*4882a593Smuzhiyun static void sclp_ocf_handler(struct evbuf_header *evbuf)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun struct gds_vector *v;
46*4882a593Smuzhiyun struct gds_subvector *sv, *netid, *cpc;
47*4882a593Smuzhiyun size_t size;
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun /* Find the 0x9f00 block. */
50*4882a593Smuzhiyun v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length,
51*4882a593Smuzhiyun 0x9f00);
52*4882a593Smuzhiyun if (!v)
53*4882a593Smuzhiyun return;
54*4882a593Smuzhiyun /* Find the 0x9f22 block inside the 0x9f00 block. */
55*4882a593Smuzhiyun v = sclp_find_gds_vector(v + 1, (void *) v + v->length, 0x9f22);
56*4882a593Smuzhiyun if (!v)
57*4882a593Smuzhiyun return;
58*4882a593Smuzhiyun /* Find the 0x81 block inside the 0x9f22 block. */
59*4882a593Smuzhiyun sv = sclp_find_gds_subvector(v + 1, (void *) v + v->length, 0x81);
60*4882a593Smuzhiyun if (!sv)
61*4882a593Smuzhiyun return;
62*4882a593Smuzhiyun /* Find the 0x01 block inside the 0x81 block. */
63*4882a593Smuzhiyun netid = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 1);
64*4882a593Smuzhiyun /* Find the 0x02 block inside the 0x81 block. */
65*4882a593Smuzhiyun cpc = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 2);
66*4882a593Smuzhiyun /* Copy network name and cpc name. */
67*4882a593Smuzhiyun spin_lock(&sclp_ocf_lock);
68*4882a593Smuzhiyun if (netid) {
69*4882a593Smuzhiyun size = min(OCF_LENGTH_HMC_NETWORK, (size_t) netid->length);
70*4882a593Smuzhiyun memcpy(hmc_network, netid + 1, size);
71*4882a593Smuzhiyun EBCASC(hmc_network, size);
72*4882a593Smuzhiyun hmc_network[size] = 0;
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun if (cpc) {
75*4882a593Smuzhiyun size = min(OCF_LENGTH_CPC_NAME, (size_t) cpc->length);
76*4882a593Smuzhiyun memset(cpc_name, 0, OCF_LENGTH_CPC_NAME);
77*4882a593Smuzhiyun memcpy(cpc_name, cpc + 1, size);
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun spin_unlock(&sclp_ocf_lock);
80*4882a593Smuzhiyun schedule_work(&sclp_ocf_change_work);
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun static struct sclp_register sclp_ocf_event = {
84*4882a593Smuzhiyun .receive_mask = EVTYP_OCF_MASK,
85*4882a593Smuzhiyun .receiver_fn = sclp_ocf_handler,
86*4882a593Smuzhiyun };
87*4882a593Smuzhiyun
sclp_ocf_cpc_name_copy(char * dst)88*4882a593Smuzhiyun void sclp_ocf_cpc_name_copy(char *dst)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun spin_lock_irq(&sclp_ocf_lock);
91*4882a593Smuzhiyun memcpy(dst, cpc_name, OCF_LENGTH_CPC_NAME);
92*4882a593Smuzhiyun spin_unlock_irq(&sclp_ocf_lock);
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun EXPORT_SYMBOL(sclp_ocf_cpc_name_copy);
95*4882a593Smuzhiyun
cpc_name_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)96*4882a593Smuzhiyun static ssize_t cpc_name_show(struct kobject *kobj,
97*4882a593Smuzhiyun struct kobj_attribute *attr, char *page)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun char name[OCF_LENGTH_CPC_NAME + 1];
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun sclp_ocf_cpc_name_copy(name);
102*4882a593Smuzhiyun name[OCF_LENGTH_CPC_NAME] = 0;
103*4882a593Smuzhiyun EBCASC(name, OCF_LENGTH_CPC_NAME);
104*4882a593Smuzhiyun return snprintf(page, PAGE_SIZE, "%s\n", name);
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun static struct kobj_attribute cpc_name_attr =
108*4882a593Smuzhiyun __ATTR(cpc_name, 0444, cpc_name_show, NULL);
109*4882a593Smuzhiyun
hmc_network_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)110*4882a593Smuzhiyun static ssize_t hmc_network_show(struct kobject *kobj,
111*4882a593Smuzhiyun struct kobj_attribute *attr, char *page)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun int rc;
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun spin_lock_irq(&sclp_ocf_lock);
116*4882a593Smuzhiyun rc = snprintf(page, PAGE_SIZE, "%s\n", hmc_network);
117*4882a593Smuzhiyun spin_unlock_irq(&sclp_ocf_lock);
118*4882a593Smuzhiyun return rc;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun static struct kobj_attribute hmc_network_attr =
122*4882a593Smuzhiyun __ATTR(hmc_network, 0444, hmc_network_show, NULL);
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun static struct attribute *ocf_attrs[] = {
125*4882a593Smuzhiyun &cpc_name_attr.attr,
126*4882a593Smuzhiyun &hmc_network_attr.attr,
127*4882a593Smuzhiyun NULL,
128*4882a593Smuzhiyun };
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun static const struct attribute_group ocf_attr_group = {
131*4882a593Smuzhiyun .attrs = ocf_attrs,
132*4882a593Smuzhiyun };
133*4882a593Smuzhiyun
ocf_init(void)134*4882a593Smuzhiyun static int __init ocf_init(void)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun int rc;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun INIT_WORK(&sclp_ocf_change_work, sclp_ocf_change_notify);
139*4882a593Smuzhiyun ocf_kset = kset_create_and_add("ocf", NULL, firmware_kobj);
140*4882a593Smuzhiyun if (!ocf_kset)
141*4882a593Smuzhiyun return -ENOMEM;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun rc = sysfs_create_group(&ocf_kset->kobj, &ocf_attr_group);
144*4882a593Smuzhiyun if (rc) {
145*4882a593Smuzhiyun kset_unregister(ocf_kset);
146*4882a593Smuzhiyun return rc;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun return sclp_register(&sclp_ocf_event);
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun device_initcall(ocf_init);
153