/**
 * @file avmio.h
 *
 * AvmIO - Common definitions for i/o based controllers B1 and T1.
 *
 * Copyright: 2000-2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: avmio.h,v 1.1.2.1 2006/02/28 21:35:10 thomas Exp $
 * Project  CAPI for BSD
 * Target   avmaic - CAPI manager driver for AVM active ISDN controllers
 * @date    01.01.2000
 * @author  "Thomas Wintergerst" <twinterg@gmx.de>
 * <p>
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
 *
 * </p>
 */

#ifndef __AVMIO_H
#define __AVMIO_H

#include <sys/types.h>          /* size_t */
#include <sys/kernel.h>         /* hz */
#include <machine/clock.h>
#include <machine/bus.h>

#include <c4b/avmaic/avmlli.h>





/* === public declarations =============================================== */





/* --- strings for wait operations --- */

#define AVMIO_READY_WAIT_MSG            "wait for ready"
#define AVMIO_WAIT_FOR_RX_FULL_MSG      "wait for rx full"
#define AVMIO_WAIT_FOR_TX_EMPTY_MSG     "wait for tx empty"



/* --- several constants --- */

/**
 * Value for DELAY to poll for some I/O states.
 * 
 * @note This value is not used for any "normal" operation. Instead it is only
 *       used when initialising or downloading a board. So it is not a critical
 *       value. It is used to not flood the buses (ISA, PCI) with i/o requests
 *       when polling for results of some board requests.
 */
#define AVMAIC_IO_POLL_DELAY            10

/**
 * Maximum total wait time to wait for i/o ready state of controller within
 * reading one slice (250ms in ticks, i.e. "<seconds> * HZ").
 */
#define AVMAIC_MAX_IO_WAIT_SLICE        (hz / 4)

/**
 * Maximum wait time to wait for ready state of controller when reading a single
 * byte or word (100ms in ticks, i.e. "<seconds> * HZ").
 */
#define AVMAIC_MAX_IO_WAIT_BYTE         (hz / 10)

/**
 * Maximum wait time for sending the (first) command byte for a message to a
 * board (250ms in ticks, i.e. "<seconds> * hz").
 *
 * This somewhat large timeout value takes into account, that when sending
 * messages like CAPI_REGISTER or CAPI_PUT_MESSAGE to a board, it may still be
 * busy with handling the message sent just before. So we give the board the
 * time needed to empty its message receive buffer.
 */
#define AVMAIC_MAX_IO_WAIT_COMMAND      (hz / 4)

/**
 * Maximum wait time for probing / attaching PC-Cards in ticks.
 *
 * PC-Cards may need some more time after beeing inserted and provided with
 * power, until they are ready to accept commands. So the probe and attach
 * routines must wait longer than for other boards before it is possible to
 * send commands to the card, i.e. until the board's send buffer is empty.
 */
#define AVMAIC_MAX_IO_WAIT_PCCARD       (hz * 60)



/* --- I/O port offsets for active AVM cards --- */

#define AVMAIC_REG_READ	        0x00
#define AVMAIC_REG_WRITE        0x01
#define AVMAIC_REG_INSTAT       0x02
#define AVMAIC_REG_OUTSTAT      0x03
#define AVMAIC_REG_ANALYSE      0x04
#define AVMAIC_REG_REVISION     0x05
#define AVMAIC_REG_RESET        0x10

#define AVMAIC_REG_T1_IRQENABLE 0x05
#define AVMAIC_REG_T1_FIFOSTAT  0x06
#define AVMAIC_REG_T1_RESETLINK 0x10
#define AVMAIC_REG_T1_ANALYSE   0x11
#define AVMAIC_REG_T1_IRQMASTER 0x12
#define AVMAIC_REG_T1_IDENT     0x17
#define AVMAIC_REG_T1_RESETBOARD 0x1F

#define AVMAIC_T1_FASTLINK      0x00
#define AVMAIC_T1_SLOWLINK      0x08

#define AVMAIC_IO_PORT_RANGE    0x20



/* --- defines for internal registers of the cards --- */

