/* vim: set ts=4 sw=4 et fdm=marker: */ /* {{{ headers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* }}} */ /* {{{ 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; } /* }}} */