blob: 25b22609e793a8880c53ef9d06ff87201ca4c3f4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * IEEE 1394 for Linux
3 *
4 * Copyright (C) 1999 Andreas E. Bombe
5 *
6 * This code is licensed under the GPL. See the file COPYING in the root
7 * directory of the kernel sources for details.
8 *
9 *
10 * Contributions:
11 *
12 * Christian Toegel <christian.toegel@gmx.at>
13 * unregister address space
14 *
15 * Manfred Weihs <weihs@ict.tuwien.ac.at>
16 * unregister address space
17 *
18 */
19
20#include <linux/config.h>
21#include <linux/slab.h>
22#include <linux/list.h>
23#include <linux/bitops.h>
24
25#include "ieee1394.h"
26#include "ieee1394_types.h"
27#include "hosts.h"
28#include "ieee1394_core.h"
29#include "highlevel.h"
30#include "nodemgr.h"
31
32
33struct hl_host_info {
34 struct list_head list;
35 struct hpsb_host *host;
36 size_t size;
37 unsigned long key;
38 void *data;
39};
40
41
42static LIST_HEAD(hl_drivers);
43static DECLARE_RWSEM(hl_drivers_sem);
44
45static LIST_HEAD(hl_irqs);
46static DEFINE_RWLOCK(hl_irqs_lock);
47
48static DEFINE_RWLOCK(addr_space_lock);
49
50/* addr_space list will have zero and max already included as bounds */
51static struct hpsb_address_ops dummy_ops = { NULL, NULL, NULL, NULL };
52static struct hpsb_address_serve dummy_zero_addr, dummy_max_addr;
53
54
55static struct hl_host_info *hl_get_hostinfo(struct hpsb_highlevel *hl,
Ben Collins2c4b69b2006-06-12 18:16:16 -040056 struct hpsb_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -070057{
58 struct hl_host_info *hi = NULL;
59
60 if (!hl || !host)
61 return NULL;
62
63 read_lock(&hl->host_info_lock);
64 list_for_each_entry(hi, &hl->host_info_list, list) {
65 if (hi->host == host) {
66 read_unlock(&hl->host_info_lock);
67 return hi;
68 }
69 }
70 read_unlock(&hl->host_info_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 return NULL;
72}
73
Linus Torvalds1da177e2005-04-16 15:20:36 -070074/* Returns a per host/driver data structure that was previously stored by
75 * hpsb_create_hostinfo. */
76void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host)
77{
78 struct hl_host_info *hi = hl_get_hostinfo(hl, host);
79
Ben Collins2c4b69b2006-06-12 18:16:16 -040080 return hi ? hi->data : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081}
82
Linus Torvalds1da177e2005-04-16 15:20:36 -070083/* If size is zero, then the return here is only valid for error checking */
84void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
85 size_t data_size)
86{
87 struct hl_host_info *hi;
88 void *data;
89 unsigned long flags;
90
91 hi = hl_get_hostinfo(hl, host);
92 if (hi) {
Ben Collins2c4b69b2006-06-12 18:16:16 -040093 HPSB_ERR("%s called hpsb_create_hostinfo when hostinfo already"
94 " exists", hl->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 return NULL;
96 }
97
Stefan Richter85511582005-11-07 06:31:45 -050098 hi = kzalloc(sizeof(*hi) + data_size, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 if (!hi)
100 return NULL;
101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 if (data_size) {
103 data = hi->data = hi + 1;
104 hi->size = data_size;
105 } else
106 data = hi;
107
108 hi->host = host;
109
110 write_lock_irqsave(&hl->host_info_lock, flags);
111 list_add_tail(&hi->list, &hl->host_info_list);
112 write_unlock_irqrestore(&hl->host_info_lock, flags);
113
114 return data;
115}
116
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
118 void *data)
119{
120 struct hl_host_info *hi;
121
122 hi = hl_get_hostinfo(hl, host);
123 if (hi) {
124 if (!hi->size && !hi->data) {
125 hi->data = data;
126 return 0;
127 } else
Ben Collins2c4b69b2006-06-12 18:16:16 -0400128 HPSB_ERR("%s called hpsb_set_hostinfo when hostinfo "
129 "already has data", hl->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 } else
131 HPSB_ERR("%s called hpsb_set_hostinfo when no hostinfo exists",
132 hl->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 return -EINVAL;
134}
135
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host)
137{
138 struct hl_host_info *hi;
139
140 hi = hl_get_hostinfo(hl, host);
141 if (hi) {
142 unsigned long flags;
143 write_lock_irqsave(&hl->host_info_lock, flags);
144 list_del(&hi->list);
145 write_unlock_irqrestore(&hl->host_info_lock, flags);
146 kfree(hi);
147 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 return;
149}
150
Ben Collins2c4b69b2006-06-12 18:16:16 -0400151void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host,
152 unsigned long key)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153{
154 struct hl_host_info *hi;
155
156 hi = hl_get_hostinfo(hl, host);
157 if (hi)
158 hi->key = key;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 return;
160}
161
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key)
163{
164 struct hl_host_info *hi;
165 void *data = NULL;
166
167 if (!hl)
168 return NULL;
169
170 read_lock(&hl->host_info_lock);
171 list_for_each_entry(hi, &hl->host_info_list, list) {
172 if (hi->key == key) {
173 data = hi->data;
174 break;
175 }
176 }
177 read_unlock(&hl->host_info_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 return data;
179}
180
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181static int highlevel_for_each_host_reg(struct hpsb_host *host, void *__data)
182{
183 struct hpsb_highlevel *hl = __data;
184
185 hl->add_host(host);
186
Ben Collins2c4b69b2006-06-12 18:16:16 -0400187 if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0)
188 HPSB_ERR("Failed to generate Configuration ROM image for host "
189 "%s-%d", hl->name, host->id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 return 0;
191}
192
193void hpsb_register_highlevel(struct hpsb_highlevel *hl)
194{
Ben Collins44515192006-06-12 18:16:01 -0400195 unsigned long flags;
196
Ben Collins2c4b69b2006-06-12 18:16:16 -0400197 INIT_LIST_HEAD(&hl->addr_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 INIT_LIST_HEAD(&hl->host_info_list);
199
200 rwlock_init(&hl->host_info_lock);
201
202 down_write(&hl_drivers_sem);
Ben Collins2c4b69b2006-06-12 18:16:16 -0400203 list_add_tail(&hl->hl_list, &hl_drivers);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 up_write(&hl_drivers_sem);
205
Ben Collins44515192006-06-12 18:16:01 -0400206 write_lock_irqsave(&hl_irqs_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 list_add_tail(&hl->irq_list, &hl_irqs);
Ben Collins44515192006-06-12 18:16:01 -0400208 write_unlock_irqrestore(&hl_irqs_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209
210 if (hl->add_host)
211 nodemgr_for_each_host(hl, highlevel_for_each_host_reg);
Ben Collins2c4b69b2006-06-12 18:16:16 -0400212 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213}
214
215static void __delete_addr(struct hpsb_address_serve *as)
216{
217 list_del(&as->host_list);
218 list_del(&as->hl_list);
219 kfree(as);
220}
221
Ben Collins2c4b69b2006-06-12 18:16:16 -0400222static void __unregister_host(struct hpsb_highlevel *hl, struct hpsb_host *host,
223 int update_cr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224{
225 unsigned long flags;
226 struct list_head *lh, *next;
227 struct hpsb_address_serve *as;
228
229 /* First, let the highlevel driver unreg */
230 if (hl->remove_host)
231 hl->remove_host(host);
232
233 /* Remove any addresses that are matched for this highlevel driver
234 * and this particular host. */
235 write_lock_irqsave(&addr_space_lock, flags);
236 list_for_each_safe (lh, next, &hl->addr_list) {
237 as = list_entry(lh, struct hpsb_address_serve, hl_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 if (as->host == host)
239 __delete_addr(as);
240 }
241 write_unlock_irqrestore(&addr_space_lock, flags);
242
243 /* Now update the config-rom to reflect anything removed by the
244 * highlevel driver. */
Ben Collins2c4b69b2006-06-12 18:16:16 -0400245 if (update_cr && host->update_config_rom &&
246 hpsb_update_config_rom_image(host) < 0)
247 HPSB_ERR("Failed to generate Configuration ROM image for host "
248 "%s-%d", hl->name, host->id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
Ben Collins2c4b69b2006-06-12 18:16:16 -0400250 /* Finally remove all the host info associated between these two. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 hpsb_destroy_hostinfo(hl, host);
252}
253
254static int highlevel_for_each_host_unreg(struct hpsb_host *host, void *__data)
255{
256 struct hpsb_highlevel *hl = __data;
257
258 __unregister_host(hl, host, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 return 0;
260}
261
262void hpsb_unregister_highlevel(struct hpsb_highlevel *hl)
263{
Ben Collins44515192006-06-12 18:16:01 -0400264 unsigned long flags;
265
266 write_lock_irqsave(&hl_irqs_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 list_del(&hl->irq_list);
Ben Collins44515192006-06-12 18:16:01 -0400268 write_unlock_irqrestore(&hl_irqs_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269
270 down_write(&hl_drivers_sem);
Ben Collins2c4b69b2006-06-12 18:16:16 -0400271 list_del(&hl->hl_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 up_write(&hl_drivers_sem);
273
274 nodemgr_for_each_host(hl, highlevel_for_each_host_unreg);
275}
276
277u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
278 struct hpsb_host *host,
279 struct hpsb_address_ops *ops,
280 u64 size, u64 alignment,
281 u64 start, u64 end)
282{
283 struct hpsb_address_serve *as, *a1, *a2;
284 struct list_head *entry;
Ben Collins67372312006-06-12 18:15:31 -0400285 u64 retval = CSR1212_INVALID_ADDR_SPACE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 unsigned long flags;
287 u64 align_mask = ~(alignment - 1);
288
289 if ((alignment & 3) || (alignment > 0x800000000000ULL) ||
Akinobu Mita37d54112006-03-26 01:39:56 -0800290 (hweight64(alignment) != 1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 HPSB_ERR("%s called with invalid alignment: 0x%048llx",
292 __FUNCTION__, (unsigned long long)alignment);
293 return retval;
294 }
295
Ben Collins8aef63f2006-06-12 18:13:21 -0400296 /* default range,
297 * avoids controller's posted write area (see OHCI 1.1 clause 1.5) */
Ben Collins67372312006-06-12 18:15:31 -0400298 if (start == CSR1212_INVALID_ADDR_SPACE &&
299 end == CSR1212_INVALID_ADDR_SPACE) {
Ben Collins8aef63f2006-06-12 18:13:21 -0400300 start = host->middle_addr_space;
Ben Collins67372312006-06-12 18:15:31 -0400301 end = CSR1212_ALL_SPACE_END;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 }
303
Ben Collins2c4b69b2006-06-12 18:16:16 -0400304 if (((start|end) & ~align_mask) || (start >= end) ||
305 (end > CSR1212_ALL_SPACE_END)) {
306 HPSB_ERR("%s called with invalid addresses "
307 "(start = %012Lx end = %012Lx)", __FUNCTION__,
308 (unsigned long long)start,(unsigned long long)end);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 return retval;
310 }
311
Stefan Richter85511582005-11-07 06:31:45 -0500312 as = kmalloc(sizeof(*as), GFP_KERNEL);
313 if (!as)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
316 INIT_LIST_HEAD(&as->host_list);
317 INIT_LIST_HEAD(&as->hl_list);
318 as->op = ops;
319 as->host = host;
320
321 write_lock_irqsave(&addr_space_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 list_for_each(entry, &host->addr_space) {
323 u64 a1sa, a1ea;
324 u64 a2sa, a2ea;
325
326 a1 = list_entry(entry, struct hpsb_address_serve, host_list);
Ben Collins2c4b69b2006-06-12 18:16:16 -0400327 a2 = list_entry(entry->next, struct hpsb_address_serve,
328 host_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
330 a1sa = a1->start & align_mask;
331 a1ea = (a1->end + alignment -1) & align_mask;
332 a2sa = a2->start & align_mask;
333 a2ea = (a2->end + alignment -1) & align_mask;
334
Ben Collins2c4b69b2006-06-12 18:16:16 -0400335 if ((a2sa - a1ea >= size) && (a2sa - start >= size) &&
336 (a2sa > start)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 as->start = max(start, a1ea);
338 as->end = as->start + size;
339 list_add(&as->host_list, entry);
340 list_add_tail(&as->hl_list, &hl->addr_list);
341 retval = as->start;
342 break;
343 }
344 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 write_unlock_irqrestore(&addr_space_lock, flags);
346
Ben Collins2c4b69b2006-06-12 18:16:16 -0400347 if (retval == CSR1212_INVALID_ADDR_SPACE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 kfree(as);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 return retval;
350}
351
352int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
Ben Collins2c4b69b2006-06-12 18:16:16 -0400353 struct hpsb_address_ops *ops, u64 start, u64 end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354{
Ben Collins2c4b69b2006-06-12 18:16:16 -0400355 struct hpsb_address_serve *as;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 struct list_head *lh;
Ben Collins2c4b69b2006-06-12 18:16:16 -0400357 int retval = 0;
358 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
Ben Collins2c4b69b2006-06-12 18:16:16 -0400360 if (((start|end) & 3) || (start >= end) ||
361 (end > CSR1212_ALL_SPACE_END)) {
362 HPSB_ERR("%s called with invalid addresses", __FUNCTION__);
363 return 0;
364 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
Stefan Richter85511582005-11-07 06:31:45 -0500366 as = kmalloc(sizeof(*as), GFP_ATOMIC);
367 if (!as)
368 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369
Ben Collins2c4b69b2006-06-12 18:16:16 -0400370 INIT_LIST_HEAD(&as->host_list);
371 INIT_LIST_HEAD(&as->hl_list);
372 as->op = ops;
373 as->start = start;
374 as->end = end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 as->host = host;
376
377 write_lock_irqsave(&addr_space_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 list_for_each(lh, &host->addr_space) {
379 struct hpsb_address_serve *as_this =
380 list_entry(lh, struct hpsb_address_serve, host_list);
381 struct hpsb_address_serve *as_next =
Ben Collins2c4b69b2006-06-12 18:16:16 -0400382 list_entry(lh->next, struct hpsb_address_serve,
383 host_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
385 if (as_this->end > as->start)
386 break;
387
388 if (as_next->start >= as->end) {
389 list_add(&as->host_list, lh);
390 list_add_tail(&as->hl_list, &hl->addr_list);
391 retval = 1;
392 break;
393 }
394 }
395 write_unlock_irqrestore(&addr_space_lock, flags);
396
397 if (retval == 0)
398 kfree(as);
Ben Collins2c4b69b2006-06-12 18:16:16 -0400399 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400}
401
402int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
Ben Collins2c4b69b2006-06-12 18:16:16 -0400403 u64 start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404{
Ben Collins2c4b69b2006-06-12 18:16:16 -0400405 int retval = 0;
406 struct hpsb_address_serve *as;
407 struct list_head *lh, *next;
408 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409
Ben Collins2c4b69b2006-06-12 18:16:16 -0400410 write_lock_irqsave(&addr_space_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 list_for_each_safe (lh, next, &hl->addr_list) {
Ben Collins2c4b69b2006-06-12 18:16:16 -0400412 as = list_entry(lh, struct hpsb_address_serve, hl_list);
413 if (as->start == start && as->host == host) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 __delete_addr(as);
Ben Collins2c4b69b2006-06-12 18:16:16 -0400415 retval = 1;
416 break;
417 }
418 }
419 write_unlock_irqrestore(&addr_space_lock, flags);
420 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421}
422
423int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
Ben Collins2c4b69b2006-06-12 18:16:16 -0400424 unsigned int channel)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425{
Ben Collins2c4b69b2006-06-12 18:16:16 -0400426 if (channel > 63) {
427 HPSB_ERR("%s called with invalid channel", __FUNCTION__);
428 return -EINVAL;
429 }
430 if (host->iso_listen_count[channel]++ == 0)
431 return host->driver->devctl(host, ISO_LISTEN_CHANNEL, channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 return 0;
433}
434
435void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
Ben Collins2c4b69b2006-06-12 18:16:16 -0400436 unsigned int channel)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437{
Ben Collins2c4b69b2006-06-12 18:16:16 -0400438 if (channel > 63) {
439 HPSB_ERR("%s called with invalid channel", __FUNCTION__);
440 return;
441 }
442 if (--host->iso_listen_count[channel] == 0)
443 host->driver->devctl(host, ISO_UNLISTEN_CHANNEL, channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444}
445
446static void init_hpsb_highlevel(struct hpsb_host *host)
447{
448 INIT_LIST_HEAD(&dummy_zero_addr.host_list);
449 INIT_LIST_HEAD(&dummy_zero_addr.hl_list);
450 INIT_LIST_HEAD(&dummy_max_addr.host_list);
451 INIT_LIST_HEAD(&dummy_max_addr.hl_list);
452
453 dummy_zero_addr.op = dummy_max_addr.op = &dummy_ops;
454
455 dummy_zero_addr.start = dummy_zero_addr.end = 0;
456 dummy_max_addr.start = dummy_max_addr.end = ((u64) 1) << 48;
457
458 list_add_tail(&dummy_zero_addr.host_list, &host->addr_space);
459 list_add_tail(&dummy_max_addr.host_list, &host->addr_space);
460}
461
462void highlevel_add_host(struct hpsb_host *host)
463{
Ben Collins2c4b69b2006-06-12 18:16:16 -0400464 struct hpsb_highlevel *hl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465
466 init_hpsb_highlevel(host);
467
468 down_read(&hl_drivers_sem);
Ben Collins2c4b69b2006-06-12 18:16:16 -0400469 list_for_each_entry(hl, &hl_drivers, hl_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 if (hl->add_host)
471 hl->add_host(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 }
Ben Collins2c4b69b2006-06-12 18:16:16 -0400473 up_read(&hl_drivers_sem);
474 if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0)
475 HPSB_ERR("Failed to generate Configuration ROM image for host "
476 "%s-%d", hl->name, host->id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477}
478
479void highlevel_remove_host(struct hpsb_host *host)
480{
Ben Collins2c4b69b2006-06-12 18:16:16 -0400481 struct hpsb_highlevel *hl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
483 down_read(&hl_drivers_sem);
484 list_for_each_entry(hl, &hl_drivers, hl_list)
485 __unregister_host(hl, host, 0);
486 up_read(&hl_drivers_sem);
487}
488
489void highlevel_host_reset(struct hpsb_host *host)
490{
Ben Collins44515192006-06-12 18:16:01 -0400491 unsigned long flags;
Ben Collins2c4b69b2006-06-12 18:16:16 -0400492 struct hpsb_highlevel *hl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
Ben Collins44515192006-06-12 18:16:01 -0400494 read_lock_irqsave(&hl_irqs_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 list_for_each_entry(hl, &hl_irqs, irq_list) {
Ben Collins2c4b69b2006-06-12 18:16:16 -0400496 if (hl->host_reset)
497 hl->host_reset(host);
498 }
Ben Collins44515192006-06-12 18:16:01 -0400499 read_unlock_irqrestore(&hl_irqs_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500}
501
502void highlevel_iso_receive(struct hpsb_host *host, void *data, size_t length)
503{
Ben Collins44515192006-06-12 18:16:01 -0400504 unsigned long flags;
Ben Collins2c4b69b2006-06-12 18:16:16 -0400505 struct hpsb_highlevel *hl;
506 int channel = (((quadlet_t *)data)[0] >> 8) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
Ben Collins2c4b69b2006-06-12 18:16:16 -0400508 read_lock_irqsave(&hl_irqs_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 list_for_each_entry(hl, &hl_irqs, irq_list) {
Ben Collins2c4b69b2006-06-12 18:16:16 -0400510 if (hl->iso_receive)
511 hl->iso_receive(host, channel, data, length);
512 }
513 read_unlock_irqrestore(&hl_irqs_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514}
515
516void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
517 void *data, size_t length)
518{
Ben Collins44515192006-06-12 18:16:01 -0400519 unsigned long flags;
Ben Collins2c4b69b2006-06-12 18:16:16 -0400520 struct hpsb_highlevel *hl;
521 int cts = ((quadlet_t *)data)[0] >> 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522
Ben Collins2c4b69b2006-06-12 18:16:16 -0400523 read_lock_irqsave(&hl_irqs_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 list_for_each_entry(hl, &hl_irqs, irq_list) {
Ben Collins2c4b69b2006-06-12 18:16:16 -0400525 if (hl->fcp_request)
526 hl->fcp_request(host, nodeid, direction, cts, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 length);
Ben Collins2c4b69b2006-06-12 18:16:16 -0400528 }
529 read_unlock_irqrestore(&hl_irqs_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530}
531
Ben Collins2c4b69b2006-06-12 18:16:16 -0400532int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr,
533 unsigned int length, u16 flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534{
Ben Collins2c4b69b2006-06-12 18:16:16 -0400535 struct hpsb_address_serve *as;
536 unsigned int partlength;
537 int rcode = RCODE_ADDRESS_ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
Ben Collins2c4b69b2006-06-12 18:16:16 -0400539 read_lock(&addr_space_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 list_for_each_entry(as, &host->addr_space, host_list) {
541 if (as->start > addr)
542 break;
543
Ben Collins2c4b69b2006-06-12 18:16:16 -0400544 if (as->end > addr) {
545 partlength = min(as->end - addr, (u64) length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
Ben Collins2c4b69b2006-06-12 18:16:16 -0400547 if (as->op->read)
548 rcode = as->op->read(host, nodeid, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 addr, partlength, flags);
Ben Collins2c4b69b2006-06-12 18:16:16 -0400550 else
551 rcode = RCODE_TYPE_ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
553 data += partlength;
Ben Collins2c4b69b2006-06-12 18:16:16 -0400554 length -= partlength;
555 addr += partlength;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
Ben Collins2c4b69b2006-06-12 18:16:16 -0400557 if ((rcode != RCODE_COMPLETE) || !length)
558 break;
559 }
560 }
561 read_unlock(&addr_space_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562
Ben Collins2c4b69b2006-06-12 18:16:16 -0400563 if (length && (rcode == RCODE_COMPLETE))
564 rcode = RCODE_ADDRESS_ERROR;
565 return rcode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566}
567
Ben Collins2c4b69b2006-06-12 18:16:16 -0400568int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data,
569 u64 addr, unsigned int length, u16 flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570{
Ben Collins2c4b69b2006-06-12 18:16:16 -0400571 struct hpsb_address_serve *as;
572 unsigned int partlength;
573 int rcode = RCODE_ADDRESS_ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
Ben Collins2c4b69b2006-06-12 18:16:16 -0400575 read_lock(&addr_space_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 list_for_each_entry(as, &host->addr_space, host_list) {
577 if (as->start > addr)
578 break;
579
Ben Collins2c4b69b2006-06-12 18:16:16 -0400580 if (as->end > addr) {
581 partlength = min(as->end - addr, (u64) length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582
Ben Collins2c4b69b2006-06-12 18:16:16 -0400583 if (as->op->write)
584 rcode = as->op->write(host, nodeid, destid,
585 data, addr, partlength,
586 flags);
587 else
588 rcode = RCODE_TYPE_ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
590 data += partlength;
Ben Collins2c4b69b2006-06-12 18:16:16 -0400591 length -= partlength;
592 addr += partlength;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
Ben Collins2c4b69b2006-06-12 18:16:16 -0400594 if ((rcode != RCODE_COMPLETE) || !length)
595 break;
596 }
597 }
598 read_unlock(&addr_space_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599
Ben Collins2c4b69b2006-06-12 18:16:16 -0400600 if (length && (rcode == RCODE_COMPLETE))
601 rcode = RCODE_ADDRESS_ERROR;
602 return rcode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603}
604
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
Ben Collins2c4b69b2006-06-12 18:16:16 -0400606 u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
607 u16 flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608{
Ben Collins2c4b69b2006-06-12 18:16:16 -0400609 struct hpsb_address_serve *as;
610 int rcode = RCODE_ADDRESS_ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
Ben Collins2c4b69b2006-06-12 18:16:16 -0400612 read_lock(&addr_space_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 list_for_each_entry(as, &host->addr_space, host_list) {
614 if (as->start > addr)
615 break;
616
Ben Collins2c4b69b2006-06-12 18:16:16 -0400617 if (as->end > addr) {
618 if (as->op->lock)
619 rcode = as->op->lock(host, nodeid, store, addr,
620 data, arg, ext_tcode,
621 flags);
622 else
623 rcode = RCODE_TYPE_ERROR;
624 break;
625 }
626 }
627 read_unlock(&addr_space_lock);
628 return rcode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629}
630
631int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
Ben Collins2c4b69b2006-06-12 18:16:16 -0400632 u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
633 u16 flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634{
Ben Collins2c4b69b2006-06-12 18:16:16 -0400635 struct hpsb_address_serve *as;
636 int rcode = RCODE_ADDRESS_ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
Ben Collins2c4b69b2006-06-12 18:16:16 -0400638 read_lock(&addr_space_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
640 list_for_each_entry(as, &host->addr_space, host_list) {
641 if (as->start > addr)
642 break;
643
Ben Collins2c4b69b2006-06-12 18:16:16 -0400644 if (as->end > addr) {
645 if (as->op->lock64)
646 rcode = as->op->lock64(host, nodeid, store,
647 addr, data, arg,
648 ext_tcode, flags);
649 else
650 rcode = RCODE_TYPE_ERROR;
651 break;
652 }
653 }
654 read_unlock(&addr_space_lock);
655 return rcode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656}