1 /* $Id: bus-device.c,v 1.10 2009/08/28 01:23:44 fredette Exp $ */
2
3 /* generic/bus-device.c - implementation of a generic bus device support: */
4
5 /*
6 * Copyright (c) 2003 Matt Fredette
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Matt Fredette.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <tme/common.h>
37 _TME_RCSID("$Id: bus-device.c,v 1.10 2009/08/28 01:23:44 fredette Exp $");
38
39 /* includes: */
40 #include <tme/generic/bus-device.h>
41
42 /* include the automatically-generated bus-device functions: */
43 #include "bus-device-auto.c"
44
45 /* this adds a generic bus TLB set: */
46 int
tme_bus_device_tlb_set_add(struct tme_bus_device * bus_device,unsigned long tlb_count,struct tme_bus_tlb * tlb)47 tme_bus_device_tlb_set_add(struct tme_bus_device *bus_device,
48 unsigned long tlb_count,
49 struct tme_bus_tlb *tlb)
50 {
51 struct tme_bus_tlb_set_info tlb_set_info;
52 struct tme_token *token;
53 struct tme_bus_connection *conn_bus;
54 int rc;
55
56 /* make the TLB set information, and allocate a token for each TLB
57 entry: */
58 assert (tlb_count > 0);
59 memset(&tlb_set_info, 0, sizeof(tlb_set_info));
60 tlb_set_info.tme_bus_tlb_set_info_token0 = tme_new(struct tme_token, tlb_count);
61 tlb_set_info.tme_bus_tlb_set_info_token_stride = sizeof(struct tme_token);
62 tlb_set_info.tme_bus_tlb_set_info_token_count = tlb_count;
63 tlb_set_info.tme_bus_tlb_set_info_bus_context = NULL;
64
65 /* connect each TLB entry with its token: */
66 token = tlb_set_info.tme_bus_tlb_set_info_token0;
67 do {
68 tme_token_init(token);
69 tlb->tme_bus_tlb_token = token;
70 tlb++;
71 token++;
72 } while (--tlb_count);
73
74 /* get our bus connection: */
75 conn_bus
76 = tme_memory_atomic_pointer_read(struct tme_bus_connection *,
77 bus_device->tme_bus_device_connection,
78 &bus_device->tme_bus_device_connection_rwlock);
79
80 /* add the TLB set: */
81 rc
82 = ((*conn_bus->tme_bus_tlb_set_add)
83 (conn_bus,
84 &tlb_set_info));
85 return (rc);
86 }
87
88 /* this scores a connection: */
89 int
tme_bus_device_connection_score(struct tme_connection * conn,unsigned int * _score)90 tme_bus_device_connection_score(struct tme_connection *conn, unsigned int *_score)
91 {
92 struct tme_bus_device *device;
93 struct tme_bus_connection *conn_bus_other_old;
94 struct tme_bus_connection *conn_bus_other_new;
95
96 /* recover our device: */
97 device = conn->tme_connection_element->tme_element_private;
98
99 /* both sides must be generic bus connections: */
100 assert(conn->tme_connection_type == TME_CONNECTION_BUS_GENERIC);
101 assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_BUS_GENERIC);
102
103 /* a simple bus device can have multiple connections, but they all
104 must be from the same bus. the only way we check that is by
105 comparing elements: */
106 conn_bus_other_old
107 = tme_memory_atomic_pointer_read(struct tme_bus_connection *,
108 device->tme_bus_device_connection,
109 &device->tme_bus_device_connection_rwlock);
110 conn_bus_other_new = (struct tme_bus_connection *) conn->tme_connection_other;
111 if (conn_bus_other_old != NULL
112 && (conn_bus_other_old->tme_bus_connection.tme_connection_element
113 != conn_bus_other_new->tme_bus_connection.tme_connection_element)) {
114 *_score = 0;
115 return (TME_OK);
116 }
117
118 /* otherwise, this is our first connection or another connection to
119 the same element. note that there's no good way to differentiate
120 a connection to a bus from a connection to just another chip, so
121 we always return a nonzero score here: */
122 *_score = 1;
123 return (TME_OK);
124 }
125
126 /* this makes a new connection: */
127 int
tme_bus_device_connection_make(struct tme_connection * conn,unsigned int state)128 tme_bus_device_connection_make(struct tme_connection *conn, unsigned int state)
129 {
130 struct tme_bus_device *device;
131
132 /* recover our device: */
133 device = conn->tme_connection_element->tme_element_private;
134
135 /* both sides must be generic bus connections: */
136 assert(conn->tme_connection_type == TME_CONNECTION_BUS_GENERIC);
137 assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_BUS_GENERIC);
138
139 /* simple bus devices are always set up to answer calls across the
140 connection, so we only have to do work when the connection
141 has gone full, namely taking the other side of the
142 connection: */
143 if (state == TME_CONNECTION_FULL) {
144
145 /* save our connection: */
146 tme_memory_atomic_pointer_write(struct tme_bus_connection *,
147 device->tme_bus_device_connection,
148 (struct tme_bus_connection *) conn->tme_connection_other,
149 &device->tme_bus_device_connection_rwlock);
150 }
151
152 return (TME_OK);
153 }
154
155 /* this breaks a connection: */
156 int
tme_bus_device_connection_break(struct tme_connection * conn,unsigned int state)157 tme_bus_device_connection_break(struct tme_connection *conn, unsigned int state)
158 {
159 abort();
160 }
161
162 static int
_tme_bus_device_signal(struct tme_bus_connection * conn_bus,unsigned int signal)163 _tme_bus_device_signal(struct tme_bus_connection *conn_bus, unsigned int signal)
164 {
165 struct tme_bus_device *device;
166 device = conn_bus->tme_bus_connection.tme_connection_element->tme_element_private;
167 return ((*device->tme_bus_device_signal)(device, signal));
168 }
169
170 static int
_tme_bus_device_intack(struct tme_bus_connection * conn_bus,unsigned int signal,int * vector)171 _tme_bus_device_intack(struct tme_bus_connection *conn_bus, unsigned int signal, int *vector)
172 {
173 struct tme_bus_device *device;
174 device = conn_bus->tme_bus_connection.tme_connection_element->tme_element_private;
175 return ((*device->tme_bus_device_intack)(device, signal, vector));
176 }
177
178 static int
_tme_bus_device_tlb_fill(struct tme_bus_connection * conn_bus,struct tme_bus_tlb * tlb,tme_bus_addr_t address,unsigned int cycles)179 _tme_bus_device_tlb_fill(struct tme_bus_connection *conn_bus, struct tme_bus_tlb *tlb,
180 tme_bus_addr_t address, unsigned int cycles)
181 {
182 struct tme_bus_device *device;
183 device = conn_bus->tme_bus_connection.tme_connection_element->tme_element_private;
184 return ((*device->tme_bus_device_tlb_fill)(device, tlb, address, cycles));
185 }
186
187 /* this makes a new connection side for a simple bus device: */
188 int
tme_bus_device_connections_new(struct tme_element * element,const char * const * args,struct tme_connection ** _conns,char ** _output)189 tme_bus_device_connections_new(struct tme_element *element,
190 const char * const *args,
191 struct tme_connection **_conns,
192 char **_output)
193 {
194 struct tme_bus_device *device;
195 struct tme_bus_connection *conn_bus;
196 struct tme_connection *conn;
197
198 /* recover our device: */
199 device = (struct tme_bus_device *) element->tme_element_private;
200
201 /* create our side of a generic bus connection: */
202 conn_bus = tme_new0(struct tme_bus_connection, 1);
203 conn = &conn_bus->tme_bus_connection;
204
205 /* fill in the generic connection: */
206 conn->tme_connection_next = *_conns;
207 conn->tme_connection_type = TME_CONNECTION_BUS_GENERIC;
208 conn->tme_connection_score = tme_bus_device_connection_score;
209 conn->tme_connection_make = tme_bus_device_connection_make;
210 conn->tme_connection_break = tme_bus_device_connection_break;
211
212 /* fill in the generic bus connection: */
213 conn_bus->tme_bus_subregions = device->tme_bus_device_subregions;
214 if (device->tme_bus_device_signal != NULL) {
215 conn_bus->tme_bus_signal = _tme_bus_device_signal;
216 }
217 if (device->tme_bus_device_intack != NULL) {
218 conn_bus->tme_bus_intack = _tme_bus_device_intack;
219 }
220 conn_bus->tme_bus_tlb_fill = _tme_bus_device_tlb_fill;
221
222 /* return the connection side possibility: */
223 *_conns = conn;
224 return (TME_OK);
225 }
226