sprom.c 4.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * Sonics Silicon Backplane
 * Common SPROM support routines
 *
 * Copyright (C) 2005-2008 Michael Buesch <mb@bu3sch.de>
 * Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
 * Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
 * Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
 * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
 *
 * Licensed under the GNU/GPL. See COPYING for details.
 */

#include "ssb_private.h"

16 17
#include <linux/ctype.h>

18

M
Michael Buesch 已提交
19 20 21
static const struct ssb_sprom *fallback_sprom;


22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len,
		     size_t sprom_size_words)
{
	int i, pos = 0;

	for (i = 0; i < sprom_size_words; i++)
		pos += snprintf(buf + pos, buf_len - pos - 1,
				"%04X", swab16(sprom[i]) & 0xFFFF);
	pos += snprintf(buf + pos, buf_len - pos - 1, "\n");

	return pos + 1;
}

static int hex2sprom(u16 *sprom, const char *dump, size_t len,
		     size_t sprom_size_words)
{
38 39
	char c, tmp[5] = { 0 };
	int err, cnt = 0;
40 41
	unsigned long parsed;

42 43 44 45 46 47 48 49 50
	/* Strip whitespace at the end. */
	while (len) {
		c = dump[len - 1];
		if (!isspace(c) && c != '\0')
			break;
		len--;
	}
	/* Length must match exactly. */
	if (len != sprom_size_words * 4)
51 52 53 54 55
		return -EINVAL;

	while (cnt < sprom_size_words) {
		memcpy(tmp, dump, 4);
		dump += 4;
56 57 58
		err = strict_strtoul(tmp, 16, &parsed);
		if (err)
			return err;
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
		sprom[cnt++] = swab16((u16)parsed);
	}

	return 0;
}

/* Common sprom device-attribute show-handler */
ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf,
			    int (*sprom_read)(struct ssb_bus *bus, u16 *sprom))
{
	u16 *sprom;
	int err = -ENOMEM;
	ssize_t count = 0;
	size_t sprom_size_words = bus->sprom_size;

	sprom = kcalloc(sprom_size_words, sizeof(u16), GFP_KERNEL);
	if (!sprom)
		goto out;

	/* Use interruptible locking, as the SPROM write might
	 * be holding the lock for several seconds. So allow userspace
	 * to cancel operation. */
	err = -ERESTARTSYS;
	if (mutex_lock_interruptible(&bus->sprom_mutex))
		goto out_kfree;
	err = sprom_read(bus, sprom);
	mutex_unlock(&bus->sprom_mutex);

	if (!err)
		count = sprom2hex(sprom, buf, PAGE_SIZE, sprom_size_words);

out_kfree:
	kfree(sprom);
out:
	return err ? err : count;
}

/* Common sprom device-attribute store-handler */
ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
			     const char *buf, size_t count,
			     int (*sprom_check_crc)(const u16 *sprom, size_t size),
			     int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom))
{
	u16 *sprom;
	int res = 0, err = -ENOMEM;
	size_t sprom_size_words = bus->sprom_size;
M
Michael Buesch 已提交
105
	struct ssb_freeze_context freeze;
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

	sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL);
	if (!sprom)
		goto out;
	err = hex2sprom(sprom, buf, count, sprom_size_words);
	if (err) {
		err = -EINVAL;
		goto out_kfree;
	}
	err = sprom_check_crc(sprom, sprom_size_words);
	if (err) {
		err = -EINVAL;
		goto out_kfree;
	}

	/* Use interruptible locking, as the SPROM write might
	 * be holding the lock for several seconds. So allow userspace
	 * to cancel operation. */
	err = -ERESTARTSYS;
	if (mutex_lock_interruptible(&bus->sprom_mutex))
		goto out_kfree;
M
Michael Buesch 已提交
127
	err = ssb_devices_freeze(bus, &freeze);
128 129 130 131 132
	if (err) {
		ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n");
		goto out_unlock;
	}
	res = sprom_write(bus, sprom);
M
Michael Buesch 已提交
133
	err = ssb_devices_thaw(&freeze);
134 135 136 137 138 139 140 141 142 143 144
	if (err)
		ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n");
out_unlock:
	mutex_unlock(&bus->sprom_mutex);
out_kfree:
	kfree(sprom);
out:
	if (res)
		return res;
	return err ? err : count;
}
M
Michael Buesch 已提交
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177

/**
 * ssb_arch_set_fallback_sprom - Set a fallback SPROM for use if no SPROM is found.
 *
 * @sprom: The SPROM data structure to register.
 *
 * With this function the architecture implementation may register a fallback
 * SPROM data structure. The fallback is only used for PCI based SSB devices,
 * where no valid SPROM can be found in the shadow registers.
 *
 * This function is useful for weird architectures that have a half-assed SSB device
 * hardwired to their PCI bus.
 *
 * Note that it does only work with PCI attached SSB devices. PCMCIA devices currently
 * don't use this fallback.
 * Architectures must provide the SPROM for native SSB devices anyway,
 * so the fallback also isn't used for native devices.
 *
 * This function is available for architecture code, only. So it is not exported.
 */
int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom)
{
	if (fallback_sprom)
		return -EEXIST;
	fallback_sprom = sprom;

	return 0;
}

const struct ssb_sprom *ssb_get_fallback_sprom(void)
{
	return fallback_sprom;
}