opal-sysparam.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /*
  2. * PowerNV system parameter code
  3. *
  4. * Copyright (C) 2013 IBM
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19. */
  20. #include <linux/kobject.h>
  21. #include <linux/mutex.h>
  22. #include <linux/slab.h>
  23. #include <linux/of.h>
  24. #include <linux/gfp.h>
  25. #include <linux/stat.h>
  26. #include <asm/opal.h>
  27. #define MAX_PARAM_DATA_LEN 64
  28. static DEFINE_MUTEX(opal_sysparam_mutex);
  29. static struct kobject *sysparam_kobj;
  30. static void *param_data_buf;
  31. struct param_attr {
  32. struct list_head list;
  33. u32 param_id;
  34. u32 param_size;
  35. struct kobj_attribute kobj_attr;
  36. };
  37. static ssize_t opal_get_sys_param(u32 param_id, u32 length, void *buffer)
  38. {
  39. struct opal_msg msg;
  40. ssize_t ret;
  41. int token;
  42. token = opal_async_get_token_interruptible();
  43. if (token < 0) {
  44. if (token != -ERESTARTSYS)
  45. pr_err("%s: Couldn't get the token, returning\n",
  46. __func__);
  47. ret = token;
  48. goto out;
  49. }
  50. ret = opal_get_param(token, param_id, (u64)buffer, length);
  51. if (ret != OPAL_ASYNC_COMPLETION)
  52. goto out_token;
  53. ret = opal_async_wait_response(token, &msg);
  54. if (ret) {
  55. pr_err("%s: Failed to wait for the async response, %zd\n",
  56. __func__, ret);
  57. goto out_token;
  58. }
  59. ret = be64_to_cpu(msg.params[1]);
  60. out_token:
  61. opal_async_release_token(token);
  62. out:
  63. return ret;
  64. }
  65. static int opal_set_sys_param(u32 param_id, u32 length, void *buffer)
  66. {
  67. struct opal_msg msg;
  68. int ret, token;
  69. token = opal_async_get_token_interruptible();
  70. if (token < 0) {
  71. if (token != -ERESTARTSYS)
  72. pr_err("%s: Couldn't get the token, returning\n",
  73. __func__);
  74. ret = token;
  75. goto out;
  76. }
  77. ret = opal_set_param(token, param_id, (u64)buffer, length);
  78. if (ret != OPAL_ASYNC_COMPLETION)
  79. goto out_token;
  80. ret = opal_async_wait_response(token, &msg);
  81. if (ret) {
  82. pr_err("%s: Failed to wait for the async response, %d\n",
  83. __func__, ret);
  84. goto out_token;
  85. }
  86. ret = be64_to_cpu(msg.params[1]);
  87. out_token:
  88. opal_async_release_token(token);
  89. out:
  90. return ret;
  91. }
  92. static ssize_t sys_param_show(struct kobject *kobj,
  93. struct kobj_attribute *kobj_attr, char *buf)
  94. {
  95. struct param_attr *attr = container_of(kobj_attr, struct param_attr,
  96. kobj_attr);
  97. ssize_t ret;
  98. mutex_lock(&opal_sysparam_mutex);
  99. ret = opal_get_sys_param(attr->param_id, attr->param_size,
  100. param_data_buf);
  101. if (ret)
  102. goto out;
  103. memcpy(buf, param_data_buf, attr->param_size);
  104. ret = attr->param_size;
  105. out:
  106. mutex_unlock(&opal_sysparam_mutex);
  107. return ret;
  108. }
  109. static ssize_t sys_param_store(struct kobject *kobj,
  110. struct kobj_attribute *kobj_attr, const char *buf, size_t count)
  111. {
  112. struct param_attr *attr = container_of(kobj_attr, struct param_attr,
  113. kobj_attr);
  114. ssize_t ret;
  115. /* MAX_PARAM_DATA_LEN is sizeof(param_data_buf) */
  116. if (count > MAX_PARAM_DATA_LEN)
  117. count = MAX_PARAM_DATA_LEN;
  118. mutex_lock(&opal_sysparam_mutex);
  119. memcpy(param_data_buf, buf, count);
  120. ret = opal_set_sys_param(attr->param_id, attr->param_size,
  121. param_data_buf);
  122. mutex_unlock(&opal_sysparam_mutex);
  123. if (!ret)
  124. ret = count;
  125. return ret;
  126. }
  127. void __init opal_sys_param_init(void)
  128. {
  129. struct device_node *sysparam;
  130. struct param_attr *attr;
  131. u32 *id, *size;
  132. int count, i;
  133. u8 *perm;
  134. if (!opal_kobj) {
  135. pr_warn("SYSPARAM: opal kobject is not available\n");
  136. goto out;
  137. }
  138. sysparam_kobj = kobject_create_and_add("sysparams", opal_kobj);
  139. if (!sysparam_kobj) {
  140. pr_err("SYSPARAM: Failed to create sysparam kobject\n");
  141. goto out;
  142. }
  143. /* Allocate big enough buffer for any get/set transactions */
  144. param_data_buf = kzalloc(MAX_PARAM_DATA_LEN, GFP_KERNEL);
  145. if (!param_data_buf) {
  146. pr_err("SYSPARAM: Failed to allocate memory for param data "
  147. "buf\n");
  148. goto out_kobj_put;
  149. }
  150. sysparam = of_find_node_by_path("/ibm,opal/sysparams");
  151. if (!sysparam) {
  152. pr_err("SYSPARAM: Opal sysparam node not found\n");
  153. goto out_param_buf;
  154. }
  155. if (!of_device_is_compatible(sysparam, "ibm,opal-sysparams")) {
  156. pr_err("SYSPARAM: Opal sysparam node not compatible\n");
  157. goto out_node_put;
  158. }
  159. /* Number of parameters exposed through DT */
  160. count = of_property_count_strings(sysparam, "param-name");
  161. if (count < 0) {
  162. pr_err("SYSPARAM: No string found of property param-name in "
  163. "the node %s\n", sysparam->name);
  164. goto out_node_put;
  165. }
  166. id = kzalloc(sizeof(*id) * count, GFP_KERNEL);
  167. if (!id) {
  168. pr_err("SYSPARAM: Failed to allocate memory to read parameter "
  169. "id\n");
  170. goto out_node_put;
  171. }
  172. size = kzalloc(sizeof(*size) * count, GFP_KERNEL);
  173. if (!size) {
  174. pr_err("SYSPARAM: Failed to allocate memory to read parameter "
  175. "size\n");
  176. goto out_free_id;
  177. }
  178. perm = kzalloc(sizeof(*perm) * count, GFP_KERNEL);
  179. if (!perm) {
  180. pr_err("SYSPARAM: Failed to allocate memory to read supported "
  181. "action on the parameter");
  182. goto out_free_size;
  183. }
  184. if (of_property_read_u32_array(sysparam, "param-id", id, count)) {
  185. pr_err("SYSPARAM: Missing property param-id in the DT\n");
  186. goto out_free_perm;
  187. }
  188. if (of_property_read_u32_array(sysparam, "param-len", size, count)) {
  189. pr_err("SYSPARAM: Missing property param-len in the DT\n");
  190. goto out_free_perm;
  191. }
  192. if (of_property_read_u8_array(sysparam, "param-perm", perm, count)) {
  193. pr_err("SYSPARAM: Missing property param-perm in the DT\n");
  194. goto out_free_perm;
  195. }
  196. attr = kzalloc(sizeof(*attr) * count, GFP_KERNEL);
  197. if (!attr) {
  198. pr_err("SYSPARAM: Failed to allocate memory for parameter "
  199. "attributes\n");
  200. goto out_free_perm;
  201. }
  202. /* For each of the parameters, populate the parameter attributes */
  203. for (i = 0; i < count; i++) {
  204. if (size[i] > MAX_PARAM_DATA_LEN) {
  205. pr_warn("SYSPARAM: Not creating parameter %d as size "
  206. "exceeds buffer length\n", i);
  207. continue;
  208. }
  209. sysfs_attr_init(&attr[i].kobj_attr.attr);
  210. attr[i].param_id = id[i];
  211. attr[i].param_size = size[i];
  212. if (of_property_read_string_index(sysparam, "param-name", i,
  213. &attr[i].kobj_attr.attr.name))
  214. continue;
  215. /* If the parameter is read-only or read-write */
  216. switch (perm[i] & 3) {
  217. case OPAL_SYSPARAM_READ:
  218. attr[i].kobj_attr.attr.mode = S_IRUGO;
  219. break;
  220. case OPAL_SYSPARAM_WRITE:
  221. attr[i].kobj_attr.attr.mode = S_IWUSR;
  222. break;
  223. case OPAL_SYSPARAM_RW:
  224. attr[i].kobj_attr.attr.mode = S_IRUGO | S_IWUSR;
  225. break;
  226. default:
  227. break;
  228. }
  229. attr[i].kobj_attr.show = sys_param_show;
  230. attr[i].kobj_attr.store = sys_param_store;
  231. if (sysfs_create_file(sysparam_kobj, &attr[i].kobj_attr.attr)) {
  232. pr_err("SYSPARAM: Failed to create sysfs file %s\n",
  233. attr[i].kobj_attr.attr.name);
  234. goto out_free_attr;
  235. }
  236. }
  237. kfree(perm);
  238. kfree(size);
  239. kfree(id);
  240. of_node_put(sysparam);
  241. return;
  242. out_free_attr:
  243. kfree(attr);
  244. out_free_perm:
  245. kfree(perm);
  246. out_free_size:
  247. kfree(size);
  248. out_free_id:
  249. kfree(id);
  250. out_node_put:
  251. of_node_put(sysparam);
  252. out_param_buf:
  253. kfree(param_data_buf);
  254. out_kobj_put:
  255. kobject_put(sysparam_kobj);
  256. out:
  257. return;
  258. }