0001
0002
0003
0004
0005
0006
0007
0008 #define pr_fmt(fmt) "opal-psr: " fmt
0009
0010 #include <linux/of.h>
0011 #include <linux/kobject.h>
0012 #include <linux/slab.h>
0013
0014 #include <asm/opal.h>
0015
0016 static DEFINE_MUTEX(psr_mutex);
0017
0018 static struct kobject *psr_kobj;
0019
0020 static struct psr_attr {
0021 u32 handle;
0022 struct kobj_attribute attr;
0023 } *psr_attrs;
0024
0025 static ssize_t psr_show(struct kobject *kobj, struct kobj_attribute *attr,
0026 char *buf)
0027 {
0028 struct psr_attr *psr_attr = container_of(attr, struct psr_attr, attr);
0029 struct opal_msg msg;
0030 int psr, ret, token;
0031
0032 token = opal_async_get_token_interruptible();
0033 if (token < 0) {
0034 pr_devel("Failed to get token\n");
0035 return token;
0036 }
0037
0038 ret = mutex_lock_interruptible(&psr_mutex);
0039 if (ret)
0040 goto out_token;
0041
0042 ret = opal_get_power_shift_ratio(psr_attr->handle, token,
0043 (u32 *)__pa(&psr));
0044 switch (ret) {
0045 case OPAL_ASYNC_COMPLETION:
0046 ret = opal_async_wait_response(token, &msg);
0047 if (ret) {
0048 pr_devel("Failed to wait for the async response\n");
0049 ret = -EIO;
0050 goto out;
0051 }
0052 ret = opal_error_code(opal_get_async_rc(msg));
0053 if (!ret) {
0054 ret = sprintf(buf, "%u\n", be32_to_cpu(psr));
0055 if (ret < 0)
0056 ret = -EIO;
0057 }
0058 break;
0059 case OPAL_SUCCESS:
0060 ret = sprintf(buf, "%u\n", be32_to_cpu(psr));
0061 if (ret < 0)
0062 ret = -EIO;
0063 break;
0064 default:
0065 ret = opal_error_code(ret);
0066 }
0067
0068 out:
0069 mutex_unlock(&psr_mutex);
0070 out_token:
0071 opal_async_release_token(token);
0072 return ret;
0073 }
0074
0075 static ssize_t psr_store(struct kobject *kobj, struct kobj_attribute *attr,
0076 const char *buf, size_t count)
0077 {
0078 struct psr_attr *psr_attr = container_of(attr, struct psr_attr, attr);
0079 struct opal_msg msg;
0080 int psr, ret, token;
0081
0082 ret = kstrtoint(buf, 0, &psr);
0083 if (ret)
0084 return ret;
0085
0086 token = opal_async_get_token_interruptible();
0087 if (token < 0) {
0088 pr_devel("Failed to get token\n");
0089 return token;
0090 }
0091
0092 ret = mutex_lock_interruptible(&psr_mutex);
0093 if (ret)
0094 goto out_token;
0095
0096 ret = opal_set_power_shift_ratio(psr_attr->handle, token, psr);
0097 switch (ret) {
0098 case OPAL_ASYNC_COMPLETION:
0099 ret = opal_async_wait_response(token, &msg);
0100 if (ret) {
0101 pr_devel("Failed to wait for the async response\n");
0102 ret = -EIO;
0103 goto out;
0104 }
0105 ret = opal_error_code(opal_get_async_rc(msg));
0106 if (!ret)
0107 ret = count;
0108 break;
0109 case OPAL_SUCCESS:
0110 ret = count;
0111 break;
0112 default:
0113 ret = opal_error_code(ret);
0114 }
0115
0116 out:
0117 mutex_unlock(&psr_mutex);
0118 out_token:
0119 opal_async_release_token(token);
0120 return ret;
0121 }
0122
0123 void __init opal_psr_init(void)
0124 {
0125 struct device_node *psr, *node;
0126 int i = 0;
0127
0128 psr = of_find_compatible_node(NULL, NULL,
0129 "ibm,opal-power-shift-ratio");
0130 if (!psr) {
0131 pr_devel("Power-shift-ratio node not found\n");
0132 return;
0133 }
0134
0135 psr_attrs = kcalloc(of_get_child_count(psr), sizeof(*psr_attrs),
0136 GFP_KERNEL);
0137 if (!psr_attrs)
0138 return;
0139
0140 psr_kobj = kobject_create_and_add("psr", opal_kobj);
0141 if (!psr_kobj) {
0142 pr_warn("Failed to create psr kobject\n");
0143 goto out;
0144 }
0145
0146 for_each_child_of_node(psr, node) {
0147 if (of_property_read_u32(node, "handle",
0148 &psr_attrs[i].handle))
0149 goto out_kobj;
0150
0151 sysfs_attr_init(&psr_attrs[i].attr.attr);
0152 if (of_property_read_string(node, "label",
0153 &psr_attrs[i].attr.attr.name))
0154 goto out_kobj;
0155 psr_attrs[i].attr.attr.mode = 0664;
0156 psr_attrs[i].attr.show = psr_show;
0157 psr_attrs[i].attr.store = psr_store;
0158 if (sysfs_create_file(psr_kobj, &psr_attrs[i].attr.attr)) {
0159 pr_devel("Failed to create psr sysfs file %s\n",
0160 psr_attrs[i].attr.attr.name);
0161 goto out_kobj;
0162 }
0163 i++;
0164 }
0165
0166 return;
0167 out_kobj:
0168 kobject_put(psr_kobj);
0169 out:
0170 kfree(psr_attrs);
0171 }