/******************************************************************************
 Hardware Programming Interface (HPI) for AudioScience
 ASI50xx, AS51xx, ASI6xxx, ASI87xx ASI89xx series adapters.
 These PCI and PCIe bus adapters are based on a
 TMS320C6205 PCI bus mastering DSP,
 and (except ASI50xx) TI TMS320C6xxx floating point DSP

 Entry point:
   int HPI6205_adapter_init(struct hpi_adapter_obj *pao);

    Copyright (C) 1997-2017  AudioScience Inc. <support@audioscience.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of version 2 of the GNU General Public License as
    published by the Free Software Foundation;

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
*******************************************************************************/
#define SOURCEFILE_NAME "hpi6205.c"

#include "hpi_internal.h"
#include "hpimsginit.h"
#include "hpimsgx.h"
#include "hpidebug.h"
#include "hpi6205.h"
#include "hpidspcd.h"
#include "hpicmn.h"

#ifdef HPI_BUILD_CACHEBBMSTREAMBUFS
/* TO BE REMOVED AFTER CONFIRMATION NO LONGER REQUIRED */
#ifdef _WIN32
#pragma message("HPI_BUILD_CACHEBBMSTREAMBUFS has no effect")
#else
#warning HPI_BUILD_CACHEBBMSTREAMBUFS has no effect
#endif
#endif

/*****************************************************************************/
// for C6205 PCI i/f
// Host Status Register (HSR) bitfields
#define C6205_HSR_INTSRC	0x01
#define C6205_HSR_INTAVAL	0x02
#define C6205_HSR_INTAM		0x04
#define C6205_HSR_CFGERR	0x08
#define C6205_HSR_EEREAD	0x10
// Host-to-DSP Control Register (HDCR) bitfields
#define C6205_HDCR_WARMRESET	0x01
#define C6205_HDCR_DSPINT	0x02
#define C6205_HDCR_PCIBOOT	0x04
// DSP Page Register (DSPP) bitfields,
// defines 4 Mbyte page that BAR0 points to
#define C6205_DSPP_MAP1		0x400

/* BAR0 maps to prefetchable 4 Mbyte memory block set by DSPP.
 * BAR1 maps to non-prefetchable 8 Mbyte memory block
 * of DSP memory mapped registers (starting at 0x01800000).
 * 0x01800000 is hardcoded in the PCI i/f, so that only the offset from this
 * needs to be added to the BAR1 base address set in the PCI config reg
 */
#define C6205_BAR1_PCI_IO_OFFSET (0x0027FFF0L)
#define C6205_BAR1_HSR  (C6205_BAR1_PCI_IO_OFFSET)
#define C6205_BAR1_HDCR (C6205_BAR1_PCI_IO_OFFSET+4)
#define C6205_BAR1_DSPP (C6205_BAR1_PCI_IO_OFFSET+8)

// used to control LED (revA) and reset C6713 (revB)
#define C6205_BAR0_TIMER1_CTL (0x01980000L)

// For first 6713 in CE1 space, using DA17,16,2
#define HPICL_ADDR	0x01400000L
#define HPICH_ADDR	0x01400004L
#define HPIAL_ADDR	0x01410000L
#define HPIAH_ADDR	0x01410004L
#define HPIDIL_ADDR	0x01420000L
#define HPIDIH_ADDR	0x01420004L
#define HPIDL_ADDR	0x01430000L
#define HPIDH_ADDR	0x01430004L

#define C6713_EMIF_GCTL		0x01800000
#define C6713_EMIF_CE1		0x01800004
#define C6713_EMIF_CE0		0x01800008
#define C6713_EMIF_CE2		0x01800010
#define C6713_EMIF_CE3		0x01800014
#define C6713_EMIF_SDRAMCTL	0x01800018
#define C6713_EMIF_SDRAMTIMING	0x0180001C
#define C6713_EMIF_SDRAMEXT	0x01800020

struct hpi_hw_obj_62 {
	// PCI registers
	__iomem uint32_t *prHSR;
	__iomem uint32_t *prHDCR;
	__iomem uint32_t *prDSPP;

	__iomem uint32_t *bar[2]; // memory-mapped BARs

	uint32_t dwDspPage;

	HpiOs_LockedMem_Handle hLockedMem;
	struct bus_master_interface_62 *pInterfaceBuffer;

	uint16_t flagOStreamJustReset[HPI_MAX_STREAMS];
	/* a non-NULL handle means there is an HPI allocated buffer */
	HpiOs_LockedMem_Handle InStreamHostBuffers[HPI_MAX_STREAMS];
	HpiOs_LockedMem_Handle OutStreamHostBuffers[HPI_MAX_STREAMS];
	/* non-zero size means a buffer exists, may be external */
	uint32_t InStreamHostBufferSize[HPI_MAX_STREAMS];
	uint32_t OutStreamHostBufferSize[HPI_MAX_STREAMS];
#ifndef HPI_BUILD_NO_STREAM_WAIT
	/* non-zero size means a client thread is waiting */
	uint32_t InStreamThreshold[HPI_MAX_STREAMS];
	uint32_t OutStreamThreshold[HPI_MAX_STREAMS];
	hpios_event InStreamEvent[HPI_MAX_STREAMS];
	hpios_event OutStreamEvent[HPI_MAX_STREAMS];
#endif

	HpiOs_LockedMem_Handle hControlCache;
	struct hpi_control_cache *pCache;
};

/*****************************************************************************/
/* local prototypes */

#ifdef HPI_OS_WDM
void CheckBeforeBbmCopy(
	struct hpi_hostbuffer_status * status,
	void *pBbmData,
	uint32_t lFirstWrite,
	uint32_t lSecondWrite
)
{
	(void)pBbmData;

	if ((lFirstWrite + status->dwHostIndex > status->dwSizeInBytes) ||
		(lSecondWrite > status->dwSizeInBytes))
		KeBugCheckEx(0xC00A5101, status->dwSizeInBytes,
			status->dwHostIndex, lFirstWrite, lSecondWrite);
}
#else
#define CheckBeforeBbmCopy(status, pBbmData, lFirstWrite, lSecondWrite)
#endif

static int WaitDspAck(
	struct hpi_hw_obj_62 *phw,
	int state,
	int timeout_us);

static void SendDspCommand(
	struct hpi_hw_obj_62 *phw,
	int cmd);

static hpi_err_t AdapterBootLoadDsp(
	struct hpi_adapter_obj *pao
);

