linux/drivers/ssb/sprom.c
<<
>>
Prefs
   1/*
   2 * Sonics Silicon Backplane
   3 * Common SPROM support routines
   4 *
   5 * Copyright (C) 2005-2008 Michael Buesch <m@bues.ch>
   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#include <linux/slab.h>
  18
  19
  20static int(*get_fallback_sprom)(struct ssb_bus *dev, struct ssb_sprom *out);
  21
  22
  23static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len,
  24                     size_t sprom_size_words)
  25{
  26        int i, pos = 0;
  27
  28        for (i = 0; i < sprom_size_words; i++)
  29                pos += scnprintf(buf + pos, buf_len - pos - 1,
  30                                "%04X", swab16(sprom[i]) & 0xFFFF);
  31        pos += scnprintf(buf + pos, buf_len - pos - 1, "\n");
  32
  33        return pos + 1;
  34}
  35
  36static int hex2sprom(u16 *sprom, const char *dump, size_t len,
  37                     size_t sprom_size_words)
  38{
  39        char c, tmp[5] = { 0 };
  40        int err, cnt = 0;
  41        unsigned long parsed;
  42
  43        /* Strip whitespace at the end. */
  44        while (len) {
  45                c = dump[len - 1];
  46                if (!isspace(c) && c != '\0')
  47                        break;
  48                len--;
  49        }
  50        /* Length must match exactly. */
  51        if (len != sprom_size_words * 4)
  52                return -EINVAL;
  53
  54        while (cnt < sprom_size_words) {
  55                memcpy(tmp, dump, 4);
  56                dump += 4;
  57                err = kstrtoul(tmp, 16, &parsed);
  58                if (err)
  59                        return err;
  60                sprom[cnt++] = swab16((u16)parsed);
  61        }
  62
  63        return 0;
  64}
  65
  66/* Common sprom device-attribute show-handler */
  67ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf,
  68                            int (*sprom_read)(struct ssb_bus *bus, u16 *sprom))
  69{
  70        u16 *sprom;
  71        int err = -ENOMEM;
  72        ssize_t count = 0;
  73        size_t sprom_size_words = bus->sprom_size;
  74
  75        sprom = kcalloc(sprom_size_words, sizeof(u16), GFP_KERNEL);
  76        if (!sprom)
  77                goto out;
  78
  79        /* Use interruptible locking, as the SPROM write might
  80         * be holding the lock for several seconds. So allow userspace
  81         * to cancel operation.
  82         */
  83        err = -ERESTARTSYS;
  84        if (mutex_lock_interruptible(&bus->sprom_mutex))
  85                goto out_kfree;
  86        err = sprom_read(bus, sprom);
  87        mutex_unlock(&bus->sprom_mutex);
  88
  89        if (!err)
  90                count = sprom2hex(sprom, buf, PAGE_SIZE, sprom_size_words);
  91
  92out_kfree:
  93        kfree(sprom);
  94out:
  95        return err ? err : count;
  96}
  97
  98/* Common sprom device-attribute store-handler */
  99ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
 100                             const char *buf, size_t count,
 101                             int (*sprom_check_crc)(const u16 *sprom, size_t size),
 102                             int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom))
 103{
 104        u16 *sprom;
 105        int res = 0, err = -ENOMEM;
 106        size_t sprom_size_words = bus->sprom_size;
 107        struct ssb_freeze_context freeze;
 108
 109        sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL);
 110        if (!sprom)
 111                goto out;
 112        err = hex2sprom(sprom, buf, count, sprom_size_words);
 113        if (err) {
 114                err = -EINVAL;
 115                goto out_kfree;
 116        }
 117        err = sprom_check_crc(sprom, sprom_size_words);
 118        if (err) {
 119                err = -EINVAL;
 120                goto out_kfree;
 121        }
 122
 123        /* Use interruptible locking, as the SPROM write might
 124         * be holding the lock for several seconds. So allow userspace
 125         * to cancel operation.
 126         */
 127        err = -ERESTARTSYS;
 128        if (mutex_lock_interruptible(&bus->sprom_mutex))
 129                goto out_kfree;
 130        err = ssb_devices_freeze(bus, &freeze);
 131        if (err) {
 132                pr_err("SPROM write: Could not freeze all devices\n");
 133                goto out_unlock;
 134        }
 135        res = sprom_write(bus, sprom);
 136        err = ssb_devices_thaw(&freeze);
 137        if (err)
 138                pr_err("SPROM write: Could not thaw all devices\n");
 139out_unlock:
 140        mutex_unlock(&bus->sprom_mutex);
 141out_kfree:
 142        kfree(sprom);
 143out:
 144        if (res)
 145                return res;
 146        return err ? err : count;
 147}
 148
 149/**
 150 * ssb_arch_register_fallback_sprom - Registers a method providing a
 151 * fallback SPROM if no SPROM is found.
 152 *
 153 * @sprom_callback: The callback function.
 154 *
 155 * With this function the architecture implementation may register a
 156 * callback handler which fills the SPROM data structure. The fallback is
 157 * only used for PCI based SSB devices, where no valid SPROM can be found
 158 * in the shadow registers.
 159 *
 160 * This function is useful for weird architectures that have a half-assed
 161 * SSB device hardwired to their PCI bus.
 162 *
 163 * Note that it does only work with PCI attached SSB devices. PCMCIA
 164 * devices currently don't use this fallback.
 165 * Architectures must provide the SPROM for native SSB devices anyway, so
 166 * the fallback also isn't used for native devices.
 167 *
 168 * This function is available for architecture code, only. So it is not
 169 * exported.
 170 */
 171int ssb_arch_register_fallback_sprom(int (*sprom_callback)(struct ssb_bus *bus,
 172                                     struct ssb_sprom *out))
 173{
 174        if (get_fallback_sprom)
 175                return -EEXIST;
 176        get_fallback_sprom = sprom_callback;
 177
 178        return 0;
 179}
 180
 181int ssb_fill_sprom_with_fallback(struct ssb_bus *bus, struct ssb_sprom *out)
 182{
 183        if (!get_fallback_sprom)
 184                return -ENOENT;
 185
 186        return get_fallback_sprom(bus, out);
 187}
 188
 189/* https://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */
 190bool ssb_is_sprom_available(struct ssb_bus *bus)
 191{
 192        /* status register only exists on chipcomon rev >= 11 and we need check
 193         * for >= 31 only
 194         */
 195        /* this routine differs from specs as we do not access SPROM directly
 196         * on PCMCIA
 197         */
 198        if (bus->bustype == SSB_BUSTYPE_PCI &&
 199            bus->chipco.dev &&  /* can be unavailable! */
 200            bus->chipco.dev->id.revision >= 31)
 201                return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM;
 202
 203        return true;
 204}
 205