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 += snprintf(buf + pos, buf_len - pos - 1,
  30                                "%04X", swab16(sprom[i]) & 0xFFFF);
  31        pos += snprintf(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 = strict_strtoul(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        err = -ERESTARTSYS;
  83        if (mutex_lock_interruptible(&bus->sprom_mutex))
  84                goto out_kfree;
  85        err = sprom_read(bus, sprom);
  86        mutex_unlock(&bus->sprom_mutex);
  87
  88        if (!err)
  89                count = sprom2hex(sprom, buf, PAGE_SIZE, sprom_size_words);
  90
  91out_kfree:
  92        kfree(sprom);
  93out:
  94        return err ? err : count;
  95}
  96
  97/* Common sprom device-attribute store-handler */
  98ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
  99                             const char *buf, size_t count,
 100                             int (*sprom_check_crc)(const u16 *sprom, size_t size),
 101                             int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom))
 102{
 103        u16 *sprom;
 104        int res = 0, err = -ENOMEM;
 105        size_t sprom_size_words = bus->sprom_size;
 106        struct ssb_freeze_context freeze;
 107
 108        sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL);
 109        if (!sprom)
 110                goto out;
 111        err = hex2sprom(sprom, buf, count, sprom_size_words);
 112        if (err) {
 113                err = -EINVAL;
 114                goto out_kfree;
 115        }
 116        err = sprom_check_crc(sprom, sprom_size_words);
 117        if (err) {
 118                err = -EINVAL;
 119                goto out_kfree;
 120        }
 121
 122        /* Use interruptible locking, as the SPROM write might
 123         * be holding the lock for several seconds. So allow userspace
 124         * to cancel operation. */
 125        err = -ERESTARTSYS;
 126        if (mutex_lock_interruptible(&bus->sprom_mutex))
 127                goto out_kfree;
 128        err = ssb_devices_freeze(bus, &freeze);
 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(&freeze);
 135        if (err)
 136                ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n");
 137out_unlock:
 138        mutex_unlock(&bus->sprom_mutex);
 139out_kfree:
 140        kfree(sprom);
 141out:
 142        if (res)
 143                return res;
 144        return err ? err : count;
 145}
 146
 147/**
 148 * ssb_arch_register_fallback_sprom - Registers a method providing a
 149 * fallback SPROM if no SPROM is found.
 150 *
 151 * @sprom_callback: The callback function.
 152 *
 153 * With this function the architecture implementation may register a
 154 * callback handler which fills the SPROM data structure. The fallback is
 155 * only used for PCI based SSB devices, where no valid SPROM can be found
 156 * in the shadow registers.
 157 *
 158 * This function is useful for weird architectures that have a half-assed
 159 * SSB device hardwired to their PCI bus.
 160 *
 161 * Note that it does only work with PCI attached SSB devices. PCMCIA
 162 * devices currently don't use this fallback.
 163 * Architectures must provide the SPROM for native SSB devices anyway, so
 164 * the fallback also isn't used for native devices.
 165 *
 166 * This function is available for architecture code, only. So it is not
 167 * exported.
 168 */
 169int ssb_arch_register_fallback_sprom(int (*sprom_callback)(struct ssb_bus *bus,
 170                                     struct ssb_sprom *out))
 171{
 172        if (get_fallback_sprom)
 173                return -EEXIST;
 174        get_fallback_sprom = sprom_callback;
 175
 176        return 0;
 177}
 178
 179int ssb_fill_sprom_with_fallback(struct ssb_bus *bus, struct ssb_sprom *out)
 180{
 181        if (!get_fallback_sprom)
 182                return -ENOENT;
 183
 184        return get_fallback_sprom(bus, out);
 185}
 186
 187/* http://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */
 188bool ssb_is_sprom_available(struct ssb_bus *bus)
 189{
 190        /* status register only exists on chipcomon rev >= 11 and we need check
 191           for >= 31 only */
 192        /* this routine differs from specs as we do not access SPROM directly
 193           on PCMCIA */
 194        if (bus->bustype == SSB_BUSTYPE_PCI &&
 195            bus->chipco.dev &&  /* can be unavailable! */
 196            bus->chipco.dev->id.revision >= 31)
 197                return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM;
 198
 199        return true;
 200}
 201
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.