static hpi_err_t MessageResponseSequence(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void HwMessage(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

#define HPI6205_TIMEOUT 1000000

static void AdapterDelete(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void AdapterShutdown(struct hpi_adapter_obj *pao);

static int AdapterIrqQueryAndClear(struct hpi_adapter_obj *pao, uint32_t message);

#ifndef HPI_BUILD_NO_STREAM_WAIT
static void AdapterMsgIrqQueryAndClear(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void AdapterIrqCallback(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);
#endif

static void OutStreamHostBufferAllocate(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void OutStreamHostBufferGetInfo(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void OutStreamHostBufferFree(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);
static void OutStreamWrite(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void OutStreamGetInfo(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void OutStreamStart(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void OutStreamOpen(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void OutStreamReset(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

#ifndef HPI_BUILD_NO_STREAM_WAIT
static void OutStreamWait(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static uint32_t OutStreamThresholdReached(
	struct hpi_hostbuffer_status * status,
	uint32_t threshold
);
#endif

static void InStreamHostBufferAllocate(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void InStreamHostBufferGetInfo(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void InStreamHostBufferFree(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void InStreamRead(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void InStreamGetInfo(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void InStreamStart(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static void InStreamReset(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

#ifndef HPI_BUILD_NO_STREAM_WAIT
static void InStreamWait(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
);

static uint32_t InStreamThresholdReached(
	struct hpi_hostbuffer_status * status,
	uint32_t threshold
);
#endif

static uint32_t BootLoader_ReadMem32(
	struct hpi_adapter_obj *pao,
	int nDspIndex,
	uint32_t dwAddress
);

static void BootLoader_WriteMem32(
	struct hpi_adapter_obj *pao,
	int nDspIndex,
	uint32_t dwAddress,
	uint32_t dwData
);

static hpi_err_t BootLoader_ConfigEmif(
	struct hpi_adapter_obj *pao,
	int nDspIndex
);

static hpi_err_t BootLoader_TestMemory(
	struct hpi_adapter_obj *pao,
	int nDspIndex,
	uint32_t dwAddress,
	uint32_t dwLength
);

static hpi_err_t BootLoader_TestInternalMemory(
	struct hpi_adapter_obj *pao,
	int nDspIndex
);

static hpi_err_t BootLoader_TestExternalMemory(
	struct hpi_adapter_obj *pao,
	int nDspIndex
);

static hpi_err_t BootLoader_TestPld(
	struct hpi_adapter_obj *pao,
	int nDspIndex
);

/*****************************************************************************/
static void ControlMessage(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;

	switch (phm->wFunction) {
	case HPI_CONTROL_GET_STATE:
		if (pao->wHasControlCache) {
			RMB(); /* make sure we see updates DMAed from DSP */
			if (HpiCheckControlCache(phw->pCache, phm, phr)) {
				break;
			}
			/* control cache must work for peak meters so report an error */
			if (phm->u.c.wAttribute == HPI_METER_PEAK) {
				phr->wError = HPI_ERROR_CONTROL_CACHING;
				break;
			}
		}
		HwMessage(pao, phm, phr);
		break;
	case HPI_CONTROL_GET_INFO:
		HwMessage(pao, phm, phr);
		break;
	case HPI_CONTROL_SET_STATE:
		HwMessage(pao, phm, phr);
		if (pao->wHasControlCache)
			HpiCmn_ControlCache_SyncToMsg(phw->pCache, phm, phr);
		break;
	default:
		phr->wError = HPI_ERROR_INVALID_FUNC;
		break;
	}
}

static void AdapterMessage(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	switch (phm->wFunction) {
	case HPI_ADAPTER_CLOSE:
		HwMessage(pao, phm, phr);
		// wait for the DSP to indicate it is idle
		if (!WaitDspAck((struct hpi_hw_obj_62 *)(pao->priv),
			H620_HIF_IDLE, HPI6205_TIMEOUT)) {
			HPI_DEBUG_LOG0(DEBUG,
				"Timeout waiting for idle on AdapterClose\n");
			phr->wError = HPI_ERROR_DSP_COMMUNICATION;
		}
		break;
	case HPI_ADAPTER_DELETE:
		AdapterDelete(pao, phm, phr);
		break;
#ifndef HPI_BUILD_NO_STREAM_WAIT
	case HPI_ADAPTER_IRQ_QUERY_AND_CLEAR:
		AdapterMsgIrqQueryAndClear(pao, phm, phr);
		break;
	case HPI_ADAPTER_IRQ_CALLBACK:
		AdapterIrqCallback(pao, phm, phr);
		break;
#else
	case HPI_ADAPTER_IRQ_QUERY_AND_CLEAR:
	case HPI_ADAPTER_IRQ_CALLBACK:
		phr->wError = HPI_ERROR_UNIMPLEMENTED;
		break;
#endif
	default:
		HwMessage(pao, phm, phr);
		break;
	}
}

static void OStreamMessage(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{

	if (phm->wObjIndex >= HPI_MAX_STREAMS) {
		phr->wError = HPI_ERROR_INVALID_OBJ_INDEX;
		HPI_DEBUG_LOG2(WARNING,
			"Message referencing invalid stream %d "
			"on adapter index %d\n",
			phm->wObjIndex, phm->wAdapterIndex);
		return;
	}

	switch (phm->wFunction) {
	case HPI_OSTREAM_WRITE:
		OutStreamWrite(pao, phm, phr);
		break;
	case HPI_OSTREAM_GET_INFO:
		OutStreamGetInfo(pao, phm, phr);
		break;
	case HPI_OSTREAM_HOSTBUFFER_ALLOC:
		OutStreamHostBufferAllocate(pao, phm, phr);
		break;
	case HPI_OSTREAM_HOSTBUFFER_GET_INFO:
		OutStreamHostBufferGetInfo(pao, phm, phr);
		break;
	case HPI_OSTREAM_HOSTBUFFER_FREE:
		OutStreamHostBufferFree(pao, phm, phr);
		break;
	case HPI_OSTREAM_START:
		OutStreamStart(pao, phm, phr);
		break;
	case HPI_OSTREAM_OPEN:
		OutStreamOpen(pao, phm, phr);
		break;
	case HPI_OSTREAM_RESET:
		OutStreamReset(pao, phm, phr);
		break;
#ifndef HPI_BUILD_NO_STREAM_WAIT
	case HPI_OSTREAM_WAIT:
		OutStreamWait(pao, phm, phr);
		break;
#endif
	default:
		HwMessage(pao, phm, phr);
		break;
	}
}

static void IStreamMessage(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{

	if (phm->wObjIndex >= HPI_MAX_STREAMS) {
		phr->wError = HPI_ERROR_INVALID_OBJ_INDEX;
		HPI_DEBUG_LOG2(WARNING,
			"Message referencing invalid stream %d "
			"on adapter index %d\n",
			phm->wObjIndex, phm->wAdapterIndex);
		return;
	}

	switch (phm->wFunction) {
	case HPI_ISTREAM_READ:
		InStreamRead(pao, phm, phr);
		break;
	case HPI_ISTREAM_GET_INFO:
		InStreamGetInfo(pao, phm, phr);
		break;
	case HPI_ISTREAM_HOSTBUFFER_ALLOC:
		InStreamHostBufferAllocate(pao, phm, phr);
		break;
	case HPI_ISTREAM_HOSTBUFFER_GET_INFO:
		InStreamHostBufferGetInfo(pao, phm, phr);
		break;
	case HPI_ISTREAM_HOSTBUFFER_FREE:
		InStreamHostBufferFree(pao, phm, phr);
		break;
	case HPI_ISTREAM_START:
		InStreamStart(pao, phm, phr);
		break;
	case HPI_ISTREAM_RESET:
		InStreamReset(pao, phm, phr);
		break;
#ifndef HPI_BUILD_NO_STREAM_WAIT
	case HPI_ISTREAM_WAIT:
		InStreamWait(pao, phm, phr);
		break;
#endif
	default:
		HwMessage(pao, phm, phr);
		break;
	}
}

/*****************************************************************************/
/** Process HPI Request/Response transactions
 */
static void HPI_6205(
		struct hpi_adapter_obj *pao,
		struct hpi_message *phm,
		struct hpi_response *phr
		)
{
	if ((pao->wDspCrashed >= 10) &&
		(phm->wFunction != HPI_ADAPTER_DEBUG_READ)){
			/* allow last resort debug read even after crash */
		HPI_InitResponse(phr, phm->wObject, phm->wFunction,
			HPI_ERROR_DSP_HARDWARE);
		HPI_DEBUG_LOG2(WARNING, " %d,%d dsp crashed.\n",
			phm->wObject, phm->wFunction);
		return;
	}

	/* Init default response  */
	phr->wError = HPI_ERROR_PROCESSING_MESSAGE;

	switch (phm->wType) {
	case HPI_TYPE_REQUEST:
		switch (phm->wObject) {
		case HPI_OBJ_SUBSYSTEM:
			phr->wError = HPI_ERROR_INVALID_FUNC;
			break;

		case HPI_OBJ_ADAPTER:
			AdapterMessage(pao, phm, phr);
			break;

		case HPI_OBJ_CONTROL:
			ControlMessage(pao, phm, phr);
			break;

		case HPI_OBJ_OSTREAM:
			OStreamMessage(pao, phm, phr);
			break;

		case HPI_OBJ_ISTREAM:
			IStreamMessage(pao, phm, phr);
			break;

		default:
			HwMessage(pao, phm, phr);
			break;
		}
		break;

	default:
		phr->wError = HPI_ERROR_INVALID_TYPE;
		break;
	}
}

/*****************************************************************************/
/* INITIALIZATION */

#ifdef HPI_OS_LINUX_KERNEL

/** Linux interrupt service routines */
static irqreturn_t HPI6205_threaded_irq_handler(int irq, void *dev_id)
{
	struct hpi_adapter_obj *pa = dev_id;

	if (pa->os.irq_thread)
		pa->os.irq_thread(pa);

#ifdef HPI_OS_DELETE
	{ /* Log ostream0 status */
		struct hpi_hostbuffer_status *stat;
		stat = pa->aOutStreamHostBufferStatus;
		if (stat)
		    printk(KERN_INFO "OStream0 status sp=%d, ad=%d, ss=%d, di=%d, hi=%d, l=%d\n",
			stat->dwSamplesProcessed,
			stat->dwAuxiliaryDataAvailable,
			stat->dwStreamState,
			// DSP index in to the host bus master buffer.
			stat->dwDspIndex,
			// Host index in to the host bus master buffer.
			stat->dwHostIndex,
			stat->dwSizeInBytes);
	}
#endif

	return IRQ_HANDLED;
}

static irqreturn_t HPI6205_irq_handler(int irq, void *dev_id)
{
	struct hpi_adapter_obj *pa = dev_id;

	if (!AdapterIrqQueryAndClear(pa, 0))
		return IRQ_NONE;

	//pa->os.irq_count++;
	//printk(KERN_INFO "HPI6205_irq_handler %d Adapter %d handled\n",
	//pa->os.irq_count, pa->index);

	/* callback will be run from thread context */
	if (pa->os.irq_thread)
		return IRQ_WAKE_THREAD;

	return IRQ_HANDLED;
}

/** Linux interrupt setup */
static void SetupInterrupt(struct hpi_adapter_obj *pao)
{
	hpi_err_t err;
	struct hpi_message hm;
	struct hpi_response hr;
	struct pci_dev *pci_dev = pao->os.pci_dev;
	struct device *dev = &pci_dev->dev;

	/* Check if current mode == Low Latency mode */
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_GET_MODE);
	err = MessageResponseSequence(pao, &hm, &hr);
	if (err || hr.wError || hr.u.ax.mode.adapter_mode != HPI_ADAPTER_MODE_LOW_LATENCY)
	{
		dev_info(dev,
			"Adapter at index %d is not in low latency mode\n",
			pao->index);
		goto no_irq;
	}

	/* Check if IRQs are supported */
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_GET_PROPERTY);
	hm.u.ax.property_set.wProperty = HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ;
	err = MessageResponseSequence(pao, &hm, &hr);
	if (err || hr.wError || !hr.u.ax.property_get.wParameter1) {
		dev_info( dev,
			"IRQs not supported by adapter at index %d\n",
			pao->index);
		goto no_irq;
	}

	/* Disable IRQ generation on DSP side by setting the rate to 0 */
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_SET_PROPERTY);
	hm.u.ax.property_set.wProperty = HPI_ADAPTER_PROPERTY_IRQ_RATE;
	hm.u.ax.property_set.wParameter1 = 0;
	hm.u.ax.property_set.wParameter2 = 0;
	err = MessageResponseSequence(pao, &hm, &hr);
	if (err || hr.wError) {
		dev_err(dev, "irq rate setting failed, aborting\n");
		goto no_irq;
	}

	/* Note: request_irq may call HPI6205_irq_handler here */
	if (devm_request_threaded_irq(dev, pci_dev->irq,
				      HPI6205_irq_handler,
				      HPI6205_threaded_irq_handler,
				      IRQF_SHARED, dev_name(dev), pao))
	{
		dev_err(dev, "request_irq(%d) failed\n", pci_dev->irq);
		goto no_irq;
	}

	pao->os.irq = pci_dev->irq;

	dev_info(dev, "using irq %d\n", pci_dev->irq);
	return;

no_irq:
	dev_info(dev, "not using interrupts");

}

/* Map first 2 PCI resources of type memory on TMS320C6205 DSP
 * Map the whole resource as described by PCI
 *
 * pcim_iomap is managed, so iounmap is handled by driver framework
 * when driver is unloaded or probe fails
 */
static int SetupBars(struct hpi_adapter_obj *pao)
{
	struct pci_dev *pci_dev = pao->os.pci_dev;
	struct hpi_hw_obj_62 *phw = pao->priv;
	int i;

	i = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
	if (!i)
		i = pci_set_consistent_dma_mask(pci_dev, DMA_BIT_MASK(32));

	if (i) {
		dev_err(&pci_dev->dev, "DMA set 32 bit mask error %d\n", i);
		return i;
	}

	for (i = 0; i < 2; i++) {
		phw->bar[i] = pcim_iomap(pci_dev, i, 0);
		if (!phw->bar[i])
			return -ENOMEM;
	}
	return 0;
}
#elif (defined HPI_OS_WDM) || (defined HPI_OS_OSX)
/* Other OSes could put code here, or do something outside this HPI */
static void SetupInterrupt(struct hpi_adapter_obj *pao)
{
}

static hpi_err_t SetupBars(struct hpi_adapter_obj *pao)
{
	struct hpi_hw_obj_62 *phw = pao->priv;

	phw->bar[0] = (__iomem uint32_t *)
		pao->os.wdm_bus_info.Pci.MemoryInfo[0].Virtual;
	phw->bar[1] = (__iomem uint32_t *)
		pao->os.wdm_bus_info.Pci.MemoryInfo[1].Virtual;

	return 0;
}
#endif

int HPI6205_adapter_init(struct hpi_adapter_obj *pao);

static const struct hpi_endpoint HPI6205_endpoint = {
	C99(.name =) "HPI6205",
	C99(.adapter_init =) HPI6205_adapter_init,
	C99(.shutdown =) AdapterShutdown,
	C99(.irq_query_and_clear =) AdapterIrqQueryAndClear,
	C99(.transact =) HPI_6205
};

/** Initialize adapter
 * allocate buffers, bootload DSPs, initialise control cache
 * expects pao to have initialized:
 * linux: os.pci_dev
 * other: os.apMemBase, ?
 */
static int _HPI6205_adapter_init(struct hpi_adapter_obj *pao)
{
	struct hpi_hw_obj_62 *phw;
	struct bus_master_interface_62 *interface;
	int i;
	hpi_err_t err;
	struct hpi_message hm;
	struct hpi_response hr;
	uint32_t nMaxStreams;

	pao->ep = HPI6205_endpoint;

	phw = HpiOs_MemAllocZero(sizeof(struct hpi_hw_obj_62));
	if (!phw)
		return HPI_ERROR_MEMORY_ALLOC;
	pao->priv = phw;

	err = SetupBars(pao);
	if (err)
		return err;

	// init error reporting
	pao->wDspCrashed = 0;
	HpiOs_SpinLock_Init(&pao->dspLock);

	for (i = 0; i < HPI_MAX_STREAMS; i++){
		phw->flagOStreamJustReset[i] = 1;
#ifndef HPI_BUILD_NO_STREAM_WAIT
		HpiOs_Event_Init(&phw->OutStreamEvent[i]);
		HpiOs_Event_Init(&phw->InStreamEvent[i]);
#endif
	}

	/* The C6205 memory area 1 is 8Mbyte window into DSP registers */
	phw->prHSR = phw->bar[1] +
		C6205_BAR1_HSR / sizeof(*phw->bar[1]);
	phw->prHDCR =
		phw->bar[1] +
		C6205_BAR1_HDCR / sizeof(*phw->bar[1]);
	phw->prDSPP =
		phw->bar[1] +
		C6205_BAR1_DSPP / sizeof(*phw->bar[1]);

	pao->wHasControlCache = 0;

	err = AdapterBootLoadDsp(pao);
	if (err)
		return err;

	HPI_DEBUG_LOG0(INFO, "Load DSP code OK\n");

	interface = phw->pInterfaceBuffer;

	// make sure the DSP has started ok
	if (!WaitDspAck(phw, H620_HIF_RESET, HPI6205_TIMEOUT * 10)) {
		HPI_DEBUG_LOG0(ERROR, "Timed out waiting reset state \n");
		return HPI_ERROR_DSP_0;
	}
	/* Note that *pao, *phw are zeroed after allocation,
	 * so pointers and flags are NULL by default.
	 * Allocate bus mastering control cache buffer and tell the DSP about it
	 */
	if (interface->aControlCache.dwNumberOfControls) {
		uint8_t *pControlCacheVirtual;

		pControlCacheVirtual = HpiOs_LockedMem_Alloc(
			&phw->hControlCache,
			interface->aControlCache.dwSizeInBytes,
			&pao->os,
			&interface->aControlCache.dwPhysicalAddress32);

		if (pControlCacheVirtual) {
			memset(pControlCacheVirtual, 0,
				interface->aControlCache.dwSizeInBytes);

			phw->pCache = HpiAllocControlCache(
				interface->aControlCache.dwNumberOfControls,
				interface->aControlCache.dwSizeInBytes,
				pControlCacheVirtual);
		}

		if (phw->pCache) {
			pao->wHasControlCache = 1;
		} else {
			if (pControlCacheVirtual)
				HpiOs_LockedMem_Free(&phw->hControlCache);
			pao->wHasControlCache = 0;
		}
	}

	SendDspCommand(phw,  H620_HIF_IDLE);

	/* Open adapter and mixer needed to set up some aspects of
	 * DSP<->HOST communication
	 */
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_OPEN);
	err = MessageResponseSequence(pao, &hm, &hr);
	if (err || hr.wError)
		return HPI_ERROR_DSP_COMMUNICATION;

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN);
	err = MessageResponseSequence(pao, &hm, &hr);
	if (err || hr.wError)
		return HPI_ERROR_DSP_COMMUNICATION;

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_GET_INFO);
	err = MessageResponseSequence(pao, &hm, &hr);
	if (err || hr.wError)
		return HPI_ERROR_DSP_COMMUNICATION;

	pao->type = hr.u.ax.info.wAdapterType;
	pao->index = hr.u.ax.info.wAdapterIndex;
	pao->instreams = (uint8_t)hr.u.ax.info.wNumIStreams;
	pao->outstreams = (uint8_t)hr.u.ax.info.wNumOStreams;

	nMaxStreams = pao->instreams + pao->outstreams;

#ifndef HPI_OS_LINUX_KERNEL
	HpiOs_LockedMem_Prepare((nMaxStreams * 6) / 10, nMaxStreams, 65536, &pao->os);
#endif

	HPI_DEBUG_LOG3(VERBOSE, "Adapter Type %x Index %d Serial %d\n",
		       pao->type, pao->index, hr.u.ax.info.dwSerialNumber
		      );

	if (phw->pCache)
		phw->pCache->adap_idx = pao->index;

	HPI_DEBUG_LOG0(INFO, "Bootload DSP OK\n");

	pao->aInStreamHostBufferStatus = phw->pInterfaceBuffer->aInStreamHostBufferStatus;
	pao->aOutStreamHostBufferStatus = phw->pInterfaceBuffer->aOutStreamHostBufferStatus;

	/* Note - not doing HPIMSGX_AdapterPrepare, HpiAddAdapter here
	 * because these mustn't happen on adapter wake
	 */

	SetupInterrupt(pao);
	return 0;
}

int HPI6205_adapter_init(struct hpi_adapter_obj *pao)
{
	int err = _HPI6205_adapter_init(pao);

	if (err)
		AdapterShutdown(pao);

	return err;
}

/** Reset DSPs and free memory areas allocated for adapter
*/
static void AdapterShutdown(struct hpi_adapter_obj *pao)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	int i;

	// reset adapter h/w
	// Reset C6713 #1
	BootLoader_WriteMem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0);
	// reset C6205
	HPIOS_MEMWRITE32(phw->prHDCR, C6205_HDCR_WARMRESET);

	HpiOs_LockedMem_Free(&phw->hControlCache);
	HpiOs_LockedMem_Free(&phw->hLockedMem);
	HpiFreeControlCache(phw->pCache);

	for (i = 0; i < HPI_MAX_STREAMS; i++)
		HpiOs_LockedMem_Free(&phw->InStreamHostBuffers[i]);

	for (i = 0; i < HPI_MAX_STREAMS; i++)
		HpiOs_LockedMem_Free(&phw->OutStreamHostBuffers[i]);

#ifndef HPI_OS_LINUX_KERNEL
	HpiOs_LockedMem_Unprepare(&pao->os);
#endif
	HpiOs_MemFree(phw);
}

#ifndef HPI_OS_LINUX_KERNEL
/* Helper functions for power of 2 buffering */
/*Linux already defines these functions */
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif

static unsigned int roundup_pow_of_two(
	unsigned int size
)
{
	unsigned int n = 0;
	if (size)
		size = size - 1;

	while (size) {
		size >>= 1;
		n++;
	}
	return 1 << n;

}
#endif
/*****************************************************************************/
/* Adapter functions */
static void AdapterDelete(struct hpi_adapter_obj *pao,
			  struct hpi_message *phm,
			  struct hpi_response *phr)
{
	AdapterShutdown(pao);
	phr->wError = 0;
}

static int AdapterIrqQueryAndClear(struct hpi_adapter_obj *pao, uint32_t message)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	uint32_t hsr = 0;

	hsr = HPIOS_MEMREAD32(phw->prHSR);
	if (hsr & C6205_HSR_INTSRC) {
		// reset the interrupt from the DSP
		HPIOS_MEMWRITE32(phw->prHSR, C6205_HSR_INTSRC);
#ifndef HPI_BUILD_NO_STREAM_WAIT
#ifdef HPI_OS_LINUX_KERNEL
		/* direct test of one stream rather than
		   going via AdapterIrqCallback */
		if (phw->InStreamHostBufferSize[0] &&
		     phw->InStreamThreshold[0]) {
			struct hpi_hostbuffer_status *s =
				&phw->pInterfaceBuffer->aInStreamHostBufferStatus[0];

			if ((s->dwDspIndex - s->dwHostIndex) >=
					phw->InStreamThreshold[0])
				HpiOs_Event_Set(&phw->InStreamEvent[0]);
		}
		if (phw->OutStreamHostBufferSize[0] &&
		     phw->OutStreamThreshold[0]) {
			struct hpi_hostbuffer_status *s =
				&phw->pInterfaceBuffer->aOutStreamHostBufferStatus[0];

			if (((s->dwHostIndex - s->dwDspIndex) +
					s->dwAuxiliaryDataAvailable) <=
						phw->OutStreamThreshold[0])
				HpiOs_Event_Set(&phw->OutStreamEvent[0]);
		}
#endif
#endif
		return HPI_IRQ_MIXER;
	}

	return HPI_IRQ_NONE;
}

#ifndef HPI_BUILD_NO_STREAM_WAIT
static void AdapterMsgIrqQueryAndClear(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	int found = AdapterIrqQueryAndClear(pao, 0);

	HPI_InitResponse(phr, phm->wObject, phm->wFunction, 0);

	phr->u.ax.irq_query.yes = found;
}

static void AdapterIrqCallback(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;
	struct hpi_hostbuffer_status *status;
	int i;

	// iterate through streams, check any that are blocked
	// signalling them if they meet the threshold
	for (i = 0; i < HPI_MAX_STREAMS; i++){
		if ( phw->InStreamHostBufferSize[i] &&
		     phw->InStreamThreshold[i] ){
			status = &interface->aInStreamHostBufferStatus[i];
			if( InStreamThresholdReached(status,
				phw->InStreamThreshold[i]) ){
					HpiOs_Event_Set(&phw->InStreamEvent[i]);
			}
		}
	}
	for (i = 0; i < HPI_MAX_STREAMS; i++){
		if ( phw->OutStreamHostBufferSize[i] &&
		     phw->OutStreamThreshold[i] ){
			status = &interface->aOutStreamHostBufferStatus[i];
			if( OutStreamThresholdReached(status,
				phw->OutStreamThreshold[i]) ){
					HpiOs_Event_Set(&phw->OutStreamEvent[i]);
			}
		}
	}
	HPI_InitResponse(phr, phm->wObject, phm->wFunction, 0);
}
#endif

/*****************************************************************************/
/* OutStream Host buffer functions */

/** Allocate or attach buffer for busmastering
*/
static void OutStreamHostBufferAllocate(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	uint32_t dwCommand = phm->u.d.u.buffer.dwCommand;
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;

	HPI_InitResponse(phr, phm->wObject, phm->wFunction, 0);

	if (dwCommand == HPI_BUFFER_CMD_EXTERNAL
		|| dwCommand == HPI_BUFFER_CMD_INTERNAL_ALLOC) {
		/* ALLOC phase, allocate a buffer with power of 2 size,
		   get its bus address for PCI bus mastering
		 */
		phm->u.d.u.buffer.dwBufferSize =
			roundup_pow_of_two(phm->u.d.u.buffer.dwBufferSize);
		/* return old size and allocated size,
		   so caller can detect change */
		phr->u.d.u.stream_info.dwDataAvailable =
			phw->OutStreamHostBufferSize[phm->wObjIndex];
		phr->u.d.u.stream_info.dwBufferSize =
			phm->u.d.u.buffer.dwBufferSize;

		if (phw->OutStreamHostBufferSize[phm->wObjIndex] ==
			phm->u.d.u.buffer.dwBufferSize) {
			/* Same size, no action required */
			return;
		}

		HpiOs_LockedMem_Free(&phw->
				OutStreamHostBuffers[phm->wObjIndex]);

		if (!HpiOs_LockedMem_Alloc(&phw->
			OutStreamHostBuffers[phm->wObjIndex],
			phm->u.d.u.buffer.dwBufferSize, &pao->os,
			&phm->u.d.u.buffer.dwPciAddress))
		{
			phr->wError = HPI_ERROR_INVALID_DATASIZE;
			phw->OutStreamHostBufferSize[phm->wObjIndex] = 0;
			return;
		}

		/* get the phys addr into msg for single call alloc caller
		 * needs to do this for split alloc (or use the same message)
		 * return the phy address for split alloc in the respose too
		 */
		phr->u.d.u.stream_info.dwAuxiliaryDataAvailable =
			phm->u.d.u.buffer.dwPciAddress;
	}

	if (dwCommand == HPI_BUFFER_CMD_EXTERNAL
		|| dwCommand == HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER) {
		/* GRANT phase.  Set up the BBM status, tell the DSP about
		   the buffer so it can start using BBM.
		 */
		struct hpi_hostbuffer_status *status;

		if (phm->u.d.u.buffer.
			dwBufferSize & (phm->u.d.u.buffer.dwBufferSize - 1)) {
			HPI_DEBUG_LOG1(ERROR,
				"Buffer size must be 2^N not %d\n",
				phm->u.d.u.buffer.dwBufferSize);
			phr->wError = HPI_ERROR_INVALID_DATASIZE;
			return;
		}
		phw->OutStreamHostBufferSize[phm->wObjIndex] =
			phm->u.d.u.buffer.dwBufferSize;
		status = &interface->aOutStreamHostBufferStatus[phm->wObjIndex];
		status->dwSamplesProcessed = 0;
		status->dwStreamState = HPI_STATE_STOPPED;
		status->dwDspIndex = 0;
		status->dwHostIndex = status->dwDspIndex;
		status->dwSizeInBytes = phm->u.d.u.buffer.dwBufferSize;
		status->dwAuxiliaryDataAvailable = 0;

		HwMessage(pao, phm, phr);

		if (phr->wError) {
			HpiOs_LockedMem_Free(&phw->
				OutStreamHostBuffers[phm->wObjIndex]);
			phw->OutStreamHostBufferSize[phm->wObjIndex] = 0;
		}
	}
}
static void OutStreamHostBufferGetInfo(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;
	struct hpi_hostbuffer_status *status;
	uint8_t *pBbmData;

	pBbmData = HpiOs_LockedMem_GetVirtAddr
			(&phw->OutStreamHostBuffers[phm->wObjIndex]);
	if (pBbmData) {
		status = &interface->aOutStreamHostBufferStatus[phm->wObjIndex];
		HPI_InitResponse(phr, HPI_OBJ_OSTREAM,
			HPI_OSTREAM_HOSTBUFFER_GET_INFO, 0);
		phr->u.d.u.hostbuffer_info.pBuffer = pBbmData;
		phr->u.d.u.hostbuffer_info.pStatus = status;
	} else {
		HPI_InitResponse(phr, HPI_OBJ_OSTREAM,
			HPI_OSTREAM_HOSTBUFFER_GET_INFO, HPI_ERROR_HOST_BUFFER_NOT_ALLOCATED);
	}
}

static void OutStreamHostBufferFree(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	uint32_t dwCommand = phm->u.d.u.buffer.dwCommand;

	if (phw->OutStreamHostBufferSize[phm->wObjIndex]) {
		if (dwCommand == HPI_BUFFER_CMD_EXTERNAL ||
			dwCommand == HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER) {
			phw->OutStreamHostBufferSize[phm->wObjIndex] = 0;
			HwMessage(pao, phm, phr);
			// Tell adapter to stop using the host buffer.
		}
		if (dwCommand == HPI_BUFFER_CMD_EXTERNAL ||
			dwCommand == HPI_BUFFER_CMD_INTERNAL_FREE)
			HpiOs_LockedMem_Free(&phw->
				OutStreamHostBuffers[phm->wObjIndex]);
	}
	/* Should HPI_ERROR_INVALID_OPERATION be returned
	   if no host buffer is allocated? */
	else
		HPI_InitResponse(phr, HPI_OBJ_OSTREAM,
		HPI_OSTREAM_HOSTBUFFER_FREE, HPI_ERROR_HOST_BUFFER_NOT_ALLOCATED);

}

static uint32_t OutStreamGetSpaceAvailable(
	struct hpi_hostbuffer_status * status
)
{
	return status->dwSizeInBytes - (status->dwHostIndex - status->dwDspIndex);
}

#ifndef HPI_BUILD_NO_STREAM_WAIT
static uint32_t OutStreamThresholdReached(
	struct hpi_hostbuffer_status * status,
	uint32_t threshold
)
{
	return ( status->dwSizeInBytes - OutStreamGetSpaceAvailable(status) +
		 status->dwAuxiliaryDataAvailable ) <= threshold;
}
#endif

static void OutStreamWrite(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;
	struct hpi_hostbuffer_status *status;
	uint32_t dwSpaceAvailable;
	uint8_t *pBbmData;

	/* If there is no BBM (size == 0), do the copy the legacy way */
	if (!phw->OutStreamHostBufferSize[phm->wObjIndex]) {

		/* force multiple of 4 bytes */
		uint32_t dwSizeRounding = phm->u.d.u.data.dwDataSize & 0x3;

		if (phw->flagOStreamJustReset[phm->wObjIndex]) {
			phw->flagOStreamJustReset[phm->wObjIndex] = 0;
		}

		phm->u.d.u.data.dwDataSize -= dwSizeRounding;

		/* there  is no BBM buffer, write via message */
		if (phm->u.d.u.data.dwDataSize)
			HwMessage(pao, phm, phr);

		/* restore the size parameter */
		phm->u.d.u.data.dwDataSize += dwSizeRounding;

		return;
	}

	HPI_InitResponse(phr, phm->wObject, phm->wFunction, 0);
	status = &interface->aOutStreamHostBufferStatus[phm->wObjIndex];

	dwSpaceAvailable = OutStreamGetSpaceAvailable(status);
	if (dwSpaceAvailable < phm->u.d.u.data.dwDataSize) {
		phr->wError = HPI_ERROR_INVALID_DATASIZE;
		return;
	}

	/* HostBuffers is used to indicate host buffer is internally allocated.
	   otherwise, assumed external, data written externally */
	pBbmData = HpiOs_LockedMem_GetVirtAddr
			(&phw->OutStreamHostBuffers[phm->wObjIndex]);
	if (phm->u.d.u.data.pbData && pBbmData) {
		uint32_t lFirstWrite;
		uint8_t *pAppData = (uint8_t *)phm->u.d.u.data.pbData;

		/* either all data,
		   or enough to fit from current to end of BBM buffer */
		lFirstWrite = min(phm->u.d.u.data.dwDataSize,
			status->dwSizeInBytes -
			(status->dwHostIndex & (status->dwSizeInBytes - 1)));

		memcpy(pBbmData +
			(status->dwHostIndex & (status->dwSizeInBytes
					- 1)), pAppData, lFirstWrite);
		/* remaining data if any */
		memcpy(pBbmData, pAppData + lFirstWrite,
			phm->u.d.u.data.dwDataSize - lFirstWrite);
	}

	/*
	 * This version relies on the DSP code triggering an OStream buffer
	 * update immediately following a SET_FORMAT call. The host has
	 * already written data into the BBM buffer, but the DSP won't know
	 * about it until dwHostIndex is adjusted.
	 */
	if (phw->flagOStreamJustReset[phm->wObjIndex]) {
		// Format can only change after reset. Must tell DSP.
		uint16_t wFunction = phm->wFunction;
		phw->flagOStreamJustReset[phm->wObjIndex] = 0;
		phm->wFunction = HPI_OSTREAM_SET_FORMAT;
		HwMessage(pao, phm, phr);	// send the format to the DSP
		phm->wFunction = wFunction;
		if (phr->wError)
			return;
	}

	status->dwHostIndex += phm->u.d.u.data.dwDataSize;
}

static void OutStreamGetInfo(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;
	struct hpi_hostbuffer_status *status;

	if (!phw->OutStreamHostBufferSize[phm->wObjIndex]) {
		HwMessage(pao, phm, phr);
		return;
	}

	HPI_InitResponse(phr, phm->wObject, phm->wFunction, 0);

	status = &interface->aOutStreamHostBufferStatus[phm->wObjIndex];

	phr->u.d.u.stream_info.wState = (uint16_t)status->dwStreamState;
	phr->u.d.u.stream_info.dwSamplesTransferred =
		status->dwSamplesProcessed;
	phr->u.d.u.stream_info.dwBufferSize = status->dwSizeInBytes;
	phr->u.d.u.stream_info.dwDataAvailable =
		status->dwSizeInBytes - OutStreamGetSpaceAvailable(status);
	phr->u.d.u.stream_info.dwAuxiliaryDataAvailable =
		status->dwAuxiliaryDataAvailable;
}
static void OutStreamStart(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	HwMessage(pao, phm, phr);
}
static void OutStreamReset(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
#ifndef HPI_BUILD_NO_STREAM_WAIT
	HpiOs_Event_Set(&phw->OutStreamEvent[phm->wObjIndex]);
#endif
	phw->flagOStreamJustReset[phm->wObjIndex] = 1;
	HwMessage(pao, phm, phr);
}
static void OutStreamOpen(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	OutStreamReset(pao, phm, phr);
}

#ifndef HPI_BUILD_NO_STREAM_WAIT
static void OutStreamWait(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;
	struct hpi_hostbuffer_status *status;

	HPI_InitResponse(phr, phm->wObject, phm->wFunction, 0);
	if (!phw->OutStreamHostBufferSize[phm->wObjIndex]) {
		phr->wError = HPI_ERROR_HOST_BUFFER_NOT_ALLOCATED;
		return;
	}
	status = &interface->aOutStreamHostBufferStatus[phm->wObjIndex];
	phw->OutStreamThreshold[phm->wObjIndex] = phm->u.d.u.dwThresholdBytes;
	if ( !OutStreamThresholdReached(status,
		phw->OutStreamThreshold[phm->wObjIndex]) ){
			HpiOs_Event_Wait(&phw->OutStreamEvent[phm->wObjIndex]);
			HpiOs_Event_Clear(&phw->OutStreamEvent[phm->wObjIndex]);
			if ( !OutStreamThresholdReached(status,
				phw->OutStreamThreshold[phm->wObjIndex]) ){
					phr->wError = HPI_ERROR_WAIT_CANCELLED;
			}
	}
	phw->OutStreamThreshold[phm->wObjIndex] = 0;
}
#endif
/*****************************************************************************/
/* InStream Host buffer functions */

static void InStreamHostBufferAllocate(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	uint32_t dwCommand = phm->u.d.u.buffer.dwCommand;
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;

	HPI_InitResponse(phr, phm->wObject, phm->wFunction, 0);

	if (dwCommand == HPI_BUFFER_CMD_EXTERNAL ||
	    dwCommand == HPI_BUFFER_CMD_INTERNAL_ALLOC) {

		phm->u.d.u.buffer.dwBufferSize =
			roundup_pow_of_two(phm->u.d.u.buffer.dwBufferSize);
		phr->u.d.u.stream_info.dwDataAvailable =
			phw->InStreamHostBufferSize[phm->wObjIndex];
		phr->u.d.u.stream_info.dwBufferSize =
			phm->u.d.u.buffer.dwBufferSize;

		if (phw->InStreamHostBufferSize[phm->wObjIndex] ==
			phm->u.d.u.buffer.dwBufferSize) {
			/* Same size, no action required */
			return;
		}

		HpiOs_LockedMem_Free(&phw->
				InStreamHostBuffers[phm->wObjIndex]);

		if (!HpiOs_LockedMem_Alloc(&phw->
			InStreamHostBuffers[phm->wObjIndex],
			phm->u.d.u.buffer.dwBufferSize, &pao->os,
			&phm->u.d.u.buffer.dwPciAddress))
		{
			phr->wError = HPI_ERROR_INVALID_DATASIZE;
			phw->InStreamHostBufferSize[phm->wObjIndex] = 0;
			return;
		}

		/* get the phys addr into msg for single call alloc. Caller
		   needs to do this for split alloc so return the phy address */
		phr->u.d.u.stream_info.dwAuxiliaryDataAvailable =
			phm->u.d.u.buffer.dwPciAddress;
	}

	if (dwCommand == HPI_BUFFER_CMD_EXTERNAL ||
		dwCommand == HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER) {
		struct hpi_hostbuffer_status *status;

		if (phm->u.d.u.buffer.
			dwBufferSize & (phm->u.d.u.buffer.dwBufferSize - 1)) {
			HPI_DEBUG_LOG1(ERROR,
				"Buffer size must be 2^N not %d\n",
				phm->u.d.u.buffer.dwBufferSize);
			phr->wError = HPI_ERROR_INVALID_DATASIZE;
			return;
		}

		phw->InStreamHostBufferSize[phm->wObjIndex] =
			phm->u.d.u.buffer.dwBufferSize;
		status = &interface->aInStreamHostBufferStatus[phm->wObjIndex];
		status->dwSamplesProcessed = 0;
		status->dwStreamState = HPI_STATE_STOPPED;
		status->dwDspIndex = 0;
		status->dwHostIndex = status->dwDspIndex;
		status->dwSizeInBytes = phm->u.d.u.buffer.dwBufferSize;
		status->dwAuxiliaryDataAvailable = 0;

		HwMessage(pao, phm, phr);

		if (phr->wError) {
			HpiOs_LockedMem_Free(&phw->
				InStreamHostBuffers[phm->wObjIndex]);
			phw->InStreamHostBufferSize[phm->wObjIndex] = 0;
		}
	}
}

static void InStreamHostBufferGetInfo(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;
	struct hpi_hostbuffer_status *status;
	uint8_t *pBbmData;


	pBbmData = HpiOs_LockedMem_GetVirtAddr
			(&phw->InStreamHostBuffers[phm->wObjIndex]);
	if (pBbmData) {
		status = &interface->aInStreamHostBufferStatus[phm->wObjIndex];
		HPI_InitResponse(phr, HPI_OBJ_ISTREAM,
			HPI_ISTREAM_HOSTBUFFER_GET_INFO, 0);
		phr->u.d.u.hostbuffer_info.pBuffer = pBbmData;
		phr->u.d.u.hostbuffer_info.pStatus = status;
	}else{
		HPI_InitResponse(phr, HPI_OBJ_ISTREAM,
			HPI_ISTREAM_HOSTBUFFER_GET_INFO, HPI_ERROR_HOST_BUFFER_NOT_ALLOCATED);
	}
}
static void InStreamHostBufferFree(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	uint32_t dwCommand = phm->u.d.u.buffer.dwCommand;

	if (phw->InStreamHostBufferSize[phm->wObjIndex]) {
		if (dwCommand == HPI_BUFFER_CMD_EXTERNAL ||
		    dwCommand == HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER) {
			phw->InStreamHostBufferSize[phm->wObjIndex] = 0;
			HwMessage(pao, phm, phr);
		}

		if (dwCommand == HPI_BUFFER_CMD_EXTERNAL ||
		    dwCommand == HPI_BUFFER_CMD_INTERNAL_FREE)
			HpiOs_LockedMem_Free(&phw->
				InStreamHostBuffers[phm->wObjIndex]);

	} else {
		/* Should HPI_ERROR_INVALID_OPERATION be returned
		   if no host buffer is allocated? */
		HPI_InitResponse(phr, HPI_OBJ_ISTREAM,
			HPI_ISTREAM_HOSTBUFFER_FREE, HPI_ERROR_HOST_BUFFER_NOT_ALLOCATED);

	}

}

#ifdef HPI_OS_DELETE
short nValue = 0;
#endif

static void InStreamStart(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	HwMessage(pao, phm, phr);
}

static void InStreamReset(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
#ifndef HPI_BUILD_NO_STREAM_WAIT
	struct hpi_hw_obj_62 *phw = pao->priv;
	HpiOs_Event_Set(&phw->InStreamEvent[phm->wObjIndex]);
#endif
	HwMessage(pao, phm, phr);
}

static uint32_t InStreamGetBytesAvailable(
	struct hpi_hostbuffer_status * status
)
{
	return status->dwDspIndex - status->dwHostIndex;
}

#ifndef HPI_BUILD_NO_STREAM_WAIT
static uint32_t InStreamThresholdReached(
	struct hpi_hostbuffer_status * status,
	uint32_t threshold
)
{
	return InStreamGetBytesAvailable(status) >=
		threshold;
}
#endif

static void InStreamRead(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;
	struct hpi_hostbuffer_status *status;
	uint32_t dwDataAvailable;
	uint8_t *pBbmData;
	uint32_t lFirstRead;
	uint8_t *pAppData = (uint8_t *)phm->u.d.u.data.pbData;

	if (!phw->InStreamHostBufferSize[phm->wObjIndex]) {
		HwMessage(pao, phm, phr);
		return;
	}
	HPI_InitResponse(phr, phm->wObject, phm->wFunction, 0);

	status = &interface->aInStreamHostBufferStatus[phm->wObjIndex];
	dwDataAvailable = InStreamGetBytesAvailable(status);
	if (dwDataAvailable < phm->u.d.u.data.dwDataSize) {
		phr->wError = HPI_ERROR_INVALID_DATASIZE;
		return;
	}

	pBbmData = HpiOs_LockedMem_GetVirtAddr
			(&phw->InStreamHostBuffers[phm->wObjIndex]);
	if (pBbmData) {
		/* either all data,
		   or enough to fit from current to end of BBM buffer */
		lFirstRead = min(phm->u.d.u.data.dwDataSize,
			status->dwSizeInBytes -
			(status->dwHostIndex & (status->dwSizeInBytes - 1)));

		memcpy(pAppData, pBbmData +
			(status->dwHostIndex & (status->dwSizeInBytes
					- 1)), lFirstRead);
		/* remaining data if any */
		memcpy(pAppData + lFirstRead,
			pBbmData, phm->u.d.u.data.dwDataSize - lFirstRead);
	}
	status->dwHostIndex += phm->u.d.u.data.dwDataSize;
#ifdef HPI_OS_DELETE
	{
		/* DEBUG */
		int i;
		long *pTest;

		pTest = (long *)phm->u.d.u.data.dwpbData;
		for (i = 0; i < phm->u.d.u.data.dwDataSize / sizeof(long);
			i++) {
			if (pTest[i])
				pTest[i]--;
		}
		pTest = (long *)((char *)buffer) +
			sizeof(struct hpi_hostbuffer_status);
		for (i = 0; i < buffer->dwSizeInBytes / sizeof(long); i++) {
			if (pTest[i])
				pTest[i]--;
		}
	}
#endif
}

static void InStreamGetInfo(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;
	struct hpi_hostbuffer_status *status;
	if (!phw->InStreamHostBufferSize[phm->wObjIndex]) {
		HwMessage(pao, phm, phr);
		return;
	}

	status = &interface->aInStreamHostBufferStatus[phm->wObjIndex];

	HPI_InitResponse(phr, phm->wObject, phm->wFunction, 0);

	phr->u.d.u.stream_info.wState = (uint16_t)status->dwStreamState;
	phr->u.d.u.stream_info.dwSamplesTransferred =
		status->dwSamplesProcessed;
	phr->u.d.u.stream_info.dwBufferSize = status->dwSizeInBytes;
	phr->u.d.u.stream_info.dwDataAvailable =
		InStreamGetBytesAvailable(status);
	phr->u.d.u.stream_info.dwAuxiliaryDataAvailable =
		status->dwAuxiliaryDataAvailable;
}

#ifndef HPI_BUILD_NO_STREAM_WAIT
static void InStreamWait(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;
	struct hpi_hostbuffer_status *status;
	HPI_InitResponse(phr, phm->wObject, phm->wFunction, 0);
	if (!phw->InStreamHostBufferSize[phm->wObjIndex] ) {
		phr->wError = HPI_ERROR_INVALID_OPERATION;
		return;
	}
	status = &interface->aInStreamHostBufferStatus[phm->wObjIndex];
	phw->InStreamThreshold[phm->wObjIndex] = phm->u.d.u.dwThresholdBytes;
	if ( !InStreamThresholdReached(status,
		phw->InStreamThreshold[phm->wObjIndex]) ){
			HpiOs_Event_Wait(&phw->InStreamEvent[phm->wObjIndex]);
			HpiOs_Event_Clear(&phw->InStreamEvent[phm->wObjIndex]);
			if ( !InStreamThresholdReached(status,
				phw->InStreamThreshold[phm->wObjIndex]) ){
					phr->wError = HPI_ERROR_WAIT_CANCELLED;
			}
	}
	phw->InStreamThreshold[phm->wObjIndex] = 0;
}
#endif

/*****************************************************************************/
/* LOW-LEVEL */
#define HPI6205_MAX_FILES_TO_LOAD 2

static hpi_err_t AdapterBootLoadDsp(struct hpi_adapter_obj *pao)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct dsp_code DspCode;
	uint16_t aBootCodeId[HPI6205_MAX_FILES_TO_LOAD];
	uint32_t dwTemp;
	int nDsp = 0;
	int i;
	hpi_err_t err = 0;

	aBootCodeId[0] = HPI_ADAPTER_ASI(0x6205);

	aBootCodeId[1] = HPIOS_PCI_SUBSYS_DEVICE(pao);
	aBootCodeId[1] = HPI_ADAPTER_FAMILY_ASI(aBootCodeId[1]);

	/* fix up cases where bootcode id[1] != subsys id */
	switch (aBootCodeId[1]) {
	case HPI_ADAPTER_FAMILY_ASI(0x5000):
		aBootCodeId[0] = aBootCodeId[1];
		aBootCodeId[1] = 0;
		break;
	case HPI_ADAPTER_FAMILY_ASI(0x5300):
	case HPI_ADAPTER_FAMILY_ASI(0x5400):
	case HPI_ADAPTER_FAMILY_ASI(0x6300):
		aBootCodeId[1] = HPI_ADAPTER_FAMILY_ASI(0x6400);
		break;
	case HPI_ADAPTER_FAMILY_ASI(0x5500):
	case HPI_ADAPTER_FAMILY_ASI(0x5600):
	case HPI_ADAPTER_FAMILY_ASI(0x6500):
		aBootCodeId[1] = HPI_ADAPTER_FAMILY_ASI(0x6600);
		break;
	case HPI_ADAPTER_FAMILY_ASI(0x8800):
		aBootCodeId[1] = HPI_ADAPTER_FAMILY_ASI(0x8900);
		break;
	default:
		break;
	}

	// reset DSP by writing a 1 to the WARMRESET bit
	dwTemp = C6205_HDCR_WARMRESET;
	HPIOS_MEMWRITE32(phw->prHDCR, dwTemp);
	HpiOs_DelayMicroSeconds(1000);

	// check that PCI i/f was configured by EEPROM
	dwTemp = HPIOS_MEMREAD32(phw->prHSR);
	if ((dwTemp & (C6205_HSR_CFGERR | C6205_HSR_EEREAD)) !=
		C6205_HSR_EEREAD) {
		HPI_DEBUG_LOG0(ERROR, "6205 EEPROM error\n");
		return HPI_ERROR_DSP_0;
	}
	dwTemp |= 0x04;
	// disable PINTA interrupt
	HPIOS_MEMWRITE32(phw->prHSR, dwTemp);

	// check control register reports PCI boot mode
	dwTemp = HPIOS_MEMREAD32(phw->prHDCR);
	if (!(dwTemp & C6205_HDCR_PCIBOOT)) {
		HPI_DEBUG_LOG0(ERROR, "HDCR not in PCI boot mode\n");
		return HPI_ERROR_DSP_0;
	}
	// try writing a few numbers to the DSP page register
	// and reading them back.
	i = 0;
	dwTemp = 3;
	HPIOS_MEMWRITE32(phw->prDSPP, dwTemp);
	if ((dwTemp | C6205_DSPP_MAP1) != HPIOS_MEMREAD32(phw->prDSPP))
		i++;
	dwTemp = 2;
	HPIOS_MEMWRITE32(phw->prDSPP, dwTemp);
	if ((dwTemp | C6205_DSPP_MAP1) != HPIOS_MEMREAD32(phw->prDSPP))
		i++;
	dwTemp = 1;
	HPIOS_MEMWRITE32(phw->prDSPP, dwTemp);
	if ((dwTemp | C6205_DSPP_MAP1) != HPIOS_MEMREAD32(phw->prDSPP))
		i++;
	// reset DSP page to the correct number
	dwTemp = 0;
	HPIOS_MEMWRITE32(phw->prDSPP, dwTemp);
	if ((dwTemp | C6205_DSPP_MAP1) != HPIOS_MEMREAD32(phw->prDSPP))
		i++;

	if (i) {
		HPI_DEBUG_LOG0(ERROR, "DSP page register write failure\n");
		return HPI_ERROR_DSP_0;
	}

	phw->dwDspPage = 0;

	/* release 6713 from reset before 6205 is bootloaded.
	   This ensures that the EMIF is inactive,
	   and the 6713 HPI gets the correct bootmode etc
	 */
	if (aBootCodeId[1] != 0) {
		// DSP 1 is a C6713
		// CLKX0 <- '1' release the C6205 bootmode pulldowns
		BootLoader_WriteMem32(pao, 0, 0x018C0024, 0x00002202);
		HpiOs_DelayMicroSeconds(100);
		// Reset the 6713 #1 - revB
		BootLoader_WriteMem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0);
		/* value of bit 3 is unknown after DSP reset, other bits shoudl be 0 */
		if (0 != (BootLoader_ReadMem32(pao, 0, (C6205_BAR0_TIMER1_CTL)) & ~8)) {
			HPI_DEBUG_LOG0(ERROR, "DSP TIMER1_CTL incorrect\n");
			return HPI_ERROR_DSP_0;
		}
		HpiOs_DelayMicroSeconds(100);

		// Release C6713 from reset - revB
		BootLoader_WriteMem32(pao, 0, C6205_BAR0_TIMER1_CTL, 4);
		if (4 != (BootLoader_ReadMem32(pao, 0, (C6205_BAR0_TIMER1_CTL)) & ~8)) {
			HPI_DEBUG_LOG0(ERROR, "DSP TIMER1_CTL incorrect\n");
			return HPI_ERROR_DSP_0;
		}
		HpiOs_DelayMicroSeconds(100);
	}

	for (nDsp = 0; nDsp < HPI6205_MAX_FILES_TO_LOAD; nDsp++) {
		if (!aBootCodeId[nDsp]) /* No more DSPs to load */
			break;

		err = BootLoader_ConfigEmif(pao, nDsp);
		if (!err)
			err = BootLoader_TestInternalMemory(pao, nDsp);
		if (!err)
			err = BootLoader_TestExternalMemory(pao, nDsp);
		if (!err)
			err = BootLoader_TestPld(pao, nDsp);
		if (!err)
			err = HpiDspCode_Open(aBootCodeId[nDsp],
					      &pao->os, &DspCode);
		if (err)
			return err;

		/* write the DSP code into the DSPs memory */
		while (1) {
			uint32_t dwLength;
			uint32_t dwAddress;
			uint32_t dwType;
			uint32_t *pdwCode = NULL;

			err = HpiDspCode_ReadWord(&DspCode, &dwLength);
			if (err)
				break;
			if (dwLength == 0xFFFFFFFF)
				break;	// end of code

			err = HpiDspCode_ReadWord(&DspCode, &dwAddress);
			if (!err)
				err = HpiDspCode_ReadWord(&DspCode, &dwType);
			if (!err)
				err = HpiDspCode_ReadBlock(dwLength, &DspCode,
							   &pdwCode);
			if (err)
				break;
			for (i = 0; i < (int)dwLength; i++) {
				BootLoader_WriteMem32(pao, nDsp,
					dwAddress, *pdwCode);
				// dummy read every 4 words
				// for 6205 advisory 1.4.4
				if (i % 4 == 0)
					BootLoader_ReadMem32(pao, nDsp,
						dwAddress);
				pdwCode++;
				dwAddress += 4;
			}

		}
		if (err) {
			HpiDspCode_Close(&DspCode);
			return err;
		}

		/* verify code */
		HpiDspCode_Rewind(&DspCode);
		while (1) {
			uint32_t dwLength = 0;
			uint32_t dwAddress = 0;
			uint32_t dwType = 0;
			uint32_t *pdwCode = NULL;
			uint32_t dwData = 0;

			HpiDspCode_ReadWord(&DspCode, &dwLength);
			if (dwLength == 0xFFFFFFFF)
				break;	// end of code

			HpiDspCode_ReadWord(&DspCode, &dwAddress);
			HpiDspCode_ReadWord(&DspCode, &dwType);
			HpiDspCode_ReadBlock(dwLength, &DspCode, &pdwCode);

			for (i = 0; i < (int)dwLength; i++) {
				dwData = BootLoader_ReadMem32(pao, nDsp,
					dwAddress);
				if (dwData != *pdwCode) {
					err = 0;
					break;
				}
				pdwCode++;
				dwAddress += 4;
			}
			if (err)
				break;
		}
		HpiDspCode_Close(&DspCode);
		if (err)
			return err;
	}
	{	/* Allocate a DMA buffer for DSP interface,
		   and tell DSP its physical PCI address */
		uint32_t dwHostMailboxAddressOnDsp;
		uint32_t dwPhysicalPCIaddress;
		uint32_t dwPhysicalPCIaddressVerify;
		int nTimeOut = 10;

		phw->pInterfaceBuffer =	HpiOs_LockedMem_Alloc(&phw->hLockedMem,
					sizeof(struct bus_master_interface_62),
					&pao->os, &dwPhysicalPCIaddress);

		if (!phw->pInterfaceBuffer)
			return HPI_ERROR_MEMORY_ALLOC;

		memset(phw->pInterfaceBuffer, 0, sizeof(*phw->pInterfaceBuffer));
		phw->pInterfaceBuffer->dwDspAck =  H620_HIF_UNKNOWN;
		HPI_DEBUG_LOG1(DEBUG, "Interface buffer address %p\n",
				phw->pInterfaceBuffer);

		/* set ack so we know when DSP is ready to go
		   (dwDspAck will be changed to HIF_RESET) */
		phw->pInterfaceBuffer->dwDspAck = H620_HIF_UNKNOWN;
		WMB(); /* ensure ack is written before dsp writes back */

		/* locate the host mailbox on the DSP. */
		dwHostMailboxAddressOnDsp = 0x80000000;
		do {
			BootLoader_WriteMem32(pao, 0,
				dwHostMailboxAddressOnDsp,
				dwPhysicalPCIaddress);
			dwPhysicalPCIaddressVerify = BootLoader_ReadMem32(pao,
						0, dwHostMailboxAddressOnDsp);
		} while ((dwPhysicalPCIaddress != dwPhysicalPCIaddressVerify) && nTimeOut--);
	}
	/* After bootloading all DSPs, start DSP0 running
	 * The DSP0 code will handle starting and synchronizing with its slaves
	 */
	HPI_DEBUG_LOG0(DEBUG, "Starting DSPs running\n");
	// enable interrupts
	dwTemp = HPIOS_MEMREAD32(phw->prHSR);
	dwTemp &= ~(uint32_t)C6205_HSR_INTAM;
	HPIOS_MEMWRITE32(phw->prHSR, dwTemp);

	// start code running...
	dwTemp = HPIOS_MEMREAD32(phw->prHDCR);
	dwTemp |= (uint32_t)C6205_HDCR_DSPINT;
	HPIOS_MEMWRITE32(phw->prHDCR, dwTemp);

	// give the DSP 10ms to start up
	HpiOs_DelayMicroSeconds(10 * 1000);
	return err;
}

/*****************************************************************************/
/* Bootloader utility functions */

static uint32_t BootLoader_ReadMem32(
	struct hpi_adapter_obj *pao,
	int nDspIndex,
	uint32_t dwAddress
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	uint32_t dwData = 0;
	__iomem uint32_t *pData;

	if (nDspIndex == 0) {
		/* DSP 0 is always C6205 */
		if ((dwAddress >= 0x01800000) & (dwAddress < 0x02000000)) {
			// BAR1 register access
			pData = phw->bar[1] +
				(dwAddress & 0x007fffff) /
				sizeof(*phw->bar[1]);
		} else {
			uint32_t dw4MPage = dwAddress >> 22L;
			if (dw4MPage != phw->dwDspPage) {
				phw->dwDspPage = dw4MPage;
				/* *INDENT OFF* */
				HPIOS_MEMWRITE32(phw->prDSPP, phw->dwDspPage);
				/* *INDENT-ON* */
			}
			dwAddress &= 0x3fffff;	// address within 4M page
			// BAR0 memory access
			pData = phw->bar[0] +
				dwAddress / sizeof(uint32_t);
		}
		dwData = HPIOS_MEMREAD32(pData);
	} else if (nDspIndex == 1) {
		/* DSP 1 is a C6713 */
		uint32_t dwLsb;
		BootLoader_WriteMem32(pao, 0, HPIAL_ADDR, dwAddress);
		BootLoader_WriteMem32(pao, 0, HPIAH_ADDR, dwAddress >> 16);
		dwLsb = BootLoader_ReadMem32(pao, 0, HPIDL_ADDR);
		dwData = BootLoader_ReadMem32(pao, 0, HPIDH_ADDR);
		dwData = (dwData << 16) | (dwLsb & 0xFFFF);
	}
	return dwData;
}
static void BootLoader_WriteMem32(
	struct hpi_adapter_obj *pao,
	int nDspIndex,
	uint32_t dwAddress,
	uint32_t dwData
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	__iomem uint32_t *pData;
	//      uint32_t dwVerifyData=0;

	if (nDspIndex == 0) {
		// DSP 0 is always C6205
		if ((dwAddress >= 0x01800000) & (dwAddress < 0x02000000)) {
			// BAR1 - DSP  register access using
			// Non-prefetchable PCI access
			pData = phw->bar[1] +
				(dwAddress & 0x007fffff) /
				sizeof(*phw->bar[1]);
		} else {
			// BAR0 access - all of DSP memory using
			// pre-fetchable PCI access
			uint32_t dw4MPage = dwAddress >> 22L;
			if (dw4MPage != phw->dwDspPage) {
				phw->dwDspPage = dw4MPage;
				/* *INDENT-OFF* */
				HPIOS_MEMWRITE32(phw->prDSPP, phw->dwDspPage);
				/* *INDENT-ON* */
			}
			dwAddress &= 0x3fffff;	// address within 4M page
			pData = phw->bar[0] +
				dwAddress / sizeof(uint32_t);
		}
		HPIOS_MEMWRITE32(pData, dwData);
	} else if (nDspIndex == 1) {
		// DSP 1 is a C6713
		BootLoader_WriteMem32(pao, 0, HPIAL_ADDR, dwAddress);
		BootLoader_WriteMem32(pao, 0, HPIAH_ADDR, dwAddress >> 16);

		// dummy read every 4 words for 6205 advisory 1.4.4
		BootLoader_ReadMem32(pao, 0, 0);

		BootLoader_WriteMem32(pao, 0, HPIDL_ADDR, dwData);
		BootLoader_WriteMem32(pao, 0, HPIDH_ADDR, dwData >> 16);

		// dummy read every 4 words for 6205 advisory 1.4.4
		BootLoader_ReadMem32(pao, 0, 0);
	}
}

static uint16_t BootLoader_ConfigEmif(
	struct hpi_adapter_obj *pao,
	int nDspIndex
)
{
	if (nDspIndex == 0) {
		uint32_t dwSetting;

		// DSP 0 is always C6205

		// Set the EMIF
		// memory map of C6205
		// 00000000-0000FFFF    16Kx32 internal program
		// 00400000-00BFFFFF    CE0     2Mx32 SDRAM running @ 100MHz

		// EMIF config
		//------------
		// Global EMIF control
		BootLoader_WriteMem32(pao, nDspIndex, 0x01800000, 0x3779);
#define WS_OFS 28
#define WST_OFS 22
#define WH_OFS 20
#define RS_OFS 16
#define RST_OFS 8
#define MTYPE_OFS 4
#define RH_OFS 0

		// EMIF CE0 setup - 2Mx32 Sync DRAM on ASI5000 cards only
		dwSetting = 0x00000030;
		BootLoader_WriteMem32(pao, nDspIndex, 0x01800008, dwSetting);
		if (dwSetting != BootLoader_ReadMem32(pao, nDspIndex,
				0x01800008)) {
			HPI_DEBUG_LOG0(ERROR, "DSP EMIF0 setup failed\n");
			return HPI_ERROR_DSP_0;
		}

		// EMIF CE1 setup - 32 bit async. This is 6713 #1 HPI,
		// which occupies D15..0. 6713 starts at 27MHz, so need
		// plenty of wait states. See dsn8701.rtf, and 6713 errata.
		// WST should be 71, but 63  is max possible
		dwSetting = (1L << WS_OFS) | (63L << WST_OFS) |
			(1L << WH_OFS) | (1L << RS_OFS) |
			(63L << RST_OFS) | (1L << RH_OFS) | (2L << MTYPE_OFS);
		BootLoader_WriteMem32(pao, nDspIndex, 0x01800004, dwSetting);
		if (dwSetting != BootLoader_ReadMem32(pao, nDspIndex,
				0x01800004)) {
			HPI_DEBUG_LOG0(ERROR, "DSP EMIF1 setup failed\n");
			return HPI_ERROR_DSP_0;
		}

		// EMIF CE2 setup - 32 bit async. This is 6713 #2 HPI,
		// which occupies D15..0. 6713 starts at 27MHz, so need
		// plenty of wait states
		dwSetting = (1L << WS_OFS) |
			(28L << WST_OFS) |
			(1L << WH_OFS) |
			(1L << RS_OFS) |
			(63L << RST_OFS) | (1L << RH_OFS) | (2L << MTYPE_OFS);
		BootLoader_WriteMem32(pao, nDspIndex, 0x01800010, dwSetting);
		if (dwSetting != BootLoader_ReadMem32(pao, nDspIndex,
				0x01800010)) {
			HPI_DEBUG_LOG0(ERROR, "DSP EMIF2 setup failed\n");
			return HPI_ERROR_DSP_0;
		}

		// EMIF CE3 setup - 32 bit async.
		// This is the PLD on the ASI5000 cards only
		dwSetting = (1L << WS_OFS) |
			(10L << WST_OFS) |
			(1L << WH_OFS) |
			(1L << RS_OFS) |
			(10L << RST_OFS) | (1L << RH_OFS) | (2L << MTYPE_OFS);
		BootLoader_WriteMem32(pao, nDspIndex, 0x01800014, dwSetting);
		if (dwSetting != BootLoader_ReadMem32(pao, nDspIndex,
				0x01800014)) {
			HPI_DEBUG_LOG0(ERROR, "DSP EMIF3 setup failed\n");
			return HPI_ERROR_DSP_0;
		}

		// set EMIF SDRAM control for 2Mx32 SDRAM (512x32x4 bank)
		//  need to use this else DSP code crashes?
		BootLoader_WriteMem32(pao, nDspIndex, 0x01800018, 0x07117000);

		// EMIF SDRAM Refresh Timing
		// EMIF SDRAM timing  (orig = 0x410, emulator = 0x61a)
		BootLoader_WriteMem32(pao, nDspIndex, 0x0180001C, 0x00000410);

	} else if (nDspIndex == 1) {
		// test access to the C6713s HPI registers
		uint32_t dwWriteData = 0, dwReadData = 0, i = 0;

		// Set up HPIC for little endian, by setiing HPIC:HWOB=1
		dwWriteData = 1;
		BootLoader_WriteMem32(pao, 0, HPICL_ADDR, dwWriteData);
		BootLoader_WriteMem32(pao, 0, HPICH_ADDR, dwWriteData);
		// C67 HPI is on lower 16bits of 32bit EMIF
		dwReadData = 0xFFF7 & BootLoader_ReadMem32(pao, 0,
			HPICL_ADDR);
		if (dwWriteData != dwReadData) {
			HPI_DEBUG_LOG2(ERROR, "HPICL %x != %x\n",
				dwWriteData, dwReadData);
			return HPI_ERROR_DSP_1;
		}
		// HPIA - walking ones test
		dwWriteData = 1;
		for (i = 0; i < 32; i++) {
			BootLoader_WriteMem32(pao, 0, HPIAL_ADDR,
				dwWriteData);
			BootLoader_WriteMem32(pao, 0, HPIAH_ADDR,
				(dwWriteData >> 16));
			dwReadData = 0xFFFF & BootLoader_ReadMem32(pao, 0,
				HPIAL_ADDR);
			dwReadData = dwReadData |
				((0xFFFF & BootLoader_ReadMem32(pao,
						0, HPIAH_ADDR))
				<< 16);
			if (dwReadData != dwWriteData) {
				HPI_DEBUG_LOG2(ERROR,"HPIA %x != %x\n",
					dwWriteData, dwReadData);
				return HPI_ERROR_DSP_1;
			}
			dwWriteData = dwWriteData << 1;
		}

		/* setup C67x PLL
		 *  ** C6713 datasheet says we cannot program PLL from HPI,
		 * and indeed if we try to set the PLL multiply from the HPI,
		 * the PLL does not seem to lock, so we enable the PLL and
		 * use the default multiply of x 7, which for a 27MHz clock
		 * gives a DSP speed of 189MHz
		 */
		// bypass PLL
		BootLoader_WriteMem32(pao, nDspIndex, 0x01B7C100, 0x0000);
		HpiOs_DelayMicroSeconds(1000);
		// EMIF = 189/3=63MHz
		BootLoader_WriteMem32(pao, nDspIndex, 0x01B7C120, 0x8002);
		// peri = 189/2
		BootLoader_WriteMem32(pao, nDspIndex, 0x01B7C11C, 0x8001);
		// cpu  = 189/1
		BootLoader_WriteMem32(pao, nDspIndex, 0x01B7C118, 0x8000);
		HpiOs_DelayMicroSeconds(1000);
		// ** SGT test to take GPO3 high when we start the PLL
		// and low when the delay is completed
		// FSX0 <- '1' (GPO3)
		BootLoader_WriteMem32(pao, 0, (0x018C0024L), 0x00002A0A);
		// PLL not bypassed
		BootLoader_WriteMem32(pao, nDspIndex, 0x01B7C100, 0x0001);
		HpiOs_DelayMicroSeconds(1000);
		// FSX0 <- '0' (GPO3)
		BootLoader_WriteMem32(pao, 0, (0x018C0024L), 0x00002A02);

		// 6205 EMIF CE1 resetup - 32 bit async.
		// Now 6713 #1 is running at 189MHz can reduce waitstates
		BootLoader_WriteMem32(pao, 0, 0x01800004,	// CE1
			(1L << WS_OFS) |
			(8L << WST_OFS) |
			(1L << WH_OFS) |
			(1L << RS_OFS) |
			(12L << RST_OFS) |
			(1L << RH_OFS) | (2L << MTYPE_OFS));

		HpiOs_DelayMicroSeconds(1000);

		// check that we can read one of the PLL registers
		// PLL should not be bypassed!
		if ((BootLoader_ReadMem32(pao, nDspIndex, 0x01B7C100) & 0xF)
			!= 0x0001) {
			HPI_DEBUG_LOG0(ERROR, "DSP PLL setting incorrect\n");
			return HPI_ERROR_DSP_1;
		}
		/* setup C67x EMIF  (note this is the only use of
		BAR1 via BootLoader_WriteMem32) */
		BootLoader_WriteMem32(pao, nDspIndex, C6713_EMIF_GCTL,
			0x000034A8);

		/* EMIF CE0 setup - 2Mx32 Sync DRAM
		   31..28       Wr setup
		   27..22       Wr strobe
		   21..20       Wr hold
		   19..16       Rd setup
		   15..14       -
		   13..8        Rd strobe
		   7..4         MTYPE   0011            Sync DRAM 32bits
		   3            Wr hold MSB
		   2..0         Rd hold
		 */
		BootLoader_WriteMem32(pao, nDspIndex, C6713_EMIF_CE0,
			0x00000030);

		/* EMIF SDRAM Extension
		   0x00
		   31-21        0000b 0000b 000b
		   20           WR2RD = 2cycles-1  = 1b

		   19-18        WR2DEAC = 3cycle-1 = 10b
		   17           WR2WR = 2cycle-1   = 1b
		   16-15        R2WDQM = 4cycle-1  = 11b
		   14-12        RD2WR = 6cycles-1  = 101b

		   11-10        RD2DEAC = 4cycle-1 = 11b
		   9            RD2RD = 2cycle-1   = 1b
		   8-7          THZP = 3cycle-1    = 10b
		   6-5          TWR  = 2cycle-1    = 01b (tWR = 17ns)
		   4            TRRD = 2cycle      = 0b  (tRRD = 14ns)
		   3-1          TRAS = 5cycle-1    = 100b (Tras=42ns)
		   1            CAS latency = 3cyc = 1b
		   (for Micron 2M32-7 operating at 100MHz)
		 */
		BootLoader_WriteMem32(pao, nDspIndex, C6713_EMIF_SDRAMEXT,
			0x001BDF29);

		/* EMIF SDRAM control - set up for a 2Mx32 SDRAM (512x32x4 bank)
		   31           -       0b       -
		   30           SDBSZ   1b              4 bank
		   29..28       SDRSZ   00b             11 row address pins

		   27..26       SDCSZ   01b             8 column address pins
		   25           RFEN    1b              refersh enabled
		   24           INIT    1b              init SDRAM!

		   23..20       TRCD    0001b			(Trcd/Tcyc)-1 = (20/10)-1 = 1

		   19..16       TRP     0001b			(Trp/Tcyc)-1 = (20/10)-1 = 1

		   15..12       TRC     0110b			(Trc/Tcyc)-1 = (70/10)-1 = 6

		   11..0        -       0000b 0000b 0000b
		 */
		BootLoader_WriteMem32(pao, nDspIndex, C6713_EMIF_SDRAMCTL,
			0x47116000);

		/* SDRAM refresh timing
		Need 4,096 refresh cycles every 64ms = 15.625us = 1562cycles of 100MHz = 0x61A
		*/
		BootLoader_WriteMem32(pao, nDspIndex, C6713_EMIF_SDRAMTIMING,
			0x00000410);

		HpiOs_DelayMicroSeconds(1000);
	} else if (nDspIndex == 2) {
		// DSP 2 is a C6713
	}

	return 0;
}

static uint16_t BootLoader_TestMemory(
	struct hpi_adapter_obj *pao,
	int nDspIndex,
	uint32_t dwStartAddress,
	uint32_t dwLength
)
{
	uint32_t i = 0, j = 0;
	uint32_t dwTestAddr = 0;
	uint32_t dwTestData = 0, dwData = 0;

	dwLength = 1000;

	// for 1st word, test each bit in the 32bit word,
	// dwLength specifies number of 32bit words to test
	//for(i=0; i<dwLength; i++)
	i = 0;
	{
		dwTestAddr = dwStartAddress + i * 4;
		dwTestData = 0x00000001;
		for (j = 0; j < 32; j++) {
			BootLoader_WriteMem32(pao, nDspIndex, dwTestAddr,
				dwTestData);
			dwData = BootLoader_ReadMem32(pao, nDspIndex,
				dwTestAddr);
			if (dwData != dwTestData) {
				HPI_DEBUG_LOG4(VERBOSE,
					"Memtest error details  "
					"%08x %08x %08x %i\n",
					dwTestAddr, dwTestData,
					dwData, nDspIndex);
				return 1;	// error
			}
			dwTestData = dwTestData << 1;
		}		// for(j)
	}			// for(i)

	// for the next 100 locations test each location, leaving it as zero
	// write a zero to the next word in memory before we read
	// the previous write to make sure every memory location is unique
	for (i = 0; i < 100; i++) {
		dwTestAddr = dwStartAddress + i * 4;
		dwTestData = 0xA5A55A5A;
		BootLoader_WriteMem32(pao, nDspIndex, dwTestAddr, dwTestData);
		BootLoader_WriteMem32(pao, nDspIndex, dwTestAddr + 4, 0);
		dwData = BootLoader_ReadMem32(pao, nDspIndex, dwTestAddr);
		if (dwData != dwTestData) {
			HPI_DEBUG_LOG4(VERBOSE,
				"Memtest error details  "
				"%08x %08x %08x %i\n",
				dwTestAddr, dwTestData, dwData, nDspIndex);
			return 1;	// error
		}
		// leave location as zero
		BootLoader_WriteMem32(pao, nDspIndex, dwTestAddr, 0x0);
	}

	// zero out entire memory block
	for (i = 0; i < dwLength; i++) {
		dwTestAddr = dwStartAddress + i * 4;
		BootLoader_WriteMem32(pao, nDspIndex, dwTestAddr, 0x0);
	}
	return 0;
}

static uint16_t BootLoader_TestInternalMemory(
	struct hpi_adapter_obj *pao,
	int nDspIndex
)
{
	int err = 0;
	if (nDspIndex == 0) {
		// DSP 0 is a C6205
		// 64K prog mem
		err = BootLoader_TestMemory(pao, nDspIndex, 0x00000000,
			0x10000);
		if (!err)
			// 64K data mem
			err = BootLoader_TestMemory(pao, nDspIndex,
				0x80000000, 0x10000);
	} else if (nDspIndex == 1) {
		// DSP 1 is a C6713
		// 192K internal mem
		err = BootLoader_TestMemory(pao, nDspIndex, 0x00000000,
			0x30000);
		if (!err)
			// 64K internal mem / L2 cache
			err = BootLoader_TestMemory(pao, nDspIndex,
				0x00030000, 0x10000);
	}

	if (err) {
		if (nDspIndex == 0) {
			HPI_DEBUG_LOG0(ERROR, "DSP internal C6205 memory test failed\n");
			return HPI_ERROR_DSP_0;
		}else{
			HPI_DEBUG_LOG0(ERROR, "DSP internal C6713 memory test failed\n");
			return HPI_ERROR_DSP_1;
		}
	}
	return 0;
}
static uint16_t BootLoader_TestExternalMemory(
	struct hpi_adapter_obj *pao,
	int nDspIndex
)
{
	uint32_t dwDRAMStartAddress = 0;
	uint32_t dwDRAMSize = 0;

	if (nDspIndex == 0) {
		// only test for SDRAM if an ASI5000 card
		if (HPIOS_PCI_SUBSYS_DEVICE(pao) == 0x5000) {
			// DSP 0 is always C6205
			dwDRAMStartAddress = 0x00400000;
			dwDRAMSize = 0x200000;
			//dwDRAMinc=1024;
		} else
			return 0;
	} else if (nDspIndex == 1) {
		// DSP 1 is a C6713
		dwDRAMStartAddress = 0x80000000;
		dwDRAMSize = 0x200000;
		//dwDRAMinc=1024;
	}

	if (BootLoader_TestMemory(pao, nDspIndex,
				  dwDRAMStartAddress, dwDRAMSize)) 
	{
		if (nDspIndex == 0) {
			HPI_DEBUG_LOG0(ERROR, "DSP external C6205 memory test failed\n");
			return HPI_ERROR_DSP_0;
		}else{
			HPI_DEBUG_LOG0(ERROR, "DSP external C6713 memory test failed\n");
			return HPI_ERROR_DSP_1;
		}
	}
	return 0;
}

static hpi_err_t BootLoader_TestPld(
	struct hpi_adapter_obj *pao,
	int nDspIndex
)
{
	uint32_t dwData = 0;
	hpi_err_t err = 0;

	if (nDspIndex == 0) {
		/* only test for DSP0 PLD on ASI5000 card */
		if (HPIOS_PCI_SUBSYS_DEVICE(pao) == 0x5000) {
			// PLD is located at CE3=0x03000000
			dwData = BootLoader_ReadMem32(pao, nDspIndex,
				0x03000008);
			if ((dwData & 0xF) != 0x5)
				err = HPI_ERROR_DSP_0;
			dwData = BootLoader_ReadMem32(pao, nDspIndex,
				0x0300000C);
			if ((dwData & 0xF) != 0xA)
				err = HPI_ERROR_DSP_0;
		}
	} else if (nDspIndex == 1) {
		// DSP 1 is a C6713
		if (HPIOS_PCI_SUBSYS_DEVICE(pao) == 0x8700) {
			// PLD is located at CE1=0x90000000
			dwData = BootLoader_ReadMem32(pao, nDspIndex,
				0x90000010);
			if ((dwData & 0xFF) != 0xAA)
				err = HPI_ERROR_DSP_1;
			// 8713 - LED on
			BootLoader_WriteMem32(pao, nDspIndex, 0x90000000,
				0x02);
		}
	}
	if (err)
		HPI_DEBUG_LOG1(ERROR, "DSP %d PLD read failure\n", nDspIndex);
	return err;
}

/** Transfer data to or from DSP
 nOperation = H620_H620_HIF_SEND_DATA or H620_HIF_GET_DATA
*/
static short Hpi6205_TransferData(
	struct hpi_adapter_obj *pao,
	uint8_t * pData,
	uint32_t dwDataSize,
	int nOperation
)
{
	struct hpi_hw_obj_62 *phw = pao->priv;
	uint32_t dwDataTransferred = 0;
	hpi_err_t err = 0;
	uint32_t dwTemp2;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;

	if (!pData)
		return HPI_ERROR_INVALID_DATA_POINTER ;

	dwDataSize &= ~3L;	// round dwDataSize down to nearest 4 bytes

	// make sure state is IDLE
	if (!WaitDspAck(phw, H620_HIF_IDLE, HPI6205_TIMEOUT))
		return HPI_ERROR_DSP_HARDWARE;

	while (dwDataTransferred < dwDataSize) {
		uint32_t nThisCopy = dwDataSize - dwDataTransferred;

		if (nThisCopy > HPI6205_SIZEOF_DATA)
			nThisCopy = HPI6205_SIZEOF_DATA;

		if (nOperation == H620_HIF_SEND_DATA)
			memcpy((void *)&interface->u.bData[0],
				&pData[dwDataTransferred], nThisCopy);

		interface->dwTransferSizeInBytes = nThisCopy;

		// DSP must change this back to nOperation
		interface->dwDspAck = 	H620_HIF_IDLE;
		SendDspCommand(phw, nOperation);

		dwTemp2 = WaitDspAck(phw, nOperation, HPI6205_TIMEOUT);
		HPI_DEBUG_LOG2(DEBUG, "Spun %d times for data xfer of %d\n",
			HPI6205_TIMEOUT - dwTemp2, nThisCopy);

		if (!dwTemp2)  {
			// timed out
			HPI_DEBUG_LOG2(ERROR,
				"Timed out waiting for "
				"state %d got %d\n",
				nOperation, interface->dwDspAck);

			break;
		}
		if (nOperation == H620_HIF_GET_DATA)
			memcpy(&pData[dwDataTransferred],
				(void *)&interface->u.bData[0], nThisCopy);

		dwDataTransferred += nThisCopy;
	}
	if (interface->dwDspAck != nOperation)
		HPI_DEBUG_LOG2(DEBUG, "interface->dwDspAck=%d, expected %d\n",
			interface->dwDspAck, nOperation);

	SendDspCommand(phw, H620_HIF_IDLE);

	return err;
}

/* wait for up to timeout_us microseconds for the DSP
   to signal state by DMA into dwDspAck
*/
static int WaitDspAck(struct hpi_hw_obj_62 *phw,
			  int state, int timeout_us)
{
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;
	int t = timeout_us/4;

	RMB(); /* ensure interface->dwDspAck is up to date */
	while ((interface->dwDspAck != state) && --t) {
		HpiOs_DelayMicroSeconds(4);
		RMB(); /* DSP changes dwDspAck by DMA */
	}

	/*HPI_DEBUG_LOG2(VERBOSE, "Spun %d for %d\n", timeout_us/4-t, state);*/
	return t * 4;
}

/* set the busmaster interface to cmd, then interrupt the DSP */
static void SendDspCommand(struct hpi_hw_obj_62 *phw, int cmd)
{
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;
	uint32_t r;

	interface->dwHostCmd = cmd;
	WMB(); /* DSP gets state by DMA, make sure it is written to memory */
	/* before we interrupt the DSP */
	r = HPIOS_MEMREAD32(phw->prHDCR);
	r |= (uint32_t)C6205_HDCR_DSPINT;
	HPIOS_MEMWRITE32(phw->prHDCR, r);
	r &= ~(uint32_t)C6205_HDCR_DSPINT;
	HPIOS_MEMWRITE32(phw->prHDCR, r);
}

#ifdef HPI_BUILD_NO_SHARED_GLOBALS
#warning messageCount is shared between driver instances!
#endif // HPI_BUILD_NO_SHARED_GLOBALS
static unsigned int messageCount;

static hpi_err_t MessageResponseSequence(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	uint32_t dwTimeOut;
	struct hpi_hw_obj_62 *phw = pao->priv;
	struct bus_master_interface_62 *interface = phw->pInterfaceBuffer;

	messageCount++;
	if (phm->wSize > sizeof(interface->u.MessageBuffer)) {
		phr->wError = HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL;
		phr->wSpecificError = sizeof(interface->u.MessageBuffer);
		phr->wSize = sizeof(struct hpi_response_header);
#ifdef HPI_OS_LINUX_KERNEL
		HPI_DEBUG_LOG2(ERROR, "Message len %d too big for buffer %zd \n",
			phm->wSize, sizeof(interface->u.MessageBuffer));
#else
		HPI_DEBUG_LOG2(ERROR, "Message len %d too big for buffer %Iu\n",
			phm->wSize, sizeof(interface->u.MessageBuffer));
#endif
		return 0;
	}

	/* Assume buffer of type struct bus_master_interface_62
	   is allocated "noncacheable" */

	if (!WaitDspAck(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) {
		HPI_DEBUG_LOG0(DEBUG, "Timeout waiting for idle\n");
		return HPI_ERROR_DSP_COMMUNICATION;
	}

	memcpy(&interface->u.MessageBuffer, phm, phm->wSize);
	// signal we want a response
	SendDspCommand(phw, H620_HIF_GET_RESP);

	dwTimeOut = WaitDspAck(phw, H620_HIF_GET_RESP, HPI6205_TIMEOUT);

	if (!dwTimeOut) {
		HPI_DEBUG_LOG2(ERROR,
			"(%u) Timed out waiting for GET_RESP state [%x]\n",
			messageCount, interface->dwDspAck);
		return HPI_ERROR_DSP_COMMUNICATION;
	}
	HPI_DEBUG_LOG2(VERBOSE,
		"(%u) Transition to GET_RESP after %u\n",
		messageCount, HPI6205_TIMEOUT - dwTimeOut);

	// read the result
	if (interface->u.ResponseBuffer.response.wSize <= phr->wSize) {
		memcpy(phr, &interface->u.ResponseBuffer, interface->u.ResponseBuffer.response.wSize);
	} else {
		HPI_DEBUG_LOG2(ERROR,
			"Response len %d too big for buffer %d\n",
			interface->u.ResponseBuffer.response.wSize,
			phr->wSize);
		memcpy(phr, &interface->u.ResponseBuffer, sizeof(struct hpi_response_header));
		phr->wError = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
		phr->wSpecificError = interface->u.ResponseBuffer.response.wSize;
		phr->wSize = sizeof(struct hpi_response_header);
	}
	// set interface back to idle
	SendDspCommand(phw, H620_HIF_IDLE);

	return HpiValidateResponse(phm, phr);
}

static void HwMessage(
	struct hpi_adapter_obj *pao,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{

	hpi_err_t err = 0;
#ifdef HPI_OS_LINUX_KERNEL
	ktime_t before = ktime_get();
	ktime_t after;
	u64 elapsed_ns;
	int microseconds;
#endif

	HpiOs_SpinLock_Lock(&pao->dspLock);

	err = MessageResponseSequence(pao, phm, phr);

	// maybe an error response
	if (err) {
		phr->wError = err;
		pao->wDspCrashed++;

		// just the header of the response is valid
		phr->wSize = sizeof(struct hpi_response_header);
		goto err;
	} else
		pao->wDspCrashed = 0;

	if (phr->wError != 0)	// something failed in the DSP
		goto err;

	switch (phm->wFunction) {
	case HPI_OSTREAM_WRITE:
	case HPI_ISTREAM_ANC_WRITE:
		err = Hpi6205_TransferData(pao,
			phm->u.d.u.data.pbData,
			phm->u.d.u.data.dwDataSize, H620_HIF_SEND_DATA);
		break;

	case HPI_ISTREAM_READ:
	case HPI_OSTREAM_ANC_READ:
		err = Hpi6205_TransferData(pao,
			phm->u.d.u.data.pbData,
			phm->u.d.u.data.dwDataSize, H620_HIF_GET_DATA);
		break;

	}
	phr->wError = err;

err:
	HpiOs_SpinLock_Unlock(&pao->dspLock);

#ifdef HPI_OS_LINUX_KERNEL
	after = ktime_get();
	elapsed_ns = ktime_to_ns(ktime_sub(after, before));
	microseconds = do_div(elapsed_ns, 1000);
	HPI_DEBUG_LOG1(VERBOSE, "DSP transaction took %d microseconds.\n", microseconds);
#endif
}
