You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
297 lines
7.7 KiB
297 lines
7.7 KiB
/* vim: set ts=4 sw=4 et fdm=marker: */
|
|
/* {{{ headers */
|
|
#include <vxWorks.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <logLib.h>
|
|
#include <vxBusLib.h>
|
|
#include <semLib.h>
|
|
#include <taskLib.h>
|
|
#include <sysLib.h>
|
|
#include <tickLib.h>
|
|
#include <iosLib.h>
|
|
#include <errnoLib.h>
|
|
#include <hwif/vxbus/vxBus.h>
|
|
#include <hwif/vxbus/hwConf.h>
|
|
#include <hwif/vxbus/vxbPlbLib.h>
|
|
#include <hwif/util/hwMemLib.h>
|
|
#include <hwif/util/vxbParamSys.h>
|
|
#include <hwif/vxbus/vxbSpiLib.h>
|
|
/* }}} */
|
|
|
|
/* {{{ FIO_SPIXFER msgs */
|
|
#define FIO_XFER_TX (1 << 0)
|
|
#define FIO_XFER_RX (1 << 1)
|
|
#define FIO_XFER_DUPLEX ((FIO_XFER_TX) | (FIO_XFER_RX))
|
|
|
|
/* ioctl */
|
|
#define FIO_SPIXFER (('P' << 16) | 0x1001)
|
|
|
|
struct spi_xfer {
|
|
int len;
|
|
int xferDir;
|
|
UINT8 *txbuf;
|
|
UINT8 *rxbuf;
|
|
};
|
|
|
|
struct spi_xfer_msg {
|
|
int count;
|
|
struct spi_xfer *xfer;
|
|
};
|
|
/* }}} */
|
|
|
|
/* {{{ spiRawDev driver data */
|
|
struct SpiRawDevDesc {
|
|
DEV_HDR devHdr;
|
|
|
|
VXB_DEVICE_ID pDev;
|
|
char name[16];
|
|
int refCount;
|
|
|
|
VXB_SPI_MAST_SPEC *specialInfo;
|
|
|
|
SEM_ID mutex;
|
|
};
|
|
/* }}} */
|
|
|
|
/* {{{ device methods */
|
|
LOCAL struct SpiRawDevDesc *spiDevOpen(struct SpiRawDevDesc *dev, const char *name, int flags, int mode);
|
|
LOCAL int spiDevClose(struct SpiRawDevDesc *dev);
|
|
LOCAL int sipDevIoctl(struct SpiRawDevDesc *dev, int cmd, _Vx_ioctl_arg_t arg);
|
|
/* }}} */
|
|
|
|
/* {{{ VxBus methods */
|
|
LOCAL void spiRawDevInstInit(VXB_DEVICE_ID pDev);
|
|
LOCAL void spiRawDevInstInit2(VXB_DEVICE_ID pDev);
|
|
LOCAL void spiRawDevInstConnect(VXB_DEVICE_ID pDev);
|
|
|
|
LOCAL void spiRawDevShow(VXB_DEVICE_ID pDev, int verbose);
|
|
LOCAL STATUS spiRawDevUnlink(VXB_DEVICE_ID pDev, void *unused);
|
|
/* }}} */
|
|
|
|
/* {{{ Structs */
|
|
/* clang-format off */
|
|
LOCAL struct drvBusFuncs spiRawDevFuncs = {
|
|
spiRawDevInstInit, /* devInstanceInit */
|
|
spiRawDevInstInit2, /* devInstanceInit2 */
|
|
spiRawDevInstConnect /* devConnect */
|
|
};
|
|
|
|
/* Publish the methods for the resources controlled with this file */
|
|
LOCAL struct vxbDeviceMethod spiRawDevMethods[] = {
|
|
DEVMETHOD (busDevShow, spiRawDevShow),
|
|
DEVMETHOD (vxbDrvUnlink, spiRawDevUnlink),
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
LOCAL struct vxbSpiRegister spiRawDevRegister = {
|
|
{
|
|
NULL , /* pNext */
|
|
VXB_DEVID_DEVICE , /* devID */
|
|
VXB_BUSID_SPI , /* busID = SPI */
|
|
VXB_VER_4_0_0 , /* vxbVersion */
|
|
"spiDev" , /* drvName */
|
|
&spiRawDevFuncs , /* pDrvBusFuncs */
|
|
spiRawDevMethods , /* pMethods */
|
|
NULL , /* devProbe */
|
|
NULL , /* pParamDefaults */
|
|
},
|
|
};
|
|
/* clang-format on */
|
|
/* }}} */
|
|
|
|
LOCAL int __spi_driver_node = -1;
|
|
|
|
void vxbSpiRawDevRegister(void)
|
|
{
|
|
(void)vxbDevRegister((struct vxbDevRegInfo *)&spiRawDevRegister);
|
|
}
|
|
|
|
/* {{{ bus functions */
|
|
LOCAL void spiRawDevInstInit(VXB_DEVICE_ID pDev)
|
|
{
|
|
struct SpiRawDevDesc *pDrvCtrl;
|
|
|
|
/* Check for vaild parameter */
|
|
VXB_ASSERT_NONNULL_V(pDev);
|
|
|
|
pDrvCtrl = (struct SpiRawDevDesc *)hwMemAlloc(sizeof(struct SpiRawDevDesc));
|
|
if (pDrvCtrl == NULL) {
|
|
return;
|
|
}
|
|
pDrvCtrl->pDev = pDev;
|
|
pDev->pDrvCtrl = pDrvCtrl;
|
|
|
|
snprintf(pDrvCtrl->name, sizeof(pDrvCtrl->name), "/spi/%d", pDev->unitNumber);
|
|
|
|
vxbNextUnitGet(pDev);
|
|
}
|
|
|
|
LOCAL void spiRawDevInstInit2(VXB_DEVICE_ID pDev)
|
|
{
|
|
struct SpiRawDevDesc *pDrvCtrl;
|
|
FUNCPTR pFunc;
|
|
|
|
/* Check for vaild parameter */
|
|
VXB_ASSERT_NONNULL_V(pDev);
|
|
|
|
pDrvCtrl = (struct SpiRawDevDesc *)pDev->pDrvCtrl;
|
|
|
|
pDrvCtrl->mutex = semBCreate(SEM_Q_PRIORITY, SEM_FULL);
|
|
pDrvCtrl->refCount = 0;
|
|
|
|
pFunc = vxbDevMethodGet(vxbDevParent(pDev), (VXB_METHOD_ID)vxbSpiSpecialGet_desc);
|
|
/* Retrieve the SPI master special information */
|
|
if (pFunc != NULL)
|
|
(*pFunc)(vxbDevParent(pDev), &pDrvCtrl->specialInfo);
|
|
}
|
|
|
|
LOCAL void spiRawDevInstConnect(VXB_DEVICE_ID pDev)
|
|
{
|
|
struct SpiRawDevDesc *pDrvCtrl;
|
|
/* Check for vaild parameter */
|
|
VXB_ASSERT_NONNULL_V(pDev);
|
|
|
|
pDrvCtrl = (struct SpiRawDevDesc *)pDev->pDrvCtrl;
|
|
|
|
if (__spi_driver_node < 0) {
|
|
/* clang-format off */
|
|
__spi_driver_node = iosDrvInstall(
|
|
(DRV_CREATE_PTR)spiDevOpen ,
|
|
(DRV_REMOVE_PTR)NULL ,
|
|
(DRV_OPEN_PTR)spiDevOpen ,
|
|
(DRV_CLOSE_PTR)spiDevClose ,
|
|
(DRV_READ_PTR)NULL ,
|
|
(DRV_WRITE_PTR)NULL ,
|
|
(DRV_IOCTL_PTR)sipDevIoctl);
|
|
/* clang-format on */
|
|
}
|
|
if (__spi_driver_node > 0) {
|
|
semTake(pDrvCtrl->mutex, WAIT_FOREVER);
|
|
iosDevAdd(&pDrvCtrl->devHdr, pDrvCtrl->name, __spi_driver_node);
|
|
semGive(pDrvCtrl->mutex);
|
|
}
|
|
}
|
|
|
|
LOCAL STATUS spiRawDevUnlink(VXB_DEVICE_ID pDev, void *unused)
|
|
{
|
|
struct SpiRawDevDesc *pDrvCtrl;
|
|
|
|
/* Check for vaild parameter */
|
|
VXB_ASSERT_NONNULL_V(pDev);
|
|
|
|
pDrvCtrl = (struct SpiRawDevDesc *)pDev->pDrvCtrl;
|
|
|
|
if (pDrvCtrl->mutex) {
|
|
semTake(pDrvCtrl->mutex, WAIT_FOREVER);
|
|
|
|
semDelete(pDrvCtrl->mutex);
|
|
pDrvCtrl->mutex = NULL;
|
|
}
|
|
#ifndef _VXBUS_BASIC_HWMEMLIB
|
|
hwMemFree((char *)pDrvCtrl);
|
|
#endif /* _VXBUS_BASIC_HWMEMLIB */
|
|
pDev->pDrvCtrl = NULL;
|
|
return OK;
|
|
}
|
|
|
|
LOCAL void spiRawDevShow(VXB_DEVICE_ID pDev, int verbose)
|
|
{
|
|
struct SpiRawDevDesc *pDrvCtrl = (struct SpiRawDevDesc *)pDev->pDrvCtrl;
|
|
|
|
/* Check for vaild parameter */
|
|
VXB_ASSERT_NONNULL_V(pDev);
|
|
|
|
printf(" %s unit %d on %s @ 0x%08x", pDev->pName, pDev->unitNumber, vxbBusTypeString(pDev->busID), pDev);
|
|
printf(" with busInfo %p\n", pDev->u.pSubordinateBus);
|
|
|
|
if (verbose) {
|
|
printf(" devName: %s\n", pDrvCtrl->name);
|
|
printf(" refCount: %d\n", pDrvCtrl->refCount);
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ device driver implementations */
|
|
LOCAL struct SpiRawDevDesc *spiDevOpen(struct SpiRawDevDesc *dev, const char *name, int flags, int mode)
|
|
{
|
|
semTake(dev->mutex, WAIT_FOREVER);
|
|
|
|
++dev->refCount;
|
|
|
|
semGive(dev->mutex);
|
|
|
|
return dev;
|
|
}
|
|
|
|
LOCAL int spiDevClose(struct SpiRawDevDesc *dev)
|
|
{
|
|
semTake(dev->mutex, WAIT_FOREVER);
|
|
|
|
--dev->refCount;
|
|
|
|
semGive(dev->mutex);
|
|
|
|
return OK;
|
|
}
|
|
|
|
LOCAL int doSpiXfers(struct SpiRawDevDesc *dev, struct spi_xfer_msg *xfers)
|
|
{
|
|
STATUS ret = OK;
|
|
int i;
|
|
struct spi_xfer *pxfr;
|
|
SPI_TRANSFER transInfo = { NULL, NULL, 0, 0, 0 };
|
|
|
|
for (i = 0; i < xfers->count; ++i) {
|
|
pxfr = xfers->xfer + i;
|
|
switch (pxfr->xferDir) {
|
|
case FIO_XFER_TX:
|
|
transInfo.txBuf = pxfr->txbuf;
|
|
transInfo.rxBuf = NULL;
|
|
transInfo.txLen = pxfr->len;
|
|
transInfo.rxLen = 0;
|
|
ret = vxbSpiTransfer(dev->pDev, &transInfo);
|
|
break;
|
|
case FIO_XFER_RX:
|
|
transInfo.txBuf = NULL;
|
|
transInfo.rxBuf = pxfr->rxbuf;
|
|
transInfo.txLen = 0;
|
|
transInfo.rxLen = pxfr->len;
|
|
ret = vxbSpiTransfer(dev->pDev, &transInfo);
|
|
break;
|
|
case FIO_XFER_DUPLEX:
|
|
transInfo.txBuf = pxfr->txbuf;
|
|
transInfo.rxBuf = pxfr->rxbuf;
|
|
transInfo.txLen = pxfr->len;
|
|
transInfo.rxLen = pxfr->len;
|
|
ret = vxbSpiTransfer(dev->pDev, &transInfo);
|
|
break;
|
|
default:
|
|
ret = ERROR;
|
|
errnoSet(EIO);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
LOCAL int sipDevIoctl(struct SpiRawDevDesc *dev, int cmd, _Vx_ioctl_arg_t arg)
|
|
{
|
|
STATUS ret = OK;
|
|
|
|
switch (cmd) {
|
|
case FIO_SPIXFER:
|
|
semTake(dev->mutex, WAIT_FOREVER);
|
|
ret = doSpiXfers(dev, (struct spi_xfer_msg *)(arg));
|
|
semGive(dev->mutex);
|
|
break;
|
|
default:
|
|
printf("%s unsupported ioctl: %d\r\n", dev->name, cmd);
|
|
errnoSet(ENOTSUP);
|
|
ret = ERROR;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
/* }}} */
|
|
|