]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/of/configfs.c
UBUNTU: [Config] drm: disable support for alien gpu (!BCM)
[mirror_ubuntu-artful-kernel.git] / drivers / of / configfs.c
1 /*
2 * Configfs entries for device-tree
3 *
4 * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11 #include <linux/ctype.h>
12 #include <linux/cpu.h>
13 #include <linux/module.h>
14 #include <linux/of.h>
15 #include <linux/of_fdt.h>
16 #include <linux/spinlock.h>
17 #include <linux/slab.h>
18 #include <linux/proc_fs.h>
19 #include <linux/configfs.h>
20 #include <linux/types.h>
21 #include <linux/stat.h>
22 #include <linux/limits.h>
23 #include <linux/file.h>
24 #include <linux/vmalloc.h>
25 #include <linux/firmware.h>
26 #include <linux/sizes.h>
27
28 #include "of_private.h"
29
30 struct cfs_overlay_item {
31 struct config_item item;
32
33 char path[PATH_MAX];
34
35 const struct firmware *fw;
36 struct device_node *overlay;
37 int ov_id;
38
39 void *dtbo;
40 int dtbo_size;
41 };
42
43 static int create_overlay(struct cfs_overlay_item *overlay, void *blob)
44 {
45 int err;
46
47 /* unflatten the tree */
48 of_fdt_unflatten_tree(blob, NULL, &overlay->overlay);
49 if (overlay->overlay == NULL) {
50 pr_err("%s: failed to unflatten tree\n", __func__);
51 err = -EINVAL;
52 goto out_err;
53 }
54 pr_debug("%s: unflattened OK\n", __func__);
55
56 /* mark it as detached */
57 of_node_set_flag(overlay->overlay, OF_DETACHED);
58
59 /* perform resolution */
60 err = of_resolve_phandles(overlay->overlay);
61 if (err != 0) {
62 pr_err("%s: Failed to resolve tree\n", __func__);
63 goto out_err;
64 }
65 pr_debug("%s: resolved OK\n", __func__);
66
67 err = of_overlay_create(overlay->overlay);
68 if (err < 0) {
69 pr_err("%s: Failed to create overlay (err=%d)\n",
70 __func__, err);
71 goto out_err;
72 }
73 overlay->ov_id = err;
74
75 out_err:
76 return err;
77 }
78
79 static inline struct cfs_overlay_item *to_cfs_overlay_item(
80 struct config_item *item)
81 {
82 return item ? container_of(item, struct cfs_overlay_item, item) : NULL;
83 }
84
85 static ssize_t cfs_overlay_item_path_show(struct config_item *item,
86 char *page)
87 {
88 struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
89 return sprintf(page, "%s\n", overlay->path);
90 }
91
92 static ssize_t cfs_overlay_item_path_store(struct config_item *item,
93 const char *page, size_t count)
94 {
95 struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
96 const char *p = page;
97 char *s;
98 int err;
99
100 /* if it's set do not allow changes */
101 if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
102 return -EPERM;
103
104 /* copy to path buffer (and make sure it's always zero terminated */
105 count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p);
106 overlay->path[sizeof(overlay->path) - 1] = '\0';
107
108 /* strip trailing newlines */
109 s = overlay->path + strlen(overlay->path);
110 while (s > overlay->path && *--s == '\n')
111 *s = '\0';
112
113 pr_debug("%s: path is '%s'\n", __func__, overlay->path);
114
115 err = request_firmware(&overlay->fw, overlay->path, NULL);
116 if (err != 0)
117 goto out_err;
118
119 err = create_overlay(overlay, (void *)overlay->fw->data);
120 if (err != 0)
121 goto out_err;
122
123 return count;
124
125 out_err:
126
127 release_firmware(overlay->fw);
128 overlay->fw = NULL;
129
130 overlay->path[0] = '\0';
131 return err;
132 }
133
134 static ssize_t cfs_overlay_item_status_show(struct config_item *item,
135 char *page)
136 {
137 struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
138
139 return sprintf(page, "%s\n",
140 overlay->ov_id >= 0 ? "applied" : "unapplied");
141 }
142
143 CONFIGFS_ATTR(cfs_overlay_item_, path);
144 CONFIGFS_ATTR_RO(cfs_overlay_item_, status);
145
146 static struct configfs_attribute *cfs_overlay_attrs[] = {
147 &cfs_overlay_item_attr_path,
148 &cfs_overlay_item_attr_status,
149 NULL,
150 };
151
152 ssize_t cfs_overlay_item_dtbo_read(struct config_item *item,
153 void *buf, size_t max_count)
154 {
155 struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
156
157 pr_debug("%s: buf=%p max_count=%zu\n", __func__,
158 buf, max_count);
159
160 if (overlay->dtbo == NULL)
161 return 0;
162
163 /* copy if buffer provided */
164 if (buf != NULL) {
165 /* the buffer must be large enough */
166 if (overlay->dtbo_size > max_count)
167 return -ENOSPC;
168
169 memcpy(buf, overlay->dtbo, overlay->dtbo_size);
170 }
171
172 return overlay->dtbo_size;
173 }
174
175 ssize_t cfs_overlay_item_dtbo_write(struct config_item *item,
176 const void *buf, size_t count)
177 {
178 struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
179 int err;
180
181 /* if it's set do not allow changes */
182 if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
183 return -EPERM;
184
185 /* copy the contents */
186 overlay->dtbo = kmemdup(buf, count, GFP_KERNEL);
187 if (overlay->dtbo == NULL)
188 return -ENOMEM;
189
190 overlay->dtbo_size = count;
191
192 err = create_overlay(overlay, overlay->dtbo);
193 if (err != 0)
194 goto out_err;
195
196 return count;
197
198 out_err:
199 kfree(overlay->dtbo);
200 overlay->dtbo = NULL;
201 overlay->dtbo_size = 0;
202
203 return err;
204 }
205
206 CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M);
207
208 static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = {
209 &cfs_overlay_item_attr_dtbo,
210 NULL,
211 };
212
213 static void cfs_overlay_release(struct config_item *item)
214 {
215 struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
216
217 if (overlay->ov_id >= 0)
218 of_overlay_destroy(overlay->ov_id);
219 if (overlay->fw)
220 release_firmware(overlay->fw);
221 /* kfree with NULL is safe */
222 kfree(overlay->dtbo);
223 kfree(overlay);
224 }
225
226 static struct configfs_item_operations cfs_overlay_item_ops = {
227 .release = cfs_overlay_release,
228 };
229
230 static struct config_item_type cfs_overlay_type = {
231 .ct_item_ops = &cfs_overlay_item_ops,
232 .ct_attrs = cfs_overlay_attrs,
233 .ct_bin_attrs = cfs_overlay_bin_attrs,
234 .ct_owner = THIS_MODULE,
235 };
236
237 static struct config_item *cfs_overlay_group_make_item(
238 struct config_group *group, const char *name)
239 {
240 struct cfs_overlay_item *overlay;
241
242 overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
243 if (!overlay)
244 return ERR_PTR(-ENOMEM);
245 overlay->ov_id = -1;
246
247 config_item_init_type_name(&overlay->item, name, &cfs_overlay_type);
248 return &overlay->item;
249 }
250
251 static void cfs_overlay_group_drop_item(struct config_group *group,
252 struct config_item *item)
253 {
254 struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
255
256 config_item_put(&overlay->item);
257 }
258
259 static struct configfs_group_operations overlays_ops = {
260 .make_item = cfs_overlay_group_make_item,
261 .drop_item = cfs_overlay_group_drop_item,
262 };
263
264 static struct config_item_type overlays_type = {
265 .ct_group_ops = &overlays_ops,
266 .ct_owner = THIS_MODULE,
267 };
268
269 static struct configfs_group_operations of_cfs_ops = {
270 /* empty - we don't allow anything to be created */
271 };
272
273 static struct config_item_type of_cfs_type = {
274 .ct_group_ops = &of_cfs_ops,
275 .ct_owner = THIS_MODULE,
276 };
277
278 struct config_group of_cfs_overlay_group;
279
280 static struct configfs_subsystem of_cfs_subsys = {
281 .su_group = {
282 .cg_item = {
283 .ci_namebuf = "device-tree",
284 .ci_type = &of_cfs_type,
285 },
286 },
287 .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex),
288 };
289
290 static int __init of_cfs_init(void)
291 {
292 int ret;
293
294 pr_info("%s\n", __func__);
295
296 config_group_init(&of_cfs_subsys.su_group);
297 config_group_init_type_name(&of_cfs_overlay_group, "overlays",
298 &overlays_type);
299 configfs_add_default_group(&of_cfs_overlay_group,
300 &of_cfs_subsys.su_group);
301
302 ret = configfs_register_subsystem(&of_cfs_subsys);
303 if (ret != 0) {
304 pr_err("%s: failed to register subsys\n", __func__);
305 goto out;
306 }
307 pr_info("%s: OK\n", __func__);
308 out:
309 return ret;
310 }
311 late_initcall(of_cfs_init);