blob: 356ab424b0a8511d2ab21a256e8b342408592de8 [file] [log] [blame]
/* $Id: bus.h,v 1.15 2009/08/29 17:35:08 fredette Exp $ */
/* tme/generic/bus.h - header file for generic bus support: */
/*
* Copyright (c) 2002, 2003 Matt Fredette
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Matt Fredette.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _TME_GENERIC_BUS_H
#define _TME_GENERIC_BUS_H
#include <tme/common.h>
_TME_RCSID("$Id: bus.h,v 1.15 2009/08/29 17:35:08 fredette Exp $");
/* includes: */
#include <tme/element.h>
#include <tme/threads.h>
#include <tme/memory.h>
#include <tme/token.h>
/* macros: */
/* the log2 of various bus sizes, named by number of bits but really
in terms of bytes: */
#define TME_BUS8_LOG2 (0)
#define TME_BUS16_LOG2 (1)
#define TME_BUS32_LOG2 (2)
#define TME_BUS64_LOG2 (3)
#define TME_BUS128_LOG2 (4)
/* bus signal flags and form: */
#define TME_BUS_SIGNAL_LEVEL_MASK (0x03)
#define TME_BUS_SIGNAL_LEVEL_LOW (0x00)
#define TME_BUS_SIGNAL_LEVEL_HIGH (0x01)
#define TME_BUS_SIGNAL_LEVEL_NEGATED (0x02)
#define TME_BUS_SIGNAL_LEVEL_ASSERTED (0x03)
#define TME_BUS_SIGNAL_EDGE (0x04)
#define _TME_BUS_SIGNAL_BITS (5)
#define TME_BUS_SIGNAL_WHICH(x) ((x) & ~((1 << _TME_BUS_SIGNAL_BITS) - 1))
#define TME_BUS_SIGNAL_INDEX(x) ((x) >> _TME_BUS_SIGNAL_BITS)
#define TME_BUS_SIGNAL_X(x) ((x) << _TME_BUS_SIGNAL_BITS)
/* all bus signal set identifiers: */
#define TME_BUS_SIGNALS_ID_GENERIC (0)
#define TME_BUS_SIGNALS_ID_I825X6 (1)
/* the generic bus signal set: */
#define TME_BUS_SIGNAL_INT(x) TME_BUS_SIGNAL_X(x)
#define TME_BUS_SIGNAL_IS_INT(x) (TME_BUS_SIGNAL_INDEX(x) < 256)
#define TME_BUS_SIGNAL_INDEX_INT(x) TME_BUS_SIGNAL_INDEX(x)
#define TME_BUS_SIGNAL_INT_UNSPEC TME_BUS_SIGNAL_X(256)
#define TME_BUS_SIGNAL_HALT TME_BUS_SIGNAL_X(257)
#define TME_BUS_SIGNAL_RESET TME_BUS_SIGNAL_X(258)
#define TME_BUS_SIGNAL_IGNORE TME_BUS_SIGNAL_X(259)
#define TME_BUS_SIGNAL_ABORT TME_BUS_SIGNAL_X(260)
#define TME_BUS_SIGNAL_DRQ TME_BUS_SIGNAL_X(261)
#define TME_BUS_SIGNAL_DACK TME_BUS_SIGNAL_X(262)
#define TME_BUS_SIGNAL_BR TME_BUS_SIGNAL_X(263)
#define TME_BUS_SIGNAL_BG TME_BUS_SIGNAL_X(264)
#define TME_BUS_SIGNALS_GENERIC { TME_BUS_SIGNALS_ID_GENERIC, TME_BUS_VERSION, 384, 0 }
/* this gets the index and mask of a bus signal bit in a byte array: */
#define TME_BUS_SIGNAL_BIT_INDEX(x) (TME_BUS_SIGNAL_INDEX(x) >> 3)
#define TME_BUS_SIGNAL_BIT_MASK(x) TME_BIT(TME_BUS_SIGNAL_INDEX(x) & 7)
/* this gets the number of bytes in a byte array of bus signal bits: */
#define TME_BUS_SIGNAL_BIT_BYTES(count) (((count) + 7) >> 3)
/* the undefined interrupt vector: */
#define TME_BUS_INTERRUPT_VECTOR_UNDEF (-1)
/* bus cycles: */
#define TME_BUS_CYCLE_UNDEF (0)
#define TME_BUS_CYCLE_READ TME_BIT(0)
#define TME_BUS_CYCLE_WRITE TME_BIT(1)
#define TME_BUS_CYCLE_LOCK TME_BIT(2)
#define TME_BUS_CYCLE_UNLOCK TME_BIT(3)
/* the maximum number of fault handlers on a TLB entry: */
#define TME_BUS_TLB_FAULT_HANDLERS (4)
/* this returns nonzero if a TLB entry is valid: */
#define tme_bus_tlb_is_valid(tlb) \
(tme_token_is_valid((tlb)->tme_bus_tlb_token))
/* this returns nonzero if a TLB entry is invalid: */
#define tme_bus_tlb_is_invalid(tlb) \
(tme_token_is_invalid((tlb)->tme_bus_tlb_token))
/* this busies a TLB entry: */
#define tme_bus_tlb_busy(tlb) tme_token_busy((tlb)->tme_bus_tlb_token)
/* this unbusies a TLB entry: */
#define tme_bus_tlb_unbusy(tlb) tme_token_unbusy((tlb)->tme_bus_tlb_token)
/* this unbusies a TLB entry for filling: */
/* NB: we must clear the invalid on the TLB's token before we fill it.
we can't do that after we fill it, because we will race with an
immediate invalidation. however, by marking it valid before we fill
it, we race with ourselves - before the TLB entry is filled, we may
run again and see a TLB entry that was actually invalidated, as
valid. to avoid this case, we invalidate the TLB entry in another
way - we poison its first and last addresses to impossible values: */
/* NB: once all element callins have been rearchitected to block until
a callout has completed, this poisoning can go away: */
#define tme_bus_tlb_unbusy_fill(tlb) \
do { \
tme_bus_tlb_unbusy(tlb); \
if (tme_bus_tlb_is_invalid(tlb)) { \
(tlb)->tme_bus_tlb_addr_first = 1; \
(tlb)->tme_bus_tlb_addr_last = 0; \
tme_token_invalid_clear((tlb)->tme_bus_tlb_token); \
} \
} while (/* CONSTCOND */ 0)
/* this adds a fault handler to a TLB entry: */
#define TME_BUS_TLB_FAULT_HANDLER(tlb, func, private) \
do { \
assert(tlb->tme_bus_tlb_fault_handler_count \
< TME_BUS_TLB_FAULT_HANDLERS); \
tlb->tme_bus_tlb_fault_handlers \
[tlb->tme_bus_tlb_fault_handler_count] \
.tme_bus_tlb_fault_handler_private = private; \
tlb->tme_bus_tlb_fault_handlers \
[tlb->tme_bus_tlb_fault_handler_count] \
.tme_bus_tlb_fault_handler = func; \
tlb->tme_bus_tlb_fault_handler_count++; \
} while (/* CONSTCOND */ 0)
/* this indexes a generic bus router array for a device with a port
size of 8 * (2 ^ siz_lg2) bits: */
#define TME_BUS_ROUTER_INDEX(siz_lg2, other_port_siz_lg2, other_port_lane_least) \
((( \
/* by the (overlapping) other port size: */ \
(other_port_siz_lg2) \
\
/* by the (overlapping) other port least lane: */ \
<< (siz_lg2)) \
+ other_port_lane_least) \
\
/* by lane number, which we add later: */ \
<< (siz_lg2))
/* this gives the number of entries that must be in a generic bus
router array for a device with a bus size of 8 * (2 ^ siz_lg2)
bits: */
#define TME_BUS_ROUTER_SIZE(siz_lg2) \
TME_BUS_ROUTER_INDEX(siz_lg2, (siz_lg2) + 1, 0)
/* bus lane routing entries: */
#define TME_BUS_LANE_WARN TME_BIT(7)
#define TME_BUS_LANE_ABORT (0x7f)
#define TME_BUS_LANE_UNDEF (0x7e)
#define TME_BUS_LANE_ROUTE_WRITE_IGNORE TME_BIT(6)
#define TME_BUS_LANE_ROUTE(x) (x)
/* this is a special bus cycle return value. it doesn't indicate a
fault, but instead tells the initiator that some other event has
happened on the bus, synchronous with the bus cycle, that the
initiator should handle: */
#define TME_BUS_CYCLE_SYNCHRONOUS_EVENT (EINTR)
/* internal bus connection types: */
#define TME_BUS_CONNECTION_INT_FLAG_ADDRESSABLE TME_BIT(0)
#define TME_BUS_CONNECTION_INT_FLAG_CONTROLLER TME_BIT(1)
/* types: */
struct tme_bus_tlb;
/* a bus address: */
typedef tme_uint32_t tme_bus_addr32_t;
#ifdef TME_HAVE_INT64_T
typedef tme_uint64_t tme_bus_addr64_t;
#endif /* TME_HAVE_INT64_T */
#if TME_BUSMAX_LOG2 <= TME_BUS32_LOG2
typedef tme_bus_addr32_t tme_bus_addr_t;
#elif TME_BUSMAX_LOG2 == TME_BUS64_LOG2
#ifndef TME_HAVE_INT64_T
#error "64-bit guests require 64-bit integer types"
#else /* TME_HAVE_INT64_T */
typedef tme_bus_addr64_t tme_bus_addr_t;
#endif /* TME_HAVE_INT64_T */
#else /* TME_BUSMAX_LOG2 */
#error "unsupported maximum guest bus size"
#endif /* TME_BUSMAX_LOG2 */
/* a bus byte lane routing entry: */
typedef tme_uint8_t tme_bus_lane_t;
/* a bus context: */
typedef tme_uint32_t tme_bus_context_t;
/* a bus cycle: */
struct tme_bus_cycle {
/* the bus cycle data buffer pointer. this points to the byte
associated with the bus address given below: */
tme_uint8_t *tme_bus_cycle_buffer;
/* how bus byte lanes are connected to the bus cycle data buffer: */
_tme_const tme_bus_lane_t *tme_bus_cycle_lane_routing;
/* the bus address: */
tme_bus_addr_t tme_bus_cycle_address;
/* when adding one to the bus address, add this to the bus cycle
data buffer pointer to get a pointer to the byte associated with
the new bus address: */
tme_int8_t tme_bus_cycle_buffer_increment;
/* the type of bus cycle: */
tme_uint8_t tme_bus_cycle_type;
/* the maximum number of addresses that could be covered by this
cycle. depending on where and how wide the initiator and
responder ports overlap, the number of addresses actually covered
may be less: */
tme_uint8_t tme_bus_cycle_size;
/* the starting lane and size of this device's port. bits 0-2 are
the log2 of the lane size of the port. zero corresponds to a
one-lane (8-bit) port, one to a two-lane (16-bit) port, etc.
bits 3-7 are the least byte lane in this device's port. zero
corresponds to D7-D0, one to D15-D8, etc.: */
tme_uint8_t tme_bus_cycle_port;
#define TME_BUS_CYCLE_PORT(lane_least, lane_size_lg2) \
(((lane_least) << 3) | (lane_size_lg2))
#define TME_BUS_CYCLE_PORT_SIZE_LG2(port) \
TME_FIELD_EXTRACTU(port, 0, 3)
#define TME_BUS_CYCLE_PORT_LANE_LEAST(port) \
TME_FIELD_EXTRACTU(port, 3, 5)
};
/* a bus cycle handler: */
typedef int (*tme_bus_cycle_handler) _TME_P((void *, struct tme_bus_cycle *));
/* a bus fault handler: */
typedef int (*tme_bus_fault_handler) _TME_P((void *, struct tme_bus_tlb *, struct tme_bus_cycle *, int));
/* a bus cacheable: */
struct tme_bus_cacheable {
/* the cacheable contents and size: */
tme_shared tme_uint8_t *tme_bus_cacheable_contents;
unsigned long tme_bus_cacheable_size;
/* the cacheable private state: */
void *tme_bus_cacheable_private;
/* this function allocates a new valids bitmask: */
tme_shared tme_uint8_t *(*tme_bus_cacheable_valids_new) _TME_P((void *, tme_uint32_t));
/* this function sets a bit in the valids bitmask: */
void (*tme_bus_cacheable_valids_set) _TME_P((void *, tme_shared tme_uint8_t *, unsigned long));
};
/* a bus TLB entry: */
struct tme_bus_tlb {
/* the bus address region covered by this TLB entry: */
tme_bus_addr_t tme_bus_tlb_addr_first;
tme_bus_addr_t tme_bus_tlb_addr_last;
/* the token associated with this TLB entry: */
struct tme_token *tme_bus_tlb_token;
/* when one or both of these pointers are not TME_EMULATOR_OFF_UNDEF,
this TLB entry allows fast (memory) reads of and/or writes to the
bus region. adding an address in the bus region to one of these
pointers yields the desired host memory address: */
_tme_const tme_shared tme_uint8_t *tme_bus_tlb_emulator_off_read;
tme_shared tme_uint8_t *tme_bus_tlb_emulator_off_write;
/* fast (memory) reads and writes are protected by this rwlock: */
tme_rwlock_t *tme_bus_tlb_rwlock;
/* if non-NULL, this bus region is cacheable memory: */
_tme_const struct tme_bus_cacheable *tme_bus_tlb_cacheable;
/* when one or both of TLB_BUS_CYCLE_READ and TLB_BUS_CYCLE_WRITE
are set in this value, this TLB entry allows slow (function call)
reads of and/or writes to the bus region: */
unsigned int tme_bus_tlb_cycles_ok;
/* adding an address in the bus region to this offset, and then
shifting that result to the right (shift > 0) or to the left
(shift < 0) yields an address for the bus cycle handler: */
tme_bus_addr_t tme_bus_tlb_addr_offset;
int tme_bus_tlb_addr_shift;
/* the bus cycle handler: */
void *tme_bus_tlb_cycle_private;
tme_bus_cycle_handler tme_bus_tlb_cycle;
/* the bus fault handlers: */
unsigned int tme_bus_tlb_fault_handler_count;
struct {
void *tme_bus_tlb_fault_handler_private;
tme_bus_fault_handler tme_bus_tlb_fault_handler;
} tme_bus_tlb_fault_handlers[TME_BUS_TLB_FAULT_HANDLERS];
};
/* a bus signals set: */
struct tme_bus_signals {
/* the bus signals set identifier: */
tme_uint32_t tme_bus_signals_id;
/* the version of the bus signals: */
tme_uint32_t tme_bus_signals_version;
/* the maximum number of bus signals in the set: */
tme_uint32_t tme_bus_signals_count;
/* the first signal in the bus signals set: */
tme_uint32_t tme_bus_signals_first;
};
/* a bus master's TLB set information: */
struct tme_bus_tlb_set_info {
/* the first token in the set: */
struct tme_token *tme_bus_tlb_set_info_token0;
/* the stride between tokens in the set, in bytes: */
unsigned long tme_bus_tlb_set_info_token_stride;
/* the count of tokens in the set: */
unsigned long tme_bus_tlb_set_info_token_count;
/* the set's optional bus context register: */
/* NB: this isn't tme_shared because the bus context, when it does
exist, is only exposed to the one bus master that can actually
change it - and we assume that changing the bus context
synchronizes the master: */
tme_bus_context_t *tme_bus_tlb_set_info_bus_context;
/* the maximum value of the bus context register: */
tme_bus_context_t tme_bus_tlb_set_info_bus_context_max;
};
/* a bus connection: */
struct tme_bus_connection {
/* the generic connection side: */
struct tme_connection tme_bus_connection;
/* the subregions on the bus for this connection. most connections
will only have one subregion, with a first address of zero and a
last address of their size on the bus: */
struct tme_bus_subregion {
/* the first and last addresses, starting from zero, of this
subregion: */
tme_bus_addr_t tme_bus_subregion_address_first;
tme_bus_addr_t tme_bus_subregion_address_last;
/* any other subregions for this bus connection: */
_tme_const struct tme_bus_subregion *tme_bus_subregion_next;
} tme_bus_subregions;
/* the bus signal set adder: */
int (*tme_bus_signals_add) _TME_P((struct tme_bus_connection *,
struct tme_bus_signals *));
/* the bus signal handler: */
int (*tme_bus_signal) _TME_P((struct tme_bus_connection *, unsigned int));
/* the bus interrupt acknowledge handler: */
int (*tme_bus_intack) _TME_P((struct tme_bus_connection *, unsigned int, int *));
/* the bus TLB set add handler: */
int (*tme_bus_tlb_set_add) _TME_P((struct tme_bus_connection *, struct tme_bus_tlb_set_info *));
/* the bus TLB entry filler: */
int (*tme_bus_tlb_fill) _TME_P((struct tme_bus_connection *, struct tme_bus_tlb *,
tme_bus_addr_t, unsigned int));
};
/* internal information about a bus connection: */
struct tme_bus_connection_int {
/* the external bus connection: */
struct tme_bus_connection tme_bus_connection_int;
/* flags on the bus connection: */
int tme_bus_connection_int_flags;
/* the first and last addresses of this connection. most code
should never use the last-address value, and instead should honor
the connection's subregions: */
tme_bus_addr_t tme_bus_connection_int_address;
tme_bus_addr_t tme_bus_connection_int_address_last;
/* the mask added to addresses sourced by this connection: */
tme_bus_addr_t tme_bus_connection_int_sourced;
/* the single interrupt signal used by this connection, when
the connection doesn't know already: */
int tme_bus_connection_int_signal_int;
/* the single interrupt vector used by this connection, when the
connection doesn't know already: */
int tme_bus_connection_int_vector_int;
/* nonzero iff we've already logged an unconfigured interrupt
signal: */
int tme_bus_connection_int_logged_int;
/* the current status of the bus signals for this connection: */
tme_uint8_t *tme_bus_connection_int_signals;
};
/* a generic bus slot: */
struct tme_bus_slot {
/* generic bus slots are kept on a list: */
struct tme_bus_slot *tme_bus_slot_next;
/* the name of this bus slot: */
char *tme_bus_slot_name;
/* the address and size of this bus slot: */
tme_bus_addr_t tme_bus_slot_address;
tme_bus_addr_t tme_bus_slot_size;
};
/* a generic bus: */
struct tme_bus {
/* the optional rwlock protecting this bus: */
tme_rwlock_t tme_bus_rwlock;
/* the address mask used on this bus: */
tme_bus_addr_t tme_bus_address_mask;
/* all connections to this bus: */
struct tme_bus_connection_int *tme_bus_connections;
/* the number of addressable connections to this bus: */
int tme_bus_addressables_count;
/* the size of the addressable connections array: */
int tme_bus_addressables_size;
/* the addressable connections array: */
struct tme_bus_addressable {
struct tme_bus_connection_int *tme_bus_addressable_connection;
_tme_const struct tme_bus_subregion *tme_bus_addressable_subregion;
} *tme_bus_addressables;
/* the bus signal sets on this bus: */
unsigned int tme_bus_signals_count;
struct tme_bus_signals *tme_bus_signals;
/* the number of devices asserting the various bus signals: */
unsigned int *tme_bus_signal_asserts;
/* any bus slots: */
struct tme_bus_slot *tme_bus_slots;
/* any bus controller connection: */
struct tme_bus_connection_int *tme_bus_controller;
};
/* prototypes: */
int tme_bus_address_search _TME_P((struct tme_bus *, tme_bus_addr_t));
int tme_bus_connection_ok _TME_P((struct tme_bus *,
struct tme_bus_connection_int *));
int tme_bus_connection_make _TME_P((struct tme_bus *,
struct tme_bus_connection_int *,
unsigned int));
int tme_bus_connection_break _TME_P((struct tme_bus *,
struct tme_bus_connection_int *,
unsigned int));
int tme_bus_tlb_fill _TME_P((struct tme_bus *,
struct tme_bus_connection_int *,
struct tme_bus_tlb *, tme_bus_addr_t, unsigned int));
int tme_bus_tlb_set_add _TME_P((struct tme_bus *,
struct tme_bus_connection_int *,
struct tme_bus_tlb_set_info *));
void tme_bus_tlb_set_invalidate _TME_P((_tme_const struct tme_bus_tlb_set_info *));
void tme_bus_tlb_map _TME_P((struct tme_bus_tlb *, tme_bus_addr_t, _tme_const struct tme_bus_tlb *, tme_bus_addr_t));
void tme_bus_tlb_initialize _TME_P((struct tme_bus_tlb *));
int tme_bus_tlb_fault _TME_P((struct tme_bus_tlb *, struct tme_bus_cycle *, int));
tme_bus_addr_t tme_bus_addr_parse _TME_P((_tme_const char *, tme_bus_addr_t));
tme_bus_addr_t tme_bus_addr_parse_any _TME_P((_tme_const char *, int *));
void tme_bus_cycle_xfer _TME_P((struct tme_bus_cycle *, struct tme_bus_cycle *));
void tme_bus_cycle_xfer_memory _TME_P((struct tme_bus_cycle *, tme_uint8_t *, tme_bus_addr_t));
void tme_bus_cycle_xfer_reg _TME_P((struct tme_bus_cycle *, void *, unsigned int));
#endif /* !_TME_GENERIC_BUS_H */