#define AVMAIC_INTREG_STAT0     0x80A00000
#define AVMAIC_INTREG_M1_STAT0  0x81200000
#define AVMAIC_INTREG_STAT1     0x80E00000





/* === prototypes of interface functions ================================= */





/* --- direct communication with the controller --- */

/**
 * Send out one byte to an i/o port.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param iReg                  I: Register offset to write to.
 * @param uVal                  I: Value to write into the register.
 *
 * @return                      Result of reading the analyse register after the
 *                              write operation.
 */
static __inline int avmio_outp
   (bus_space_tag_t    t,
    bus_space_handle_t h,
    int                iReg,
    unsigned int       uVal);

/**
 * Receive one byte from a i/o port.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param iReg                  I: Register offset to read from.
 *
 * @return                      The value read from the register.
 */
static __inline int avmio_inp
   (bus_space_tag_t    t,
    bus_space_handle_t h,
    int                iReg);
    
/**
 * Check for filled receive buffer.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 *
 * @retval 0                    The receive buffer is (still) empty.
 * @retval !0                   The receive buffer is filled and ready to read
 *                              from.
 */
static __inline int avmio_rx_full
   (bus_space_tag_t    t,
    bus_space_handle_t h);
    
/**
 * Check for empty send buffer.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 *
 * @retval 0                    The send buffer is (still) full.
 * @retval !0                   The send buffer is empty and ready to write to.
 */
static __inline int avmio_tx_empty
   (bus_space_tag_t    t,
    bus_space_handle_t h);

/**
 * Get byte of data from the controller receive buffer with timeout.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param piTimeout             I/O: Timeout for the read operation in ticks
 *                                 (<seconds> * HZ). On function return the
 *                                 value will be set to the time remaining
 *                                 (less than null if timeout occurred). This
 *                                 can be used to maintain a single timeout
 *                                 over a couple of read operations.
 *
 * @return                      The value read from the receive buffer.
 */
extern unsigned char avmio_get_byte
   (bus_space_tag_t     t,
    bus_space_handle_t  h,
    int                *piTimeout);

/**
 * Write a byte of data to the controller send buffer with timeout.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param ucVal                 I: The value to write.
 * @param piTimeout             I/O: Timeout for the write operation in ticks
 *                                 (<seconds> * HZ). On function return the
 *                                 value will be set to the time remaining
 *                                 (less than null if timeout occurred). This
 *                                 can be used to maintain a single timeout
 *                                 over a couple of write operations.
 *
 * @retval 0                    The write operation was successful.
 * @retval -1                   The write operation failed because of a timeout.
 */
extern int avmio_put_byte
   (bus_space_tag_t     t,
    bus_space_handle_t  h,
    unsigned char       ucVal,
    int                *piTimeout);

/**
 * Write a byte of data to the controller send buffer.
 *
 * This function works nearly the same way as avmio_put_byte(). The only
 * difference is that this function not just writes the byte, but also reads
 * the analyze register of the controller after the write operation.
 *
 * The timeout for the read operation is only maintained internally. So there
 * is no support for one single timeout for multiple consecutive read
 * operations. Instead we use a timeout value of 250ms. This somewhat large
 * timeout value takes into account, that this function is normally called for
 * the first byte (command code) of main operations like CAPI_REGISTER or
 * CAPI_PUT_MESSAGE. When sending these messages to the board, it may still be
 * busy with handling the message sent just before. So we give the board the
 * time needed to empty its message receive buffer. If the board were unable to
 * accept another message, it would have reported this through a special
 * message (AVMAIC_RECEIVE_STOP) and we would be in a busy state, not accepting
 * any new message.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param ucVal                 I: The value to write.
 *
 * @retval 0                    The write operation was successful.
 * @retval -1                   The write operation failed because of a timeout.
 */
extern int avmio_safe_put_byte
   (bus_space_tag_t    t,
    bus_space_handle_t h,
    unsigned char      ucVal);

/**
 * Get 32-bit word of data from the controller receive buffer.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 *
 * @return                       The date value read from the controller.
 */
extern unsigned int avmio_get_word
   (bus_space_tag_t    t,
    bus_space_handle_t h);

