]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/ssb/sprom.c
signalfd: fill in ssi_int for posix timers and message queues
[karo-tx-linux.git] / drivers / ssb / sprom.c
1 /*
2  * Sonics Silicon Backplane
3  * Common SPROM support routines
4  *
5  * Copyright (C) 2005-2008 Michael Buesch <mb@bu3sch.de>
6  * Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
7  * Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
8  * Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
9  * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
10  *
11  * Licensed under the GNU/GPL. See COPYING for details.
12  */
13
14 #include "ssb_private.h"
15
16 #include <linux/ctype.h>
17
18
19 static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len,
20                      size_t sprom_size_words)
21 {
22         int i, pos = 0;
23
24         for (i = 0; i < sprom_size_words; i++)
25                 pos += snprintf(buf + pos, buf_len - pos - 1,
26                                 "%04X", swab16(sprom[i]) & 0xFFFF);
27         pos += snprintf(buf + pos, buf_len - pos - 1, "\n");
28
29         return pos + 1;
30 }
31
32 static int hex2sprom(u16 *sprom, const char *dump, size_t len,
33                      size_t sprom_size_words)
34 {
35         char c, tmp[5] = { 0 };
36         int err, cnt = 0;
37         unsigned long parsed;
38
39         /* Strip whitespace at the end. */
40         while (len) {
41                 c = dump[len - 1];
42                 if (!isspace(c) && c != '\0')
43                         break;
44                 len--;
45         }
46         /* Length must match exactly. */
47         if (len != sprom_size_words * 4)
48                 return -EINVAL;
49
50         while (cnt < sprom_size_words) {
51                 memcpy(tmp, dump, 4);
52                 dump += 4;
53                 err = strict_strtoul(tmp, 16, &parsed);
54                 if (err)
55                         return err;
56                 sprom[cnt++] = swab16((u16)parsed);
57         }
58
59         return 0;
60 }
61
62 /* Common sprom device-attribute show-handler */
63 ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf,
64                             int (*sprom_read)(struct ssb_bus *bus, u16 *sprom))
65 {
66         u16 *sprom;
67         int err = -ENOMEM;
68         ssize_t count = 0;
69         size_t sprom_size_words = bus->sprom_size;
70
71         sprom = kcalloc(sprom_size_words, sizeof(u16), GFP_KERNEL);
72         if (!sprom)
73                 goto out;
74
75         /* Use interruptible locking, as the SPROM write might
76          * be holding the lock for several seconds. So allow userspace
77          * to cancel operation. */
78         err = -ERESTARTSYS;
79         if (mutex_lock_interruptible(&bus->sprom_mutex))
80                 goto out_kfree;
81         err = sprom_read(bus, sprom);
82         mutex_unlock(&bus->sprom_mutex);
83
84         if (!err)
85                 count = sprom2hex(sprom, buf, PAGE_SIZE, sprom_size_words);
86
87 out_kfree:
88         kfree(sprom);
89 out:
90         return err ? err : count;
91 }
92
93 /* Common sprom device-attribute store-handler */
94 ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
95                              const char *buf, size_t count,
96                              int (*sprom_check_crc)(const u16 *sprom, size_t size),
97                              int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom))
98 {
99         u16 *sprom;
100         int res = 0, err = -ENOMEM;
101         size_t sprom_size_words = bus->sprom_size;
102
103         sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL);
104         if (!sprom)
105                 goto out;
106         err = hex2sprom(sprom, buf, count, sprom_size_words);
107         if (err) {
108                 err = -EINVAL;
109                 goto out_kfree;
110         }
111         err = sprom_check_crc(sprom, sprom_size_words);
112         if (err) {
113                 err = -EINVAL;
114                 goto out_kfree;
115         }
116
117         /* Use interruptible locking, as the SPROM write might
118          * be holding the lock for several seconds. So allow userspace
119          * to cancel operation. */
120         err = -ERESTARTSYS;
121         if (mutex_lock_interruptible(&bus->sprom_mutex))
122                 goto out_kfree;
123         err = ssb_devices_freeze(bus);
124         if (err == -EOPNOTSUPP) {
125                 ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. "
126                            "No suspend support. Is CONFIG_PM enabled?\n");
127                 goto out_unlock;
128         }
129         if (err) {
130                 ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n");
131                 goto out_unlock;
132         }
133         res = sprom_write(bus, sprom);
134         err = ssb_devices_thaw(bus);
135         if (err)
136                 ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n");
137 out_unlock:
138         mutex_unlock(&bus->sprom_mutex);
139 out_kfree:
140         kfree(sprom);
141 out:
142         if (res)
143                 return res;
144         return err ? err : count;
145 }