/* $Id: stp22xx-impl.h,v 1.2 2009/08/29 21:17:06 fredette Exp $ */

/* ic/stp22xx/stp22xx-impl.h - implementation header file for STP2200,
   STP2202, STP2220, and STP2222 emulation: */

/*
 * Copyright (c) 2009 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 _STP22XX_IMPL_H
#define _STP22XX_IMPL_H

#include <tme/common.h>
_TME_RCSID("$Id: stp22xx-impl.h,v 1.2 2009/08/29 21:17:06 fredette Exp $");

/* includes: */
#include <tme/completion.h>
#include <tme/bus/upa.h>

/* macros: */

/* this macro wraps code used during the transition to the new bus
   interface.  the new bus interface will remove everything from
   struct tme_bus_tlb starting with the tme_bus_tlb_cycles_ok member
   until the end.  this will remove all slow cycle information from a
   TLB, so TLB filling will only give fast read and write information.

   every bus connection will then have a slow cycle function.  when a
   master needs to do a slow cycle, through its bus connection it will
   arbitrate for the bus and then start its cycle.  the bus
   implementation will steer the cycle towards the slave, which may be
   across a bridge and over another bus, etc.  slow bus cycles will
   then follow the actual path from master to slave, with bus
   implementations ordering bus access and cycles as needed.

   this seems better than the old interface, where masters called
   directly into slaves through function pointers in a TLB.  in the
   old interface, buses couldn't enforce any ordering, because they
   weren't involved.  this created the risk that a master in the
   middle of running a bus cycle could be called as a slave over the
   same bus connection.  real devices never have to face that, and
   without eliminating that here, turning on preemptive threading
   seems impossible.

   the new cycle function in a bus connection will take the bus
   connection, the struct tme_bus_cycle *, a struct tme_completion *,
   and a pointer to a mask of cycle types that can be done fast.  as
   the slow bus cycle makes its way from master to slave, if any point
   along that path can't allow fast reads or writes, those bits are
   cleared from the mask.  when the slow cycle completes, if the
   master finds that any cycles can be done fast again, it may choose
   to fill a TLB entry again to get the new fast cycle information.
   this frees slaves from having to track filled TLBs that weren't
   filled with any fast cycle information, just so they can be
   invalidated when addresses become fast-capable to make sure that
   masters refill them.

   eventually, all of the code wrapped by this macro will be
   removed: */
#define TME_STP22XX_BUS_TRANSITION		(TRUE)

/* the maximum number of completions: */
#define TME_STP22XX_COMPLETIONS_MAX		(2)

/* the maximum number of delayed completions: */
#define TME_STP22XX_COMPLETIONS_DELAYED_MAX	(2)

/* condition states: */
#define TME_STP22XX_COND_STATE_IDLE		(0)
#define TME_STP22XX_COND_STATE_RUNNING		(1)
#define TME_STP22XX_COND_STATE_WAITING		(2)
#define TME_STP22XX_COND_STATE_NOTIFIED		(3)

/* types: */
struct tme_stp22xx;

/* a completion handler: */
typedef void (*_tme_stp22xx_completion_handler_t) _TME_P((struct tme_stp22xx *, struct tme_completion *, void *));

/* a condition: */
struct tme_stp22xx_cond {
  int tme_stp22xx_cond_state;
  tme_cond_t tme_stp22xx_cond_cond;
};

/* a connection: */
union tme_stp22xx_conn {
  struct tme_bus_connection *tme_stp22xx_conn_bus;
  struct tme_upa_bus_connection *tme_stp22xx_conn_upa;
};

/* the device: */
struct tme_stp22xx {
  
  /* backpointer to the device's element: */
  struct tme_element *tme_stp22xx_element;

  /* the mutex protecting the device: */
  tme_mutex_t tme_stp22xx_mutex;

  /* the size of the part-specific structure: */
  unsigned long tme_stp22xx_sizeof;

  /* this is nonzero if the run function is running: */
  int tme_stp22xx_running;

  /* the run function: */
  void (*tme_stp22xx_run) _TME_P((struct tme_stp22xx *));

  /* our completions: */
  struct tme_completion tme_stp22xx_completions[TME_STP22XX_COMPLETIONS_MAX];
  _tme_stp22xx_completion_handler_t tme_stp22xx_completion_handlers[TME_STP22XX_COMPLETIONS_MAX];
  void * tme_stp22xx_completion_args[TME_STP22XX_COMPLETIONS_MAX];

  /* any delayed completions: */
  /* NB: this array is always NULL-terminated: */
  struct tme_completion *tme_stp22xx_completions_delayed[TME_STP22XX_COMPLETIONS_DELAYED_MAX + 1];

