linux/drivers/s390/char/sclp_config.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *    Copyright IBM Corp. 2007
   4 *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
   5 */
   6
   7#define KMSG_COMPONENT "sclp_config"
   8#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
   9
  10#include <linux/init.h>
  11#include <linux/errno.h>
  12#include <linux/cpu.h>
  13#include <linux/device.h>
  14#include <linux/workqueue.h>
  15#include <linux/slab.h>
  16#include <linux/sysfs.h>
  17#include <asm/smp.h>
  18
  19#include "sclp.h"
  20
  21struct conf_mgm_data {
  22        u8 reserved;
  23        u8 ev_qualifier;
  24} __attribute__((packed));
  25
  26#define OFB_DATA_MAX 64
  27
  28struct sclp_ofb_evbuf {
  29        struct evbuf_header header;
  30        struct conf_mgm_data cm_data;
  31        char ev_data[OFB_DATA_MAX];
  32} __packed;
  33
  34struct sclp_ofb_sccb {
  35        struct sccb_header header;
  36        struct sclp_ofb_evbuf ofb_evbuf;
  37} __packed;
  38
  39#define EV_QUAL_CPU_CHANGE      1
  40#define EV_QUAL_CAP_CHANGE      3
  41#define EV_QUAL_OPEN4BUSINESS   5
  42
  43static struct work_struct sclp_cpu_capability_work;
  44static struct work_struct sclp_cpu_change_work;
  45
  46static void sclp_cpu_capability_notify(struct work_struct *work)
  47{
  48        int cpu;
  49        struct device *dev;
  50
  51        s390_update_cpu_mhz();
  52        pr_info("CPU capability may have changed\n");
  53        cpus_read_lock();
  54        for_each_online_cpu(cpu) {
  55                dev = get_cpu_device(cpu);
  56                kobject_uevent(&dev->kobj, KOBJ_CHANGE);
  57        }
  58        cpus_read_unlock();
  59}
  60
  61static void __ref sclp_cpu_change_notify(struct work_struct *work)
  62{
  63        lock_device_hotplug();
  64        smp_rescan_cpus();
  65        unlock_device_hotplug();
  66}
  67
  68static void sclp_conf_receiver_fn(struct evbuf_header *evbuf)
  69{
  70        struct conf_mgm_data *cdata;
  71
  72        cdata = (struct conf_mgm_data *)(evbuf + 1);
  73        switch (cdata->ev_qualifier) {
  74        case EV_QUAL_CPU_CHANGE:
  75                schedule_work(&sclp_cpu_change_work);
  76                break;
  77        case EV_QUAL_CAP_CHANGE:
  78                schedule_work(&sclp_cpu_capability_work);
  79                break;
  80        }
  81}
  82
  83static struct sclp_register sclp_conf_register =
  84{
  85#ifdef CONFIG_SCLP_OFB
  86        .send_mask    = EVTYP_CONFMGMDATA_MASK,
  87#endif
  88        .receive_mask = EVTYP_CONFMGMDATA_MASK,
  89        .receiver_fn  = sclp_conf_receiver_fn,
  90};
  91
  92#ifdef CONFIG_SCLP_OFB
  93static int sclp_ofb_send_req(char *ev_data, size_t len)
  94{
  95        static DEFINE_MUTEX(send_mutex);
  96        struct sclp_ofb_sccb *sccb;
  97        int rc, response;
  98
  99        if (len > OFB_DATA_MAX)
 100                return -EINVAL;
 101        sccb = (struct sclp_ofb_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
 102        if (!sccb)
 103                return -ENOMEM;
 104        /* Setup SCCB for Control-Program Identification */
 105        sccb->header.length = sizeof(struct sclp_ofb_sccb);
 106        sccb->ofb_evbuf.header.length = sizeof(struct sclp_ofb_evbuf);
 107        sccb->ofb_evbuf.header.type = EVTYP_CONFMGMDATA;
 108        sccb->ofb_evbuf.cm_data.ev_qualifier = EV_QUAL_OPEN4BUSINESS;
 109        memcpy(sccb->ofb_evbuf.ev_data, ev_data, len);
 110
 111        if (!(sclp_conf_register.sclp_receive_mask & EVTYP_CONFMGMDATA_MASK))
 112                pr_warn("SCLP receiver did not register to receive "
 113                        "Configuration Management Data Events.\n");
 114
 115        mutex_lock(&send_mutex);
 116        rc = sclp_sync_request(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
 117        mutex_unlock(&send_mutex);
 118        if (rc)
 119                goto out;
 120        response = sccb->header.response_code;
 121        if (response != 0x0020) {
 122                pr_err("Open for Business request failed with response code "
 123                       "0x%04x\n", response);
 124                rc = -EIO;
 125        }
 126out:
 127        free_page((unsigned long)sccb);
 128        return rc;
 129}
 130
 131static ssize_t sysfs_ofb_data_write(struct file *filp, struct kobject *kobj,
 132                                    struct bin_attribute *bin_attr,
 133                                    char *buf, loff_t off, size_t count)
 134{
 135        int rc;
 136
 137        rc = sclp_ofb_send_req(buf, count);
 138        return rc ?: count;
 139}
 140
 141static const struct bin_attribute ofb_bin_attr = {
 142        .attr = {
 143                .name = "event_data",
 144                .mode = S_IWUSR,
 145        },
 146        .write = sysfs_ofb_data_write,
 147};
 148#endif
 149
 150static int __init sclp_ofb_setup(void)
 151{
 152#ifdef CONFIG_SCLP_OFB
 153        struct kset *ofb_kset;
 154        int rc;
 155
 156        ofb_kset = kset_create_and_add("ofb", NULL, firmware_kobj);
 157        if (!ofb_kset)
 158                return -ENOMEM;
 159        rc = sysfs_create_bin_file(&ofb_kset->kobj, &ofb_bin_attr);
 160        if (rc) {
 161                kset_unregister(ofb_kset);
 162                return rc;
 163        }
 164#endif
 165        return 0;
 166}
 167
 168static int __init sclp_conf_init(void)
 169{
 170        int rc;
 171
 172        INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify);
 173        INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify);
 174        rc = sclp_register(&sclp_conf_register);
 175        if (rc)
 176                return rc;
 177        return sclp_ofb_setup();
 178}
 179
 180__initcall(sclp_conf_init);
 181