of: configfs: Use of_overlay_fdt_apply API call
[platform/kernel/linux-rpi.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 inline struct cfs_overlay_item *to_cfs_overlay_item(
44                 struct config_item *item)
45 {
46         return item ? container_of(item, struct cfs_overlay_item, item) : NULL;
47 }
48
49 static ssize_t cfs_overlay_item_path_show(struct config_item *item,
50                 char *page)
51 {
52         struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
53         return sprintf(page, "%s\n", overlay->path);
54 }
55
56 static ssize_t cfs_overlay_item_path_store(struct config_item *item,
57                 const char *page, size_t count)
58 {
59         struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
60         const char *p = page;
61         char *s;
62         int err;
63
64         /* if it's set do not allow changes */
65         if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
66                 return -EPERM;
67
68         /* copy to path buffer (and make sure it's always zero terminated */
69         count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p);
70         overlay->path[sizeof(overlay->path) - 1] = '\0';
71
72         /* strip trailing newlines */
73         s = overlay->path + strlen(overlay->path);
74         while (s > overlay->path && *--s == '\n')
75                 *s = '\0';
76
77         pr_debug("%s: path is '%s'\n", __func__, overlay->path);
78
79         err = request_firmware(&overlay->fw, overlay->path, NULL);
80         if (err != 0)
81                 goto out_err;
82
83         err = of_overlay_fdt_apply((void *)overlay->fw->data,
84                                    (u32)overlay->fw->size, &overlay->ov_id);
85         if (err != 0)
86                 goto out_err;
87
88         return count;
89
90 out_err:
91
92         release_firmware(overlay->fw);
93         overlay->fw = NULL;
94
95         overlay->path[0] = '\0';
96         return err;
97 }
98
99 static ssize_t cfs_overlay_item_status_show(struct config_item *item,
100                 char *page)
101 {
102         struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
103
104         return sprintf(page, "%s\n",
105                         overlay->ov_id > 0 ? "applied" : "unapplied");
106 }
107
108 CONFIGFS_ATTR(cfs_overlay_item_, path);
109 CONFIGFS_ATTR_RO(cfs_overlay_item_, status);
110
111 static struct configfs_attribute *cfs_overlay_attrs[] = {
112         &cfs_overlay_item_attr_path,
113         &cfs_overlay_item_attr_status,
114         NULL,
115 };
116
117 ssize_t cfs_overlay_item_dtbo_read(struct config_item *item,
118                 void *buf, size_t max_count)
119 {
120         struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
121
122         pr_debug("%s: buf=%p max_count=%zu\n", __func__,
123                         buf, max_count);
124
125         if (overlay->dtbo == NULL)
126                 return 0;
127
128         /* copy if buffer provided */
129         if (buf != NULL) {
130                 /* the buffer must be large enough */
131                 if (overlay->dtbo_size > max_count)
132                         return -ENOSPC;
133
134                 memcpy(buf, overlay->dtbo, overlay->dtbo_size);
135         }
136
137         return overlay->dtbo_size;
138 }
139
140 ssize_t cfs_overlay_item_dtbo_write(struct config_item *item,
141                 const void *buf, size_t count)
142 {
143         struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
144         int err;
145
146         /* if it's set do not allow changes */
147         if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
148                 return -EPERM;
149
150         /* copy the contents */
151         overlay->dtbo = kmemdup(buf, count, GFP_KERNEL);
152         if (overlay->dtbo == NULL)
153                 return -ENOMEM;
154
155         overlay->dtbo_size = count;
156
157         err = of_overlay_fdt_apply(overlay->dtbo, overlay->dtbo_size,
158                                    &overlay->ov_id);
159         if (err != 0)
160                 goto out_err;
161
162         return count;
163
164 out_err:
165         kfree(overlay->dtbo);
166         overlay->dtbo = NULL;
167         overlay->dtbo_size = 0;
168         overlay->ov_id = 0;
169
170         return err;
171 }
172
173 CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M);
174
175 static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = {
176         &cfs_overlay_item_attr_dtbo,
177         NULL,
178 };
179
180 static void cfs_overlay_release(struct config_item *item)
181 {
182         struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
183
184         if (overlay->ov_id > 0)
185                 of_overlay_remove(&overlay->ov_id);
186         if (overlay->fw)
187                 release_firmware(overlay->fw);
188         /* kfree with NULL is safe */
189         kfree(overlay->dtbo);
190         kfree(overlay);
191 }
192
193 static struct configfs_item_operations cfs_overlay_item_ops = {
194         .release        = cfs_overlay_release,
195 };
196
197 static struct config_item_type cfs_overlay_type = {
198         .ct_item_ops    = &cfs_overlay_item_ops,
199         .ct_attrs       = cfs_overlay_attrs,
200         .ct_bin_attrs   = cfs_overlay_bin_attrs,
201         .ct_owner       = THIS_MODULE,
202 };
203
204 static struct config_item *cfs_overlay_group_make_item(
205                 struct config_group *group, const char *name)
206 {
207         struct cfs_overlay_item *overlay;
208
209         overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
210         if (!overlay)
211                 return ERR_PTR(-ENOMEM);
212
213         config_item_init_type_name(&overlay->item, name, &cfs_overlay_type);
214         return &overlay->item;
215 }
216
217 static void cfs_overlay_group_drop_item(struct config_group *group,
218                 struct config_item *item)
219 {
220         struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
221
222         config_item_put(&overlay->item);
223 }
224
225 static struct configfs_group_operations overlays_ops = {
226         .make_item      = cfs_overlay_group_make_item,
227         .drop_item      = cfs_overlay_group_drop_item,
228 };
229
230 static struct config_item_type overlays_type = {
231         .ct_group_ops   = &overlays_ops,
232         .ct_owner       = THIS_MODULE,
233 };
234
235 static struct configfs_group_operations of_cfs_ops = {
236         /* empty - we don't allow anything to be created */
237 };
238
239 static struct config_item_type of_cfs_type = {
240         .ct_group_ops   = &of_cfs_ops,
241         .ct_owner       = THIS_MODULE,
242 };
243
244 struct config_group of_cfs_overlay_group;
245
246 static struct configfs_subsystem of_cfs_subsys = {
247         .su_group = {
248                 .cg_item = {
249                         .ci_namebuf = "device-tree",
250                         .ci_type = &of_cfs_type,
251                 },
252         },
253         .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex),
254 };
255
256 static int __init of_cfs_init(void)
257 {
258         int ret;
259
260         pr_info("%s\n", __func__);
261
262         config_group_init(&of_cfs_subsys.su_group);
263         config_group_init_type_name(&of_cfs_overlay_group, "overlays",
264                         &overlays_type);
265         configfs_add_default_group(&of_cfs_overlay_group,
266                         &of_cfs_subsys.su_group);
267
268         ret = configfs_register_subsystem(&of_cfs_subsys);
269         if (ret != 0) {
270                 pr_err("%s: failed to register subsys\n", __func__);
271                 goto out;
272         }
273         pr_info("%s: OK\n", __func__);
274 out:
275         return ret;
276 }
277 late_initcall(of_cfs_init);