  /* the undefined connection index: */
  tme_uint32_t tme_stp22xx_conn_index_null;

  /* any pending master connection index: */
  tme_uint32_t tme_stp22xx_master_conn_index_pending;

  /* any current master connection index and its completion: */
  tme_uint32_t tme_stp22xx_master_conn_index;
  struct tme_completion **tme_stp22xx_master_completion;

#if TME_STP22XX_BUS_TRANSITION
  /* the token for filling TLB entries for slave cycles: */
  struct tme_token tme_stp22xx_slave_cycle_tlb_token;
#endif /* TME_STP22XX_BUS_TRANSITION */

  /* any current slave connection: */
  struct tme_bus_connection *tme_stp22xx_slave_conn_bus;

  /* the connections: */
  /* NB: this must be the last member of this structure; the
     part-specific structure allocates enough space for its real
     size: */
  union tme_stp22xx_conn tme_stp22xx_conns[0];
};

/* prototypes: */

/* this busies a generic bus connection: */
struct tme_bus_connection *tme_stp22xx_busy_bus _TME_P((struct tme_stp22xx *, tme_uint32_t));

/* this unbusies a generic bus connection: */
void tme_stp22xx_unbusy_bus _TME_P((struct tme_stp22xx *, struct tme_bus_connection *));

/* this busies a slave generic bus connection: */
struct tme_bus_connection *tme_stp22xx_slave_busy_bus _TME_P((struct tme_stp22xx *, tme_uint32_t));

/* this unbusies a slave generic bus connection: */
void tme_stp22xx_slave_unbusy _TME_P((struct tme_stp22xx *));

/* this enters: */
struct tme_stp22xx *tme_stp22xx_enter _TME_P((struct tme_stp22xx *));

/* this enters as the bus master: */
struct tme_stp22xx *tme_stp22xx_enter_master _TME_P((struct tme_bus_connection *));

/* this leaves: */
void tme_stp22xx_leave _TME_P((struct tme_stp22xx *));

/* this waits on a condition, with an optional sleep time: */
void tme_stp22xx_cond_sleep_yield _TME_P((struct tme_stp22xx *, struct tme_stp22xx_cond *, const tme_time_t *));

/* this validates a completion: */
void tme_stp22xx_completion_validate _TME_P((struct tme_stp22xx *, struct tme_completion *));

/* this allocates a completion: */
struct tme_completion *tme_stp22xx_completion_alloc _TME_P((struct tme_stp22xx *, _tme_stp22xx_completion_handler_t, void *));

/* this calls out a bus signal to a connection: */
void tme_stp22xx_callout_signal _TME_P((struct tme_stp22xx *, tme_uint32_t, unsigned int, _tme_stp22xx_completion_handler_t));

/* this completes a bus grant: */
void tme_stp22xx_complete_bg _TME_P((struct tme_stp22xx *, struct tme_completion *, void *));

/* this completes a bus operation between master and slave: */
void tme_stp22xx_complete_master _TME_P((struct tme_stp22xx *, struct tme_completion *, void *));

/* this is a no-op completion: */
void tme_stp22xx_complete_nop _TME_P((struct tme_stp22xx *, struct tme_completion *, void *));

/* this runs a slave bus cycle: */
void tme_stp22xx_slave_cycle _TME_P((struct tme_bus_connection *, tme_uint32_t, struct tme_bus_cycle *,	tme_uint32_t *,	struct tme_completion **));

/* this fills a TLB entry: */
void tme_stp22xx_tlb_fill _TME_P((struct tme_bus_connection *, struct tme_bus_tlb *, tme_uint32_t, tme_bus_addr64_t, unsigned int));

/* this adds a TLB set: */
void tme_stp22xx_tlb_set_add _TME_P((struct tme_bus_connection *, struct tme_bus_tlb_set_info *, struct tme_completion *));

#if TME_STP22XX_BUS_TRANSITION

/* this is the bus TLB set add transition glue: */
int tme_stp22xx_tlb_set_add_transition _TME_P((struct tme_bus_connection *, struct tme_bus_tlb_set_info *));
#define tme_stp22xx_tlb_set_add tme_stp22xx_tlb_set_add_transition

#endif /* TME_STP22XX_BUS_TRANSITION */

/* this notifies a condition: */
void tme_stp22xx_cond_notify _TME_P((struct tme_stp22xx_cond *));

/* this initializes a condition: */
void tme_stp22xx_cond_init _TME_P((struct tme_stp22xx_cond *));

/* this initializes an stp22xx: */
void tme_stp22xx_init _TME_P((struct tme_stp22xx *, unsigned long, tme_uint32_t));

#endif /* !_STP22XX_IMPL_H */
