1 /* -*- c-basic-offset: 8 -*-
3 * cmp.c - Connection Management Procedures
4 * Copyright (C) 2001 Kristian Høgsberg
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.
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.
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.
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.
28 * - Add IEC61883 unit directory when loading this module. This
29 * requires a run-time changeable config rom.
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>
39 #include "highlevel.h"
41 #include "ieee1394_core.h"
49 void (*update)(struct cmp_pcr *plug, void *data);
54 struct hpsb_host *host;
58 quadlet_t ompr_quadlet;
64 quadlet_t impr_quadlet;
68 struct list_head link;
76 #define CSR_PCR_MAP 0x900
77 #define CSR_PCR_MAP_END 0x9fc
79 static struct hpsb_highlevel *cmp_highlevel;
81 static LIST_HEAD(host_list);
82 static spinlock_t host_list_lock = SPIN_LOCK_UNLOCKED;
84 static struct cmp_host *
85 lookup_cmp_host(struct hpsb_host *host)
92 spin_lock_irqsave(&host_list_lock, flags);
93 list_for_each(lh, &host_list) {
94 ch = list_entry(lh, struct cmp_host, link);
98 spin_unlock_irqrestore(&host_list_lock, flags);
100 if (lh == &host_list)
107 cmp_register_opcr(struct hpsb_host *host, int opcr_number, int payload,
108 void (*update)(struct cmp_pcr *pcr, void *data),
114 ch = lookup_cmp_host(host);
116 if (opcr_number >= ch->u.ompr.nplugs ||
117 ch->opcr[opcr_number].update != NULL)
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;
132 void cmp_unregister_opcr(struct hpsb_host *host, struct cmp_pcr *opcr)
137 ch = lookup_cmp_host(host);
138 plug = (struct plug *)opcr;
139 if (plug - ch->opcr >= ch->u.ompr.nplugs) BUG();
141 plug->u.pcr.online = 0;
145 static void reset_plugs(struct cmp_host *ch)
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;
157 static void cmp_add_host(struct hpsb_host *host)
161 ch = kmalloc(sizeof *ch, SLAB_KERNEL);
163 HPSB_ERR("Failed to allocate cmp_host");
166 memset(ch, 0, sizeof *ch);
168 ch->u.ompr.rate = SPEED_100;
169 ch->u.ompr.bcast_channel_base = 63;
170 ch->u.ompr.nplugs = 2;
173 spin_lock_irq(&host_list_lock);
174 list_add_tail(&ch->link, &host_list);
175 spin_unlock_irq(&host_list_lock);
178 static void cmp_host_reset(struct hpsb_host *host)
182 ch = lookup_cmp_host(host);
183 if (ch == NULL) BUG();
187 static void cmp_remove_host(struct hpsb_host *host)
191 ch = lookup_cmp_host(host);
192 if (ch == NULL) BUG();
194 spin_lock_irq(&host_list_lock);
196 spin_unlock_irq(&host_list_lock);
201 static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf,
202 u64 addr, unsigned int length, u16 flags)
204 int csraddr = addr - CSR_REGISTER_BASE;
209 return RCODE_TYPE_ERROR;
211 ch = lookup_cmp_host(host);
212 if (csraddr == 0x900) {
213 *buf = cpu_to_be32(ch->u.ompr_quadlet);
214 return RCODE_COMPLETE;
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;
221 else if (csraddr < 0x980) {
222 return RCODE_ADDRESS_ERROR;
224 else if (csraddr == 0x980) {
225 *buf = cpu_to_be32(ch->v.impr_quadlet);
226 return RCODE_COMPLETE;
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;
234 return RCODE_ADDRESS_ERROR;
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)
240 int csraddr = addr - CSR_REGISTER_BASE;
244 ch = lookup_cmp_host(host);
246 if (extcode != EXTCODE_COMPARE_SWAP)
247 return RCODE_TYPE_ERROR;
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);
255 return RCODE_COMPLETE;
257 if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) {
258 plug = (csraddr - 0x904) / 4;
259 *store = cpu_to_be32(ch->opcr[plug].u.quadlet);
262 ch->opcr[plug].u.quadlet = be32_to_cpu(data);
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);
269 return RCODE_COMPLETE;
271 else if (csraddr < 0x980) {
272 return RCODE_ADDRESS_ERROR;
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);
280 return RCODE_COMPLETE;
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);
287 ch->ipcr[plug].u.quadlet = be32_to_cpu(data);
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);
294 return RCODE_COMPLETE;
297 return RCODE_ADDRESS_ERROR;
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,
307 static struct hpsb_address_ops pcr_ops = {
312 /* Module interface */
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");
319 EXPORT_SYMBOL(cmp_register_opcr);
320 EXPORT_SYMBOL(cmp_unregister_opcr);
322 static int __init cmp_init_module (void)
324 cmp_highlevel = hpsb_register_highlevel ("cmp",
326 if (cmp_highlevel == NULL) {
327 HPSB_ERR("cmp: unable to register highlevel ops");
331 hpsb_register_addrspace(cmp_highlevel, &pcr_ops,
332 CSR_REGISTER_BASE + CSR_PCR_MAP,
333 CSR_REGISTER_BASE + CSR_PCR_MAP_END);
335 HPSB_INFO("Loaded CMP driver");
340 static void __exit cmp_exit_module (void)
342 hpsb_unregister_highlevel(cmp_highlevel);
344 HPSB_INFO("Unloaded CMP driver");
347 module_init(cmp_init_module);
348 module_exit(cmp_exit_module);