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