mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
3.6 KiB
171 lines
3.6 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* PowerNV OPAL Power-Shift-Ratio interface |
|
* |
|
* Copyright 2017 IBM Corp. |
|
*/ |
|
|
|
#define pr_fmt(fmt) "opal-psr: " fmt |
|
|
|
#include <linux/of.h> |
|
#include <linux/kobject.h> |
|
#include <linux/slab.h> |
|
|
|
#include <asm/opal.h> |
|
|
|
static DEFINE_MUTEX(psr_mutex); |
|
|
|
static struct kobject *psr_kobj; |
|
|
|
static struct psr_attr { |
|
u32 handle; |
|
struct kobj_attribute attr; |
|
} *psr_attrs; |
|
|
|
static ssize_t psr_show(struct kobject *kobj, struct kobj_attribute *attr, |
|
char *buf) |
|
{ |
|
struct psr_attr *psr_attr = container_of(attr, struct psr_attr, attr); |
|
struct opal_msg msg; |
|
int psr, ret, token; |
|
|
|
token = opal_async_get_token_interruptible(); |
|
if (token < 0) { |
|
pr_devel("Failed to get token\n"); |
|
return token; |
|
} |
|
|
|
ret = mutex_lock_interruptible(&psr_mutex); |
|
if (ret) |
|
goto out_token; |
|
|
|
ret = opal_get_power_shift_ratio(psr_attr->handle, token, |
|
(u32 *)__pa(&psr)); |
|
switch (ret) { |
|
case OPAL_ASYNC_COMPLETION: |
|
ret = opal_async_wait_response(token, &msg); |
|
if (ret) { |
|
pr_devel("Failed to wait for the async response\n"); |
|
ret = -EIO; |
|
goto out; |
|
} |
|
ret = opal_error_code(opal_get_async_rc(msg)); |
|
if (!ret) { |
|
ret = sprintf(buf, "%u\n", be32_to_cpu(psr)); |
|
if (ret < 0) |
|
ret = -EIO; |
|
} |
|
break; |
|
case OPAL_SUCCESS: |
|
ret = sprintf(buf, "%u\n", be32_to_cpu(psr)); |
|
if (ret < 0) |
|
ret = -EIO; |
|
break; |
|
default: |
|
ret = opal_error_code(ret); |
|
} |
|
|
|
out: |
|
mutex_unlock(&psr_mutex); |
|
out_token: |
|
opal_async_release_token(token); |
|
return ret; |
|
} |
|
|
|
static ssize_t psr_store(struct kobject *kobj, struct kobj_attribute *attr, |
|
const char *buf, size_t count) |
|
{ |
|
struct psr_attr *psr_attr = container_of(attr, struct psr_attr, attr); |
|
struct opal_msg msg; |
|
int psr, ret, token; |
|
|
|
ret = kstrtoint(buf, 0, &psr); |
|
if (ret) |
|
return ret; |
|
|
|
token = opal_async_get_token_interruptible(); |
|
if (token < 0) { |
|
pr_devel("Failed to get token\n"); |
|
return token; |
|
} |
|
|
|
ret = mutex_lock_interruptible(&psr_mutex); |
|
if (ret) |
|
goto out_token; |
|
|
|
ret = opal_set_power_shift_ratio(psr_attr->handle, token, psr); |
|
switch (ret) { |
|
case OPAL_ASYNC_COMPLETION: |
|
ret = opal_async_wait_response(token, &msg); |
|
if (ret) { |
|
pr_devel("Failed to wait for the async response\n"); |
|
ret = -EIO; |
|
goto out; |
|
} |
|
ret = opal_error_code(opal_get_async_rc(msg)); |
|
if (!ret) |
|
ret = count; |
|
break; |
|
case OPAL_SUCCESS: |
|
ret = count; |
|
break; |
|
default: |
|
ret = opal_error_code(ret); |
|
} |
|
|
|
out: |
|
mutex_unlock(&psr_mutex); |
|
out_token: |
|
opal_async_release_token(token); |
|
return ret; |
|
} |
|
|
|
void __init opal_psr_init(void) |
|
{ |
|
struct device_node *psr, *node; |
|
int i = 0; |
|
|
|
psr = of_find_compatible_node(NULL, NULL, |
|
"ibm,opal-power-shift-ratio"); |
|
if (!psr) { |
|
pr_devel("Power-shift-ratio node not found\n"); |
|
return; |
|
} |
|
|
|
psr_attrs = kcalloc(of_get_child_count(psr), sizeof(*psr_attrs), |
|
GFP_KERNEL); |
|
if (!psr_attrs) |
|
return; |
|
|
|
psr_kobj = kobject_create_and_add("psr", opal_kobj); |
|
if (!psr_kobj) { |
|
pr_warn("Failed to create psr kobject\n"); |
|
goto out; |
|
} |
|
|
|
for_each_child_of_node(psr, node) { |
|
if (of_property_read_u32(node, "handle", |
|
&psr_attrs[i].handle)) |
|
goto out_kobj; |
|
|
|
sysfs_attr_init(&psr_attrs[i].attr.attr); |
|
if (of_property_read_string(node, "label", |
|
&psr_attrs[i].attr.attr.name)) |
|
goto out_kobj; |
|
psr_attrs[i].attr.attr.mode = 0664; |
|
psr_attrs[i].attr.show = psr_show; |
|
psr_attrs[i].attr.store = psr_store; |
|
if (sysfs_create_file(psr_kobj, &psr_attrs[i].attr.attr)) { |
|
pr_devel("Failed to create psr sysfs file %s\n", |
|
psr_attrs[i].attr.attr.name); |
|
goto out_kobj; |
|
} |
|
i++; |
|
} |
|
|
|
return; |
|
out_kobj: |
|
kobject_put(psr_kobj); |
|
out: |
|
kfree(psr_attrs); |
|
}
|
|
|