0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #include "ssb_private.h"
0015
0016 #include <linux/ctype.h>
0017 #include <linux/slab.h>
0018
0019
0020 static int(*get_fallback_sprom)(struct ssb_bus *dev, struct ssb_sprom *out);
0021
0022
0023 static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len,
0024 size_t sprom_size_words)
0025 {
0026 int i, pos = 0;
0027
0028 for (i = 0; i < sprom_size_words; i++)
0029 pos += scnprintf(buf + pos, buf_len - pos - 1,
0030 "%04X", swab16(sprom[i]) & 0xFFFF);
0031 pos += scnprintf(buf + pos, buf_len - pos - 1, "\n");
0032
0033 return pos + 1;
0034 }
0035
0036 static int hex2sprom(u16 *sprom, const char *dump, size_t len,
0037 size_t sprom_size_words)
0038 {
0039 char c, tmp[5] = { 0 };
0040 int err, cnt = 0;
0041 unsigned long parsed;
0042
0043
0044 while (len) {
0045 c = dump[len - 1];
0046 if (!isspace(c) && c != '\0')
0047 break;
0048 len--;
0049 }
0050
0051 if (len != sprom_size_words * 4)
0052 return -EINVAL;
0053
0054 while (cnt < sprom_size_words) {
0055 memcpy(tmp, dump, 4);
0056 dump += 4;
0057 err = kstrtoul(tmp, 16, &parsed);
0058 if (err)
0059 return err;
0060 sprom[cnt++] = swab16((u16)parsed);
0061 }
0062
0063 return 0;
0064 }
0065
0066
0067 ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf,
0068 int (*sprom_read)(struct ssb_bus *bus, u16 *sprom))
0069 {
0070 u16 *sprom;
0071 int err = -ENOMEM;
0072 ssize_t count = 0;
0073 size_t sprom_size_words = bus->sprom_size;
0074
0075 sprom = kcalloc(sprom_size_words, sizeof(u16), GFP_KERNEL);
0076 if (!sprom)
0077 goto out;
0078
0079
0080
0081
0082
0083 err = -ERESTARTSYS;
0084 if (mutex_lock_interruptible(&bus->sprom_mutex))
0085 goto out_kfree;
0086 err = sprom_read(bus, sprom);
0087 mutex_unlock(&bus->sprom_mutex);
0088
0089 if (!err)
0090 count = sprom2hex(sprom, buf, PAGE_SIZE, sprom_size_words);
0091
0092 out_kfree:
0093 kfree(sprom);
0094 out:
0095 return err ? err : count;
0096 }
0097
0098
0099 ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
0100 const char *buf, size_t count,
0101 int (*sprom_check_crc)(const u16 *sprom, size_t size),
0102 int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom))
0103 {
0104 u16 *sprom;
0105 int res = 0, err = -ENOMEM;
0106 size_t sprom_size_words = bus->sprom_size;
0107 struct ssb_freeze_context freeze;
0108
0109 sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL);
0110 if (!sprom)
0111 goto out;
0112 err = hex2sprom(sprom, buf, count, sprom_size_words);
0113 if (err) {
0114 err = -EINVAL;
0115 goto out_kfree;
0116 }
0117 err = sprom_check_crc(sprom, sprom_size_words);
0118 if (err) {
0119 err = -EINVAL;
0120 goto out_kfree;
0121 }
0122
0123
0124
0125
0126
0127 err = -ERESTARTSYS;
0128 if (mutex_lock_interruptible(&bus->sprom_mutex))
0129 goto out_kfree;
0130 err = ssb_devices_freeze(bus, &freeze);
0131 if (err) {
0132 pr_err("SPROM write: Could not freeze all devices\n");
0133 goto out_unlock;
0134 }
0135 res = sprom_write(bus, sprom);
0136 err = ssb_devices_thaw(&freeze);
0137 if (err)
0138 pr_err("SPROM write: Could not thaw all devices\n");
0139 out_unlock:
0140 mutex_unlock(&bus->sprom_mutex);
0141 out_kfree:
0142 kfree(sprom);
0143 out:
0144 if (res)
0145 return res;
0146 return err ? err : count;
0147 }
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171 int ssb_arch_register_fallback_sprom(int (*sprom_callback)(struct ssb_bus *bus,
0172 struct ssb_sprom *out))
0173 {
0174 if (get_fallback_sprom)
0175 return -EEXIST;
0176 get_fallback_sprom = sprom_callback;
0177
0178 return 0;
0179 }
0180
0181 int ssb_fill_sprom_with_fallback(struct ssb_bus *bus, struct ssb_sprom *out)
0182 {
0183 if (!get_fallback_sprom)
0184 return -ENOENT;
0185
0186 return get_fallback_sprom(bus, out);
0187 }
0188
0189
0190 bool ssb_is_sprom_available(struct ssb_bus *bus)
0191 {
0192
0193
0194
0195
0196
0197
0198 if (bus->bustype == SSB_BUSTYPE_PCI &&
0199 bus->chipco.dev &&
0200 bus->chipco.dev->id.revision >= 31)
0201 return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM;
0202
0203 return true;
0204 }