/**
 * Write 32-bit word of data to controller.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param uVal                  I: The data value to write to the controller.
 *
 * @retval 0                    The write operation was successful.
 * @retval -1                   The write operation failed because of a timeout.
 */
extern int avmio_put_word
   (bus_space_tag_t    t,
    bus_space_handle_t h,
    unsigned int       uVal);
    
/**
 * Read data from internal controller register.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param iReg                  I: The internal register number to read from.
 *
 * @return                      The value read from the controller register.
 */
extern unsigned int avmio_read_reg
   (bus_space_tag_t    t,
    bus_space_handle_t h,
    int                iReg);
    
/**
 * Write data to internal controller register.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param iReg                  I: The internal register number to write to.
 * @param uVal                  I: The value to write.
 *
 * @return Nothing.
 */
extern void avmio_write_reg
   (bus_space_tag_t    t,
    bus_space_handle_t h,
    int                iReg,
    unsigned int       uVal);
    
/**
 * Get slice of data from controller.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param paucBuf               O: Address of the destination buffer for the
 *                                 slice data.
 * @param nBufLen               I: Length of the destination buffer at paucBuf.
 *
 * @return                      The length of the slice read. This may be larger
 *                              than the buffer size specified in nBufLen. The
 *                              bytes that did not fit into the buffer were
 *                              discarded.
 */
extern size_t avmio_get_slice
   (bus_space_tag_t     t,
    bus_space_handle_t  h,
    unsigned char      *paucBuf,
    size_t              nBufLen);

/**
 * Write slice of data to controller.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param paucBuf               I: Address of the data buffer to write.
 * @param nBufLen               I: Length of the data buffer at paucBuf.
 *
 * @retval 0                    The data slice was written successfully.
 * @retval -1                   An error occurred, the slice was not completely
 *                              written.
 */
static __inline int avmio_put_slice
   (bus_space_tag_t      t,
    bus_space_handle_t   h,
    const unsigned char *paucBuf,
    size_t               nBufLen);



/* --- some helper functions --- */

/**
 * Reset an i/o based board.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 *
 * @return Nothing.
 */
extern void avmio_reset
   (bus_space_tag_t    t,
    bus_space_handle_t h);

/**
 * Get test bit in card.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param cardType              I: The card type to determine the correct
 *                                 register.
 *
 * @retval 0                    Test bit is not set.
 * @retval !0                   Test bit is set.
 */
extern int avmio_get_test_bit
   (bus_space_tag_t    t,
    bus_space_handle_t h,
    AvmAicCardType_t   cardType);

/**
 * Set test bit in card.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param fOn                   I:
 *                              - 0
 *                                 Set test bit.
 *                              - !0
 *                                 Reset test bit.
 * @param cardType              I: The card type to determine the correct
 *                                 register.
 *
 * @return Nothing.
 */
extern void avmio_set_test_bit
   (bus_space_tag_t    t,
    bus_space_handle_t h,
    int                fOn,
    AvmAicCardType_t   cardType);

/* disable irq on a non-T1 controller */
static __inline int avmio_disable_interrupt
   (bus_space_tag_t    t,
    bus_space_handle_t h);

/* disable irq on a T1 controller */
static __inline int avmio_t1_disable_interrupt
   (bus_space_tag_t    t,
    bus_space_handle_t h);

/* enable irq on the controller */
static __inline int avmio_enable_interrupt
   (bus_space_tag_t    t,
    bus_space_handle_t h);
    


/* --- performing i/o based board specific parts of CAPI calls --- */

/* register a CAPI application at a controller */
extern unsigned avmio_app_register
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uMaxLogicalConnections,
    unsigned          uMaxBDataBlocks,
    unsigned          uMaxBDataLen,
    unsigned          uApplID);

/* release a CAPI application at a controller */
extern unsigned avmio_app_release
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uApplID);



/* --- handling interrupt calls of i/o based controllers --- */

/* receive information about a new established B3 connection */
extern void avmio_receive_new_ncci
   (AvmAicSc_t *pSc);
   
/* receive information about a released B3 connection or application */
extern void avmio_receive_free_ncci
   (AvmAicSc_t *pSc);





/* === definition of inline functions ==================================== */





