diff options
author | Michael Buesch <mb@bu3sch.de> | 2008-03-10 12:26:32 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-03-13 19:32:32 -0400 |
commit | e7ec2e3230633a858af1b0b359f6c4670dbeb997 (patch) | |
tree | c43dbd7f6cab0ac066c039697528312d802617ef /drivers/ssb/sprom.c | |
parent | 068edceb7e73c05f77e204442ea8f86e238575da (diff) |
ssb: Add SPROM/invariants support for PCMCIA devices
This adds support for reading/writing the SPROM invariants
for PCMCIA based devices.
Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/ssb/sprom.c')
-rw-r--r-- | drivers/ssb/sprom.c | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c new file mode 100644 index 000000000000..3668edb39315 --- /dev/null +++ b/drivers/ssb/sprom.c | |||
@@ -0,0 +1,133 @@ | |||
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 | |||
17 | static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, | ||
18 | size_t sprom_size_words) | ||
19 | { | ||
20 | int i, pos = 0; | ||
21 | |||
22 | for (i = 0; i < sprom_size_words; i++) | ||
23 | pos += snprintf(buf + pos, buf_len - pos - 1, | ||
24 | "%04X", swab16(sprom[i]) & 0xFFFF); | ||
25 | pos += snprintf(buf + pos, buf_len - pos - 1, "\n"); | ||
26 | |||
27 | return pos + 1; | ||
28 | } | ||
29 | |||
30 | static int hex2sprom(u16 *sprom, const char *dump, size_t len, | ||
31 | size_t sprom_size_words) | ||
32 | { | ||
33 | char tmp[5] = { 0 }; | ||
34 | int cnt = 0; | ||
35 | unsigned long parsed; | ||
36 | |||
37 | if (len < sprom_size_words * 2) | ||
38 | return -EINVAL; | ||
39 | |||
40 | while (cnt < sprom_size_words) { | ||
41 | memcpy(tmp, dump, 4); | ||
42 | dump += 4; | ||
43 | parsed = simple_strtoul(tmp, NULL, 16); | ||
44 | sprom[cnt++] = swab16((u16)parsed); | ||
45 | } | ||
46 | |||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | /* Common sprom device-attribute show-handler */ | ||
51 | ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf, | ||
52 | int (*sprom_read)(struct ssb_bus *bus, u16 *sprom)) | ||
53 | { | ||
54 | u16 *sprom; | ||
55 | int err = -ENOMEM; | ||
56 | ssize_t count = 0; | ||
57 | size_t sprom_size_words = bus->sprom_size; | ||
58 | |||
59 | sprom = kcalloc(sprom_size_words, sizeof(u16), GFP_KERNEL); | ||
60 | if (!sprom) | ||
61 | goto out; | ||
62 | |||
63 | /* Use interruptible locking, as the SPROM write might | ||
64 | * be holding the lock for several seconds. So allow userspace | ||
65 | * to cancel operation. */ | ||
66 | err = -ERESTARTSYS; | ||
67 | if (mutex_lock_interruptible(&bus->sprom_mutex)) | ||
68 | goto out_kfree; | ||
69 | err = sprom_read(bus, sprom); | ||
70 | mutex_unlock(&bus->sprom_mutex); | ||
71 | |||
72 | if (!err) | ||
73 | count = sprom2hex(sprom, buf, PAGE_SIZE, sprom_size_words); | ||
74 | |||
75 | out_kfree: | ||
76 | kfree(sprom); | ||
77 | out: | ||
78 | return err ? err : count; | ||
79 | } | ||
80 | |||
81 | /* Common sprom device-attribute store-handler */ | ||
82 | ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, | ||
83 | const char *buf, size_t count, | ||
84 | int (*sprom_check_crc)(const u16 *sprom, size_t size), | ||
85 | int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom)) | ||
86 | { | ||
87 | u16 *sprom; | ||
88 | int res = 0, err = -ENOMEM; | ||
89 | size_t sprom_size_words = bus->sprom_size; | ||
90 | |||
91 | sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); | ||
92 | if (!sprom) | ||
93 | goto out; | ||
94 | err = hex2sprom(sprom, buf, count, sprom_size_words); | ||
95 | if (err) { | ||
96 | err = -EINVAL; | ||
97 | goto out_kfree; | ||
98 | } | ||
99 | err = sprom_check_crc(sprom, sprom_size_words); | ||
100 | if (err) { | ||
101 | err = -EINVAL; | ||
102 | goto out_kfree; | ||
103 | } | ||
104 | |||
105 | /* Use interruptible locking, as the SPROM write might | ||
106 | * be holding the lock for several seconds. So allow userspace | ||
107 | * to cancel operation. */ | ||
108 | err = -ERESTARTSYS; | ||
109 | if (mutex_lock_interruptible(&bus->sprom_mutex)) | ||
110 | goto out_kfree; | ||
111 | err = ssb_devices_freeze(bus); | ||
112 | if (err == -EOPNOTSUPP) { | ||
113 | ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. " | ||
114 | "No suspend support. Is CONFIG_PM enabled?\n"); | ||
115 | goto out_unlock; | ||
116 | } | ||
117 | if (err) { | ||
118 | ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); | ||
119 | goto out_unlock; | ||
120 | } | ||
121 | res = sprom_write(bus, sprom); | ||
122 | err = ssb_devices_thaw(bus); | ||
123 | if (err) | ||
124 | ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); | ||
125 | out_unlock: | ||
126 | mutex_unlock(&bus->sprom_mutex); | ||
127 | out_kfree: | ||
128 | kfree(sprom); | ||
129 | out: | ||
130 | if (res) | ||
131 | return res; | ||
132 | return err ? err : count; | ||
133 | } | ||