Linux-2.6.12-rc2
[linux-flexiantxendom0-natty.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 #include <linux/interrupt.h>
38
39 #include "hosts.h"
40 #include "highlevel.h"
41 #include "ieee1394.h"
42 #include "ieee1394_core.h"
43 #include "cmp.h"
44
45 struct plug {
46         union {
47                 struct cmp_pcr pcr;
48                 quadlet_t quadlet;
49         } u;
50         void (*update)(struct cmp_pcr *plug, void *data);
51         void *data;
52 };
53
54 struct cmp_host {
55         struct hpsb_host *host;
56
57         union {
58                 struct cmp_mpr ompr;
59                 quadlet_t ompr_quadlet;
60         } u;
61         struct plug opcr[2];
62
63         union {
64                 struct cmp_mpr impr;
65                 quadlet_t impr_quadlet;
66         } v;
67         struct plug ipcr[2];
68 };
69
70 enum {
71         CMP_P2P_CONNECTION,
72         CMP_BC_CONNECTION
73 };
74
75 #define CSR_PCR_MAP      0x900
76 #define CSR_PCR_MAP_END  0x9fc
77
78 static struct hpsb_highlevel cmp_highlevel;
79
80 static void cmp_add_host(struct hpsb_host *host);
81 static void cmp_host_reset(struct hpsb_host *host);
82 static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf,
83                     u64 addr, size_t length, u16 flags);
84 static int pcr_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
85                     u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 flags);
86
87 static struct hpsb_highlevel cmp_highlevel = {
88         .name =         "cmp",
89         .add_host =     cmp_add_host,
90         .host_reset =   cmp_host_reset,
91 };
92
93 static struct hpsb_address_ops pcr_ops = {
94         .read = pcr_read,
95         .lock = pcr_lock,
96 };
97
98
99 struct cmp_pcr *
100 cmp_register_opcr(struct hpsb_host *host, int opcr_number, int payload,
101                   void (*update)(struct cmp_pcr *pcr, void *data),
102                   void *data)
103 {
104         struct cmp_host *ch;
105         struct plug *plug;
106
107         ch = hpsb_get_hostinfo(&cmp_highlevel, host);
108
109         if (opcr_number >= ch->u.ompr.nplugs ||
110             ch->opcr[opcr_number].update != NULL)
111                 return NULL;
112
113         plug = &ch->opcr[opcr_number];
114         plug->u.pcr.online = 1;
115         plug->u.pcr.bcast_count = 0;
116         plug->u.pcr.p2p_count = 0;
117         plug->u.pcr.overhead = 0;
118         plug->u.pcr.payload = payload;
119         plug->update = update;
120         plug->data = data;
121
122         return &plug->u.pcr;
123 }
124
125 void cmp_unregister_opcr(struct hpsb_host *host, struct cmp_pcr *opcr)
126 {
127         struct cmp_host *ch;
128         struct plug *plug;
129
130         ch = hpsb_get_hostinfo(&cmp_highlevel, host);
131         plug = (struct plug *)opcr;
132         if (plug - ch->opcr >= ch->u.ompr.nplugs) BUG();
133
134         plug->u.pcr.online = 0;
135         plug->update = NULL;
136 }
137
138 static void reset_plugs(struct cmp_host *ch)
139 {
140         int i;
141
142         ch->u.ompr.non_persistent_ext = 0xff;
143         for (i = 0; i < ch->u.ompr.nplugs; i++) {
144                 ch->opcr[i].u.pcr.bcast_count = 0;
145                 ch->opcr[i].u.pcr.p2p_count = 0;
146                 ch->opcr[i].u.pcr.overhead = 0;
147         }
148 }
149
150 static void cmp_add_host(struct hpsb_host *host)
151 {
152         struct cmp_host *ch = hpsb_create_hostinfo(&cmp_highlevel, host, sizeof (*ch));
153
154         if (ch == NULL) {
155                 HPSB_ERR("Failed to allocate cmp_host");
156                 return;
157         }
158
159         hpsb_register_addrspace(&cmp_highlevel, host, &pcr_ops,
160                                 CSR_REGISTER_BASE + CSR_PCR_MAP,
161                                 CSR_REGISTER_BASE + CSR_PCR_MAP_END);
162
163         ch->host = host;
164         ch->u.ompr.rate = IEEE1394_SPEED_100;
165         ch->u.ompr.bcast_channel_base = 63;
166         ch->u.ompr.nplugs = 2;
167
168         reset_plugs(ch);
169 }
170
171 static void cmp_host_reset(struct hpsb_host *host)
172 {
173         struct cmp_host *ch;
174
175         ch = hpsb_get_hostinfo(&cmp_highlevel, host);
176         if (ch == NULL) {
177                 HPSB_ERR("cmp: Tried to reset unknown host");
178                 return;
179         }
180
181         reset_plugs(ch);
182 }
183
184 static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf,
185                     u64 addr, size_t length, u16 flags)
186 {
187         int csraddr = addr - CSR_REGISTER_BASE;
188         int plug;
189         struct cmp_host *ch;
190
191         if (length != 4)
192                 return RCODE_TYPE_ERROR;
193
194         ch = hpsb_get_hostinfo(&cmp_highlevel, host);
195         if (csraddr == 0x900) {
196                 *buf = cpu_to_be32(ch->u.ompr_quadlet);
197                 return RCODE_COMPLETE;
198         }
199         else if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
200                 plug = (csraddr - 0x904) / 4;
201                 *buf = cpu_to_be32(ch->opcr[plug].u.quadlet);
202                 return RCODE_COMPLETE;
203         }
204         else if (csraddr < 0x980) {
205                 return RCODE_ADDRESS_ERROR;
206         }
207         else if (csraddr == 0x980) {
208                 *buf = cpu_to_be32(ch->v.impr_quadlet);
209                 return RCODE_COMPLETE;
210         }
211         else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) {
212                 plug = (csraddr - 0x984) / 4;
213                 *buf = cpu_to_be32(ch->ipcr[plug].u.quadlet);
214                 return RCODE_COMPLETE;
215         }
216         else
217                 return RCODE_ADDRESS_ERROR;
218 }
219
220 static int pcr_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
221                     u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 flags)
222 {
223         int csraddr = addr - CSR_REGISTER_BASE;
224         int plug;
225         struct cmp_host *ch;
226
227         ch = hpsb_get_hostinfo(&cmp_highlevel, host);
228
229         if (extcode != EXTCODE_COMPARE_SWAP)
230                 return RCODE_TYPE_ERROR;
231
232         if (csraddr == 0x900) {
233                 /* FIXME: Ignore writes to bits 30-31 and 0-7 */
234                 *store = cpu_to_be32(ch->u.ompr_quadlet);
235                 if (arg == cpu_to_be32(ch->u.ompr_quadlet))
236                         ch->u.ompr_quadlet = be32_to_cpu(data);
237
238                 return RCODE_COMPLETE;
239         }
240         if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
241                 plug = (csraddr - 0x904) / 4;
242                 *store = cpu_to_be32(ch->opcr[plug].u.quadlet);
243
244                 if (arg == *store)
245                         ch->opcr[plug].u.quadlet = be32_to_cpu(data);
246
247                 if (be32_to_cpu(*store) != ch->opcr[plug].u.quadlet &&
248                     ch->opcr[plug].update != NULL)
249                         ch->opcr[plug].update(&ch->opcr[plug].u.pcr,
250                                               ch->opcr[plug].data);
251
252                 return RCODE_COMPLETE;
253         }
254         else if (csraddr < 0x980) {
255                 return RCODE_ADDRESS_ERROR;
256         }
257         else if (csraddr == 0x980) {
258                 /* FIXME: Ignore writes to bits 24-31 and 0-7 */
259                 *store = cpu_to_be32(ch->u.ompr_quadlet);
260                 if (arg == cpu_to_be32(ch->u.ompr_quadlet))
261                         ch->u.ompr_quadlet = be32_to_cpu(data);
262
263                 return RCODE_COMPLETE;
264         }
265         else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) {
266                 plug = (csraddr - 0x984) / 4;
267                 *store = cpu_to_be32(ch->ipcr[plug].u.quadlet);
268
269                 if (arg == *store)
270                         ch->ipcr[plug].u.quadlet = be32_to_cpu(data);
271
272                 if (be32_to_cpu(*store) != ch->ipcr[plug].u.quadlet &&
273                     ch->ipcr[plug].update != NULL)
274                         ch->ipcr[plug].update(&ch->ipcr[plug].u.pcr,
275                                               ch->ipcr[plug].data);
276
277                 return RCODE_COMPLETE;
278         }
279         else
280                 return RCODE_ADDRESS_ERROR;
281 }
282
283
284 /* Module interface */
285
286 MODULE_AUTHOR("Kristian Hogsberg <hogsberg@users.sf.net>");
287 MODULE_DESCRIPTION("Connection Management Procedures (CMP)");
288 MODULE_SUPPORTED_DEVICE("cmp");
289 MODULE_LICENSE("GPL");
290
291 EXPORT_SYMBOL(cmp_register_opcr);
292 EXPORT_SYMBOL(cmp_unregister_opcr);
293
294 static int __init cmp_init_module (void)
295 {
296         hpsb_register_highlevel (&cmp_highlevel);
297
298         HPSB_INFO("Loaded CMP driver");
299
300         return 0;
301 }
302
303 static void __exit cmp_exit_module (void)
304 {
305         hpsb_unregister_highlevel(&cmp_highlevel);
306
307         HPSB_INFO("Unloaded CMP driver");
308 }
309
310 module_init(cmp_init_module);
311 module_exit(cmp_exit_module);