setup enviroment for compilation
[linux-2.4.21-pre4.git] / drivers / ieee1394 / cmp.c
1 /* -*- c-basic-offset: 8 -*-
2  *
3  * cmp.c - Connection Management Procedures
4  * Copyright (C) 2001 Kristian Høgsberg
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 /* TODO
22  * ----
23  *
24  * - Implement IEC61883-1 output plugs and connection management.
25  *   This should probably be part of the general subsystem, as it could
26  *   be shared with dv1394.
27  *
28  * - Add IEC61883 unit directory when loading this module.  This
29  *   requires a run-time changeable config rom.
30  */
31
32 #include <linux/module.h>
33 #include <linux/list.h>
34 #include <linux/sched.h>
35 #include <linux/types.h>
36 #include <linux/wait.h>
37
38 #include "hosts.h"
39 #include "highlevel.h"
40 #include "ieee1394.h"
41 #include "ieee1394_core.h"
42 #include "cmp.h"
43
44 struct plug {
45         union {
46                 struct cmp_pcr pcr;
47                 quadlet_t quadlet;
48         } u;
49         void (*update)(struct cmp_pcr *plug, void *data);
50         void *data;
51 };
52
53 struct cmp_host {
54         struct hpsb_host *host;
55
56         union {
57                 struct cmp_mpr ompr;
58                 quadlet_t ompr_quadlet;
59         } u;
60         struct plug opcr[2];
61
62         union {
63                 struct cmp_mpr impr;
64                 quadlet_t impr_quadlet;
65         } v;
66         struct plug ipcr[2];
67
68         struct list_head link;
69 };
70
71 enum {
72         CMP_P2P_CONNECTION,
73         CMP_BC_CONNECTION
74 };
75
76 #define CSR_PCR_MAP      0x900
77 #define CSR_PCR_MAP_END  0x9fc
78
79 static struct hpsb_highlevel *cmp_highlevel;
80
81 static LIST_HEAD(host_list);
82 static spinlock_t host_list_lock = SPIN_LOCK_UNLOCKED;
83
84 static struct cmp_host *
85 lookup_cmp_host(struct hpsb_host *host)
86 {
87         struct cmp_host *ch;
88         struct list_head *lh;
89         unsigned long flags;
90
91         ch = NULL;
92         spin_lock_irqsave(&host_list_lock, flags);
93         list_for_each(lh, &host_list) {
94                 ch = list_entry(lh, struct cmp_host, link);
95                 if (ch->host == host)
96                         break;
97         }
98         spin_unlock_irqrestore(&host_list_lock, flags);
99
100         if (lh == &host_list)
101                 return NULL;
102         else
103                 return ch;
104 }
105
106 struct cmp_pcr *
107 cmp_register_opcr(struct hpsb_host *host, int opcr_number, int payload,
108                   void (*update)(struct cmp_pcr *pcr, void *data),
109                   void *data)
110 {
111         struct cmp_host *ch;
112         struct plug *plug;
113
114         ch = lookup_cmp_host(host);
115
116         if (opcr_number >= ch->u.ompr.nplugs ||
117             ch->opcr[opcr_number].update != NULL)
118                 return NULL;
119
120         plug = &ch->opcr[opcr_number];
121         plug->u.pcr.online = 1;
122         plug->u.pcr.bcast_count = 0;
123         plug->u.pcr.p2p_count = 0;
124         plug->u.pcr.overhead = 0;
125         plug->u.pcr.payload = payload;
126         plug->update = update;
127         plug->data = data;
128
129         return &plug->u.pcr;
130 }
131
132 void cmp_unregister_opcr(struct hpsb_host *host, struct cmp_pcr *opcr)
133 {
134         struct cmp_host *ch;
135         struct plug *plug;
136
137         ch = lookup_cmp_host(host);
138         plug = (struct plug *)opcr;
139         if (plug - ch->opcr >= ch->u.ompr.nplugs) BUG();
140
141         plug->u.pcr.online = 0;
142         plug->update = NULL;
143 }
144
145 static void reset_plugs(struct cmp_host *ch)
146 {
147         int i;
148
149         ch->u.ompr.non_persistent_ext = 0xff;
150         for (i = 0; i < ch->u.ompr.nplugs; i++) {
151                 ch->opcr[i].u.pcr.bcast_count = 0;
152                 ch->opcr[i].u.pcr.p2p_count = 0;
153                 ch->opcr[i].u.pcr.overhead = 0;
154         }
155 }
156
157 static void cmp_add_host(struct hpsb_host *host)
158 {
159         struct cmp_host *ch;
160
161         ch = kmalloc(sizeof *ch, SLAB_KERNEL);
162         if (ch == NULL) {
163                 HPSB_ERR("Failed to allocate cmp_host");
164                 return;
165         }
166         memset(ch, 0, sizeof *ch);
167         ch->host = host;
168         ch->u.ompr.rate = SPEED_100;
169         ch->u.ompr.bcast_channel_base = 63;
170         ch->u.ompr.nplugs = 2;
171         reset_plugs(ch);
172
173         spin_lock_irq(&host_list_lock);
174         list_add_tail(&ch->link, &host_list);
175         spin_unlock_irq(&host_list_lock);
176 }
177
178 static void cmp_host_reset(struct hpsb_host *host)
179 {
180         struct cmp_host *ch;
181
182         ch = lookup_cmp_host(host);
183         if (ch == NULL) BUG();
184         reset_plugs(ch);
185 }
186
187 static void cmp_remove_host(struct hpsb_host *host)
188 {
189         struct cmp_host *ch;
190
191         ch = lookup_cmp_host(host);
192         if (ch == NULL) BUG();
193
194         spin_lock_irq(&host_list_lock);
195         list_del(&ch->link);
196         spin_unlock_irq(&host_list_lock);
197
198         kfree(ch);
199 }
200
201 static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf,
202                     u64 addr, unsigned int length, u16 flags)
203 {
204         int csraddr = addr - CSR_REGISTER_BASE;
205         int plug;
206         struct cmp_host *ch;
207         
208         if (length != 4)
209                 return RCODE_TYPE_ERROR;
210
211         ch = lookup_cmp_host(host);
212         if (csraddr == 0x900) {
213                 *buf = cpu_to_be32(ch->u.ompr_quadlet);
214                 return RCODE_COMPLETE;   
215         }
216         else if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
217                 plug = (csraddr - 0x904) / 4;
218                 *buf = cpu_to_be32(ch->opcr[plug].u.quadlet);
219                 return RCODE_COMPLETE;
220         }
221         else if (csraddr < 0x980) {
222                 return RCODE_ADDRESS_ERROR;
223         }
224         else if (csraddr == 0x980) {
225                 *buf = cpu_to_be32(ch->v.impr_quadlet);
226                 return RCODE_COMPLETE;   
227         }
228         else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) {
229                 plug = (csraddr - 0x984) / 4;
230                 *buf = cpu_to_be32(ch->ipcr[plug].u.quadlet);
231                 return RCODE_COMPLETE;
232         }
233         else
234                 return RCODE_ADDRESS_ERROR;
235 }
236
237 static int pcr_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
238                     u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 flags)
239 {
240         int csraddr = addr - CSR_REGISTER_BASE;
241         int plug;
242         struct cmp_host *ch;
243
244         ch = lookup_cmp_host(host);
245         
246         if (extcode != EXTCODE_COMPARE_SWAP) 
247                 return RCODE_TYPE_ERROR;
248         
249         if (csraddr == 0x900) {
250                 /* FIXME: Ignore writes to bits 30-31 and 0-7 */
251                 *store = cpu_to_be32(ch->u.ompr_quadlet);
252                 if (arg == cpu_to_be32(ch->u.ompr_quadlet))
253                         ch->u.ompr_quadlet = be32_to_cpu(data);
254
255                 return RCODE_COMPLETE;
256         }
257         if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
258                 plug = (csraddr - 0x904) / 4;
259                 *store = cpu_to_be32(ch->opcr[plug].u.quadlet);
260
261                 if (arg == *store)
262                         ch->opcr[plug].u.quadlet = be32_to_cpu(data);
263
264                 if (be32_to_cpu(*store) != ch->opcr[plug].u.quadlet &&
265                     ch->opcr[plug].update != NULL)
266                         ch->opcr[plug].update(&ch->opcr[plug].u.pcr,
267                                               ch->opcr[plug].data);
268
269                 return RCODE_COMPLETE;
270         }
271         else if (csraddr < 0x980) {
272                 return RCODE_ADDRESS_ERROR;
273         }
274         else if (csraddr == 0x980) {
275                 /* FIXME: Ignore writes to bits 24-31 and 0-7 */
276                 *store = cpu_to_be32(ch->u.ompr_quadlet);
277                 if (arg == cpu_to_be32(ch->u.ompr_quadlet))
278                         ch->u.ompr_quadlet = be32_to_cpu(data);
279
280                 return RCODE_COMPLETE;
281         }
282         else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) {
283                 plug = (csraddr - 0x984) / 4;
284                 *store = cpu_to_be32(ch->ipcr[plug].u.quadlet);
285
286                 if (arg == *store)
287                         ch->ipcr[plug].u.quadlet = be32_to_cpu(data);
288
289                 if (be32_to_cpu(*store) != ch->ipcr[plug].u.quadlet &&
290                     ch->ipcr[plug].update != NULL)
291                         ch->ipcr[plug].update(&ch->ipcr[plug].u.pcr,
292                                               ch->ipcr[plug].data);
293
294                 return RCODE_COMPLETE;
295         }
296         else
297                 return RCODE_ADDRESS_ERROR;
298 }
299
300
301 static struct hpsb_highlevel_ops cmp_highlevel_ops = {
302         .add_host =     cmp_add_host,
303         .remove_host =  cmp_remove_host,
304         .host_reset =   cmp_host_reset,
305 };
306
307 static struct hpsb_address_ops pcr_ops = {
308         .read = pcr_read,
309         .lock = pcr_lock,
310 };
311
312 /* Module interface */
313
314 MODULE_AUTHOR("Kristian Hogsberg <hogsberg@users.sf.net>");
315 MODULE_DESCRIPTION("Connection Management Procedures (CMP)");
316 MODULE_SUPPORTED_DEVICE("cmp");
317 MODULE_LICENSE("GPL");
318
319 EXPORT_SYMBOL(cmp_register_opcr);
320 EXPORT_SYMBOL(cmp_unregister_opcr);
321
322 static int __init cmp_init_module (void)
323 {
324         cmp_highlevel = hpsb_register_highlevel ("cmp",
325                                                  &cmp_highlevel_ops);
326         if (cmp_highlevel == NULL) {
327                 HPSB_ERR("cmp: unable to register highlevel ops");
328                 return -EIO;
329         }
330
331         hpsb_register_addrspace(cmp_highlevel, &pcr_ops,
332                                 CSR_REGISTER_BASE + CSR_PCR_MAP,
333                                 CSR_REGISTER_BASE + CSR_PCR_MAP_END);
334
335         HPSB_INFO("Loaded CMP driver");
336
337         return 0;
338 }
339
340 static void __exit cmp_exit_module (void)
341 {
342         hpsb_unregister_highlevel(cmp_highlevel);
343
344         HPSB_INFO("Unloaded CMP driver");
345 }
346
347 module_init(cmp_init_module);
348 module_exit(cmp_exit_module);