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

/* 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;
}
/* }}} */