/*+-----------------------------------------------------------------------+  */
/*| (C) ADDI-DATA GmbH Dieselstra�e 3 D-77833 Ottersweier                 |  */
/*+-----------------------------------------------------------------------+  */
/*| Tel : +49 (0) 7223/9493-0     | email : info@addi-data.com            |  */
/*| Fax : +49 (0) 7223/9493-92    | Internet : http://www.addi-data.com   |*/
/*+-------------------------------+---------------------------------------+  */
/*| Project : APCI-3600           | Compiler : Microsoft Visual C++       |  */
/*| Module name : buff_alloc.c    |  Version : 6.0                        |  */
/*+-------------------------------+---------------------------------------+  */
/*| Author : Guinot Catherine       Date : 05/08/2004                     |  */
/*+-----------------------------------------------------------------------+  */
/*| Description : Allocation of the continuous memory or descriptors      |  */
/*|               and ring buffer                                         |  */
/*+-----------------------------------------------------------------------+  */
/*|                             UPDATES                                   |  */
/*+----------+-----------+------------------------------------------------+  */
/*| Date     | Author    | Description of updates                         |  */
/*+----------+-----------+------------------------------------------------+  */
/*|          |           |                                                |  */
/*+----------+-----------+------------------------------------------------+  */
#include "apci3600-private.h"
#if defined(CONFIG_CMA)
#include <linux/dma-mapping.h>
#elif defined(CONFIG_BIGPHYS_AREA)
#include <linux/bigphysarea.h>
#else
#warning Neither CONFIG_CMA nor CONFIG_BIGPHYS_AREA was set! Memory allocation may fail because of this.
#endif

#ifdef BUFF_ALLOC_DEBUG
#define BUFF_ALLOC_DPRINTK(fmt, args...)	printk("%s: " fmt, __FUNCTION__ , ## args)
#define BUFF_ALLOC_MARK(); printk( "%s: %s:%i\n",__FILE__, __FUNCTION__ , __LINE__)
#else
#define BUFF_ALLOC_DPRINTK(fmt, args...)
#define BUFF_ALLOC_MARK();
#endif	


