/*
 * MlxPciPriv.c
 *
 *  Created on: Jan 21, 2015
 *      Author: maord
 */

#include <ipxe/pci.h>

#include "../../mlx_utils/include/private/mlx_pci_priv.h"


static
mlx_status
mlx_pci_config_byte(
		IN mlx_utils *utils,
		IN mlx_boolean read,
		IN mlx_uint32 offset,
		IN OUT mlx_uint8 *buffer
		)
{
	mlx_status status = MLX_SUCCESS;
	if (read) {
		status = pci_read_config_byte(utils->pci, offset, buffer);
	}else {
		status = pci_write_config_byte(utils->pci, offset, *buffer);
	}
	return status;
}

static
mlx_status
mlx_pci_config_word(
		IN mlx_utils *utils,
		IN mlx_boolean read,
		IN mlx_uint32 offset,
		IN OUT mlx_uint16 *buffer
		)
{
	mlx_status status = MLX_SUCCESS;
	if (read) {
		status = pci_read_config_word(utils->pci, offset, buffer);
	}else {
		status = pci_write_config_word(utils->pci, offset, *buffer);
	}
	return status;
}

static
mlx_status
mlx_pci_config_dword(
		IN mlx_utils *utils,
		IN mlx_boolean read,
		IN mlx_uint32 offset,
		IN OUT mlx_uint32 *buffer
		)
{
	mlx_status status = MLX_SUCCESS;
	if (read) {
		status = pci_read_config_dword(utils->pci, offset, buffer);
	}else {
		status = pci_write_config_dword(utils->pci, offset, *buffer);
	}
	return status;
}
static
mlx_status
mlx_pci_config(
		IN mlx_utils *utils,
		IN mlx_boolean read,
		IN mlx_pci_width width,
		IN mlx_uint32 offset,
		IN mlx_uintn count,
		IN OUT mlx_void *buffer
		)
{
	mlx_status status = MLX_SUCCESS;
	mlx_uint8 *tmp =  (mlx_uint8*)buffer;
	mlx_uintn iteration = 0;
	if( width == MlxPciWidthUint64) {
		width = MlxPciWidthUint32;
		count = count * 2;
	}

	for(;iteration < count ; iteration++) {
		switch (width){
		case MlxPciWidthUint8:
			status = mlx_pci_config_byte(utils, read , offset++, tmp++);
			break;
		case MlxPciWidthUint16:
			status = mlx_pci_config_word(utils, read , offset, (mlx_uint16*)tmp);
			tmp += 2;
			offset += 2;
			break;
		case MlxPciWidthUint32:
			status = mlx_pci_config_dword(utils, read , offset, (mlx_uint32*)tmp);
			tmp += 4;
			offset += 4;
			break;
		default:
			status = MLX_INVALID_PARAMETER;
		}
		if(status != MLX_SUCCESS) {
			goto config_error;
		}
	}
config_error:
	return status;
}
mlx_status
mlx_pci_init_priv(
			IN mlx_utils *utils
			)
{
	mlx_status status = MLX_SUCCESS;
	adjust_pci_device ( utils->pci );
#ifdef DEVICE_CX3
	utils->config = pci_ioremap ( utils->pci, pci_bar_start ( utils->pci, PCI_BASE_ADDRESS_0),
			0x100000 );
#endif
	return status;
}

mlx_status
mlx_pci_teardown_priv(
			IN mlx_utils *utils __attribute__ ((unused))
			)
{
	mlx_status status = MLX_SUCCESS;
#ifdef DEVICE_CX3
	iounmap( utils->config );
#endif
	return status;
}

mlx_status
mlx_pci_read_priv(
			IN mlx_utils *utils,
			IN mlx_pci_width width,
			IN mlx_uint32 offset,
			IN mlx_uintn count,
			OUT mlx_void *buffer
			)
{
	mlx_status status = MLX_SUCCESS;
	status = mlx_pci_config(utils, TRUE, width, offset, count, buffer);
	return status;
}

mlx_status
mlx_pci_write_priv(
			IN mlx_utils *utils,
			IN mlx_pci_width width,
			IN mlx_uint32 offset,
			IN mlx_uintn count,
			IN mlx_void *buffer
			)
{
	mlx_status status = MLX_SUCCESS;
	status = mlx_pci_config(utils, FALSE, width, offset, count, buffer);
	return status;
}

mlx_status
mlx_pci_mem_read_priv(
				IN mlx_utils *utils  __attribute__ ((unused)),
				IN mlx_pci_width width  __attribute__ ((unused)),
				IN mlx_uint8 bar_index  __attribute__ ((unused)),
				IN mlx_uint64 offset,
				IN mlx_uintn count  __attribute__ ((unused)),
				OUT mlx_void *buffer
				)
{
	if (buffer == NULL || width != MlxPciWidthUint32)
		return MLX_INVALID_PARAMETER;
	*((mlx_uint32 *)buffer) = readl(offset);
	return MLX_SUCCESS;
}

mlx_status
mlx_pci_mem_write_priv(
				IN mlx_utils *utils  __attribute__ ((unused)),
				IN mlx_pci_width width  __attribute__ ((unused)),
				IN mlx_uint8 bar_index  __attribute__ ((unused)),
				IN mlx_uint64 offset,
				IN mlx_uintn count  __attribute__ ((unused)),
				IN mlx_void *buffer
				)
{
	if (buffer == NULL || width != MlxPciWidthUint32)
		return MLX_INVALID_PARAMETER;
	barrier();
	writel(*((mlx_uint32 *)buffer), offset);
	return MLX_SUCCESS;
}
