Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Sonics Silicon Backplane
0003  * Common SPROM support routines
0004  *
0005  * Copyright (C) 2005-2008 Michael Buesch <m@bues.ch>
0006  * Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
0007  * Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
0008  * Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
0009  * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
0010  *
0011  * Licensed under the GNU/GPL. See COPYING for details.
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     /* Strip whitespace at the end. */
0044     while (len) {
0045         c = dump[len - 1];
0046         if (!isspace(c) && c != '\0')
0047             break;
0048         len--;
0049     }
0050     /* Length must match exactly. */
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 /* Common sprom device-attribute show-handler */
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     /* Use interruptible locking, as the SPROM write might
0080      * be holding the lock for several seconds. So allow userspace
0081      * to cancel operation.
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 /* Common sprom device-attribute store-handler */
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     /* Use interruptible locking, as the SPROM write might
0124      * be holding the lock for several seconds. So allow userspace
0125      * to cancel operation.
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  * ssb_arch_register_fallback_sprom - Registers a method providing a
0151  * fallback SPROM if no SPROM is found.
0152  *
0153  * @sprom_callback: The callback function.
0154  *
0155  * With this function the architecture implementation may register a
0156  * callback handler which fills the SPROM data structure. The fallback is
0157  * only used for PCI based SSB devices, where no valid SPROM can be found
0158  * in the shadow registers.
0159  *
0160  * This function is useful for weird architectures that have a half-assed
0161  * SSB device hardwired to their PCI bus.
0162  *
0163  * Note that it does only work with PCI attached SSB devices. PCMCIA
0164  * devices currently don't use this fallback.
0165  * Architectures must provide the SPROM for native SSB devices anyway, so
0166  * the fallback also isn't used for native devices.
0167  *
0168  * This function is available for architecture code, only. So it is not
0169  * exported.
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 /* https://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */
0190 bool ssb_is_sprom_available(struct ssb_bus *bus)
0191 {
0192     /* status register only exists on chipcomon rev >= 11 and we need check
0193      * for >= 31 only
0194      */
0195     /* this routine differs from specs as we do not access SPROM directly
0196      * on PCMCIA
0197      */
0198     if (bus->bustype == SSB_BUSTYPE_PCI &&
0199         bus->chipco.dev &&  /* can be unavailable! */
0200         bus->chipco.dev->id.revision >= 31)
0201         return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM;
0202 
0203     return true;
0204 }