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