Blame view

drivers/ssb/sprom.c 5.19 KB
e7ec2e323   Michael Buesch   ssb: Add SPROM/in...
1
2
3
4
  /*
   * Sonics Silicon Backplane
   * Common SPROM support routines
   *
eb032b983   Michael Buesch   Update my e-mail ...
5
   * Copyright (C) 2005-2008 Michael Buesch <m@bues.ch>
e7ec2e323   Michael Buesch   ssb: Add SPROM/in...
6
7
8
9
10
11
12
13
14
   * 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"
e33761e6f   Michael Buesch   ssb: Fix range ch...
15
  #include <linux/ctype.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
16
  #include <linux/slab.h>
e33761e6f   Michael Buesch   ssb: Fix range ch...
17

e7ec2e323   Michael Buesch   ssb: Add SPROM/in...
18

b3ae52b6b   Hauke Mehrtens   SSB: Change fallb...
19
  static int(*get_fallback_sprom)(struct ssb_bus *dev, struct ssb_sprom *out);
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
20

e7ec2e323   Michael Buesch   ssb: Add SPROM/in...
21
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, "
  ");
  
  	return pos + 1;
  }
  
  static int hex2sprom(u16 *sprom, const char *dump, size_t len,
  		     size_t sprom_size_words)
  {
e33761e6f   Michael Buesch   ssb: Fix range ch...
38
39
  	char c, tmp[5] = { 0 };
  	int err, cnt = 0;
e7ec2e323   Michael Buesch   ssb: Add SPROM/in...
40
  	unsigned long parsed;
e33761e6f   Michael Buesch   ssb: Fix range ch...
41
42
43
44
45
46
47
48
49
  	/* 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)
e7ec2e323   Michael Buesch   ssb: Add SPROM/in...
50
51
52
53
54
  		return -EINVAL;
  
  	while (cnt < sprom_size_words) {
  		memcpy(tmp, dump, 4);
  		dump += 4;
6b1ea4b29   Jingoo Han   ssb: sprom: repla...
55
  		err = kstrtoul(tmp, 16, &parsed);
e33761e6f   Michael Buesch   ssb: Fix range ch...
56
57
  		if (err)
  			return err;
e7ec2e323   Michael Buesch   ssb: Add SPROM/in...
58
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
  		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;
3ba6018aa   Michael Buesch   ssb: Fix SPROM wr...
104
  	struct ssb_freeze_context freeze;
e7ec2e323   Michael Buesch   ssb: Add SPROM/in...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  
  	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;
3ba6018aa   Michael Buesch   ssb: Fix SPROM wr...
126
  	err = ssb_devices_freeze(bus, &freeze);
e7ec2e323   Michael Buesch   ssb: Add SPROM/in...
127
  	if (err) {
33a606ac8   Joe Perches   ssb: Convert ssb_...
128
129
  		ssb_err("SPROM write: Could not freeze all devices
  ");
e7ec2e323   Michael Buesch   ssb: Add SPROM/in...
130
131
132
  		goto out_unlock;
  	}
  	res = sprom_write(bus, sprom);
3ba6018aa   Michael Buesch   ssb: Fix SPROM wr...
133
  	err = ssb_devices_thaw(&freeze);
e7ec2e323   Michael Buesch   ssb: Add SPROM/in...
134
  	if (err)
33a606ac8   Joe Perches   ssb: Convert ssb_...
135
136
  		ssb_err("SPROM write: Could not thaw all devices
  ");
e7ec2e323   Michael Buesch   ssb: Add SPROM/in...
137
138
139
140
141
142
143
144
145
  out_unlock:
  	mutex_unlock(&bus->sprom_mutex);
  out_kfree:
  	kfree(sprom);
  out:
  	if (res)
  		return res;
  	return err ? err : count;
  }
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
146
147
  
  /**
b3ae52b6b   Hauke Mehrtens   SSB: Change fallb...
148
149
   * ssb_arch_register_fallback_sprom - Registers a method providing a
   * fallback SPROM if no SPROM is found.
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
150
   *
b3ae52b6b   Hauke Mehrtens   SSB: Change fallb...
151
   * @sprom_callback: The callback function.
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
152
   *
b3ae52b6b   Hauke Mehrtens   SSB: Change fallb...
153
154
155
156
   * With this function the architecture implementation may register a
   * callback handler which fills the SPROM data structure. The fallback is
   * only used for PCI based SSB devices, where no valid SPROM can be found
   * in the shadow registers.
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
157
   *
b3ae52b6b   Hauke Mehrtens   SSB: Change fallb...
158
159
   * This function is useful for weird architectures that have a half-assed
   * SSB device hardwired to their PCI bus.
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
160
   *
b3ae52b6b   Hauke Mehrtens   SSB: Change fallb...
161
162
163
164
   * 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.
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
165
   *
b3ae52b6b   Hauke Mehrtens   SSB: Change fallb...
166
167
   * This function is available for architecture code, only. So it is not
   * exported.
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
168
   */
b3ae52b6b   Hauke Mehrtens   SSB: Change fallb...
169
170
  int ssb_arch_register_fallback_sprom(int (*sprom_callback)(struct ssb_bus *bus,
  				     struct ssb_sprom *out))
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
171
  {
b3ae52b6b   Hauke Mehrtens   SSB: Change fallb...
172
  	if (get_fallback_sprom)
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
173
  		return -EEXIST;
b3ae52b6b   Hauke Mehrtens   SSB: Change fallb...
174
  	get_fallback_sprom = sprom_callback;
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
175
176
177
  
  	return 0;
  }
b3ae52b6b   Hauke Mehrtens   SSB: Change fallb...
178
  int ssb_fill_sprom_with_fallback(struct ssb_bus *bus, struct ssb_sprom *out)
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
179
  {
b3ae52b6b   Hauke Mehrtens   SSB: Change fallb...
180
181
182
183
  	if (!get_fallback_sprom)
  		return -ENOENT;
  
  	return get_fallback_sprom(bus, out);
e79c1ba84   Michael Buesch   ssb: Add SPROM fa...
184
  }
d53cdbb94   John W. Linville   ssb: do not read ...
185
186
187
188
189
190
191
192
193
  
  /* http://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */
  bool ssb_is_sprom_available(struct ssb_bus *bus)
  {
  	/* status register only exists on chipcomon rev >= 11 and we need check
  	   for >= 31 only */
  	/* this routine differs from specs as we do not access SPROM directly
  	   on PCMCIA */
  	if (bus->bustype == SSB_BUSTYPE_PCI &&
25985edce   Lucas De Marchi   Fix common misspe...
194
  	    bus->chipco.dev &&	/* can be unavailable! */
d53cdbb94   John W. Linville   ssb: do not read ...
195
196
197
198
199
  	    bus->chipco.dev->id.revision >= 31)
  		return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM;
  
  	return true;
  }