Revert "ar71xx: Allow to set the RXDV, RXD, TXD, TXE delays for QCA955x"
[openwrt.git] / target / linux / ar71xx / files / arch / mips / ath79 / routerboot.c
1 /*
2  *  RouterBoot helper routines
3  *
4  *  Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
5  *
6  *  This program is free software; you can redistribute it and/or modify it
7  *  under the terms of the GNU General Public License version 2 as published
8  *  by the Free Software Foundation.
9  */
10
11 #define pr_fmt(fmt) "rb: " fmt
12
13 #include <linux/kernel.h>
14 #include <linux/kobject.h>
15 #include <linux/slab.h>
16 #include <linux/errno.h>
17 #include <linux/routerboot.h>
18 #include <linux/rle.h>
19 #include <linux/lzo.h>
20
21 #include "routerboot.h"
22
23 #define RB_BLOCK_SIZE           0x1000
24 #define RB_ART_SIZE             0x10000
25 #define RB_MAGIC_ERD            0x00455244      /* extended radio data */
26
27 static struct rb_info rb_info;
28
29 static u32 get_u32(void *buf)
30 {
31         u8 *p = buf;
32
33         return ((u32) p[3] + ((u32) p[2] << 8) + ((u32) p[1] << 16) +
34                ((u32) p[0] << 24));
35 }
36
37 static u16 get_u16(void *buf)
38 {
39         u8 *p = buf;
40
41         return (u16) p[1] + ((u16) p[0] << 8);
42 }
43
44 __init int
45 routerboot_find_magic(u8 *buf, unsigned int buflen, u32 *offset, bool hard)
46 {
47         u32 magic_ref = hard ? RB_MAGIC_HARD : RB_MAGIC_SOFT;
48         u32 magic;
49         u32 cur = *offset;
50
51         while (cur < buflen) {
52                 magic = get_u32(buf + cur);
53                 if (magic == magic_ref) {
54                         *offset = cur;
55                         return 0;
56                 }
57
58                 cur += 0x1000;
59         }
60
61         return -ENOENT;
62 }
63
64 __init int
65 routerboot_find_tag(u8 *buf, unsigned int buflen, u16 tag_id,
66                     u8 **tag_data, u16 *tag_len)
67 {
68         uint32_t magic;
69         bool align = false;
70         int ret;
71
72         if (buflen < 4)
73                 return -EINVAL;
74
75         magic = get_u32(buf);
76         switch (magic) {
77         case RB_MAGIC_ERD:
78                 align = true;
79                 /* fall trough */
80         case RB_MAGIC_HARD:
81                 /* skip magic value */
82                 buf += 4;
83                 buflen -= 4;
84                 break;
85
86         case RB_MAGIC_SOFT:
87                 if (buflen < 8)
88                         return -EINVAL;
89
90                 /* skip magic and CRC value */
91                 buf += 8;
92                 buflen -= 8;
93
94                 break;
95
96         default:
97                 return -EINVAL;
98         }
99
100         ret = -ENOENT;
101         while (buflen > 2) {
102                 u16 id;
103                 u16 len;
104
105                 len = get_u16(buf);
106                 buf += 2;
107                 buflen -= 2;
108
109                 if (buflen < 2)
110                         break;
111
112                 id = get_u16(buf);
113                 buf += 2;
114                 buflen -= 2;
115
116                 if (id == RB_ID_TERMINATOR)
117                         break;
118
119                 if (buflen < len)
120                         break;
121
122                 if (id == tag_id) {
123                         if (tag_len)
124                                 *tag_len = len;
125                         if (tag_data)
126                                 *tag_data = buf;
127                         ret = 0;
128                         break;
129                 }
130
131                 if (align)
132                         len = (len + 3) / 4;
133
134                 buf += len;
135                 buflen -= len;
136         }
137
138         return ret;
139 }
140
141 static inline int
142 rb_find_hard_cfg_tag(u16 tag_id, u8 **tag_data, u16 *tag_len)
143 {
144         if (!rb_info.hard_cfg_data ||
145             !rb_info.hard_cfg_size)
146                 return -ENOENT;
147
148         return routerboot_find_tag(rb_info.hard_cfg_data,
149                                    rb_info.hard_cfg_size,
150                                    tag_id, tag_data, tag_len);
151 }
152
153 __init const char *
154 rb_get_board_name(void)
155 {
156         u16 tag_len;
157         u8 *tag;
158         int err;
159
160         err = rb_find_hard_cfg_tag(RB_ID_BOARD_NAME, &tag, &tag_len);
161         if (err)
162                 return NULL;
163
164         return tag;
165 }
166
167 __init u32
168 rb_get_hw_options(void)
169 {
170         u16 tag_len;
171         u8 *tag;
172         int err;
173
174         err = rb_find_hard_cfg_tag(RB_ID_HW_OPTIONS, &tag, &tag_len);
175         if (err)
176                 return 0;
177
178         return get_u32(tag);
179 }
180
181 static void * __init
182 __rb_get_wlan_data(u16 id)
183 {
184         u16 tag_len;
185         u8 *tag;
186         void *buf;
187         int err;
188         u32 magic;
189         size_t src_done;
190         size_t dst_done;
191
192         err = rb_find_hard_cfg_tag(RB_ID_WLAN_DATA, &tag, &tag_len);
193         if (err) {
194                 pr_err("no calibration data found\n");
195                 goto err;
196         }
197
198         buf = kmalloc(RB_ART_SIZE, GFP_KERNEL);
199         if (buf == NULL) {
200                 pr_err("no memory for calibration data\n");
201                 goto err;
202         }
203
204         magic = get_u32(tag);
205         if (magic == RB_MAGIC_ERD) {
206                 u8 *erd_data;
207                 u16 erd_len;
208
209                 if (id == 0)
210                         goto err_free;
211
212                 err = routerboot_find_tag(tag, tag_len, id,
213                                           &erd_data, &erd_len);
214                 if (err) {
215                         pr_err("no ERD data found for id %u\n", id);
216                         goto err_free;
217                 }
218
219                 dst_done = RB_ART_SIZE;
220                 err = lzo1x_decompress_safe(erd_data, erd_len, buf, &dst_done);
221                 if (err) {
222                         pr_err("unable to decompress calibration data %d\n",
223                                err);
224                         goto err_free;
225                 }
226         } else {
227                 if (id != 0)
228                         goto err_free;
229
230                 err = rle_decode((char *) tag, tag_len, buf, RB_ART_SIZE,
231                                  &src_done, &dst_done);
232                 if (err) {
233                         pr_err("unable to decode calibration data\n");
234                         goto err_free;
235                 }
236         }
237
238         return buf;
239
240 err_free:
241         kfree(buf);
242 err:
243         return NULL;
244 }
245
246 __init void *
247 rb_get_wlan_data(void)
248 {
249         return __rb_get_wlan_data(0);
250 }
251
252 __init void *
253 rb_get_ext_wlan_data(u16 id)
254 {
255         return __rb_get_wlan_data(id);
256 }
257
258 __init const struct rb_info *
259 rb_init_info(void *data, unsigned int size)
260 {
261         unsigned int offset;
262
263         if (size == 0 || (size % RB_BLOCK_SIZE) != 0)
264                 return NULL;
265
266         for (offset = 0; offset < size; offset += RB_BLOCK_SIZE) {
267                 u32 magic;
268
269                 magic = get_u32(data + offset);
270                 switch (magic) {
271                 case RB_MAGIC_HARD:
272                         rb_info.hard_cfg_offs = offset;
273                         break;
274
275                 case RB_MAGIC_SOFT:
276                         rb_info.soft_cfg_offs = offset;
277                         break;
278                 }
279         }
280
281         if (!rb_info.hard_cfg_offs) {
282                 pr_err("could not find a valid RouterBOOT hard config\n");
283                 return NULL;
284         }
285
286         if (!rb_info.soft_cfg_offs) {
287                 pr_err("could not find a valid RouterBOOT soft config\n");
288                 return NULL;
289         }
290
291         rb_info.hard_cfg_size = RB_BLOCK_SIZE;
292         rb_info.hard_cfg_data = kmemdup(data + rb_info.hard_cfg_offs,
293                                         RB_BLOCK_SIZE, GFP_KERNEL);
294         if (!rb_info.hard_cfg_data)
295                 return NULL;
296
297         rb_info.board_name = rb_get_board_name();
298         rb_info.hw_options = rb_get_hw_options();
299
300         return &rb_info;
301 }
302
303 static char *rb_ext_wlan_data;
304
305 static ssize_t
306 rb_ext_wlan_data_read(struct file *filp, struct kobject *kobj,
307                       struct bin_attribute *attr, char *buf,
308                       loff_t off, size_t count)
309 {
310          if (off + count > attr->size)
311                  return -EFBIG;
312
313          memcpy(buf, &rb_ext_wlan_data[off], count);
314
315          return count;
316 }
317
318 static const struct bin_attribute rb_ext_wlan_data_attr = {
319         .attr = {
320                 .name = "ext_wlan_data",
321                 .mode = S_IRUSR | S_IWUSR,
322         },
323         .read = rb_ext_wlan_data_read,
324         .size = RB_ART_SIZE,
325 };
326
327 static int __init rb_sysfs_init(void)
328 {
329         struct kobject *rb_kobj;
330         int ret;
331
332         rb_ext_wlan_data = rb_get_ext_wlan_data(1);
333         if (rb_ext_wlan_data == NULL)
334                 return -ENOENT;
335
336         rb_kobj = kobject_create_and_add("routerboot", firmware_kobj);
337         if (rb_kobj == NULL) {
338                 ret = -ENOMEM;
339                 pr_err("unable to create sysfs entry\n");
340                 goto err_free_wlan_data;
341         }
342
343         ret = sysfs_create_bin_file(rb_kobj, &rb_ext_wlan_data_attr);
344         if (ret) {
345                 pr_err("unable to create sysfs file, %d\n", ret);
346                 goto err_put_kobj;
347         }
348
349         return 0;
350
351 err_put_kobj:
352         kobject_put(rb_kobj);
353 err_free_wlan_data:
354         kfree(rb_ext_wlan_data);
355         return ret;
356 }
357
358 late_initcall(rb_sysfs_init);