Merge branch 'drm-fixes' of git://people.freedesktop.org/~airlied/linux
[firefly-linux-kernel-4.4.55.git] / drivers / mtd / ofpart.c
1 /*
2  * Flash partitions described by the OF (or flattened) device tree
3  *
4  * Copyright © 2006 MontaVista Software Inc.
5  * Author: Vitaly Wool <vwool@ru.mvista.com>
6  *
7  * Revised to handle newer style flash binding by:
8  *   Copyright © 2007 David Gibson, IBM Corporation.
9  *
10  * This program is free software; you can redistribute  it and/or modify it
11  * under  the terms of  the GNU General  Public License as published by the
12  * Free Software Foundation;  either version 2 of the  License, or (at your
13  * option) any later version.
14  */
15
16 #include <linux/module.h>
17 #include <linux/init.h>
18 #include <linux/of.h>
19 #include <linux/mtd/mtd.h>
20 #include <linux/slab.h>
21 #include <linux/mtd/partitions.h>
22
23 static int parse_ofpart_partitions(struct mtd_info *master,
24                                    struct mtd_partition **pparts,
25                                    struct mtd_part_parser_data *data)
26 {
27         struct device_node *node;
28         const char *partname;
29         struct device_node *pp;
30         int nr_parts, i;
31
32
33         if (!data)
34                 return 0;
35
36         node = data->of_node;
37         if (!node)
38                 return 0;
39
40         /* First count the subnodes */
41         pp = NULL;
42         nr_parts = 0;
43         while ((pp = of_get_next_child(node, pp)))
44                 nr_parts++;
45
46         if (nr_parts == 0)
47                 return 0;
48
49         *pparts = kzalloc(nr_parts * sizeof(**pparts), GFP_KERNEL);
50         if (!*pparts)
51                 return -ENOMEM;
52
53         pp = NULL;
54         i = 0;
55         while ((pp = of_get_next_child(node, pp))) {
56                 const __be32 *reg;
57                 int len;
58
59                 reg = of_get_property(pp, "reg", &len);
60                 if (!reg) {
61                         nr_parts--;
62                         continue;
63                 }
64
65                 (*pparts)[i].offset = be32_to_cpu(reg[0]);
66                 (*pparts)[i].size = be32_to_cpu(reg[1]);
67
68                 partname = of_get_property(pp, "label", &len);
69                 if (!partname)
70                         partname = of_get_property(pp, "name", &len);
71                 (*pparts)[i].name = (char *)partname;
72
73                 if (of_get_property(pp, "read-only", &len))
74                         (*pparts)[i].mask_flags = MTD_WRITEABLE;
75
76                 i++;
77         }
78
79         if (!i) {
80                 of_node_put(pp);
81                 pr_err("No valid partition found on %s\n", node->full_name);
82                 kfree(*pparts);
83                 *pparts = NULL;
84                 return -EINVAL;
85         }
86
87         return nr_parts;
88 }
89
90 static struct mtd_part_parser ofpart_parser = {
91         .owner = THIS_MODULE,
92         .parse_fn = parse_ofpart_partitions,
93         .name = "ofpart",
94 };
95
96 static int parse_ofoldpart_partitions(struct mtd_info *master,
97                                       struct mtd_partition **pparts,
98                                       struct mtd_part_parser_data *data)
99 {
100         struct device_node *dp;
101         int i, plen, nr_parts;
102         const struct {
103                 __be32 offset, len;
104         } *part;
105         const char *names;
106
107         if (!data)
108                 return 0;
109
110         dp = data->of_node;
111         if (!dp)
112                 return 0;
113
114         part = of_get_property(dp, "partitions", &plen);
115         if (!part)
116                 return 0; /* No partitions found */
117
118         pr_warning("Device tree uses obsolete partition map binding: %s\n",
119                         dp->full_name);
120
121         nr_parts = plen / sizeof(part[0]);
122
123         *pparts = kzalloc(nr_parts * sizeof(*(*pparts)), GFP_KERNEL);
124         if (!pparts)
125                 return -ENOMEM;
126
127         names = of_get_property(dp, "partition-names", &plen);
128
129         for (i = 0; i < nr_parts; i++) {
130                 (*pparts)[i].offset = be32_to_cpu(part->offset);
131                 (*pparts)[i].size   = be32_to_cpu(part->len) & ~1;
132                 /* bit 0 set signifies read only partition */
133                 if (be32_to_cpu(part->len) & 1)
134                         (*pparts)[i].mask_flags = MTD_WRITEABLE;
135
136                 if (names && (plen > 0)) {
137                         int len = strlen(names) + 1;
138
139                         (*pparts)[i].name = (char *)names;
140                         plen -= len;
141                         names += len;
142                 } else {
143                         (*pparts)[i].name = "unnamed";
144                 }
145
146                 part++;
147         }
148
149         return nr_parts;
150 }
151
152 static struct mtd_part_parser ofoldpart_parser = {
153         .owner = THIS_MODULE,
154         .parse_fn = parse_ofoldpart_partitions,
155         .name = "ofoldpart",
156 };
157
158 static int __init ofpart_parser_init(void)
159 {
160         int rc;
161         rc = register_mtd_parser(&ofpart_parser);
162         if (rc)
163                 goto out;
164
165         rc = register_mtd_parser(&ofoldpart_parser);
166         if (!rc)
167                 return 0;
168
169         deregister_mtd_parser(&ofoldpart_parser);
170 out:
171         return rc;
172 }
173
174 module_init(ofpart_parser_init);
175
176 MODULE_LICENSE("GPL");
177 MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree");
178 MODULE_AUTHOR("Vitaly Wool, David Gibson");
179 /*
180  * When MTD core cannot find the requested parser, it tries to load the module
181  * with the same name. Since we provide the ofoldpart parser, we should have
182  * the corresponding alias.
183  */
184 MODULE_ALIAS("ofoldpart");