diff options
author | Kristian Høgsberg <krh@redhat.com> | 2006-12-19 19:58:27 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-03-09 16:02:33 -0500 |
commit | 3038e353cfaf548eb94f02b172b9dbe412abd24c (patch) | |
tree | 70e50c20e117e2dacb7cd810d00fe595e60d26ce /drivers/firewire/fw-card.c | |
parent | 08e15e81a40e3241ce93b4a43886f3abda184aa6 (diff) |
firewire: Add core firewire stack.
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire/fw-card.c')
-rw-r--r-- | drivers/firewire/fw-card.c | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c new file mode 100644 index 000000000000..d8abd70ce843 --- /dev/null +++ b/drivers/firewire/fw-card.c | |||
@@ -0,0 +1,384 @@ | |||
1 | /* -*- c-basic-offset: 8 -*- | ||
2 | * | ||
3 | * fw-card.c - card level functions | ||
4 | * | ||
5 | * Copyright (C) 2005-2006 Kristian Hoegsberg <krh@bitplanet.net> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software Foundation, | ||
19 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/errno.h> | ||
24 | #include <linux/device.h> | ||
25 | #include "fw-transaction.h" | ||
26 | #include "fw-topology.h" | ||
27 | |||
28 | /* The lib/crc16.c implementation uses the standard (0x8005) | ||
29 | * polynomial, but we need the ITU-T (or CCITT) polynomial (0x1021). | ||
30 | * The implementation below works on an array of host-endian u32 | ||
31 | * words, assuming they'll be transmited msb first. */ | ||
32 | static u16 | ||
33 | crc16_itu_t(const u32 *buffer, size_t length) | ||
34 | { | ||
35 | int shift, i; | ||
36 | u32 data; | ||
37 | u16 sum, crc = 0; | ||
38 | |||
39 | for (i = 0; i < length; i++) { | ||
40 | data = *buffer++; | ||
41 | for (shift = 28; shift >= 0; shift -= 4 ) { | ||
42 | sum = ((crc >> 12) ^ (data >> shift)) & 0xf; | ||
43 | crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); | ||
44 | } | ||
45 | crc &= 0xffff; | ||
46 | } | ||
47 | |||
48 | return crc; | ||
49 | } | ||
50 | |||
51 | static LIST_HEAD(card_list); | ||
52 | |||
53 | static LIST_HEAD(descriptor_list); | ||
54 | static int descriptor_count; | ||
55 | |||
56 | #define bib_crc(v) ((v) << 0) | ||
57 | #define bib_crc_length(v) ((v) << 16) | ||
58 | #define bib_info_length(v) ((v) << 24) | ||
59 | |||
60 | #define bib_link_speed(v) ((v) << 0) | ||
61 | #define bib_generation(v) ((v) << 4) | ||
62 | #define bib_max_rom(v) ((v) << 8) | ||
63 | #define bib_max_receive(v) ((v) << 12) | ||
64 | #define bib_cyc_clk_acc(v) ((v) << 16) | ||
65 | #define bib_pmc ((1) << 27) | ||
66 | #define bib_bmc ((1) << 28) | ||
67 | #define bib_isc ((1) << 29) | ||
68 | #define bib_cmc ((1) << 30) | ||
69 | #define bib_imc ((1) << 31) | ||
70 | |||
71 | static u32 * | ||
72 | generate_config_rom (struct fw_card *card, size_t *config_rom_length) | ||
73 | { | ||
74 | struct fw_descriptor *desc; | ||
75 | static u32 config_rom[256]; | ||
76 | int i, j, length; | ||
77 | |||
78 | /* Initialize contents of config rom buffer. On the OHCI | ||
79 | * controller, block reads to the config rom accesses the host | ||
80 | * memory, but quadlet read access the hardware bus info block | ||
81 | * registers. That's just crack, but it means we should make | ||
82 | * sure the contents of bus info block in host memory mathces | ||
83 | * the version stored in the OHCI registers. */ | ||
84 | |||
85 | memset(config_rom, 0, sizeof config_rom); | ||
86 | config_rom[0] = bib_crc_length(4) | bib_info_length(4) | bib_crc(0); | ||
87 | config_rom[1] = 0x31333934; | ||
88 | |||
89 | config_rom[2] = | ||
90 | bib_link_speed(card->link_speed) | | ||
91 | bib_generation(card->config_rom_generation++ % 14 + 2) | | ||
92 | bib_max_rom(2) | | ||
93 | bib_max_receive(card->max_receive) | | ||
94 | bib_isc | bib_cmc | bib_imc; | ||
95 | config_rom[3] = card->guid >> 32; | ||
96 | config_rom[4] = card->guid; | ||
97 | |||
98 | /* Generate root directory. */ | ||
99 | i = 5; | ||
100 | config_rom[i++] = 0; | ||
101 | config_rom[i++] = 0x0c0083c0; /* node capabilities */ | ||
102 | config_rom[i++] = 0x03d00d1e; /* vendor id */ | ||
103 | j = i + descriptor_count; | ||
104 | |||
105 | /* Generate root directory entries for descriptors. */ | ||
106 | list_for_each_entry (desc, &descriptor_list, link) { | ||
107 | config_rom[i] = desc->key | (j - i); | ||
108 | i++; | ||
109 | j += desc->length; | ||
110 | } | ||
111 | |||
112 | /* Update root directory length. */ | ||
113 | config_rom[5] = (i - 5 - 1) << 16; | ||
114 | |||
115 | /* End of root directory, now copy in descriptors. */ | ||
116 | list_for_each_entry (desc, &descriptor_list, link) { | ||
117 | memcpy(&config_rom[i], desc->data, desc->length * 4); | ||
118 | i += desc->length; | ||
119 | } | ||
120 | |||
121 | /* Calculate CRCs for all blocks in the config rom. This | ||
122 | * assumes that CRC length and info length are identical for | ||
123 | * the bus info block, which is always the case for this | ||
124 | * implementation. */ | ||
125 | for (i = 0; i < j; i += length + 1) { | ||
126 | length = (config_rom[i] >> 16) & 0xff; | ||
127 | config_rom[i] |= crc16_itu_t(&config_rom[i + 1], length); | ||
128 | } | ||
129 | |||
130 | *config_rom_length = j; | ||
131 | |||
132 | return config_rom; | ||
133 | } | ||
134 | |||
135 | static void | ||
136 | update_config_roms (void) | ||
137 | { | ||
138 | struct fw_card *card; | ||
139 | u32 *config_rom; | ||
140 | size_t length; | ||
141 | |||
142 | list_for_each_entry (card, &card_list, link) { | ||
143 | config_rom = generate_config_rom(card, &length); | ||
144 | card->driver->set_config_rom(card, config_rom, length); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | int | ||
149 | fw_core_add_descriptor (struct fw_descriptor *desc) | ||
150 | { | ||
151 | size_t i; | ||
152 | |||
153 | /* Check descriptor is valid; the length of all blocks in the | ||
154 | * descriptor has to add up to exactly the length of the | ||
155 | * block. */ | ||
156 | i = 0; | ||
157 | while (i < desc->length) | ||
158 | i += (desc->data[i] >> 16) + 1; | ||
159 | |||
160 | if (i != desc->length) | ||
161 | return -1; | ||
162 | |||
163 | down_write(&fw_bus_type.subsys.rwsem); | ||
164 | |||
165 | list_add_tail (&desc->link, &descriptor_list); | ||
166 | descriptor_count++; | ||
167 | update_config_roms(); | ||
168 | |||
169 | up_write(&fw_bus_type.subsys.rwsem); | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | EXPORT_SYMBOL(fw_core_add_descriptor); | ||
174 | |||
175 | void | ||
176 | fw_core_remove_descriptor (struct fw_descriptor *desc) | ||
177 | { | ||
178 | down_write(&fw_bus_type.subsys.rwsem); | ||
179 | |||
180 | list_del(&desc->link); | ||
181 | descriptor_count--; | ||
182 | update_config_roms(); | ||
183 | |||
184 | up_write(&fw_bus_type.subsys.rwsem); | ||
185 | } | ||
186 | EXPORT_SYMBOL(fw_core_remove_descriptor); | ||
187 | |||
188 | static void | ||
189 | release_card(struct device *device) | ||
190 | { | ||
191 | struct fw_card *card = | ||
192 | container_of(device, struct fw_card, card_device); | ||
193 | |||
194 | kfree(card); | ||
195 | } | ||
196 | |||
197 | static void | ||
198 | flush_timer_callback(unsigned long data) | ||
199 | { | ||
200 | struct fw_card *card = (struct fw_card *)data; | ||
201 | |||
202 | fw_flush_transactions(card); | ||
203 | } | ||
204 | |||
205 | void | ||
206 | fw_card_initialize(struct fw_card *card, struct fw_card_driver *driver, | ||
207 | struct device *device) | ||
208 | { | ||
209 | static int index; | ||
210 | |||
211 | card->index = index++; | ||
212 | card->driver = driver; | ||
213 | card->device = device; | ||
214 | card->current_tlabel = 0; | ||
215 | card->tlabel_mask = 0; | ||
216 | card->color = 0; | ||
217 | |||
218 | INIT_LIST_HEAD(&card->transaction_list); | ||
219 | spin_lock_init(&card->lock); | ||
220 | setup_timer(&card->flush_timer, | ||
221 | flush_timer_callback, (unsigned long)card); | ||
222 | |||
223 | card->local_node = NULL; | ||
224 | |||
225 | card->card_device.bus = &fw_bus_type; | ||
226 | card->card_device.release = release_card; | ||
227 | card->card_device.parent = card->device; | ||
228 | snprintf(card->card_device.bus_id, sizeof card->card_device.bus_id, | ||
229 | "fwcard%d", card->index); | ||
230 | |||
231 | device_initialize(&card->card_device); | ||
232 | } | ||
233 | EXPORT_SYMBOL(fw_card_initialize); | ||
234 | |||
235 | int | ||
236 | fw_card_add(struct fw_card *card, | ||
237 | u32 max_receive, u32 link_speed, u64 guid) | ||
238 | { | ||
239 | int retval; | ||
240 | u32 *config_rom; | ||
241 | size_t length; | ||
242 | |||
243 | card->max_receive = max_receive; | ||
244 | card->link_speed = link_speed; | ||
245 | card->guid = guid; | ||
246 | |||
247 | /* FIXME: add #define's for phy registers. */ | ||
248 | /* Activate link_on bit and contender bit in our self ID packets.*/ | ||
249 | if (card->driver->update_phy_reg(card, 4, 0, 0x80 | 0x40) < 0) | ||
250 | return -EIO; | ||
251 | |||
252 | retval = device_add(&card->card_device); | ||
253 | if (retval < 0) { | ||
254 | fw_error("Failed to register card device."); | ||
255 | return retval; | ||
256 | } | ||
257 | |||
258 | /* The subsystem grabs a reference when the card is added and | ||
259 | * drops it when the driver calls fw_core_remove_card. */ | ||
260 | fw_card_get(card); | ||
261 | |||
262 | down_write(&fw_bus_type.subsys.rwsem); | ||
263 | config_rom = generate_config_rom (card, &length); | ||
264 | list_add_tail(&card->link, &card_list); | ||
265 | up_write(&fw_bus_type.subsys.rwsem); | ||
266 | |||
267 | return card->driver->enable(card, config_rom, length); | ||
268 | } | ||
269 | EXPORT_SYMBOL(fw_card_add); | ||
270 | |||
271 | |||
272 | /* The next few functions implements a dummy driver that use once a | ||
273 | * card driver shuts down an fw_card. This allows the driver to | ||
274 | * cleanly unload, as all IO to the card will be handled by the dummy | ||
275 | * driver instead of calling into the (possibly) unloaded module. The | ||
276 | * dummy driver just fails all IO. */ | ||
277 | |||
278 | static int | ||
279 | dummy_enable(struct fw_card *card, u32 *config_rom, size_t length) | ||
280 | { | ||
281 | BUG(); | ||
282 | return -1; | ||
283 | } | ||
284 | |||
285 | static int | ||
286 | dummy_update_phy_reg(struct fw_card *card, int address, | ||
287 | int clear_bits, int set_bits) | ||
288 | { | ||
289 | return -ENODEV; | ||
290 | } | ||
291 | |||
292 | static int | ||
293 | dummy_set_config_rom(struct fw_card *card, | ||
294 | u32 *config_rom, size_t length) | ||
295 | { | ||
296 | /* We take the card out of card_list before setting the dummy | ||
297 | * driver, so this should never get called. */ | ||
298 | BUG(); | ||
299 | return -1; | ||
300 | } | ||
301 | |||
302 | static void | ||
303 | dummy_send_request(struct fw_card *card, struct fw_packet *packet) | ||
304 | { | ||
305 | packet->callback(packet, card, -ENODEV); | ||
306 | } | ||
307 | |||
308 | static void | ||
309 | dummy_send_response(struct fw_card *card, struct fw_packet *packet) | ||
310 | { | ||
311 | packet->callback(packet, card, -ENODEV); | ||
312 | } | ||
313 | |||
314 | static int | ||
315 | dummy_enable_phys_dma(struct fw_card *card, | ||
316 | int node_id, int generation) | ||
317 | { | ||
318 | return -ENODEV; | ||
319 | } | ||
320 | |||
321 | static struct fw_card_driver dummy_driver = { | ||
322 | .name = "dummy", | ||
323 | .enable = dummy_enable, | ||
324 | .update_phy_reg = dummy_update_phy_reg, | ||
325 | .set_config_rom = dummy_set_config_rom, | ||
326 | .send_request = dummy_send_request, | ||
327 | .send_response = dummy_send_response, | ||
328 | .enable_phys_dma = dummy_enable_phys_dma | ||
329 | }; | ||
330 | |||
331 | void | ||
332 | fw_core_remove_card(struct fw_card *card) | ||
333 | { | ||
334 | card->driver->update_phy_reg(card, 4, 0x80 | 0x40, 0); | ||
335 | fw_core_initiate_bus_reset(card, 1); | ||
336 | |||
337 | down_write(&fw_bus_type.subsys.rwsem); | ||
338 | list_del(&card->link); | ||
339 | up_write(&fw_bus_type.subsys.rwsem); | ||
340 | |||
341 | /* Set up the dummy driver. */ | ||
342 | card->driver = &dummy_driver; | ||
343 | |||
344 | fw_flush_transactions(card); | ||
345 | |||
346 | fw_destroy_nodes(card); | ||
347 | |||
348 | /* This also drops the subsystem reference. */ | ||
349 | device_unregister(&card->card_device); | ||
350 | } | ||
351 | EXPORT_SYMBOL(fw_core_remove_card); | ||
352 | |||
353 | struct fw_card * | ||
354 | fw_card_get(struct fw_card *card) | ||
355 | { | ||
356 | get_device(&card->card_device); | ||
357 | |||
358 | return card; | ||
359 | } | ||
360 | EXPORT_SYMBOL(fw_card_get); | ||
361 | |||
362 | /* An assumption for fw_card_put() is that the card driver allocates | ||
363 | * the fw_card struct with kalloc and that it has been shut down | ||
364 | * before the last ref is dropped. */ | ||
365 | void | ||
366 | fw_card_put(struct fw_card *card) | ||
367 | { | ||
368 | put_device(&card->card_device); | ||
369 | } | ||
370 | EXPORT_SYMBOL(fw_card_put); | ||
371 | |||
372 | int | ||
373 | fw_core_initiate_bus_reset(struct fw_card *card, int short_reset) | ||
374 | { | ||
375 | u32 address; | ||
376 | |||
377 | if (short_reset) | ||
378 | address = 5; | ||
379 | else | ||
380 | address = 1; | ||
381 | |||
382 | return card->driver->update_phy_reg(card, address, 0, 0x40); | ||
383 | } | ||
384 | EXPORT_SYMBOL(fw_core_initiate_bus_reset); | ||