blob: d17d4eabdf2b8b1ef64f7e9df3be2f7befa37857 [file] [log] [blame]
/******************************************************************************
Copyright (c) 2006-2015 Lantiq Deutschland GmbH
Copyright (c) 2015 Lantiq Beteiligungs-GmbH & Co.KG
Copyright 2018, Intel Corporation.
For licensing information, see the file 'LICENSE' in the root folder of
this software module.
******************************************************************************/
/**
\file dxs_access.c
Implementation of low level access functions.
*/
/* ========================================================================== */
/* Includes */
/* ========================================================================== */
#include <stdio.h>
#include <semaphore.h>
#include "dxs_lib.h"
#include "dxs_errno.h"
#include "dxs_error.h"
#include "dxs_spi.h"
#include "dxs_access.h"
#include "dxs_init.h"
#ifdef EVENT_LOGGER_DEBUG
#include <sys/ioctl.h>
#endif
#include "dxslic_spi.h"
/* ========================================================================== */
/* Macro definitions */
/* ========================================================================== */
/* SPI header length in bytes */
#define DXS_SPI_HDR_LENGTH 2 /* in bytes */
/* ========================================================================== */
/* Type definitions */
/* ========================================================================== */
/* ========================================================================== */
/* Global variables */
/* ========================================================================== */
/* ========================================================================== */
/* Function prototypes */
/* ========================================================================== */
#if 0
//debug
static int32_t dxs_spi_write(
int fd,
uint8_t offset,
uint16_t *pbuf,
uint32_t len);
static int32_t dxs_spi_read(
int fd,
uint8_t offset,
uint16_t *pbuf,
uint32_t len);
#endif
/* ========================================================================== */
/* Function implementation */
/* ========================================================================== */
#ifdef EVENT_LOGGER_DEBUG
static void dxs_el_trace_reg_wr(DXS_DEVICE_t *pDev,
int32_t reg_offset,
uint16_t *reg_data,
int32_t data_count)
{
EL_IoctlAddLog_t stLog;
stLog.nLogType = EL_LOG_TYPE_REG_WR;
stLog.nOrygLogType = EL_MAX_LOG_TYPE;
stLog.nDevType = DXS_LIB_DEV_TYPE;
stLog.nDevNum = pDev->nDevNum;
stLog.nChNum = 0;
stLog.uLogDetails.stReg_Wr.nReg_offset = (reg_offset);
stLog.uLogDetails.stReg_Wr.pReg_data = (uint8_t *)(reg_data);
stLog.uLogDetails.stReg_Wr.nDataLength = (data_count);
stLog.uLogDetails.stReg_Wr.nCount = (data_count);
ioctl(pDev->el_fd, EL_ADD_LOG, (int32_t)&stLog);
}
static void dxs_el_trace_reg_rd(DXS_DEVICE_t *pDev,
int32_t reg_offset,
uint16_t *reg_data,
int32_t data_count)
{
EL_IoctlAddLog_t stLog;
stLog.nLogType = EL_LOG_TYPE_REG_RD;
stLog.nOrygLogType = EL_MAX_LOG_TYPE;
stLog.nDevType = DXS_LIB_DEV_TYPE;
stLog.nDevNum = pDev->nDevNum;
stLog.nChNum = 0;
stLog.uLogDetails.stReg_Rd.nReg_offset = (reg_offset);
stLog.uLogDetails.stReg_Rd.pReg_data = (uint8_t *)(reg_data);
stLog.uLogDetails.stReg_Rd.nDataLength = (data_count);
stLog.uLogDetails.stReg_Rd.nCount = (data_count);
ioctl(pDev->el_fd, EL_ADD_LOG, (int32_t)&stLog);
}
#endif
static uint32_t dxs_spi_max_frag_size_get()
{
DXS_DEVICE_t *pDev = dxs_get_dev(0);
return (pDev->access == DXS_ACCESS_CSI) ?
SPI_MAX_BUFF_SIZE_CSI : SPI_MAXBYTES_SIZE;
}
/**
Write a value to one chip register.
\param pDev Pointer to device struct.
\param offset Register offset (address).
\param pValue 16-bit word value to write.
\return
Error code from dxs_spi_write().
*/
int32_t DXS_RegWrite( DXS_DEVICE_t *pDev,
uint8_t offset,
uint16_t nValue)
{
int32_t ret;
if (pDev->access == DXS_ACCESS_CSI)
{
uint16_t tmp[2];
tmp[0] = tmp[1] = nValue;
ret = dxs_spi_write(pDev->spidev, offset, tmp, sizeof(tmp));
}
else
{
uint16_t tmp = nValue;
ret = dxs_spi_write(pDev->spidev, offset, &tmp, 2);
}
#ifdef EVENT_LOGGER_DEBUG
dxs_el_trace_reg_wr(pDev, offset, &nValue, 2);
#endif
/* fprintf (stderr, "[rw] %02X: %04X\n", offset, tmp); */
DXS_RETURN(ret);
}
/**
Read a value from one chip register.
\param pDev Pointer to device struct.
\param offset Register offset (address).
\param pValue Pointer to variable where to return
the 16-bit word value.
\return
Error code from dxs_spi_read().
*/
int32_t DXS_RegRead( DXS_DEVICE_t *pDev,
uint8_t offset,
uint16_t *pValue)
{
int32_t ret;
if (pDev->access == DXS_ACCESS_CSI)
{
uint16_t tmp[2];
ret = dxs_spi_read(pDev->spidev, offset, tmp, sizeof(tmp));
*pValue = tmp[1];
}
else
{
ret = dxs_spi_read(pDev->spidev, offset, pValue, 2);
}
#ifdef EVENT_LOGGER_DEBUG
dxs_el_trace_reg_rd(pDev, offset, pValue, 2);
#endif
/* fprintf (stderr, "[rr] %02X: %04X\n", offset, *pValue); */
DXS_RETURN(ret);
}
/**
Write multiple consecutive chip registers.
\param pDev Pointer to device struct.
\param offset Register offset (address).
\param pValue Pointer to array with 16-bit word values to write.
\param count Number of 16-bit words to write.
\return
Error code from dxs_spi_write().
*/
int32_t DXS_RegWriteMulti(
DXS_DEVICE_t *pDev,
uint8_t offset,
uint16_t *nValue,
uint8_t count)
{
int32_t ret;
ret = dxs_spi_write(pDev->spidev, offset, nValue, (2*count));
#ifdef EVENT_LOGGER_DEBUG
dxs_el_trace_reg_wr(pDev, offset, nValue, (2*count));
#endif
DXS_RETURN(ret);
}
/**
Read multiple consecutive chip registers.
\param pDev Pointer to device struct.
\param offset Register offset (address).
\param pValue Pointer to array where to return the 16-bit word values.
\param count Number of 16-bit words to read.
\return
Error code from dxs_spi_read().
*/
int32_t DXS_RegReadMulti(
DXS_DEVICE_t *pDev,
uint8_t offset,
uint16_t *pValue,
uint8_t count)
{
int32_t ret;
ret = dxs_spi_read(pDev->spidev, offset, pValue, (2*count));
#ifdef EVENT_LOGGER_DEBUG
dxs_el_trace_reg_rd(pDev, offset, pValue, (2*count));
#endif
DXS_RETURN(ret);
}
/* ************************ endianess correct copy ****************************/
/**
Copy a 16-bit-word buffer into a byte buffer respecting the endianess.
Odd and even offsets and length are handled by this function.
In place copy is possible but otherwise the buffers must not overlap.
\param pBbuf Pointer to byte buffer. (Destination)
\param pWbuf Pointer to 16-bit-word buffer. (Source)
\param nWoffset Offset in bytes within the source buffer.
\param nB Number of bytes to be copied.
*/
void DXS_cpw2b ( uint8_t *pBbuf,
const uint16_t * const pWbuf,
const uint32_t nWoffset,
const uint32_t nB)
{
uint32_t i;
uint16_t nWord;
for (i = 0; i < nB; i++)
{
nWord = pWbuf[(nWoffset + i) >> 1];
pBbuf[i] = (uint8_t)
((((nWoffset+i) % 2) ? nWord : nWord >> 8) & 0xFF);
}
}
/**
Copy a byte buffer into a 16-bit-word buffer respecting the endianness.
In place copy is possible but otherwise the buffers must not overlap.
When an odd number of bytes is to be copied the missing byte in the
16-bit-word is filled with zero.
\param pWbuf Word buffer.
\param pBbuf Byte buffer.
\param nB Number of Bytes to be copied.
*/
void DXS_cpb2w ( uint16_t *pWbuf,
const uint8_t * const pBbuf,
uint32_t nB)
{
uint32_t i;
uint16_t nWord;
for (i=0; i < nB; i+=2)
{
/* Copy each byte separately into the word buffer. Make sure that when
an index out of range is addressed zero is copied instead. */
nWord = (uint16_t)pBbuf[i+0] << 8;
if ((i+1) < nB)
{
/* LSB is only added when index is within range. */
nWord |= (uint16_t)pBbuf[i+1];
}
pWbuf[i>>1] = nWord;
}
}
/**
Copy a byte buffer into a 32-bit-word buffer respecting the endianness.
In place copy is possible but otherwise the buffers must not overlap.
When an odd number of bytes is to be copied the missing bytes in the
32-bit-word is filled with zero.
\param pWbuf Word buffer.
\param pBbuf Byte buffer.
\param nB Number of Bytes to be copied.
*/
void DXS_cpb2dw ( uint32_t *pWbuf,
const uint8_t * const pBbuf,
uint32_t nB)
{
uint32_t i;
uint32_t nWord;
for (i=0; i < nB; i+=4)
{
/* Copy each byte separately into the word buffer. Make sure that when
an index out of range is addressed zero is copied instead. */
nWord = (uint32_t)pBbuf[i+0] << 24;
if ((i+1) < nB)
nWord |= (uint32_t)pBbuf[i+1] << 16;
if ((i+2) < nB)
nWord |= (uint32_t)pBbuf[i+2] << 8;
if ((i+3) < nB)
nWord |= (uint32_t)pBbuf[i+3];
pWbuf[i>>2] = nWord;
}
}
/***************************** SPI bus adaption ******************************/
/**
Write a number of bytes to a given DXS register using SPI.
If the length of the data exceeds the SPI_MAXBYTES_SIZE this function
fragments the data into multiple blocks and calls the SPI write interface
repeatedly until all data is transmitted.
\param fd Filedescriptor of the SPI driver.
\param offset DXS register address.
\param pbuf Pointer to buffer with data to be written.
\param len Number of bytes to write.
\return
DXS_statusOk or error code.
*/
int32_t dxs_spi_write(int fd, uint8_t offset, uint16_t *pbuf, uint32_t len)
{
uint8_t tx_buf[SPI_MAXBYTES_SIZE];
uint32_t tx_buf_length,
written,
fragment_length;
int32_t ret = DXS_statusOk;
uint32_t max_frag_sz = dxs_spi_max_frag_size_get();
/* TODO: len and offset assertion */
/* TODO: setup SPI */
/* This variable counts the number of bytes already written. */
written = 0;
/* Build SPI header (16-bit word) */
#if SPI_IOC_WR_BITS_PER_WORD_8BITS
/* SPI_IOC_WR_BITS_PER_WORD is 8bits */
tx_buf[0] = 0x7E;
tx_buf[1] = offset;
if (offset == DXS_REG_CSI_M_CFG || offset == DXS_REG_CSI_M_STAT)
{
tx_buf[1] <<= 1;
}
#else
/* SPI_IOC_WR_BITS_PER_WORD is 16bits */
tx_buf[0] = offset;
tx_buf[1] = 0x7E;
if (offset == DXS_REG_CSI_M_CFG || offset == DXS_REG_CSI_M_STAT)
{
tx_buf[0] <<= 1;
}
#endif
/* The tx_buf now contains the SPI header. */
tx_buf_length = DXS_SPI_HDR_LENGTH;
while ((ret == DXS_statusOk) && (written < len))
{
/* Calculate the amount of payload bytes that can be sent during one
write call. */
fragment_length = len - written; /* remaining bytes to transfer */
if (fragment_length > (max_frag_sz - tx_buf_length))
{
/* Limit the transfer to the maximum the SPI can transmit. */
fragment_length = (max_frag_sz - tx_buf_length);
}
#if SPI_IOC_WR_BITS_PER_WORD_8BITS
/* SPI_IOC_WR_BITS_PER_WORD is 8bits */
/* Copy the payload into the write buffer with correct endianess. */
DXS_cpw2b(tx_buf+tx_buf_length, pbuf, written, fragment_length);
#else
/* SPI_IOC_WR_BITS_PER_WORD is 16bits */
memcpy(tx_buf+tx_buf_length, (uint8_t *)pbuf+written, fragment_length);
#endif
/* Increment by the number of payload bytes written. */
tx_buf_length += fragment_length;
written += fragment_length;
/* Access SPI for writing (rx buffer is NULL). */
ret = dxs_spi_linux_read_write(fd, tx_buf, NULL, tx_buf_length);
/* ret is evaluated before the next iteration. */
/* Preserve and reuse the SPI header for the next fragment. */
tx_buf_length = DXS_SPI_HDR_LENGTH;
}
DXS_RETURN(ret);
}
/**
Read a number of bytes from a given DXS register using SPI.
If the length of the data to be read exceeds the SPI_MAXBYTES_SIZE this
function calls the SPI read function repeatedly until all data is read
and reassembles the data in the return buffer.
\param fd Filedescriptor of the SPI driver.
\param offset DXS register address.
\param pbuf Pointer to buffer where to return the data.
\param len Number of bytes to be read.
\return
DXS_statusOk or error code.
*/
int32_t dxs_spi_read(int fd, uint8_t offset, uint16_t *pbuf, uint32_t len)
{
uint8_t tx_buf[SPI_MAXBYTES_SIZE] = {0},
rx_buf[SPI_MAXBYTES_SIZE] = {0};
uint32_t tx_buf_length,
read,
fragment_length;
int32_t ret = DXS_statusOk;
uint32_t max_frag_sz = dxs_spi_max_frag_size_get();
/* TODO: len and offset assertion */
/* TODO: setup SPI */
/* tx_buf is the buffer for writing the read request. Because SPI in general
is full duplex the complete buffer is set to zero to avoid sending random
data after the SPI header that we set below. */
/* Build SPI header (16-bit word) */
#if SPI_IOC_WR_BITS_PER_WORD_8BITS
/* SPI_IOC_WR_BITS_PER_WORD is 8bits */
tx_buf[0] = 0xBE;
tx_buf[1] = offset;
if (offset == DXS_REG_CSI_M_CFG || offset == DXS_REG_CSI_M_STAT)
{
tx_buf[1] <<= 1;
}
#else
/* SPI_IOC_WR_BITS_PER_WORD is 16bits */
tx_buf[0] = offset;
tx_buf[1] = 0xBE;
if (offset == DXS_REG_CSI_M_CFG || offset == DXS_REG_CSI_M_STAT)
{
tx_buf[0] <<= 1;
}
#endif
/* The tx_buf contains now the SPI header. */
tx_buf_length = DXS_SPI_HDR_LENGTH;
/* Loop while more data needs to be read. */
for (read = 0; (ret == DXS_statusOk) && (read < len); read += fragment_length)
{
/* Calculate the amount of payload bytes that can be read during one
read call. */
fragment_length = len - read; /* remaining bytes to read */
if (fragment_length > (max_frag_sz - tx_buf_length))
{
/* Limit the transfer to the maximum the SPI can transmit. */
fragment_length = (max_frag_sz - tx_buf_length);
}
/* Access SPI for writing and reading. */
ret = dxs_spi_linux_read_write(fd, tx_buf, rx_buf,
tx_buf_length + fragment_length);
if (ret == DXS_statusOk)
{
#if SPI_IOC_WR_BITS_PER_WORD_8BITS
/* SPI_IOC_WR_BITS_PER_WORD is 8bits */
/* Copy the byte buffer into the 16-bit-word buffer respecting
the endianess. */
DXS_cpb2w (pbuf + (read/2),
rx_buf + DXS_SPI_HDR_LENGTH, fragment_length);
#else
/* SPI_IOC_WR_BITS_PER_WORD is 16bits */
memcpy((uint8_t *)pbuf+read, rx_buf + DXS_SPI_HDR_LENGTH, fragment_length);
#endif
}
/* ret is evaluated before the next iteration. */
}
DXS_RETURN(ret);
}
/**
Return the number of payload bytes that can be transferred over SPI
without being fragmented into multiple SPI transfers.
\return
Maximum number of bytes for a single SPI transfer.
*/
uint32_t DXS_spi_blocksize_get(DXS_DEVICE_t *pDev)
{
return (pDev->access == DXS_ACCESS_CSI) ?
(SPI_MAX_BUFF_SIZE_CSI - DXS_SPI_HDR_LENGTH) :
(SPI_MAXBYTES_SIZE - DXS_SPI_HDR_LENGTH);
}
/**
Get device interrupt status.
\return
none
*/
void DXS_StatusGet(DXS_DEVICE_t *pDev)
{
uint16_t nRegInt1 = 0, nRegInt2 = 0;
if (pDev->flags & DXS_DEV_ACCESS_INITIALIZED)
{
DXS_RegRead(pDev, DXS_REG_INT1, &nRegInt1);
if (nRegInt1 & DXS_REG_INT1_ERR)
{
DXS_RegRead(pDev, DXS_REG_INT2, &nRegInt2);
if (nRegInt2 & DXS_REG_INT2_CBO_UFL)
{
fprintf (stderr, "Out-Box Underflow, dev%d\n", pDev->nDevNum);
/* acknowledge the interrupt */
DXS_RegWrite(pDev, DXS_REG_INT2, nRegInt2);
}
if (nRegInt2 & DXS_REG_INT2_CBI_OFL)
{
fprintf (stderr, "In-Box Overflow, dev%d\n", pDev->nDevNum);
/* acknowledge the interrupt */
DXS_RegWrite(pDev, DXS_REG_INT2, nRegInt2);
}
}
/* new data in the outbox */
if ((nRegInt1 & DXS_REG_INT1_CBO_RDY) &&
(pDev->flags & DXS_DEV_OBX_HND_INITIALIZED))
sem_post(&pDev->obxSemaphore);
}
}