omap3530/beagle_drivers/wb/api/hal/cyashalbeagleboard.cpp
changeset 27 117faf51deac
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/omap3530/beagle_drivers/wb/api/hal/cyashalbeagleboard.cpp	Wed Mar 03 13:10:32 2010 +0000
@@ -0,0 +1,1306 @@
+// Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// omap3530/beagle_drivers/wb/cyashalbeagleboard_spi.cpp
+//
+
+#include <kern_priv.h>
+#include <beagle/beagle_gpio.h>
+#include <beagle/variant.h>
+#include <assp/omap3530_assp/omap3530_assp_priv.h>
+#include <assp/omap3530_assp/omap3530_irqmap.h> // GPIO interrupts
+#include <assp/omap3530_assp/omap3530_gpio.h>
+
+#include <assp.h> // Required for definition of TIsr
+
+#include <cyasregs.h> // Astoria register definitions
+
+#ifdef __CY_ASTORIA_BEAGLEBOARD_SPI__HAL__
+
+#include "cyashalbeagleboard_spi.h"
+#include "cyaserr.h"
+#include "cyasregs.h"
+#include "cyasdma.h"
+#include "cyasintr.h"
+#ifdef FIRMWARE_NOPPORT
+
+#ifdef OVERCLOCK_SD
+#include "cyastfw_sd_mmc_rel_nopport_Ast121_68.h"
+#else
+#include "cyastfw_sd_mmc_rel_nopport.h"
+#endif
+
+#else
+
+#ifdef OVERCLOCK_SD
+#include "cyastfw_sd_mmc_rel_silicon_Ast121_68.h"
+#else
+#include "cyastfw_sd_mmc_rel_silicon.h"
+#endif
+
+#endif
+
+#include "cyasusbinit.h"
+
+/*
+ * For performance reasons, we handle storage endpoint transfers upto 4 KB
+ * within the HAL itself.
+ */
+#define CYASSTORAGE_WRITE_EP_NUM	(4)
+#define CYASSTORAGE_READ_EP_NUM		(8)
+#define CYASSTORAGE_MAX_XFER_SIZE	(2*32768)
+
+/*#define MONITOR_THREAD 1*/
+#define INT_DE 1
+
+/* DFC queue */
+TDfc* gpDfc;
+
+/*
+ * The type of DMA operation, per endpoint
+ */
+typedef enum CyAsHalDmaType
+{
+    CyAsHalRead,
+    CyAsHalWrite,
+    CyAsHalNone
+} CyAsHalDmaType ;
+
+typedef struct CyAsHalEndpointDma
+{
+    CyBool buffer_valid ;
+    uint16_t *data_p ;
+    uint32_t size ;
+    /*struct scatterlist* sg_p ;
+    uint16_t scatter_index ;*/
+    uint32_t bytes_xfered ;
+    uint16_t transfer_cnt ;
+    CyAsHalDmaType type ;
+    CyBool pending ;
+} CyAsHalEndpointDma ;
+
+/*
+ * The list of OMAP devices (should be one)
+ */
+static CyAsOmapDevKernel *m_omap_list_p = 0 ;
+
+/*
+ * The callback to call after DMA operations are complete
+ */
+static CyAsHalDmaCompleteCallback callback = 0 ;
+
+/*
+ * Pending data size for the endpoints
+ */
+static CyAsHalEndpointDma EndPoints[16] ;
+
+/* Forward declaration */
+static void CyHandleDRQInterrupt(CyAsOmapDevKernel *dev_p) ;
+
+static volatile uint32_t Intr_Disabled = 0 ;
+static volatile CyAsHalDeviceTag gDevTag = 0 ;
+
+#define CYAS_INT_MASK (CY_AS_MEM_P0_INTR_REG_MCUINT | CY_AS_MEM_P0_INTR_REG_MBINT | \
+                       CY_AS_MEM_P0_INTR_REG_PMINT | CY_AS_MEM_P0_INTR_REG_PLLLOCKINT)
+#define CYAS_DRQ_MASK (CY_AS_MEM_P0_INTR_REG_DRQINT)
+
+
+static void
+CyAstoriaISR (
+    void *dev_id)
+{
+#ifdef SPI_DEBUG_LOG
+	Kern::Printf("CyAstoriaISR...");
+#endif
+	gpDfc->Add();
+
+#ifdef SPI_DEBUG_LOG
+	Kern::Printf("Disable interrupt");
+#endif
+	/* Disable Interrupt Here, it will be re-enabled by DFCs */
+	GPIO::DisableInterrupt(KGPIO_INT) ;
+}
+
+static void
+CyAstoriaIntHandler_DFC (
+    TAny *aPtr)
+{
+    CyAsOmapDevKernel *dev_p = (CyAsOmapDevKernel *)aPtr ;
+    uint16_t          read_val = 0 ;
+#ifdef SPI_DEBUG_LOG
+	Kern::Printf("CyAstoriaIntHandler called...");
+#endif
+    {
+		read_val = CyAsHalReadRegister((CyAsHalDeviceTag)dev_p, CY_AS_MEM_P0_INTR_REG) ;
+		if (read_val & CYAS_INT_MASK)
+		{
+			CyAsIntrServiceInterrupt((CyAsHalDeviceTag)dev_p) ;
+		}
+
+		if (read_val & CYAS_DRQ_MASK)
+		{
+			CyHandleDRQInterrupt(dev_p) ;
+		}
+    }
+	GPIO::EnableInterrupt(KGPIO_INT) ;
+#ifdef SPI_DEBUG_LOG
+	Kern::Printf("Enable interrupt\n");
+#endif
+}
+
+extern TmtpAstDev * g_pAstDevice ;
+
+static int
+CyAsHalConfigureInterrupts (
+    void *dev_p,
+	void *handler)
+{
+
+    return CyAsHalBeagleBoard__SetupISR(handler, dev_p) ;
+}
+//nxz-debug
+void
+MyOmapStartIntr(CyAsHalDeviceTag tag)
+{
+    CyAsOmapDevKernel *dev_p = (CyAsOmapDevKernel *)tag ;
+	int ret ;
+
+    const uint16_t mask = CY_AS_MEM_P0_INTR_REG_DRQINT | CY_AS_MEM_P0_INTR_REG_MBINT ;
+
+	/* Setup DFC */
+	gpDfc = new TDfc( CyAstoriaIntHandler_DFC, tag, Kern::DfcQue0(), 1 ) ;
+
+    /* Register for interrupts */
+    ret = CyAsHalConfigureInterrupts (dev_p,(void*)CyAstoriaISR) ;
+	if ( ret != 0 )
+		Kern::Printf("ERROR: CyAsHalConfigureInterrupts failed\n");
+
+    /* enable only MBox & DRQ interrupts for now */
+    CyAsHalWriteRegister((CyAsHalDeviceTag)dev_p, CY_AS_MEM_P0_INT_MASK_REG, mask) ;
+}
+
+volatile CyBool interrupt_fired = CyFalse;
+
+//This is the Diagnostics Interrupt Handler
+void
+MyIntTestHandler(
+	void *dev_id)
+{
+    CyAsOmapDevKernel * dev_tag = (CyAsOmapDevKernel*) dev_id ;
+
+	Kern::Printf("Diag: CyAsDiagIntHandler called\n");
+
+	if(interrupt_fired == CyFalse)
+	{
+			Kern::Printf("Diag: CyAsDiagIntHandler, first instance of INT# \n");
+			interrupt_fired = CyTrue;
+
+			//return INT to high state, only called for override testing
+			CyAsHalWriteRegister(dev_tag, CY_AS_MEM_P0_VM_SET, 0x0745);
+	}
+	//return INT to high state, only called for override testing
+	CyAsHalWriteRegister(dev_tag, CY_AS_MEM_P0_VM_SET, 0x0745);
+
+}
+
+
+
+/*
+* These are the functions that are not part of the HAL layer, but are required to be called
+* for this HAL.
+*/
+int StartOMAPKernel(const char *pgm, CyAsHalDeviceTag *tag, CyBool debug)
+{
+    CyAsOmapDevKernel *dev_p ;
+    CyAsHalSleepChannel channel ;
+    int i;
+
+    (void)debug ; /* No debug mode support as of now */
+
+    Kern::Printf ("<1>startOMAPKernel called\n");
+
+    /*
+     * Initialize the HAL level endpoint DMA data.
+     */
+    for(i = 0 ; i < sizeof(EndPoints)/sizeof(EndPoints[0]) ; i++)
+    {
+        EndPoints[i].data_p = 0 ;
+        EndPoints[i].pending = CyFalse ;
+        EndPoints[i].size = 0 ;
+        EndPoints[i].type = CyAsHalNone ;
+		EndPoints[i].bytes_xfered = 0 ;
+		EndPoints[i].transfer_cnt = 0 ;
+    }
+
+    dev_p = (CyAsOmapDevKernel *)CyAsHalAlloc(sizeof(CyAsOmapDevKernel)) ;
+    if (dev_p == 0)
+    {
+		Kern::Printf("<1>%s: out of memory allocating OMAP device structure\n", pgm) ;
+		return 0 ;
+    }
+    dev_p->m_sig = CY_AS_OMAP_KERNEL_HAL_SIG ;
+    dev_p->m_addr_base = 0 ;
+
+    CyAsHalBeagleBoard__ConfigureSPI();
+
+#ifdef HW_TEST
+{
+    uint16_t readVal = 0 ;
+	uint16_t timeOutCounter = 0 ;
+	uint16_t errCnt = 0 ;
+
+	Kern::Printf( "<1> Regsiter Test ------------->\n") ;
+
+	readVal = CyAsHalReadRegister(dev_p, CY_AS_MEM_CM_WB_CFG_ID) ;
+	Kern::Printf("ID reg 0x%04x\n",readVal);
+        if ( readVal != 0xa200 )
+	{
+		Kern::Printf("ERROR: Wrong Antioch ID reg value\n");
+		Kern::Printf("ERROR: Exp:  0x%04x\n",0xa200);
+		Kern::Printf("ERROR: Act:  0x%04x\n",readVal);
+		errCnt++;
+	}
+
+	readVal = CyAsHalReadRegister(dev_p, CY_AS_MEM_P0_VM_SET) ;
+	Kern::Printf("P0_VM_SET 0x%04x\n",readVal);
+	if ( readVal != 0x45 )
+	{
+		Kern::Printf("ERROR: Wrong Antioch P0_VM_SET reg value\n");
+		Kern::Printf("ERROR: Exp:  0x%04x\n",0x45);
+		Kern::Printf("ERROR: Act:  0x%04x\n",readVal);
+		errCnt++;
+	}
+
+	readVal = CyAsHalReadRegister(dev_p, CY_AS_MEM_P0_RSE_ALLOCATE) ;
+	Kern::Printf("P0_RSE_ALLOCATE 0x%04x\n",readVal);
+	if ( readVal != 0x26 )
+	{
+		Kern::Printf("ERROR: Wrong Antioch P0_RSE_ALLOCATE reg value\n");
+		Kern::Printf("ERROR: Exp:  0x%04x\n",0x26);
+		Kern::Printf("ERROR: Act:  0x%04x\n",readVal);
+		errCnt++;
+	}
+
+	CyAsHalWriteRegister(dev_p, CY_AS_MEM_MCU_MAILBOX0, 0x0);
+	CyAsHalWriteRegister(dev_p, CY_AS_MEM_MCU_MAILBOX1, 0xFFFF);
+	CyAsHalWriteRegister(dev_p, CY_AS_MEM_MCU_MAILBOX2, 0xAAAA);
+	CyAsHalWriteRegister(dev_p, CY_AS_MEM_MCU_MAILBOX3, 0x5555);
+
+	readVal = CyAsHalReadRegister(dev_p, CY_AS_MEM_MCU_MAILBOX0) ;
+	Kern::Printf("mailbox0 0x%04x\n",readVal);
+	if ( readVal != 0x0 )
+	{
+		Kern::Printf("ERROR: Wrong Antioch MAILBOX0 reg value\n");
+		Kern::Printf("ERROR: Exp:  0x%04x\n",0x0);
+		Kern::Printf("ERROR: Act:  0x%04x\n",readVal);
+		errCnt++;
+	}
+
+	readVal = CyAsHalReadRegister(dev_p, CY_AS_MEM_MCU_MAILBOX1) ;
+	Kern::Printf("mailbox1 0x%04x\n",readVal);
+	if ( readVal != 0xffff )
+	{
+		Kern::Printf("ERROR: Wrong Antioch MAILBOX1 reg value\n");
+		Kern::Printf("ERROR: Exp:  0x%04x\n",0xffff);
+		Kern::Printf("ERROR: Act:  0x%04x\n",readVal);
+		errCnt++;
+	}
+
+	readVal = CyAsHalReadRegister(dev_p, CY_AS_MEM_MCU_MAILBOX2) ;
+    Kern::Printf("mailbox2 0x%04x\n",readVal);
+	if ( readVal != 0xaaaa )
+	{
+		Kern::Printf("ERROR: Wrong Antioch MAILBOX2 reg value\n");
+		Kern::Printf("ERROR: Exp:  0x%04x\n",0xaaaa);
+		Kern::Printf("ERROR: Act:  0x%04x\n",readVal);
+		errCnt++;
+	}
+
+	readVal = CyAsHalReadRegister(dev_p, CY_AS_MEM_MCU_MAILBOX3) ;
+    Kern::Printf("mailbox3 0x%04x\n",readVal);
+	if ( readVal != 0x5555 )
+	{
+		Kern::Printf("ERROR: Wrong Antioch MAILBOX3 reg value\n");
+		Kern::Printf("ERROR: Exp:  0x%04x\n",0x5555);
+		Kern::Printf("ERROR: Act:  0x%04x\n",readVal);
+		errCnt++;
+	}
+
+	Kern::Printf( "<1> Interrupt Test ------------->\n") ;
+	Kern::Printf("Checking that INT# can execute Procesor ISR:\n");
+
+	CyAsHalConfigureInterrupts (dev_p,(void*)MyIntTestHandler) ;
+
+	//overrid default INT# value, cause Astoria to assert INT#
+	CyAsHalWriteRegister(dev_p, CY_AS_MEM_P0_VM_SET, 0x0545);
+
+	do {
+		timeOutCounter++;
+	}while((interrupt_fired == CyFalse) && (timeOutCounter < 255));
+
+	if(interrupt_fired == CyTrue)
+	{
+		Kern::Printf("INT# fired Processor ISR\n");
+	}
+	else
+	{
+		Kern::Printf("ERROR: INT# did not fire processor ISR %i\n", interrupt_fired);
+		errCnt++;
+	}
+
+	if ( errCnt == 0 )
+	{
+		Kern::Printf("HW test passed!!\n") ;
+	}
+	else
+		Kern::Printf("HW test failed (%d)\n",errCnt) ;
+
+	Kern::Printf("Reset Astoria\n") ;
+	CyAsHalWriteRegister(dev_p, CY_AS_MEM_RST_CTRL_REG, CY_AS_MEM_RST_CTRL_REG_HARD) ;
+	return 0 ;
+}
+#endif
+
+    /*
+     * Now perform a hard reset of the device to have the new settings take
+     * effect
+     */
+    Kern::Printf("<1>West Bridge Device Enters hard reset\n") ;
+    CyAsHalWriteRegister(dev_p, CY_AS_MEM_RST_CTRL_REG, CY_AS_MEM_RST_CTRL_REG_HARD) ;
+
+    /*
+     * Sleep for 100 ms to be sure reset is complete.
+     */
+    Kern::Printf("<1>Sleep 100ms\n") ;
+    CyAsHalCreateSleepChannel (&channel) ;
+    CyAsHalSleepOn(&channel, 100) ;
+    CyAsHalDestroySleepChannel(&channel) ;
+	Kern::Printf("<1>MyOmapStartIntr\n") ;
+    MyOmapStartIntr(dev_p);
+
+    dev_p->m_next_p = m_omap_list_p ;
+    m_omap_list_p = dev_p ;
+
+    *tag = dev_p ;
+    gDevTag = dev_p ;
+
+    return 1 ;
+}
+
+int StopOMAPKernel(const char *pgm, CyAsHalDeviceTag tag)
+{
+    CyAsOmapDevKernel *dev_p = (CyAsOmapDevKernel *)tag ;
+
+    if (0 == dev_p)
+		return 1 ;
+
+    Kern::Printf ("<1>StopOMAPKernel called\n");
+    if (dev_p->m_sig != CY_AS_OMAP_KERNEL_HAL_SIG)
+    {
+		Kern::Printf("<1>%s: %s: bad TAG parameter passed to function\n", pgm, __FUNCTION__) ;
+		return 1 ;
+    }
+
+    Kern::Printf ("<1>West Bridge OMAP Kernel: Done cleaning thread\n") ;
+	/* TODO: */
+
+    CyAsHalFree(dev_p) ;
+
+    return 1 ;
+}
+
+
+/*************************************************************************************************
+*
+* Below are the functions that communicate with the West Bridge device.  These are system dependent
+* and must be defined by the HAL layer for a given system.
+*
+*************************************************************************************************/
+
+void
+CyAsHalWriteEP(CyAsHalDeviceTag tag, uint16_t addr, uint8_t* data, uint16_t size)
+{
+    CyAsOmapDevKernel *dev_p = (CyAsOmapDevKernel *)tag ;
+GPIO::DisableInterrupt(KGPIO_INT) ;
+
+    /* Do additional error checks while in debug mode. */
+    if (dev_p->m_sig != CY_AS_OMAP_KERNEL_HAL_SIG)
+    {
+		Kern::Printf("<1>%s: bad TAG parameter passed to function\n",  __FUNCTION__) ;
+		return ;
+    }
+
+    if (addr & 0x80)
+    {
+		Kern::Printf ("<1>West Bridge write EP address 0x%x out of range\n", addr);
+		return ;
+    }
+
+	CyAsHalBeagleBoardMcSPI4Ch0_WriteEP(addr, data, size) ;
+GPIO::EnableInterrupt(KGPIO_INT) ;
+    return ;
+}
+
+void
+CyAsHalReadEP(CyAsHalDeviceTag tag, uint16_t addr, uint8_t* data, uint16_t size)
+{
+    CyAsOmapDevKernel *dev_p = (CyAsOmapDevKernel *)tag ;
+GPIO::DisableInterrupt(KGPIO_INT) ;
+
+    /* Do additional error checks while in debug mode. */
+    if (dev_p->m_sig != CY_AS_OMAP_KERNEL_HAL_SIG)
+    {
+		Kern::Printf("<1>%s: bad TAG parameter passed to function\n",  __FUNCTION__) ;
+		return ;
+    }
+
+    if (addr & 0x80)
+    {
+		Kern::Printf ("<1>West Bridge write EP address 0x%x out of range\n", addr);
+		return ;
+    }
+
+	CyAsHalBeagleBoardMcSPI4Ch0_ReadEP(addr, data, size) ;
+GPIO::EnableInterrupt(KGPIO_INT) ;
+    return ;
+}
+
+/*
+* This function must be defined to write a register within the West Bridge
+* device.  The addr value is the address of the register to write with
+* respect to the base address of the West Bridge device.
+*/
+void
+CyAsHalWriteRegister(CyAsHalDeviceTag tag, uint16_t addr, uint16_t data)
+{
+    CyAsOmapDevKernel *dev_p = (CyAsOmapDevKernel *)tag ;
+GPIO::DisableInterrupt(KGPIO_INT) ;
+
+    /* Do additional error checks while in debug mode. */
+    if (dev_p->m_sig != CY_AS_OMAP_KERNEL_HAL_SIG)
+    {
+		Kern::Printf("<1>%s: bad TAG parameter passed to function\n",  __FUNCTION__) ;
+		return ;
+    }
+
+    if (addr > CYAS_DEV_MAX_ADDR)
+    {
+		Kern::Printf ("<1>West Bridge write address 0x%x out of range\n", addr);
+		return ;
+    }
+
+	CyAsHalBeagleBoardMcSPI4Ch0_WriteReg((TUint32) addr, (TUint16) data) ;
+#ifdef SPI_DEBUG_LOG
+	Kern::Printf("CyAsHalWriteRegister(0x%02x,0x%04x)\n",addr,data) ;
+#endif
+GPIO::EnableInterrupt(KGPIO_INT) ;
+    return ;
+
+}
+
+/*
+* This function must be defined to read a register from the West Bridge
+* device.  The addr value is the address of the register to read with
+* respect to the base address of the West Bridge device.
+*/
+uint16_t
+CyAsHalReadRegister(CyAsHalDeviceTag tag, uint16_t addr)
+{
+    uint16_t data  = 0 ;
+    CyAsOmapDevKernel *dev_p = (CyAsOmapDevKernel *)tag ;
+GPIO::DisableInterrupt(KGPIO_INT) ;
+
+    if (dev_p->m_sig != CY_AS_OMAP_KERNEL_HAL_SIG)
+    {
+		Kern::Printf("<1>%s: bad TAG parameter passed to function\n",  __FUNCTION__) ;
+		return 0 ;
+    }
+
+    if (addr > CYAS_DEV_MAX_ADDR)
+    {
+		Kern::Printf("<1>West Bridge read address 0x%x out of range\n", addr) ;
+		return 0 ;
+    }
+
+    CyAsHalBeagleBoardMcSPI4Ch0_ReadReg((TUint32) addr, (TUint16*) &data) ;
+#ifdef SPI_DEBUG_LOG
+    Kern::Printf("CyAsHalReadRegister(0x%02x,0x%04x)\n",addr,data) ;
+#endif
+GPIO::EnableInterrupt(KGPIO_INT) ;
+    return data ;
+}
+
+static void
+CyServiceEPDmaReadRequest(CyAsOmapDevKernel *dev_p, uint8_t ep)
+{
+    CyAsHalDeviceTag tag = (CyAsHalDeviceTag)dev_p ;
+    uint16_t  v ;
+    uint32_t  size ;
+    uint16_t  addr = CY_AS_MEM_P0_EP2_DMA_REG + ep - 2 ;
+    uint8_t *dptr = (uint8_t*)EndPoints[ep].data_p ;
+
+
+#ifdef SPI_DEBUG_LOG
+	Kern::Printf("CyServiceEPDmaReadRequest...");
+#endif
+    /* Read the amount of data available */
+    v = CyAsHalReadRegister(tag, addr) ;
+    size =  v & CY_AS_MEM_P0_EPn_DMA_REG_COUNT_MASK ;
+
+	CyAsHalReadEP(tag, ep, dptr, size) ;
+
+    /*
+     * Now, reset the DMAVAL bit indicating that the data has been read
+     */
+
+    CyAsHalWriteRegister(tag, addr, 0) ;
+
+    EndPoints[ep].pending      = CyFalse ;
+    EndPoints[ep].type         = CyAsHalNone ;
+    EndPoints[ep].buffer_valid = CyFalse ;
+
+    if (callback)
+		callback(tag, ep, size, CY_AS_ERROR_SUCCESS) ;
+}
+
+static void
+CyServiceEPDmaWriteRequest(CyAsOmapDevKernel *dev_p, uint8_t ep)
+{
+    CyAsHalDeviceTag tag = (CyAsHalDeviceTag)dev_p ;
+    uint16_t  addr = CY_AS_MEM_P0_EP2_DMA_REG + ep - 2 ;
+    uint8_t *dptr = (uint8_t *)EndPoints[ep].data_p ;
+    uint32_t  size ;
+
+
+#ifdef SPI_DEBUG_LOG
+	Kern::Printf("CyServiceEPDmaWriteRequest...");
+#endif
+
+    if ( ep == CYASSTORAGE_WRITE_EP_NUM )
+    {
+		if ( EndPoints[ep].size == 0x180 )
+		{
+			size = 0x180 ;
+		}
+		else
+			size = 0x200 ;
+    }
+    else
+		size = EndPoints[ep].size ;
+#ifdef SPI_DEBUG_LOG
+    Kern::Printf("Service DMA write request EP%d, size=%d, ep.size=%d\n",ep,size,EndPoints[ep].size);
+#endif
+	#if 1
+	CyAsHalWriteEP(tag, ep, dptr, size) ;
+	#else
+	{
+		uint16_t v ;
+		int i ;
+ 		for(i = 0 ; i < size / 2 ; i++)
+		{
+			v = dptr[0] | (dptr[1] << 8) ;
+			CyAsHalWriteRegister(tag, ep, v) ;
+			dptr++ ;
+			dptr++ ;
+		}
+	}
+	#endif
+
+    /*
+     * Now, write the DMAVAL bit to indicate we are done transferring data and that the data
+     * can now be sent via USB to the USB host, sent to storage, or used internally.
+     */
+    CyAsHalWriteRegister(tag, addr, size) ;
+
+    /* We have sent the data, mark it as false */
+    EndPoints[ep].pending = CyFalse ;
+    EndPoints[ep].type     = CyAsHalNone ;
+    EndPoints[ep].buffer_valid = CyFalse ;
+
+    /*
+     * Finally, tell the USB subsystem that the data is gone and we can accept the
+     * next request if one exists.
+     */
+    if (callback)
+		callback(tag, ep, size, CY_AS_ERROR_SUCCESS) ;
+}
+
+static void
+CyHandleDRQInterrupt(CyAsOmapDevKernel *dev_p)
+{
+    uint16_t v ;
+    static uint8_t service_ep = 2 ;
+    static CyBool indrq = CyFalse ;
+
+    /*Kern::Printf("<1>DRQ interrupt detected\n") ;*/
+    if (indrq)
+    {
+#ifndef NDEBUG
+
+	Kern::Printf("<1>+++++++++  Nested DRQ interrupt detected\n") ;
+        v = CyAsHalReadRegister((CyAsHalDeviceTag)dev_p, CY_AS_MEM_P0_INTR_REG) ;
+	Kern::Printf("<1>+++++++++ INTR_REG = %04x\n",v) ;
+    	v = CyAsHalReadRegister((CyAsHalDeviceTag)dev_p, CY_AS_MEM_P0_DRQ) ;
+	Kern::Printf("<1>+++++++++ DRQ_REG = %04x\n",v) ;
+#endif
+	return ;
+    }
+
+    indrq = CyTrue ;
+
+    /*
+     * We have received a DMA interrupt
+     */
+    v = CyAsHalReadRegister((CyAsHalDeviceTag)dev_p, CY_AS_MEM_P0_DRQ) ;
+    if (v == 0)
+    {
+#ifndef NDEBUG
+	/*Stray DRQ is possible because we will check DRQ register before exit handler*/
+	Kern::Printf("<1>+++++++++  Stray DRQ interrupt detected\n") ;
+#endif
+	indrq = CyFalse;
+	return ;
+    }
+
+    /*
+     * Now, pick a given DMA request to handle, for now, we just go round robin.  Each bit
+     * position in the service_mask represents an endpoint from EP2 to EP15.  We rotate through
+     * each of the endpoints to find one that needs to be serviced.
+     */
+    while ((v & (1 << service_ep)) == 0)
+    {
+	if (service_ep == 15)
+	    service_ep = 2 ;
+	else
+	    service_ep++ ;
+    }
+
+    if ((v & (1 << service_ep)) == 0)
+    {
+	Kern::Printf("<1>+++++++++  Internal error, this should not happen\n") ;
+	indrq = CyFalse;
+	return ;
+    }
+
+    if (EndPoints[service_ep].type == CyAsHalWrite)
+		CyServiceEPDmaWriteRequest(dev_p, service_ep) ;
+    else if (EndPoints[service_ep].type == CyAsHalRead)
+		CyServiceEPDmaReadRequest(dev_p, service_ep) ;
+
+#ifndef NDEBUG
+    else
+    {
+        Kern::Printf("+++++++ Interrupt occurred, but there is no DMA operation pending - check DRQ_MASK logic\n") ;
+    }
+#endif
+
+    /* Now bump the EP ahead, so other endpoints get a shot before the one we just serviced */
+    if (EndPoints[service_ep].type == CyAsHalNone)
+    {
+	if (service_ep == 15)
+	    service_ep = 2 ;
+	else
+	    service_ep++ ;
+    }
+
+    indrq = CyFalse ;
+}
+
+void
+CyAsHalDmaCancelRequest(CyAsHalDeviceTag tag, uint8_t ep)
+{
+    if (EndPoints[ep].pending)
+        CyAsHalWriteRegister(tag, CY_AS_MEM_P0_EP2_DMA_REG + ep - 2, 0) ;
+
+    EndPoints[ep].buffer_valid = CyFalse ;
+}
+
+/*
+* This function must be defined to transfer a block of data to the West Bridge device.  This
+* function can use the burst write (DMA) capabilities of West Bridge to do this, or it can
+* just copy the data using writes.
+*/
+void
+CyAsHalDmaSetupWrite(CyAsHalDeviceTag tag, uint8_t ep, void *buf, uint32_t size, uint16_t maxsize)
+{
+    uint32_t addr ;
+    uint16_t v ;
+#ifdef SPI_DEBUG_LOG
+	Kern::Printf("CyAsHalDmaSetupWrite (%d,%d)",size,maxsize);
+#endif
+    /* No EP0 or EP1 traffic should get here */
+    CyAsHalAssert(ep != 0 && ep != 1) ;
+
+    /*
+     * If this asserts, we have an ordering problem.  Another DMA request is coming down
+     * before the previous one has completed.
+     */
+    CyAsHalAssert(EndPoints[ep].buffer_valid == CyFalse) ;
+
+    /*
+     * Store the data for the interrupt service routine
+     */
+    EndPoints[ep].buffer_valid = CyTrue ;
+    EndPoints[ep].data_p = (uint16_t*)buf ;
+    EndPoints[ep].size = size ;
+    EndPoints[ep].type = CyAsHalWrite ;
+
+    /*
+     * Tell West Bridge we are ready to send data on the given endpoint
+     */
+    {
+		addr = CY_AS_MEM_P0_EP2_DMA_REG + ep - 2 ;
+		v = (size & CY_AS_MEM_P0_EPn_DMA_REG_COUNT_MASK) | CY_AS_MEM_P0_EPn_DMA_REG_DMAVAL ;
+    }
+
+    CyAsHalWriteRegister(tag, addr, v) ;
+
+}
+
+/*
+* This function must be defined to transfer a block of data from the West Bridge device.  This
+* function can use the burst read (DMA) capabilities of West Bridge to do this, or it can just
+* copy the data using reads.
+*/
+void
+CyAsHalDmaSetupRead(CyAsHalDeviceTag tag, uint8_t ep, void *buf, uint32_t size, uint16_t maxsize)
+{
+    uint32_t addr ;
+    uint16_t v ;
+#ifdef SPI_DEBUG_LOG
+	Kern::Printf("CyAsHalDmaSetupRead (%d,%d)",size,maxsize);
+#endif
+    /* No EP0 or EP1 traffic should get here */
+    CyAsHalAssert(ep != 0 && ep != 1) ;
+    CyAsHalAssert(EndPoints[ep].buffer_valid == CyFalse) ;
+
+    EndPoints[ep].buffer_valid = CyTrue ;
+    EndPoints[ep].data_p = (uint16_t*)buf ;
+    EndPoints[ep].size = size ;
+    EndPoints[ep].type = CyAsHalRead ;
+
+    /*
+     * Program the EP DMA register for Storage endpoints only.
+     */
+    if (ep == 2)
+    {
+		if (size == 1)
+		  size = 2 ;
+
+		addr = CY_AS_MEM_P0_EP2_DMA_REG + ep - 2 ;
+		v = (size & CY_AS_MEM_P0_EPn_DMA_REG_COUNT_MASK) | CY_AS_MEM_P0_EPn_DMA_REG_DMAVAL;
+		CyAsHalWriteRegister(tag, addr, v );
+    }
+    else if (ep == CYASSTORAGE_READ_EP_NUM)
+    {
+        addr = CY_AS_MEM_P0_EP8_DMA_REG ;
+
+		/* This transfer is always done in 512 byte chunks. */
+        v = 0x200 | CY_AS_MEM_P0_EPn_DMA_REG_DMAVAL ;
+        CyAsHalWriteRegister(tag, addr, v) ;
+	}
+}
+
+/*
+* This function must be defined to allow the West Bridge API to register a callback function that is
+* called when a DMA transfer is complete.
+*/
+void
+CyAsHalDmaRegisterCallback(CyAsHalDeviceTag tag, CyAsHalDmaCompleteCallback cb)
+{
+    callback = cb ;
+}
+
+/*
+* This function must be defined to return the maximum size of DMA request that can be handled
+* on the given endpoint.  The return value should be the maximum size in bytes that the DMA
+* module can handle.
+*/
+uint32_t
+CyAsHalDmaMaxRequestSize(CyAsHalDeviceTag tag, CyAsEndPointNumber_t ep)
+{
+    /* Storage reads and writes are always done in 512 byte blocks. So, we do the count
+       handling within the HAL, and save on some of the data transfer delay.
+     */
+    if ((ep == CYASSTORAGE_READ_EP_NUM) || (ep == CYASSTORAGE_WRITE_EP_NUM))
+	return CYASSTORAGE_MAX_XFER_SIZE;
+    else
+	/*
+	 * For the USB - Processor endpoints, the maximum transfer size depends on
+	 * the speed of USB operation. So, we use the following constant to
+	 * indicate to the API that splitting of the data into chunks less than
+	 * or equal to the max transfer size should be handled internally.
+	 */
+	return CY_AS_DMA_MAX_SIZE_HW_SIZE;
+}
+
+/*
+ * This function must be defined to set the state of the WAKEUP pin on the West Bridge device.  Generally
+ * this is done via a GPIO of some type.
+ */
+CyBool
+CyAsHalSetWakeupPin(CyAsHalDeviceTag tag, CyBool state)
+{
+    /* Not supported as of now. */
+    return CyFalse ;
+}
+
+void
+CyAsHalPllLockLossHandler(CyAsHalDeviceTag tag)
+{
+    Kern::Printf("Error: West Bridge PLL has lost lock\n") ;
+    Kern::Printf("Please check the input voltage levels and clock, and restart the system\n") ;
+}
+
+#define CYASHAL_REGISTERS_TO_SAVE_COUNT         (12)
+static uint16_t gAstoriaRegCache[CYASHAL_REGISTERS_TO_SAVE_COUNT][2] = {
+    {CY_AS_MEM_P0_ENDIAN,        0x0000},
+    {CY_AS_MEM_PMU_UPDATE,       0x0000},
+    {CY_AS_MEM_P0_VM_SET,        0x0000},
+    {CY_AS_MEM_P0_INT_MASK_REG,  0x0000},
+    {CY_AS_MEM_P0_RSE_ALLOCATE,  0x0000},
+    {CY_AS_MEM_P0_RSE_MASK,      0x0000},
+    {CY_AS_MEM_P0_DRQ_MASK,      0x0000},
+    {CY_AS_MEM_IROS_SLB_DATARET, 0x0000},
+    {CY_AS_MEM_IROS_IO_CFG,      0x0000},
+    {CY_AS_MEM_IROS_PLL_CFG,     0x0000},
+    {CY_AS_MEM_IROS_PXB_DATARET, 0x0000},
+    {CY_AS_MEM_IROS_SLEEP_CFG,   0x0000}
+};
+
+void
+CyAsHalReadRegsBeforeStandby (
+        CyAsHalDeviceTag tag)
+{
+    CyAsOmapDevKernel *dev_p = (CyAsOmapDevKernel *)tag;
+    int i, pos;
+
+    if (dev_p->m_sig != CY_AS_OMAP_KERNEL_HAL_SIG)
+    {
+        Kern::Printf("%s: bad TAG parameter passed to function\n", __FUNCTION__);
+        return;
+    }
+
+    {
+        for (i = 0; i < CYASHAL_REGISTERS_TO_SAVE_COUNT; i++)
+        {
+            gAstoriaRegCache[i][1] = CyAsHalReadRegister (tag, gAstoriaRegCache[i][0]);
+
+            /* Special handling required for the RSE_ALLOCATE register, so as to
+               ensure that the SD_IO pin ownership is set correctly.
+             */
+            if (gAstoriaRegCache[i][0] == CY_AS_MEM_P0_RSE_ALLOCATE)
+            {
+                /* For each of the 4 two bits fields in the register, set it to 'b01 if the
+                 * resource is currently allocated to the P port, and 'b10 if it is currently
+                 * allocated to Astoria.
+                 */
+                for (pos = 0; pos < 8; pos+= 2)
+                {
+                    if (((gAstoriaRegCache[i][1] >> pos) & 0x03) == 0x03)
+                        gAstoriaRegCache[i][1] = (gAstoriaRegCache[i][1] & (0xFF ^ (0x03 << pos))) | (0x01 << pos);
+                    else
+                        gAstoriaRegCache[i][1] = (gAstoriaRegCache[i][1] & (0xFF ^ (0x03 << pos))) | (0x02 << pos);
+                }
+            }
+        }
+    }
+}
+
+void
+CyAsHalRestoreRegsAfterStandby (
+        CyAsHalDeviceTag tag)
+{
+    CyAsOmapDevKernel *dev_p = (CyAsOmapDevKernel *)tag;
+    int i;
+
+    if (dev_p->m_sig != CY_AS_OMAP_KERNEL_HAL_SIG)
+    {
+        Kern::Printf("%s: bad TAG parameter passed to function\n", __FUNCTION__);
+        return;
+    }
+
+    {
+        /* Write 0xFF into the mask register so that all fields can be updated.
+           The mask will get updated to its proper value later.
+         */
+        CyAsHalWriteRegister (tag, CY_AS_MEM_P0_RSE_MASK, 0xFF);
+        for (i = 0; i < CYASHAL_REGISTERS_TO_SAVE_COUNT; i++)
+        {
+            CyAsHalWriteRegister (tag, gAstoriaRegCache[i][0], gAstoriaRegCache[i][1]);
+        }
+    }
+}
+
+void
+CyAsHalInitDevRegisters(
+	CyAsHalDeviceTag tag,
+	CyBool           is_standby_wakeup)
+{
+	(void)tag;
+	(void)is_standby_wakeup;
+	Kern::Printf("CyAsHalInitDevRegisters...");
+	return ;
+}
+
+
+void
+CyAsHalPrintMessage2(const char* msg)
+{
+	Kern::Printf("%s",msg);
+}
+
+/*************************************************************************************************
+*
+* Below are the functions that must be defined to provide the basic operating system services
+* required by the API.
+*
+*************************************************************************************************/
+
+/*
+ * This function is required by the API to allocate memory.  This function is expected to work
+ * exactly like malloc().
+ */
+void *
+CyAsHalAlloc(uint32_t cnt)
+{
+    void *ret_p ;
+    /*ret_p = kmalloc(cnt, GFP_KERNEL) ;*/
+    ret_p = (void*) new TUint8[cnt];
+    return ret_p ;
+}
+
+/*
+ * This function is required by the API to free memory allocated with CyAsHalAlloc().  This function is
+ * expected to work exacly like free().
+ */
+void
+CyAsHalFree(void *mem_p)
+{
+    delete [] (mem_p) ;
+}
+
+/*
+ * Allocator that can be used in interrupt context. We have to ensure that the kmalloc
+ * call does not sleep in this case.
+ */
+void *
+CyAsHalCBAlloc(uint32_t cnt)
+{
+    void *ret_p ;
+
+	/* TODO:
+    ret_p = kmalloc(cnt, GFP_ATOMIC) ;*/
+	ret_p = (void*) new TUint8[cnt];
+    return ret_p ;
+}
+
+/*
+ * This function is required to set a block of memory to a specific value.  This function is
+ * expected to work exactly like memset()
+ */
+void
+CyAsHalMemSet(void *ptr, uint8_t value, uint32_t cnt)
+{
+	/*uint32_t i ;
+	uint8_t* b = (uint8_t*) ptr ;
+	for ( i = 0 ; i < cnt ; i++ )
+		*b = value ;
+	 TODO: */
+    memset(ptr, value, cnt) ;
+}
+
+
+/*
+ * This function is expected to create a sleep channel.  The data structure that represents the
+ * sleep channel is given by the pointer in the argument.
+ */
+CyBool
+CyAsHalCreateSleepChannel(CyAsHalSleepChannel *channel)
+{
+    /* TODO:
+    init_waitqueue_head(&channel->wq) ;
+    return CyTrue ;*/
+    if (channel)
+    	channel->wq = 0 ;
+    return CyTrue ;
+}
+
+/*
+ * This function is expected to destroy a sleep channel.  The data structure that represents the
+ * sleep channel is given by the pointer in the argument.
+ */
+CyBool
+CyAsHalDestroySleepChannel(CyAsHalSleepChannel *channel)
+{
+	/* TODO:
+	return CyTrue ;*/
+	channel->wq =1 ;
+    return CyTrue ;
+}
+
+CyBool
+CyAsHalSleepOn(CyAsHalSleepChannel *channel, uint32_t ms)
+{
+    /* TODO:
+    interruptible_sleep_on_timeout (&channel->wq, (ms * HZ/1000)) ;
+    return CyTrue ;*/
+
+    NKern::Sleep(ms);
+    return CyTrue;
+}
+
+CyBool
+CyAsHalWake(CyAsHalSleepChannel *channel)
+{
+
+    /* TODO:
+    wake_up (&channel->wq) ;
+    return CyTrue ;*/
+    channel->wq = 1 ;
+    return CyTrue ;
+
+}
+
+uint32_t
+CyAsHalDisableInterrupts()
+{
+    uint16_t v = CyAsHalReadRegister(gDevTag,CY_AS_MEM_P0_INT_MASK_REG);
+    if ( !Intr_Disabled )
+    {
+        CyAsHalWriteRegister(gDevTag,CY_AS_MEM_P0_INT_MASK_REG,0);
+    }
+    Intr_Disabled++ ;
+	return (uint32_t)v ;
+}
+
+void
+CyAsHalEnableInterrupts(uint32_t val)
+{
+    Intr_Disabled-- ;
+    if ( !Intr_Disabled )
+    {
+        val = CYAS_INT_MASK | CYAS_DRQ_MASK ;
+        CyAsHalWriteRegister(gDevTag,CY_AS_MEM_P0_INT_MASK_REG,(uint16_t)val);
+        /*CyAsHalWriteRegister(gDevTag,CY_AS_MEM_P0_INT_MASK_REG,0x1800);*/
+    }
+}
+
+void
+CyAsHalSleep150 (void) /* Sleep atleast 150ns */
+{
+    uint32_t i, j;
+
+    j = 0;
+    for (i = 0; i < 100; i++)
+	j += (~i);
+}
+
+int j = 0;
+void
+CyAsHalSleep(uint32_t ms)
+{
+    int i;
+
+    while (ms--)
+    {
+	i = 60000;
+	while (i--)
+	{
+	    j += ((i * ~i) + 3 * i + 79);
+	}
+    }
+}
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+int gInitComplete = 0 ;
+
+/*
+ * This callback function is called for events from the MISC module.  The primary event
+ * processed by this function is the CyAsEventMiscInitialized which is sent once the
+ * West Bridge device is initialized and ready to accept requests.
+ */
+static void
+CyMiscCallback(CyAsDeviceHandle h, CyAsMiscEventType evtype, void *evdata)
+{
+    (void)h ;
+    (void)evdata ;
+
+    switch(evtype)
+    {
+    case CyAsEventMiscInitialized:
+    	Kern::Printf("Firmware initialized");
+        gInitComplete = CyTrue ;
+        break ;
+    default:
+        break ;
+    }
+}
+
+int
+CyAsHalAstoriaInit(void)
+{
+ 	uint32_t ret = 0 ;
+ 	char* pgm = "Beagleboard HAL";
+    CyAsDeviceHandle dev_handle ;
+    CyAsDeviceConfig config ;
+    CyAsHalDeviceTag tag ;
+
+    if (!StartOMAPKernel(pgm, &tag, CyFalse))
+    {
+#ifndef HW_TEST
+		Kern::Printf("ERROR: Cannot start OMAPKernel\n") ;
+#endif
+		return 1 ;
+	}
+
+    /*
+     * Create a handle to a West Bridge device.  It tag is used to identifiy the specific hardware
+     * we are talking to.
+     */
+    Kern::Printf("** Create API device\n") ;
+    ret = CyAsMiscCreateDevice(&dev_handle, tag) ;
+    if (ret != CY_AS_ERROR_SUCCESS)
+    {
+        Kern::Printf("%s: cannot initailize the West Bridge API - code %d\n", pgm, ret) ;
+        return 1 ;
+    }
+    Kern::Printf("** API device created\n") ;
+
+    /*
+     * Configure the physical bus so we can talk to the West Bridge device.  This is the first required
+     * API call after creating a device.
+     */
+    Kern::Printf("** API device configuring...\n");
+    memset(&config, 0, sizeof(config)) ;
+    ret = CyAsMiscConfigureDevice(dev_handle, &config) ;
+    if (ret != CY_AS_ERROR_SUCCESS)
+    {
+        Kern::Printf("%s: cannot configure the West Bridge device - code %d\n", pgm, ret) ;
+        return 1 ;
+    }
+    Kern::Printf("** API device configured\n") ;
+
+    /*
+     * Register a callback to process events from the MISC module.
+     */
+    Kern::Printf("** Register Callback...\n") ;
+    ret = CyAsMiscRegisterCallback(dev_handle, CyMiscCallback) ;
+    if (ret != CY_AS_ERROR_SUCCESS)
+    {
+        Kern::Printf("%s: cannot configure the West Bridge device - code %d\n", pgm, ret) ;
+        return 1 ;
+    }
+	Kern::Printf("** Register Callback done\n") ;
+	/*
+	 * Download the firmware to the device.  Until the firmware has been downloaded, the West Bridge
+	 * device cannot response to requests from the P port processor.
+	 */
+	Kern::Printf("** Download firmware...\n") ;
+#ifdef FIRMWARE_NOPPORT
+
+#ifdef OVERCLOCK_SD
+	ret = CyAsMiscDownloadFirmware(dev_handle, CyAnFirmware.fw_image,
+			(uint16_t)CYANFW_SIZE, 0, 0) ;
+#else
+	ret = CyAsMiscDownloadFirmware(dev_handle, cyastfw_sd_mmc_rel_nopport_array.fw_image,
+			(uint16_t)CYASTFW_SD_MMC_REL_NOPPORT_SIZE, 0, 0) ;
+#endif
+
+#else
+
+#ifdef OVERCLOCK_SD
+	ret = CyAsMiscDownloadFirmware(dev_handle, CyAnFirmware.fw_image,
+			(uint16_t)CYANFW_SIZE, 0, 0) ;
+#else
+	ret = CyAsMiscDownloadFirmware(dev_handle, cyastfw_sd_mmc_rel_silicon_array.fw_image,
+			(uint16_t)CYASTFW_SD_MMC_REL_SILICON_SIZE, 0, 0) ;
+#endif
+
+#endif
+	if (ret != CY_AS_ERROR_SUCCESS)
+	{
+		Kern::Printf("%s: cannot download the antioch firmware - code %d\n", pgm, ret) ;
+		return 1 ;
+	}
+	Kern::Printf("** API device loaded firmware\n") ;
+
+    /*
+     * Note, if a firmware image is not provided, the firmware can still be loaded if the West Bridge device
+     * is in DEBUG mode.  In debug mode, the firmware can be loaded via USB.  In general however, the firmware
+     * should be loaded via the P Port and therefore a firmware file name should be provided.
+     */
+
+    /*
+     * Wait for the initialization event telling me that the firmware
+     * has completed initialization and is ready to go.  The sleep of 100 ms
+     * is used to insure that we do not burn too many CPU cycles while waiting on
+     * the firmware initialization to finish.
+     */
+#ifndef FIRMWARE_NOPPORT
+    while (!gInitComplete)
+    {
+		NKern::Sleep(1000);
+	}
+
+	Kern::Printf("Firmware donwloaded successfully!!") ;
+#if 0
+#ifdef STORAGE_TEST
+	ret = CyAsSymbianStorageTest("Symbian WB Test",dev_handle, tag);
+	if ( !ret )
+	{
+		Kern::Printf("%s: CyAsSymbianStorageTest failed - code %d\n", pgm, ret) ;
+		return 1 ;
+	}
+	Kern::Printf("** CyAsSymbianStorageTest done\n") ;
+#else
+	ret = CyAsAPIUsbInit("Symbian WB Test",dev_handle, tag);
+	if ( !ret )
+	{
+		Kern::Printf("%s: CyAsAPIUsbInit failed - code %d\n", pgm, ret) ;
+		return 1 ;
+	}
+	Kern::Printf("** CyAsAPIUsbInit done\n") ;
+#endif
+#endif
+
+#endif
+
+	ret = CyAsAPIGetHandle(dev_handle, tag);
+	Kern::Printf("** CyAsAPIGetHandle done\n") ;
+
+	return 0 ;
+}
+
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+
+#else
+/*
+* Some compilers do not like empty C files, so if the OMAP hal is not being
+* compiled, we compile this single function.  We do this so that for a given target
+* HAL there are not multiple sources for the HAL functions.
+*/
+void MyOMAPKernelHalDummyFunction(
+	void)
+{
+}
+
+#endif
+
+
+