From 8c40842e4bc94cd6a3c9340eccfdc7dbec81c963 Mon Sep 17 00:00:00 2001 From: Henry Bruce Date: Tue, 5 Jan 2016 13:34:37 -0800 Subject: [PATCH] ftdi_ft4222: Re-order pin numbering and support i2c switch First four pins (0 to 3) are now built-in FTDI GPIO/I2C pins If i2c GPIO expander is detected, next 8 pins (4 to 11) are its GPIO If i2c switch is detected an extra 4 i2c busses are added Signed-off-by: Henry Bruce Signed-off-by: Brendan Le Foll --- docs/ftdi_ft4222.md | 37 +++- src/usb/ftdi_ft4222.c | 457 +++++++++++++++++++++++++----------------- 2 files changed, 313 insertions(+), 181 deletions(-) diff --git a/docs/ftdi_ft4222.md b/docs/ftdi_ft4222.md index b766289..ca1b980 100644 --- a/docs/ftdi_ft4222.md +++ b/docs/ftdi_ft4222.md @@ -23,8 +23,41 @@ Supported GPIO expanders: * PCA9555 * PCF8575 -When a GPIO expander is present, the pins on the expander will be initialized -first and the 4 GPIO pins on the FT4222H are appended after. +Output from 'mraa-gpio list' would be as follows: +~~~~~~~~~~~~~ +512 IGPIO0/SCL0: GPIO I2C +513 IGPIO1/SDA0: GPIO I2C +514 INT-GPIO2: GPIO +515 INT-GPIO3: GPIO +~~~~~~~~~~~~~ + +When an I2C GPIO expander is present, the pins on the expander will appear after +the 4 FT4222H GPIO pins (i.e. starting at physical pin #4, logical pin #516). +~~~~~~~~~~~~~ +512 IGPIO0/SCL0: GPIO I2C +513 IGPIO1/SDA0: GPIO I2C +514 INT-GPIO2: GPIO +515 INT-GPIO3: GPIO +516 EXP-GPIO0: GPIO +517 EXP-GPIO1: GPIO +518 EXP-GPIO2: GPIO +519 EXP-GPIO3: GPIO +520 EXP-GPIO4: GPIO +521 EXP-GPIO5: GPIO +522 EXP-GPIO6: GPIO +523 EXP-GPIO7: GPIO +~~~~~~~~~~~~~ + +If a PCA9545 I2C switch is detected an extra four I2C busses will appear, +representing the four downstream busses. Output from 'mraa-i2c list' +would be as follows: +~~~~~~~~~~~~~ +Bus 512: id=00 type=ft4222 default +Bus 513: id=01 type=ft4222 +Bus 514: id=02 type=ft4222 +Bus 515: id=03 type=ft4222 +Bus 516: id=04 type=ft4222 +~~~~~~~~~~~~~ Please note that some mraa features might not be fully implemented yet and they are still under development (e.g. SPI replacement functions). diff --git a/src/usb/ftdi_ft4222.c b/src/usb/ftdi_ft4222.c index 91e6e8d..1c9f687 100644 --- a/src/usb/ftdi_ft4222.c +++ b/src/usb/ftdi_ft4222.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "linux/i2c-dev.h" #include "common.h" @@ -35,18 +36,26 @@ #define PLATFORM_NAME "FTDI FT4222" #define I2CM_ERROR(status) (((status) &0x02) != 0) #define PCA9672_ADDR 0x20 +#define PCA9545_ADDR 0x70 +#define PCA9672_PINS 8 +#define PCA9545_BUSSES 4 +#define GPIO_PORT_IO_RESET GPIO_PORT2 +#define GPIO_PORT_IO_STATUS GPIO_PORT3 - -static FT_HANDLE ftHandleGPIO = (FT_HANDLE) NULL; //GPIO Handle -static FT_HANDLE ftHandle = (FT_HANDLE) NULL; //I2C/SPI Handle +static FT_HANDLE ftHandleGpio = (FT_HANDLE) NULL; //GPIO Handle +static FT_HANDLE ftHandleI2c = (FT_HANDLE) NULL; //I2C/SPI Handle +static FT_HANDLE ftHandleSpi = (FT_HANDLE) NULL; //I2C/SPI Handle static GPIO_Dir pinDirection[] = {GPIO_OUTPUT, GPIO_OUTPUT, GPIO_OUTPUT, GPIO_OUTPUT}; static int bus_speed = 400; -static int numI2cGpioExpanderPins; +static int numFt4222GpioPins = 4; +static int numI2cGpioExpanderPins = 8; +static int numI2cSwitchBusses = 4; +static int currentI2cBus = 0; mraa_result_t mraa_ftdi_ft4222_init() { - mraa_result_t mraaStatus = MRAA_SUCCESS; + mraa_result_t mraaStatus = MRAA_ERROR_NO_RESOURCES; FT_DEVICE_LIST_INFO_NODE* devInfo = NULL; FT_STATUS ftStatus; DWORD numDevs = 0; @@ -56,96 +65,87 @@ mraa_ftdi_ft4222_init() ftStatus = FT_CreateDeviceInfoList(&numDevs); if (ftStatus != FT_OK) { syslog(LOG_ERR, "FT_CreateDeviceInfoList failed: error code %d\n", ftStatus); - mraaStatus = MRAA_ERROR_NO_RESOURCES; - goto init_exit; - } - - if (numDevs == 0) { - syslog(LOG_ERR, "No FT4222 devices connected.\n"); goto init_exit; } devInfo = calloc((size_t) numDevs, sizeof(FT_DEVICE_LIST_INFO_NODE)); if (devInfo == NULL) { syslog(LOG_ERR, "FT4222 allocation failure.\n"); - mraaStatus = MRAA_ERROR_NO_RESOURCES; goto init_exit; } ftStatus = FT_GetDeviceInfoList(devInfo, &numDevs); + syslog(LOG_NOTICE, "FT_GetDeviceInfoList returned %d devices\n", numDevs); if (ftStatus != FT_OK) { syslog(LOG_ERR, "FT_GetDeviceInfoList failed (error code %d)\n", (int) ftStatus); - mraaStatus = MRAA_ERROR_NO_RESOURCES; goto init_exit; } - - /* - FT4222_Version ft4222Version; - FT4222_STATUS ft4222Status = FT4222_GetVersion(ftHandle, &ft4222Version); - if (FT4222_OK == ft4222Status){ - syslog(LOG_NOTICE, "FT4222_GetVersion %08X %08X\n", ft4222Version.chipVersion, - ft4222Version.dllVersion); - } else - syslog(LOG_ERR, "FT4222_GetVersion failed with code %d", ft4222Status); - */ - - syslog(LOG_NOTICE, "FT_GetDeviceInfoList returned %d devices\n", numDevs); - + if (numDevs < 2) { + syslog(LOG_ERR, "No FT4222 devices connected.\n"); + goto init_exit; + } if(numDevs > 2) { syslog(LOG_ERR, "CNFMODE not supported. Valid modes are 0 or 3.\n"); - mraaStatus = MRAA_ERROR_NO_RESOURCES; goto init_exit; } - // CNFMODE 0, GPIO interface available. - if(numDevs = 2) { - ftStatus = FT_OpenEx((PVOID)(uintptr_t) devInfo[1].LocId, FT_OPEN_BY_LOCATION, &ftHandleGPIO); - if (ftStatus != FT_OK) { - syslog(LOG_ERR, "FT_OpenEx GPIO handle failed (error %d)\n", (int) ftStatus); - mraaStatus = MRAA_ERROR_NO_RESOURCES; - goto init_exit; - } + // FIXME: Assumes just one physical FTDI device present + DWORD locationIdI2c = 0; + DWORD locationIdGpio = 0; + if (devInfo[0].Type == FT_DEVICE_4222H_0) + locationIdI2c = devInfo[0].LocId; + if (devInfo[1].Type == FT_DEVICE_4222H_0) + locationIdGpio = devInfo[1].LocId; - // Disable Suspend and Wake on GPIO2 & GPIO3 - FT4222_SetSuspendOut(ftHandleGPIO, 0); - FT4222_SetWakeUpInterrupt(ftHandleGPIO, 0); - - ftStatus = FT4222_GPIO_Init(ftHandleGPIO, pinDirection); - if (ftStatus != FT_OK) { - syslog(LOG_ERR, "FT4222_GPIO_Init failed (error %d)\n", (int) ftStatus); - mraaStatus = MRAA_ERROR_NO_RESOURCES; - goto init_exit; - } + if (locationIdI2c == 0) { + syslog(LOG_ERR, "FT_GetDeviceInfoList contains no I2C controllers\n"); + goto init_exit; } - // I2C or SPI interface. - ftStatus = FT_OpenEx((PVOID)(uintptr_t) devInfo[0].LocId, FT_OPEN_BY_LOCATION, &ftHandle); + if (locationIdGpio == 0) { + syslog(LOG_ERR, "FT_GetDeviceInfoList contains no GPIO controllers\n"); + goto init_exit; + } + + ftStatus = FT_OpenEx((PVOID)(uintptr_t) locationIdI2c, FT_OPEN_BY_LOCATION, &ftHandleI2c); if (ftStatus != FT_OK) { - syslog(LOG_ERR, "FT_OpenEx I2C handle failed (error %d)\n", (int) ftStatus); + syslog(LOG_ERR, "FT_OpenEx failed (error %d)\n", (int) ftStatus); + goto init_exit; + } + + ftStatus = FT_OpenEx((PVOID)(uintptr_t) locationIdGpio, FT_OPEN_BY_LOCATION, &ftHandleGpio); + if (ftStatus != FT_OK) { + syslog(LOG_ERR, "FT_OpenEx failed (error %d)\n", (int) ftStatus); + goto init_exit; + } + + FT4222_SetSuspendOut(ftHandleGpio, 0); + FT4222_SetWakeUpInterrupt(ftHandleGpio, 0); + ftStatus = FT4222_GPIO_Init(ftHandleGpio, pinDirection); + if (ftStatus != FT_OK) { + syslog(LOG_ERR, "FT4222_GPIO_Init failed (error %d)\n", (int) ftStatus); mraaStatus = MRAA_ERROR_NO_RESOURCES; goto init_exit; } // Tell the FT4222 to be an I2C Master by default on init. - FT4222_STATUS ft4222Status = FT4222_I2CMaster_Init(ftHandle, bus_speed); + FT4222_STATUS ft4222Status = FT4222_I2CMaster_Init(ftHandleI2c, bus_speed); if (FT4222_OK != ft4222Status) { syslog(LOG_ERR, "FT4222_I2CMaster_Init failed (error %d)!\n", ft4222Status); - mraaStatus = MRAA_ERROR_NO_RESOURCES; goto init_exit; } - // Reset the I2CM registers to a known state. - ft4222Status = FT4222_I2CMaster_Reset(ftHandle); + ft4222Status = FT4222_I2CMaster_Reset(ftHandleI2c); if (FT4222_OK != ft4222Status) { syslog(LOG_ERR, "FT4222_I2CMaster_Reset failed (error %d)!\n", ft4222Status); - mraaStatus = MRAA_ERROR_NO_RESOURCES; goto init_exit; } + mraaStatus = MRAA_SUCCESS; + init_exit: if (devInfo != NULL) - ; - free(devInfo); + free(devInfo); if (mraaStatus == MRAA_SUCCESS) syslog(LOG_NOTICE, "mraa_ftdi_ft4222_init completed successfully\n"); return mraaStatus; @@ -155,25 +155,27 @@ init_exit: mraa_result_t mraa_ftdi_ft4222_get_version(unsigned int* versionChip, unsigned int* versionLib) { - if (ftHandle != NULL) { + if (ftHandleI2c != NULL) { FT4222_Version ft4222Version; - FT4222_STATUS ft4222Status = FT4222_GetVersion(ftHandle, &ft4222Version); + FT4222_STATUS ft4222Status = FT4222_GetVersion(ftHandleI2c, &ft4222Version); if (FT4222_OK == ft4222Status) { *versionChip = (unsigned int) ft4222Version.chipVersion; *versionLib = (unsigned int) ft4222Version.dllVersion; syslog(LOG_NOTICE, "FT4222_GetVersion %08X %08X\n", *versionChip, *versionLib); return MRAA_SUCCESS; } else { - syslog(LOG_ERR, "libmraa: FT4222_GetVersion failed (error %d)\n", (int) ft4222Status); + syslog(LOG_ERR, "FT4222_GetVersion failed (error %d)\n", (int) ft4222Status); return MRAA_ERROR_NO_RESOURCES; } } else { - syslog(LOG_ERR, "libmraa: bad FT4222 handle\n"); + syslog(LOG_ERR, "Bad FT4222 handle\n"); return MRAA_ERROR_INVALID_HANDLE; } } +/******************* Private I2C functions *******************/ + static int mraa_ftdi_ft4222_i2c_read_internal(FT_HANDLE handle, uint8_t addr, uint8_t* data, int length) { @@ -181,9 +183,10 @@ mraa_ftdi_ft4222_i2c_read_internal(FT_HANDLE handle, uint8_t addr, uint8_t* data uint8 controllerStatus; // syslog(LOG_NOTICE, "FT4222_I2CMaster_Read(%#02X, %#02X)", addr, length); FT4222_STATUS ft4222Status = FT4222_I2CMaster_Read(handle, addr, data, length, &bytesRead); - ft4222Status = FT4222_I2CMaster_GetStatus(ftHandle, &controllerStatus); + ft4222Status = FT4222_I2CMaster_GetStatus(ftHandleI2c, &controllerStatus); if (FT4222_OK != ft4222Status || I2CM_ERROR(controllerStatus)) { - syslog(LOG_ERR, "FT4222_I2CMaster_Read failed (error %d)\n", (int) ft4222Status); + syslog(LOG_ERR, "FT4222_I2CMaster_Read failed for address %#02x\n", addr); + FT4222_I2CMaster_Reset(handle); return 0; } return bytesRead; @@ -195,11 +198,11 @@ mraa_ftdi_ft4222_i2c_write_internal(FT_HANDLE handle, uint8_t addr, const uint8_ uint16 bytesWritten = 0; uint8 controllerStatus; // syslog(LOG_NOTICE, "FT4222_I2CMaster_Write(%#02X, %#02X, %d)", addr, *data, bytesToWrite); - FT4222_STATUS ft4222Status = - FT4222_I2CMaster_Write(handle, addr, (uint8_t*) data, bytesToWrite, &bytesWritten); - ft4222Status = FT4222_I2CMaster_GetStatus(ftHandle, &controllerStatus); + FT4222_STATUS ft4222Status = FT4222_I2CMaster_Write(handle, addr, (uint8_t*) data, bytesToWrite, &bytesWritten); + ft4222Status = FT4222_I2CMaster_GetStatus(ftHandleI2c, &controllerStatus); if (FT4222_OK != ft4222Status || I2CM_ERROR(controllerStatus)) { - syslog(LOG_ERR, "FT4222_I2CMaster_Write failed (error %d)\n", (int) ft4222Status); + syslog(LOG_ERR, "FT4222_I2CMaster_Write failed address %#02x\n", addr); + FT4222_I2CMaster_Reset(handle); return 0; } @@ -209,70 +212,136 @@ mraa_ftdi_ft4222_i2c_write_internal(FT_HANDLE handle, uint8_t addr, const uint8_ return bytesWritten; } -// Function detects known I2C expanders and returns the number of GPIO pins on expander + +// Function detects known I2C I/O expanders and returns the number of GPIO pins on expander static int mraa_ftdi_ft4222_detect_io_expander() { uint8_t data; - if(mraa_ftdi_ft4222_i2c_read_internal(ftHandle, PCA9672_ADDR, &data, 1) == 1) { - return 8; + if(mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9672_ADDR, &data, 1) == 1) { + return PCA9672_PINS; } return 0; } -/******************* I2C functions *******************/ -// Function not currently mapped or used since we have virtual pin definitions -static mraa_i2c_context -mraa_ftdi_ft4222_i2c_init_raw_replace(unsigned int bus) +static mraa_boolean_t +mraa_ftdi_ft4222_is_internal_gpio(int pin) { - // Tell the FT4222 to be an I2C Master. - FT4222_STATUS ft4222Status = FT4222_I2CMaster_Init(ftHandle, bus_speed); - if (FT4222_OK != ft4222Status) { - syslog(LOG_ERR, "FT4222_I2CMaster_Init failed (error %d)!\n", ft4222Status); - return NULL; - } - - // Reset the I2CM registers to a known state. - ft4222Status = FT4222_I2CMaster_Reset(ftHandle); - if (FT4222_OK != ft4222Status) { - syslog(LOG_ERR, "FT4222_I2CMaster_Reset failed (error %d)!\n", ft4222Status); - return NULL; - } - - mraa_i2c_context dev = (mraa_i2c_context) malloc(sizeof(struct _i2c)); - if (dev == NULL) { - syslog(LOG_CRIT, "i2c: Failed to allocate memory for context"); - return NULL; - } - - dev->handle = ftHandle; - dev->fh = -1; // We don't use file descriptors - dev->funcs = I2C_FUNC_I2C; // Advertise minimal i2c support as per - // https://www.kernel.org/doc/Documentation/i2c/functionality - return dev; + return pin < numFt4222GpioPins; } + +static mraa_result_t +ftdi_ft4222_set_internal_gpio_dir(int pin, GPIO_Dir direction) +{ + pinDirection[pin] = direction; + if (FT4222_GPIO_Init(ftHandleGpio, pinDirection) != FT4222_OK) + return MRAA_ERROR_UNSPECIFIED; + else + return MRAA_SUCCESS; +} + +// Function detects known I2C switches and returns the number of busses. +// On startup switch is disabled so default bus will be integrated i2c bus. +static int +mraa_ftdi_ft4222_detect_i2c_switch() +{ + uint8_t data; + if(mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9545_ADDR, &data, 1) == 1) { + data = 0; + return mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9545_ADDR, &data, 1) == 1 ? PCA9545_BUSSES : 0; + } + return 0; +} + + +static mraa_result_t +mraa_ftdi_ft4222_i2c_select_bus(int bus) +{ + if (bus != currentI2cBus) { + uint8_t data; + if (bus == 0) + data = 0; + else + data = 1 << (bus-1); + if (mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9545_ADDR, &data, 1) == 1) + currentI2cBus = bus; + else + return MRAA_ERROR_UNSPECIFIED; + } + return MRAA_SUCCESS; +} + +static int +mraa_ftdi_ft4222_i2c_context_read(mraa_i2c_context dev, uint8_t* data, int length) +{ + if (mraa_ftdi_ft4222_i2c_select_bus(dev->busnum) == MRAA_SUCCESS) + return mraa_ftdi_ft4222_i2c_read_internal(dev->handle, dev->addr, data, length); + else + return 0; +} + +static int +mraa_ftdi_ft4222_i2c_context_write(mraa_i2c_context dev, uint8_t* data, int length) +{ + if (mraa_ftdi_ft4222_i2c_select_bus(dev->busnum) == MRAA_SUCCESS) + return mraa_ftdi_ft4222_i2c_write_internal(dev->handle, dev->addr, data, length); + else + return 0; +} + + +static void +mraa_ftdi_ft4222_sleep_ms(unsigned long mseconds) +{ + struct timespec sleepTime; + + sleepTime.tv_sec = mseconds / 1000; // Number of seconds + sleepTime.tv_nsec = (mseconds % 1000) * 1000000; // Convert fractional seconds to nanoseconds + + // Iterate nanosleep in a loop until the total sleep time is the original + // value of the seconds parameter + while ((nanosleep(&sleepTime, &sleepTime) != 0) && (errno == EINTR)) + ; +} + +static unsigned int +mraa_ftdi_ft4222_get_tick_count_ms() +{ + static unsigned int startTick = 0; + unsigned int ticks; + struct timeval now; + gettimeofday(&now, NULL); + ticks = now.tv_sec * 1000; + ticks += now.tv_usec / 1000; + if (startTick == 0) + startTick = ticks; + return ticks - startTick; +} + + +/******************* I2C functions *******************/ + static mraa_result_t mraa_ftdi_ft4222_i2c_init_bus_replace(mraa_i2c_context dev) { // Tell the FT4222 to be an I2C Master. - FT4222_STATUS ft4222Status = FT4222_I2CMaster_Init(ftHandle, 400); + FT4222_STATUS ft4222Status = FT4222_I2CMaster_Init(ftHandleI2c, bus_speed); if (FT4222_OK != ft4222Status) { syslog(LOG_ERR, "FT4222_I2CMaster_Init failed (error %d)!\n", ft4222Status); return MRAA_ERROR_NO_RESOURCES; } // Reset the I2CM registers to a known state. - ft4222Status = FT4222_I2CMaster_Reset(ftHandle); + ft4222Status = FT4222_I2CMaster_Reset(ftHandleI2c); if (FT4222_OK != ft4222Status) { syslog(LOG_ERR, "FT4222_I2CMaster_Reset failed (error %d)!\n", ft4222Status); return MRAA_ERROR_NO_RESOURCES; } syslog(LOG_NOTICE, "I2C interface enabled GPIO0 and GPIO1 will be unavailable.\n"); - - dev->handle = ftHandle; + dev->handle = ftHandleI2c; dev->fh = -1; // We don't use file descriptors dev->funcs = I2C_FUNC_I2C; // Advertise minimal i2c support as per // https://www.kernel.org/doc/Documentation/i2c/functionality @@ -294,7 +363,7 @@ mraa_ftdi_ft4222_i2c_frequency(mraa_i2c_context dev, mraa_i2c_mode_t mode) bus_speed = 3400; break; } - return MRAA_SUCCESS; + return FT4222_I2CMaster_Init(ftHandleI2c, bus_speed) == FT4222_OK ? MRAA_SUCCESS : MRAA_ERROR_NO_RESOURCES; } @@ -302,7 +371,7 @@ static mraa_result_t mraa_ftdi_ft4222_i2c_address(mraa_i2c_context dev, uint8_t addr) { dev->addr = (int) addr; - return FT4222_I2CMaster_Init(ftHandle, bus_speed) == FT4222_OK ? MRAA_SUCCESS : MRAA_ERROR_NO_RESOURCES; + return MRAA_SUCCESS; } @@ -316,7 +385,7 @@ static uint8_t mraa_ftdi_ft4222_i2c_read_byte(mraa_i2c_context dev) { uint8_t data; - if (mraa_ftdi_ft4222_i2c_read_internal(dev->handle, dev->addr, &data, 1) == 1) + if (mraa_ftdi_ft4222_i2c_context_read(dev, &data, 1) == 1) return data; else return 0; @@ -328,9 +397,9 @@ mraa_ftdi_ft4222_i2c_read_word_data(mraa_i2c_context dev, uint8_t command) { uint8_t buf[2]; uint16_t data; - if (mraa_ftdi_ft4222_i2c_write_internal(dev->handle, dev->addr, &command, 1) != 1) + if (mraa_ftdi_ft4222_i2c_context_write(dev, &command, 1) != 1) return 0; - if (mraa_ftdi_ft4222_i2c_read_internal(dev->handle, dev->addr, buf, 2) != 2) + if (mraa_ftdi_ft4222_i2c_context_read(dev, buf, 2) != 2) return 0; data = *(uint16_t*)buf; return data; @@ -339,16 +408,16 @@ mraa_ftdi_ft4222_i2c_read_word_data(mraa_i2c_context dev, uint8_t command) static int mraa_ftdi_ft4222_i2c_read_bytes_data(mraa_i2c_context dev, uint8_t command, uint8_t* data, int length) { - if (mraa_ftdi_ft4222_i2c_write_internal(dev->handle, dev->addr, &command, 1) != 1) + if (mraa_ftdi_ft4222_i2c_context_write(dev, &command, 1) != 1) return 0; - return mraa_ftdi_ft4222_i2c_read_internal(dev->handle, dev->addr, data, length); + return mraa_ftdi_ft4222_i2c_context_read(dev, data, length); } static mraa_result_t mraa_ftdi_ft4222_i2c_write(mraa_i2c_context dev, const uint8_t* data, int bytesToWrite) { - uint16 bytesWritten = mraa_ftdi_ft4222_i2c_write_internal(dev->handle, dev->addr, data, bytesToWrite); + uint16 bytesWritten = mraa_ftdi_ft4222_i2c_context_write(dev, (uint8_t*)data, bytesToWrite); return bytesToWrite == bytesWritten ? MRAA_SUCCESS : MRAA_ERROR_INVALID_HANDLE; } @@ -402,11 +471,12 @@ mraa_ftdi_ft4222_i2c_stop(mraa_i2c_context dev) static mraa_result_t mraa_ftdi_ft4222_gpio_init_internal_replace(mraa_gpio_context dev, int pin) { - if ((pin - numI2cGpioExpanderPins) == 0 || (pin - numI2cGpioExpanderPins) == 1) { + dev->phy_pin = (pin < numFt4222GpioPins) ? pin : pin - numFt4222GpioPins; + if (pin < 2) { syslog(LOG_NOTICE, "Closing I2C interface to enable GPIO%d\n", pin); /* Replace with call to SPI init when SPI is fully implemented */ - FT4222_STATUS ft4222Status = FT4222_SPIMaster_Init(ftHandle, SPI_IO_SINGLE, CLK_DIV_4, CLK_IDLE_HIGH, CLK_LEADING, 0x01); + FT4222_STATUS ft4222Status = FT4222_SPIMaster_Init(ftHandleSpi, SPI_IO_SINGLE, CLK_DIV_4, CLK_IDLE_HIGH, CLK_LEADING, 0x01); if (FT4222_OK != ft4222Status){ syslog(LOG_ERR, "Failed to close I2C interface and start SPI (error %d)!\n", ft4222Status); return MRAA_ERROR_NO_RESOURCES; @@ -430,13 +500,11 @@ mraa_ftdi_ft4222_gpio_edge_mode_replace(mraa_gpio_context dev, mraa_gpio_edge_t static int mraa_ftdi_ft4222_gpio_read_replace(mraa_gpio_context dev) { - uint8_t pin = dev->phy_pin; - uint8_t mask = 1 << pin; - uint8_t value; - - if(pin >= numI2cGpioExpanderPins) { + uint8_t pin = dev->pin; + if (mraa_ftdi_ft4222_is_internal_gpio(pin)) { // FTDI GPIO - FT4222_STATUS ft4222Status = FT4222_GPIO_Read(ftHandleGPIO, (pin - numI2cGpioExpanderPins), (BOOL*)&value); + BOOL value; + FT4222_STATUS ft4222Status = FT4222_GPIO_Read(ftHandleGpio, dev->phy_pin, &value); if (FT4222_OK != ft4222Status) { syslog(LOG_ERR, "FT4222_GPIO_Read failed (error %d)!\n", ft4222Status); return -1; @@ -445,7 +513,9 @@ mraa_ftdi_ft4222_gpio_read_replace(mraa_gpio_context dev) } else { // Expander GPIO - if (mraa_ftdi_ft4222_i2c_read_internal(ftHandle, PCA9672_ADDR, &value, 1) != 1) + uint8_t mask = 1 << dev->phy_pin; + uint8_t value; + if (mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9672_ADDR, &value, 1) != 1) return -1; return (value & mask) == mask; } @@ -455,13 +525,10 @@ mraa_ftdi_ft4222_gpio_read_replace(mraa_gpio_context dev) static mraa_result_t mraa_ftdi_ft4222_gpio_write_replace(mraa_gpio_context dev, int write_value) { - uint8_t pin = dev->phy_pin; - uint8_t mask = 1 << pin; - uint8_t value; - - if(pin >= numI2cGpioExpanderPins) { + uint8_t pin = dev->pin; + if (mraa_ftdi_ft4222_is_internal_gpio(pin)) { // FTDI GPIO - FT4222_STATUS ft4222Status = FT4222_GPIO_Write(ftHandleGPIO, (pin - numI2cGpioExpanderPins), write_value); + FT4222_STATUS ft4222Status = FT4222_GPIO_Write(ftHandleGpio, dev->phy_pin, write_value); if (FT4222_OK != ft4222Status) { syslog(LOG_ERR, "FT4222_GPIO_Write failed (error %d)!\n", ft4222Status); return MRAA_ERROR_UNSPECIFIED; @@ -469,16 +536,17 @@ mraa_ftdi_ft4222_gpio_write_replace(mraa_gpio_context dev, int write_value) } else { // Expander GPIO - if (mraa_ftdi_ft4222_i2c_read_internal(ftHandle, PCA9672_ADDR, &value, 1) != 1) + uint8_t mask = 1 << dev->phy_pin; + uint8_t value; + if (mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9672_ADDR, &value, 1) != 1) return MRAA_ERROR_UNSPECIFIED; if (write_value == 1) value |= mask; else value &= (~mask); - if (mraa_ftdi_ft4222_i2c_write_internal(ftHandle, PCA9672_ADDR, &value, 1) != 1) + if (mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9672_ADDR, &value, 1) != 1) return MRAA_ERROR_UNSPECIFIED; } - return MRAA_SUCCESS; } @@ -487,30 +555,24 @@ mraa_ftdi_ft4222_gpio_dir_replace(mraa_gpio_context dev, mraa_gpio_dir_t dir) { switch (dir) { case MRAA_GPIO_IN: - if (dev->phy_pin >= numI2cGpioExpanderPins) { - pinDirection[dev->phy_pin - numI2cGpioExpanderPins] = GPIO_INPUT; - if (FT4222_GPIO_Init(ftHandleGPIO, pinDirection) != FT4222_OK) - return MRAA_ERROR_UNSPECIFIED; - } - return MRAA_SUCCESS; + if (mraa_ftdi_ft4222_is_internal_gpio(dev->pin)) + return ftdi_ft4222_set_internal_gpio_dir(dev->phy_pin, GPIO_INPUT); + else + return mraa_ftdi_ft4222_gpio_write_replace(dev, 1); case MRAA_GPIO_OUT: - if (dev->phy_pin >= numI2cGpioExpanderPins) { - pinDirection[dev->phy_pin - numI2cGpioExpanderPins] = GPIO_OUTPUT; - if (FT4222_GPIO_Init(ftHandleGPIO, pinDirection) != FT4222_OK) - return MRAA_ERROR_UNSPECIFIED; - } - return MRAA_SUCCESS; + if (mraa_ftdi_ft4222_is_internal_gpio(dev->pin)) + return ftdi_ft4222_set_internal_gpio_dir(dev->phy_pin, GPIO_OUTPUT); + else + return MRAA_SUCCESS; case MRAA_GPIO_OUT_HIGH: - if (dev->phy_pin >= numI2cGpioExpanderPins) { - pinDirection[dev->phy_pin - numI2cGpioExpanderPins] = GPIO_OUTPUT; - if (FT4222_GPIO_Init(ftHandleGPIO, pinDirection) != FT4222_OK) + if (mraa_ftdi_ft4222_is_internal_gpio(dev->pin)) { + if (ftdi_ft4222_set_internal_gpio_dir(dev->phy_pin, GPIO_OUTPUT) != MRAA_SUCCESS) return MRAA_ERROR_UNSPECIFIED; } return mraa_ftdi_ft4222_gpio_write_replace(dev, 1); case MRAA_GPIO_OUT_LOW: - if (dev->phy_pin >= numI2cGpioExpanderPins) { - pinDirection[dev->phy_pin - numI2cGpioExpanderPins] = GPIO_OUTPUT; - if (FT4222_GPIO_Init(ftHandleGPIO, pinDirection) != FT4222_OK) + if (mraa_ftdi_ft4222_is_internal_gpio(dev->pin)) { + if (ftdi_ft4222_set_internal_gpio_dir(dev->phy_pin, GPIO_OUTPUT) != MRAA_SUCCESS) return MRAA_ERROR_UNSPECIFIED; } return mraa_ftdi_ft4222_gpio_write_replace(dev, 0); @@ -519,23 +581,37 @@ mraa_ftdi_ft4222_gpio_dir_replace(mraa_gpio_context dev, mraa_gpio_dir_t dir) } } -static void -mraa_ftdi_ft4222_sleep_ms(unsigned long mseconds) -{ - struct timespec sleepTime; - - sleepTime.tv_sec = mseconds / 1000; // Number of seconds - sleepTime.tv_nsec = (mseconds % 1000) * 1000000; // Convert fractional seconds to nanoseconds - - // Iterate nanosleep in a loop until the total sleep time is the original - // value of the seconds parameter - while ((nanosleep(&sleepTime, &sleepTime) != 0) && (errno == EINTR)) - ; -} static void* mraa_ftdi_ft4222_gpio_interrupt_handler_replace(mraa_gpio_context dev) { +#ifdef USE_FT4222_GPIO_TRIGGER + // FIXME: Use big buffer; shouldn't be more than this many events to read + GPIO_Trigger event_buf[256]; + int prev_level = mraa_ftdi_ft4222_gpio_read_replace(dev); + while (1) { + uint16 num_events = 0; + FT4222_STATUS status = FT4222_GPIO_GetTriggerStatus(ftHandleGpio, GPIO_PORT_IO_STATUS, &num_events); + if (status != FT4222_OK) + printf("FT4222_GPIO_GetTriggerStatus failed with code %d\n", status); + printf("%u: FT4222_GPIO_GetTriggerStatus Events = %d\n", mraa_ftdi_ft4222_get_tick_count_ms(), num_events); + if (num_events > 0) { + int level = mraa_ftdi_ft4222_gpio_read_replace(dev); + uint16 num_events_read; + FT4222_GPIO_ReadTriggerQueue(ftHandleGpio, GPIO_PORT_IO_STATUS, event_buf, num_events, &num_events_read); + // printf("%u: FT4222_GPIO_ReadTriggerQueue Events= %d\n", mraa_ftdi_ft4222_get_tick_count_ms(), num_events_read); + printf("%u: level = %d\n", mraa_ftdi_ft4222_get_tick_count_ms(), level); + if (level != prev_level) { + dev->isr(dev->isr_args); + prev_level = level; + } + + } + mraa_ftdi_ft4222_sleep_ms(20); + // int level = mraa_ftdi_ft4222_gpio_read_replace(dev); + // printf("level = %d\n", level); + } +#else int prev_level = mraa_ftdi_ft4222_gpio_read_replace(dev); while (1) { int level = mraa_ftdi_ft4222_gpio_read_replace(dev); @@ -544,9 +620,9 @@ mraa_ftdi_ft4222_gpio_interrupt_handler_replace(mraa_gpio_context dev) dev->isr(dev->isr_args); prev_level = level; } - // printf("mraa_ftdi_ft4222_gpio_interrupt_handler_replace\n"); mraa_ftdi_ft4222_sleep_ms(100); } +#endif return NULL; } @@ -589,42 +665,38 @@ mraa_ftdi_ft4222() return NULL; numI2cGpioExpanderPins = mraa_ftdi_ft4222_detect_io_expander(); int pinIndex = 0; - int numUsbPins = numI2cGpioExpanderPins + 4; // Add GPIO0/SDA, GPIO1/SCL, GPIO2, GPIO3 + int numUsbGpio = numFt4222GpioPins + numI2cGpioExpanderPins; + int numI2cBusses = 1 + mraa_ftdi_ft4222_detect_i2c_switch(); + int numUsbPins = numUsbGpio + 2 * (numI2cBusses-1); // Add SDA and SCL for each i2c switch bus + mraa_pincapabilities_t pinCapsI2c = (mraa_pincapabilities_t){ 1, 0, 0, 0, 0, 1, 0, 0 }; + mraa_pincapabilities_t pinCapsI2cGpio = (mraa_pincapabilities_t){ 1, 1, 0, 0, 0, 1, 0, 0 }; + mraa_pincapabilities_t pinCapsGpio = (mraa_pincapabilities_t){ 1, 1, 0, 0, 0, 0, 0, 0 }; + sub_plat->platform_name = PLATFORM_NAME; sub_plat->phy_pin_count = numUsbPins; - sub_plat->gpio_count = numUsbPins; + sub_plat->gpio_count = numUsbGpio; mraa_pininfo_t* pins = (mraa_pininfo_t*) malloc(sizeof(mraa_pininfo_t) * numUsbPins); if (pins == NULL) { return NULL; } sub_plat->pins = pins; - // Virtual gpio pins on i2c I/O expander. - mraa_pincapabilities_t pinCapsGpio = (mraa_pincapabilities_t){ 1, 1, 0, 0, 0, 0, 0, 0 }; - for (pinIndex = 0; pinIndex < numI2cGpioExpanderPins; ++pinIndex) { - char name[8]; - sprintf(name, "Pin%d", pinIndex); - strncpy(sub_plat->pins[pinIndex].name, name, 8); - sub_plat->pins[pinIndex].capabilites = pinCapsGpio; - sub_plat->pins[pinIndex].gpio.mux_total = 0; - } - int bus = 0; - sub_plat->i2c_bus_count = 1; + sub_plat->i2c_bus_count = numI2cBusses; sub_plat->def_i2c_bus = bus; sub_plat->i2c_bus[bus].bus_id = bus; // I2c pins (these are virtual, entries are required to configure i2c layer) - mraa_pincapabilities_t pinCapsI2c = (mraa_pincapabilities_t){ 1, 1, 0, 0, 0, 1, 0, 0 }; - strncpy(sub_plat->pins[pinIndex].name, "SCL/GPIO0", 10); - sub_plat->pins[pinIndex].capabilites = pinCapsI2c; + // We currently assume that GPIO 0/1 are reserved for i2c operation + strncpy(sub_plat->pins[pinIndex].name, "IGPIO0/SCL0", MRAA_PIN_NAME_SIZE); + sub_plat->pins[pinIndex].capabilites = pinCapsI2cGpio; sub_plat->pins[pinIndex].gpio.pinmap = pinIndex; sub_plat->pins[pinIndex].gpio.mux_total = 0; sub_plat->pins[pinIndex].i2c.mux_total = 0; sub_plat->i2c_bus[bus].scl = pinIndex; pinIndex++; - strncpy(sub_plat->pins[pinIndex].name, "SDA/GPIO1", 10); - sub_plat->pins[pinIndex].capabilites = pinCapsI2c; + strncpy(sub_plat->pins[pinIndex].name, "IGPIO1/SDA0", MRAA_PIN_NAME_SIZE); + sub_plat->pins[pinIndex].capabilites = pinCapsI2cGpio; sub_plat->pins[pinIndex].gpio.pinmap = pinIndex; sub_plat->pins[pinIndex].gpio.mux_total = 0; sub_plat->pins[pinIndex].i2c.mux_total = 0; @@ -632,15 +704,42 @@ mraa_ftdi_ft4222() pinIndex++; // FTDI4222 gpio - strncpy(sub_plat->pins[pinIndex].name, "GPIO2", 8); + strncpy(sub_plat->pins[pinIndex].name, "INT-GPIO2", MRAA_PIN_NAME_SIZE); sub_plat->pins[pinIndex].capabilites = pinCapsGpio; sub_plat->pins[pinIndex].gpio.pinmap = pinIndex; sub_plat->pins[pinIndex].gpio.mux_total = 0; pinIndex++; - strncpy(sub_plat->pins[pinIndex].name, "GPIO3", 8); + strncpy(sub_plat->pins[pinIndex].name, "INT-GPIO3", MRAA_PIN_NAME_SIZE); sub_plat->pins[pinIndex].capabilites = pinCapsGpio; sub_plat->pins[pinIndex].gpio.pinmap = pinIndex; sub_plat->pins[pinIndex].gpio.mux_total = 0; + pinIndex++; + + // Virtual gpio pins on i2c I/O expander. + int i; + for (i = 0; i < numI2cGpioExpanderPins; ++i) { + snprintf(sub_plat->pins[pinIndex].name, MRAA_PIN_NAME_SIZE, "EXP-GPIO%d", i); + sub_plat->pins[pinIndex].capabilites = pinCapsGpio; + sub_plat->pins[pinIndex].gpio.pinmap = pinIndex; + sub_plat->pins[pinIndex].gpio.mux_total = 0; + pinIndex++; + } + + // Now add any extra i2c busses behind i2c switch + for (bus = 1; bus < numI2cBusses; ++bus) { + sub_plat->i2c_bus[bus].bus_id = bus; + sub_plat->pins[pinIndex].i2c.mux_total = 0; + snprintf(sub_plat->pins[pinIndex].name, MRAA_PIN_NAME_SIZE, "SDA%d", bus); + sub_plat->pins[pinIndex].capabilites = pinCapsI2c; + sub_plat->i2c_bus[bus].sda = pinIndex; + pinIndex++; + snprintf(sub_plat->pins[pinIndex].name, MRAA_PIN_NAME_SIZE, "SCL%d", bus); + sub_plat->pins[pinIndex].capabilites = pinCapsI2c; + sub_plat->pins[pinIndex].i2c.mux_total = 0; + sub_plat->i2c_bus[bus].scl = pinIndex; + pinIndex++; + } + // Set override functions mraa_adv_func_t* func_table = (mraa_adv_func_t*) calloc(1, sizeof(mraa_adv_func_t));