/* allocate buffer in bigphysicalarea managed memory */  
int __needlock i_ADDIUTILS_GetDMABufferAllocationInformation(struct pci_dev *pdev,str_PCIDMAInformation *ps_PCAllocatedMemoryBuffer)
{
	uint32_t nbBigPhysAllocatedPages;

#if defined(CONFIG_BIGPHYS_AREA)
	int i_FreeMemorySize = 0;	

	BUFF_ALLOC_MARK();

	/* Get the big physical area memory size */
	bigphysarea_kinfo (&i_FreeMemorySize);

	/* Bytes in pages */
	nbBigPhysAllocatedPages = (i_FreeMemorySize >> PAGE_SHIFT);

	/* We store this address to free this memory later */
	ps_PCAllocatedMemoryBuffer->BigphysareaHandle = (void *) bigphysarea_alloc_pages (nbBigPhysAllocatedPages, 1, (GFP_KERNEL | GFP_DMA));
#else
    dma_addr_t dma_handle;

	BUFF_ALLOC_MARK();

    nbBigPhysAllocatedPages = 4096; /* in bytes */
    dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
    ps_PCAllocatedMemoryBuffer->BigphysareaHandle = dma_alloc_coherent(&pdev->dev, nbBigPhysAllocatedPages, &dma_handle, GFP_KERNEL | GFP_DMA);
#endif

	/* Set a working copy */
	ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0] = ps_PCAllocatedMemoryBuffer->BigphysareaHandle;

	/* Check if the allocation is done as wish */
	if (ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0] != NULL) 
	{	
		/* Size in bytes of the allocated buffer */
		ps_PCAllocatedMemoryBuffer->ul_BufferSize = nbBigPhysAllocatedPages * PAGE_SIZE;
		ps_PCAllocatedMemoryBuffer->ul_BufferNbr = 1;
		ps_PCAllocatedMemoryBuffer->ul_Status = 1;
		
	#if defined(__MIPSEL__)	
		/* #warning MIPS (EL) */
		dma_cache_wback_inv((unsigned long)ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0],ps_PCAllocatedMemoryBuffer->ul_BufferSize);
		ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0] = (uint32_t *)KSEG1ADDR(ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0]);
		ps_PCAllocatedMemoryBuffer->ul_PhysicalAddress [0] = PHYSADDR((volatile void *)ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0]);
	#elif defined(__MIPSEB__)
		/* #warning MIPS (EB) */
		dma_cache_wback_inv(ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0],ps_PCAllocatedMemoryBuffer->ul_BufferSize);
		ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0] = (uint32_t *)KSEG1ADDR(ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0]);
		ps_PCAllocatedMemoryBuffer->ul_PhysicalAddress [0] = PHYSADDR((volatile void *)ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0]);
	#else
		/* #warning other than MIPS */
		#if LINUX_VERSION_CODE > KERNEL_VERSION(3,16,0)
			ps_PCAllocatedMemoryBuffer->ul_PhysicalAddress [0] = virt_to_phys((volatile void *)ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0]);
		#else
			ps_PCAllocatedMemoryBuffer->ul_PhysicalAddress [0] = virt_to_bus((volatile void *)ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0]);
		#endif
	#endif

		BUFF_ALLOC_DPRINTK("Bigphysarea buffer: Address returned by bigphysarea_alloc_pages : %x\n",(uint32_t) ps_PCAllocatedMemoryBuffer->BigphysareaHandle);
		BUFF_ALLOC_DPRINTK("Bigphysarea buffer: Address to use : %x\n",(uint32_t)  ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0]);
		BUFF_ALLOC_DPRINTK("Bigphysarea buffer: Allocator Buffer Phys Address : %x\n",(uint32_t) ps_PCAllocatedMemoryBuffer->ul_PhysicalAddress [0]);
		BUFF_ALLOC_DPRINTK("Bigphysarea buffer: Allocator Buffer Size : %u KBytes\n",(ps_PCAllocatedMemoryBuffer->ul_BufferSize / 1024));
		BUFF_ALLOC_DPRINTK("Bigphysarea buffer: Number of pages allocated: %lu of %lu Bytes\n",ps_PCAllocatedMemoryBuffer->ul_BufferSize/ PAGE_SIZE,PAGE_SIZE);
	}
	else 
	{
		ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0] = NULL;
		ps_PCAllocatedMemoryBuffer->ul_PhysicalAddress [0] = 0;
		ps_PCAllocatedMemoryBuffer->ul_BufferSize = 0;
		ps_PCAllocatedMemoryBuffer->ul_BufferNbr = 0;
		ps_PCAllocatedMemoryBuffer->ul_Status = 0;
	}

	/*the pv_DMABufferRing0[1] is an alias of pv_DMABufferRing0[0], ie it s the same address space (physical and logical)*/
	ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[1] = ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0];
	ps_PCAllocatedMemoryBuffer->ul_PhysicalAddress [1] = ps_PCAllocatedMemoryBuffer->ul_PhysicalAddress [0];

	/* Return on pointer on the allocated memory */
	return ((unsigned long)ps_PCAllocatedMemoryBuffer->pv_DMABufferRing0[0]);
}

int __needlock i_ADDIUTILS_AllocDescriptorsMemory (struct pci_dev *pdev, uint32_t size)
{
    str_descriptorMemory *descriptorMemory = &(APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.descriptorMemory);
	uint32_t nPages;

#if defined(CONFIG_BIGPHYS_AREA)
	int freeMem = 0;

	/* Get the big physical area memory size */
	bigphysarea_kinfo (&freeMem);

	/* Bytes to allocate in pages */
	nPages = (size >> PAGE_SHIFT);

	/* We store this address to free this memory later */
	descriptorMemory->pv_DescriptorMemoryAddressForFree = (void *) bigphysarea_alloc_pages (nPages, 1, GFP_KERNEL);
#else
    dma_addr_t dma_handle;

    nPages = 4096; /* in bytes */
    dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
    descriptorMemory->pv_DescriptorMemoryAddressForFree = dma_alloc_coherent(&pdev->dev, nPages, &dma_handle, GFP_KERNEL);
#endif

	/* Check if the allocation is done */
	if (descriptorMemory->pv_DescriptorMemoryAddressForFree == NULL)
	{
		return 0;
	}

	descriptorMemory->pv_DescriptorMemoryAddress = descriptorMemory->pv_DescriptorMemoryAddressForFree;
	descriptorMemory->dw_DescriptorMemorySize = (nPages * PAGE_SIZE);

#if defined(__MIPSEL__)
	/* #warning MIPS (EL) */
	dma_cache_wback_inv ((unsigned long)descriptorMemory->pv_DescriptorMemoryAddress, descriptorMemory->dw_DescriptorMemorySize);
	descriptorMemory->pv_DescriptorMemoryAddress = (uint32_t *)KSEG1ADDR(descriptorMemory->pv_DescriptorMemoryAddress);
	descriptorMemory->dw_DescriptorMemoryPhysicalAddress = PHYSADDR((volatile void *)descriptorMemory->pv_DescriptorMemoryAddress);
#elif defined(__MIPSEB__)
	/* #warning MIPS (EB) */
	dma_cache_wback_inv (descriptorMemory->pv_DescriptorMemoryAddress, descriptorMemory->dw_DescriptorMemorySize);
	descriptorMemory->pv_DescriptorMemoryAddress = (uint32_t *)KSEG1ADDR(descriptorMemory->pv_DescriptorMemoryAddress);
	descriptorMemory->dw_DescriptorMemoryPhysicalAddress = PHYSADDR((volatile void *)descriptorMemory->pv_DescriptorMemoryAddress);
#else
	/* #warning other than MIPS */
	#if LINUX_VERSION_CODE > KERNEL_VERSION(3,16,0)
		descriptorMemory->pv_DescriptorMemoryAddress = (void *)virt_to_phys((volatile void *) descriptorMemory->pv_DescriptorMemoryAddress);
	#else
		descriptorMemory->pv_DescriptorMemoryAddress = (void *)virt_to_bus((volatile void *) descriptorMemory->pv_DescriptorMemoryAddress);
	#endif
#endif

//	printk ("descriptorMemory->pv_DescriptorMemoryAddress %x\n", descriptorMemory->pv_DescriptorMemoryAddress);
//	printk ("descriptorMemory->dw_DescriptorMemoryPhysicalAddress %x\n", descriptorMemory->dw_DescriptorMemoryPhysicalAddress);
//	printk ("descriptorMemory->pv_DescriptorMemoryAddressForFree %x\n", descriptorMemory->pv_DescriptorMemoryAddressForFree);
//	printk ("descriptorMemory->dw_DescriptorMemorySize %u\n", descriptorMemory->dw_DescriptorMemorySize);

	return (descriptorMemory->dw_DescriptorMemorySize);
}