/**
 * Send out one byte to an i/o port.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param iReg                  I: Register offset to write to.
 * @param uVal                  I: Value to write into the register.
 *
 * @return                      Result of reading the analyse register after the
 *                              write operation.
 */

static __inline int avmio_outp
   (bus_space_tag_t    t,
    bus_space_handle_t h,
    int                iReg,
    unsigned int       uVal)
{
   bus_space_write_1 (t, h, iReg, (unsigned char) uVal);
   return (bus_space_read_1 (t, h, AVMAIC_REG_ANALYSE));
} /* avmio_outp */





/**
 * Receive one byte from a i/o port.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param iReg                  I: Register offset to read from.
 *
 * @return                      The value read from the register.
 */

static __inline int avmio_inp
   (bus_space_tag_t    t,
    bus_space_handle_t h,
    int                iReg)
{
   return (bus_space_read_1 (t, h, iReg));
} /* avmio_inp */





/**
 * Check for filled receive buffer.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 *
 * @retval 0                    The receive buffer is (still) empty.
 * @retval !0                   The receive buffer is filled and ready to read
 *                              from.
 */

static __inline int avmio_rx_full
   (bus_space_tag_t    t,
    bus_space_handle_t h)
{
   return ((bus_space_read_1 (t, h, AVMAIC_REG_INSTAT) & 0x01) != 0);
} /* avmio_rx_full */





/**
 * Check for empty send buffer.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 *
 * @retval 0                    The send buffer is (still) full.
 * @retval !0                   The send buffer is empty and ready to write to.
 */

static __inline int avmio_tx_empty
   (bus_space_tag_t    t,
    bus_space_handle_t h)
{
   return ((bus_space_read_1 (t, h, AVMAIC_REG_OUTSTAT) & 0x01) != 0);
} /* avmio_tx_empty */





/**
 * Write slice of data to controller.
 *
 * @param t                     I: Tag for i/o operations.
 * @param h                     I: Handle for i/o operations.
 * @param paucBuf               I: Address of the data buffer to write.
 * @param nBufLen               I: Length of the data buffer at paucBuf.
 *
 * @retval 0                    The data slice was written successfully.
 * @retval -1                   An error occurred, the slice was not completely
 *                              written.
 */

static __inline int avmio_put_slice
   (bus_space_tag_t      t,
    bus_space_handle_t   h,
    const unsigned char *paucBuf,
    size_t               nBufLen)
{
   int iTimeout = AVMAIC_MAX_IO_WAIT_SLICE;
   int i;

   /* first write length of the buffer */
   (void) avmio_put_word (t, h, nBufLen);

   /* now write every byte of the data buffer specified */
   i = (int) nBufLen;
   while (i-- > 0)
   {
      if (avmio_put_byte (t, h, *paucBuf, &iTimeout) != 0)
      {
         DBG0 (LOG_ERROR,
               "Tag 0x%03X, handle 0x%03X: Timeout writing slice at byte %zu of %zu",
               (int) t, (int) h, nBufLen - i, nBufLen);
         return (-1);
      }
      ++paucBuf;
   }

   return (0);
} /* avmio_put_slice */





/*
	disable irq on a non-T1 controller
	----------------------------------
*/

static __inline int avmio_disable_interrupt
   (bus_space_tag_t    t,
    bus_space_handle_t h)
{
   return (avmio_outp (t, h, AVMAIC_REG_INSTAT, 0x00));
} /* avmio_disable_interrupt */





/*
	disable irq on a T1 ISA controller
	----------------------------------
*/

static __inline int avmio_t1_disable_interrupt
   (bus_space_tag_t    t,
    bus_space_handle_t h)
{
   return (avmio_outp (t, h, AVMAIC_REG_T1_IRQMASTER, 0x00));
} /* avmio_t1_disable_interrupt */





/*
	enable irq on the controller
	----------------------------
*/

static __inline int avmio_enable_interrupt
   (bus_space_tag_t    t,
    bus_space_handle_t h)
{
   return (avmio_outp (t, h, AVMAIC_REG_INSTAT, 0x02));
} /* avmio_enable_interrupt */





#endif /* __AVMIO_H */