//--------------------------------------------------------------------------------
/* Update the board Analog Input / Chronometer Computer Buffer information, using data set by a call to i_ADDIUTILS_GetDMABufferAllocationInformation 
 * 
 * may be used to reset the state of the buffer
 * 
 * */
int __needlock i_ADDIUTILS_ResetDMABuffer(struct pci_dev * pdev)
{
	BUFF_ALLOC_MARK();	
	 	
	/* save address of first cell of the bigphysarea allocated buffer for RING0 */  
	APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.pdw_DMABuffer[0] =
		(uint32_t)(uintptr_t)APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_BoardInfos.s_PCAllocatedMemoryBuffer.pv_DMABufferRing0[0];

	BUFF_ALLOC_DPRINTK ("APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.pdw_DMABuffer[0]=%X\n",APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.pdw_DMABuffer[0]);

	/* save address of first cell of the bigphysarea allocated buffer for RING3 (actually the same as RING0) */
	APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.pdw_DMABuffer[1] =
	APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.pdw_DMABuffer[0];

	/* save physical address */
	APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.dw_PhysicalAddress =
	APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_BoardInfos.s_PCAllocatedMemoryBuffer.ul_PhysicalAddress[0];

	BUFF_ALLOC_DPRINTK ("APCI3600_PRIVDATA(pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.dw_PhysicalAddress : %lX\n", (unsigned long)APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.dw_PhysicalAddress);

	/* save buffer size */
	APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.dw_BufferSize = 
	APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_BoardInfos.s_PCAllocatedMemoryBuffer.ul_BufferSize;

	BUFF_ALLOC_DPRINTK ("Bigphysarea Handle: %lX\n",(unsigned long)APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_BoardInfos.s_PCAllocatedMemoryBuffer.BigphysareaHandle);
	BUFF_ALLOC_DPRINTK ("Bigphysarea: start virt %lX start phys %lX size %lX\n",
		(unsigned long)APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_BoardInfos.s_PCAllocatedMemoryBuffer.pv_DMABufferRing0[0],
		(unsigned long)APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_BoardInfos.s_PCAllocatedMemoryBuffer.ul_PhysicalAddress[0],
		(unsigned long)APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_BoardInfos.s_PCAllocatedMemoryBuffer.ul_BufferSize);

	BUFF_ALLOC_DPRINTK ("Ringbuffer: start virt %lX start phys %lX size %lX\n",
		(unsigned long)APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.pdw_DMABuffer[0],
		(unsigned long)APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.dw_PhysicalAddress,
		(unsigned long)APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.dw_BufferSize);

	/* reset state of buffer */ 
	APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.b_Empty = 1;
	APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.b_Full = 0;
	APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.dw_ReadPointer = 0;
	APCI3600_PRIVDATA (pdev)->s_PCI3600_BoardInformation.s_AnaInpChronomPCDMABufferInfos.dw_WritePointer = 0;
	
	return 0;
}
